VirtualBox

source: vbox/trunk/src/VBox/Devices/Graphics/DevVGA.cpp@ 103417

Last change on this file since 103417 was 103417, checked in by vboxsync, 2 months ago

Devices/Graphics,VMM: Fix some unused function warnings, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 266.1 KB
Line 
1/* $Id: DevVGA.cpp 103417 2024-02-19 08:44:55Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 * --------------------------------------------------------------------
27 *
28 * This code is based on:
29 *
30 * QEMU VGA Emulator.
31 *
32 * Copyright (c) 2003 Fabrice Bellard
33 *
34 * Permission is hereby granted, free of charge, to any person obtaining a copy
35 * of this software and associated documentation files (the "Software"), to deal
36 * in the Software without restriction, including without limitation the rights
37 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 * copies of the Software, and to permit persons to whom the Software is
39 * furnished to do so, subject to the following conditions:
40 *
41 * The above copyright notice and this permission notice shall be included in
42 * all copies or substantial portions of the Software.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
47 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
50 * THE SOFTWARE.
51 */
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57
58/* WARNING!!! All defines that affect VGAState should be placed in DevVGA.h !!!
59 * NEVER place them here as this would lead to VGASTATE inconsistency
60 * across different .cpp files !!!
61 */
62
63#ifdef VBOX_WITH_HGSMI
64#define PCIDEV_2_VGASTATE(pPciDev) ((PVGASTATE)((uintptr_t)pPciDev - RT_OFFSETOF(VGASTATE, Dev)))
65#endif /* VBOX_WITH_HGSMI */
66
67/* VGA text mode blinking constants (cursor and blinking chars). */
68#define VGA_BLINK_PERIOD_FULL (RT_NS_100MS * 4) /**< Blink cycle length. */
69#define VGA_BLINK_PERIOD_ON (RT_NS_100MS * 2) /**< How long cursor/text is visible. */
70
71/* EGA compatible switch values (in high nibble).
72 * XENIX 2.1.x/2.2.x is known to rely on the switch values.
73 */
74#define EGA_SWITCHES 0x90 /* Off-on-on-off, high-res color EGA display. */
75
76
77/*********************************************************************************************************************************
78* Header Files *
79*********************************************************************************************************************************/
80#define LOG_GROUP LOG_GROUP_DEV_VGA
81#include <iprt/asm-mem.h>
82#include <VBox/vmm/pdmdev.h>
83#include <VBox/vmm/pgm.h>
84#include <VBox/AssertGuest.h>
85#ifdef IN_RING3
86# include <iprt/mem.h>
87# include <iprt/ctype.h>
88#endif /* IN_RING3 */
89#include <iprt/assert.h>
90#include <iprt/asm.h>
91#include <iprt/file.h>
92#include <iprt/time.h>
93#include <iprt/string.h>
94#include <iprt/uuid.h>
95
96#include <iprt/formats/bmp.h>
97
98#include <VBox/VMMDev.h>
99#include <VBoxVideo.h>
100#include <VBox/bioslogo.h>
101
102/* should go BEFORE any other DevVGA include to make all DevVGA.h config defines be visible */
103#include "DevVGA.h"
104
105#if defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
106# include "DevVGAModes.h"
107# include <stdio.h> /* sscan */
108#endif
109
110#include "VBoxDD.h"
111#include "VBoxDD2.h"
112
113#ifdef VBOX_WITH_VMSVGA
114#include "DevVGA-SVGA.h"
115#endif
116
117
118/*********************************************************************************************************************************
119* Structures and Typedefs *
120*********************************************************************************************************************************/
121
122/** The BIOS boot menu text position, X. */
123#define LOGO_F12TEXT_X 304
124/** The BIOS boot menu text position, Y. */
125#define LOGO_F12TEXT_Y 460
126
127/** Width of the "Press F12 to select boot device." bitmap.
128 Anything that exceeds the limit of F12BootText below is filled with
129 background. */
130#define LOGO_F12TEXT_WIDTH 286
131/** Height of the boot device selection bitmap, see LOGO_F12TEXT_WIDTH. */
132#define LOGO_F12TEXT_HEIGHT 12
133
134/** The BIOS logo delay time (msec). */
135#define LOGO_DELAY_TIME 2000
136
137#define LOGO_MAX_WIDTH 640
138#define LOGO_MAX_HEIGHT 480
139#define LOGO_MAX_SIZE LOGO_MAX_WIDTH * LOGO_MAX_HEIGHT * 4
140
141
142/*********************************************************************************************************************************
143* Global Variables *
144*********************************************************************************************************************************/
145#ifdef IN_RING3
146/* "Press F12 to select boot device." bitmap. */
147static const uint8_t g_abLogoF12BootText[] =
148{
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x0F, 0x7C,
152 0xF8, 0xF0, 0x01, 0xE0, 0x81, 0x9F, 0x3F, 0x00, 0x70, 0xF8, 0x00, 0xE0, 0xC3,
153 0x07, 0x0F, 0x1F, 0x3E, 0x70, 0x00, 0xF0, 0xE1, 0xC3, 0x07, 0x0E, 0x00, 0x6E,
154 0x7C, 0x60, 0xE0, 0xE1, 0xC3, 0x07, 0xC6, 0x80, 0x81, 0x31, 0x63, 0xC6, 0x00,
155 0x30, 0x80, 0x61, 0x0C, 0x00, 0x36, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
156 0x18, 0x36, 0x00, 0xCC, 0x8C, 0x19, 0xC3, 0x06, 0xC0, 0x8C, 0x31, 0x3C, 0x30,
157 0x8C, 0x19, 0x83, 0x31, 0x60, 0x60, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x60, 0x18,
158 0x00, 0x80, 0xC1, 0x18, 0x00, 0x30, 0x06, 0x60, 0x18, 0x30, 0x80, 0x01, 0x00,
159 0x33, 0x63, 0xC6, 0x30, 0x00, 0x30, 0x63, 0x80, 0x19, 0x0C, 0x03, 0x06, 0x00,
160 0x0C, 0x18, 0x18, 0xC0, 0x81, 0x03, 0x00, 0x03, 0x18, 0x0C, 0x00, 0x60, 0x30,
161 0x06, 0x00, 0x87, 0x01, 0x18, 0x06, 0x0C, 0x60, 0x00, 0xC0, 0xCC, 0x98, 0x31,
162 0x0C, 0x00, 0xCC, 0x18, 0x30, 0x0C, 0xC3, 0x80, 0x01, 0x00, 0x03, 0x66, 0xFE,
163 0x18, 0x30, 0x00, 0xC0, 0x02, 0x06, 0x06, 0x00, 0x18, 0x8C, 0x01, 0x60, 0xE0,
164 0x0F, 0x86, 0x3F, 0x03, 0x18, 0x00, 0x30, 0x33, 0x66, 0x0C, 0x03, 0x00, 0x33,
165 0xFE, 0x0C, 0xC3, 0x30, 0xE0, 0x0F, 0xC0, 0x87, 0x9B, 0x31, 0x63, 0xC6, 0x00,
166 0xF0, 0x80, 0x01, 0x03, 0x00, 0x06, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
167 0x18, 0x06, 0x00, 0x6C, 0x8C, 0x19, 0xC3, 0x00, 0x80, 0x8D, 0x31, 0xC3, 0x30,
168 0x8C, 0x19, 0x03, 0x30, 0xB3, 0xC3, 0x87, 0x0F, 0x1F, 0x00, 0x2C, 0x60, 0x80,
169 0x01, 0xE0, 0x87, 0x0F, 0x00, 0x3E, 0x7C, 0x60, 0xF0, 0xE1, 0xE3, 0x07, 0x00,
170 0x0F, 0x3E, 0x7C, 0xFC, 0x00, 0xC0, 0xC3, 0xC7, 0x30, 0x0E, 0x3E, 0x7C, 0x00,
171 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1E, 0xC0, 0x00, 0x60, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
173 0x0C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x87, 0x31, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30,
176 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0xF8, 0x83, 0xC1, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
178 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
182};
183#endif /* IN_RING3 */
184
185#ifndef VBOX_DEVICE_STRUCT_TESTCASE /* Till the end of the file - doesn't count indent wise. */
186
187#ifdef _MSC_VER
188# pragma warning(push)
189# pragma warning(disable:4310 4245) /* Buggy warnings: cast truncates constant value; conversion from 'int' to 'const uint8_t', signed/unsigned mismatch */
190#endif
191
192/* force some bits to zero */
193static const uint8_t sr_mask[8] = {
194 (uint8_t)~0xfc,
195 (uint8_t)~0xc2,
196 (uint8_t)~0xf0,
197 (uint8_t)~0xc0,
198 (uint8_t)~0xf1,
199 (uint8_t)~0xff,
200 (uint8_t)~0xff,
201 (uint8_t)~0x01,
202};
203
204static const uint8_t gr_mask[16] = {
205 (uint8_t)~0xf0, /* 0x00 */
206 (uint8_t)~0xf0, /* 0x01 */
207 (uint8_t)~0xf0, /* 0x02 */
208 (uint8_t)~0xe0, /* 0x03 */
209 (uint8_t)~0xfc, /* 0x04 */
210 (uint8_t)~0x84, /* 0x05 */
211 (uint8_t)~0xf0, /* 0x06 */
212 (uint8_t)~0xf0, /* 0x07 */
213 (uint8_t)~0x00, /* 0x08 */
214 (uint8_t)~0xff, /* 0x09 */
215 (uint8_t)~0xff, /* 0x0a */
216 (uint8_t)~0xff, /* 0x0b */
217 (uint8_t)~0xff, /* 0x0c */
218 (uint8_t)~0xff, /* 0x0d */
219 (uint8_t)~0xff, /* 0x0e */
220 (uint8_t)~0xff, /* 0x0f */
221};
222
223#ifdef _MSC_VER
224# pragma warning(pop)
225#endif
226
227#define cbswap_32(__x) \
228 ((uint32_t)((((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
229 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
230 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
231 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
232
233#ifdef WORDS_BIGENDIAN
234# define PAT(x) cbswap_32(x)
235#else
236# define PAT(x) (x)
237#endif
238
239#ifdef WORDS_BIGENDIAN
240# define BIG 1
241#else
242# define BIG 0
243#endif
244
245#ifdef WORDS_BIGENDIAN
246#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
247#else
248#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
249#endif
250
251static const uint32_t mask16[16] = {
252 PAT(0x00000000),
253 PAT(0x000000ff),
254 PAT(0x0000ff00),
255 PAT(0x0000ffff),
256 PAT(0x00ff0000),
257 PAT(0x00ff00ff),
258 PAT(0x00ffff00),
259 PAT(0x00ffffff),
260 PAT(0xff000000),
261 PAT(0xff0000ff),
262 PAT(0xff00ff00),
263 PAT(0xff00ffff),
264 PAT(0xffff0000),
265 PAT(0xffff00ff),
266 PAT(0xffffff00),
267 PAT(0xffffffff),
268};
269
270#undef PAT
271
272#ifdef WORDS_BIGENDIAN
273# define PAT(x) (x)
274#else
275# define PAT(x) cbswap_32(x)
276#endif
277
278#ifdef IN_RING3
279
280static const uint32_t dmask16[16] = {
281 PAT(0x00000000),
282 PAT(0x000000ff),
283 PAT(0x0000ff00),
284 PAT(0x0000ffff),
285 PAT(0x00ff0000),
286 PAT(0x00ff00ff),
287 PAT(0x00ffff00),
288 PAT(0x00ffffff),
289 PAT(0xff000000),
290 PAT(0xff0000ff),
291 PAT(0xff00ff00),
292 PAT(0xff00ffff),
293 PAT(0xffff0000),
294 PAT(0xffff00ff),
295 PAT(0xffffff00),
296 PAT(0xffffffff),
297};
298
299static const uint32_t dmask4[4] = {
300 PAT(0x00000000),
301 PAT(0x0000ffff),
302 PAT(0xffff0000),
303 PAT(0xffffffff),
304};
305
306static uint32_t expand4[256];
307static uint16_t expand2[256];
308static uint8_t expand4to8[16];
309
310#endif /* IN_RING3 */
311
312
313/**
314 * Mark a page in VGA A0000-BFFFF range as remapped.
315 *
316 * @param pThis VGA instance data.
317 * @param offVGAMem The offset within VGA memory.
318 */
319DECLINLINE(void) vgaMarkRemapped(PVGASTATE pThis, RTGCPHYS offVGAMem)
320{
321 AssertMsg(offVGAMem < _128K, ("offVGAMem = %RGp\n", offVGAMem));
322 pThis->bmPageRemappedVGA |= RT_BIT_32((uint32_t)offVGAMem >> GUEST_PAGE_SHIFT);
323}
324
325
326/**
327 * Checks if a page in VGA A0000-BFFFF range is remapped.
328 *
329 * @returns true if remapped.
330 * @returns false if not remapped (accesses will trap).
331 * @param pThis VGA instance data.
332 * @param offVGAMem The offset within VGA memory.
333 */
334DECLINLINE(bool) vgaIsRemapped(PVGASTATE pThis, RTGCPHYS offVGAMem)
335{
336 AssertMsg(offVGAMem < _128K, ("offVGAMem = %RGp\n", offVGAMem));
337 return pThis->bmPageRemappedVGA & RT_BIT_32((uint32_t)offVGAMem >> GUEST_PAGE_SHIFT);
338}
339
340
341/**
342 * Reset page remap tracking bits.
343 *
344 * @param pThis VGA instance data.
345 */
346DECLINLINE(void) vgaResetRemapped(PVGASTATE pThis)
347{
348 pThis->bmPageRemappedVGA = 0;
349}
350
351
352/**
353 * Set a VRAM page dirty.
354 *
355 * @param pThis VGA instance data.
356 * @param offVRAM The VRAM offset of the page to set.
357 */
358DECLINLINE(void) vgaR3MarkDirty(PVGASTATE pThis, RTGCPHYS offVRAM)
359{
360 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
361 ASMBitSet(&pThis->bmDirtyBitmap[0], offVRAM >> GUEST_PAGE_SHIFT);
362 pThis->fHasDirtyBits = true;
363}
364
365#ifdef IN_RING3
366
367/**
368 * Tests if a VRAM page is dirty.
369 *
370 * @returns true if dirty.
371 * @returns false if clean.
372 * @param pThis VGA instance data.
373 * @param offVRAM The VRAM offset of the page to check.
374 */
375DECLINLINE(bool) vgaR3IsDirty(PVGASTATE pThis, RTGCPHYS offVRAM)
376{
377 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
378 return ASMBitTest(&pThis->bmDirtyBitmap[0], offVRAM >> GUEST_PAGE_SHIFT);
379}
380
381
382/**
383 * Reset dirty flags in a give range.
384 *
385 * @param pThis VGA instance data.
386 * @param offVRAMStart Offset into the VRAM buffer of the first page.
387 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
388 */
389DECLINLINE(void) vgaR3ResetDirty(PVGASTATE pThis, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
390{
391 Assert(offVRAMStart < pThis->vram_size);
392 Assert(offVRAMEnd <= pThis->vram_size);
393 Assert(offVRAMStart < offVRAMEnd);
394 ASMBitClearRange(&pThis->bmDirtyBitmap[0], offVRAMStart >> GUEST_PAGE_SHIFT, offVRAMEnd >> GUEST_PAGE_SHIFT);
395}
396
397
398/**
399 * Queries the VRAM dirty bits and resets the monitoring.
400 */
401static void vgaR3UpdateDirtyBitsAndResetMonitoring(PPDMDEVINS pDevIns, PVGASTATE pThis)
402{
403 size_t const cbBitmap = RT_ALIGN_Z(RT_MIN(pThis->vram_size, VGA_VRAM_MAX), GUEST_PAGE_SIZE * 64) / GUEST_PAGE_SIZE / 8;
404
405 /*
406 * If we don't have any dirty bits from MMIO accesses, we can just query
407 * straight into the dirty buffer.
408 */
409 if (!pThis->fHasDirtyBits)
410 {
411 int rc = PDMDevHlpMmio2QueryAndResetDirtyBitmap(pDevIns, pThis->hMmio2VRam, pThis->bmDirtyBitmap, cbBitmap);
412 AssertRC(rc);
413 }
414 /*
415 * Otherwise we'll have to query and merge the two.
416 */
417 else
418 {
419 uint64_t bmDirtyPages[VGA_VRAM_MAX / GUEST_PAGE_SIZE / 64]; /* (256 MB VRAM -> 8KB bitmap) */
420 int rc = PDMDevHlpMmio2QueryAndResetDirtyBitmap(pDevIns, pThis->hMmio2VRam, bmDirtyPages, cbBitmap);
421 if (RT_SUCCESS(rc))
422 {
423 /** @todo could use ORPS or VORPS here, I think. */
424 uint64_t *pbmDst = pThis->bmDirtyBitmap;
425 size_t const cTodo = cbBitmap / sizeof(uint64_t);
426
427 /* Do 64 bytes at a time first. */
428 size_t const cTodoFirst = cTodo & ~(size_t)7;
429 size_t idx;
430 for (idx = 0; idx < cTodoFirst; idx += 8)
431 {
432 pbmDst[idx + 0] |= bmDirtyPages[idx + 0];
433 pbmDst[idx + 1] |= bmDirtyPages[idx + 1];
434 pbmDst[idx + 2] |= bmDirtyPages[idx + 2];
435 pbmDst[idx + 3] |= bmDirtyPages[idx + 3];
436 pbmDst[idx + 4] |= bmDirtyPages[idx + 4];
437 pbmDst[idx + 5] |= bmDirtyPages[idx + 5];
438 pbmDst[idx + 6] |= bmDirtyPages[idx + 6];
439 pbmDst[idx + 7] |= bmDirtyPages[idx + 7];
440 }
441
442 /* Then do a mopup of anything remaining. */
443 switch (cTodo - idx)
444 {
445 case 7: pbmDst[idx + 6] |= bmDirtyPages[idx + 6]; RT_FALL_THRU();
446 case 6: pbmDst[idx + 5] |= bmDirtyPages[idx + 5]; RT_FALL_THRU();
447 case 5: pbmDst[idx + 4] |= bmDirtyPages[idx + 4]; RT_FALL_THRU();
448 case 4: pbmDst[idx + 3] |= bmDirtyPages[idx + 3]; RT_FALL_THRU();
449 case 3: pbmDst[idx + 2] |= bmDirtyPages[idx + 2]; RT_FALL_THRU();
450 case 2: pbmDst[idx + 1] |= bmDirtyPages[idx + 1]; RT_FALL_THRU();
451 case 1: pbmDst[idx] |= bmDirtyPages[idx]; break;
452 case 0: break;
453 default: AssertFailedBreak();
454 }
455
456 pThis->fHasDirtyBits = false;
457 }
458 }
459}
460
461#endif /* IN_RING3 */
462
463/* Update the values needed for calculating Vertical Retrace and
464 * Display Enable status bits more or less accurately. The Display Enable
465 * bit is set (indicating *disabled* display signal) when either the
466 * horizontal (hblank) or vertical (vblank) blanking is active. The
467 * Vertical Retrace bit is set when vertical retrace (vsync) is active.
468 * Unless the CRTC is horribly misprogrammed, vsync implies vblank.
469 */
470static void vga_update_retrace_state(PVGASTATE pThis)
471{
472 unsigned htotal_cclks, vtotal_lines, chars_per_sec;
473 unsigned hblank_start_cclk, hblank_end_cclk, hblank_width, hblank_skew_cclks;
474 unsigned vsync_start_line, vsync_end, vsync_width;
475 unsigned vblank_start_line, vblank_end, vblank_width;
476 unsigned char_dots, clock_doubled, clock_index;
477 const int clocks[] = {25175000, 28322000, 25175000, 25175000};
478 vga_retrace_s *r = &pThis->retrace_state;
479
480 /* For horizontal timings, we only care about the blanking start/end. */
481 htotal_cclks = pThis->cr[0x00] + 5;
482 hblank_start_cclk = pThis->cr[0x02];
483 hblank_end_cclk = (pThis->cr[0x03] & 0x1f) + ((pThis->cr[0x05] & 0x80) >> 2);
484 hblank_skew_cclks = (pThis->cr[0x03] >> 5) & 3;
485
486 /* For vertical timings, we need both the blanking start/end... */
487 vtotal_lines = pThis->cr[0x06] + ((pThis->cr[0x07] & 1) << 8) + ((pThis->cr[0x07] & 0x20) << 4) + 2;
488 vblank_start_line = pThis->cr[0x15] + ((pThis->cr[0x07] & 8) << 5) + ((pThis->cr[0x09] & 0x20) << 4);
489 vblank_end = pThis->cr[0x16];
490 /* ... and the vertical retrace (vsync) start/end. */
491 vsync_start_line = pThis->cr[0x10] + ((pThis->cr[0x07] & 4) << 6) + ((pThis->cr[0x07] & 0x80) << 2);
492 vsync_end = pThis->cr[0x11] & 0xf;
493
494 /* Calculate the blanking and sync widths. The way it's implemented in
495 * the VGA with limited-width compare counters is quite a piece of work.
496 */
497 hblank_width = (hblank_end_cclk - hblank_start_cclk) & 0x3f;/* 6 bits */
498 vblank_width = (vblank_end - vblank_start_line) & 0xff; /* 8 bits */
499 vsync_width = (vsync_end - vsync_start_line) & 0xf; /* 4 bits */
500
501 /* Calculate the dot and character clock rates. */
502 clock_doubled = (pThis->sr[0x01] >> 3) & 1; /* Clock doubling bit. */
503 clock_index = (pThis->msr >> 2) & 3;
504 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9; /* 8 or 9 dots per cclk. */
505
506 chars_per_sec = clocks[clock_index] / char_dots;
507 Assert(chars_per_sec); /* Can't possibly be zero. */
508
509 htotal_cclks <<= clock_doubled;
510
511 /* Calculate the number of cclks per entire frame. */
512 r->frame_cclks = vtotal_lines * htotal_cclks;
513 Assert(r->frame_cclks); /* Can't possibly be zero. */
514
515 if (r->v_freq_hz) { /* Could be set to emulate a specific rate. */
516 r->cclk_ns = 1000000000 / (r->frame_cclks * r->v_freq_hz);
517 } else {
518 r->cclk_ns = 1000000000 / chars_per_sec;
519 }
520 Assert(r->cclk_ns);
521 r->frame_ns = r->frame_cclks * r->cclk_ns;
522
523 /* Calculate timings in cclks/lines. Stored but not directly used. */
524 r->hb_start = hblank_start_cclk + hblank_skew_cclks;
525 r->hb_end = hblank_start_cclk + hblank_width + hblank_skew_cclks;
526 r->h_total = htotal_cclks;
527 Assert(r->h_total); /* Can't possibly be zero. */
528
529 r->vb_start = vblank_start_line;
530 r->vb_end = vblank_start_line + vblank_width + 1;
531 r->vs_start = vsync_start_line;
532 r->vs_end = vsync_start_line + vsync_width + 1;
533
534 /* Calculate timings in nanoseconds. For easier comparisons, the frame
535 * is considered to start at the beginning of the vertical and horizontal
536 * blanking period.
537 */
538 r->h_total_ns = htotal_cclks * r->cclk_ns;
539 r->hb_end_ns = hblank_width * r->cclk_ns;
540 r->vb_end_ns = vblank_width * r->h_total_ns;
541 r->vs_start_ns = (r->vs_start - r->vb_start) * r->h_total_ns;
542 r->vs_end_ns = (r->vs_end - r->vb_start) * r->h_total_ns;
543 Assert(r->h_total_ns); /* See h_total. */
544}
545
546static uint8_t vga_retrace(PPDMDEVINS pDevIns, PVGASTATE pThis)
547{
548 vga_retrace_s *r = &pThis->retrace_state;
549
550 if (r->frame_ns) {
551 uint8_t val = pThis->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE);
552 unsigned cur_frame_ns, cur_line_ns;
553 uint64_t time_ns;
554
555 time_ns = PDMDevHlpTMTimeVirtGetNano(pDevIns);
556
557 /* Determine the time within the frame. */
558 cur_frame_ns = time_ns % r->frame_ns;
559
560 /* See if we're in the vertical blanking period... */
561 if (cur_frame_ns < r->vb_end_ns) {
562 val |= ST01_DISP_ENABLE;
563 /* ... and additionally in the vertical sync period. */
564 if (cur_frame_ns >= r->vs_start_ns && cur_frame_ns <= r->vs_end_ns)
565 val |= ST01_V_RETRACE;
566 } else {
567 /* Determine the time within the current scanline. */
568 cur_line_ns = cur_frame_ns % r->h_total_ns;
569 /* See if we're in the horizontal blanking period. */
570 if (cur_line_ns < r->hb_end_ns)
571 val |= ST01_DISP_ENABLE;
572 }
573 return val;
574 } else {
575 return pThis->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE);
576 }
577}
578
579static int vga_ioport_invalid(PVGASTATE pThis, uint32_t addr)
580{
581 if (pThis->msr & MSR_COLOR_EMULATION) {
582 /* Color */
583 return (addr >= 0x3b0 && addr <= 0x3bf);
584 } else {
585 /* Monochrome */
586 return (addr >= 0x3d0 && addr <= 0x3df);
587 }
588}
589
590static uint32_t vga_ioport_read(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t addr)
591{
592 int val, index;
593
594 /* check port range access depending on color/monochrome mode */
595 if (vga_ioport_invalid(pThis, addr)) {
596 val = 0xff;
597 Log(("VGA: following read ignored\n"));
598 } else {
599 switch(addr) {
600 case 0x3c0:
601 if (pThis->ar_flip_flop == 0) {
602 val = pThis->ar_index;
603 } else {
604 val = 0;
605 }
606 break;
607 case 0x3c1:
608 index = pThis->ar_index & 0x1f;
609 if (index < 21)
610 val = pThis->ar[index];
611 else
612 val = 0;
613 break;
614 case 0x3c2:
615 val = pThis->st00;
616 break;
617 case 0x3c4:
618 val = pThis->sr_index;
619 break;
620 case 0x3c5:
621 val = pThis->sr[pThis->sr_index];
622 Log2(("vga: read SR%x = 0x%02x\n", pThis->sr_index, val));
623 break;
624 case 0x3c7:
625 val = pThis->dac_state;
626 break;
627 case 0x3c8:
628 val = pThis->dac_write_index;
629 break;
630 case 0x3c9:
631 Assert(pThis->dac_sub_index < 3);
632 val = pThis->palette[pThis->dac_read_index * 3 + pThis->dac_sub_index];
633 if (++pThis->dac_sub_index == 3) {
634 pThis->dac_sub_index = 0;
635 pThis->dac_read_index++;
636 }
637 break;
638 case 0x3ca:
639 val = pThis->fcr;
640 break;
641 case 0x3cc:
642 val = pThis->msr;
643 break;
644 case 0x3ce:
645 val = pThis->gr_index;
646 break;
647 case 0x3cf:
648 val = pThis->gr[pThis->gr_index];
649 Log2(("vga: read GR%x = 0x%02x\n", pThis->gr_index, val));
650 break;
651 case 0x3b4:
652 case 0x3d4:
653 val = pThis->cr_index;
654 break;
655 case 0x3b5:
656 case 0x3d5:
657 val = pThis->cr[pThis->cr_index];
658 Log2(("vga: read CR%x = 0x%02x\n", pThis->cr_index, val));
659 break;
660 case 0x3ba:
661 case 0x3da:
662 val = pThis->st01 = vga_retrace(pDevIns, pThis);
663 pThis->ar_flip_flop = 0;
664 break;
665 default:
666 val = 0x00;
667 break;
668 }
669 }
670 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
671 return val;
672}
673
674static void vga_ioport_write(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t addr, uint32_t val)
675{
676 int index;
677
678 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
679
680 /* check port range access depending on color/monochrome mode */
681 if (vga_ioport_invalid(pThis, addr)) {
682 Log(("VGA: previous write ignored\n"));
683 return;
684 }
685
686 switch(addr) {
687 case 0x3c0:
688 case 0x3c1:
689 if (pThis->ar_flip_flop == 0) {
690 val &= 0x3f;
691 pThis->ar_index = val;
692 } else {
693 index = pThis->ar_index & 0x1f;
694 switch(index) {
695 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
696 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
697 pThis->ar[index] = val & 0x3f;
698 break;
699 case 0x10:
700 pThis->ar[index] = val & ~0x10;
701 break;
702 case 0x11:
703 pThis->ar[index] = val;
704 break;
705 case 0x12:
706 pThis->ar[index] = val & ~0xc0;
707 break;
708 case 0x13:
709 pThis->ar[index] = val & ~0xf0;
710 break;
711 case 0x14:
712 pThis->ar[index] = val & ~0xf0;
713 break;
714 default:
715 break;
716 }
717 }
718 pThis->ar_flip_flop ^= 1;
719 break;
720 case 0x3c2:
721 pThis->msr = val & ~0x10;
722 if (pThis->fRealRetrace)
723 vga_update_retrace_state(pThis);
724 /* The two clock select bits also determine which of the four switches
725 * is reflected in bit 4 of Input Status Register 0.
726 * This is EGA compatible behavior. See the IBM EGA Tech Ref.
727 */
728 pThis->st00 = (pThis->st00 & ~0x10) | ((EGA_SWITCHES >> ((val >> 2) & 0x3) & 0x10));
729 break;
730 case 0x3c4:
731 pThis->sr_index = val & 7;
732 break;
733 case 0x3c5:
734 Log2(("vga: write SR%x = 0x%02x\n", pThis->sr_index, val));
735 pThis->sr[pThis->sr_index] = val & sr_mask[pThis->sr_index];
736 /* Allow SR07 to disable VBE. */
737 if (pThis->sr_index == 0x07 && !(val & 1))
738 {
739 pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] = VBE_DISPI_DISABLED;
740 pThis->bank_offset = 0;
741 }
742 if (pThis->fRealRetrace && pThis->sr_index == 0x01)
743 vga_update_retrace_state(pThis);
744#ifndef IN_RC
745 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
746 if ( pThis->sr_index == 4 /* mode */
747 || pThis->sr_index == 2 /* plane mask */)
748 {
749 if (pThis->bmPageRemappedVGA != 0)
750 {
751 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
752 STAM_COUNTER_INC(&pThis->StatMapReset);
753 vgaResetRemapped(pThis);
754 }
755 }
756#endif
757 break;
758 case 0x3c7:
759 pThis->dac_read_index = val;
760 pThis->dac_sub_index = 0;
761 pThis->dac_state = 3;
762 break;
763 case 0x3c8:
764 pThis->dac_write_index = val;
765 pThis->dac_sub_index = 0;
766 pThis->dac_state = 0;
767 break;
768 case 0x3c9:
769 Assert(pThis->dac_sub_index < 3);
770 pThis->dac_cache[pThis->dac_sub_index] = val;
771 if (++pThis->dac_sub_index == 3) {
772 memcpy(&pThis->palette[pThis->dac_write_index * 3], pThis->dac_cache, 3);
773 pThis->dac_sub_index = 0;
774 pThis->dac_write_index++;
775 }
776 break;
777 case 0x3ce:
778 pThis->gr_index = val & 0x0f;
779 break;
780 case 0x3cf:
781 Log2(("vga: write GR%x = 0x%02x\n", pThis->gr_index, val));
782 Assert(pThis->gr_index < RT_ELEMENTS(gr_mask));
783 pThis->gr[pThis->gr_index] = val & gr_mask[pThis->gr_index];
784
785#ifndef IN_RC
786 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
787 if (pThis->gr_index == 6 /* memory map mode */)
788 {
789 if (pThis->bmPageRemappedVGA != 0)
790 {
791 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
792 STAM_COUNTER_INC(&pThis->StatMapReset);
793 vgaResetRemapped(pThis);
794 }
795 }
796#endif
797 break;
798
799 case 0x3b4:
800 case 0x3d4:
801 pThis->cr_index = val;
802 break;
803 case 0x3b5:
804 case 0x3d5:
805 Log2(("vga: write CR%x = 0x%02x\n", pThis->cr_index, val));
806 /* handle CR0-7 protection */
807 if ((pThis->cr[0x11] & 0x80) && pThis->cr_index <= 7) {
808 /* can always write bit 4 of CR7 */
809 if (pThis->cr_index == 7)
810 pThis->cr[7] = (pThis->cr[7] & ~0x10) | (val & 0x10);
811 return;
812 }
813 pThis->cr[pThis->cr_index] = val;
814
815 if (pThis->fRealRetrace) {
816 /* The following registers are only updated during a mode set. */
817 switch(pThis->cr_index) {
818 case 0x00:
819 case 0x02:
820 case 0x03:
821 case 0x05:
822 case 0x06:
823 case 0x07:
824 case 0x09:
825 case 0x10:
826 case 0x11:
827 case 0x15:
828 case 0x16:
829 vga_update_retrace_state(pThis);
830 break;
831 }
832 }
833 break;
834 case 0x3ba:
835 case 0x3da:
836 pThis->fcr = val & 0x10;
837 break;
838 }
839}
840
841#ifdef CONFIG_BOCHS_VBE
842
843static uint32_t vbe_read_cfg(PVGASTATE pThis)
844{
845 const uint16_t u16Cfg = pThis->vbe_regs[VBE_DISPI_INDEX_CFG];
846 const uint16_t u16Id = u16Cfg & VBE_DISPI_CFG_MASK_ID;
847 const bool fQuerySupport = RT_BOOL(u16Cfg & VBE_DISPI_CFG_MASK_SUPPORT);
848
849 uint32_t val = 0;
850 switch (u16Id)
851 {
852 case VBE_DISPI_CFG_ID_VERSION: val = 1; break;
853 case VBE_DISPI_CFG_ID_VRAM_SIZE: val = pThis->vram_size; break;
854 case VBE_DISPI_CFG_ID_3D: val = pThis->f3DEnabled; break;
855# ifdef VBOX_WITH_VMSVGA
856 case VBE_DISPI_CFG_ID_VMSVGA: val = pThis->fVMSVGAEnabled; break;
857 case VBE_DISPI_CFG_ID_VMSVGA_DX: val = pThis->fVMSVGA10; break;
858# endif
859 default:
860 return 0; /* Not supported. */
861 }
862
863 return fQuerySupport ? 1 : val;
864}
865
866static uint32_t vbe_ioport_read_index(PVGASTATE pThis, uint32_t addr)
867{
868 uint32_t val = pThis->vbe_index;
869 NOREF(addr);
870 return val;
871}
872
873static uint32_t vbe_ioport_read_data(PVGASTATE pThis, uint32_t addr)
874{
875 uint32_t val;
876 NOREF(addr);
877
878 uint16_t const idxVbe = pThis->vbe_index;
879 if (idxVbe < VBE_DISPI_INDEX_NB)
880 {
881 RT_UNTRUSTED_VALIDATED_FENCE();
882 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS)
883 {
884 switch (idxVbe)
885 {
886 /* XXX: do not hardcode ? */
887 case VBE_DISPI_INDEX_XRES:
888 val = VBE_DISPI_MAX_XRES;
889 break;
890 case VBE_DISPI_INDEX_YRES:
891 val = VBE_DISPI_MAX_YRES;
892 break;
893 case VBE_DISPI_INDEX_BPP:
894 val = VBE_DISPI_MAX_BPP;
895 break;
896 default:
897 Assert(idxVbe < VBE_DISPI_INDEX_NB);
898 val = pThis->vbe_regs[idxVbe];
899 break;
900 }
901 }
902 else
903 {
904 switch (idxVbe)
905 {
906 case VBE_DISPI_INDEX_VBOX_VIDEO:
907 /* Reading from the port means that the old additions are requesting the number of monitors. */
908 val = 1;
909 break;
910 case VBE_DISPI_INDEX_CFG:
911 val = vbe_read_cfg(pThis);
912 break;
913 default:
914 Assert(idxVbe < VBE_DISPI_INDEX_NB);
915 val = pThis->vbe_regs[idxVbe];
916 break;
917 }
918 }
919 }
920 else
921 val = 0;
922 Log(("VBE: read index=0x%x val=0x%x\n", idxVbe, val));
923 return val;
924}
925
926# define VBE_PITCH_ALIGN 4 /* Align pitch to 32 bits - Qt requires that. */
927
928/* Calculate scanline pitch based on bit depth and width in pixels. */
929static uint32_t calc_line_pitch(uint16_t bpp, uint16_t width)
930{
931 uint32_t pitch, aligned_pitch;
932
933 if (bpp <= 4)
934 pitch = width >> 1;
935 else
936 pitch = width * ((bpp + 7) >> 3);
937
938 /* Align the pitch to some sensible value. */
939 aligned_pitch = (pitch + (VBE_PITCH_ALIGN - 1)) & ~(VBE_PITCH_ALIGN - 1);
940 if (aligned_pitch != pitch)
941 Log(("VBE: Line pitch %d aligned to %d bytes\n", pitch, aligned_pitch));
942
943 return aligned_pitch;
944}
945
946static void recalculate_data(PVGASTATE pThis)
947{
948 uint16_t cBPP = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
949 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
950 uint16_t cX = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
951 if (!cBPP || !cX)
952 return; /* Not enough data has been set yet. */
953 uint32_t cbLinePitch = calc_line_pitch(cBPP, cVirtWidth);
954 if (!cbLinePitch)
955 cbLinePitch = calc_line_pitch(cBPP, cX);
956 if (!cbLinePitch)
957 return;
958 uint32_t cVirtHeight = pThis->vram_size / cbLinePitch;
959 uint16_t offX = pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
960 uint16_t offY = pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
961 uint32_t offStart = cbLinePitch * offY;
962 if (cBPP == 4)
963 offStart += offX >> 1;
964 else
965 offStart += offX * ((cBPP + 7) >> 3);
966 offStart >>= 2;
967 pThis->vbe_line_offset = RT_MIN(cbLinePitch, pThis->vram_size);
968 pThis->vbe_start_addr = RT_MIN(offStart, pThis->vram_size);
969
970 /* The VBE_DISPI_INDEX_VIRT_HEIGHT is used to prevent setting resolution bigger than
971 * the VRAM size permits. It is used instead of VBE_DISPI_INDEX_YRES *only* in case
972 * pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] < pThis->vbe_regs[VBE_DISPI_INDEX_YRES].
973 * Note that VBE_DISPI_INDEX_VIRT_HEIGHT has to be clipped to UINT16_MAX, which happens
974 * with small resolutions and big VRAM. */
975 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = cVirtHeight >= UINT16_MAX ? UINT16_MAX : (uint16_t)cVirtHeight;
976}
977
978static void vbe_ioport_write_index(PVGASTATE pThis, uint32_t addr, uint32_t val)
979{
980 pThis->vbe_index = val;
981 NOREF(addr);
982}
983
984static VBOXSTRICTRC vbe_ioport_write_data(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t addr, uint32_t val)
985{
986 uint32_t max_bank;
987 RT_NOREF(pThisCC, addr);
988
989 if (pThis->vbe_index <= VBE_DISPI_INDEX_NB) {
990 bool fRecalculate = false;
991 Log(("VBE: write index=0x%x val=0x%x\n", pThis->vbe_index, val));
992 switch(pThis->vbe_index) {
993 case VBE_DISPI_INDEX_ID:
994 if (val == VBE_DISPI_ID0 ||
995 val == VBE_DISPI_ID1 ||
996 val == VBE_DISPI_ID2 ||
997 val == VBE_DISPI_ID3 ||
998 val == VBE_DISPI_ID4 ||
999 /* VBox extensions. */
1000 val == VBE_DISPI_ID_VBOX_VIDEO ||
1001 val == VBE_DISPI_ID_ANYX ||
1002# ifdef VBOX_WITH_HGSMI
1003 val == VBE_DISPI_ID_HGSMI ||
1004# endif
1005 val == VBE_DISPI_ID_CFG)
1006 {
1007 pThis->vbe_regs[pThis->vbe_index] = val;
1008 }
1009 break;
1010 case VBE_DISPI_INDEX_XRES:
1011 if (val <= VBE_DISPI_MAX_XRES)
1012 {
1013 pThis->vbe_regs[pThis->vbe_index] = val;
1014 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = val;
1015 fRecalculate = true;
1016 }
1017 break;
1018 case VBE_DISPI_INDEX_YRES:
1019 if (val <= VBE_DISPI_MAX_YRES)
1020 pThis->vbe_regs[pThis->vbe_index] = val;
1021 break;
1022 case VBE_DISPI_INDEX_BPP:
1023 if (val == 0)
1024 val = 8;
1025 if (val == 4 || val == 8 || val == 15 ||
1026 val == 16 || val == 24 || val == 32) {
1027 pThis->vbe_regs[pThis->vbe_index] = val;
1028 fRecalculate = true;
1029 }
1030 break;
1031 case VBE_DISPI_INDEX_BANK:
1032 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] <= 4)
1033 max_bank = pThis->vbe_bank_max >> 2; /* Each bank really covers 256K */
1034 else
1035 max_bank = pThis->vbe_bank_max;
1036 /* Old software may pass garbage in the high byte of bank. If the maximum
1037 * bank fits into a single byte, toss the high byte the user supplied.
1038 */
1039 if (max_bank < 0x100)
1040 val &= 0xff;
1041 if (val > max_bank)
1042 val = max_bank;
1043 pThis->vbe_regs[pThis->vbe_index] = val;
1044 pThis->bank_offset = (val << 16);
1045
1046# ifndef IN_RC
1047 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1048 if (pThis->bmPageRemappedVGA != 0)
1049 {
1050 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
1051 STAM_COUNTER_INC(&pThis->StatMapReset);
1052 vgaResetRemapped(pThis);
1053 }
1054# endif
1055 break;
1056
1057 case VBE_DISPI_INDEX_ENABLE:
1058# ifndef IN_RING3
1059 return VINF_IOM_R3_IOPORT_WRITE;
1060# else
1061 {
1062 if ((val & VBE_DISPI_ENABLED) &&
1063 !(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
1064 int h, shift_control;
1065 /* Check the values before we screw up with a resolution which is too big or small. */
1066 size_t cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1067 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
1068 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
1069 else
1070 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] * ((pThis->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
1071 cb *= pThis->vbe_regs[VBE_DISPI_INDEX_YRES];
1072 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
1073 if (!cVirtWidth)
1074 cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1075 if ( !cVirtWidth
1076 || !pThis->vbe_regs[VBE_DISPI_INDEX_YRES]
1077 || cb > pThis->vram_size)
1078 {
1079 AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
1080 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_YRES], cb, pThis->vram_size));
1081 return VINF_SUCCESS; /* Note: silent failure like before */
1082 }
1083
1084 /* When VBE interface is enabled, it is reset. */
1085 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
1086 pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
1087 fRecalculate = true;
1088
1089 /* clear the screen (should be done in BIOS) */
1090 if (!(val & VBE_DISPI_NOCLEARMEM)) {
1091 uint16_t cY = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
1092 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
1093 uint16_t cbLinePitch = pThis->vbe_line_offset;
1094 memset(pThisCC->pbVRam, 0,
1095 cY * cbLinePitch);
1096 }
1097
1098 /* we initialize the VGA graphic mode (should be done
1099 in BIOS) */
1100 pThis->gr[0x06] = (pThis->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
1101 pThis->cr[0x17] |= 3; /* no CGA modes */
1102 pThis->cr[0x13] = pThis->vbe_line_offset >> 3;
1103 /* width */
1104 pThis->cr[0x01] = (cVirtWidth >> 3) - 1;
1105 /* height (only meaningful if < 1024) */
1106 h = pThis->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
1107 pThis->cr[0x12] = h;
1108 pThis->cr[0x07] = (pThis->cr[0x07] & ~0x42) |
1109 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
1110 /* line compare to 1023 */
1111 pThis->cr[0x18] = 0xff;
1112 pThis->cr[0x07] |= 0x10;
1113 pThis->cr[0x09] |= 0x40;
1114
1115 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
1116 shift_control = 0;
1117 pThis->sr[0x01] &= ~8; /* no double line */
1118 } else {
1119 shift_control = 2;
1120 pThis->sr[4] |= 0x08; /* set chain 4 mode */
1121 pThis->sr[2] |= 0x0f; /* activate all planes */
1122 /* Indicate non-VGA mode in SR07. */
1123 pThis->sr[7] |= 1;
1124 }
1125 pThis->gr[0x05] = (pThis->gr[0x05] & ~0x60) | (shift_control << 5);
1126 pThis->cr[0x09] &= ~0x9f; /* no double scan */
1127 /* sunlover 30.05.2007
1128 * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
1129 * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vgaR3UpdateDisplay.
1130 * But the VBE mode is graphics, so not a blank anymore.
1131 */
1132 pThis->ar_index |= 0x20;
1133 } else {
1134 /* XXX: the bios should do that */
1135 /* sunlover 21.12.2006
1136 * Here is probably more to reset. When this was executed in GC
1137 * then the *update* functions could not detect a mode change.
1138 * Or may be these update function should take the pThis->vbe_regs[pThis->vbe_index]
1139 * into account when detecting a mode change.
1140 *
1141 * The 'mode reset not detected' problem is now fixed by executing the
1142 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
1143 * LFBChange callback.
1144 */
1145 pThis->bank_offset = 0;
1146 }
1147 pThis->vbe_regs[pThis->vbe_index] = val;
1148 /*
1149 * LFB video mode is either disabled or changed. Notify the display
1150 * and reset VBVA.
1151 */
1152 pThisCC->pDrv->pfnLFBModeChange(pThisCC->pDrv, (val & VBE_DISPI_ENABLED) != 0);
1153# ifdef VBOX_WITH_HGSMI
1154 VBVAOnVBEChanged(pThis, pThisCC);
1155# endif
1156
1157 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1158 if (pThis->bmPageRemappedVGA != 0)
1159 {
1160 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
1161 STAM_COUNTER_INC(&pThis->StatMapReset);
1162 vgaResetRemapped(pThis);
1163 }
1164 break;
1165 }
1166# endif /* IN_RING3 */
1167 case VBE_DISPI_INDEX_VIRT_WIDTH:
1168 case VBE_DISPI_INDEX_X_OFFSET:
1169 case VBE_DISPI_INDEX_Y_OFFSET:
1170 {
1171 pThis->vbe_regs[pThis->vbe_index] = val;
1172 fRecalculate = true;
1173 }
1174 break;
1175 case VBE_DISPI_INDEX_VBOX_VIDEO:
1176# ifndef IN_RING3
1177 return VINF_IOM_R3_IOPORT_WRITE;
1178# else
1179 /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
1180 if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
1181 pThisCC->pDrv->pfnProcessAdapterData(pThisCC->pDrv, NULL, 0);
1182 else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
1183 pThisCC->pDrv->pfnProcessAdapterData(pThisCC->pDrv, pThisCC->pbVRam, pThis->vram_size);
1184 else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
1185 pThisCC->pDrv->pfnProcessDisplayData(pThisCC->pDrv, pThisCC->pbVRam, val & 0xFFFF);
1186# endif /* IN_RING3 */
1187 break;
1188 case VBE_DISPI_INDEX_CFG:
1189 pThis->vbe_regs[pThis->vbe_index] = val;
1190 break;
1191 default:
1192 break;
1193 }
1194
1195 if (fRecalculate)
1196 recalculate_data(pThis);
1197 }
1198 return VINF_SUCCESS;
1199}
1200
1201#endif /* CONFIG_BOCHS_VBE */
1202
1203/* called for accesses between 0xa0000 and 0xc0000 */
1204static uint32_t vga_mem_readb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS addr, int *prc)
1205{
1206 int plane;
1207 uint32_t ret;
1208
1209 Log3(("vga: read [0x%x] -> ", addr));
1210
1211#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RZ
1212 /* VMSVGA keeps the VGA and SVGA framebuffers separate unlike this boch-based
1213 VGA implementation, so we fake it by going to ring-3 and using a heap buffer. */
1214 if (!pThis->svga.fEnabled)
1215 { /*likely*/ }
1216 else
1217 {
1218 *prc = VINF_IOM_R3_MMIO_READ;
1219 return 0;
1220 }
1221#endif
1222
1223
1224 /* convert to VGA memory offset */
1225 addr &= 0x1ffff;
1226#ifndef IN_RC
1227 RTGCPHYS const offMmio = addr; /* save original MMIO range offset */
1228#endif
1229
1230 int const memory_map_mode = (pThis->gr[6] >> 2) & 3;
1231 switch(memory_map_mode) {
1232 case 0:
1233 break;
1234 case 1:
1235 if (addr >= 0x10000)
1236 return 0xff;
1237 addr += pThis->bank_offset;
1238 break;
1239 case 2:
1240 addr -= 0x10000;
1241 if (addr >= 0x8000)
1242 return 0xff;
1243 break;
1244 default:
1245 case 3:
1246 addr -= 0x18000;
1247 if (addr >= 0x8000)
1248 return 0xff;
1249 break;
1250 }
1251
1252 if (pThis->sr[4] & 0x08) {
1253 /* chain 4 mode : simplest access */
1254#ifndef IN_RC
1255 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1256 if ( (pThis->sr[2] & 3) == 3
1257 && !vgaIsRemapped(pThis, offMmio)
1258 && pThis->GCPhysVRAM)
1259 {
1260 /** @todo only allow read access (doesn't work now) */
1261 STAM_COUNTER_INC(&pThis->StatMapPage);
1262 PDMDevHlpMmioMapMmio2Page(pDevIns, pThis->hMmioLegacy, offMmio, pThis->hMmio2VRam, addr, X86_PTE_RW | X86_PTE_P);
1263 /* Set as dirty as write accesses won't be noticed now. */
1264 vgaR3MarkDirty(pThis, addr);
1265 vgaMarkRemapped(pThis, offMmio);
1266 }
1267#endif /* !IN_RC */
1268 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr, *prc);
1269#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1270 ret = !pThis->svga.fEnabled ? pThisCC->pbVRam[addr]
1271 : addr < VMSVGA_VGA_FB_BACKUP_SIZE ? pThisCC->svga.pbVgaFrameBufferR3[addr] : 0xff;
1272#else
1273 ret = pThisCC->pbVRam[addr];
1274#endif
1275 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1276 /* odd/even mode (aka text mode mapping) */
1277 plane = (pThis->gr[4] & 2) | (addr & 1);
1278 /* See the comment for a similar line in vga_mem_writeb. */
1279 RTGCPHYS off = ((addr & ~1) * 4) | plane;
1280 VERIFY_VRAM_READ_OFF_RETURN(pThis, off, *prc);
1281#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1282 ret = !pThis->svga.fEnabled ? pThisCC->pbVRam[off]
1283 : off < VMSVGA_VGA_FB_BACKUP_SIZE ? pThisCC->svga.pbVgaFrameBufferR3[off] : 0xff;
1284#else
1285 ret = pThisCC->pbVRam[off];
1286#endif
1287 } else {
1288 /* standard VGA latched access */
1289 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr * 4 + 3, *prc);
1290#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1291 pThis->latch = !pThis->svga.fEnabled ? ((uint32_t *)pThisCC->pbVRam)[addr]
1292 : addr * 4 + 3 < VMSVGA_VGA_FB_BACKUP_SIZE ? ((uint32_t *)pThisCC->svga.pbVgaFrameBufferR3)[addr] : UINT32_MAX;
1293#else
1294 pThis->latch = ((uint32_t *)pThisCC->pbVRam)[addr];
1295#endif
1296 if (!(pThis->gr[5] & 0x08)) {
1297 /* read mode 0 */
1298 plane = pThis->gr[4];
1299 ret = GET_PLANE(pThis->latch, plane);
1300 } else {
1301 /* read mode 1 */
1302 ret = (pThis->latch ^ mask16[pThis->gr[2]]) & mask16[pThis->gr[7]];
1303 ret |= ret >> 16;
1304 ret |= ret >> 8;
1305 ret = (~ret) & 0xff;
1306 }
1307 }
1308 Log3((" 0x%02x\n", ret));
1309 return ret;
1310}
1311
1312/**
1313 * called for accesses between 0xa0000 and 0xc0000
1314 */
1315static VBOXSTRICTRC vga_mem_writeb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS addr, uint32_t val)
1316{
1317 int plane, write_mode, b, func_select, mask;
1318 uint32_t write_mask, bit_mask, set_mask;
1319
1320 Log3(("vga: [0x%x] = 0x%02x\n", addr, val));
1321
1322#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RZ
1323 /* VMSVGA keeps the VGA and SVGA framebuffers separate unlike this boch-based
1324 VGA implementation, so we fake it by going to ring-3 and using a heap buffer. */
1325 if (!pThis->svga.fEnabled) { /*likely*/ }
1326 else return VINF_IOM_R3_MMIO_WRITE;
1327#endif
1328
1329 /* convert to VGA memory offset */
1330 addr &= 0x1ffff;
1331#ifndef IN_RC
1332 RTGCPHYS const offMmio = addr; /* save original MMIO range offset */
1333#endif
1334
1335 int const memory_map_mode = (pThis->gr[6] >> 2) & 3;
1336 switch(memory_map_mode) {
1337 case 0:
1338 break;
1339 case 1:
1340 if (addr >= 0x10000)
1341 return VINF_SUCCESS;
1342 addr += pThis->bank_offset;
1343 break;
1344 case 2:
1345 addr -= 0x10000;
1346 if (addr >= 0x8000)
1347 return VINF_SUCCESS;
1348 break;
1349 default:
1350 case 3:
1351 addr -= 0x18000;
1352 if (addr >= 0x8000)
1353 return VINF_SUCCESS;
1354 break;
1355 }
1356
1357 if (pThis->sr[4] & 0x08) {
1358 /* chain 4 mode : simplest access */
1359 plane = addr & 3;
1360 mask = (1 << plane);
1361 if (pThis->sr[2] & mask) {
1362#ifndef IN_RC
1363 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1364 if ( (pThis->sr[2] & 3) == 3
1365 && !vgaIsRemapped(pThis, offMmio)
1366 && pThis->GCPhysVRAM)
1367 {
1368 STAM_COUNTER_INC(&pThis->StatMapPage);
1369 PDMDevHlpMmioMapMmio2Page(pDevIns, pThis->hMmioLegacy, offMmio, pThis->hMmio2VRam, addr, X86_PTE_RW | X86_PTE_P);
1370 vgaMarkRemapped(pThis, offMmio);
1371 }
1372#endif /* !IN_RC */
1373
1374 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1375#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1376 if (!pThis->svga.fEnabled)
1377 pThisCC->pbVRam[addr] = val;
1378 else if (addr < VMSVGA_VGA_FB_BACKUP_SIZE)
1379 pThisCC->svga.pbVgaFrameBufferR3[addr] = val;
1380 else
1381 {
1382 Log(("vga: chain4: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1383 return VINF_SUCCESS;
1384 }
1385#else
1386 pThisCC->pbVRam[addr] = val;
1387#endif
1388 Log3(("vga: chain4: [0x%x]\n", addr));
1389 pThis->plane_updated |= mask; /* only used to detect font change */
1390 vgaR3MarkDirty(pThis, addr);
1391 }
1392 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1393 /* odd/even mode (aka text mode mapping); GR4 does not affect writes! */
1394 plane = addr & 1;
1395 mask = (1 << plane);
1396 if (pThis->sr[2] & mask) {
1397 /* 'addr' is offset in a plane, bit 0 selects the plane.
1398 * Mask the bit 0, convert plane index to vram offset,
1399 * that is multiply by the number of planes,
1400 * and select the plane byte in the vram offset.
1401 */
1402 addr = ((addr & ~1) * 4) | plane;
1403 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1404#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1405 if (!pThis->svga.fEnabled)
1406 pThisCC->pbVRam[addr] = val;
1407 else if (addr < VMSVGA_VGA_FB_BACKUP_SIZE)
1408 pThisCC->svga.pbVgaFrameBufferR3[addr] = val;
1409 else
1410 {
1411 Log(("vga: odd/even: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1412 return VINF_SUCCESS;
1413 }
1414#else
1415 pThisCC->pbVRam[addr] = val;
1416#endif
1417 Log3(("vga: odd/even: [0x%x]\n", addr));
1418 pThis->plane_updated |= mask; /* only used to detect font change */
1419 vgaR3MarkDirty(pThis, addr);
1420 }
1421 } else {
1422 /* standard VGA latched access */
1423 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr * 4 + 3);
1424
1425 write_mode = pThis->gr[5] & 3;
1426 switch(write_mode) {
1427 default:
1428 case 0:
1429 /* rotate */
1430 b = pThis->gr[3] & 7;
1431 val = ((val >> b) | (val << (8 - b))) & 0xff;
1432 val |= val << 8;
1433 val |= val << 16;
1434
1435 /* apply set/reset mask */
1436 set_mask = mask16[pThis->gr[1]];
1437 val = (val & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
1438 bit_mask = pThis->gr[8];
1439 break;
1440 case 1:
1441 val = pThis->latch;
1442 goto do_write;
1443 case 2:
1444 val = mask16[val & 0x0f];
1445 bit_mask = pThis->gr[8];
1446 break;
1447 case 3:
1448 /* rotate */
1449 b = pThis->gr[3] & 7;
1450 val = (val >> b) | (val << (8 - b));
1451
1452 bit_mask = pThis->gr[8] & val;
1453 val = mask16[pThis->gr[0]];
1454 break;
1455 }
1456
1457 /* apply logical operation */
1458 func_select = pThis->gr[3] >> 3;
1459 switch(func_select) {
1460 case 0:
1461 default:
1462 /* nothing to do */
1463 break;
1464 case 1:
1465 /* and */
1466 val &= pThis->latch;
1467 break;
1468 case 2:
1469 /* or */
1470 val |= pThis->latch;
1471 break;
1472 case 3:
1473 /* xor */
1474 val ^= pThis->latch;
1475 break;
1476 }
1477
1478 /* apply bit mask */
1479 bit_mask |= bit_mask << 8;
1480 bit_mask |= bit_mask << 16;
1481 val = (val & bit_mask) | (pThis->latch & ~bit_mask);
1482
1483 do_write:
1484 /* mask data according to sr[2] */
1485 mask = pThis->sr[2];
1486 pThis->plane_updated |= mask; /* only used to detect font change */
1487 write_mask = mask16[mask];
1488#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1489 uint32_t *pu32Dst;
1490 if (!pThis->svga.fEnabled)
1491 pu32Dst = &((uint32_t *)pThisCC->pbVRam)[addr];
1492 else if (addr * 4 + 3 < VMSVGA_VGA_FB_BACKUP_SIZE)
1493 pu32Dst = &((uint32_t *)pThisCC->svga.pbVgaFrameBufferR3)[addr];
1494 else
1495 {
1496 Log(("vga: latch: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1497 return VINF_SUCCESS;
1498 }
1499 *pu32Dst = (*pu32Dst & ~write_mask) | (val & write_mask);
1500#else
1501 ((uint32_t *)pThisCC->pbVRam)[addr] = (((uint32_t *)pThisCC->pbVRam)[addr] & ~write_mask)
1502 | (val & write_mask);
1503#endif
1504 Log3(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", addr * 4, write_mask, val));
1505 vgaR3MarkDirty(pThis, (addr * 4));
1506 }
1507
1508 return VINF_SUCCESS;
1509}
1510
1511#ifdef IN_RING3
1512
1513typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1514 const uint8_t *font_ptr, int h,
1515 uint32_t fgcol, uint32_t bgcol,
1516 int dscan);
1517typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1518 const uint8_t *font_ptr, int h,
1519 uint32_t fgcol, uint32_t bgcol, int dup9);
1520typedef void vga_draw_line_func(PVGASTATE pThis, PVGASTATECC pThisCC, uint8_t *pbDst, const uint8_t *pbSrc, int width);
1521
1522static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1523{
1524 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1525}
1526
1527static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1528{
1529 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1530}
1531
1532static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1533{
1534 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1535}
1536
1537static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1538{
1539 return (r << 16) | (g << 8) | b;
1540}
1541
1542#define DEPTH 8
1543#include "DevVGATmpl.h"
1544
1545#define DEPTH 15
1546#include "DevVGATmpl.h"
1547
1548#define DEPTH 16
1549#include "DevVGATmpl.h"
1550
1551#define DEPTH 32
1552#include "DevVGATmpl.h"
1553
1554static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1555{
1556 unsigned int col;
1557 col = rgb_to_pixel8(r, g, b);
1558 col |= col << 8;
1559 col |= col << 16;
1560 return col;
1561}
1562
1563static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1564{
1565 unsigned int col;
1566 col = rgb_to_pixel15(r, g, b);
1567 col |= col << 16;
1568 return col;
1569}
1570
1571static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1572{
1573 unsigned int col;
1574 col = rgb_to_pixel16(r, g, b);
1575 col |= col << 16;
1576 return col;
1577}
1578
1579static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1580{
1581 return rgb_to_pixel32(r, g, b);
1582}
1583
1584/** return true if the palette was modified */
1585static bool vgaR3UpdatePalette16(PVGASTATE pThis, PVGASTATER3 pThisCC)
1586{
1587 bool full_update = false;
1588 int i;
1589 uint32_t v, col, *palette;
1590
1591 palette = pThis->last_palette;
1592 for(i = 0; i < 16; i++) {
1593 v = pThis->ar[i];
1594 if (pThis->ar[0x10] & 0x80)
1595 v = ((pThis->ar[0x14] & 0xf) << 4) | (v & 0xf);
1596 else
1597 v = ((pThis->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1598 v = v * 3;
1599 col = pThisCC->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1600 c6_to_8(pThis->palette[v + 1]),
1601 c6_to_8(pThis->palette[v + 2]));
1602 if (col != palette[i]) {
1603 full_update = true;
1604 palette[i] = col;
1605 }
1606 }
1607 return full_update;
1608}
1609
1610/** return true if the palette was modified */
1611static bool vgaR3UpdatePalette256(PVGASTATE pThis, PVGASTATER3 pThisCC)
1612{
1613 bool full_update = false;
1614 int i;
1615 uint32_t v, col, *palette;
1616 int wide_dac;
1617
1618 palette = pThis->last_palette;
1619 v = 0;
1620 wide_dac = (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
1621 == (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
1622 for(i = 0; i < 256; i++) {
1623 if (wide_dac)
1624 col = pThisCC->rgb_to_pixel(pThis->palette[v],
1625 pThis->palette[v + 1],
1626 pThis->palette[v + 2]);
1627 else
1628 col = pThisCC->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1629 c6_to_8(pThis->palette[v + 1]),
1630 c6_to_8(pThis->palette[v + 2]));
1631 if (col != palette[i]) {
1632 full_update = true;
1633 palette[i] = col;
1634 }
1635 v += 3;
1636 }
1637 return full_update;
1638}
1639
1640static void vgaR3GetOffsets(PVGASTATE pThis,
1641 uint32_t *pline_offset,
1642 uint32_t *pstart_addr,
1643 uint32_t *pline_compare)
1644{
1645 uint32_t start_addr, line_offset, line_compare;
1646#ifdef CONFIG_BOCHS_VBE
1647 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1648 line_offset = pThis->vbe_line_offset;
1649 start_addr = pThis->vbe_start_addr;
1650 line_compare = 65535;
1651 } else
1652#endif
1653 {
1654 /* compute line_offset in bytes */
1655 line_offset = pThis->cr[0x13];
1656 line_offset <<= 3;
1657 if (!(pThis->cr[0x14] & 0x40) && !(pThis->cr[0x17] & 0x40))
1658 {
1659 /* Word mode. Used for odd/even modes. */
1660 line_offset *= 2;
1661 }
1662
1663 /* starting address */
1664 start_addr = pThis->cr[0x0d] | (pThis->cr[0x0c] << 8);
1665
1666 /* line compare */
1667 line_compare = pThis->cr[0x18] |
1668 ((pThis->cr[0x07] & 0x10) << 4) |
1669 ((pThis->cr[0x09] & 0x40) << 3);
1670 }
1671 *pline_offset = line_offset;
1672 *pstart_addr = start_addr;
1673 *pline_compare = line_compare;
1674}
1675
1676/** update start_addr and line_offset. Return TRUE if modified */
1677static bool vgaR3UpdateBasicParams(PVGASTATE pThis, PVGASTATER3 pThisCC)
1678{
1679 bool full_update = false;
1680 uint32_t start_addr, line_offset, line_compare;
1681
1682 pThisCC->get_offsets(pThis, &line_offset, &start_addr, &line_compare);
1683
1684 if (line_offset != pThis->line_offset ||
1685 start_addr != pThis->start_addr ||
1686 line_compare != pThis->line_compare) {
1687 pThis->line_offset = line_offset;
1688 pThis->start_addr = start_addr;
1689 pThis->line_compare = line_compare;
1690 full_update = true;
1691 }
1692 return full_update;
1693}
1694
1695static inline int vgaR3GetDepthIndex(int depth)
1696{
1697 switch(depth) {
1698 default:
1699 case 8:
1700 return 0;
1701 case 15:
1702 return 1;
1703 case 16:
1704 return 2;
1705 case 32:
1706 return 3;
1707 }
1708}
1709
1710static vga_draw_glyph8_func * const vga_draw_glyph8_table[4] = {
1711 vga_draw_glyph8_8,
1712 vga_draw_glyph8_16,
1713 vga_draw_glyph8_16,
1714 vga_draw_glyph8_32,
1715};
1716
1717static vga_draw_glyph8_func * const vga_draw_glyph16_table[4] = {
1718 vga_draw_glyph16_8,
1719 vga_draw_glyph16_16,
1720 vga_draw_glyph16_16,
1721 vga_draw_glyph16_32,
1722};
1723
1724static vga_draw_glyph9_func * const vga_draw_glyph9_table[4] = {
1725 vga_draw_glyph9_8,
1726 vga_draw_glyph9_16,
1727 vga_draw_glyph9_16,
1728 vga_draw_glyph9_32,
1729};
1730
1731static const uint8_t cursor_glyph[32 * 4] = {
1732 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1733 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1734 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1735 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1736 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1737 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1738 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1739 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1740 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1741 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1742 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1743 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1744 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1745 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1746 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1747 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1748};
1749
1750static const uint8_t empty_glyph[32 * 4] = { 0 };
1751
1752/**
1753 * Text mode update
1754 */
1755static int vgaR3DrawText(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update,
1756 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
1757{
1758 int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
1759 int cx_min, cx_max, linesize, x_incr;
1760 int cx_min_upd, cx_max_upd, cy_start;
1761 uint32_t offset, fgcol, bgcol, v, cursor_offset;
1762 uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
1763 const uint8_t *font_ptr, *font_base[2];
1764 int dup9, line_offset, depth_index, dscan;
1765 uint32_t *palette;
1766 uint32_t *ch_attr_ptr;
1767 vga_draw_glyph8_func *vga_draw_glyph8;
1768 vga_draw_glyph9_func *vga_draw_glyph9;
1769 uint64_t time_ns;
1770 bool blink_on, chr_blink_flip, cur_blink_flip;
1771 bool blink_enabled, blink_do_redraw;
1772 int uline_pos;
1773 int s_incr;
1774
1775 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
1776 palette = pThis->last_palette;
1777
1778 /* compute font data address (in plane 2) */
1779 v = pThis->sr[3];
1780 offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
1781 if (offset != pThis->font_offsets[0]) {
1782 pThis->font_offsets[0] = offset;
1783 full_update = true;
1784 }
1785 font_base[0] = pThisCC->pbVRam + offset;
1786
1787 offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
1788 font_base[1] = pThisCC->pbVRam + offset;
1789 if (offset != pThis->font_offsets[1]) {
1790 pThis->font_offsets[1] = offset;
1791 full_update = true;
1792 }
1793 if (pThis->plane_updated & (1 << 2)) {
1794 /* if the plane 2 was modified since the last display, it
1795 indicates the font may have been modified */
1796 pThis->plane_updated = 0;
1797 full_update = true;
1798 }
1799
1800 /* Underline position */
1801 uline_pos = pThis->cr[0x14] & 0x1f;
1802 if (uline_pos != pThis->last_uline) {
1803 pThis->last_uline = uline_pos;
1804 full_update = true;
1805 }
1806
1807 blink_enabled = !!(pThis->ar[0x10] & 0x08); /* Attribute controller blink enable. */
1808 if (blink_enabled != pThis->last_blink) {
1809 pThis->last_blink = blink_enabled;
1810 full_update = true;
1811 }
1812
1813 full_update |= vgaR3UpdateBasicParams(pThis, pThisCC);
1814
1815 /* Evaluate word/byte mode. Need to count by 4 because text is only in plane 0. */
1816 s_incr = pThis->cr[0x17] & 0x40 ? 4 : 8;
1817
1818 unsigned addr_mask;
1819 if (!(pThis->cr[0x17] & 0x40) && !(pThis->cr[0x17] & 0x20))
1820 addr_mask = 0xffff; /* Wrap at 64K, for CGA and 64K EGA compatibility. */
1821 else
1822 addr_mask = 0x3ffff; /* Wrap at 256K, standard VGA. */
1823
1824 line_offset = pThis->line_offset;
1825 s1 = pThisCC->pbVRam + ((pThis->start_addr * s_incr) & addr_mask);
1826
1827 /* double scanning - not for 9-wide modes */
1828 dscan = (pThis->cr[9] >> 7) & 1;
1829
1830 /* total width & height */
1831 cheight = (pThis->cr[9] & 0x1f) + 1;
1832 cw = 8;
1833 if (!(pThis->sr[1] & 0x01))
1834 cw = 9;
1835 if (pThis->sr[1] & 0x08)
1836 cw = 16; /* NOTE: no 18 pixel wide */
1837 x_incr = cw * ((pDrv->cBits + 7) >> 3);
1838 width = (pThis->cr[0x01] + 1);
1839 if (pThis->cr[0x06] == 100) {
1840 /* ugly hack for CGA 160x100x16 - explain me the logic */
1841 height = 100;
1842 } else {
1843 height = pThis->cr[0x12] |
1844 ((pThis->cr[0x07] & 0x02) << 7) |
1845 ((pThis->cr[0x07] & 0x40) << 3);
1846 height = (height + 1) / cheight;
1847 }
1848 /** @todo r=michaln This conditional is questionable; we should be able
1849 * to draw whatver the guest asks for. */
1850 if ((height * width) > CH_ATTR_SIZE) {
1851 /* better than nothing: exit if transient size is too big */
1852 return VINF_SUCCESS;
1853 }
1854
1855 if (width != (int)pThis->last_width || height != (int)pThis->last_height ||
1856 cw != pThis->last_cw || cheight != pThis->last_ch) {
1857 if (fFailOnResize)
1858 {
1859 /* The caller does not want to call the pfnResize. */
1860 return VERR_TRY_AGAIN;
1861 }
1862 pThis->last_scr_width = width * cw;
1863 pThis->last_scr_height = height * cheight;
1864 /* For text modes the direct use of guest VRAM is not implemented, so bpp and cbLine are 0 here. */
1865 int rc = pDrv->pfnResize(pDrv, 0, NULL, 0, pThis->last_scr_width, pThis->last_scr_height);
1866 pThis->last_width = width;
1867 pThis->last_height = height;
1868 pThis->last_ch = cheight;
1869 pThis->last_cw = cw;
1870 full_update = true;
1871 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1872 return rc;
1873 AssertRC(rc);
1874 }
1875 cursor_offset = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) - pThis->start_addr;
1876 if (cursor_offset != pThis->cursor_offset ||
1877 pThis->cr[0xa] != pThis->cursor_start ||
1878 pThis->cr[0xb] != pThis->cursor_end) {
1879 /* if the cursor position changed, we update the old and new
1880 chars */
1881 if (pThis->cursor_offset < CH_ATTR_SIZE)
1882 pThis->last_ch_attr[pThis->cursor_offset] = UINT32_MAX;
1883 if (cursor_offset < CH_ATTR_SIZE)
1884 pThis->last_ch_attr[cursor_offset] = UINT32_MAX;
1885 pThis->cursor_offset = cursor_offset;
1886 pThis->cursor_start = pThis->cr[0xa];
1887 pThis->cursor_end = pThis->cr[0xb];
1888 }
1889 cursor_ptr = pThisCC->pbVRam + (((pThis->start_addr + cursor_offset) * s_incr) & addr_mask);
1890 depth_index = vgaR3GetDepthIndex(pDrv->cBits);
1891 if (cw == 16)
1892 vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
1893 else
1894 vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
1895 vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
1896
1897 dest = pDrv->pbData;
1898 linesize = pDrv->cbScanline;
1899 ch_attr_ptr = pThis->last_ch_attr;
1900 cy_start = -1;
1901 cx_max_upd = -1;
1902 cx_min_upd = width;
1903
1904 /* Figure out if we're in the visible period of the blink cycle. */
1905 time_ns = PDMDevHlpTMTimeVirtGetNano(pDevIns);
1906 blink_on = (time_ns % VGA_BLINK_PERIOD_FULL) < VGA_BLINK_PERIOD_ON;
1907 chr_blink_flip = false;
1908 cur_blink_flip = false;
1909 if (pThis->last_chr_blink != blink_on)
1910 {
1911 /* Currently cursor and characters blink at the same rate, but they might not. */
1912 pThis->last_chr_blink = blink_on;
1913 pThis->last_cur_blink = blink_on;
1914 chr_blink_flip = true;
1915 cur_blink_flip = true;
1916 }
1917
1918 for(cy = 0; cy < (height - dscan); cy = cy + (1 << dscan)) {
1919 d1 = dest;
1920 src = s1;
1921 cx_min = width;
1922 cx_max = -1;
1923 for(cx = 0; cx < width; cx++) {
1924 ch_attr = *(uint16_t *)src;
1925 /* Figure out if character needs redrawing due to blink state change. */
1926 blink_do_redraw = blink_enabled && chr_blink_flip && (ch_attr & 0x8000);
1927 if (full_update || ch_attr != (int)*ch_attr_ptr || blink_do_redraw || (src == cursor_ptr && cur_blink_flip)) {
1928 if (cx < cx_min)
1929 cx_min = cx;
1930 if (cx > cx_max)
1931 cx_max = cx;
1932 if (reset_dirty)
1933 *ch_attr_ptr = ch_attr;
1934#ifdef WORDS_BIGENDIAN
1935 ch = ch_attr >> 8;
1936 cattr = ch_attr & 0xff;
1937#else
1938 ch = ch_attr & 0xff;
1939 cattr = ch_attr >> 8;
1940#endif
1941 font_ptr = font_base[(cattr >> 3) & 1];
1942 font_ptr += 32 * 4 * ch;
1943 bgcol = palette[cattr >> 4];
1944 fgcol = palette[cattr & 0x0f];
1945
1946 if (blink_enabled && (cattr & 0x80))
1947 {
1948 bgcol = palette[(cattr >> 4) & 7];
1949 if (!blink_on)
1950 font_ptr = empty_glyph;
1951 }
1952
1953 if (cw != 9) {
1954 if (pThis->fRenderVRAM)
1955 vga_draw_glyph8(d1, linesize, font_ptr, cheight, fgcol, bgcol, dscan);
1956 } else {
1957 dup9 = 0;
1958 if (ch >= 0xb0 && ch <= 0xdf && (pThis->ar[0x10] & 0x04))
1959 dup9 = 1;
1960 if (pThis->fRenderVRAM)
1961 vga_draw_glyph9(d1, linesize, font_ptr, cheight, fgcol, bgcol, dup9);
1962 }
1963
1964 /* Underline. Typically turned off by setting it past cell height. */
1965 if (((cattr & 0x03) == 1) && (uline_pos < cheight))
1966 {
1967 int h;
1968
1969 d = d1 + (linesize * uline_pos << dscan);
1970 h = 1;
1971
1972 if (cw != 9) {
1973 if (pThis->fRenderVRAM)
1974 vga_draw_glyph8(d, linesize, cursor_glyph, h, fgcol, bgcol, dscan);
1975 } else {
1976 if (pThis->fRenderVRAM)
1977 vga_draw_glyph9(d, linesize, cursor_glyph, h, fgcol, bgcol, 1);
1978 }
1979 }
1980
1981 /* Cursor. */
1982 if (src == cursor_ptr &&
1983 !(pThis->cr[0x0a] & 0x20)) {
1984 int line_start, line_last, h;
1985
1986 /* draw the cursor if within the visible period */
1987 if (blink_on) {
1988 line_start = pThis->cr[0x0a] & 0x1f;
1989 line_last = pThis->cr[0x0b] & 0x1f;
1990 /* XXX: check that */
1991 if (line_last > cheight - 1)
1992 line_last = cheight - 1;
1993 if (line_last >= line_start && line_start < cheight) {
1994 h = line_last - line_start + 1;
1995 d = d1 + (linesize * line_start << dscan);
1996 if (cw != 9) {
1997 if (pThis->fRenderVRAM)
1998 vga_draw_glyph8(d, linesize, cursor_glyph, h, fgcol, bgcol, dscan);
1999 } else {
2000 if (pThis->fRenderVRAM)
2001 vga_draw_glyph9(d, linesize, cursor_glyph, h, fgcol, bgcol, 1);
2002 }
2003 }
2004 }
2005 }
2006 }
2007 d1 += x_incr;
2008 src += s_incr; /* Even in text mode, word/byte mode matters. */
2009 if (src > (pThisCC->pbVRam + addr_mask))
2010 src = pThisCC->pbVRam;
2011 ch_attr_ptr++;
2012 }
2013 if (cx_max != -1) {
2014 /* Keep track of the bounding rectangle for updates. */
2015 if (cy_start == -1)
2016 cy_start = cy;
2017 if (cx_min_upd > cx_min)
2018 cx_min_upd = cx_min;
2019 if (cx_max_upd < cx_max)
2020 cx_max_upd = cx_max;
2021 } else if (cy_start >= 0) {
2022 /* Flush updates to display. */
2023 pDrv->pfnUpdateRect(pDrv, cx_min_upd * cw, cy_start * cheight,
2024 (cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
2025 cy_start = -1;
2026 cx_max_upd = -1;
2027 cx_min_upd = width;
2028 }
2029
2030 dest += linesize * cheight << dscan;
2031 s1 += line_offset;
2032
2033 /* Line compare works in text modes, too. */
2034 /** @todo r=michaln This is inaccurate; text should be rendered line by line
2035 * and line compare checked after every line. */
2036 if ((uint32_t)cy == (pThis->line_compare / cheight))
2037 s1 = pThisCC->pbVRam;
2038
2039 if (s1 > (pThisCC->pbVRam + addr_mask))
2040 s1 = s1 - (addr_mask + 1);
2041 }
2042 if (cy_start >= 0)
2043 /* Flush any remaining changes to display. */
2044 pDrv->pfnUpdateRect(pDrv, cx_min_upd * cw, cy_start * cheight,
2045 (cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
2046 return VINF_SUCCESS;
2047}
2048
2049enum {
2050 VGA_DRAW_LINE2,
2051 VGA_DRAW_LINE2D2,
2052 VGA_DRAW_LINE4,
2053 VGA_DRAW_LINE4D2,
2054 VGA_DRAW_LINE8D2,
2055 VGA_DRAW_LINE8,
2056 VGA_DRAW_LINE15,
2057 VGA_DRAW_LINE16,
2058 VGA_DRAW_LINE24,
2059 VGA_DRAW_LINE32,
2060 VGA_DRAW_LINE_NB
2061};
2062
2063static vga_draw_line_func * const vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
2064 vga_draw_line2_8,
2065 vga_draw_line2_16,
2066 vga_draw_line2_16,
2067 vga_draw_line2_32,
2068
2069 vga_draw_line2d2_8,
2070 vga_draw_line2d2_16,
2071 vga_draw_line2d2_16,
2072 vga_draw_line2d2_32,
2073
2074 vga_draw_line4_8,
2075 vga_draw_line4_16,
2076 vga_draw_line4_16,
2077 vga_draw_line4_32,
2078
2079 vga_draw_line4d2_8,
2080 vga_draw_line4d2_16,
2081 vga_draw_line4d2_16,
2082 vga_draw_line4d2_32,
2083
2084 vga_draw_line8d2_8,
2085 vga_draw_line8d2_16,
2086 vga_draw_line8d2_16,
2087 vga_draw_line8d2_32,
2088
2089 vga_draw_line8_8,
2090 vga_draw_line8_16,
2091 vga_draw_line8_16,
2092 vga_draw_line8_32,
2093
2094 vga_draw_line15_8,
2095 vga_draw_line15_15,
2096 vga_draw_line15_16,
2097 vga_draw_line15_32,
2098
2099 vga_draw_line16_8,
2100 vga_draw_line16_15,
2101 vga_draw_line16_16,
2102 vga_draw_line16_32,
2103
2104 vga_draw_line24_8,
2105 vga_draw_line24_15,
2106 vga_draw_line24_16,
2107 vga_draw_line24_32,
2108
2109 vga_draw_line32_8,
2110 vga_draw_line32_15,
2111 vga_draw_line32_16,
2112 vga_draw_line32_32,
2113};
2114
2115static int vgaR3GetBpp(PVGASTATE pThis)
2116{
2117 int ret;
2118#ifdef CONFIG_BOCHS_VBE
2119 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2120 ret = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
2121 } else
2122#endif
2123 {
2124 ret = 0;
2125 }
2126 return ret;
2127}
2128
2129static void vgaR3GetResolution(PVGASTATE pThis, int *pwidth, int *pheight)
2130{
2131 int width, height;
2132#ifdef CONFIG_BOCHS_VBE
2133 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2134 width = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
2135 height = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
2136 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
2137 } else
2138#endif
2139 {
2140 width = (pThis->cr[0x01] + 1) * 8;
2141 height = pThis->cr[0x12] |
2142 ((pThis->cr[0x07] & 0x02) << 7) |
2143 ((pThis->cr[0x07] & 0x40) << 3);
2144 height = (height + 1);
2145 }
2146 *pwidth = width;
2147 *pheight = height;
2148}
2149
2150
2151/**
2152 * Performs the display driver resizing when in graphics mode.
2153 *
2154 * This will recalc / update any status data depending on the driver
2155 * properties (bit depth mostly).
2156 *
2157 * @returns VINF_SUCCESS on success.
2158 * @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
2159 * @param pThis Pointer to the shared VGA state.
2160 * @param pThisCC Pointer to the ring-3 VGA state.
2161 * @param cx The width.
2162 * @param cy The height.
2163 * @param pDrv The display connector.
2164 */
2165static int vgaR3ResizeGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, int cx, int cy, PDMIDISPLAYCONNECTOR *pDrv)
2166{
2167 const unsigned cBits = pThisCC->get_bpp(pThis);
2168
2169 int rc;
2170 AssertReturn(cx, VERR_INVALID_PARAMETER);
2171 AssertReturn(cy, VERR_INVALID_PARAMETER);
2172 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2173
2174 if (!pThis->line_offset)
2175 return VERR_INTERNAL_ERROR;
2176
2177#if 0 //def VBOX_WITH_VDMA
2178 /** @todo we get a second resize here when VBVA is on, while we actually should not */
2179 /* do not do pfnResize in case VBVA is on since all mode changes are performed over VBVA
2180 * we are checking for VDMA state here to ensure this code works only for WDDM driver,
2181 * although we should avoid calling pfnResize for XPDM as well, since pfnResize is actually an extra resize
2182 * event and generally only pfnVBVAxxx calls should be used with HGSMI + VBVA
2183 *
2184 * The reason for doing this for WDDM driver only now is to avoid regressions of the current code */
2185 PVBOXVDMAHOST pVdma = pThisCC->pVdma;
2186 if (pVdma && vboxVDMAIsEnabled(pVdma))
2187 rc = VINF_SUCCESS;
2188 else
2189#endif
2190 {
2191 /* Skip the resize if the values are not valid. */
2192 if (pThis->start_addr * 4 + pThis->line_offset * cy < pThis->vram_size)
2193 /* Take into account the programmed start address (in DWORDs) of the visible screen. */
2194 rc = pDrv->pfnResize(pDrv, cBits, pThisCC->pbVRam + pThis->start_addr * 4, pThis->line_offset, cx, cy);
2195 else
2196 {
2197 /* Change nothing in the VGA state. Lets hope the guest will eventually programm correct values. */
2198 return VERR_TRY_AGAIN;
2199 }
2200 }
2201
2202 /* last stuff */
2203 pThis->last_bpp = cBits;
2204 pThis->last_scr_width = cx;
2205 pThis->last_scr_height = cy;
2206 pThis->last_width = cx;
2207 pThis->last_height = cy;
2208
2209 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
2210 return rc;
2211 AssertRC(rc);
2212
2213 /* update palette */
2214 switch (pDrv->cBits)
2215 {
2216 case 32: pThisCC->rgb_to_pixel = rgb_to_pixel32_dup; break;
2217 case 16:
2218 default: pThisCC->rgb_to_pixel = rgb_to_pixel16_dup; break;
2219 case 15: pThisCC->rgb_to_pixel = rgb_to_pixel15_dup; break;
2220 case 8: pThisCC->rgb_to_pixel = rgb_to_pixel8_dup; break;
2221 }
2222 if (pThis->shift_control == 0)
2223 vgaR3UpdatePalette16(pThis, pThisCC);
2224 else if (pThis->shift_control == 1)
2225 vgaR3UpdatePalette16(pThis, pThisCC);
2226 return VINF_SUCCESS;
2227}
2228
2229# ifdef VBOX_WITH_VMSVGA
2230
2231# if 0 /* unused? */
2232int vgaR3UpdateDisplay(PVGASTATE pThis, PVGASTATER3 pThisCC, unsigned xStart, unsigned yStart, unsigned cx, unsigned cy, PDMIDISPLAYCONNECTOR *pDrv)
2233{
2234 uint32_t v;
2235 vga_draw_line_func *vga_draw_line;
2236
2237 if (!pThis->fRenderVRAM)
2238 {
2239 pDrv->pfnUpdateRect(pDrv, xStart, yStart, cx, cy);
2240 return VINF_SUCCESS;
2241 }
2242 /** @todo might crash if a blit follows a resolution change very quickly (seen this many times!) */
2243
2244 if ( pThis->svga.uWidth == VMSVGA_VAL_UNINITIALIZED
2245 || pThis->svga.uHeight == VMSVGA_VAL_UNINITIALIZED
2246 || pThis->svga.uBpp == VMSVGA_VAL_UNINITIALIZED)
2247 {
2248 /* Intermediate state; skip redraws. */
2249 AssertFailed();
2250 return VINF_SUCCESS;
2251 }
2252
2253 uint32_t cBits;
2254 switch (pThis->svga.uBpp) {
2255 default:
2256 case 0:
2257 case 8:
2258 AssertFailed();
2259 return VERR_NOT_IMPLEMENTED;
2260 case 15:
2261 v = VGA_DRAW_LINE15;
2262 cBits = 16;
2263 break;
2264 case 16:
2265 v = VGA_DRAW_LINE16;
2266 cBits = 16;
2267 break;
2268 case 24:
2269 v = VGA_DRAW_LINE24;
2270 cBits = 24;
2271 break;
2272 case 32:
2273 v = VGA_DRAW_LINE32;
2274 cBits = 32;
2275 break;
2276 }
2277 vga_draw_line = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2278
2279 uint32_t offSrc = (xStart * cBits) / 8 + pThis->svga.cbScanline * yStart;
2280 uint32_t offDst = (xStart * RT_ALIGN(pDrv->cBits, 8)) / 8 + pDrv->cbScanline * yStart;
2281
2282 uint8_t *pbDst = pDrv->pbData + offDst;
2283 uint8_t const *pbSrc = pThisCC->pbVRam + offSrc;
2284
2285 for (unsigned y = yStart; y < yStart + cy; y++)
2286 {
2287 vga_draw_line(pThis, pThisCC, pbDst, pbSrc, cx);
2288
2289 pbDst += pDrv->cbScanline;
2290 pbSrc += pThis->svga.cbScanline;
2291 }
2292 pDrv->pfnUpdateRect(pDrv, xStart, yStart, cx, cy);
2293
2294 return VINF_SUCCESS;
2295}
2296# endif
2297
2298/**
2299 * graphic modes
2300 */
2301static int vmsvgaR3DrawGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, bool fFullUpdate,
2302 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
2303{
2304 RT_NOREF1(fFailOnResize);
2305
2306 uint32_t const cx = pThis->last_scr_width;
2307 uint32_t const cxDisplay = cx;
2308 uint32_t const cy = pThis->last_scr_height;
2309 uint32_t cBits = pThis->last_bpp;
2310
2311 if ( cx == VMSVGA_VAL_UNINITIALIZED
2312 || cx == 0
2313 || cy == VMSVGA_VAL_UNINITIALIZED
2314 || cy == 0
2315 || cBits == VMSVGA_VAL_UNINITIALIZED
2316 || cBits == 0)
2317 {
2318 /* Intermediate state; skip redraws. */
2319 return VINF_SUCCESS;
2320 }
2321
2322 unsigned v;
2323 switch (cBits)
2324 {
2325 case 8:
2326 /* Note! experimental, not sure if this really works... */
2327 /** @todo fFullUpdate |= vgaR3UpdatePalette256(pThis); - need fFullUpdate but not
2328 * copying anything to last_palette. */
2329 v = VGA_DRAW_LINE8;
2330 break;
2331 case 15:
2332 v = VGA_DRAW_LINE15;
2333 cBits = 16;
2334 break;
2335 case 16:
2336 v = VGA_DRAW_LINE16;
2337 break;
2338 case 24:
2339 v = VGA_DRAW_LINE24;
2340 break;
2341 case 32:
2342 v = VGA_DRAW_LINE32;
2343 break;
2344 default:
2345 case 0:
2346 AssertFailed();
2347 return VERR_NOT_IMPLEMENTED;
2348 }
2349 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2350
2351 Assert(!pThisCC->cursor_invalidate);
2352 Assert(!pThisCC->cursor_draw_line);
2353 //not used// if (pThisCC->cursor_invalidate)
2354 //not used// pThisCC->cursor_invalidate(pThis);
2355
2356 uint8_t *pbDst = pDrv->pbData;
2357 uint32_t cbDstScanline = pDrv->cbScanline;
2358 uint32_t offSrcStart = 0; /* always start at the beginning of the framebuffer */
2359 uint32_t cbScanline = (cx * cBits + 7) / 8; /* The visible width of a scanline. */
2360 uint32_t yUpdateRectTop = UINT32_MAX;
2361 uint32_t offPageMin = UINT32_MAX;
2362 int32_t offPageMax = -1;
2363 uint32_t y;
2364 for (y = 0; y < cy; y++)
2365 {
2366 uint32_t offSrcLine = offSrcStart + y * cbScanline;
2367 uint32_t offPage0 = offSrcLine & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2368 uint32_t offPage1 = (offSrcLine + cbScanline - 1) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2369 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2370 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2371 * anything wider than 2050 pixels @32bpp. Need to check all pages
2372 * between the first and last one. */
2373 bool fUpdate = fFullUpdate || vgaR3IsDirty(pThis, offPage0) || vgaR3IsDirty(pThis, offPage1);
2374 if (offPage1 - offPage0 > GUEST_PAGE_SIZE)
2375 /* if wide line, can use another page */
2376 fUpdate |= vgaR3IsDirty(pThis, offPage0 + GUEST_PAGE_SIZE);
2377 /* explicit invalidation for the hardware cursor */
2378 fUpdate |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2379 if (fUpdate)
2380 {
2381 if (yUpdateRectTop == UINT32_MAX)
2382 yUpdateRectTop = y;
2383 if (offPage0 < offPageMin)
2384 offPageMin = offPage0;
2385 if ((int32_t)offPage1 > offPageMax)
2386 offPageMax = offPage1;
2387 if (pThis->fRenderVRAM)
2388 pfnVgaDrawLine(pThis, pThisCC, pbDst, pThisCC->pbVRam + offSrcLine, cx);
2389 //not used// if (pThisCC->cursor_draw_line)
2390 //not used// pThisCC->cursor_draw_line(pThis, pbDst, y);
2391 }
2392 else if (yUpdateRectTop != UINT32_MAX)
2393 {
2394 /* flush to display */
2395 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop));
2396 pDrv->pfnUpdateRect(pDrv, 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop);
2397 yUpdateRectTop = UINT32_MAX;
2398 }
2399 pbDst += cbDstScanline;
2400 }
2401 if (yUpdateRectTop != UINT32_MAX)
2402 {
2403 /* flush to display */
2404 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop));
2405 pDrv->pfnUpdateRect(pDrv, 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop);
2406 }
2407
2408 /* reset modified pages */
2409 if (offPageMax != -1 && reset_dirty)
2410 vgaR3ResetDirty(pThis, offPageMin, offPageMax + GUEST_PAGE_SIZE);
2411 memset(pThis->invalidated_y_table, 0, ((cy + 31) >> 5) * 4);
2412
2413 return VINF_SUCCESS;
2414}
2415
2416# endif /* VBOX_WITH_VMSVGA */
2417
2418/**
2419 * graphic modes
2420 */
2421static int vgaR3DrawGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update, bool fFailOnResize, bool reset_dirty,
2422 PDMIDISPLAYCONNECTOR *pDrv)
2423{
2424 int y1, y2, y, page_min, page_max, linesize, y_start, double_scan;
2425 int width, height, shift_control, line_offset, page0, page1, bwidth, bits;
2426 int disp_width, multi_run;
2427 uint8_t *d;
2428 uint32_t v, addr1, addr;
2429 vga_draw_line_func *pfnVgaDrawLine;
2430
2431 bool offsets_changed = vgaR3UpdateBasicParams(pThis, pThisCC);
2432
2433 full_update |= offsets_changed;
2434
2435 pThisCC->get_resolution(pThis, &width, &height);
2436 disp_width = width;
2437
2438 shift_control = (pThis->gr[0x05] >> 5) & 3;
2439 double_scan = (pThis->cr[0x09] >> 7);
2440 multi_run = double_scan;
2441 if (shift_control != pThis->shift_control ||
2442 double_scan != pThis->double_scan) {
2443 full_update = true;
2444 pThis->shift_control = shift_control;
2445 pThis->double_scan = double_scan;
2446 }
2447
2448 if (shift_control == 0) {
2449 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
2450 if (pThis->sr[0x01] & 8) {
2451 v = VGA_DRAW_LINE4D2;
2452 disp_width <<= 1;
2453 } else {
2454 v = VGA_DRAW_LINE4;
2455 }
2456 bits = 4;
2457 } else if (shift_control == 1) {
2458 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
2459 if (pThis->sr[0x01] & 8) {
2460 v = VGA_DRAW_LINE2D2;
2461 disp_width <<= 1;
2462 } else {
2463 v = VGA_DRAW_LINE2;
2464 }
2465 bits = 4;
2466 } else {
2467 switch(pThisCC->get_bpp(pThis)) {
2468 default:
2469 case 0:
2470 full_update |= vgaR3UpdatePalette256(pThis, pThisCC);
2471 v = VGA_DRAW_LINE8D2;
2472 bits = 4;
2473 break;
2474 case 8:
2475 full_update |= vgaR3UpdatePalette256(pThis, pThisCC);
2476 v = VGA_DRAW_LINE8;
2477 bits = 8;
2478 break;
2479 case 15:
2480 v = VGA_DRAW_LINE15;
2481 bits = 16;
2482 break;
2483 case 16:
2484 v = VGA_DRAW_LINE16;
2485 bits = 16;
2486 break;
2487 case 24:
2488 v = VGA_DRAW_LINE24;
2489 bits = 24;
2490 break;
2491 case 32:
2492 v = VGA_DRAW_LINE32;
2493 bits = 32;
2494 break;
2495 }
2496 }
2497 if ( disp_width != (int)pThis->last_width
2498 || height != (int)pThis->last_height
2499 || pThisCC->get_bpp(pThis) != (int)pThis->last_bpp
2500 || (offsets_changed && !pThis->fRenderVRAM))
2501 {
2502 if (fFailOnResize)
2503 {
2504 /* The caller does not want to call the pfnResize. */
2505 return VERR_TRY_AGAIN;
2506 }
2507 int rc = vgaR3ResizeGraphic(pThis, pThisCC, disp_width, height, pDrv);
2508 if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
2509 return rc;
2510 full_update = true;
2511 }
2512
2513 if (pThis->fRenderVRAM)
2514 {
2515 /* Do not update the destination buffer if it is not big enough.
2516 * Can happen if the resize request was ignored by the driver.
2517 * Compare with 'disp_width', because it is what the framebuffer has been resized to.
2518 */
2519 if ( pDrv->cx != (uint32_t)disp_width
2520 || pDrv->cy != (uint32_t)height)
2521 {
2522 LogRel(("Framebuffer mismatch: vga %dx%d, drv %dx%d!!!\n",
2523 disp_width, height,
2524 pDrv->cx, pDrv->cy));
2525 return VINF_SUCCESS;
2526 }
2527 }
2528
2529 pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2530
2531 if (pThisCC->cursor_invalidate)
2532 pThisCC->cursor_invalidate(pThis);
2533
2534 line_offset = pThis->line_offset;
2535#if 0
2536 Log(("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
2537 width, height, v, line_offset, pThis->cr[9], pThis->cr[0x17], pThis->line_compare, pThis->sr[0x01]));
2538#endif
2539 addr1 = (pThis->start_addr * 4);
2540 bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
2541 y_start = -1;
2542 page_min = 0x7fffffff;
2543 page_max = -1;
2544 d = pDrv->pbData;
2545 linesize = pDrv->cbScanline;
2546
2547 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
2548 pThis->vga_addr_mask = 0x3ffff;
2549 else
2550 pThis->vga_addr_mask = UINT32_MAX;
2551
2552 y1 = 0;
2553 y2 = pThis->cr[0x09] & 0x1F; /* starting row scan count */
2554 for(y = 0; y < height; y++) {
2555 addr = addr1;
2556 /* CGA/MDA compatibility. Note that these addresses are all
2557 * shifted left by two compared to VGA specs.
2558 */
2559 if (!(pThis->cr[0x17] & 1)) {
2560 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2561 }
2562 if (!(pThis->cr[0x17] & 2)) {
2563 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2564 }
2565 addr &= pThis->vga_addr_mask;
2566 page0 = addr & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2567 page1 = (addr + bwidth - 1) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2568 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2569 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2570 * anything wider than 2050 pixels @32bpp. Need to check all pages
2571 * between the first and last one. */
2572 bool update = full_update || vgaR3IsDirty(pThis, page0) || vgaR3IsDirty(pThis, page1);
2573 if (page1 - page0 > GUEST_PAGE_SIZE) {
2574 /* if wide line, can use another page */
2575 update |= vgaR3IsDirty(pThis, page0 + GUEST_PAGE_SIZE);
2576 }
2577 /* explicit invalidation for the hardware cursor */
2578 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2579 if (update) {
2580 if (y_start < 0)
2581 y_start = y;
2582 if (page0 < page_min)
2583 page_min = page0;
2584 if (page1 > page_max)
2585 page_max = page1;
2586 if (pThis->fRenderVRAM)
2587 pfnVgaDrawLine(pThis, pThisCC, d, pThisCC->pbVRam + addr, width);
2588 if (pThisCC->cursor_draw_line)
2589 pThisCC->cursor_draw_line(pThis, d, y);
2590 } else {
2591 if (y_start >= 0) {
2592 /* flush to display */
2593 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2594 y_start = -1;
2595 }
2596 }
2597 if (!multi_run) {
2598 y1++;
2599 multi_run = double_scan;
2600
2601 if (y2 == 0) {
2602 y2 = pThis->cr[0x09] & 0x1F;
2603 addr1 += line_offset;
2604 } else {
2605 --y2;
2606 }
2607 } else {
2608 multi_run--;
2609 }
2610 /* line compare acts on the displayed lines */
2611 if ((uint32_t)y == pThis->line_compare)
2612 addr1 = 0;
2613 d += linesize;
2614 }
2615 if (y_start >= 0) {
2616 /* flush to display */
2617 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2618 }
2619 /* reset modified pages */
2620 if (page_max != -1 && reset_dirty) {
2621 vgaR3ResetDirty(pThis, page_min, page_max + GUEST_PAGE_SIZE);
2622 }
2623 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2624 return VINF_SUCCESS;
2625}
2626
2627/**
2628 * blanked modes
2629 */
2630static int vgaR3DrawBlank(PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update,
2631 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
2632{
2633 int i, w, val;
2634 uint8_t *d;
2635 uint32_t cbScanline = pDrv->cbScanline;
2636 uint32_t page_min, page_max;
2637
2638 if (pThis->last_width != 0)
2639 {
2640 if (fFailOnResize)
2641 {
2642 /* The caller does not want to call the pfnResize. */
2643 return VERR_TRY_AGAIN;
2644 }
2645 pThis->last_width = 0;
2646 pThis->last_height = 0;
2647 /* For blanking signal width=0, height=0, bpp=0 and cbLine=0 here.
2648 * There is no screen content, which distinguishes it from text mode. */
2649 pDrv->pfnResize(pDrv, 0, NULL, 0, 0, 0);
2650 }
2651 /* reset modified pages, i.e. everything */
2652 if (reset_dirty && pThis->last_scr_height > 0)
2653 {
2654 page_min = (pThis->start_addr * 4) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2655 /* round up page_max by one page, as otherwise this can be -GUEST_PAGE_SIZE,
2656 * which causes assertion trouble in vgaR3ResetDirty. */
2657 page_max = (pThis->start_addr * 4 + pThis->line_offset * pThis->last_scr_height - 1 + GUEST_PAGE_SIZE)
2658 & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2659 vgaR3ResetDirty(pThis, page_min, page_max + GUEST_PAGE_SIZE);
2660 }
2661 if (pDrv->pbData == pThisCC->pbVRam) /* Do not clear the VRAM itself. */
2662 return VINF_SUCCESS;
2663 if (!full_update)
2664 return VINF_SUCCESS;
2665 if (pThis->last_scr_width <= 0 || pThis->last_scr_height <= 0)
2666 return VINF_SUCCESS;
2667 if (pDrv->cBits == 8)
2668 val = pThisCC->rgb_to_pixel(0, 0, 0);
2669 else
2670 val = 0;
2671 w = pThis->last_scr_width * ((pDrv->cBits + 7) >> 3);
2672 d = pDrv->pbData;
2673 if (pThis->fRenderVRAM)
2674 {
2675 for(i = 0; i < (int)pThis->last_scr_height; i++) {
2676 memset(d, val, w);
2677 d += cbScanline;
2678 }
2679 }
2680 pDrv->pfnUpdateRect(pDrv, 0, 0, pThis->last_scr_width, pThis->last_scr_height);
2681 return VINF_SUCCESS;
2682}
2683
2684
2685#define GMODE_TEXT 0
2686#define GMODE_GRAPH 1
2687#define GMODE_BLANK 2
2688#ifdef VBOX_WITH_VMSVGA
2689#define GMODE_SVGA 3
2690#endif
2691
2692/**
2693 * Worker for vgaR3PortUpdateDisplay(), vgaR3UpdateDisplayAllInternal() and
2694 * vgaR3PortTakeScreenshot().
2695 */
2696static int vgaR3UpdateDisplay(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATER3 pThisCC, bool fUpdateAll,
2697 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv, int32_t *pcur_graphic_mode)
2698{
2699 int rc = VINF_SUCCESS;
2700 int graphic_mode;
2701
2702 if (pDrv->cBits == 0) {
2703 /* nothing to do */
2704 } else {
2705 switch(pDrv->cBits) {
2706 case 8:
2707 pThisCC->rgb_to_pixel = rgb_to_pixel8_dup;
2708 break;
2709 case 15:
2710 pThisCC->rgb_to_pixel = rgb_to_pixel15_dup;
2711 break;
2712 default:
2713 case 16:
2714 pThisCC->rgb_to_pixel = rgb_to_pixel16_dup;
2715 break;
2716 case 32:
2717 pThisCC->rgb_to_pixel = rgb_to_pixel32_dup;
2718 break;
2719 }
2720
2721#ifdef VBOX_WITH_VMSVGA
2722 if (pThis->svga.fEnabled) {
2723 graphic_mode = GMODE_SVGA;
2724 }
2725 else
2726#endif
2727 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2728 graphic_mode = GMODE_BLANK;
2729 } else {
2730 graphic_mode = pThis->gr[6] & 1 ? GMODE_GRAPH : GMODE_TEXT;
2731 }
2732 bool full_update = fUpdateAll || graphic_mode != *pcur_graphic_mode;
2733 if (full_update) {
2734 *pcur_graphic_mode = graphic_mode;
2735 }
2736 switch(graphic_mode) {
2737 case GMODE_TEXT:
2738 rc = vgaR3DrawText(pDevIns, pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2739 break;
2740 case GMODE_GRAPH:
2741 rc = vgaR3DrawGraphic(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2742 break;
2743#ifdef VBOX_WITH_VMSVGA
2744 case GMODE_SVGA:
2745 rc = vmsvgaR3DrawGraphic(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2746 break;
2747#endif
2748 case GMODE_BLANK:
2749 default:
2750 rc = vgaR3DrawBlank(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2751 break;
2752 }
2753 }
2754 return rc;
2755}
2756
2757/**
2758 * Worker for vgaR3SaveExec().
2759 */
2760static void vga_save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PVGASTATE pThis)
2761{
2762 int i;
2763
2764 pHlp->pfnSSMPutU32(pSSM, pThis->latch);
2765 pHlp->pfnSSMPutU8(pSSM, pThis->sr_index);
2766 pHlp->pfnSSMPutMem(pSSM, pThis->sr, 8);
2767 pHlp->pfnSSMPutU8(pSSM, pThis->gr_index);
2768 pHlp->pfnSSMPutMem(pSSM, pThis->gr, 16);
2769 pHlp->pfnSSMPutU8(pSSM, pThis->ar_index);
2770 pHlp->pfnSSMPutMem(pSSM, pThis->ar, 21);
2771 pHlp->pfnSSMPutU32(pSSM, pThis->ar_flip_flop);
2772 pHlp->pfnSSMPutU8(pSSM, pThis->cr_index);
2773 pHlp->pfnSSMPutMem(pSSM, pThis->cr, 256);
2774 pHlp->pfnSSMPutU8(pSSM, pThis->msr);
2775 pHlp->pfnSSMPutU8(pSSM, pThis->fcr);
2776 pHlp->pfnSSMPutU8(pSSM, pThis->st00);
2777 pHlp->pfnSSMPutU8(pSSM, pThis->st01);
2778
2779 pHlp->pfnSSMPutU8(pSSM, pThis->dac_state);
2780 pHlp->pfnSSMPutU8(pSSM, pThis->dac_sub_index);
2781 pHlp->pfnSSMPutU8(pSSM, pThis->dac_read_index);
2782 pHlp->pfnSSMPutU8(pSSM, pThis->dac_write_index);
2783 pHlp->pfnSSMPutMem(pSSM, pThis->dac_cache, 3);
2784 pHlp->pfnSSMPutMem(pSSM, pThis->palette, 768);
2785
2786 pHlp->pfnSSMPutU32(pSSM, pThis->bank_offset);
2787#ifdef CONFIG_BOCHS_VBE
2788 AssertCompile(RT_ELEMENTS(pThis->vbe_regs) < 256);
2789 pHlp->pfnSSMPutU8(pSSM, (uint8_t)RT_ELEMENTS(pThis->vbe_regs));
2790 pHlp->pfnSSMPutU16(pSSM, pThis->vbe_index);
2791 for(i = 0; i < (int)RT_ELEMENTS(pThis->vbe_regs); i++)
2792 pHlp->pfnSSMPutU16(pSSM, pThis->vbe_regs[i]);
2793 pHlp->pfnSSMPutU32(pSSM, pThis->vbe_start_addr);
2794 pHlp->pfnSSMPutU32(pSSM, pThis->vbe_line_offset);
2795#else
2796 pHlp->pfnSSMPutU8(pSSM, 0);
2797#endif
2798}
2799
2800
2801/**
2802 * Worker for vgaR3LoadExec().
2803 */
2804static int vga_load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PVGASTATE pThis, int version_id)
2805{
2806 int is_vbe, i;
2807 uint32_t u32Dummy;
2808 uint8_t u8;
2809
2810 pHlp->pfnSSMGetU32(pSSM, &pThis->latch);
2811 pHlp->pfnSSMGetU8(pSSM, &pThis->sr_index);
2812 pHlp->pfnSSMGetMem(pSSM, pThis->sr, 8);
2813 pHlp->pfnSSMGetU8(pSSM, &pThis->gr_index);
2814 pHlp->pfnSSMGetMem(pSSM, pThis->gr, 16);
2815 pHlp->pfnSSMGetU8(pSSM, &pThis->ar_index);
2816 pHlp->pfnSSMGetMem(pSSM, pThis->ar, 21);
2817 pHlp->pfnSSMGetS32(pSSM, &pThis->ar_flip_flop);
2818 pHlp->pfnSSMGetU8(pSSM, &pThis->cr_index);
2819 pHlp->pfnSSMGetMem(pSSM, pThis->cr, 256);
2820 pHlp->pfnSSMGetU8(pSSM, &pThis->msr);
2821 pHlp->pfnSSMGetU8(pSSM, &pThis->fcr);
2822 pHlp->pfnSSMGetU8(pSSM, &pThis->st00);
2823 pHlp->pfnSSMGetU8(pSSM, &pThis->st01);
2824
2825 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_state);
2826 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_sub_index);
2827 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_read_index);
2828 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_write_index);
2829 pHlp->pfnSSMGetMem(pSSM, pThis->dac_cache, 3);
2830 pHlp->pfnSSMGetMem(pSSM, pThis->palette, 768);
2831
2832 pHlp->pfnSSMGetS32(pSSM, &pThis->bank_offset);
2833 pHlp->pfnSSMGetU8(pSSM, &u8);
2834 is_vbe = !!u8;
2835#ifdef CONFIG_BOCHS_VBE
2836 if (!is_vbe)
2837 {
2838 Log(("vga_load: !is_vbe !!\n"));
2839 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2840 }
2841
2842 if (u8 == 1)
2843 u8 = VBE_DISPI_INDEX_NB_SAVED; /* Used to save so many registers. */
2844 if (u8 > RT_ELEMENTS(pThis->vbe_regs))
2845 {
2846 Log(("vga_load: saved %d, expected %d!!\n", u8, RT_ELEMENTS(pThis->vbe_regs)));
2847 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2848 }
2849
2850 pHlp->pfnSSMGetU16(pSSM, &pThis->vbe_index);
2851 for(i = 0; i < (int)u8; i++)
2852 pHlp->pfnSSMGetU16(pSSM, &pThis->vbe_regs[i]);
2853 if (version_id <= VGA_SAVEDSTATE_VERSION_INV_VHEIGHT)
2854 recalculate_data(pThis); /* <- re-calculate the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] since it might be invalid */
2855 pHlp->pfnSSMGetU32(pSSM, &pThis->vbe_start_addr);
2856 pHlp->pfnSSMGetU32(pSSM, &pThis->vbe_line_offset);
2857 if (version_id < 2)
2858 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
2859 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
2860#else
2861 if (is_vbe)
2862 {
2863 Log(("vga_load: is_vbe !!\n"));
2864 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2865 }
2866#endif
2867
2868 /* force refresh */
2869 pThis->graphic_mode = -1;
2870 return 0;
2871}
2872
2873
2874/**
2875 * Worker for vgaR3Construct().
2876 */
2877static void vgaR3InitExpand(void)
2878{
2879 int i, j, v, b;
2880
2881 for(i = 0;i < 256; i++) {
2882 v = 0;
2883 for(j = 0; j < 8; j++) {
2884 v |= ((i >> j) & 1) << (j * 4);
2885 }
2886 expand4[i] = v;
2887
2888 v = 0;
2889 for(j = 0; j < 4; j++) {
2890 v |= ((i >> (2 * j)) & 3) << (j * 4);
2891 }
2892 expand2[i] = v;
2893 }
2894 for(i = 0; i < 16; i++) {
2895 v = 0;
2896 for(j = 0; j < 4; j++) {
2897 b = ((i >> j) & 1);
2898 v |= b << (2 * j);
2899 v |= b << (2 * j + 1);
2900 }
2901 expand4to8[i] = v;
2902 }
2903}
2904
2905#endif /* IN_RING3 */
2906
2907
2908
2909/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2910
2911#define VGA_IOPORT_WRITE_PLACEHOLDER(a_uPort, a_cPorts) do {\
2912 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); \
2913 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo))); \
2914 AssertCompile(RT_IS_POWER_OF_TWO(a_cPorts)); \
2915 Assert((unsigned)offPort - (unsigned)(a_uPort) < (unsigned)(a_cPorts)); \
2916 NOREF(pvUser); \
2917 if (cb == 1) \
2918 vga_ioport_write(pDevIns, pThis, offPort, u32); \
2919 else if (cb == 2) \
2920 { \
2921 vga_ioport_write(pDevIns, pThis, offPort, u32 & 0xff); \
2922 vga_ioport_write(pDevIns, pThis, offPort + 1, u32 >> 8); \
2923 } \
2924 return VINF_SUCCESS; \
2925 } while (0)
2926
2927#define VGA_IOPORT_READ_PLACEHOLDER(a_uPort, a_cPorts) do {\
2928 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); \
2929 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo))); \
2930 AssertCompile(RT_IS_POWER_OF_TWO(a_cPorts)); \
2931 Assert((unsigned)offPort - (unsigned)(a_uPort) < (unsigned)(a_cPorts)); \
2932 NOREF(pvUser); \
2933 if (cb == 1) \
2934 *pu32 = vga_ioport_read(pDevIns, pThis, offPort); \
2935 else if (cb == 2) \
2936 { \
2937 uint32_t u32 = vga_ioport_read(pDevIns, pThis, offPort); \
2938 u32 |= vga_ioport_read(pDevIns, pThis, offPort + 1) << 8; \
2939 *pu32 = u32; \
2940 } \
2941 else \
2942 return VERR_IOM_IOPORT_UNUSED; \
2943 return VINF_SUCCESS; \
2944 } while (0)
2945
2946/**
2947 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c0-0x3c1 Attribute Controller.}
2948 */
2949static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortArWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2950{
2951 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c0, 2);
2952}
2953
2954/**
2955 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c0-0x3c1 Attribute Controller.}
2956 */
2957static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortArRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2958{
2959 VGA_IOPORT_READ_PLACEHOLDER(0x3c0, 2);
2960}
2961
2962
2963/**
2964 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c2 Miscellaneous Register.}
2965 */
2966static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMsrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2967{
2968 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c2, 1);
2969}
2970
2971/**
2972 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c2 Status register 0.}
2973 */
2974static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSt00Read(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2975{
2976 VGA_IOPORT_READ_PLACEHOLDER(0x3c2, 1);
2977}
2978
2979
2980/**
2981 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c3 Unused.}
2982 */
2983static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortUnusedWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2984{
2985 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c3, 1);
2986}
2987
2988/**
2989 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c3 Unused.}
2990 */
2991static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortUnusedRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2992{
2993 VGA_IOPORT_READ_PLACEHOLDER(0x3c3, 1);
2994}
2995
2996
2997/**
2998 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c4-0x3c5 Sequencer.}
2999 */
3000static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3001{
3002 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c4, 2);
3003}
3004
3005/**
3006 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c4-0x3c5 Sequencer.}
3007 */
3008static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3009{
3010 VGA_IOPORT_READ_PLACEHOLDER(0x3c4, 2);
3011}
3012
3013
3014/**
3015 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c6-0x3c9 DAC.}
3016 */
3017static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortDacWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3018{
3019 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c6, 4);
3020}
3021
3022/**
3023 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c6-0x3c9 DAC.}
3024 */
3025static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortDacRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3026{
3027 VGA_IOPORT_READ_PLACEHOLDER(0x3c6, 4);
3028}
3029
3030
3031/**
3032 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ca-0x3cd Graphics Position?}
3033 */
3034static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortPosWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3035{
3036 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ca, 4);
3037}
3038
3039/**
3040 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ca-0x3cd Graphics Position?}
3041 */
3042static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortPosRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3043{
3044 VGA_IOPORT_READ_PLACEHOLDER(0x3ca, 4);
3045}
3046
3047
3048/**
3049 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ce-0x3cf Graphics Controller.}
3050 */
3051static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortGrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3052{
3053 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ce, 2);
3054}
3055
3056/**
3057 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ca-0x3cf Graphics Controller.}
3058 */
3059static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortGrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3060{
3061 VGA_IOPORT_READ_PLACEHOLDER(0x3ce, 2);
3062}
3063
3064
3065/**
3066 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3b4-0x3b5 MDA CRT control.}
3067 */
3068static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaCrtWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3069{
3070 /** @todo do vga_ioport_invalid here */
3071 VGA_IOPORT_WRITE_PLACEHOLDER(0x3b4, 2);
3072}
3073
3074/**
3075 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3b4-0x3b5 MDA CRT control.}
3076 */
3077static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaCrtRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3078{
3079 /** @todo do vga_ioport_invalid here */
3080 VGA_IOPORT_READ_PLACEHOLDER(0x3b4, 2);
3081}
3082
3083
3084/**
3085 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ba MDA feature/status.}
3086 */
3087static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaFcrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3088{
3089 /** @todo do vga_ioport_invalid here */
3090 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ba, 1);
3091}
3092
3093/**
3094 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ba MDA feature/status.}
3095 */
3096static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaStRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3097{
3098 /** @todo do vga_ioport_invalid here */
3099 VGA_IOPORT_READ_PLACEHOLDER(0x3ba, 1);
3100}
3101
3102
3103/**
3104 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3d4-0x3d5 CGA CRT control.}
3105 */
3106static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaCrtWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3107{
3108 /** @todo do vga_ioport_invalid here */
3109 VGA_IOPORT_WRITE_PLACEHOLDER(0x3d4, 2);
3110}
3111
3112/**
3113 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3d4-0x3d5 CGA CRT control.}
3114 */
3115static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaCrtRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3116{
3117 /** @todo do vga_ioport_invalid here */
3118 VGA_IOPORT_READ_PLACEHOLDER(0x3d4, 2);
3119}
3120
3121
3122/**
3123 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3da CGA feature/status.}
3124 */
3125static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaFcrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3126{
3127 /** @todo do vga_ioport_invalid here */
3128 VGA_IOPORT_WRITE_PLACEHOLDER(0x3da, 1);
3129}
3130
3131/**
3132 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3da CGA feature/status.}
3133 */
3134static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaStRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3135{
3136 /** @todo do vga_ioport_invalid here */
3137 VGA_IOPORT_READ_PLACEHOLDER(0x3da, 1);
3138}
3139
3140
3141/**
3142 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Data Port OUT handler (0x1ce).}
3143 */
3144static DECLCALLBACK(VBOXSTRICTRC)
3145vgaIoPortWriteVbeData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3146{
3147 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3148 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3149 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3150
3151 NOREF(pvUser);
3152
3153#ifndef IN_RING3
3154 /*
3155 * This has to be done on the host in order to execute the connector callbacks.
3156 */
3157 if ( pThis->vbe_index == VBE_DISPI_INDEX_ENABLE
3158 || pThis->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
3159 {
3160 Log(("vgaIoPortWriteVbeData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
3161 return VINF_IOM_R3_IOPORT_WRITE;
3162 }
3163#endif
3164#ifdef VBE_BYTEWISE_IO
3165 if (cb == 1)
3166 {
3167 if (!pThis->fWriteVBEData)
3168 {
3169 if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
3170 && (u32 & VBE_DISPI_ENABLED))
3171 {
3172 pThis->fWriteVBEData = false;
3173 return vbe_ioport_write_data(pDevIns, pThis, pThisCC, offPort, u32 & 0xFF);
3174 }
3175
3176 pThis->cbWriteVBEData = u32 & 0xFF;
3177 pThis->fWriteVBEData = true;
3178 return VINF_SUCCESS;
3179 }
3180
3181 u32 = (pThis->cbWriteVBEData << 8) | (u32 & 0xFF);
3182 pThis->fWriteVBEData = false;
3183 cb = 2;
3184 }
3185#endif
3186 if (cb == 2 || cb == 4)
3187 return vbe_ioport_write_data(pDevIns, pThis, pThisCC, offPort, u32);
3188 AssertMsgFailed(("vgaIoPortWriteVbeData: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3189
3190 return VINF_SUCCESS;
3191}
3192
3193
3194/**
3195 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Index Port OUT handler (0x1ce).}
3196 */
3197static DECLCALLBACK(VBOXSTRICTRC)
3198vgaIoPortWriteVbeIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3199{
3200 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
3201 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3202
3203#ifdef VBE_BYTEWISE_IO
3204 if (cb == 1)
3205 {
3206 if (!pThis->fWriteVBEIndex)
3207 {
3208 pThis->cbWriteVBEIndex = u32 & 0x00FF;
3209 pThis->fWriteVBEIndex = true;
3210 return VINF_SUCCESS;
3211 }
3212 pThis->fWriteVBEIndex = false;
3213 vbe_ioport_write_index(pThis, offPort, (pThis->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
3214 return VINF_SUCCESS;
3215 }
3216#endif
3217
3218 if (cb == 2)
3219 vbe_ioport_write_index(pThis, offPort, u32);
3220 else
3221 ASSERT_GUEST_MSG_FAILED(("vgaIoPortWriteVbeIndex: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3222 return VINF_SUCCESS;
3223}
3224
3225
3226/**
3227 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Data Port IN handler (0x1cf).}
3228 */
3229static DECLCALLBACK(VBOXSTRICTRC)
3230vgaIoPortReadVbeData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3231{
3232 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
3233 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3234
3235#ifdef VBE_BYTEWISE_IO
3236 if (cb == 1)
3237 {
3238 if (!pThis->fReadVBEData)
3239 {
3240 *pu32 = (vbe_ioport_read_data(pThis, offPort) >> 8) & 0xFF;
3241 pThis->fReadVBEData = true;
3242 return VINF_SUCCESS;
3243 }
3244 *pu32 = vbe_ioport_read_data(pThis, offPort) & 0xFF;
3245 pThis->fReadVBEData = false;
3246 return VINF_SUCCESS;
3247 }
3248#endif
3249 if (cb == 2)
3250 {
3251 *pu32 = vbe_ioport_read_data(pThis, offPort);
3252 return VINF_SUCCESS;
3253 }
3254 if (cb == 4)
3255 {
3256 if (pThis->vbe_regs[VBE_DISPI_INDEX_ID] == VBE_DISPI_ID_CFG)
3257 *pu32 = vbe_ioport_read_data(pThis, offPort); /* New interface. */
3258 else
3259 *pu32 = pThis->vram_size; /* Quick hack for getting the vram size. */
3260 return VINF_SUCCESS;
3261 }
3262 AssertMsgFailed(("vgaIoPortReadVbeData: offPort=%#x cb=%d\n", offPort, cb));
3263 return VERR_IOM_IOPORT_UNUSED;
3264}
3265
3266
3267/**
3268 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Index Port IN handler (0x1cf).}
3269 */
3270static DECLCALLBACK(VBOXSTRICTRC)
3271vgaIoPortReadVbeIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3272{
3273 NOREF(pvUser);
3274 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3275 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3276
3277#ifdef VBE_BYTEWISE_IO
3278 if (cb == 1)
3279 {
3280 if (!pThis->fReadVBEIndex)
3281 {
3282 *pu32 = (vbe_ioport_read_index(pThis, offPort) >> 8) & 0xFF;
3283 pThis->fReadVBEIndex = true;
3284 return VINF_SUCCESS;
3285 }
3286 *pu32 = vbe_ioport_read_index(pThis, offPort) & 0xFF;
3287 pThis->fReadVBEIndex = false;
3288 return VINF_SUCCESS;
3289 }
3290#endif
3291 if (cb == 2)
3292 {
3293 *pu32 = vbe_ioport_read_index(pThis, offPort);
3294 return VINF_SUCCESS;
3295 }
3296 AssertMsgFailed(("vgaIoPortReadVbeIndex: offPort=%#x cb=%d\n", offPort, cb));
3297 return VERR_IOM_IOPORT_UNUSED;
3298}
3299
3300#if defined(VBOX_WITH_HGSMI) && defined(IN_RING3)
3301
3302/**
3303 * @callback_method_impl{FNIOMIOPORTNEWOUT,HGSMI OUT handler.}
3304 */
3305static DECLCALLBACK(VBOXSTRICTRC)
3306vgaR3IOPortHgsmiWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3307{
3308 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3309 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3310 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3311 LogFlowFunc(("offPort=0x%x u32=0x%x cb=%u\n", offPort, u32, cb));
3312
3313 NOREF(pvUser);
3314
3315 if (cb == 4)
3316 {
3317 switch (offPort)
3318 {
3319 case VGA_PORT_HGSMI_HOST: /* Host */
3320 {
3321# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
3322 if (u32 == HGSMIOFFSET_VOID)
3323 {
3324 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSectIRQ, VERR_SEM_BUSY);
3325 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSectIRQ, rcLock);
3326
3327 if (pThis->fu32PendingGuestFlags == 0)
3328 {
3329 PDMDevHlpPCISetIrqNoWait(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
3330 HGSMIClearHostGuestFlags(pThisCC->pHGSMI,
3331 HGSMIHOSTFLAGS_IRQ
3332 | HGSMIHOSTFLAGS_VSYNC
3333 | HGSMIHOSTFLAGS_HOTPLUG
3334 | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES);
3335 }
3336 else
3337 {
3338 HGSMISetHostGuestFlags(pThisCC->pHGSMI, HGSMIHOSTFLAGS_IRQ | pThis->fu32PendingGuestFlags);
3339 pThis->fu32PendingGuestFlags = 0;
3340 /* Keep the IRQ unchanged. */
3341 }
3342
3343 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSectIRQ);
3344 }
3345 else
3346# endif
3347 {
3348 HGSMIHostWrite(pThisCC->pHGSMI, u32);
3349 }
3350 break;
3351 }
3352
3353 case VGA_PORT_HGSMI_GUEST: /* Guest */
3354 HGSMIGuestWrite(pThisCC->pHGSMI, u32);
3355 break;
3356
3357 default:
3358# ifdef DEBUG_sunlover
3359 AssertMsgFailed(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3360# endif
3361 break;
3362 }
3363 }
3364 else
3365 {
3366 /** @todo r=bird: According to Ralf Brown, one and two byte accesses to the
3367 * 0x3b0-0x3b1 and 0x3b2-0x3b3 I/O port pairs should work the same as
3368 * 0x3b4-0x3b5 (MDA CRT control). */
3369 Log(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x - possible valid MDA CRT access\n", offPort, cb, u32));
3370# ifdef DEBUG_sunlover
3371 AssertMsgFailed(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3372# endif
3373 STAM_REL_COUNTER_INC(&pThis->StatHgsmiMdaCgaAccesses);
3374 }
3375
3376 return VINF_SUCCESS;
3377}
3378
3379
3380/**
3381 * @callback_method_impl{FNIOMIOPORTNEWOUT,HGSMI IN handler.}
3382 */
3383static DECLCALLBACK(VBOXSTRICTRC)
3384vgaR3IOPortHgmsiRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3385{
3386 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3387 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3388 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3389 LogFlowFunc(("offPort=0x%x cb=%d\n", offPort, cb));
3390
3391 NOREF(pvUser);
3392
3393 VBOXSTRICTRC rc = VINF_SUCCESS;
3394 if (cb == 4)
3395 {
3396 switch (offPort)
3397 {
3398 case VGA_PORT_HGSMI_HOST: /* Host */
3399 *pu32 = HGSMIHostRead(pThisCC->pHGSMI);
3400 break;
3401 case VGA_PORT_HGSMI_GUEST: /* Guest */
3402 *pu32 = HGSMIGuestRead(pThisCC->pHGSMI);
3403 break;
3404 default:
3405 rc = VERR_IOM_IOPORT_UNUSED;
3406 break;
3407 }
3408 }
3409 else
3410 {
3411 /** @todo r=bird: According to Ralf Brown, one and two byte accesses to the
3412 * 0x3b0-0x3b1 and 0x3b2-0x3b3 I/O port pairs should work the same as
3413 * 0x3b4-0x3b5 (MDA CRT control). */
3414 Log(("vgaR3IOPortHgmsiRead: offPort=%#x cb=%d - possible valid MDA CRT access\n", offPort, cb));
3415 STAM_REL_COUNTER_INC(&pThis->StatHgsmiMdaCgaAccesses);
3416 rc = VERR_IOM_IOPORT_UNUSED;
3417 }
3418
3419 return rc;
3420}
3421
3422#endif /* VBOX_WITH_HGSMI && IN_RING3*/
3423
3424
3425
3426
3427/* -=-=-=-=-=- All Contexts -=-=-=-=-=- */
3428
3429/**
3430 * @internal. For use inside VGAGCMemoryFillWrite only.
3431 * Macro for apply logical operation and bit mask.
3432 */
3433#define APPLY_LOGICAL_AND_MASK(pThis, val, bit_mask) \
3434 /* apply logical operation */ \
3435 switch (pThis->gr[3] >> 3)\
3436 { \
3437 case 0: \
3438 default:\
3439 /* nothing to do */ \
3440 break; \
3441 case 1: \
3442 /* and */ \
3443 val &= pThis->latch; \
3444 break; \
3445 case 2: \
3446 /* or */ \
3447 val |= pThis->latch; \
3448 break; \
3449 case 3: \
3450 /* xor */ \
3451 val ^= pThis->latch; \
3452 break; \
3453 } \
3454 /* apply bit mask */ \
3455 val = (val & bit_mask) | (pThis->latch & ~bit_mask)
3456
3457/**
3458 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3459 * This is the advanced version of vga_mem_writeb function.
3460 *
3461 * @returns VBox status code.
3462 * @param pThis The shared VGA instance data.
3463 * @param pThisCC The VGA instance data for the current context.
3464 * @param pvUser User argument - ignored.
3465 * @param GCPhysAddr Physical address of memory to write.
3466 * @param u32Item Data to write, up to 4 bytes.
3467 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3468 * @param cItems Number of data items to write.
3469 */
3470static int vgaInternalMMIOFill(PVGASTATE pThis, PVGASTATECC pThisCC, void *pvUser, RTGCPHYS GCPhysAddr,
3471 uint32_t u32Item, unsigned cbItem, unsigned cItems)
3472{
3473 uint32_t b;
3474 uint32_t write_mask, bit_mask, set_mask;
3475 uint32_t aVal[4];
3476 unsigned i;
3477 NOREF(pvUser);
3478
3479 for (i = 0; i < cbItem; i++)
3480 {
3481 aVal[i] = u32Item & 0xff;
3482 u32Item >>= 8;
3483 }
3484
3485 /* convert to VGA memory offset */
3486 /// @todo add check for the end of region
3487 GCPhysAddr &= 0x1ffff;
3488 switch((pThis->gr[6] >> 2) & 3) {
3489 case 0:
3490 break;
3491 case 1:
3492 if (GCPhysAddr >= 0x10000)
3493 return VINF_SUCCESS;
3494 GCPhysAddr += pThis->bank_offset;
3495 break;
3496 case 2:
3497 GCPhysAddr -= 0x10000;
3498 if (GCPhysAddr >= 0x8000)
3499 return VINF_SUCCESS;
3500 break;
3501 default:
3502 case 3:
3503 GCPhysAddr -= 0x18000;
3504 if (GCPhysAddr >= 0x8000)
3505 return VINF_SUCCESS;
3506 break;
3507 }
3508
3509 if (pThis->sr[4] & 0x08) {
3510 /* chain 4 mode : simplest access */
3511 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3512
3513 while (cItems-- > 0)
3514 for (i = 0; i < cbItem; i++)
3515 {
3516 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3517 {
3518 pThisCC->pbVRam[GCPhysAddr] = aVal[i];
3519 vgaR3MarkDirty(pThis, GCPhysAddr);
3520 }
3521 GCPhysAddr++;
3522 }
3523 } else if (pThis->gr[5] & 0x10) {
3524 /* odd/even mode (aka text mode mapping) */
3525 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3526 while (cItems-- > 0)
3527 for (i = 0; i < cbItem; i++)
3528 {
3529 unsigned plane = GCPhysAddr & 1;
3530 if (pThis->sr[2] & (1 << plane)) {
3531 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) * 4) | plane;
3532 pThisCC->pbVRam[PhysAddr2] = aVal[i];
3533 vgaR3MarkDirty(pThis, PhysAddr2);
3534 }
3535 GCPhysAddr++;
3536 }
3537 } else {
3538 /* standard VGA latched access */
3539 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3540
3541 switch(pThis->gr[5] & 3) {
3542 default:
3543 case 0:
3544 /* rotate */
3545 b = pThis->gr[3] & 7;
3546 bit_mask = pThis->gr[8];
3547 bit_mask |= bit_mask << 8;
3548 bit_mask |= bit_mask << 16;
3549 set_mask = mask16[pThis->gr[1]];
3550
3551 for (i = 0; i < cbItem; i++)
3552 {
3553 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3554 aVal[i] |= aVal[i] << 8;
3555 aVal[i] |= aVal[i] << 16;
3556
3557 /* apply set/reset mask */
3558 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3559
3560 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3561 }
3562 break;
3563 case 1:
3564 for (i = 0; i < cbItem; i++)
3565 aVal[i] = pThis->latch;
3566 break;
3567 case 2:
3568 bit_mask = pThis->gr[8];
3569 bit_mask |= bit_mask << 8;
3570 bit_mask |= bit_mask << 16;
3571 for (i = 0; i < cbItem; i++)
3572 {
3573 aVal[i] = mask16[aVal[i] & 0x0f];
3574
3575 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3576 }
3577 break;
3578 case 3:
3579 /* rotate */
3580 b = pThis->gr[3] & 7;
3581
3582 for (i = 0; i < cbItem; i++)
3583 {
3584 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3585 bit_mask = pThis->gr[8] & aVal[i];
3586 bit_mask |= bit_mask << 8;
3587 bit_mask |= bit_mask << 16;
3588 aVal[i] = mask16[pThis->gr[0]];
3589
3590 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3591 }
3592 break;
3593 }
3594
3595 /* mask data according to sr[2] */
3596 write_mask = mask16[pThis->sr[2]];
3597
3598 /* actually write data */
3599 if (cbItem == 1)
3600 {
3601 /* The most frequently case is 1 byte I/O. */
3602 while (cItems-- > 0)
3603 {
3604 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3605 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3606 GCPhysAddr++;
3607 }
3608 }
3609 else if (cbItem == 2)
3610 {
3611 /* The second case is 2 bytes I/O. */
3612 while (cItems-- > 0)
3613 {
3614 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3615 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3616 GCPhysAddr++;
3617
3618 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3619 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3620 GCPhysAddr++;
3621 }
3622 }
3623 else
3624 {
3625 /* And the rest is 4 bytes. */
3626 Assert(cbItem == 4);
3627 while (cItems-- > 0)
3628 for (i = 0; i < cbItem; i++)
3629 {
3630 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3631 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3632 GCPhysAddr++;
3633 }
3634 }
3635 }
3636 return VINF_SUCCESS;
3637}
3638
3639#undef APPLY_LOGICAL_AND_MASK
3640
3641/**
3642 * @callback_method_impl{FNIOMMMIONEWFILL,
3643 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM and
3644 * from the inside of VGADeviceGC.cpp. This is the advanced version of
3645 * vga_mem_writeb function.}
3646 */
3647static DECLCALLBACK(VBOXSTRICTRC)
3648vgaMmioFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3649{
3650 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3651 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3652 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3653
3654 return vgaInternalMMIOFill(pThis, pThisCC, pvUser, off, u32Item, cbItem, cItems);
3655}
3656
3657
3658/**
3659 * @callback_method_impl{FNIOMMMIONEWREAD,
3660 * Legacy VGA memory (0xa0000 - 0xbffff) read hook\, to be called from IOM.}
3661 *
3662 * @note The @a off is an absolute address in the 0xa0000 - 0xbffff range, not
3663 * an offset.
3664 */
3665static DECLCALLBACK(VBOXSTRICTRC) vgaMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3666{
3667 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3668 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3669 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3670 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3671 NOREF(pvUser);
3672
3673 int rc = VINF_SUCCESS;
3674 switch (cb)
3675 {
3676 case 1:
3677 *(uint8_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc);
3678 break;
3679 case 2:
3680/** @todo This and the wider accesses maybe misbehave when accessing bytes
3681 * crossing the 512KB VRAM boundrary if the access is handled in
3682 * ring-0 and operating in latched mode. */
3683 *(uint16_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3684 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8);
3685 break;
3686 case 4:
3687 *(uint32_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3688 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8)
3689 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 2, &rc) << 16)
3690 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 3, &rc) << 24);
3691 break;
3692
3693 case 8:
3694 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3695 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8)
3696 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 2, &rc) << 16)
3697 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 3, &rc) << 24)
3698 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 4, &rc) << 32)
3699 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 5, &rc) << 40)
3700 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 6, &rc) << 48)
3701 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 7, &rc) << 56);
3702 break;
3703
3704 default:
3705 {
3706 uint8_t *pbData = (uint8_t *)pv;
3707 while (cb-- > 0)
3708 {
3709 *pbData++ = vga_mem_readb(pDevIns, pThis, pThisCC, off++, &rc);
3710 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3711 break;
3712 }
3713 }
3714 }
3715
3716 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3717 return rc;
3718}
3719
3720/**
3721 * @callback_method_impl{FNIOMMMIONEWWRITE,
3722 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM.}
3723 *
3724 * @note The @a off is an absolute address in the 0xa0000 - 0xbffff range, not
3725 * an offset.
3726 */
3727static DECLCALLBACK(VBOXSTRICTRC) vgaMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3728{
3729 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3730 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3731 uint8_t const *pbSrc = (uint8_t const *)pv;
3732 NOREF(pvUser);
3733 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3734 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3735
3736 VBOXSTRICTRC rc;
3737 switch (cb)
3738 {
3739 case 1:
3740 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off, *pbSrc);
3741 break;
3742#if 1
3743 case 2:
3744 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3745 if (RT_LIKELY(rc == VINF_SUCCESS))
3746 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3747 break;
3748 case 4:
3749 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3750 if (RT_LIKELY(rc == VINF_SUCCESS))
3751 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3752 if (RT_LIKELY(rc == VINF_SUCCESS))
3753 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 2, pbSrc[2]);
3754 if (RT_LIKELY(rc == VINF_SUCCESS))
3755 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 3, pbSrc[3]);
3756 break;
3757 case 8:
3758 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3759 if (RT_LIKELY(rc == VINF_SUCCESS))
3760 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3761 if (RT_LIKELY(rc == VINF_SUCCESS))
3762 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 2, pbSrc[2]);
3763 if (RT_LIKELY(rc == VINF_SUCCESS))
3764 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 3, pbSrc[3]);
3765 if (RT_LIKELY(rc == VINF_SUCCESS))
3766 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 4, pbSrc[4]);
3767 if (RT_LIKELY(rc == VINF_SUCCESS))
3768 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 5, pbSrc[5]);
3769 if (RT_LIKELY(rc == VINF_SUCCESS))
3770 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 6, pbSrc[6]);
3771 if (RT_LIKELY(rc == VINF_SUCCESS))
3772 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 7, pbSrc[7]);
3773 break;
3774#else
3775 case 2:
3776 rc = vgaMmioFill(pDevIns, off, *(uint16_t *)pv, 2, 1);
3777 break;
3778 case 4:
3779 rc = vgaMmioFill(pDevIns, off, *(uint32_t *)pv, 4, 1);
3780 break;
3781 case 8:
3782 rc = vgaMmioFill(pDevIns, off, *(uint64_t *)pv, 8, 1);
3783 break;
3784#endif
3785 default:
3786 rc = VINF_SUCCESS;
3787 while (cb-- > 0 && rc == VINF_SUCCESS)
3788 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off++, *pbSrc++);
3789 break;
3790
3791 }
3792 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3793 return rc;
3794}
3795
3796
3797/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3798
3799/**
3800 * @callback_method_impl{FNIOMIOPORTNEWIN,
3801 * Port I/O Handler for VGA BIOS IN operations.}
3802 */
3803static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortReadBios(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3804{
3805 RT_NOREF(pDevIns, pvUser, offPort, pu32, cb);
3806 return VERR_IOM_IOPORT_UNUSED;
3807}
3808
3809/**
3810 * @callback_method_impl{FNIOMIOPORTNEWOUT,
3811 * Port I/O Handler for VGA BIOS IN operations.}
3812 */
3813static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortWriteBios(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3814{
3815 RT_NOREF2(pDevIns, pvUser);
3816 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3817 Assert(offPort == 0); RT_NOREF(offPort);
3818
3819 /*
3820 * VGA BIOS char printing.
3821 */
3822 if (cb == 1)
3823 {
3824#if 0
3825 switch (u32)
3826 {
3827 case '\r': Log(("vgabios: <return>\n")); break;
3828 case '\n': Log(("vgabios: <newline>\n")); break;
3829 case '\t': Log(("vgabios: <tab>\n")); break;
3830 default:
3831 Log(("vgabios: %c\n", u32));
3832 }
3833#else
3834 static int s_fLastWasNotNewline = 0; /* We are only called in a single-threaded way */
3835 if (s_fLastWasNotNewline == 0)
3836 Log(("vgabios: "));
3837 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3838 Log(("%c", u32));
3839 if (u32 == '\n')
3840 s_fLastWasNotNewline = 0;
3841 else
3842 s_fLastWasNotNewline = 1;
3843#endif
3844 return VINF_SUCCESS;
3845 }
3846
3847 /* not in use. */
3848 return VERR_IOM_IOPORT_UNUSED;
3849}
3850
3851
3852/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3853
3854#ifdef IN_RING3
3855
3856/**
3857 * @callback_method_impl{FNIOMIOPORTNEWOUT,
3858 * Port I/O Handler for VBE Extra OUT operations.}
3859 */
3860static DECLCALLBACK(VBOXSTRICTRC)
3861vbeR3IOPortWriteVbeExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3862{
3863 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3864 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3865 RT_NOREF(offPort, pvUser);
3866
3867 if (cb == 2)
3868 {
3869 Log(("vbeR3IOPortWriteVbeExtra: addr=%#RX32\n", u32));
3870 pThisCC->u16VBEExtraAddress = u32;
3871 }
3872 else
3873 Log(("vbeR3IOPortWriteVbeExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3874
3875 return VINF_SUCCESS;
3876}
3877
3878
3879/**
3880 * @callback_method_impl{FNIOMIOPORTNEWIN,
3881 * Port I/O Handler for VBE Extra IN operations.}
3882 */
3883static DECLCALLBACK(VBOXSTRICTRC)
3884vbeR3IoPortReadVbeExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3885{
3886 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3887 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3888 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3889 RT_NOREF(offPort, pvUser);
3890
3891 int rc = VINF_SUCCESS;
3892 if (pThisCC->u16VBEExtraAddress == 0xffff)
3893 {
3894 Log(("vbeR3IoPortReadVbeExtra: Requested number of 64k video banks\n"));
3895 *pu32 = pThis->vram_size / _64K;
3896 }
3897 else if ( pThisCC->u16VBEExtraAddress >= pThisCC->cbVBEExtraData
3898 || pThisCC->u16VBEExtraAddress + cb > pThisCC->cbVBEExtraData)
3899 {
3900 *pu32 = 0;
3901 Log(("vbeR3IoPortReadVbeExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3902 pThisCC->u16VBEExtraAddress, pThisCC->u16VBEExtraAddress, pThisCC->cbVBEExtraData, pThisCC->cbVBEExtraData));
3903 }
3904 else
3905 {
3906 RT_UNTRUSTED_VALIDATED_FENCE();
3907 if (cb == 1)
3908 {
3909 *pu32 = pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress] & 0xFF;
3910
3911 Log(("vbeR3IoPortReadVbeExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3912 }
3913 else if (cb == 2)
3914 {
3915 *pu32 = pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress]
3916 | (uint32_t)pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress + 1] << 8;
3917
3918 Log(("vbeR3IoPortReadVbeExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3919 }
3920 else
3921 {
3922 Log(("vbeR3IoPortReadVbeExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3923 rc = VERR_IOM_IOPORT_UNUSED;
3924 }
3925 }
3926
3927 return rc;
3928}
3929
3930
3931/**
3932 * Parse the logo bitmap data at init time.
3933 *
3934 * @returns VBox status code.
3935 *
3936 * @param pThisCC The VGA instance data for ring-3.
3937 */
3938static int vbeR3ParseBitmap(PVGASTATECC pThisCC)
3939{
3940 /*
3941 * Get bitmap header data
3942 */
3943 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
3944 PBMPFILEHDR pFileHdr = (PBMPFILEHDR)(pThisCC->pbLogo + sizeof(LOGOHDR));
3945 PBMPWIN3XINFOHDR pCoreHdr = (PBMPWIN3XINFOHDR)(pThisCC->pbLogo + sizeof(LOGOHDR) + sizeof(BMPFILEHDR));
3946
3947 if (pFileHdr->uType == BMP_HDR_MAGIC)
3948 {
3949 switch (pCoreHdr->cbSize)
3950 {
3951 case BMP_HDR_SIZE_OS21:
3952 {
3953 PBMPOS2COREHDR pOs2Hdr = (PBMPOS2COREHDR)pCoreHdr;
3954 pThisCC->cxLogo = pOs2Hdr->uWidth;
3955 pThisCC->cyLogo = pOs2Hdr->uHeight;
3956 pThisCC->cLogoPlanes = pOs2Hdr->cPlanes;
3957 pThisCC->cLogoBits = pOs2Hdr->cBits;
3958 pThisCC->LogoCompression = BMP_COMPRESSION_TYPE_NONE;
3959 pThisCC->cLogoUsedColors = 0;
3960 break;
3961 }
3962
3963 case BMP_HDR_SIZE_OS22:
3964 {
3965 PBMPOS2COREHDR2 pOs22Hdr = (PBMPOS2COREHDR2)pCoreHdr;
3966 pThisCC->cxLogo = pOs22Hdr->uWidth;
3967 pThisCC->cyLogo = pOs22Hdr->uHeight;
3968 pThisCC->cLogoPlanes = pOs22Hdr->cPlanes;
3969 pThisCC->cLogoBits = pOs22Hdr->cBits;
3970 pThisCC->LogoCompression = pOs22Hdr->enmCompression;
3971 pThisCC->cLogoUsedColors = pOs22Hdr->cClrUsed;
3972 break;
3973 }
3974
3975 case BMP_HDR_SIZE_WIN3X:
3976 pThisCC->cxLogo = pCoreHdr->uWidth;
3977 pThisCC->cyLogo = pCoreHdr->uHeight;
3978 pThisCC->cLogoPlanes = pCoreHdr->cPlanes;
3979 pThisCC->cLogoBits = pCoreHdr->cBits;
3980 pThisCC->LogoCompression = pCoreHdr->enmCompression;
3981 pThisCC->cLogoUsedColors = pCoreHdr->cClrUsed;
3982 break;
3983
3984 default:
3985 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pCoreHdr->cbSize),
3986 VERR_INVALID_PARAMETER);
3987 break;
3988 }
3989
3990 AssertLogRelMsgReturn(pThisCC->cxLogo <= LOGO_MAX_WIDTH && pThisCC->cyLogo <= LOGO_MAX_HEIGHT,
3991 ("Bitmap %ux%u is too big.\n", pThisCC->cxLogo, pThisCC->cyLogo),
3992 VERR_INVALID_PARAMETER);
3993
3994 AssertLogRelMsgReturn(pThisCC->cLogoPlanes == 1,
3995 ("Bitmap planes %u != 1.\n", pThisCC->cLogoPlanes),
3996 VERR_INVALID_PARAMETER);
3997
3998 AssertLogRelMsgReturn(pThisCC->cLogoBits == 4 || pThisCC->cLogoBits == 8 || pThisCC->cLogoBits == 24,
3999 ("Unsupported %u depth.\n", pThisCC->cLogoBits),
4000 VERR_INVALID_PARAMETER);
4001
4002 AssertLogRelMsgReturn(pThisCC->cLogoUsedColors <= 256,
4003 ("Unsupported %u colors.\n", pThisCC->cLogoUsedColors),
4004 VERR_INVALID_PARAMETER);
4005
4006 AssertLogRelMsgReturn(pThisCC->LogoCompression == BMP_COMPRESSION_TYPE_NONE,
4007 ("Unsupported %u compression.\n", pThisCC->LogoCompression),
4008 VERR_INVALID_PARAMETER);
4009
4010 AssertLogRelMsgReturn(pLogoHdr->cbLogo > pFileHdr->offBits,
4011 ("Wrong bitmap data offset %u, cbLogo=%u.\n", pFileHdr->offBits, pLogoHdr->cbLogo),
4012 VERR_INVALID_PARAMETER);
4013
4014 uint32_t const cbFileData = pLogoHdr->cbLogo - pFileHdr->offBits;
4015 uint32_t cbImageData = (uint32_t)pThisCC->cxLogo * pThisCC->cyLogo * pThisCC->cLogoPlanes;
4016 if (pThisCC->cLogoBits == 4)
4017 cbImageData /= 2;
4018 else if (pThisCC->cLogoBits == 24)
4019 cbImageData *= 3;
4020 AssertLogRelMsgReturn(cbImageData <= cbFileData,
4021 ("Wrong BMP header data %u (cbLogo=%u offBits=%u)\n", cbImageData, pFileHdr->offBits, pLogoHdr->cbLogo),
4022 VERR_INVALID_PARAMETER);
4023
4024 AssertLogRelMsgReturn(pLogoHdr->cbLogo == pFileHdr->cbFileSize,
4025 ("Wrong bitmap file size %u, cbLogo=%u.\n", pFileHdr->cbFileSize, pLogoHdr->cbLogo),
4026 VERR_INVALID_PARAMETER);
4027
4028 /*
4029 * Read bitmap palette
4030 */
4031 if (!pThisCC->cLogoUsedColors)
4032 pThisCC->cLogoPalEntries = 1 << (pThisCC->cLogoPlanes * pThisCC->cLogoBits);
4033 else
4034 pThisCC->cLogoPalEntries = pThisCC->cLogoUsedColors;
4035
4036 if (pThisCC->cLogoPalEntries)
4037 {
4038 const uint8_t *pbPal = pThisCC->pbLogo + sizeof(LOGOHDR) + sizeof(BMPFILEHDR) + pCoreHdr->cbSize; /* ASSUMES Size location (safe) */
4039
4040 for (uint16_t i = 0; i < pThisCC->cLogoPalEntries; i++)
4041 {
4042 uint16_t j;
4043 uint32_t u32Pal = 0;
4044
4045 for (j = 0; j < 3; j++)
4046 {
4047 uint8_t b = *pbPal++;
4048 u32Pal <<= 8;
4049 u32Pal |= b;
4050 }
4051
4052 pbPal++; /* skip unused byte */
4053 pThisCC->au32LogoPalette[i] = u32Pal;
4054 }
4055 }
4056
4057 /*
4058 * Bitmap data offset
4059 */
4060 pThisCC->pbLogoBitmap = pThisCC->pbLogo + sizeof(LOGOHDR) + pFileHdr->offBits;
4061 }
4062 else
4063 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
4064
4065 return VINF_SUCCESS;
4066}
4067
4068
4069/**
4070 * Show logo bitmap data.
4071 *
4072 * @param cBits Logo depth.
4073 * @param xLogo Logo X position.
4074 * @param yLogo Logo Y position.
4075 * @param cxLogo Logo width.
4076 * @param cyLogo Logo height.
4077 * @param fInverse True if the bitmask is black on white (only for 1bpp)
4078 * @param iStep Fade in/fade out step.
4079 * @param pu32Palette Palette data.
4080 * @param pbSrc Source buffer.
4081 * @param pbDst Destination buffer.
4082 */
4083static void vbeR3ShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo,
4084 bool fInverse, uint8_t iStep, const uint32_t *pu32Palette, const uint8_t *pbSrc, uint8_t *pbDst)
4085{
4086 uint16_t i;
4087 size_t cbPadBytes = 0;
4088 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
4089 uint16_t cyLeft = cyLogo;
4090
4091 pbDst += xLogo * 4 + yLogo * cbLineDst;
4092
4093 switch (cBits)
4094 {
4095 case 1:
4096 pbDst += cyLogo * cbLineDst;
4097 cbPadBytes = 0;
4098 break;
4099
4100 case 4:
4101 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
4102 cbPadBytes = 0;
4103 else if ((cxLogo % 8) <= 2)
4104 cbPadBytes = 3;
4105 else if ((cxLogo % 8) <= 4)
4106 cbPadBytes = 2;
4107 else
4108 cbPadBytes = 1;
4109 break;
4110
4111 case 8:
4112 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
4113 break;
4114
4115 case 24:
4116 cbPadBytes = cxLogo % 4;
4117 break;
4118 }
4119
4120 uint8_t j = 0, c = 0;
4121
4122 while (cyLeft-- > 0)
4123 {
4124 uint8_t *pbTmpDst = pbDst;
4125
4126 if (cBits != 1)
4127 j = 0;
4128
4129 for (i = 0; i < cxLogo; i++)
4130 {
4131 switch (cBits)
4132 {
4133 case 1:
4134 {
4135 if (!j)
4136 c = *pbSrc++;
4137
4138 if (c & 1)
4139 {
4140 if (fInverse)
4141 {
4142 *pbTmpDst++ = 0;
4143 *pbTmpDst++ = 0;
4144 *pbTmpDst++ = 0;
4145 pbTmpDst++;
4146 }
4147 else
4148 {
4149 uint8_t pix = 0xFF * iStep / LOGO_SHOW_STEPS;
4150 *pbTmpDst++ = pix;
4151 *pbTmpDst++ = pix;
4152 *pbTmpDst++ = pix;
4153 pbTmpDst++;
4154 }
4155 }
4156 else
4157 pbTmpDst += 4;
4158 c >>= 1;
4159 j = (j + 1) % 8;
4160 break;
4161 }
4162
4163 case 4:
4164 {
4165 if (!j)
4166 c = *pbSrc++;
4167
4168 uint8_t pix = (c >> 4) & 0xF;
4169 c <<= 4;
4170
4171 uint32_t u32Pal = pu32Palette[pix];
4172
4173 pix = (u32Pal >> 16) & 0xFF;
4174 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4175 pix = (u32Pal >> 8) & 0xFF;
4176 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4177 pix = u32Pal & 0xFF;
4178 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4179 pbTmpDst++;
4180
4181 j = (j + 1) % 2;
4182 break;
4183 }
4184
4185 case 8:
4186 {
4187 uint32_t u32Pal = pu32Palette[*pbSrc++];
4188
4189 uint8_t pix = (u32Pal >> 16) & 0xFF;
4190 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4191 pix = (u32Pal >> 8) & 0xFF;
4192 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4193 pix = u32Pal & 0xFF;
4194 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4195 pbTmpDst++;
4196 break;
4197 }
4198
4199 case 24:
4200 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4201 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4202 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4203 pbTmpDst++;
4204 break;
4205 }
4206 }
4207
4208 pbDst -= cbLineDst;
4209 pbSrc += cbPadBytes;
4210 }
4211}
4212
4213
4214/**
4215 * @callback_method_impl{FNIOMIOPORTNEWOUT,
4216 * Port I/O Handler for BIOS Logo OUT operations.}
4217 */
4218static DECLCALLBACK(VBOXSTRICTRC)
4219vbeR3IoPortWriteCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
4220{
4221 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4222 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4223 RT_NOREF(pvUser, offPort);
4224
4225 Log(("vbeR3IoPortWriteCmdLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
4226
4227 if (cb == 2)
4228 {
4229 /* Get the logo command */
4230 switch (u32 & 0xFF00)
4231 {
4232 case LOGO_CMD_SET_OFFSET:
4233 pThisCC->offLogoData = u32 & 0xFF;
4234 break;
4235
4236 case LOGO_CMD_SHOW_BMP:
4237 {
4238 uint8_t iStep = u32 & 0xFF;
4239 const uint8_t *pbSrc = pThisCC->pbLogoBitmap;
4240 uint8_t *pbDst;
4241 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
4242 uint32_t offDirty = 0;
4243 uint16_t xLogo = (LOGO_MAX_WIDTH - pThisCC->cxLogo) / 2;
4244 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThisCC->cyLogo) / 2;
4245
4246 /* Check VRAM size */
4247 if (pThis->vram_size < LOGO_MAX_SIZE)
4248 break;
4249
4250 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4251 pbDst = pThisCC->pbVRam + LOGO_MAX_SIZE;
4252 else
4253 pbDst = pThisCC->pbVRam;
4254
4255 /* Clear screen - except on power on... */
4256 if (!pThisCC->fLogoClearScreen)
4257 {
4258 /* Clear vram */
4259 uint32_t *pu32Dst = (uint32_t *)pbDst;
4260 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4261 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4262 *pu32Dst++ = 0;
4263 pThisCC->fLogoClearScreen = true;
4264 }
4265
4266 /* Show the bitmap. */
4267 vbeR3ShowBitmap(pThisCC->cLogoBits, xLogo, yLogo,
4268 pThisCC->cxLogo, pThisCC->cyLogo,
4269 false, iStep, &pThisCC->au32LogoPalette[0],
4270 pbSrc, pbDst);
4271
4272 /* Show the 'Press F12...' text. */
4273 if (pLogoHdr->fu8ShowBootMenu == 2)
4274 vbeR3ShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4275 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4276 pThisCC->fBootMenuInverse, iStep, &pThisCC->au32LogoPalette[0],
4277 &g_abLogoF12BootText[0], pbDst);
4278
4279 /* Blit the offscreen buffer. */
4280 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4281 {
4282 uint32_t *pu32TmpDst = (uint32_t *)pThisCC->pbVRam;
4283 uint32_t *pu32TmpSrc = (uint32_t *)(pThisCC->pbVRam + LOGO_MAX_SIZE);
4284 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4285 {
4286 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4287 *pu32TmpDst++ = *pu32TmpSrc++;
4288 }
4289 }
4290
4291 /* Set the dirty flags. */
4292 while (offDirty <= LOGO_MAX_SIZE)
4293 {
4294 vgaR3MarkDirty(pThis, offDirty);
4295 offDirty += GUEST_PAGE_SIZE;
4296 }
4297 break;
4298 }
4299
4300 default:
4301 Log(("vbeR3IoPortWriteCmdLogo: invalid command %d\n", u32));
4302 pThisCC->LogoCommand = LOGO_CMD_NOP;
4303 break;
4304 }
4305
4306 return VINF_SUCCESS;
4307 }
4308
4309 Log(("vbeR3IoPortWriteCmdLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4310 return VINF_SUCCESS;
4311}
4312
4313
4314/**
4315 * @callback_method_impl{FNIOMIOPORTIN,
4316 * Port I/O Handler for BIOS Logo IN operations.}
4317 */
4318static DECLCALLBACK(VBOXSTRICTRC)
4319vbeR3IoPortReadCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
4320{
4321 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4322 RT_NOREF(pvUser, offPort);
4323
4324 if (pThisCC->offLogoData + cb > pThisCC->cbLogo)
4325 {
4326 Log(("vbeR3IoPortReadCmdLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4327 pThisCC->offLogoData, pThisCC->offLogoData, pThisCC->cbLogo, pThisCC->cbLogo));
4328 return VINF_SUCCESS;
4329 }
4330 RT_UNTRUSTED_VALIDATED_FENCE();
4331
4332 PCRTUINT64U p = (PCRTUINT64U)&pThisCC->pbLogo[pThisCC->offLogoData];
4333 switch (cb)
4334 {
4335 case 1: *pu32 = p->au8[0]; break;
4336 case 2: *pu32 = p->au16[0]; break;
4337 case 4: *pu32 = p->au32[0]; break;
4338 //case 8: *pu32 = p->au64[0]; break;
4339 default: AssertFailed(); break;
4340 }
4341 Log(("vbeR3IoPortReadCmdLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThisCC->offLogoData, pThisCC->offLogoData, cb, cb, pu32));
4342
4343 pThisCC->LogoCommand = LOGO_CMD_NOP;
4344 pThisCC->offLogoData += cb;
4345
4346 return VINF_SUCCESS;
4347}
4348
4349
4350/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4351
4352/**
4353 * @callback_method_impl{FNDBGFHANDLERDEV,
4354 * Dumps several interesting bits of the VGA state that are difficult to
4355 * decode from the registers.}
4356 */
4357static DECLCALLBACK(void) vgaR3InfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4358{
4359 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4360 int is_graph, double_scan;
4361 int w, h, char_height, char_dots;
4362 int val, vfreq_hz, hfreq_hz;
4363 vga_retrace_s *r = &pThis->retrace_state;
4364 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4365 const char *mem_map[] = { "A000-BFFF", "A000-AFFF", "B000-B7FF", "B800-BFFF" };
4366 NOREF(pszArgs);
4367
4368 is_graph = pThis->gr[6] & 1;
4369 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4370 double_scan = pThis->cr[9] >> 7;
4371 pHlp->pfnPrintf(pHlp, "decoding memory at %s\n", mem_map[(pThis->gr[6] >> 2) & 3]);
4372 pHlp->pfnPrintf(pHlp, "Misc status reg. MSR:%02X\n", pThis->msr);
4373 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4374 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4375 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4376 val = pThis->cr[0] + 5;
4377 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4378 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4379 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4380 val = pThis->cr[1] + 1;
4381 w = val * char_dots;
4382 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4383 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4384 h = val;
4385 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4386 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4387 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4388 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4389 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4390 if (!is_graph)
4391 {
4392 uint8_t ch_stride;
4393
4394 ch_stride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4395 val = (pThis->cr[9] & 0x1f) + 1;
4396 char_height = val;
4397 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4398 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4399
4400 uint32_t cbLine;
4401 uint32_t offStart;
4402 uint32_t offCursr;
4403 uint32_t uLineCompareIgn;
4404 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4405 if (!cbLine)
4406 cbLine = 80 * ch_stride;
4407 offStart *= ch_stride;
4408 offCursr = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) * ch_stride;
4409 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4410 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4411 pHlp->pfnPrintf(pHlp, "offCursr: %#x\n", offCursr);
4412 }
4413 if (pThis->fRealRetrace)
4414 {
4415 val = r->hb_start;
4416 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4417 val = r->hb_end;
4418 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4419 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4420 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4421 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4422 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4423 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4424 {
4425 vfreq_hz = 1000000000 / r->frame_ns;
4426 hfreq_hz = 1000000000 / r->h_total_ns;
4427 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4428 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4429 }
4430 }
4431 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4432
4433# ifdef VBOX_WITH_VMSVGA
4434 if (pThis->svga.fEnabled)
4435 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4436 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4437# endif
4438}
4439
4440
4441/**
4442 * Prints a separator line.
4443 *
4444 * @param pHlp Callback functions for doing output.
4445 * @param cCols The number of columns.
4446 * @param pszTitle The title text, NULL if none.
4447 */
4448static void vgaR3InfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4449{
4450 if (pszTitle)
4451 {
4452 size_t cchTitle = strlen(pszTitle);
4453 if (cchTitle + 6 >= cCols)
4454 {
4455 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4456 cCols = 0;
4457 }
4458 else
4459 {
4460 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4461 cCols -= cchLeft + cchTitle + 2;
4462 while (cchLeft-- > 0)
4463 pHlp->pfnPrintf(pHlp, "-");
4464 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4465 }
4466 }
4467
4468 while (cCols-- > 0)
4469 pHlp->pfnPrintf(pHlp, "-");
4470 pHlp->pfnPrintf(pHlp, "\n");
4471}
4472
4473
4474/**
4475 * Worker for vgaR3InfoText.
4476 *
4477 * @param pThis The shared VGA state.
4478 * @param pThisCC The VGA state for ring-3.
4479 * @param pHlp Callback functions for doing output.
4480 * @param offStart Where to start dumping (relative to the VRAM).
4481 * @param cbLine The source line length (aka line_offset).
4482 * @param cCols The number of columns on the screen.
4483 * @param cRows The number of rows to dump.
4484 * @param iScrBegin The row at which the current screen output starts.
4485 * @param iScrEnd The row at which the current screen output end
4486 * (exclusive).
4487 */
4488static void vgaR3InfoTextWorker(PVGASTATE pThis, PVGASTATER3 pThisCC, PCDBGFINFOHLP pHlp,
4489 uint32_t offStart, uint32_t cbLine,
4490 uint32_t cCols, uint32_t cRows,
4491 uint32_t iScrBegin, uint32_t iScrEnd)
4492{
4493 /* Title, */
4494 char szTitle[32];
4495 if (iScrBegin || iScrEnd < cRows)
4496 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4497 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4498 else
4499 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4500
4501 /* Do the dumping. */
4502 uint8_t const *pbSrcOuter = pThisCC->pbVRam + offStart;
4503 uint8_t const cStride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4504 uint32_t iRow;
4505 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4506 {
4507 if ((uintptr_t)(pbSrcOuter + cbLine - pThisCC->pbVRam) > pThis->vram_size) {
4508 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4509 break;
4510 }
4511
4512 if (iRow == 0)
4513 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4514 else if (iRow == iScrBegin)
4515 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4516 else if (iRow == iScrEnd)
4517 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4518
4519 uint8_t const *pbSrc = pbSrcOuter;
4520 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4521 {
4522 if (RT_C_IS_PRINT(*pbSrc))
4523 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4524 else
4525 pHlp->pfnPrintf(pHlp, ".");
4526 pbSrc += cStride; /* chars are spaced 8 or sometimes 4 bytes apart */
4527 }
4528 pHlp->pfnPrintf(pHlp, "\n");
4529 }
4530
4531 /* Final separator. */
4532 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4533}
4534
4535
4536/**
4537 * @callback_method_impl{FNDBGFHANDLERDEV,
4538 * Dumps VGA memory formatted as ASCII text\, no attributes. Only looks at
4539 * the first page.}
4540 */
4541static DECLCALLBACK(void) vgaR3InfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4542{
4543 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4544 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4545
4546 /*
4547 * Parse args.
4548 */
4549 bool fAll = true;
4550 if (pszArgs && *pszArgs)
4551 {
4552 if (!strcmp(pszArgs, "all"))
4553 fAll = true;
4554 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4555 fAll = false;
4556 else
4557 {
4558 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4559 return;
4560 }
4561 }
4562
4563 /*
4564 * Check that we're in text mode and that the VRAM is accessible.
4565 */
4566 if (!(pThis->gr[6] & 1))
4567 {
4568 uint8_t *pbSrc = pThisCC->pbVRam;
4569 if (pbSrc)
4570 {
4571 /*
4572 * Figure out the display size and where the text is.
4573 *
4574 * Note! We're cutting quite a few corners here and this code could
4575 * do with some brushing up. Dumping from the start of the
4576 * frame buffer is done intentionally so that we're more
4577 * likely to obtain the full scrollback of a linux panic.
4578 * windbg> .printf "------ start -----\n"; .for (r $t0 = 0; @$t0 < 25; r $t0 = @$t0 + 1) { .for (r $t1 = 0; @$t1 < 80; r $t1 = @$t1 + 1) { .printf "%c", by( (@$t0 * 80 + @$t1) * 8 + 100f0000) }; .printf "\n" }; .printf "------ end -----\n";
4579 */
4580 uint32_t cbLine;
4581 uint32_t offStart;
4582 uint32_t uLineCompareIgn;
4583 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4584 if (!cbLine)
4585 cbLine = 80 * 8;
4586 offStart *= 8;
4587
4588 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4589 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4590 uint32_t uDblScan = pThis->cr[9] >> 7;
4591 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4592 if (cScrRows < 25)
4593 cScrRows = 25;
4594 uint32_t iScrBegin = offStart / cbLine;
4595 uint32_t cRows = iScrBegin + cScrRows;
4596 uint32_t cCols = cbLine / 8;
4597
4598 if (fAll)
4599 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart - iScrBegin * cbLine, cbLine,
4600 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4601 else
4602 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4603 }
4604 else
4605 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4606 }
4607 else
4608 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4609}
4610
4611
4612/**
4613 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4614 */
4615static DECLCALLBACK(void) vgaR3InfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4616{
4617 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4618 NOREF(pszArgs);
4619
4620 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4621 Assert(sizeof(pThis->sr) >= 8);
4622 for (unsigned i = 0; i < 8; ++i)
4623 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4624 pHlp->pfnPrintf(pHlp, "\n");
4625}
4626
4627
4628/**
4629 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4630 */
4631static DECLCALLBACK(void) vgaR3InfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4632{
4633 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4634 unsigned i;
4635 NOREF(pszArgs);
4636
4637 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4638 Assert(sizeof(pThis->cr) >= 24);
4639 for (i = 0; i < 10; ++i)
4640 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4641 pHlp->pfnPrintf(pHlp, "\n");
4642 for (i = 10; i < 20; ++i)
4643 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4644 pHlp->pfnPrintf(pHlp, "\n");
4645 for (i = 20; i < 25; ++i)
4646 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4647 pHlp->pfnPrintf(pHlp, "\n");
4648}
4649
4650
4651/**
4652 * @callback_method_impl{FNDBGFHANDLERDEV,
4653 * Dumps VGA Graphics Controller registers.}
4654 */
4655static DECLCALLBACK(void) vgaR3InfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4656{
4657 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4658 NOREF(pszArgs);
4659
4660 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4661 Assert(sizeof(pThis->gr) >= 9);
4662 for (unsigned i = 0; i < 9; ++i)
4663 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4664 pHlp->pfnPrintf(pHlp, "\n");
4665}
4666
4667
4668/**
4669 * @callback_method_impl{FNDBGFHANDLERDEV,
4670 * Dumps VGA Attribute Controller registers.}
4671 */
4672static DECLCALLBACK(void) vgaR3InfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4673{
4674 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4675 unsigned i;
4676 NOREF(pszArgs);
4677
4678 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4679 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4680 Assert(sizeof(pThis->ar) >= 0x14);
4681 pHlp->pfnPrintf(pHlp, " Palette:");
4682 for (i = 0; i < 0x10; ++i)
4683 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4684 pHlp->pfnPrintf(pHlp, "\n");
4685 for (i = 0x10; i <= 0x14; ++i)
4686 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4687 pHlp->pfnPrintf(pHlp, "\n");
4688}
4689
4690
4691/**
4692 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4693 */
4694static DECLCALLBACK(void) vgaR3InfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4695{
4696 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4697 NOREF(pszArgs);
4698
4699 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4700 for (unsigned i = 0; i < 0x100; ++i)
4701 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4702 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4703}
4704
4705
4706/**
4707 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4708 */
4709static DECLCALLBACK(void) vgaR3InfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4710{
4711 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4712 NOREF(pszArgs);
4713
4714 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4715 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4716 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4717 else
4718 {
4719 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4720 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4721 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4722 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4723 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4724 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4725 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4726 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4727 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4728 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4729 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4730 pHlp->pfnPrintf(pHlp, " DAC: %d-bit\n", pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_8BIT_DAC ? 8 : 6);
4731 }
4732}
4733
4734
4735/**
4736 * @callback_method_impl{FNDBGFHANDLERDEV,
4737 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4738 * in human-readable form.}
4739 */
4740static DECLCALLBACK(void) vgaR3InfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4741{
4742 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4743 NOREF(pszArgs);
4744
4745 unsigned val1 = (pThis->gr[5] >> 3) & 1;
4746 unsigned val2 = pThis->gr[5] & 3;
4747 pHlp->pfnPrintf(pHlp, "read mode : %u write mode: %u\n", val1, val2);
4748 val1 = pThis->gr[0];
4749 val2 = pThis->gr[1];
4750 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4751 val1 = pThis->gr[2];
4752 val2 = pThis->gr[4] & 3;
4753 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %u\n", val1, val2);
4754 val1 = pThis->gr[3] & 7;
4755 val2 = (pThis->gr[3] >> 3) & 3;
4756 pHlp->pfnPrintf(pHlp, "rotate : %u function : %u\n", val1, val2);
4757 val1 = pThis->gr[7];
4758 val2 = pThis->gr[8];
4759 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4760 val1 = pThis->sr[2];
4761 val2 = pThis->sr[4] & 8;
4762 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4763}
4764
4765
4766/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4767
4768/**
4769 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4770 */
4771static DECLCALLBACK(void *) vgaR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4772{
4773 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IBase);
4774 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4775 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThisCC->IPort);
4776# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
4777 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThisCC->IVBVACallbacks);
4778# endif
4779 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4780 return NULL;
4781}
4782
4783
4784/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4785
4786/**
4787 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4788 */
4789static DECLCALLBACK(int) vgaR3PortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4790{
4791 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, ILeds);
4792 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4793 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4794 switch (iLUN)
4795 {
4796 /* LUN #0 is the only one for which we have a status LED. */
4797 case 0:
4798 {
4799 *ppLed = &pThis->Led3D;
4800 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4801 return VINF_SUCCESS;
4802 }
4803
4804 default:
4805 AssertMsgFailed(("Invalid LUN #%u\n", iLUN));
4806 return VERR_PDM_NO_SUCH_LUN;
4807 }
4808}
4809
4810
4811/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4812
4813/**
4814 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnResize}
4815 */
4816static DECLCALLBACK(int) vgaR3DummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t cBits, void *pvVRAM,
4817 uint32_t cbLine, uint32_t cx, uint32_t cy)
4818{
4819 RT_NOREF(pInterface, cBits, pvVRAM, cbLine, cx, cy);
4820 return VINF_SUCCESS;
4821}
4822
4823
4824/**
4825 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnUpdateRect}
4826 */
4827static DECLCALLBACK(void) vgaR3DummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4828{
4829 RT_NOREF(pInterface, x, y, cx, cy);
4830}
4831
4832
4833/**
4834 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnRefresh}
4835 */
4836static DECLCALLBACK(void) vgaR3DummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4837{
4838 NOREF(pInterface);
4839}
4840
4841
4842/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4843
4844/**
4845 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
4846 */
4847static DECLCALLBACK(int) vgaR3PortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4848{
4849 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4850 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4851 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4852
4853 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4854 AssertRCReturn(rc, rc);
4855
4856# ifdef VBOX_WITH_VMSVGA
4857 if ( pThis->svga.fEnabled
4858 && !pThis->svga.fTraces)
4859 {
4860 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4861 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4862 return VINF_SUCCESS;
4863 }
4864#endif
4865
4866# ifndef VBOX_WITH_HGSMI
4867 /* This should be called only in non VBVA mode. */
4868# else
4869 if (VBVAUpdateDisplay(pThis, pThisCC) == VINF_SUCCESS)
4870 {
4871 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4872 return VINF_SUCCESS;
4873 }
4874# endif /* VBOX_WITH_HGSMI */
4875
4876 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4877
4878 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4879 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4880
4881 if (pThis->bmPageRemappedVGA != 0)
4882 {
4883 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4884 STAM_COUNTER_INC(&pThis->StatMapReset);
4885 vgaResetRemapped(pThis);
4886 }
4887
4888 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, false /*fFailOnResize*/, true /*reset_dirty*/,
4889 pThisCC->pDrv, &pThis->graphic_mode);
4890 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4891 return rc;
4892}
4893
4894
4895/**
4896 * Internal vgaR3PortUpdateDisplayAll worker called under pThis->CritSect.
4897 */
4898static int vgaR3UpdateDisplayAllInternal(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool fFailOnResize)
4899{
4900# ifdef VBOX_WITH_VMSVGA
4901 if ( !pThis->svga.fEnabled
4902 || pThis->svga.fTraces)
4903# endif
4904 {
4905 /* Update the dirty bits. */
4906 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4907 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4908 }
4909
4910 if (pThis->bmPageRemappedVGA != 0)
4911 {
4912 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4913 STAM_COUNTER_INC(&pThis->StatMapReset);
4914 vgaResetRemapped(pThis);
4915 }
4916
4917 pThis->graphic_mode = -1; /* force full update */
4918
4919 return vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, true /*fUpdateAll*/, fFailOnResize,
4920 true /*reset_dirty*/, pThisCC->pDrv, &pThis->graphic_mode);
4921}
4922
4923
4924/**
4925 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
4926 */
4927static DECLCALLBACK(int) vgaR3PortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4928{
4929 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4930 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4931 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4932
4933 /* This is called both in VBVA mode and normal modes. */
4934
4935# ifdef DEBUG_sunlover
4936 LogFlow(("vgaR3PortUpdateDisplayAll\n"));
4937# endif /* DEBUG_sunlover */
4938
4939 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4940 AssertRCReturn(rc, rc);
4941
4942 rc = vgaR3UpdateDisplayAllInternal(pDevIns, pThis, pThisCC, fFailOnResize);
4943
4944 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4945 return rc;
4946}
4947
4948
4949/**
4950 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
4951 */
4952static DECLCALLBACK(int) vgaR3PortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4953{
4954 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4955 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4956 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4957
4958 /*
4959 * Update the interval, notify the VMSVGA FIFO thread if sleeping,
4960 * then restart or stop the timer.
4961 */
4962 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
4963
4964# ifdef VBOX_WITH_VMSVGA
4965 if (pThis->svga.fFIFOThreadSleeping)
4966 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
4967# endif
4968
4969 if (cMilliesInterval)
4970 return PDMDevHlpTimerSetMillies(pDevIns, pThis->hRefreshTimer, cMilliesInterval);
4971 return PDMDevHlpTimerStop(pDevIns, pThis->hRefreshTimer);
4972}
4973
4974
4975/**
4976 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
4977 */
4978static DECLCALLBACK(int) vgaR3PortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4979{
4980 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4981 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4982 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4983
4984 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
4985
4986 *pcBits = vgaR3GetBpp(pThis);
4987 if (pcx)
4988 *pcx = pThis->last_scr_width;
4989 if (pcy)
4990 *pcy = pThis->last_scr_height;
4991 return VINF_SUCCESS;
4992}
4993
4994
4995/**
4996 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
4997 */
4998static DECLCALLBACK(int) vgaR3PortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
4999 uint32_t *pcx, uint32_t *pcy)
5000{
5001 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5002 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5003 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5004 PDMDEV_ASSERT_EMT(pDevIns);
5005
5006 LogFlow(("vgaR3PortTakeScreenshot: ppbData=%p pcbData=%p pcx=%p pcy=%p\n", ppbData, pcbData, pcx, pcy));
5007
5008 /*
5009 * Validate input.
5010 */
5011 if (!RT_VALID_PTR(ppbData) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
5012 return VERR_INVALID_PARAMETER;
5013
5014 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5015 AssertRCReturn(rc, rc);
5016
5017 /*
5018 * Get screenshot. This function will fail if a resize is required.
5019 * So there is not need to do a 'vgaR3UpdateDisplayAllInternal' before taking screenshot.
5020 */
5021
5022 /*
5023 * Allocate the buffer for 32 bits per pixel bitmap
5024 *
5025 * Note! The size can't be zero or greater than the size of the VRAM.
5026 * Inconsistent VGA device state can cause the incorrect size values.
5027 */
5028 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
5029 if (cbRequired && cbRequired <= pThis->vram_size)
5030 {
5031 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbRequired);
5032 if (pbData != NULL)
5033 {
5034 /*
5035 * Only 3 methods, assigned below, will be called during the screenshot update.
5036 * All other are already set to NULL.
5037 */
5038 /* The display connector interface is temporarily replaced with the fake one. */
5039 PDMIDISPLAYCONNECTOR Connector;
5040 RT_ZERO(Connector);
5041 Connector.pbData = pbData;
5042 Connector.cBits = 32;
5043 Connector.cx = pThis->last_scr_width;
5044 Connector.cy = pThis->last_scr_height;
5045 Connector.cbScanline = Connector.cx * 4;
5046 Connector.pfnRefresh = vgaR3DummyRefresh;
5047 Connector.pfnResize = vgaR3DummyResize;
5048 Connector.pfnUpdateRect = vgaR3DummyUpdateRect;
5049
5050 int32_t cur_graphic_mode = -1;
5051
5052 bool fSavedRenderVRAM = pThis->fRenderVRAM;
5053 pThis->fRenderVRAM = true;
5054
5055 /*
5056 * Take the screenshot.
5057 *
5058 * The second parameter is 'false' because the current display state is being rendered to an
5059 * external buffer using a fake connector. That is if display is blanked, we expect a black
5060 * screen in the external buffer.
5061 * If there is a pending resize, the function will fail.
5062 */
5063 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, true /*fFailOnResize*/,
5064 false /*reset_dirty*/, &Connector, &cur_graphic_mode);
5065
5066 pThis->fRenderVRAM = fSavedRenderVRAM;
5067
5068 if (rc == VINF_SUCCESS)
5069 {
5070 /*
5071 * Return the result.
5072 */
5073 *ppbData = pbData;
5074 *pcbData = cbRequired;
5075 *pcx = Connector.cx;
5076 *pcy = Connector.cy;
5077 }
5078 else
5079 {
5080 /* If we do not return a success, then the data buffer must be freed. */
5081 RTMemFree(pbData);
5082 if (RT_SUCCESS_NP(rc))
5083 {
5084 AssertMsgFailed(("%Rrc\n", rc));
5085 rc = VERR_INTERNAL_ERROR_5;
5086 }
5087 }
5088 }
5089 else
5090 rc = VERR_NO_MEMORY;
5091 }
5092 else
5093 rc = VERR_NOT_SUPPORTED;
5094
5095 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5096
5097 LogFlow(("vgaR3PortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
5098 return rc;
5099}
5100
5101
5102/**
5103 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
5104 */
5105static DECLCALLBACK(void) vgaR3PortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
5106{
5107 NOREF(pInterface);
5108
5109 LogFlow(("vgaR3PortFreeScreenshot: pbData=%p\n", pbData));
5110
5111 RTMemFree(pbData);
5112}
5113
5114
5115/**
5116 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
5117 */
5118static DECLCALLBACK(int) vgaR3PortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
5119 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
5120{
5121 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5122 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5123 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5124 PDMDEV_ASSERT_EMT(pDevIns);
5125 LogFlow(("vgaR3PortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
5126
5127 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5128 AssertRCReturn(rc, rc);
5129
5130 /*
5131 * Validate input.
5132 */
5133 if ( pvData
5134 && x < pThisCC->pDrv->cx
5135 && cx <= pThisCC->pDrv->cx
5136 && cx + x <= pThisCC->pDrv->cx
5137 && y < pThisCC->pDrv->cy
5138 && cy <= pThisCC->pDrv->cy
5139 && cy + y <= pThisCC->pDrv->cy)
5140 {
5141 /*
5142 * Determine bytes per pixel in the destination buffer.
5143 */
5144 size_t cbPixelDst = 0;
5145 switch (pThisCC->pDrv->cBits)
5146 {
5147 case 8:
5148 cbPixelDst = 1;
5149 break;
5150 case 15:
5151 case 16:
5152 cbPixelDst = 2;
5153 break;
5154 case 24:
5155 cbPixelDst = 3;
5156 break;
5157 case 32:
5158 cbPixelDst = 4;
5159 break;
5160 default:
5161 rc = VERR_INVALID_PARAMETER;
5162 break;
5163 }
5164 if (RT_SUCCESS(rc))
5165 {
5166 /*
5167 * The blitting loop.
5168 */
5169 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
5170 uint8_t *pbSrc = (uint8_t *)pvData;
5171 size_t cbLineDst = pThisCC->pDrv->cbScanline;
5172 uint8_t *pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5173 uint32_t cyLeft = cy;
5174 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5175 Assert(pfnVgaDrawLine);
5176 while (cyLeft-- > 0)
5177 {
5178 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5179 pbDst += cbLineDst;
5180 pbSrc += cbLineSrc;
5181 }
5182
5183 /*
5184 * Invalidate the area.
5185 */
5186 pThisCC->pDrv->pfnUpdateRect(pThisCC->pDrv, x, y, cx, cy);
5187 }
5188 }
5189 else
5190 rc = VERR_INVALID_PARAMETER;
5191
5192 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5193
5194 LogFlow(("vgaR3PortDisplayBlt: returns %Rrc\n", rc));
5195 return rc;
5196}
5197
5198
5199/**
5200 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
5201 */
5202static DECLCALLBACK(void) vgaR3PortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
5203{
5204 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5205 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5206 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5207 uint32_t v;
5208
5209 uint32_t cbPixelDst;
5210 uint32_t cbLineDst;
5211 uint8_t *pbDst;
5212
5213 uint32_t cbPixelSrc;
5214 uint32_t cbLineSrc;
5215 uint8_t *pbSrc;
5216
5217
5218# ifdef DEBUG_sunlover
5219 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d\n", x, y, cx, cy));
5220# endif /* DEBUG_sunlover */
5221
5222 Assert(pInterface);
5223
5224 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5225 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
5226
5227 /* Check if there is something to do at all. */
5228 if (!pThis->fRenderVRAM)
5229 {
5230 /* The framebuffer uses the guest VRAM directly. */
5231# ifdef DEBUG_sunlover
5232 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do fRender is false.\n"));
5233# endif /* DEBUG_sunlover */
5234 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5235 return;
5236 }
5237
5238 Assert(pThisCC->pDrv);
5239 Assert(pThisCC->pDrv->pbData);
5240
5241 /* Correct negative x and y coordinates. */
5242 if (x < 0)
5243 {
5244 x += cx; /* Compute xRight which is also the new width. */
5245 cx = (x < 0) ? 0 : x;
5246 x = 0;
5247 }
5248
5249 if (y < 0)
5250 {
5251 y += cy; /* Compute yBottom, which is also the new height. */
5252 cy = (y < 0) ? 0 : y;
5253 y = 0;
5254 }
5255
5256 /* Also check if coords are greater than the display resolution. */
5257 if (x + cx > pThisCC->pDrv->cx)
5258 {
5259 // x < 0 is not possible here
5260 cx = pThisCC->pDrv->cx > (uint32_t)x? pThisCC->pDrv->cx - x: 0;
5261 }
5262
5263 if (y + cy > pThisCC->pDrv->cy)
5264 {
5265 // y < 0 is not possible here
5266 cy = pThisCC->pDrv->cy > (uint32_t)y? pThisCC->pDrv->cy - y: 0;
5267 }
5268
5269# ifdef DEBUG_sunlover
5270 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, cx, cy));
5271# endif
5272
5273 /* Check if there is something to do at all. */
5274 if (cx == 0 || cy == 0)
5275 {
5276 /* Empty rectangle. */
5277# ifdef DEBUG_sunlover
5278 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do: %dx%d\n", cx, cy));
5279#endif
5280 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5281 return;
5282 }
5283
5284 /** @todo This method should be made universal and not only for VBVA.
5285 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5286 * changed.
5287 */
5288
5289 /* Choose the rendering function. */
5290 switch(pThisCC->get_bpp(pThis))
5291 {
5292 default:
5293 case 0:
5294 /* A LFB mode is already disabled, but the callback is still called
5295 * by Display because VBVA buffer is being flushed.
5296 * Nothing to do, just return.
5297 */
5298 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5299 return;
5300 case 8:
5301 v = VGA_DRAW_LINE8;
5302 break;
5303 case 15:
5304 v = VGA_DRAW_LINE15;
5305 break;
5306 case 16:
5307 v = VGA_DRAW_LINE16;
5308 break;
5309 case 24:
5310 v = VGA_DRAW_LINE24;
5311 break;
5312 case 32:
5313 v = VGA_DRAW_LINE32;
5314 break;
5315 }
5316
5317 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5318
5319 /* Compute source and destination addresses and pitches. */
5320 cbPixelDst = (pThisCC->pDrv->cBits + 7) / 8;
5321 cbLineDst = pThisCC->pDrv->cbScanline;
5322 pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5323
5324 cbPixelSrc = (pThisCC->get_bpp(pThis) + 7) / 8;
5325 uint32_t offSrc, u32Dummy;
5326 pThisCC->get_offsets(pThis, &cbLineSrc, &offSrc, &u32Dummy);
5327
5328 /* Assume that rendering is performed only on visible part of VRAM.
5329 * This is true because coordinates were verified.
5330 */
5331 pbSrc = pThisCC->pbVRam;
5332 pbSrc += offSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5333
5334 /* Render VRAM to framebuffer. */
5335
5336# ifdef DEBUG_sunlover
5337 LogFlow(("vgaR3PortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDst, cbLineDst, cbPixelDst, pbSrc, cbLineSrc, cbPixelSrc));
5338# endif
5339
5340 while (cy-- > 0)
5341 {
5342 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5343 pbDst += cbLineDst;
5344 pbSrc += cbLineSrc;
5345 }
5346
5347 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5348# ifdef DEBUG_sunlover
5349 LogFlow(("vgaR3PortUpdateDisplayRect: completed.\n"));
5350# endif
5351}
5352
5353
5354/**
5355 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
5356 */
5357static DECLCALLBACK(int)
5358vgaR3PortCopyRect(PPDMIDISPLAYPORT pInterface,
5359 uint32_t cx, uint32_t cy,
5360 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
5361 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
5362 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
5363 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
5364{
5365 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5366 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5367 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5368 uint32_t v;
5369
5370# ifdef DEBUG_sunlover
5371 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, cx, cy, xDst, yDst));
5372# endif
5373
5374 Assert(pInterface);
5375 Assert(pThisCC->pDrv);
5376
5377 int32_t xSrcCorrected = xSrc;
5378 int32_t ySrcCorrected = ySrc;
5379 uint32_t cxCorrected = cx;
5380 uint32_t cyCorrected = cy;
5381
5382 /* Correct source coordinates to be within the source bitmap. */
5383 if (xSrcCorrected < 0)
5384 {
5385 xSrcCorrected += cxCorrected; /* Compute xRight which is also the new width. */
5386 cxCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5387 xSrcCorrected = 0;
5388 }
5389
5390 if (ySrcCorrected < 0)
5391 {
5392 ySrcCorrected += cyCorrected; /* Compute yBottom, which is also the new height. */
5393 cyCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5394 ySrcCorrected = 0;
5395 }
5396
5397 /* Also check if coords are greater than the display resolution. */
5398 if (xSrcCorrected + cxCorrected > cxSrc)
5399 {
5400 /* xSrcCorrected < 0 is not possible here */
5401 cxCorrected = cxSrc > (uint32_t)xSrcCorrected ? cxSrc - xSrcCorrected : 0;
5402 }
5403
5404 if (ySrcCorrected + cyCorrected > cySrc)
5405 {
5406 /* y < 0 is not possible here */
5407 cyCorrected = cySrc > (uint32_t)ySrcCorrected ? cySrc - ySrcCorrected : 0;
5408 }
5409
5410# ifdef DEBUG_sunlover
5411 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, cxCorrected, cyCorrected));
5412# endif
5413
5414 /* Check if there is something to do at all. */
5415 if (cxCorrected == 0 || cyCorrected == 0)
5416 {
5417 /* Empty rectangle. */
5418# ifdef DEBUG_sunlover
5419 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", cxCorrected, cyCorrected));
5420# endif
5421 return VINF_SUCCESS;
5422 }
5423
5424 /* Check that the corrected source rectangle is within the destination.
5425 * Note: source rectangle is adjusted, but the target must be large enough.
5426 */
5427 if ( xDst < 0
5428 || yDst < 0
5429 || xDst + cxCorrected > cxDst
5430 || yDst + cyCorrected > cyDst)
5431 {
5432 return VERR_INVALID_PARAMETER;
5433 }
5434
5435 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5436 AssertRCReturn(rc, rc);
5437
5438 /* This method only works if the VGA device is in a VBE mode or not paused VBVA mode.
5439 * VGA modes are reported to the caller by returning VERR_INVALID_STATE.
5440 *
5441 * If VBE_DISPI_ENABLED is set, then it is a VBE or VBE compatible VBVA mode. Both of them can be handled.
5442 *
5443 * If VBE_DISPI_ENABLED is clear, then it is either a VGA mode or a VBVA mode set by guest additions
5444 * which have VBVACAPS_USE_VBVA_ONLY capability.
5445 * When VBE_DISPI_ENABLED is being cleared and VBVACAPS_USE_VBVA_ONLY is not set (i.e. guest wants a VGA mode),
5446 * then VBVAOnVBEChanged makes sure that VBVA is paused.
5447 * That is a not paused VBVA means that the video mode can be handled even if VBE_DISPI_ENABLED is clear.
5448 */
5449 if ( (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0
5450 && VBVAIsPaused(pThisCC)
5451# ifdef VBOX_WITH_VMSVGA
5452 && !pThis->svga.fEnabled
5453# endif
5454 )
5455 {
5456 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5457 return VERR_INVALID_STATE;
5458 }
5459
5460 /* Choose the rendering function. */
5461 switch (cSrcBitsPerPixel)
5462 {
5463 default:
5464 case 0:
5465 /* Nothing to do, just return. */
5466 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5467 return VINF_SUCCESS;
5468 case 8:
5469 v = VGA_DRAW_LINE8;
5470 break;
5471 case 15:
5472 v = VGA_DRAW_LINE15;
5473 break;
5474 case 16:
5475 v = VGA_DRAW_LINE16;
5476 break;
5477 case 24:
5478 v = VGA_DRAW_LINE24;
5479 break;
5480 case 32:
5481 v = VGA_DRAW_LINE32;
5482 break;
5483 }
5484
5485 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(cDstBitsPerPixel)];
5486
5487 /* Compute source and destination addresses and pitches. */
5488 uint32_t cbPixelDst = (cDstBitsPerPixel + 7) / 8;
5489 uint32_t cbLineDst = cbDstLine;
5490 uint8_t *pbDstCur = pbDst + yDst * cbLineDst + xDst * cbPixelDst;
5491
5492 uint32_t cbPixelSrc = (cSrcBitsPerPixel + 7) / 8;
5493 uint32_t cbLineSrc = cbSrcLine;
5494 const uint8_t *pbSrcCur = pbSrc + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5495
5496# ifdef DEBUG_sunlover
5497 LogFlow(("vgaR3PortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDstCur, cbLineDst, cbPixelDst, pbSrcCur, cbLineSrc, cbPixelSrc));
5498# endif
5499
5500 while (cyCorrected-- > 0)
5501 {
5502 pfnVgaDrawLine(pThis, pThisCC, pbDstCur, pbSrcCur, cxCorrected);
5503 pbDstCur += cbLineDst;
5504 pbSrcCur += cbLineSrc;
5505 }
5506
5507 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5508# ifdef DEBUG_sunlover
5509 LogFlow(("vgaR3PortCopyRect: completed.\n"));
5510# endif
5511 return VINF_SUCCESS;
5512}
5513
5514
5515/**
5516 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
5517 */
5518static DECLCALLBACK(void) vgaR3PortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5519{
5520 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5521 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5522 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5523
5524 LogFlow(("vgaR3PortSetRenderVRAM: fRender = %d\n", fRender));
5525
5526 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5527 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
5528
5529 pThis->fRenderVRAM = fRender;
5530
5531 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5532}
5533
5534
5535/**
5536 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
5537 */
5538static DECLCALLBACK(void) vgaR3PortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
5539 bool fSupportsMoveCursor)
5540{
5541 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
5542}
5543
5544
5545/**
5546 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
5547 */
5548static DECLCALLBACK(void) vgaR3PortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
5549{
5550 RT_NOREF(pInterface, x, y, fOutOfRange);
5551}
5552
5553
5554/**
5555 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
5556 */
5557static DECLCALLBACK(void) vgaR3TimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
5558{
5559 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5560 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5561 RT_NOREF(pvUser);
5562
5563 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5564 VBVARaiseIrq(pDevIns, pThis, pThisCC, HGSMIHOSTFLAGS_VSYNC);
5565
5566 if (pThisCC->pDrv)
5567 pThisCC->pDrv->pfnRefresh(pThisCC->pDrv);
5568
5569 if (pThis->cMilliesRefreshInterval)
5570 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
5571
5572# ifdef VBOX_WITH_VIDEOHWACCEL
5573 vbvaTimerCb(pDevIns, pThis, pThisCC);
5574# endif
5575
5576# ifdef VBOX_WITH_VMSVGA
5577 /*
5578 * Call the VMSVGA FIFO poller/watchdog so we can wake up the thread if
5579 * there is work to be done.
5580 */
5581 if (pThis->svga.fFIFOThreadSleeping && pThis->svga.fEnabled && pThis->svga.fConfigured)
5582 vmsvgaR3FifoWatchdogTimer(pDevIns, pThis, pThisCC);
5583# endif
5584}
5585
5586# ifdef VBOX_WITH_VMSVGA
5587
5588/**
5589 * Helper for VMSVGA.
5590 */
5591int vgaR3RegisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis, uint64_t cbFrameBuffer)
5592{
5593 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5594 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5595 RT_NOREF(cbFrameBuffer);
5596 AssertRC(rc);
5597 return rc;
5598}
5599
5600
5601/**
5602 * Helper for VMSVGA.
5603 */
5604int vgaR3UnregisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis)
5605{
5606 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5607 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, false /*fEnabled*/);
5608 AssertRC(rc);
5609 return rc;
5610}
5611
5612# endif /* VBOX_WITH_VMSVGA */
5613
5614
5615/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5616
5617/**
5618 * @callback_method_impl{FNPCIIOREGIONMAP, Mapping/unmapping the VRAM MMI2 region}
5619 */
5620static DECLCALLBACK(int) vgaR3PciIORegionVRamMapUnmap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5621 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
5622{
5623 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5624 Log(("vgaR3PciIORegionVRamMapUnmap: iRegion=%d GCPhysAddress=%RGp cb=%RGp enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5625 RT_NOREF(pPciDev, cb);
5626
5627# ifdef VBOX_WITH_VMSVGA
5628 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5629 && ( enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH
5630 || (enmType == PCI_ADDRESS_SPACE_MEM && pThis->fVMSVGAEnabled && pThis->fStateLoaded)), VERR_INTERNAL_ERROR);
5631# else
5632 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5633 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5634# endif
5635
5636 Assert(pPciDev == pDevIns->apPciDevs[0]);
5637
5638 /* Note! We cannot take the device lock here as that would create a lock order
5639 problem as the caller has taken the PDM lock prior to calling us. If
5640 we did, we will get trouble later when raising interrupts while owning
5641 the device lock (e.g. vmsvgaR3FifoLoop). */
5642
5643 int rc;
5644 if (GCPhysAddress != NIL_RTGCPHYS)
5645 {
5646 /*
5647 * Make sure the dirty page tracking state is up to date before mapping it.
5648 */
5649# ifdef VBOX_WITH_VMSVGA
5650 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam,
5651 !pThis->svga.fEnabled ||(pThis->svga.fEnabled && pThis->svga.fVRAMTracking));
5652# else
5653 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5654# endif
5655 AssertLogRelRC(rc);
5656
5657 /*
5658 * Map the VRAM.
5659 */
5660 rc = PDMDevHlpMmio2Map(pDevIns, pThis->hMmio2VRam, GCPhysAddress);
5661 AssertLogRelRC(rc);
5662 if (RT_SUCCESS(rc))
5663 {
5664 pThis->GCPhysVRAM = GCPhysAddress;
5665 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5666
5667 rc = VINF_PCI_MAPPING_DONE; /* caller doesn't care about any other status, so no problem overwriting error here */
5668 }
5669 }
5670 else
5671 {
5672 /*
5673 * Unmapping of the VRAM in progress (caller will do that).
5674 */
5675 Assert(pThis->GCPhysVRAM);
5676 pThis->GCPhysVRAM = 0;
5677 rc = VINF_SUCCESS;
5678 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5679 }
5680 return rc;
5681}
5682
5683
5684# ifdef VBOX_WITH_VMSVGA /* Currently not needed in the non-VMSVGA mode, but keeping it flexible for later. */
5685/**
5686 * @interface_method_impl{PDMPCIDEV,pfnRegionLoadChangeHookR3}
5687 */
5688static DECLCALLBACK(int) vgaR3PciRegionLoadChangeHook(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5689 uint64_t cbRegion, PCIADDRESSSPACE enmType,
5690 PFNPCIIOREGIONOLDSETTER pfnOldSetter, PFNPCIIOREGIONSWAP pfnSwapRegions)
5691{
5692 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5693
5694# ifdef VBOX_WITH_VMSVGA
5695 if (pThis->fVMSVGAEnabled)
5696 {
5697 /*
5698 * We messed up BAR order for the hybrid devices in 6.0 (see #9359).
5699 * It should have been compatible with the VBox VGA device and had the
5700 * VRAM region first and I/O second, but instead the I/O region ended
5701 * up first and VRAM second like the VMSVGA device.
5702 *
5703 * So, we have to detect that here and reconfigure the memory regions.
5704 * Region numbers are used in our (and the PCI bus') interfaction with
5705 * PGM, so PGM needs to be informed too.
5706 */
5707 if ( iRegion == 0
5708 && iRegion == pThis->pciRegions.iVRAM
5709 && (enmType & PCI_ADDRESS_SPACE_IO))
5710 {
5711 LogRel(("VGA: Detected old BAR config, making adjustments.\n"));
5712
5713 /* Update the entries. */
5714 pThis->pciRegions.iIO = 0;
5715 pThis->pciRegions.iVRAM = 1;
5716
5717 /* Update PGM on the region number change so it won't barf when restoring state. */
5718 AssertLogRelReturn(pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo, VERR_VERSION_MISMATCH);
5719 int rc = pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo(pDevIns, pThis->hMmio2VRam, 1);
5720 AssertLogRelRCReturn(rc, rc);
5721 /** @todo Update the I/O port too, only currently we don't give a hoot about
5722 * the region number in the I/O port registrations so it can wait...
5723 * (Only visible in the 'info ioport' output IIRC). */
5724
5725 /* Update the calling PCI device. */
5726 AssertLogRelReturn(pfnSwapRegions, VERR_INTERNAL_ERROR_2);
5727 rc = pfnSwapRegions(pPciDev, 0, 1);
5728 AssertLogRelRCReturn(rc, rc);
5729
5730 return rc;
5731 }
5732
5733 /*
5734 * The VMSVGA changed the default FIFO size from 128KB to 2MB after 5.1.
5735 */
5736 if (iRegion == pThis->pciRegions.iFIFO)
5737 {
5738 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5739 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5740 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5741
5742 /* If the size didn't change we're fine, so just return already. */
5743 if (cbRegion == pThis->svga.cbFIFO)
5744 return VINF_SUCCESS;
5745
5746 /* If the size is larger than the current configuration, refuse to load. */
5747 AssertLogRelMsgReturn(cbRegion <= pThis->svga.cbFIFOConfig,
5748 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x\n",
5749 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO),
5750 VERR_SSM_LOAD_CONFIG_MISMATCH);
5751
5752 /* Adjust the size down. */
5753 int rc = PDMDevHlpMmio2Reduce(pDevIns, pThis->hMmio2VmSvgaFifo, cbRegion);
5754 AssertLogRelMsgRCReturn(rc,
5755 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x: %Rrc\n",
5756 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO, rc),
5757 rc);
5758 pThis->svga.cbFIFO = cbRegion;
5759 return rc;
5760
5761 }
5762
5763 /*
5764 * VRAM used to be non-prefetchable till 6.1.0, so we end up here when restoring
5765 * states older than that with 6.1.0 and later. We just have to check that
5766 * the size and basic type matches, then return VINF_SUCCESS to ACK it.
5767 */
5768 if (iRegion == pThis->pciRegions.iVRAM)
5769 {
5770 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5771 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5772 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5773 /* The size must be the same. */
5774 AssertLogRelMsgReturn(cbRegion == pThis->vram_size,
5775 ("cbRegion=%#RGp vram_size=%#x\n", cbRegion, pThis->vram_size),
5776 VERR_SSM_LOAD_CONFIG_MISMATCH);
5777 return VINF_SUCCESS;
5778 }
5779
5780 /* Emulate callbacks for 5.1 and older saved states by recursion. */
5781 if (iRegion == UINT32_MAX)
5782 {
5783 int rc = vgaR3PciRegionLoadChangeHook(pDevIns, pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD,
5784 PCI_ADDRESS_SPACE_MEM, NULL, NULL);
5785 if (RT_SUCCESS(rc))
5786 rc = pfnOldSetter(pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM);
5787 return rc;
5788 }
5789 }
5790# endif /* VBOX_WITH_VMSVGA */
5791
5792 return VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE;
5793}
5794# endif /* VBOX_WITH_VMSVGA */
5795
5796
5797/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5798
5799/**
5800 * Saves a important bits of the VGA device config.
5801 *
5802 * @param pHlp The device helpers (for SSM functions).
5803 * @param pThis The shared VGA instance data.
5804 * @param pSSM The saved state handle.
5805 */
5806static void vgaR3SaveConfig(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PSSMHANDLE pSSM)
5807{
5808 pHlp->pfnSSMPutU32(pSSM, pThis->vram_size);
5809 pHlp->pfnSSMPutU32(pSSM, pThis->cMonitors);
5810}
5811
5812
5813/**
5814 * @callback_method_impl{FNSSMDEVLIVEEXEC}
5815 */
5816static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5817{
5818 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5819 Assert(uPass == 0); NOREF(uPass);
5820 vgaR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
5821 return VINF_SSM_DONT_CALL_AGAIN;
5822}
5823
5824
5825/**
5826 * @callback_method_impl{FNSSMDEVSAVEPREP}
5827 */
5828static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5829{
5830# ifdef VBOX_WITH_VIDEOHWACCEL
5831 RT_NOREF(pSSM);
5832 return vboxVBVASaveStatePrep(pDevIns);
5833# else
5834 RT_NOREF(pDevIns, pSSM);
5835 return VINF_SUCCESS;
5836# endif
5837}
5838
5839
5840/**
5841 * @callback_method_impl{FNSSMDEVSAVEDONE}
5842 */
5843static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5844{
5845# ifdef VBOX_WITH_VIDEOHWACCEL
5846 RT_NOREF(pSSM);
5847 return vboxVBVASaveStateDone(pDevIns);
5848# else
5849 RT_NOREF(pDevIns, pSSM);
5850 return VINF_SUCCESS;
5851# endif
5852}
5853
5854
5855/**
5856 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5857 */
5858static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5859{
5860 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5861 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5862 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5863
5864# ifdef VBOX_WITH_VDMA
5865 vboxVDMASaveStateExecPrep(pThisCC->pVdma);
5866# endif
5867
5868 vgaR3SaveConfig(pHlp, pThis, pSSM);
5869 vga_save(pHlp, pSSM, PDMDEVINS_2_DATA(pDevIns, PVGASTATE));
5870
5871 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5872# ifdef VBOX_WITH_HGSMI
5873 pHlp->pfnSSMPutBool(pSSM, true);
5874 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5875# else
5876 int rc = pHlp->pfnSSMPutBool(pSSM, false);
5877# endif
5878
5879 AssertRCReturn(rc, rc);
5880
5881 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5882# ifdef VBOX_WITH_VDMA
5883 rc = pHlp->pfnSSMPutU32(pSSM, 1);
5884 AssertRCReturn(rc, rc);
5885 rc = vboxVDMASaveStateExecPerform(pHlp, pThisCC->pVdma, pSSM);
5886# else
5887 rc = pHlp->pfnSSMPutU32(pSSM, 0);
5888# endif
5889 AssertRCReturn(rc, rc);
5890
5891# ifdef VBOX_WITH_VDMA
5892 vboxVDMASaveStateExecDone(pThisCC->pVdma);
5893# endif
5894
5895 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5896# ifdef VBOX_WITH_VMSVGA
5897 if (pThis->fVMSVGAEnabled)
5898 {
5899 rc = vmsvgaR3SaveExec(pDevIns, pSSM);
5900 AssertRCReturn(rc, rc);
5901 }
5902# endif
5903 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5904
5905 return rc;
5906}
5907
5908
5909/**
5910 * @callback_method_impl{FNSSMDEVLOADPREP}
5911 */
5912static DECLCALLBACK(int) vgaR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5913{
5914 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5915 RT_NOREF(pSSM);
5916 pThis->fStateLoaded = true;
5917 return VINF_SUCCESS;
5918}
5919
5920
5921/**
5922 * @callback_method_impl{FNSSMDEVLOADEXEC}
5923 */
5924static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5925{
5926 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5927 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5928 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5929 int rc;
5930
5931 pThis->fStateLoaded = true;
5932
5933 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5934 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5935
5936 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5937 {
5938 /* Check the config */
5939 uint32_t cbVRam;
5940 rc = pHlp->pfnSSMGetU32(pSSM, &cbVRam);
5941 AssertRCReturn(rc, rc);
5942 if (pThis->vram_size != cbVRam)
5943 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5944
5945 uint32_t cMonitors;
5946 rc = pHlp->pfnSSMGetU32(pSSM, &cMonitors);
5947 AssertRCReturn(rc, rc);
5948 if (pThis->cMonitors != cMonitors)
5949 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5950 }
5951
5952 if (uPass == SSM_PASS_FINAL)
5953 {
5954 rc = vga_load(pHlp, pSSM, pThis, uVersion);
5955 if (RT_FAILURE(rc))
5956 return rc;
5957
5958 /*
5959 * Restore the HGSMI state, if present.
5960 */
5961 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5962 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5963 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5964 {
5965 rc = pHlp->pfnSSMGetBool(pSSM, &fWithHgsmi);
5966 AssertRCReturn(rc, rc);
5967 }
5968 if (fWithHgsmi)
5969 {
5970# ifdef VBOX_WITH_HGSMI
5971 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5972 AssertRCReturn(rc, rc);
5973# else
5974 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5975# endif
5976 }
5977
5978 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5979 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5980 {
5981 uint32_t u32;
5982 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
5983 if (u32)
5984 {
5985# ifdef VBOX_WITH_VDMA
5986 if (u32 == 1)
5987 {
5988 rc = vboxVDMASaveLoadExecPerform(pHlp, pThisCC->pVdma, pSSM, uVersion);
5989 AssertRCReturn(rc, rc);
5990 }
5991 else
5992# endif
5993 {
5994 LogRel(("invalid CmdVbva version info\n"));
5995 return VERR_VERSION_MISMATCH;
5996 }
5997 }
5998 }
5999
6000 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
6001# ifdef VBOX_WITH_VMSVGA
6002 if (pThis->fVMSVGAEnabled)
6003 {
6004 rc = vmsvgaR3LoadExec(pDevIns, pSSM, uVersion, uPass);
6005 AssertRCReturn(rc, rc);
6006 }
6007# endif
6008 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
6009 }
6010 return VINF_SUCCESS;
6011}
6012
6013
6014/**
6015 * @@callback_method_impl{FNSSMDEVLOADDONE}
6016 */
6017static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
6018{
6019 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6020 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6021 int rc;
6022 RT_NOREF(pThisCC, pThis, pSSM);
6023
6024# ifdef VBOX_WITH_HGSMI
6025 rc = vboxVBVALoadStateDone(pDevIns);
6026 AssertRCReturn(rc, rc);
6027# ifdef VBOX_WITH_VDMA
6028 rc = vboxVDMASaveLoadDone(pThisCC->pVdma);
6029 AssertRCReturn(rc, rc);
6030# endif
6031 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
6032 VBVAOnVBEChanged(pThis, pThisCC);
6033# endif
6034# ifdef VBOX_WITH_VMSVGA
6035 if (pThis->fVMSVGAEnabled)
6036 {
6037 rc = vmsvgaR3LoadDone(pDevIns);
6038 AssertRCReturn(rc, rc);
6039 }
6040# endif
6041 return VINF_SUCCESS;
6042}
6043
6044
6045/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
6046
6047/**
6048 * @interface_method_impl{PDMDEVREG,pfnResume}
6049 */
6050static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
6051{
6052 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6053 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6054 VBVAOnResume(pDevIns, pThis, pThisCC);
6055}
6056
6057
6058/**
6059 * @interface_method_impl{PDMDEVREG,pfnReset}
6060 */
6061static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
6062{
6063 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6064 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6065 char *pchStart;
6066 char *pchEnd;
6067 LogFlow(("vgaReset\n"));
6068
6069 if (pThisCC->pVdma)
6070 vboxVDMAReset(pThisCC->pVdma);
6071
6072# ifdef VBOX_WITH_VMSVGA
6073 if (pThis->fVMSVGAEnabled)
6074 vmsvgaR3Reset(pDevIns);
6075# endif
6076
6077# ifdef VBOX_WITH_HGSMI
6078 VBVAReset(pDevIns, pThis, pThisCC);
6079# endif
6080
6081
6082 /* Clear the VRAM ourselves. */
6083 if (pThisCC->pbVRam && pThis->vram_size)
6084 memset(pThisCC->pbVRam, 0, pThis->vram_size);
6085
6086 /*
6087 * Zero most of it.
6088 *
6089 * Unlike vga_reset we're leaving out a few members which we believe
6090 * must remain unchanged....
6091 */
6092 /* 1st part. */
6093 pchStart = (char *)&pThis->latch;
6094 pchEnd = (char *)&pThis->invalidated_y_table;
6095 memset(pchStart, 0, pchEnd - pchStart);
6096
6097 /* 2nd part. */
6098 pchStart = (char *)&pThis->last_palette;
6099 pchEnd = (char *)&pThis->u32Marker;
6100 memset(pchStart, 0, pchEnd - pchStart);
6101
6102
6103 /*
6104 * Restore and re-init some bits.
6105 */
6106 pThisCC->get_bpp = vgaR3GetBpp;
6107 pThisCC->get_offsets = vgaR3GetOffsets;
6108 pThisCC->get_resolution = vgaR3GetResolution;
6109 pThis->graphic_mode = -1; /* Force full update. */
6110# ifdef CONFIG_BOCHS_VBE
6111 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
6112 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
6113 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
6114 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
6115# endif /* CONFIG_BOCHS_VBE */
6116 pThis->st00 = 0x70; /* Static except for bit 4. */
6117
6118 /*
6119 * Reset the LFB mapping.
6120 */
6121 if ( ( pDevIns->fRCEnabled
6122 || pDevIns->fR0Enabled)
6123 && pThis->GCPhysVRAM != 0
6124 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
6125 {
6126 /** @todo r=bird: This used to be a PDMDevHlpPGMHandlerPhysicalReset call.
6127 * Not quite sure if it was/is needed. Besides, where do we reset the
6128 * dirty bitmap (bmDirtyBitmap)? */
6129 int rc = PDMDevHlpMmio2ResetDirtyBitmap(pDevIns, pThis->hMmio2VRam);
6130 AssertRC(rc);
6131 }
6132 if (pThis->bmPageRemappedVGA != 0)
6133 {
6134 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
6135 STAM_COUNTER_INC(&pThis->StatMapReset);
6136 vgaResetRemapped(pThis);
6137 }
6138
6139 /*
6140 * Reset the logo data.
6141 */
6142 pThisCC->LogoCommand = LOGO_CMD_NOP;
6143 pThisCC->offLogoData = 0;
6144
6145 /* notify port handler */
6146 if (pThisCC->pDrv)
6147 {
6148 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* hack around lock order issue. */
6149
6150 pThisCC->pDrv->pfnReset(pThisCC->pDrv);
6151 pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, false, false, 0, 0, 0, 0, NULL);
6152
6153 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
6154 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
6155 }
6156
6157 /* Reset latched access mask. */
6158 pThis->uMaskLatchAccess = 0x3ff;
6159 pThis->cLatchAccesses = 0;
6160 pThis->u64LastLatchedAccess = 0;
6161 pThis->iMask = 0;
6162
6163 /* Reset retrace emulation. */
6164 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
6165}
6166
6167
6168/**
6169 * @interface_method_impl{PDMDEVREG,pfnPowerOn}
6170 */
6171static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
6172{
6173 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6174 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6175# ifdef VBOX_WITH_VMSVGA
6176 if (pThis->fVMSVGAEnabled)
6177 vmsvgaR3PowerOn(pDevIns);
6178# endif
6179 VBVAOnResume(pDevIns, pThis, pThisCC);
6180}
6181
6182
6183/**
6184 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
6185 */
6186static DECLCALLBACK(void) vgaR3PowerOff(PPDMDEVINS pDevIns)
6187{
6188 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6189 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6190 RT_NOREF(pThis, pThisCC);
6191# ifdef VBOX_WITH_VMSVGA
6192 if (pThis->fVMSVGAEnabled)
6193 vmsvgaR3PowerOff(pDevIns);
6194# endif
6195}
6196
6197
6198/**
6199 * @interface_method_impl{PDMDEVREG,pfnRelocate}
6200 */
6201static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
6202{
6203# ifdef VBOX_WITH_RAW_MODE_KEEP
6204 if (offDelta)
6205 {
6206 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6207 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
6208
6209 pThisRC->pbVRam += offDelta;
6210 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6211 }
6212# else
6213 RT_NOREF(pDevIns, offDelta);
6214# endif
6215}
6216
6217
6218/**
6219 * @interface_method_impl{PDMDEVREG,pfnAttach}
6220 *
6221 * This is like plugging in the monitor after turning on the PC.
6222 */
6223static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6224{
6225 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6226 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6227
6228 RT_NOREF(pThis);
6229
6230 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
6231 ("VGA device does not support hotplugging\n"),
6232 VERR_INVALID_PARAMETER);
6233
6234 switch (iLUN)
6235 {
6236 /* LUN #0: Display port. */
6237 case 0:
6238 {
6239 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "Display Port");
6240 if (RT_SUCCESS(rc))
6241 {
6242 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIDISPLAYCONNECTOR);
6243 if (pThisCC->pDrv)
6244 {
6245 /* pThisCC->pDrv->pbData can be NULL when there is no framebuffer. */
6246 if ( pThisCC->pDrv->pfnRefresh
6247 && pThisCC->pDrv->pfnResize
6248 && pThisCC->pDrv->pfnUpdateRect)
6249 rc = VINF_SUCCESS;
6250 else
6251 {
6252 Assert(pThisCC->pDrv->pfnRefresh);
6253 Assert(pThisCC->pDrv->pfnResize);
6254 Assert(pThisCC->pDrv->pfnUpdateRect);
6255 pThisCC->pDrv = NULL;
6256 pThisCC->pDrvBase = NULL;
6257 rc = VERR_INTERNAL_ERROR;
6258 }
6259# ifdef VBOX_WITH_VIDEOHWACCEL
6260 if(rc == VINF_SUCCESS)
6261 {
6262 rc = vbvaVHWAConstruct(pDevIns, pThis, pThisCC);
6263 if (rc != VERR_NOT_IMPLEMENTED)
6264 AssertRC(rc);
6265 }
6266# endif
6267 }
6268 else
6269 {
6270 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
6271 pThisCC->pDrvBase = NULL;
6272 rc = VERR_PDM_MISSING_INTERFACE;
6273 }
6274 }
6275 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6276 {
6277 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6278 rc = VINF_SUCCESS;
6279 }
6280 else
6281 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
6282 return rc;
6283 }
6284
6285 default:
6286 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6287 return VERR_PDM_NO_SUCH_LUN;
6288 }
6289}
6290
6291
6292/**
6293 * @interface_method_impl{PDMDEVREG,pfnDetach}
6294 *
6295 * This is like unplugging the monitor while the PC is still running.
6296 */
6297static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6298{
6299 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6300 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
6301 RT_NOREF(fFlags);
6302
6303 /*
6304 * Reset the interfaces and update the controller state.
6305 */
6306 switch (iLUN)
6307 {
6308 /* LUN #0: Display port. */
6309 case 0:
6310 pThisCC->pDrv = NULL;
6311 pThisCC->pDrvBase = NULL;
6312 break;
6313
6314 default:
6315 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6316 break;
6317 }
6318}
6319
6320
6321/**
6322 * @interface_method_impl{PDMDEVREG,pfnDestruct}
6323 */
6324static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6325{
6326 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
6327 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6328 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6329 LogFlow(("vgaR3Destruct:\n"));
6330
6331# ifdef VBOX_WITH_VDMA
6332 if (pThisCC->pVdma)
6333 vboxVDMADestruct(pThisCC->pVdma);
6334# endif
6335
6336# ifdef VBOX_WITH_VMSVGA
6337 if (pThis->fVMSVGAEnabled)
6338 vmsvgaR3Destruct(pDevIns);
6339# endif
6340
6341# ifdef VBOX_WITH_HGSMI
6342 VBVADestroy(pThisCC);
6343# endif
6344
6345 /*
6346 * Free MM heap pointers.
6347 */
6348 if (pThisCC->pbVBEExtraData)
6349 {
6350 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVBEExtraData);
6351 pThisCC->pbVBEExtraData = NULL;
6352 }
6353 if (pThisCC->pbVgaBios)
6354 {
6355 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6356 pThisCC->pbVgaBios = NULL;
6357 }
6358
6359 if (pThisCC->pszVgaBiosFile)
6360 {
6361 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6362 pThisCC->pszVgaBiosFile = NULL;
6363 }
6364
6365 if (pThisCC->pszLogoFile)
6366 {
6367 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
6368 pThisCC->pszLogoFile = NULL;
6369 }
6370
6371 if (pThisCC->pbLogo)
6372 {
6373 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbLogo);
6374 pThisCC->pbLogo = NULL;
6375 }
6376
6377# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
6378 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSectIRQ);
6379# endif
6380 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
6381 return VINF_SUCCESS;
6382}
6383
6384
6385/**
6386 * Adjust VBE mode information
6387 *
6388 * Depending on the configured VRAM size, certain parts of VBE mode
6389 * information must be updated.
6390 *
6391 * @param pThis The device instance data.
6392 * @param pMode The mode information structure.
6393 */
6394static void vgaR3AdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6395{
6396 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6397 unsigned bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6398 /* The "number of image pages" is really the max page index... */
6399 unsigned maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6400 if (maxPage > 255)
6401 maxPage = 255; /* 8-bit value. */
6402 pMode->info.NumberOfImagePages = maxPage;
6403 pMode->info.LinNumberOfPages = maxPage;
6404}
6405
6406
6407/**
6408 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6409 */
6410static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6411{
6412 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6413 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6414 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6415 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
6416 int rc;
6417 unsigned i;
6418 uint32_t cCustomModes;
6419 uint32_t cyReduction;
6420 uint32_t cbPitch;
6421 PVBEHEADER pVBEDataHdr;
6422 ModeInfoListItem *pCurMode;
6423 unsigned cb;
6424
6425 Assert(iInstance == 0);
6426
6427 /*
6428 * Init static data.
6429 */
6430 static bool s_fExpandDone = false;
6431 if (!s_fExpandDone)
6432 {
6433 s_fExpandDone = true;
6434 vgaR3InitExpand();
6435 }
6436
6437 /*
6438 * Validate configuration.
6439 */
6440 static const char s_szMscWorkaround[] = "VRamSize"
6441 "|MonitorCount"
6442 "|FadeIn"
6443 "|FadeOut"
6444 "|LogoTime"
6445 "|LogoFile"
6446 "|ShowBootMenu"
6447 "|BiosRom"
6448 "|RealRetrace"
6449 "|CustomVideoModes"
6450 "|HeightReduction"
6451 "|CustomVideoMode1"
6452 "|CustomVideoMode2"
6453 "|CustomVideoMode3"
6454 "|CustomVideoMode4"
6455 "|CustomVideoMode5"
6456 "|CustomVideoMode6"
6457 "|CustomVideoMode7"
6458 "|CustomVideoMode8"
6459 "|CustomVideoMode9"
6460 "|CustomVideoMode10"
6461 "|CustomVideoMode11"
6462 "|CustomVideoMode12"
6463 "|CustomVideoMode13"
6464 "|CustomVideoMode14"
6465 "|CustomVideoMode15"
6466 "|CustomVideoMode16"
6467 "|MaxBiosXRes"
6468 "|MaxBiosYRes"
6469# ifdef VBOX_WITH_VMSVGA
6470 "|VMSVGAEnabled"
6471 "|VMSVGA10"
6472 "|VMSVGAPciId"
6473 "|VMSVGAPciBarLayout"
6474 "|VMSVGAFifoSize"
6475 "|VmSvga3"
6476 "|VmSvgaExposeLegacyVga"
6477# endif
6478# ifdef VBOX_WITH_VMSVGA3D
6479 "|VMSVGA3dEnabled"
6480 "|VMSVGA3dOverlayEnabled"
6481 "|VMSVGA3dMSAA"
6482# endif
6483 "|SuppressNewYearSplash"
6484 "|3DEnabled";
6485
6486 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, s_szMscWorkaround, "");
6487
6488 /*
6489 * Init state data.
6490 */
6491 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6492 AssertLogRelRCReturn(rc, rc);
6493 if (pThis->vram_size > VGA_VRAM_MAX)
6494 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6495 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6496 if (pThis->vram_size < VGA_VRAM_MIN)
6497 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6498 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6499 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6500 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6501 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6502
6503 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6504 AssertLogRelRCReturn(rc, rc);
6505
6506 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
6507
6508 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "3DEnabled", &pThis->f3DEnabled, false);
6509 AssertLogRelRCReturn(rc, rc);
6510 Log(("VGA: f3DEnabled=%RTbool\n", pThis->f3DEnabled));
6511
6512# ifdef VBOX_WITH_VMSVGA
6513 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6514 AssertLogRelRCReturn(rc, rc);
6515 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6516
6517 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA10", &pThis->fVMSVGA10, true);
6518 AssertLogRelRCReturn(rc, rc);
6519 Log(("VMSVGA: VMSVGA10 = %d\n", pThis->fVMSVGA10));
6520
6521 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciId", &pThis->fVMSVGAPciId, false);
6522 AssertLogRelRCReturn(rc, rc);
6523 Log(("VMSVGA: VMSVGAPciId = %d\n", pThis->fVMSVGAPciId));
6524
6525 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciBarLayout", &pThis->fVMSVGAPciBarLayout, pThis->fVMSVGAPciId);
6526 AssertLogRelRCReturn(rc, rc);
6527 Log(("VMSVGA: VMSVGAPciBarLayout = %d\n", pThis->fVMSVGAPciBarLayout));
6528
6529 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VmSvga3", &pThis->fVmSvga3, false);
6530 AssertLogRelRCReturn(rc, rc);
6531 Log(("VMSVGA: VmSvga3 = %RTbool\n", pThis->fVmSvga3));
6532
6533 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VmSvgaExposeLegacyVga", &pThis->fLegacyVgaEnabled, true);
6534 AssertLogRelRCReturn(rc, rc);
6535 Log(("VMSVGA: VmSvgaExposeLegacyVga = %RTbool\n", pThis->fLegacyVgaEnabled));
6536
6537 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6538 AssertLogRelRCReturn(rc, rc);
6539 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6540 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6541 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6542 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6543 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6544# endif
6545# ifdef VBOX_WITH_VMSVGA3D
6546 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6547 AssertLogRelRCReturn(rc, rc);
6548 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6549
6550 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dOverlayEnabled", &pThis->svga.f3DOverlayEnabled, false);
6551 AssertLogRelRCReturn(rc, rc);
6552 Log(("VMSVGA: VMSVGA3dOverlayEnabled = %d\n", pThis->svga.f3DOverlayEnabled));
6553
6554 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dMSAA", &pThis->svga.fVMSVGA3dMSAA, true);
6555 AssertLogRelRCReturn(rc, rc);
6556 Log(("VMSVGA: VMSVGA3dMSAA = %d\n", pThis->svga.fVMSVGA3dMSAA));
6557# endif
6558
6559# ifdef VBOX_WITH_VMSVGA
6560 if (pThis->fVMSVGAPciBarLayout)
6561 {
6562 if (pThis->fVmSvga3)
6563 {
6564 pThis->pciRegions.iIO = 0;
6565 pThis->pciRegions.iVRAM = 2;
6566 }
6567 else
6568 {
6569 pThis->pciRegions.iIO = 0;
6570 pThis->pciRegions.iVRAM = 1;
6571 }
6572 }
6573 else
6574 {
6575 pThis->pciRegions.iVRAM = 0;
6576 pThis->pciRegions.iIO = 1;
6577 }
6578 pThis->pciRegions.iFIFO = 2;
6579# else
6580 pThis->pciRegions.iVRAM = 0;
6581# endif
6582
6583 pThisCC->pDevIns = pDevIns;
6584
6585 vgaR3Reset(pDevIns);
6586
6587 /* The PCI devices configuration. */
6588 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
6589 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
6590
6591# ifdef VBOX_WITH_VMSVGA
6592 if (pThis->fVMSVGAEnabled)
6593 {
6594 /* Extend our VGA device with VMWare SVGA functionality. */
6595 if (pThis->fVMSVGAPciId)
6596 {
6597 PDMPciDevSetVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6598 if (pThis->fVmSvga3)
6599 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA3);
6600 else
6601 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6602 }
6603 else
6604 {
6605 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6606 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6607 }
6608 PDMPciDevSetSubSystemVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6609 if (pThis->fVmSvga3)
6610 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA3);
6611 else
6612 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6613 }
6614 else
6615# endif /* VBOX_WITH_VMSVGA */
6616 {
6617 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6618 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6619 }
6620 PDMPciDevSetClassSub(pPciDev, 0x00); /* VGA controller */
6621 PDMPciDevSetClassBase(pPciDev, 0x03);
6622 PDMPciDevSetHeaderType(pPciDev, 0x00);
6623# if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6624 PDMPciDevSetInterruptPin(pPciDev, 1);
6625# endif
6626
6627 /* the interfaces. */
6628 pThisCC->IBase.pfnQueryInterface = vgaR3PortQueryInterface;
6629
6630 pThisCC->IPort.pfnUpdateDisplay = vgaR3PortUpdateDisplay;
6631 pThisCC->IPort.pfnUpdateDisplayAll = vgaR3PortUpdateDisplayAll;
6632 pThisCC->IPort.pfnQueryVideoMode = vgaR3PortQueryVideoMode;
6633 pThisCC->IPort.pfnSetRefreshRate = vgaR3PortSetRefreshRate;
6634 pThisCC->IPort.pfnTakeScreenshot = vgaR3PortTakeScreenshot;
6635 pThisCC->IPort.pfnFreeScreenshot = vgaR3PortFreeScreenshot;
6636 pThisCC->IPort.pfnDisplayBlt = vgaR3PortDisplayBlt;
6637 pThisCC->IPort.pfnUpdateDisplayRect = vgaR3PortUpdateDisplayRect;
6638 pThisCC->IPort.pfnCopyRect = vgaR3PortCopyRect;
6639 pThisCC->IPort.pfnSetRenderVRAM = vgaR3PortSetRenderVRAM;
6640 pThisCC->IPort.pfnSetViewport = NULL;
6641 pThisCC->IPort.pfnReportMonitorPositions = NULL;
6642# ifdef VBOX_WITH_VMSVGA
6643 if (pThis->fVMSVGAEnabled)
6644 {
6645 pThisCC->IPort.pfnSetViewport = vmsvgaR3PortSetViewport;
6646 pThisCC->IPort.pfnReportMonitorPositions = vmsvgaR3PortReportMonitorPositions;
6647 }
6648# endif
6649 pThisCC->IPort.pfnSendModeHint = vbvaR3PortSendModeHint;
6650 pThisCC->IPort.pfnReportHostCursorCapabilities = vgaR3PortReportHostCursorCapabilities;
6651 pThisCC->IPort.pfnReportHostCursorPosition = vgaR3PortReportHostCursorPosition;
6652
6653# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
6654 pThisCC->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaR3VHWACommandCompleteAsync;
6655# endif
6656
6657 pThisCC->ILeds.pfnQueryStatusLed = vgaR3PortQueryStatusLed;
6658 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6659
6660 /*
6661 * We use our own critical section to avoid unncessary pointer indirections
6662 * in interface methods (as well as for historical reasons).
6663 */
6664 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6665 AssertRCReturn(rc, rc);
6666 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6667 AssertRCReturn(rc, rc);
6668
6669# ifdef VBOX_WITH_HGSMI
6670 /*
6671 * This critical section is used by vgaR3IOPortHgsmiWrite, VBVARaiseIrq and VBVAOnResume
6672 * for some IRQ related synchronization.
6673 */
6674 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6675 AssertRCReturn(rc, rc);
6676# endif
6677
6678 /*
6679 * PCI device registration.
6680 */
6681 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6682 if (RT_FAILURE(rc))
6683 return rc;
6684 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6685 if (pPciDev->uDevFn != 16 && iInstance == 0)
6686 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pPciDev->uDevFn));
6687
6688# ifdef VBOX_WITH_VMSVGA
6689 pThis->hIoPortVmSvga = NIL_IOMIOPORTHANDLE;
6690 pThis->hMmio2VmSvgaFifo = NIL_PGMMMIO2HANDLE;
6691 pThis->hMmioSvga3 = NIL_IOMMMIOHANDLE;
6692 if (pThis->fVMSVGAEnabled)
6693 {
6694 if (pThis->fVmSvga3)
6695 {
6696 /* Register the MMIO register region. */
6697 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, pThis->pciRegions.iIO, 4 * _1M, PCI_ADDRESS_SPACE_MEM,
6698 vmsvga3MmioWrite, vmsvga3MmioRead, NULL /*pvUser*/,
6699 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED,
6700 "VMSVGA3-MMIO", &pThis->hMmioSvga3);
6701 AssertRCReturn(rc, rc);
6702 }
6703 else
6704 {
6705 /* Register the io command ports. */
6706 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, pThis->pciRegions.iIO, 0x10, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/,
6707 "VMSVGA", NULL /*paExtDescs*/, &pThis->hIoPortVmSvga);
6708 AssertRCReturn(rc, rc);
6709
6710 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iFIFO, pThis->svga.cbFIFO,
6711 PCI_ADDRESS_SPACE_MEM, 0 /*fFlags*/, vmsvgaR3PciIORegionFifoMapUnmap,
6712 "VMSVGA-FIFO", (void **)&pThisCC->svga.pau32FIFO, &pThis->hMmio2VmSvgaFifo);
6713 AssertRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6714 N_("Failed to create VMSVGA FIFO (%u bytes)"), pThis->svga.cbFIFO));
6715 }
6716
6717 pPciDev->pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6718 }
6719# endif /* VBOX_WITH_VMSVGA */
6720
6721 /*
6722 * Allocate VRAM and create a PCI region for it.
6723 */
6724 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iVRAM, pThis->vram_size,
6725 PCI_ADDRESS_SPACE_MEM_PREFETCH, PGMPHYS_MMIO2_FLAGS_TRACK_DIRTY_PAGES,
6726 vgaR3PciIORegionVRamMapUnmap, "VRam", (void **)&pThisCC->pbVRam, &pThis->hMmio2VRam);
6727 AssertLogRelRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6728 N_("Failed to allocate %u bytes of VRAM"), pThis->vram_size));
6729
6730 if (pThis->fLegacyVgaEnabled)
6731 {
6732 /*
6733 * Register I/O ports.
6734 */
6735# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_phIoPort) do { \
6736 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, a_uPort, a_cPorts, IOM_IOPORT_F_ABS, \
6737 a_pfnWrite, a_pfnRead, "VGA - " a_szDesc, NULL /*paExtDescs*/, a_phIoPort); \
6738 AssertRCReturn(rc, rc); \
6739 } while (0)
6740 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", &pThis->hIoPortAr);
6741 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", &pThis->hIoPortMsrSt00);
6742 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", &pThis->hIoPort3c3);
6743 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", &pThis->hIoPortSr);
6744 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", &pThis->hIoPortDac);
6745 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ &pThis->hIoPortPos);
6746 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", &pThis->hIoPortGr);
6747
6748 /* Note! Ralf Brown lists 0x3b0-0x3b1, 0x3b2-0x3b3 and 0x3b6-0x3b7 as "the same as" 0x3b4-0x3b5. */
6749 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", &pThis->hIoPortMdaCrt);
6750 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", &pThis->hIoPortMdaFcrSt);
6751 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", &pThis->hIoPortCgaCrt);
6752 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", &pThis->hIoPortCgaFcrSt);
6753
6754# ifdef CONFIG_BOCHS_VBE
6755 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", &pThis->hIoPortVbeIndex);
6756 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", &pThis->hIoPortVbeData);
6757# endif /* CONFIG_BOCHS_VBE */
6758
6759# ifdef VBOX_WITH_HGSMI
6760 /* Use reserved VGA IO ports for HGSMI. */
6761 REG_PORT(VGA_PORT_HGSMI_HOST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI host (3b0-3b3)", &pThis->hIoPortHgsmiHost);
6762 REG_PORT(VGA_PORT_HGSMI_GUEST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI guest (3d0-3d3)", &pThis->hIoPortHgsmiGuest);
6763# endif /* VBOX_WITH_HGSMI */
6764
6765# undef REG_PORT
6766
6767 /* vga bios */
6768 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_PRINTF_PORT, 1 /*cPorts*/, vgaIoPortWriteBios, vgaIoPortReadBios,
6769 "VGA BIOS debug/panic", NULL /*paExtDescs*/, &pThis->hIoPortBios);
6770 AssertRCReturn(rc, rc);
6771
6772 /*
6773 * The MDA/CGA/EGA/VGA/whatever fixed MMIO area.
6774 */
6775 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, 0x000a0000, 0x00020000,
6776 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
6777 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
6778 vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/,
6779 "VGA - VGA Video Buffer", &pThis->hMmioLegacy);
6780 AssertRCReturn(rc, rc);
6781
6782 /*
6783 * Get the VGA BIOS ROM file name.
6784 */
6785 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThisCC->pszVgaBiosFile);
6786 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6787 {
6788 pThisCC->pszVgaBiosFile = NULL;
6789 rc = VINF_SUCCESS;
6790 }
6791 else if (RT_FAILURE(rc))
6792 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6793 else if (!*pThisCC->pszVgaBiosFile)
6794 {
6795 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6796 pThisCC->pszVgaBiosFile = NULL;
6797 }
6798
6799 /*
6800 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6801 */
6802 RTFILE FileVgaBios = NIL_RTFILE;
6803 if (pThisCC->pszVgaBiosFile)
6804 {
6805 rc = RTFileOpen(&FileVgaBios, pThisCC->pszVgaBiosFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6806 if (RT_SUCCESS(rc))
6807 {
6808 rc = RTFileQuerySize(FileVgaBios, &pThisCC->cbVgaBios);
6809 if (RT_SUCCESS(rc))
6810 {
6811 if ( RT_ALIGN(pThisCC->cbVgaBios, _4K) != pThisCC->cbVgaBios
6812 || pThisCC->cbVgaBios > _64K
6813 || pThisCC->cbVgaBios < 16 * _1K)
6814 rc = VERR_TOO_MUCH_DATA;
6815 }
6816 }
6817 if (RT_FAILURE(rc))
6818 {
6819 /*
6820 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6821 */
6822 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThisCC->pszVgaBiosFile, rc));
6823 RTFileClose(FileVgaBios);
6824 FileVgaBios = NIL_RTFILE;
6825 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6826 pThisCC->pszVgaBiosFile = NULL;
6827 }
6828 }
6829
6830 /*
6831 * Attempt to get the VGA BIOS ROM data from file.
6832 */
6833 if (pThisCC->pszVgaBiosFile)
6834 {
6835 /*
6836 * Allocate buffer for the VGA BIOS ROM data.
6837 */
6838 pThisCC->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbVgaBios);
6839 if (pThisCC->pbVgaBios)
6840 {
6841 rc = RTFileRead(FileVgaBios, pThisCC->pbVgaBios, pThisCC->cbVgaBios, NULL);
6842 if (RT_FAILURE(rc))
6843 {
6844 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThisCC->cbVgaBios, rc));
6845 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6846 pThisCC->pbVgaBios = NULL;
6847 }
6848 rc = VINF_SUCCESS;
6849 }
6850 else
6851 rc = VERR_NO_MEMORY;
6852 }
6853 else
6854 pThisCC->pbVgaBios = NULL;
6855
6856 /* cleanup */
6857 if (FileVgaBios != NIL_RTFILE)
6858 RTFileClose(FileVgaBios);
6859
6860 /* If we were unable to get the data from file for whatever reason, fall
6861 back to the built-in ROM image. */
6862 const uint8_t *pbVgaBiosBinary;
6863 uint64_t cbVgaBiosBinary;
6864 uint32_t fFlags = 0;
6865 if (pThisCC->pbVgaBios == NULL)
6866 {
6867 CPUMMICROARCH enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
6868 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6869 || enmMicroarch == kCpumMicroarch_Intel_80186
6870 || enmMicroarch == kCpumMicroarch_NEC_V20
6871 || enmMicroarch == kCpumMicroarch_NEC_V30)
6872 {
6873 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6874 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6875 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6876 }
6877 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6878 {
6879 pbVgaBiosBinary = g_abVgaBiosBinary286;
6880 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6881 LogRel(("VGA: Using the 286 BIOS image!\n"));
6882 }
6883 else
6884 {
6885 pbVgaBiosBinary = g_abVgaBiosBinary386;
6886 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6887 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6888 }
6889 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6890 }
6891 else
6892 {
6893 pbVgaBiosBinary = pThisCC->pbVgaBios;
6894 cbVgaBiosBinary = pThisCC->cbVgaBios;
6895 }
6896
6897 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6898 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, GUEST_PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6899 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6900 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6901 fFlags, "VGA BIOS");
6902 AssertRCReturn(rc, rc);
6903 }
6904
6905 /*
6906 * Saved state.
6907 */
6908 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6909 NULL, vgaR3LiveExec, NULL,
6910 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6911 vgaR3LoadPrep, vgaR3LoadExec, vgaR3LoadDone);
6912 AssertRCReturn(rc, rc);
6913
6914 /*
6915 * Create the refresh timer.
6916 */
6917 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, vgaR3TimerRefresh, NULL,
6918 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "VGA Refresh", &pThis->hRefreshTimer);
6919 AssertRCReturn(rc, rc);
6920
6921 /*
6922 * Attach to the display.
6923 */
6924 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6925 AssertRCReturn(rc, rc);
6926
6927 /*
6928 * Initialize the retrace flag.
6929 */
6930 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6931 AssertLogRelRCReturn(rc, rc);
6932
6933 uint16_t maxBiosXRes;
6934 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6935 AssertLogRelRCReturn(rc, rc);
6936 uint16_t maxBiosYRes;
6937 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6938 AssertLogRelRCReturn(rc, rc);
6939
6940 if (pThis->fLegacyVgaEnabled)
6941 {
6942 /*
6943 * Compute buffer size for the VBE BIOS Extra Data.
6944 */
6945 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6946
6947 rc = pHlp->pfnCFGMQueryU32(pCfg, "HeightReduction", &cyReduction);
6948 if (RT_SUCCESS(rc) && cyReduction)
6949 cb *= 2; /* Default mode list will be twice long */
6950 else
6951 cyReduction = 0;
6952
6953 rc = pHlp->pfnCFGMQueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6954 if (RT_SUCCESS(rc) && cCustomModes)
6955 cb += sizeof(ModeInfoListItem) * cCustomModes;
6956 else
6957 cCustomModes = 0;
6958
6959 /*
6960 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6961 */
6962 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6963 pThisCC->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6964 pThisCC->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThisCC->cbVBEExtraData);
6965 if (!pThisCC->pbVBEExtraData)
6966 return VERR_NO_MEMORY;
6967
6968 pVBEDataHdr = (PVBEHEADER)pThisCC->pbVBEExtraData;
6969 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6970 pVBEDataHdr->cbData = cb;
6971
6972 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6973 for (i = 0; i < MODE_INFO_SIZE; i++)
6974 {
6975 uint32_t pixelWidth, reqSize;
6976 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6977 pixelWidth = 2;
6978 else
6979 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6980 reqSize = mode_info_list[i].info.XResolution
6981 * mode_info_list[i].info.YResolution
6982 * pixelWidth;
6983 if (reqSize >= pThis->vram_size)
6984 continue;
6985 if (!reqSize)
6986 continue;
6987 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6988 || mode_info_list[i].info.YResolution > maxBiosYRes)
6989 continue;
6990 *pCurMode = mode_info_list[i];
6991 vgaR3AdjustModeInfo(pThis, pCurMode);
6992 pCurMode++;
6993 }
6994
6995 /*
6996 * Copy default modes with subtracted YResolution.
6997 */
6998 if (cyReduction)
6999 {
7000 ModeInfoListItem *pDefMode = mode_info_list;
7001 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
7002 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
7003 {
7004 uint32_t pixelWidth, reqSize;
7005 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
7006 pixelWidth = 2;
7007 else
7008 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
7009 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
7010 if (reqSize >= pThis->vram_size)
7011 continue;
7012 if ( pDefMode->info.XResolution > maxBiosXRes
7013 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
7014 continue;
7015 *pCurMode = *pDefMode;
7016 pCurMode->mode += 0x30;
7017 pCurMode->info.YResolution -= cyReduction;
7018 pCurMode++;
7019 }
7020 }
7021
7022
7023 /*
7024 * Add custom modes.
7025 */
7026 if (cCustomModes)
7027 {
7028 uint16_t u16CurMode = VBE_VBOX_MODE_CUSTOM1;
7029 for (i = 1; i <= cCustomModes; i++)
7030 {
7031 char szExtraDataKey[sizeof("CustomVideoModeXX")];
7032 char *pszExtraData = NULL;
7033
7034 /* query and decode the custom mode string. */
7035 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
7036 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
7037 if (RT_SUCCESS(rc))
7038 {
7039 ModeInfoListItem *pDefMode = mode_info_list;
7040 unsigned int cx, cy, cBits, cParams, j;
7041 uint16_t u16DefMode;
7042
7043 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
7044 if ( cParams != 3
7045 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
7046 {
7047 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
7048 return VERR_VGA_INVALID_CUSTOM_MODE;
7049 }
7050 if (!cx || !cy)
7051 {
7052 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cx=%u, cy=%u\n", pszExtraData, szExtraDataKey, cx, cy));
7053 return VERR_VGA_INVALID_CUSTOM_MODE;
7054 }
7055 cbPitch = calc_line_pitch(cBits, cx);
7056 if (cy * cbPitch >= pThis->vram_size)
7057 {
7058 AssertMsgFailed(("Configuration error: custom video mode %dx%dx%dbits is too large for the virtual video memory of %dMb. Please increase the video memory size.\n",
7059 cx, cy, cBits, pThis->vram_size / _1M));
7060 return VERR_VGA_INVALID_CUSTOM_MODE;
7061 }
7062 PDMDevHlpMMHeapFree(pDevIns, pszExtraData);
7063
7064 /* Use defaults from max@bpp mode. */
7065 switch (cBits)
7066 {
7067 case 8:
7068 u16DefMode = VBE_VESA_MODE_1024X768X8;
7069 break;
7070
7071 case 16:
7072 u16DefMode = VBE_VESA_MODE_1024X768X565;
7073 break;
7074
7075 case 24:
7076 u16DefMode = VBE_VESA_MODE_1024X768X888;
7077 break;
7078
7079 case 32:
7080 u16DefMode = VBE_OWN_MODE_1024X768X8888;
7081 break;
7082
7083 default: /* gcc, shut up! */
7084 AssertMsgFailed(("gone postal!\n"));
7085 continue;
7086 }
7087
7088 /* mode_info_list is not terminated */
7089 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
7090 pDefMode++;
7091 Assert(j < MODE_INFO_SIZE);
7092
7093 *pCurMode = *pDefMode;
7094 pCurMode->mode = u16CurMode++;
7095
7096 /* adjust defaults */
7097 pCurMode->info.XResolution = cx;
7098 pCurMode->info.YResolution = cy;
7099 pCurMode->info.BytesPerScanLine = cbPitch;
7100 pCurMode->info.LinBytesPerScanLine = cbPitch;
7101 vgaR3AdjustModeInfo(pThis, pCurMode);
7102
7103 /* commit it */
7104 pCurMode++;
7105 }
7106 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
7107 {
7108 AssertMsgFailed(("pHlp->pfnCFGMQueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
7109 return rc;
7110 }
7111 } /* foreach custom mode key */
7112 }
7113
7114 /*
7115 * Add the "End of list" mode.
7116 */
7117 memset(pCurMode, 0, sizeof(*pCurMode));
7118 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
7119
7120 /*
7121 * Register I/O Port for the VBE BIOS Extra Data.
7122 */
7123 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_EXTRA_PORT, 1 /*cPorts*/, vbeR3IOPortWriteVbeExtra, vbeR3IoPortReadVbeExtra,
7124 "VBE BIOS Extra Data", NULL /*paExtDesc*/, &pThis->hIoPortVbeExtra);
7125 AssertRCReturn(rc, rc);
7126
7127 /*
7128 * Register I/O Port for the BIOS Logo.
7129 */
7130 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, LOGO_IO_PORT, 1 /*cPorts*/, vbeR3IoPortWriteCmdLogo, vbeR3IoPortReadCmdLogo,
7131 "BIOS Logo", NULL /*paExtDesc*/, &pThis->hIoPortCmdLogo);
7132 AssertRCReturn(rc, rc);
7133
7134 /*
7135 * Register debugger info callbacks.
7136 */
7137 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaR3InfoState);
7138 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaR3InfoText);
7139 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaR3InfoCR);
7140 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaR3InfoGR);
7141 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaR3InfoSR);
7142 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaR3InfoAR);
7143 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaR3InfoPlanar);
7144 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaR3InfoDAC);
7145 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaR3InfoVBE);
7146 }
7147
7148 /*
7149 * Construct the logo header.
7150 */
7151 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
7152
7153 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
7154 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7155 LogoHdr.fu8FadeIn = 1;
7156 else if (RT_FAILURE(rc))
7157 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeIn\" as integer failed"));
7158
7159 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
7160 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7161 LogoHdr.fu8FadeOut = 1;
7162 else if (RT_FAILURE(rc))
7163 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeOut\" as integer failed"));
7164
7165 rc = pHlp->pfnCFGMQueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
7166 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7167 LogoHdr.u16LogoMillies = 0;
7168 else if (RT_FAILURE(rc))
7169 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"LogoTime\" as integer failed"));
7170
7171 rc = pHlp->pfnCFGMQueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
7172 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7173 LogoHdr.fu8ShowBootMenu = 0;
7174 else if (RT_FAILURE(rc))
7175 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
7176
7177# if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
7178 /* Disable the logo abd menu if all default settings. */
7179 if ( LogoHdr.fu8FadeIn
7180 && LogoHdr.fu8FadeOut
7181 && LogoHdr.u16LogoMillies == 0
7182 && LogoHdr.fu8ShowBootMenu == 2)
7183 {
7184 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
7185 LogoHdr.u16LogoMillies = 500;
7186 }
7187# endif
7188
7189 /* Delay the logo a little bit */
7190 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
7191 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
7192
7193 /*
7194 * Get the Logo file name.
7195 */
7196 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LogoFile", &pThisCC->pszLogoFile);
7197 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7198 pThisCC->pszLogoFile = NULL;
7199 else if (RT_FAILURE(rc))
7200 return PDMDEV_SET_ERROR(pDevIns, rc,
7201 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
7202 else if (!*pThisCC->pszLogoFile)
7203 {
7204 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7205 pThisCC->pszLogoFile = NULL;
7206 }
7207
7208 /*
7209 * Determine the logo size, open any specified logo file in the process.
7210 */
7211 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7212 RTFILE FileLogo = NIL_RTFILE;
7213 if (pThisCC->pszLogoFile)
7214 {
7215 rc = RTFileOpen(&FileLogo, pThisCC->pszLogoFile,
7216 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
7217 if (RT_SUCCESS(rc))
7218 {
7219 uint64_t cbFile;
7220 rc = RTFileQuerySize(FileLogo, &cbFile);
7221 if (RT_SUCCESS(rc))
7222 {
7223 if (cbFile > 0 && cbFile < 32*_1M)
7224 LogoHdr.cbLogo = (uint32_t)cbFile;
7225 else
7226 rc = VERR_TOO_MUCH_DATA;
7227 }
7228 }
7229 if (RT_FAILURE(rc))
7230 {
7231 /*
7232 * Ignore failure and fall back to the default logo.
7233 */
7234 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThisCC->pszLogoFile, rc));
7235 if (FileLogo != NIL_RTFILE)
7236 RTFileClose(FileLogo);
7237 FileLogo = NIL_RTFILE;
7238 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7239 pThisCC->pszLogoFile = NULL;
7240 }
7241 }
7242
7243 /*
7244 * Disable graphic splash screen if it doesn't fit into VRAM.
7245 */
7246 if (pThis->vram_size < LOGO_MAX_SIZE)
7247 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7248
7249 /*
7250 * Allocate buffer for the logo data.
7251 * Let us fall back to default logo on read failure.
7252 */
7253 pThisCC->cbLogo = LogoHdr.cbLogo;
7254 if (g_cbVgaDefBiosLogo)
7255 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogo);
7256# ifndef VBOX_OSE
7257 if (g_cbVgaDefBiosLogoNY)
7258 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogoNY);
7259# endif
7260 pThisCC->cbLogo += sizeof(LogoHdr);
7261
7262 pThisCC->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbLogo);
7263 if (pThisCC->pbLogo)
7264 {
7265 /*
7266 * Write the logo header.
7267 */
7268 PLOGOHDR pLogoHdr = (PLOGOHDR)pThisCC->pbLogo;
7269 *pLogoHdr = LogoHdr;
7270
7271 /*
7272 * Write the logo bitmap.
7273 */
7274 if (pThisCC->pszLogoFile)
7275 {
7276 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7277 if (RT_SUCCESS(rc))
7278 rc = vbeR3ParseBitmap(pThisCC);
7279 if (RT_FAILURE(rc))
7280 {
7281 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7282 rc, pThisCC->pszLogoFile));
7283 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7284 }
7285 }
7286 if ( !pThisCC->pszLogoFile
7287 || RT_FAILURE(rc))
7288 {
7289# ifndef VBOX_OSE
7290 RTTIMESPEC Now;
7291 RTTimeLocalNow(&Now);
7292 RTTIME T;
7293 RTTimeLocalExplode(&T, &Now);
7294 bool fSuppressNewYearSplash = false;
7295 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SuppressNewYearSplash", &fSuppressNewYearSplash, true);
7296 if ( !fSuppressNewYearSplash
7297 && (T.u16YearDay > 353 || T.u16YearDay < 10))
7298 {
7299 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogoNY;
7300 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogoNY, LogoHdr.cbLogo);
7301 pThisCC->fBootMenuInverse = true;
7302 }
7303 else
7304# endif
7305 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7306 rc = vbeR3ParseBitmap(pThisCC);
7307 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Parsing of internal bitmap failed! vbeR3ParseBitmap() -> %Rrc\n", rc), rc);
7308 }
7309
7310 rc = VINF_SUCCESS;
7311 }
7312 else
7313 rc = VERR_NO_MEMORY;
7314
7315 /*
7316 * Cleanup.
7317 */
7318 if (FileLogo != NIL_RTFILE)
7319 RTFileClose(FileLogo);
7320
7321# ifdef VBOX_WITH_HGSMI
7322 VBVAInit(pDevIns, pThis, pThisCC);
7323# endif
7324
7325# ifdef VBOX_WITH_VDMA
7326 if (rc == VINF_SUCCESS)
7327 {
7328 rc = vboxVDMAConstruct(pThis, pThisCC, 1024);
7329 AssertRC(rc);
7330 }
7331# endif
7332
7333# ifdef VBOX_WITH_VMSVGA
7334 if ( rc == VINF_SUCCESS
7335 && pThis->fVMSVGAEnabled)
7336 rc = vmsvgaR3Init(pDevIns);
7337# endif
7338
7339 /*
7340 * Statistics.
7341 */
7342# ifdef VBOX_WITH_STATISTICS
7343 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7344 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7345 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7346 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7347 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapPage, STAMTYPE_COUNTER, "MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMmioMapMmio2Page.");
7348 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapReset, STAMTYPE_COUNTER, "MapPageReset", STAMUNIT_OCCURENCES, "Calls to IOMMmioResetRegion.");
7349 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaR3PortUpdateDisplay().");
7350# endif
7351# ifdef VBOX_WITH_HGSMI
7352 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatHgsmiMdaCgaAccesses, STAMTYPE_COUNTER, "HgmsiMdaCgaAccesses", STAMUNIT_OCCURENCES, "Number of non-HGMSI accesses for 03b0-3b3 and 03d0-3d3.");
7353# endif
7354
7355 /* Init latched access mask. */
7356 pThis->uMaskLatchAccess = 0x3ff;
7357
7358 if (RT_SUCCESS(rc))
7359 {
7360 PPDMIBASE pBase;
7361 /*
7362 * Attach status driver (optional).
7363 */
7364 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
7365 if (RT_SUCCESS(rc))
7366 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7367 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7368 {
7369 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7370 rc = VINF_SUCCESS;
7371 }
7372 else
7373 {
7374 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7375 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7376 }
7377 }
7378 return rc;
7379}
7380
7381#else /* !IN_RING3 */
7382
7383/**
7384 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
7385 */
7386static DECLCALLBACK(int) vgaRZConstruct(PPDMDEVINS pDevIns)
7387{
7388 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
7389 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
7390 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
7391
7392 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
7393 AssertRCReturn(rc, rc);
7394
7395 if (pThis->fLegacyVgaEnabled)
7396 {
7397 /*
7398 * Set I/O port callbacks for this context.
7399 * We just copy the ring-3 registration bits and remove the '&' before the handle.
7400 */
7401# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_hIoPort) do { \
7402 rc = PDMDevHlpIoPortSetUpContext(pDevIns, a_hIoPort, a_pfnWrite, a_pfnRead, NULL /*pvUser*/); \
7403 AssertRCReturn(rc, rc); \
7404 } while (0)
7405
7406 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", pThis->hIoPortAr);
7407 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", pThis->hIoPortMsrSt00);
7408 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", pThis->hIoPort3c3);
7409 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", pThis->hIoPortSr);
7410 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", pThis->hIoPortDac);
7411 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ pThis->hIoPortPos);
7412 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", pThis->hIoPortGr);
7413
7414 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", pThis->hIoPortMdaCrt);
7415 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", pThis->hIoPortMdaFcrSt);
7416 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", pThis->hIoPortCgaCrt);
7417 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", pThis->hIoPortCgaFcrSt);
7418
7419# ifdef CONFIG_BOCHS_VBE
7420 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", pThis->hIoPortVbeIndex);
7421 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", pThis->hIoPortVbeData);
7422# endif /* CONFIG_BOCHS_VBE */
7423
7424# undef REG_PORT
7425
7426 /* BIOS port: */
7427 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortBios, vgaIoPortWriteBios, vgaIoPortReadBios, NULL /*pvUser*/);
7428 AssertRCReturn(rc, rc);
7429
7430 /*
7431 * MMIO.
7432 */
7433 rc = PDMDevHlpMmioSetUpContextEx(pDevIns, pThis->hMmioLegacy, vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/);
7434 AssertRCReturn(rc, rc);
7435 }
7436
7437# ifdef VBOX_WITH_VMSVGA
7438 if (pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE)
7439 {
7440 AssertReturn(pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7441 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortVmSvga, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/);
7442 AssertRCReturn(rc, rc);
7443 }
7444 else
7445 AssertReturn(!pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7446
7447 if (pThis->hMmioSvga3 != NIL_IOMMMIOHANDLE)
7448 {
7449 AssertReturn(pThis->fVMSVGAEnabled && pThis->fVmSvga3, VERR_INVALID_STATE);
7450 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioSvga3, vmsvga3MmioWrite, vmsvga3MmioRead, NULL /*pvUser*/);
7451 AssertRCReturn(rc, rc);
7452 }
7453 else
7454 AssertReturn(!pThis->fVmSvga3, VERR_INVALID_STATE);
7455# endif
7456
7457 /*
7458 * Map the start of the VRAM into this context.
7459 */
7460# if defined(VBOX_WITH_2X_4GB_ADDR_SPACE) || (defined(IN_RING0) && defined(VGA_WITH_PARTIAL_RING0_MAPPING))
7461 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VRam, 0 /* off */, VGA_MAPPING_SIZE, (void **)&pThisCC->pbVRam);
7462 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMmio2SetUpContext(,VRAM,0,%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
7463# endif
7464
7465 /*
7466 * Map the first page of the VMSVGA FIFO into this context (not raw-mode).
7467 * We currently only access SVGA_FIFO_MIN, SVGA_FIFO_PITCHLOCK, and SVGA_FIFO_BUSY.
7468 */
7469# if defined(VBOX_WITH_VMSVGA) && !defined(IN_RC)
7470 AssertCompile((RT_MAX(SVGA_FIFO_MIN, RT_MAX(SVGA_FIFO_PITCHLOCK, SVGA_FIFO_BUSY)) + 1) * sizeof(uint32_t) < GUEST_PAGE_SIZE);
7471 if ( pThis->fVMSVGAEnabled
7472 && !pThis->fVmSvga3)
7473 {
7474 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VmSvgaFifo, 0 /* off */, GUEST_PAGE_SIZE,
7475 (void **)&pThisCC->svga.pau32FIFO);
7476 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
7477 }
7478 else
7479 AssertReturn(pThis->hMmio2VmSvgaFifo == NIL_PGMMMIO2HANDLE, VERR_INVALID_STATE);
7480# endif
7481
7482 return VINF_SUCCESS;
7483}
7484
7485#endif /* !IN_RING3 */
7486
7487/**
7488 * The device registration structure.
7489 */
7490const PDMDEVREG g_DeviceVga =
7491{
7492 /* .u32Version = */ PDM_DEVREG_VERSION,
7493 /* .uReserved0 = */ 0,
7494 /* .szName = */ "vga",
7495 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
7496 /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS,
7497 /* .cMaxInstances = */ 1,
7498 /* .uSharedVersion = */ 42,
7499 /* .cbInstanceShared = */ sizeof(VGASTATE),
7500 /* .cbInstanceCC = */ sizeof(VGASTATECC),
7501 /* .cbInstanceRC = */ sizeof(VGASTATERC),
7502 /* .cMaxPciDevices = */ 1,
7503 /* .cMaxMsixVectors = */ 0,
7504 /* .pszDescription = */ "VGA Adaptor with VESA extensions.",
7505#if defined(IN_RING3)
7506 /* .pszRCMod = */ "VBoxDDRC.rc",
7507 /* .pszR0Mod = */ "VBoxDDR0.r0",
7508 /* .pfnConstruct = */ vgaR3Construct,
7509 /* .pfnDestruct = */ vgaR3Destruct,
7510 /* .pfnRelocate = */ vgaR3Relocate,
7511 /* .pfnMemSetup = */ NULL,
7512 /* .pfnPowerOn = */ vgaR3PowerOn,
7513 /* .pfnReset = */ vgaR3Reset,
7514 /* .pfnSuspend = */ NULL,
7515 /* .pfnResume = */ vgaR3Resume,
7516 /* .pfnAttach = */ vgaAttach,
7517 /* .pfnDetach = */ vgaDetach,
7518 /* .pfnQueryInterface = */ NULL,
7519 /* .pfnInitComplete = */ NULL,
7520 /* .pfnPowerOff = */ vgaR3PowerOff,
7521 /* .pfnSoftReset = */ NULL,
7522 /* .pfnReserved0 = */ NULL,
7523 /* .pfnReserved1 = */ NULL,
7524 /* .pfnReserved2 = */ NULL,
7525 /* .pfnReserved3 = */ NULL,
7526 /* .pfnReserved4 = */ NULL,
7527 /* .pfnReserved5 = */ NULL,
7528 /* .pfnReserved6 = */ NULL,
7529 /* .pfnReserved7 = */ NULL,
7530#elif defined(IN_RING0)
7531 /* .pfnEarlyConstruct = */ NULL,
7532 /* .pfnConstruct = */ vgaRZConstruct,
7533 /* .pfnDestruct = */ NULL,
7534 /* .pfnFinalDestruct = */ NULL,
7535 /* .pfnRequest = */ NULL,
7536 /* .pfnReserved0 = */ NULL,
7537 /* .pfnReserved1 = */ NULL,
7538 /* .pfnReserved2 = */ NULL,
7539 /* .pfnReserved3 = */ NULL,
7540 /* .pfnReserved4 = */ NULL,
7541 /* .pfnReserved5 = */ NULL,
7542 /* .pfnReserved6 = */ NULL,
7543 /* .pfnReserved7 = */ NULL,
7544#elif defined(IN_RC)
7545 /* .pfnConstruct = */ vgaRZConstruct,
7546 /* .pfnReserved0 = */ NULL,
7547 /* .pfnReserved1 = */ NULL,
7548 /* .pfnReserved2 = */ NULL,
7549 /* .pfnReserved3 = */ NULL,
7550 /* .pfnReserved4 = */ NULL,
7551 /* .pfnReserved5 = */ NULL,
7552 /* .pfnReserved6 = */ NULL,
7553 /* .pfnReserved7 = */ NULL,
7554#else
7555# error "Not in IN_RING3, IN_RING0 or IN_RC!"
7556#endif
7557 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
7558};
7559
7560#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7561
7562/*
7563 * Local Variables:
7564 * nuke-trailing-whitespace-p:nil
7565 * End:
7566 */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use