VirtualBox

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

Last change on this file since 108641 was 108641, checked in by vboxsync, 5 weeks ago

Removed 2D video acceleration (aka VHWA / VBOX_WITH_VIDEOHWACCEL). bugref:10756

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 264.8 KB
Line 
1/* $Id: DevVGA.cpp 108641 2025-03-20 12:48:42Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 AssertReturnVoid(offVRAMStart < pThis->vram_size);
392 AssertReturnVoid(offVRAMEnd <= pThis->vram_size);
393 AssertReturnVoid(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 val = pThis->ar_index;
602 break;
603 case 0x3c1:
604 index = pThis->ar_index & 0x1f;
605 if (index < 21)
606 val = pThis->ar[index];
607 else
608 val = 0;
609 break;
610 case 0x3c2:
611 val = pThis->st00;
612 break;
613 case 0x3c4:
614 val = pThis->sr_index;
615 break;
616 case 0x3c5:
617 val = pThis->sr[pThis->sr_index];
618 Log2(("vga: read SR%x = 0x%02x\n", pThis->sr_index, val));
619 break;
620 case 0x3c7:
621 val = pThis->dac_state;
622 break;
623 case 0x3c8:
624 val = pThis->dac_write_index;
625 break;
626 case 0x3c9:
627 Assert(pThis->dac_sub_index < 3);
628 val = pThis->palette[pThis->dac_read_index * 3 + pThis->dac_sub_index];
629 if (++pThis->dac_sub_index == 3) {
630 pThis->dac_sub_index = 0;
631 pThis->dac_read_index++;
632 }
633 break;
634 case 0x3ca:
635 val = pThis->fcr;
636 break;
637 case 0x3cc:
638 val = pThis->msr;
639 break;
640 case 0x3ce:
641 val = pThis->gr_index;
642 break;
643 case 0x3cf:
644 val = pThis->gr[pThis->gr_index];
645 Log2(("vga: read GR%x = 0x%02x\n", pThis->gr_index, val));
646 break;
647 case 0x3b4:
648 case 0x3d4:
649 val = pThis->cr_index;
650 break;
651 case 0x3b5:
652 case 0x3d5:
653 val = pThis->cr[pThis->cr_index];
654 Log2(("vga: read CR%x = 0x%02x\n", pThis->cr_index, val));
655 break;
656 case 0x3ba:
657 case 0x3da:
658 val = pThis->st01 = vga_retrace(pDevIns, pThis);
659 pThis->ar_flip_flop = 0;
660 break;
661 default:
662 val = 0x00;
663 break;
664 }
665 }
666 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
667 return val;
668}
669
670static void vga_ioport_write(PPDMDEVINS pDevIns, PVGASTATE pThis, uint32_t addr, uint32_t val)
671{
672 int index;
673
674 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
675
676 /* check port range access depending on color/monochrome mode */
677 if (vga_ioport_invalid(pThis, addr)) {
678 Log(("VGA: previous write ignored\n"));
679 return;
680 }
681
682 switch(addr) {
683 case 0x3c0:
684 case 0x3c1:
685 if (pThis->ar_flip_flop == 0) {
686 val &= 0x3f;
687 pThis->ar_index = val;
688 } else {
689 index = pThis->ar_index & 0x1f;
690 switch(index) {
691 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
692 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
693 pThis->ar[index] = val & 0x3f;
694 break;
695 case 0x10:
696 pThis->ar[index] = val & ~0x10;
697 break;
698 case 0x11:
699 pThis->ar[index] = val;
700 break;
701 case 0x12:
702 pThis->ar[index] = val & ~0xc0;
703 break;
704 case 0x13:
705 pThis->ar[index] = val & ~0xf0;
706 break;
707 case 0x14:
708 pThis->ar[index] = val & ~0xf0;
709 break;
710 default:
711 break;
712 }
713 }
714 pThis->ar_flip_flop ^= 1;
715 break;
716 case 0x3c2:
717 pThis->msr = val & ~0x10;
718 if (pThis->fRealRetrace)
719 vga_update_retrace_state(pThis);
720 /* The two clock select bits also determine which of the four switches
721 * is reflected in bit 4 of Input Status Register 0.
722 * This is EGA compatible behavior. See the IBM EGA Tech Ref.
723 */
724 pThis->st00 = (pThis->st00 & ~0x10) | ((EGA_SWITCHES >> ((val >> 2) & 0x3) & 0x10));
725 break;
726 case 0x3c4:
727 pThis->sr_index = val & 7;
728 break;
729 case 0x3c5:
730 Log2(("vga: write SR%x = 0x%02x\n", pThis->sr_index, val));
731 pThis->sr[pThis->sr_index] = val & sr_mask[pThis->sr_index];
732 /* Allow SR07 to disable VBE. */
733 if (pThis->sr_index == 0x07 && !(val & 1))
734 {
735 pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] = VBE_DISPI_DISABLED;
736 pThis->bank_offset = 0;
737 }
738 if (pThis->fRealRetrace && pThis->sr_index == 0x01)
739 vga_update_retrace_state(pThis);
740#ifndef IN_RC
741 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
742 if ( pThis->sr_index == 4 /* mode */
743 || pThis->sr_index == 2 /* plane mask */)
744 {
745 if (pThis->bmPageRemappedVGA != 0)
746 {
747 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
748 STAM_COUNTER_INC(&pThis->StatMapReset);
749 vgaResetRemapped(pThis);
750 }
751 }
752#endif
753 break;
754 case 0x3c7:
755 pThis->dac_read_index = val;
756 pThis->dac_sub_index = 0;
757 pThis->dac_state = 3;
758 break;
759 case 0x3c8:
760 pThis->dac_write_index = val;
761 pThis->dac_sub_index = 0;
762 pThis->dac_state = 0;
763 break;
764 case 0x3c9:
765 Assert(pThis->dac_sub_index < 3);
766 pThis->dac_cache[pThis->dac_sub_index] = val;
767 if (++pThis->dac_sub_index == 3) {
768 memcpy(&pThis->palette[pThis->dac_write_index * 3], pThis->dac_cache, 3);
769 pThis->dac_sub_index = 0;
770 pThis->dac_write_index++;
771 }
772 break;
773 case 0x3ce:
774 pThis->gr_index = val & 0x0f;
775 break;
776 case 0x3cf:
777 Log2(("vga: write GR%x = 0x%02x\n", pThis->gr_index, val));
778 Assert(pThis->gr_index < RT_ELEMENTS(gr_mask));
779 pThis->gr[pThis->gr_index] = val & gr_mask[pThis->gr_index];
780
781#ifndef IN_RC
782 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
783 if (pThis->gr_index == 6 /* memory map mode */)
784 {
785 if (pThis->bmPageRemappedVGA != 0)
786 {
787 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
788 STAM_COUNTER_INC(&pThis->StatMapReset);
789 vgaResetRemapped(pThis);
790 }
791 }
792#endif
793 break;
794
795 case 0x3b4:
796 case 0x3d4:
797 pThis->cr_index = val;
798 break;
799 case 0x3b5:
800 case 0x3d5:
801 Log2(("vga: write CR%x = 0x%02x\n", pThis->cr_index, val));
802 /* handle CR0-7 protection */
803 if ((pThis->cr[0x11] & 0x80) && pThis->cr_index <= 7) {
804 /* can always write bit 4 of CR7 */
805 if (pThis->cr_index == 7)
806 pThis->cr[7] = (pThis->cr[7] & ~0x10) | (val & 0x10);
807 return;
808 }
809 pThis->cr[pThis->cr_index] = val;
810
811 if (pThis->fRealRetrace) {
812 /* The following registers are only updated during a mode set. */
813 switch(pThis->cr_index) {
814 case 0x00:
815 case 0x02:
816 case 0x03:
817 case 0x05:
818 case 0x06:
819 case 0x07:
820 case 0x09:
821 case 0x10:
822 case 0x11:
823 case 0x15:
824 case 0x16:
825 vga_update_retrace_state(pThis);
826 break;
827 }
828 }
829 break;
830 case 0x3ba:
831 case 0x3da:
832 pThis->fcr = val & 0x10;
833 break;
834 }
835}
836
837#ifdef CONFIG_BOCHS_VBE
838
839static uint32_t vbe_read_cfg(PVGASTATE pThis)
840{
841 const uint16_t u16Cfg = pThis->vbe_regs[VBE_DISPI_INDEX_CFG];
842 const uint16_t u16Id = u16Cfg & VBE_DISPI_CFG_MASK_ID;
843 const bool fQuerySupport = RT_BOOL(u16Cfg & VBE_DISPI_CFG_MASK_SUPPORT);
844
845 uint32_t val = 0;
846 switch (u16Id)
847 {
848 case VBE_DISPI_CFG_ID_VERSION: val = 1; break;
849 case VBE_DISPI_CFG_ID_VRAM_SIZE: val = pThis->vram_size; break;
850 case VBE_DISPI_CFG_ID_3D: val = pThis->f3DEnabled; break;
851# ifdef VBOX_WITH_VMSVGA
852 case VBE_DISPI_CFG_ID_VMSVGA: val = pThis->fVMSVGAEnabled; break;
853 case VBE_DISPI_CFG_ID_VMSVGA_DX: val = pThis->fVMSVGA10; break;
854# endif
855 default:
856 return 0; /* Not supported. */
857 }
858
859 return fQuerySupport ? 1 : val;
860}
861
862static uint32_t vbe_ioport_read_index(PVGASTATE pThis, uint32_t addr)
863{
864 uint32_t val = pThis->vbe_index;
865 NOREF(addr);
866 return val;
867}
868
869static uint32_t vbe_ioport_read_data(PVGASTATE pThis, uint32_t addr)
870{
871 uint32_t val;
872 NOREF(addr);
873
874 uint16_t const idxVbe = pThis->vbe_index;
875 if (idxVbe < VBE_DISPI_INDEX_NB)
876 {
877 RT_UNTRUSTED_VALIDATED_FENCE();
878 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS)
879 {
880 switch (idxVbe)
881 {
882 /* XXX: do not hardcode ? */
883 case VBE_DISPI_INDEX_XRES:
884 val = VBE_DISPI_MAX_XRES;
885 break;
886 case VBE_DISPI_INDEX_YRES:
887 val = VBE_DISPI_MAX_YRES;
888 break;
889 case VBE_DISPI_INDEX_BPP:
890 val = VBE_DISPI_MAX_BPP;
891 break;
892 default:
893 Assert(idxVbe < VBE_DISPI_INDEX_NB);
894 val = pThis->vbe_regs[idxVbe];
895 break;
896 }
897 }
898 else
899 {
900 switch (idxVbe)
901 {
902 case VBE_DISPI_INDEX_VBOX_VIDEO:
903 /* Reading from the port means that the old additions are requesting the number of monitors. */
904 val = 1;
905 break;
906 case VBE_DISPI_INDEX_CFG:
907 val = vbe_read_cfg(pThis);
908 break;
909 default:
910 Assert(idxVbe < VBE_DISPI_INDEX_NB);
911 val = pThis->vbe_regs[idxVbe];
912 break;
913 }
914 }
915 }
916 else
917 val = 0;
918 Log(("VBE: read index=0x%x val=0x%x\n", idxVbe, val));
919 return val;
920}
921
922# define VBE_PITCH_ALIGN 4 /* Align pitch to 32 bits - Qt requires that. */
923
924/* Calculate scanline pitch based on bit depth and width in pixels. */
925static uint32_t calc_line_pitch(uint16_t bpp, uint16_t width)
926{
927 uint32_t pitch, aligned_pitch;
928
929 if (bpp <= 4)
930 pitch = width >> 1;
931 else
932 pitch = width * ((bpp + 7) >> 3);
933
934 /* Align the pitch to some sensible value. */
935 aligned_pitch = (pitch + (VBE_PITCH_ALIGN - 1)) & ~(VBE_PITCH_ALIGN - 1);
936 if (aligned_pitch != pitch)
937 Log(("VBE: Line pitch %d aligned to %d bytes\n", pitch, aligned_pitch));
938
939 return aligned_pitch;
940}
941
942static void recalculate_data(PVGASTATE pThis)
943{
944 uint16_t cBPP = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
945 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
946 uint16_t cX = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
947 if (!cBPP || !cX)
948 return; /* Not enough data has been set yet. */
949 uint32_t cbLinePitch = calc_line_pitch(cBPP, cVirtWidth);
950 if (!cbLinePitch)
951 cbLinePitch = calc_line_pitch(cBPP, cX);
952 if (!cbLinePitch)
953 return;
954 uint32_t cVirtHeight = pThis->vram_size / cbLinePitch;
955 uint16_t offX = pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
956 uint16_t offY = pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
957 uint32_t offStart = cbLinePitch * offY;
958 if (cBPP == 4)
959 offStart += offX >> 1;
960 else
961 offStart += offX * ((cBPP + 7) >> 3);
962
963 pThis->vbe_line_offset = RT_MIN(cbLinePitch, pThis->vram_size);
964 pThis->vbe_start_addr = RT_MIN(offStart, pThis->vram_size) >> 2;
965
966 /* The VBE_DISPI_INDEX_VIRT_HEIGHT is used to prevent setting resolution bigger than
967 * the VRAM size permits. It is used instead of VBE_DISPI_INDEX_YRES *only* in case
968 * pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] < pThis->vbe_regs[VBE_DISPI_INDEX_YRES].
969 * Note that VBE_DISPI_INDEX_VIRT_HEIGHT has to be clipped to UINT16_MAX, which happens
970 * with small resolutions and big VRAM. */
971 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = cVirtHeight >= UINT16_MAX ? UINT16_MAX : (uint16_t)cVirtHeight;
972}
973
974static void vbe_ioport_write_index(PVGASTATE pThis, uint32_t addr, uint32_t val)
975{
976 pThis->vbe_index = val;
977 NOREF(addr);
978}
979
980static VBOXSTRICTRC vbe_ioport_write_data(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, uint32_t addr, uint32_t val)
981{
982 uint32_t max_bank;
983 RT_NOREF(pThisCC, addr);
984
985 uint16_t const idxVbe = pThis->vbe_index;
986 if (idxVbe <= VBE_DISPI_INDEX_NB)
987 {
988 RT_UNTRUSTED_VALIDATED_FENCE();
989
990 bool fRecalculate = false;
991 Log(("VBE: write index=0x%x val=0x%x\n", idxVbe, val));
992 switch(idxVbe) {
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[VBE_DISPI_INDEX_ID] = val;
1008 }
1009 break;
1010 case VBE_DISPI_INDEX_XRES:
1011 if (val <= VBE_DISPI_MAX_XRES)
1012 {
1013 pThis->vbe_regs[VBE_DISPI_INDEX_XRES] = 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[VBE_DISPI_INDEX_YRES] = 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[VBE_DISPI_INDEX_BPP] = 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[VBE_DISPI_INDEX_BANK] = 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;
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 ASSERT_GUEST_MSG_FAILED(("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],
1081 cb, pThis->vram_size));
1082 return VINF_SUCCESS; /* Note: silent failure like before */
1083 }
1084
1085 /* When VBE interface is enabled, it is reset. */
1086 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
1087 pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
1088 fRecalculate = true;
1089
1090 /* clear the screen (should be done in BIOS) */
1091 if (!(val & VBE_DISPI_NOCLEARMEM)) {
1092 uint16_t cY = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
1093 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
1094 uint16_t cbLinePitch = pThis->vbe_line_offset;
1095 memset(pThisCC->pbVRam, 0,
1096 cY * cbLinePitch);
1097 }
1098
1099 /* we initialize the VGA graphic mode (should be done
1100 in BIOS) */
1101 pThis->gr[0x06] = (pThis->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
1102 pThis->cr[0x17] |= 3; /* no CGA modes */
1103 pThis->cr[0x13] = pThis->vbe_line_offset >> 3;
1104 /* width */
1105 pThis->cr[0x01] = (cVirtWidth >> 3) - 1;
1106 /* height (only meaningful if < 1024) */
1107 h = pThis->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
1108 pThis->cr[0x12] = h;
1109 pThis->cr[0x07] = (pThis->cr[0x07] & ~0x42) |
1110 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
1111 /* line compare to 1023 */
1112 pThis->cr[0x18] = 0xff;
1113 pThis->cr[0x07] |= 0x10;
1114 pThis->cr[0x09] |= 0x40;
1115
1116 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
1117 shift_control = 0;
1118 pThis->sr[0x01] &= ~8; /* no double line */
1119 } else {
1120 shift_control = 2;
1121 pThis->sr[4] |= 0x08; /* set chain 4 mode */
1122 pThis->sr[2] |= 0x0f; /* activate all planes */
1123 /* Indicate non-VGA mode in SR07. */
1124 pThis->sr[7] |= 1;
1125 }
1126 pThis->gr[0x05] = (pThis->gr[0x05] & ~0x60) | (shift_control << 5);
1127 pThis->cr[0x09] &= ~0x9f; /* no double scan */
1128 /* sunlover 30.05.2007
1129 * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
1130 * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vgaR3UpdateDisplay.
1131 * But the VBE mode is graphics, so not a blank anymore.
1132 */
1133 pThis->ar_index |= 0x20;
1134 } else {
1135 /* XXX: the bios should do that */
1136 /* sunlover 21.12.2006
1137 * Here is probably more to reset. When this was executed in GC
1138 * then the *update* functions could not detect a mode change.
1139 * Or may be these update function should take the pThis->vbe_regs[pThis->vbe_index]
1140 * into account when detecting a mode change.
1141 *
1142 * The 'mode reset not detected' problem is now fixed by executing the
1143 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
1144 * LFBChange callback.
1145 */
1146 pThis->bank_offset = 0;
1147 }
1148 pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] = val;
1149 /*
1150 * LFB video mode is either disabled or changed. Notify the display
1151 * and reset VBVA.
1152 */
1153 pThisCC->pDrv->pfnLFBModeChange(pThisCC->pDrv, (val & VBE_DISPI_ENABLED) != 0);
1154# ifdef VBOX_WITH_HGSMI
1155 VBVAOnVBEChanged(pThis, pThisCC);
1156# endif
1157
1158 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1159 if (pThis->bmPageRemappedVGA != 0)
1160 {
1161 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
1162 STAM_COUNTER_INC(&pThis->StatMapReset);
1163 vgaResetRemapped(pThis);
1164 }
1165 break;
1166 }
1167# endif /* IN_RING3 */
1168 case VBE_DISPI_INDEX_VIRT_WIDTH:
1169 case VBE_DISPI_INDEX_X_OFFSET:
1170 case VBE_DISPI_INDEX_Y_OFFSET:
1171 {
1172 pThis->vbe_regs[idxVbe] = val;
1173 fRecalculate = true;
1174 }
1175 break;
1176 case VBE_DISPI_INDEX_VBOX_VIDEO:
1177# ifndef IN_RING3
1178 return VINF_IOM_R3_IOPORT_WRITE;
1179# else
1180 /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
1181 if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
1182 pThisCC->pDrv->pfnProcessAdapterData(pThisCC->pDrv, NULL, 0);
1183 else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
1184 pThisCC->pDrv->pfnProcessAdapterData(pThisCC->pDrv, pThisCC->pbVRam, pThis->vram_size);
1185 else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
1186 pThisCC->pDrv->pfnProcessDisplayData(pThisCC->pDrv, pThisCC->pbVRam, val & 0xFFFF);
1187# endif /* IN_RING3 */
1188 break;
1189 case VBE_DISPI_INDEX_CFG:
1190 pThis->vbe_regs[VBE_DISPI_INDEX_CFG] = val;
1191 break;
1192 default:
1193 break;
1194 }
1195
1196 if (fRecalculate)
1197 recalculate_data(pThis);
1198 }
1199 return VINF_SUCCESS;
1200}
1201
1202#endif /* CONFIG_BOCHS_VBE */
1203
1204/* called for accesses between 0xa0000 and 0xc0000 */
1205static uint32_t vga_mem_readb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS addr, int *prc)
1206{
1207 int plane;
1208 uint32_t ret;
1209
1210 Log3(("vga: read [0x%x] -> ", addr));
1211
1212#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RZ
1213 /* VMSVGA keeps the VGA and SVGA framebuffers separate unlike this boch-based
1214 VGA implementation, so we fake it by going to ring-3 and using a heap buffer. */
1215 if (!pThis->svga.fEnabled)
1216 { /*likely*/ }
1217 else
1218 {
1219 *prc = VINF_IOM_R3_MMIO_READ;
1220 return 0;
1221 }
1222#endif
1223
1224
1225 /* convert to VGA memory offset */
1226 addr &= 0x1ffff;
1227#ifndef IN_RC
1228 RTGCPHYS const offMmio = addr; /* save original MMIO range offset */
1229#endif
1230
1231 int const memory_map_mode = (pThis->gr[6] >> 2) & 3;
1232 switch(memory_map_mode) {
1233 case 0:
1234 break;
1235 case 1:
1236 if (addr >= 0x10000)
1237 return 0xff;
1238 addr += pThis->bank_offset;
1239 break;
1240 case 2:
1241 addr -= 0x10000;
1242 if (addr >= 0x8000)
1243 return 0xff;
1244 break;
1245 default:
1246 case 3:
1247 addr -= 0x18000;
1248 if (addr >= 0x8000)
1249 return 0xff;
1250 break;
1251 }
1252
1253 if (pThis->sr[4] & 0x08) {
1254 /* chain 4 mode : simplest access */
1255#ifndef IN_RC
1256 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1257 if ( (pThis->sr[2] & 3) == 3
1258 && !vgaIsRemapped(pThis, offMmio)
1259 && pThis->GCPhysVRAM)
1260 {
1261 /** @todo only allow read access (doesn't work now) */
1262 STAM_COUNTER_INC(&pThis->StatMapPage);
1263 PDMDevHlpMmioMapMmio2Page(pDevIns, pThis->hMmioLegacy, offMmio, pThis->hMmio2VRam, addr, X86_PTE_RW | X86_PTE_P);
1264 /* Set as dirty as write accesses won't be noticed now. */
1265 vgaR3MarkDirty(pThis, addr);
1266 vgaMarkRemapped(pThis, offMmio);
1267 }
1268#endif /* !IN_RC */
1269 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr, *prc);
1270#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1271 ret = !pThis->svga.fEnabled ? pThisCC->pbVRam[addr]
1272 : addr < VMSVGA_VGA_FB_BACKUP_SIZE ? pThisCC->svga.pbVgaFrameBufferR3[addr] : 0xff;
1273#else
1274 ret = pThisCC->pbVRam[addr];
1275#endif
1276 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1277 /* odd/even mode (aka text mode mapping) */
1278 plane = (pThis->gr[4] & 2) | (addr & 1);
1279 /* See the comment for a similar line in vga_mem_writeb. */
1280 RTGCPHYS off = ((addr & ~1) * 4) | plane;
1281 VERIFY_VRAM_READ_OFF_RETURN(pThis, off, *prc);
1282#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1283 ret = !pThis->svga.fEnabled ? pThisCC->pbVRam[off]
1284 : off < VMSVGA_VGA_FB_BACKUP_SIZE ? pThisCC->svga.pbVgaFrameBufferR3[off] : 0xff;
1285#else
1286 ret = pThisCC->pbVRam[off];
1287#endif
1288 } else {
1289 /* standard VGA latched access */
1290 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr * 4 + 3, *prc);
1291#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1292 pThis->latch = !pThis->svga.fEnabled ? ((uint32_t *)pThisCC->pbVRam)[addr]
1293 : addr * 4 + 3 < VMSVGA_VGA_FB_BACKUP_SIZE ? ((uint32_t *)pThisCC->svga.pbVgaFrameBufferR3)[addr] : UINT32_MAX;
1294#else
1295 pThis->latch = ((uint32_t *)pThisCC->pbVRam)[addr];
1296#endif
1297 if (!(pThis->gr[5] & 0x08)) {
1298 /* read mode 0 */
1299 plane = pThis->gr[4];
1300 ret = GET_PLANE(pThis->latch, plane);
1301 } else {
1302 /* read mode 1 */
1303 ret = (pThis->latch ^ mask16[pThis->gr[2]]) & mask16[pThis->gr[7]];
1304 ret |= ret >> 16;
1305 ret |= ret >> 8;
1306 ret = (~ret) & 0xff;
1307 }
1308 }
1309 Log3((" 0x%02x\n", ret));
1310 return ret;
1311}
1312
1313/**
1314 * called for accesses between 0xa0000 and 0xc0000
1315 */
1316static VBOXSTRICTRC vga_mem_writeb(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, RTGCPHYS addr, uint32_t val)
1317{
1318 int plane, write_mode, b, func_select, mask;
1319 uint32_t write_mask, bit_mask, set_mask;
1320
1321 Log3(("vga: [0x%x] = 0x%02x\n", addr, val));
1322
1323#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RZ
1324 /* VMSVGA keeps the VGA and SVGA framebuffers separate unlike this boch-based
1325 VGA implementation, so we fake it by going to ring-3 and using a heap buffer. */
1326 if (!pThis->svga.fEnabled) { /*likely*/ }
1327 else return VINF_IOM_R3_MMIO_WRITE;
1328#endif
1329
1330 /* convert to VGA memory offset */
1331 addr &= 0x1ffff;
1332#ifndef IN_RC
1333 RTGCPHYS const offMmio = addr; /* save original MMIO range offset */
1334#endif
1335
1336 int const memory_map_mode = (pThis->gr[6] >> 2) & 3;
1337 switch(memory_map_mode) {
1338 case 0:
1339 break;
1340 case 1:
1341 if (addr >= 0x10000)
1342 return VINF_SUCCESS;
1343 addr += pThis->bank_offset;
1344 break;
1345 case 2:
1346 addr -= 0x10000;
1347 if (addr >= 0x8000)
1348 return VINF_SUCCESS;
1349 break;
1350 default:
1351 case 3:
1352 addr -= 0x18000;
1353 if (addr >= 0x8000)
1354 return VINF_SUCCESS;
1355 break;
1356 }
1357
1358 if (pThis->sr[4] & 0x08) {
1359 /* chain 4 mode : simplest access */
1360 plane = addr & 3;
1361 mask = (1 << plane);
1362 if (pThis->sr[2] & mask) {
1363#ifndef IN_RC
1364 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1365 if ( (pThis->sr[2] & 3) == 3
1366 && !vgaIsRemapped(pThis, offMmio)
1367 && pThis->GCPhysVRAM)
1368 {
1369 STAM_COUNTER_INC(&pThis->StatMapPage);
1370 PDMDevHlpMmioMapMmio2Page(pDevIns, pThis->hMmioLegacy, offMmio, pThis->hMmio2VRam, addr, X86_PTE_RW | X86_PTE_P);
1371 vgaMarkRemapped(pThis, offMmio);
1372 }
1373#endif /* !IN_RC */
1374
1375 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1376#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1377 if (!pThis->svga.fEnabled)
1378 pThisCC->pbVRam[addr] = val;
1379 else if (addr < VMSVGA_VGA_FB_BACKUP_SIZE)
1380 pThisCC->svga.pbVgaFrameBufferR3[addr] = val;
1381 else
1382 {
1383 Log(("vga: chain4: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1384 return VINF_SUCCESS;
1385 }
1386#else
1387 pThisCC->pbVRam[addr] = val;
1388#endif
1389 Log3(("vga: chain4: [0x%x]\n", addr));
1390 pThis->plane_updated |= mask; /* only used to detect font change */
1391 vgaR3MarkDirty(pThis, addr);
1392 }
1393 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1394 /* odd/even mode (aka text mode mapping); GR4 does not affect writes! */
1395 plane = addr & 1;
1396 mask = (1 << plane);
1397 if (pThis->sr[2] & mask) {
1398 /* 'addr' is offset in a plane, bit 0 selects the plane.
1399 * Mask the bit 0, convert plane index to vram offset,
1400 * that is multiply by the number of planes,
1401 * and select the plane byte in the vram offset.
1402 */
1403 addr = ((addr & ~1) * 4) | plane;
1404 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1405#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1406 if (!pThis->svga.fEnabled)
1407 pThisCC->pbVRam[addr] = val;
1408 else if (addr < VMSVGA_VGA_FB_BACKUP_SIZE)
1409 pThisCC->svga.pbVgaFrameBufferR3[addr] = val;
1410 else
1411 {
1412 Log(("vga: odd/even: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1413 return VINF_SUCCESS;
1414 }
1415#else
1416 pThisCC->pbVRam[addr] = val;
1417#endif
1418 Log3(("vga: odd/even: [0x%x]\n", addr));
1419 pThis->plane_updated |= mask; /* only used to detect font change */
1420 vgaR3MarkDirty(pThis, addr);
1421 }
1422 } else {
1423 /* standard VGA latched access */
1424 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr * 4 + 3);
1425
1426 write_mode = pThis->gr[5] & 3;
1427 switch(write_mode) {
1428 default:
1429 case 0:
1430 /* rotate */
1431 b = pThis->gr[3] & 7;
1432 val = ((val >> b) | (val << (8 - b))) & 0xff;
1433 val |= val << 8;
1434 val |= val << 16;
1435
1436 /* apply set/reset mask */
1437 set_mask = mask16[pThis->gr[1]];
1438 val = (val & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
1439 bit_mask = pThis->gr[8];
1440 break;
1441 case 1:
1442 val = pThis->latch;
1443 goto do_write;
1444 case 2:
1445 val = mask16[val & 0x0f];
1446 bit_mask = pThis->gr[8];
1447 break;
1448 case 3:
1449 /* rotate */
1450 b = pThis->gr[3] & 7;
1451 val = (val >> b) | (val << (8 - b));
1452
1453 bit_mask = pThis->gr[8] & val;
1454 val = mask16[pThis->gr[0]];
1455 break;
1456 }
1457
1458 /* apply logical operation */
1459 func_select = pThis->gr[3] >> 3;
1460 switch(func_select) {
1461 case 0:
1462 default:
1463 /* nothing to do */
1464 break;
1465 case 1:
1466 /* and */
1467 val &= pThis->latch;
1468 break;
1469 case 2:
1470 /* or */
1471 val |= pThis->latch;
1472 break;
1473 case 3:
1474 /* xor */
1475 val ^= pThis->latch;
1476 break;
1477 }
1478
1479 /* apply bit mask */
1480 bit_mask |= bit_mask << 8;
1481 bit_mask |= bit_mask << 16;
1482 val = (val & bit_mask) | (pThis->latch & ~bit_mask);
1483
1484 do_write:
1485 /* mask data according to sr[2] */
1486 mask = pThis->sr[2];
1487 pThis->plane_updated |= mask; /* only used to detect font change */
1488 write_mask = mask16[mask];
1489#ifdef VMSVGA_WITH_VGA_FB_BACKUP_AND_IN_RING3
1490 uint32_t *pu32Dst;
1491 if (!pThis->svga.fEnabled)
1492 pu32Dst = &((uint32_t *)pThisCC->pbVRam)[addr];
1493 else if (addr * 4 + 3 < VMSVGA_VGA_FB_BACKUP_SIZE)
1494 pu32Dst = &((uint32_t *)pThisCC->svga.pbVgaFrameBufferR3)[addr];
1495 else
1496 {
1497 Log(("vga: latch: out of vmsvga VGA framebuffer bounds! addr=%#x\n", addr));
1498 return VINF_SUCCESS;
1499 }
1500 *pu32Dst = (*pu32Dst & ~write_mask) | (val & write_mask);
1501#else
1502 ((uint32_t *)pThisCC->pbVRam)[addr] = (((uint32_t *)pThisCC->pbVRam)[addr] & ~write_mask)
1503 | (val & write_mask);
1504#endif
1505 Log3(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", addr * 4, write_mask, val));
1506 vgaR3MarkDirty(pThis, (addr * 4));
1507 }
1508
1509 return VINF_SUCCESS;
1510}
1511
1512#ifdef IN_RING3
1513
1514typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1515 const uint8_t *font_ptr, int h,
1516 uint32_t fgcol, uint32_t bgcol,
1517 int dscan);
1518typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1519 const uint8_t *font_ptr, int h,
1520 uint32_t fgcol, uint32_t bgcol, int dup9);
1521typedef void vga_draw_line_func(PVGASTATE pThis, PVGASTATECC pThisCC, uint8_t *pbDst, const uint8_t *pbSrc, int width);
1522
1523static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1524{
1525 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1526}
1527
1528static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1529{
1530 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1531}
1532
1533static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1534{
1535 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1536}
1537
1538static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1539{
1540 return (r << 16) | (g << 8) | b;
1541}
1542
1543#define DEPTH 8
1544#include "DevVGATmpl.h"
1545
1546#define DEPTH 15
1547#include "DevVGATmpl.h"
1548
1549#define DEPTH 16
1550#include "DevVGATmpl.h"
1551
1552#define DEPTH 32
1553#include "DevVGATmpl.h"
1554
1555static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1556{
1557 unsigned int col;
1558 col = rgb_to_pixel8(r, g, b);
1559 col |= col << 8;
1560 col |= col << 16;
1561 return col;
1562}
1563
1564static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1565{
1566 unsigned int col;
1567 col = rgb_to_pixel15(r, g, b);
1568 col |= col << 16;
1569 return col;
1570}
1571
1572static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1573{
1574 unsigned int col;
1575 col = rgb_to_pixel16(r, g, b);
1576 col |= col << 16;
1577 return col;
1578}
1579
1580static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1581{
1582 return rgb_to_pixel32(r, g, b);
1583}
1584
1585/** return true if the palette was modified */
1586static bool vgaR3UpdatePalette16(PVGASTATE pThis, PVGASTATER3 pThisCC)
1587{
1588 bool full_update = false;
1589 int i;
1590 uint32_t v, col, *palette;
1591
1592 palette = pThis->last_palette;
1593 for(i = 0; i < 16; i++) {
1594 v = pThis->ar[i];
1595 if (pThis->ar[0x10] & 0x80)
1596 v = ((pThis->ar[0x14] & 0xf) << 4) | (v & 0xf);
1597 else
1598 v = ((pThis->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1599 v = v * 3;
1600 col = pThisCC->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1601 c6_to_8(pThis->palette[v + 1]),
1602 c6_to_8(pThis->palette[v + 2]));
1603 if (col != palette[i]) {
1604 full_update = true;
1605 palette[i] = col;
1606 }
1607 }
1608 return full_update;
1609}
1610
1611/** return true if the palette was modified */
1612static bool vgaR3UpdatePalette256(PVGASTATE pThis, PVGASTATER3 pThisCC)
1613{
1614 bool full_update = false;
1615 int i;
1616 uint32_t v, col, *palette;
1617 int wide_dac;
1618
1619 palette = pThis->last_palette;
1620 v = 0;
1621 wide_dac = (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
1622 == (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
1623 for(i = 0; i < 256; i++) {
1624 if (wide_dac)
1625 col = pThisCC->rgb_to_pixel(pThis->palette[v],
1626 pThis->palette[v + 1],
1627 pThis->palette[v + 2]);
1628 else
1629 col = pThisCC->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1630 c6_to_8(pThis->palette[v + 1]),
1631 c6_to_8(pThis->palette[v + 2]));
1632 if (col != palette[i]) {
1633 full_update = true;
1634 palette[i] = col;
1635 }
1636 v += 3;
1637 }
1638 return full_update;
1639}
1640
1641static void vgaR3GetOffsets(PVGASTATE pThis,
1642 uint32_t *pline_offset,
1643 uint32_t *pstart_addr,
1644 uint32_t *pline_compare)
1645{
1646 uint32_t start_addr, line_offset, line_compare;
1647#ifdef CONFIG_BOCHS_VBE
1648 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1649 line_offset = pThis->vbe_line_offset;
1650 start_addr = pThis->vbe_start_addr;
1651 line_compare = 65535;
1652 } else
1653#endif
1654 {
1655 /* compute line_offset in bytes */
1656 line_offset = pThis->cr[0x13];
1657 line_offset <<= 3;
1658 if (!(pThis->cr[0x14] & 0x40) && !(pThis->cr[0x17] & 0x40))
1659 {
1660 /* Word mode. Used for odd/even modes. */
1661 line_offset *= 2;
1662 }
1663
1664 /* starting address */
1665 start_addr = pThis->cr[0x0d] | (pThis->cr[0x0c] << 8);
1666
1667 /* line compare */
1668 line_compare = pThis->cr[0x18] |
1669 ((pThis->cr[0x07] & 0x10) << 4) |
1670 ((pThis->cr[0x09] & 0x40) << 3);
1671 }
1672 *pline_offset = line_offset;
1673 *pstart_addr = start_addr;
1674 *pline_compare = line_compare;
1675}
1676
1677/** update start_addr and line_offset. Return TRUE if modified */
1678static bool vgaR3UpdateBasicParams(PVGASTATE pThis, PVGASTATER3 pThisCC)
1679{
1680 bool full_update = false;
1681 uint32_t start_addr, line_offset, line_compare;
1682
1683 pThisCC->get_offsets(pThis, &line_offset, &start_addr, &line_compare);
1684
1685 if (line_offset != pThis->line_offset ||
1686 start_addr != pThis->start_addr ||
1687 line_compare != pThis->line_compare) {
1688 pThis->line_offset = line_offset;
1689 pThis->start_addr = start_addr;
1690 pThis->line_compare = line_compare;
1691 full_update = true;
1692 }
1693 return full_update;
1694}
1695
1696static inline int vgaR3GetDepthIndex(int depth)
1697{
1698 switch(depth) {
1699 default:
1700 case 8:
1701 return 0;
1702 case 15:
1703 return 1;
1704 case 16:
1705 return 2;
1706 case 32:
1707 return 3;
1708 }
1709}
1710
1711static vga_draw_glyph8_func * const vga_draw_glyph8_table[4] = {
1712 vga_draw_glyph8_8,
1713 vga_draw_glyph8_16,
1714 vga_draw_glyph8_16,
1715 vga_draw_glyph8_32,
1716};
1717
1718static vga_draw_glyph8_func * const vga_draw_glyph16_table[4] = {
1719 vga_draw_glyph16_8,
1720 vga_draw_glyph16_16,
1721 vga_draw_glyph16_16,
1722 vga_draw_glyph16_32,
1723};
1724
1725static vga_draw_glyph9_func * const vga_draw_glyph9_table[4] = {
1726 vga_draw_glyph9_8,
1727 vga_draw_glyph9_16,
1728 vga_draw_glyph9_16,
1729 vga_draw_glyph9_32,
1730};
1731
1732static const uint8_t cursor_glyph[32 * 4] = {
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 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1749};
1750
1751static const uint8_t empty_glyph[32 * 4] = { 0 };
1752
1753/**
1754 * Text mode update
1755 */
1756static int vgaR3DrawText(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update,
1757 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
1758{
1759 int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
1760 int cx_min, cx_max, linesize, x_incr;
1761 int cx_min_upd, cx_max_upd, cy_start;
1762 uint32_t offset, fgcol, bgcol, v, cursor_offset;
1763 uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
1764 const uint8_t *font_ptr, *font_base[2];
1765 int dup9, line_offset, depth_index, dscan;
1766 uint32_t *palette;
1767 uint32_t *ch_attr_ptr;
1768 vga_draw_glyph8_func *vga_draw_glyph8;
1769 vga_draw_glyph9_func *vga_draw_glyph9;
1770 uint64_t time_ns;
1771 bool blink_on, chr_blink_flip, cur_blink_flip;
1772 bool blink_enabled, blink_do_redraw;
1773 int uline_pos;
1774 int s_incr;
1775
1776 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
1777 palette = pThis->last_palette;
1778
1779 /* compute font data address (in plane 2) */
1780 v = pThis->sr[3];
1781 offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
1782 if (offset != pThis->font_offsets[0]) {
1783 pThis->font_offsets[0] = offset;
1784 full_update = true;
1785 }
1786 font_base[0] = pThisCC->pbVRam + offset;
1787
1788 offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
1789 font_base[1] = pThisCC->pbVRam + offset;
1790 if (offset != pThis->font_offsets[1]) {
1791 pThis->font_offsets[1] = offset;
1792 full_update = true;
1793 }
1794 if (pThis->plane_updated & (1 << 2)) {
1795 /* if the plane 2 was modified since the last display, it
1796 indicates the font may have been modified */
1797 pThis->plane_updated = 0;
1798 full_update = true;
1799 }
1800
1801 /* Underline position */
1802 uline_pos = pThis->cr[0x14] & 0x1f;
1803 if (uline_pos != pThis->last_uline) {
1804 pThis->last_uline = uline_pos;
1805 full_update = true;
1806 }
1807
1808 blink_enabled = !!(pThis->ar[0x10] & 0x08); /* Attribute controller blink enable. */
1809 if (blink_enabled != pThis->last_blink) {
1810 pThis->last_blink = blink_enabled;
1811 full_update = true;
1812 }
1813
1814 full_update |= vgaR3UpdateBasicParams(pThis, pThisCC);
1815
1816 /* Evaluate word/byte mode. Need to count by 4 because text is only in plane 0. */
1817 s_incr = pThis->cr[0x17] & 0x40 ? 4 : 8;
1818
1819 unsigned addr_mask;
1820 if (!(pThis->cr[0x17] & 0x40) && !(pThis->cr[0x17] & 0x20))
1821 addr_mask = 0xffff; /* Wrap at 64K, for CGA and 64K EGA compatibility. */
1822 else
1823 addr_mask = 0x3ffff; /* Wrap at 256K, standard VGA. */
1824
1825 line_offset = pThis->line_offset;
1826 s1 = pThisCC->pbVRam + ((pThis->start_addr * s_incr) & addr_mask);
1827
1828 /* double scanning - not for 9-wide modes */
1829 dscan = (pThis->cr[9] >> 7) & 1;
1830
1831 /* total width & height */
1832 cheight = (pThis->cr[9] & 0x1f) + 1;
1833 cw = 8;
1834 if (!(pThis->sr[1] & 0x01))
1835 cw = 9;
1836 if (pThis->sr[1] & 0x08)
1837 cw = 16; /* NOTE: no 18 pixel wide */
1838 x_incr = cw * ((pDrv->cBits + 7) >> 3);
1839 width = (pThis->cr[0x01] + 1);
1840 if (pThis->cr[0x06] == 100) {
1841 /* ugly hack for CGA 160x100x16 - explain me the logic */
1842 height = 100;
1843 } else {
1844 height = pThis->cr[0x12] |
1845 ((pThis->cr[0x07] & 0x02) << 7) |
1846 ((pThis->cr[0x07] & 0x40) << 3);
1847 height = (height + 1) / cheight;
1848 }
1849 /** @todo r=michaln This conditional is questionable; we should be able
1850 * to draw whatver the guest asks for. */
1851 if ((height * width) > CH_ATTR_SIZE) {
1852 /* better than nothing: exit if transient size is too big */
1853 return VINF_SUCCESS;
1854 }
1855
1856 if (width != (int)pThis->last_width || height != (int)pThis->last_height ||
1857 cw != pThis->last_cw || cheight != pThis->last_ch) {
1858 if (fFailOnResize)
1859 {
1860 /* The caller does not want to call the pfnResize. */
1861 return VERR_TRY_AGAIN;
1862 }
1863 pThis->last_scr_width = width * cw;
1864 pThis->last_scr_height = height * cheight;
1865 /* For text modes the direct use of guest VRAM is not implemented, so bpp and cbLine are 0 here. */
1866 int rc = pDrv->pfnResize(pDrv, 0, NULL, 0, pThis->last_scr_width, pThis->last_scr_height);
1867 pThis->last_width = width;
1868 pThis->last_height = height;
1869 pThis->last_ch = cheight;
1870 pThis->last_cw = cw;
1871 full_update = true;
1872 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1873 return rc;
1874 AssertRC(rc);
1875 }
1876 cursor_offset = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) - pThis->start_addr;
1877 if (cursor_offset != pThis->cursor_offset ||
1878 pThis->cr[0xa] != pThis->cursor_start ||
1879 pThis->cr[0xb] != pThis->cursor_end) {
1880 /* if the cursor position changed, we update the old and new
1881 chars */
1882 if (pThis->cursor_offset < CH_ATTR_SIZE)
1883 pThis->last_ch_attr[pThis->cursor_offset] = UINT32_MAX;
1884 if (cursor_offset < CH_ATTR_SIZE)
1885 pThis->last_ch_attr[cursor_offset] = UINT32_MAX;
1886 pThis->cursor_offset = cursor_offset;
1887 pThis->cursor_start = pThis->cr[0xa];
1888 pThis->cursor_end = pThis->cr[0xb];
1889 }
1890 cursor_ptr = pThisCC->pbVRam + (((pThis->start_addr + cursor_offset) * s_incr) & addr_mask);
1891 depth_index = vgaR3GetDepthIndex(pDrv->cBits);
1892 if (cw == 16)
1893 vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
1894 else
1895 vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
1896 vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
1897
1898 dest = pDrv->pbData;
1899 linesize = pDrv->cbScanline;
1900 ch_attr_ptr = pThis->last_ch_attr;
1901 cy_start = -1;
1902 cx_max_upd = -1;
1903 cx_min_upd = width;
1904
1905 /* Figure out if we're in the visible period of the blink cycle. */
1906 time_ns = PDMDevHlpTMTimeVirtGetNano(pDevIns);
1907 blink_on = (time_ns % VGA_BLINK_PERIOD_FULL) < VGA_BLINK_PERIOD_ON;
1908 chr_blink_flip = false;
1909 cur_blink_flip = false;
1910 if (pThis->last_chr_blink != blink_on)
1911 {
1912 /* Currently cursor and characters blink at the same rate, but they might not. */
1913 pThis->last_chr_blink = blink_on;
1914 pThis->last_cur_blink = blink_on;
1915 chr_blink_flip = true;
1916 cur_blink_flip = true;
1917 }
1918
1919 for(cy = 0; cy < (height - dscan); cy = cy + (1 << dscan)) {
1920 d1 = dest;
1921 src = s1;
1922 cx_min = width;
1923 cx_max = -1;
1924 for(cx = 0; cx < width; cx++) {
1925 ch_attr = *(uint16_t *)src;
1926 /* Figure out if character needs redrawing due to blink state change. */
1927 blink_do_redraw = blink_enabled && chr_blink_flip && (ch_attr & 0x8000);
1928 if (full_update || ch_attr != (int)*ch_attr_ptr || blink_do_redraw || (src == cursor_ptr && cur_blink_flip)) {
1929 if (cx < cx_min)
1930 cx_min = cx;
1931 if (cx > cx_max)
1932 cx_max = cx;
1933 if (reset_dirty)
1934 *ch_attr_ptr = ch_attr;
1935#ifdef WORDS_BIGENDIAN
1936 ch = ch_attr >> 8;
1937 cattr = ch_attr & 0xff;
1938#else
1939 ch = ch_attr & 0xff;
1940 cattr = ch_attr >> 8;
1941#endif
1942 font_ptr = font_base[(cattr >> 3) & 1];
1943 font_ptr += 32 * 4 * ch;
1944 bgcol = palette[cattr >> 4];
1945 fgcol = palette[cattr & 0x0f];
1946
1947 if (blink_enabled && (cattr & 0x80))
1948 {
1949 bgcol = palette[(cattr >> 4) & 7];
1950 if (!blink_on)
1951 font_ptr = empty_glyph;
1952 }
1953
1954 if (cw != 9) {
1955 if (pThis->fRenderVRAM)
1956 vga_draw_glyph8(d1, linesize, font_ptr, cheight, fgcol, bgcol, dscan);
1957 } else {
1958 dup9 = 0;
1959 if (ch >= 0xb0 && ch <= 0xdf && (pThis->ar[0x10] & 0x04))
1960 dup9 = 1;
1961 if (pThis->fRenderVRAM)
1962 vga_draw_glyph9(d1, linesize, font_ptr, cheight, fgcol, bgcol, dup9);
1963 }
1964
1965 /* Underline. Typically turned off by setting it past cell height. */
1966 if (((cattr & 0x03) == 1) && (uline_pos < cheight))
1967 {
1968 int h;
1969
1970 d = d1 + (linesize * uline_pos << dscan);
1971 h = 1;
1972
1973 if (cw != 9) {
1974 if (pThis->fRenderVRAM)
1975 vga_draw_glyph8(d, linesize, cursor_glyph, h, fgcol, bgcol, dscan);
1976 } else {
1977 if (pThis->fRenderVRAM)
1978 vga_draw_glyph9(d, linesize, cursor_glyph, h, fgcol, bgcol, 1);
1979 }
1980 }
1981
1982 /* Cursor. */
1983 if (src == cursor_ptr &&
1984 !(pThis->cr[0x0a] & 0x20)) {
1985 int line_start, line_last, h;
1986
1987 /* draw the cursor if within the visible period */
1988 if (blink_on) {
1989 line_start = pThis->cr[0x0a] & 0x1f;
1990 line_last = pThis->cr[0x0b] & 0x1f;
1991 /* XXX: check that */
1992 if (line_last > cheight - 1)
1993 line_last = cheight - 1;
1994 if (line_last >= line_start && line_start < cheight) {
1995 h = line_last - line_start + 1;
1996 d = d1 + (linesize * line_start << dscan);
1997 if (cw != 9) {
1998 if (pThis->fRenderVRAM)
1999 vga_draw_glyph8(d, linesize, cursor_glyph, h, fgcol, bgcol, dscan);
2000 } else {
2001 if (pThis->fRenderVRAM)
2002 vga_draw_glyph9(d, linesize, cursor_glyph, h, fgcol, bgcol, 1);
2003 }
2004 }
2005 }
2006 }
2007 }
2008 d1 += x_incr;
2009 src += s_incr; /* Even in text mode, word/byte mode matters. */
2010 if (src > (pThisCC->pbVRam + addr_mask))
2011 src = pThisCC->pbVRam;
2012 ch_attr_ptr++;
2013 }
2014 if (cx_max != -1) {
2015 /* Keep track of the bounding rectangle for updates. */
2016 if (cy_start == -1)
2017 cy_start = cy;
2018 if (cx_min_upd > cx_min)
2019 cx_min_upd = cx_min;
2020 if (cx_max_upd < cx_max)
2021 cx_max_upd = cx_max;
2022 } else if (cy_start >= 0) {
2023 /* Flush updates to display. */
2024 pDrv->pfnUpdateRect(pDrv, cx_min_upd * cw, cy_start * cheight,
2025 (cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
2026 cy_start = -1;
2027 cx_max_upd = -1;
2028 cx_min_upd = width;
2029 }
2030
2031 dest += linesize * cheight << dscan;
2032 s1 += line_offset;
2033
2034 /* Line compare works in text modes, too. */
2035 /** @todo r=michaln This is inaccurate; text should be rendered line by line
2036 * and line compare checked after every line. */
2037 if ((uint32_t)cy == (pThis->line_compare / cheight))
2038 s1 = pThisCC->pbVRam;
2039
2040 if (s1 > (pThisCC->pbVRam + addr_mask))
2041 s1 = s1 - (addr_mask + 1);
2042 }
2043 if (cy_start >= 0)
2044 /* Flush any remaining changes to display. */
2045 pDrv->pfnUpdateRect(pDrv, cx_min_upd * cw, cy_start * cheight,
2046 (cx_max_upd - cx_min_upd + 1) * cw, (cy - cy_start) * cheight);
2047 return VINF_SUCCESS;
2048}
2049
2050enum {
2051 VGA_DRAW_LINE2,
2052 VGA_DRAW_LINE2D2,
2053 VGA_DRAW_LINE4,
2054 VGA_DRAW_LINE4D2,
2055 VGA_DRAW_LINE8D2,
2056 VGA_DRAW_LINE8,
2057 VGA_DRAW_LINE15,
2058 VGA_DRAW_LINE16,
2059 VGA_DRAW_LINE24,
2060 VGA_DRAW_LINE32,
2061 VGA_DRAW_LINE_NB
2062};
2063
2064static vga_draw_line_func * const vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
2065 vga_draw_line2_8,
2066 vga_draw_line2_16,
2067 vga_draw_line2_16,
2068 vga_draw_line2_32,
2069
2070 vga_draw_line2d2_8,
2071 vga_draw_line2d2_16,
2072 vga_draw_line2d2_16,
2073 vga_draw_line2d2_32,
2074
2075 vga_draw_line4_8,
2076 vga_draw_line4_16,
2077 vga_draw_line4_16,
2078 vga_draw_line4_32,
2079
2080 vga_draw_line4d2_8,
2081 vga_draw_line4d2_16,
2082 vga_draw_line4d2_16,
2083 vga_draw_line4d2_32,
2084
2085 vga_draw_line8d2_8,
2086 vga_draw_line8d2_16,
2087 vga_draw_line8d2_16,
2088 vga_draw_line8d2_32,
2089
2090 vga_draw_line8_8,
2091 vga_draw_line8_16,
2092 vga_draw_line8_16,
2093 vga_draw_line8_32,
2094
2095 vga_draw_line15_8,
2096 vga_draw_line15_15,
2097 vga_draw_line15_16,
2098 vga_draw_line15_32,
2099
2100 vga_draw_line16_8,
2101 vga_draw_line16_15,
2102 vga_draw_line16_16,
2103 vga_draw_line16_32,
2104
2105 vga_draw_line24_8,
2106 vga_draw_line24_15,
2107 vga_draw_line24_16,
2108 vga_draw_line24_32,
2109
2110 vga_draw_line32_8,
2111 vga_draw_line32_15,
2112 vga_draw_line32_16,
2113 vga_draw_line32_32,
2114};
2115
2116static int vgaR3GetBpp(PVGASTATE pThis)
2117{
2118 int ret;
2119#ifdef CONFIG_BOCHS_VBE
2120 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2121 ret = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
2122 } else
2123#endif
2124 {
2125 ret = 0;
2126 }
2127 return ret;
2128}
2129
2130static void vgaR3GetResolution(PVGASTATE pThis, int *pwidth, int *pheight)
2131{
2132 int width, height;
2133#ifdef CONFIG_BOCHS_VBE
2134 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
2135 width = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
2136 height = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
2137 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
2138 } else
2139#endif
2140 {
2141 width = (pThis->cr[0x01] + 1) * 8;
2142 height = pThis->cr[0x12] |
2143 ((pThis->cr[0x07] & 0x02) << 7) |
2144 ((pThis->cr[0x07] & 0x40) << 3);
2145 height = (height + 1);
2146 }
2147 *pwidth = width;
2148 *pheight = height;
2149}
2150
2151
2152/**
2153 * Performs the display driver resizing when in graphics mode.
2154 *
2155 * This will recalc / update any status data depending on the driver
2156 * properties (bit depth mostly).
2157 *
2158 * @returns VINF_SUCCESS on success.
2159 * @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
2160 * @param pThis Pointer to the shared VGA state.
2161 * @param pThisCC Pointer to the ring-3 VGA state.
2162 * @param cx The width.
2163 * @param cy The height.
2164 * @param pDrv The display connector.
2165 */
2166static int vgaR3ResizeGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, int cx, int cy, PDMIDISPLAYCONNECTOR *pDrv)
2167{
2168 const unsigned cBits = pThisCC->get_bpp(pThis);
2169
2170 int rc;
2171 AssertReturn(cx, VERR_INVALID_PARAMETER);
2172 AssertReturn(cy, VERR_INVALID_PARAMETER);
2173 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2174
2175 if (!pThis->line_offset)
2176 return VERR_INTERNAL_ERROR;
2177
2178#if 0 //def VBOX_WITH_VDMA
2179 /** @todo we get a second resize here when VBVA is on, while we actually should not */
2180 /* do not do pfnResize in case VBVA is on since all mode changes are performed over VBVA
2181 * we are checking for VDMA state here to ensure this code works only for WDDM driver,
2182 * although we should avoid calling pfnResize for XPDM as well, since pfnResize is actually an extra resize
2183 * event and generally only pfnVBVAxxx calls should be used with HGSMI + VBVA
2184 *
2185 * The reason for doing this for WDDM driver only now is to avoid regressions of the current code */
2186 PVBOXVDMAHOST pVdma = pThisCC->pVdma;
2187 if (pVdma && vboxVDMAIsEnabled(pVdma))
2188 rc = VINF_SUCCESS;
2189 else
2190#endif
2191 {
2192 /* Skip the resize if the values are not valid. */
2193 if (pThis->start_addr * 4 + pThis->line_offset * cy < pThis->vram_size)
2194 /* Take into account the programmed start address (in DWORDs) of the visible screen. */
2195 rc = pDrv->pfnResize(pDrv, cBits, pThisCC->pbVRam + pThis->start_addr * 4, pThis->line_offset, cx, cy);
2196 else
2197 {
2198 /* Change nothing in the VGA state. Lets hope the guest will eventually programm correct values. */
2199 return VERR_TRY_AGAIN;
2200 }
2201 }
2202
2203 /* last stuff */
2204 pThis->last_bpp = cBits;
2205 pThis->last_scr_width = cx;
2206 pThis->last_scr_height = cy;
2207 pThis->last_width = cx;
2208 pThis->last_height = cy;
2209
2210 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
2211 return rc;
2212 AssertRC(rc);
2213
2214 /* update palette */
2215 switch (pDrv->cBits)
2216 {
2217 case 32: pThisCC->rgb_to_pixel = rgb_to_pixel32_dup; break;
2218 case 16:
2219 default: pThisCC->rgb_to_pixel = rgb_to_pixel16_dup; break;
2220 case 15: pThisCC->rgb_to_pixel = rgb_to_pixel15_dup; break;
2221 case 8: pThisCC->rgb_to_pixel = rgb_to_pixel8_dup; break;
2222 }
2223 if (pThis->shift_control == 0)
2224 vgaR3UpdatePalette16(pThis, pThisCC);
2225 else if (pThis->shift_control == 1)
2226 vgaR3UpdatePalette16(pThis, pThisCC);
2227 return VINF_SUCCESS;
2228}
2229
2230# ifdef VBOX_WITH_VMSVGA
2231
2232# if 0 /* unused? */
2233int vgaR3UpdateDisplay(PVGASTATE pThis, PVGASTATER3 pThisCC, unsigned xStart, unsigned yStart, unsigned cx, unsigned cy, PDMIDISPLAYCONNECTOR *pDrv)
2234{
2235 uint32_t v;
2236 vga_draw_line_func *vga_draw_line;
2237
2238 if (!pThis->fRenderVRAM)
2239 {
2240 pDrv->pfnUpdateRect(pDrv, xStart, yStart, cx, cy);
2241 return VINF_SUCCESS;
2242 }
2243 /** @todo might crash if a blit follows a resolution change very quickly (seen this many times!) */
2244
2245 if ( pThis->svga.uWidth == VMSVGA_VAL_UNINITIALIZED
2246 || pThis->svga.uHeight == VMSVGA_VAL_UNINITIALIZED
2247 || pThis->svga.uBpp == VMSVGA_VAL_UNINITIALIZED)
2248 {
2249 /* Intermediate state; skip redraws. */
2250 AssertFailed();
2251 return VINF_SUCCESS;
2252 }
2253
2254 uint32_t cBits;
2255 switch (pThis->svga.uBpp) {
2256 default:
2257 case 0:
2258 case 8:
2259 AssertFailed();
2260 return VERR_NOT_IMPLEMENTED;
2261 case 15:
2262 v = VGA_DRAW_LINE15;
2263 cBits = 16;
2264 break;
2265 case 16:
2266 v = VGA_DRAW_LINE16;
2267 cBits = 16;
2268 break;
2269 case 24:
2270 v = VGA_DRAW_LINE24;
2271 cBits = 24;
2272 break;
2273 case 32:
2274 v = VGA_DRAW_LINE32;
2275 cBits = 32;
2276 break;
2277 }
2278 vga_draw_line = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2279
2280 uint32_t offSrc = (xStart * cBits) / 8 + pThis->svga.cbScanline * yStart;
2281 uint32_t offDst = (xStart * RT_ALIGN(pDrv->cBits, 8)) / 8 + pDrv->cbScanline * yStart;
2282
2283 uint8_t *pbDst = pDrv->pbData + offDst;
2284 uint8_t const *pbSrc = pThisCC->pbVRam + offSrc;
2285
2286 for (unsigned y = yStart; y < yStart + cy; y++)
2287 {
2288 vga_draw_line(pThis, pThisCC, pbDst, pbSrc, cx);
2289
2290 pbDst += pDrv->cbScanline;
2291 pbSrc += pThis->svga.cbScanline;
2292 }
2293 pDrv->pfnUpdateRect(pDrv, xStart, yStart, cx, cy);
2294
2295 return VINF_SUCCESS;
2296}
2297# endif
2298
2299/**
2300 * graphic modes
2301 */
2302static int vmsvgaR3DrawGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, bool fFullUpdate,
2303 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
2304{
2305 RT_NOREF1(fFailOnResize);
2306
2307 uint32_t const cx = pThis->last_scr_width;
2308 uint32_t const cxDisplay = cx;
2309 uint32_t const cy = pThis->last_scr_height;
2310 uint32_t cBits = pThis->last_bpp;
2311
2312 if ( cx == VMSVGA_VAL_UNINITIALIZED
2313 || cx == 0
2314 || cy == VMSVGA_VAL_UNINITIALIZED
2315 || cy == 0
2316 || cBits == VMSVGA_VAL_UNINITIALIZED
2317 || cBits == 0)
2318 {
2319 /* Intermediate state; skip redraws. */
2320 return VINF_SUCCESS;
2321 }
2322
2323 unsigned v;
2324 switch (cBits)
2325 {
2326 case 8:
2327 /* Note! experimental, not sure if this really works... */
2328 /** @todo fFullUpdate |= vgaR3UpdatePalette256(pThis); - need fFullUpdate but not
2329 * copying anything to last_palette. */
2330 v = VGA_DRAW_LINE8;
2331 break;
2332 case 15:
2333 v = VGA_DRAW_LINE15;
2334 cBits = 16;
2335 break;
2336 case 16:
2337 v = VGA_DRAW_LINE16;
2338 break;
2339 case 24:
2340 v = VGA_DRAW_LINE24;
2341 break;
2342 case 32:
2343 v = VGA_DRAW_LINE32;
2344 break;
2345 default:
2346 /*case 0: - Superfluous, checked already in the if above */
2347 AssertFailed();
2348 return VERR_NOT_IMPLEMENTED;
2349 }
2350 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2351
2352 Assert(!pThisCC->cursor_invalidate);
2353 Assert(!pThisCC->cursor_draw_line);
2354 //not used// if (pThisCC->cursor_invalidate)
2355 //not used// pThisCC->cursor_invalidate(pThis);
2356
2357 uint8_t *pbDst = pDrv->pbData;
2358 uint32_t cbDstScanline = pDrv->cbScanline;
2359 uint32_t offSrcStart = 0; /* always start at the beginning of the framebuffer */
2360 uint32_t cbScanline = (cx * cBits + 7) / 8; /* The visible width of a scanline. */
2361 uint32_t yUpdateRectTop = UINT32_MAX;
2362 uint32_t offPageMin = UINT32_MAX;
2363 int32_t offPageMax = -1;
2364 uint32_t y;
2365 for (y = 0; y < cy; y++)
2366 {
2367 uint32_t offSrcLine = offSrcStart + y * cbScanline;
2368 uint32_t offPage0 = offSrcLine & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2369 uint32_t offPage1 = (offSrcLine + cbScanline - 1) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2370 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2371 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2372 * anything wider than 2050 pixels @32bpp. Need to check all pages
2373 * between the first and last one. */
2374 bool fUpdate = fFullUpdate || vgaR3IsDirty(pThis, offPage0) || vgaR3IsDirty(pThis, offPage1);
2375 if (offPage1 - offPage0 > GUEST_PAGE_SIZE)
2376 /* if wide line, can use another page */
2377 fUpdate |= vgaR3IsDirty(pThis, offPage0 + GUEST_PAGE_SIZE);
2378 /* explicit invalidation for the hardware cursor */
2379 fUpdate |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2380 if (fUpdate)
2381 {
2382 if (yUpdateRectTop == UINT32_MAX)
2383 yUpdateRectTop = y;
2384 if (offPage0 < offPageMin)
2385 offPageMin = offPage0;
2386 if ((int32_t)offPage1 > offPageMax)
2387 offPageMax = offPage1;
2388 if (pThis->fRenderVRAM)
2389 pfnVgaDrawLine(pThis, pThisCC, pbDst, pThisCC->pbVRam + offSrcLine, cx);
2390 //not used// if (pThisCC->cursor_draw_line)
2391 //not used// pThisCC->cursor_draw_line(pThis, pbDst, y);
2392 }
2393 else if (yUpdateRectTop != UINT32_MAX)
2394 {
2395 /* flush to display */
2396 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop));
2397 pDrv->pfnUpdateRect(pDrv, 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop);
2398 yUpdateRectTop = UINT32_MAX;
2399 }
2400 pbDst += cbDstScanline;
2401 }
2402 if (yUpdateRectTop != UINT32_MAX)
2403 {
2404 /* flush to display */
2405 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop));
2406 pDrv->pfnUpdateRect(pDrv, 0, yUpdateRectTop, cxDisplay, y - yUpdateRectTop);
2407 }
2408
2409 /* reset modified pages */
2410 if (offPageMax != -1 && reset_dirty)
2411 vgaR3ResetDirty(pThis, offPageMin, offPageMax + GUEST_PAGE_SIZE);
2412 memset(pThis->invalidated_y_table, 0, ((cy + 31) >> 5) * 4);
2413
2414 return VINF_SUCCESS;
2415}
2416
2417# endif /* VBOX_WITH_VMSVGA */
2418
2419/**
2420 * graphic modes
2421 */
2422static int vgaR3DrawGraphic(PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update, bool fFailOnResize, bool reset_dirty,
2423 PDMIDISPLAYCONNECTOR *pDrv)
2424{
2425 int y1, y2, y, page_min, page_max, linesize, y_start, double_scan;
2426 int width, height, shift_control, line_offset, page0, page1, bwidth, bits;
2427 int disp_width, multi_run;
2428 uint8_t *d;
2429 uint32_t v, addr1, addr;
2430 vga_draw_line_func *pfnVgaDrawLine;
2431
2432 bool offsets_changed = vgaR3UpdateBasicParams(pThis, pThisCC);
2433
2434 full_update |= offsets_changed;
2435
2436 pThisCC->get_resolution(pThis, &width, &height);
2437 disp_width = width;
2438
2439 shift_control = (pThis->gr[0x05] >> 5) & 3;
2440 double_scan = (pThis->cr[0x09] >> 7);
2441 multi_run = double_scan;
2442 if (shift_control != pThis->shift_control ||
2443 double_scan != pThis->double_scan) {
2444 full_update = true;
2445 pThis->shift_control = shift_control;
2446 pThis->double_scan = double_scan;
2447 }
2448
2449 if (shift_control == 0) {
2450 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
2451 if (pThis->sr[0x01] & 8) {
2452 v = VGA_DRAW_LINE4D2;
2453 disp_width <<= 1;
2454 } else {
2455 v = VGA_DRAW_LINE4;
2456 }
2457 bits = 4;
2458 } else if (shift_control == 1) {
2459 full_update |= vgaR3UpdatePalette16(pThis, pThisCC);
2460 if (pThis->sr[0x01] & 8) {
2461 v = VGA_DRAW_LINE2D2;
2462 disp_width <<= 1;
2463 } else {
2464 v = VGA_DRAW_LINE2;
2465 }
2466 bits = 4;
2467 } else {
2468 switch(pThisCC->get_bpp(pThis)) {
2469 default:
2470 case 0:
2471 full_update |= vgaR3UpdatePalette256(pThis, pThisCC);
2472 v = VGA_DRAW_LINE8D2;
2473 bits = 4;
2474 break;
2475 case 8:
2476 full_update |= vgaR3UpdatePalette256(pThis, pThisCC);
2477 v = VGA_DRAW_LINE8;
2478 bits = 8;
2479 break;
2480 case 15:
2481 v = VGA_DRAW_LINE15;
2482 bits = 16;
2483 break;
2484 case 16:
2485 v = VGA_DRAW_LINE16;
2486 bits = 16;
2487 break;
2488 case 24:
2489 v = VGA_DRAW_LINE24;
2490 bits = 24;
2491 break;
2492 case 32:
2493 v = VGA_DRAW_LINE32;
2494 bits = 32;
2495 break;
2496 }
2497 }
2498 if ( disp_width != (int)pThis->last_width
2499 || height != (int)pThis->last_height
2500 || pThisCC->get_bpp(pThis) != (int)pThis->last_bpp
2501 || (offsets_changed && !pThis->fRenderVRAM))
2502 {
2503 if (fFailOnResize)
2504 {
2505 /* The caller does not want to call the pfnResize. */
2506 return VERR_TRY_AGAIN;
2507 }
2508 int rc = vgaR3ResizeGraphic(pThis, pThisCC, disp_width, height, pDrv);
2509 if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
2510 return rc;
2511 full_update = true;
2512 }
2513
2514 if (pThis->fRenderVRAM)
2515 {
2516 /* Do not update the destination buffer if it is not big enough.
2517 * Can happen if the resize request was ignored by the driver.
2518 * Compare with 'disp_width', because it is what the framebuffer has been resized to.
2519 */
2520 if ( pDrv->cx != (uint32_t)disp_width
2521 || pDrv->cy != (uint32_t)height)
2522 {
2523 LogRel(("Framebuffer mismatch: vga %dx%d, drv %dx%d!!!\n",
2524 disp_width, height,
2525 pDrv->cx, pDrv->cy));
2526 return VINF_SUCCESS;
2527 }
2528 }
2529
2530 pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pDrv->cBits)];
2531
2532 if (pThisCC->cursor_invalidate)
2533 pThisCC->cursor_invalidate(pThis);
2534
2535 line_offset = pThis->line_offset;
2536#if 0
2537 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",
2538 width, height, v, line_offset, pThis->cr[9], pThis->cr[0x17], pThis->line_compare, pThis->sr[0x01]));
2539#endif
2540 addr1 = (pThis->start_addr * 4);
2541 bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
2542 y_start = -1;
2543 page_min = 0x7fffffff;
2544 page_max = -1;
2545 d = pDrv->pbData;
2546 linesize = pDrv->cbScanline;
2547
2548 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
2549 pThis->vga_addr_mask = 0x3ffff;
2550 else
2551 pThis->vga_addr_mask = UINT32_MAX;
2552
2553 y1 = 0;
2554 y2 = pThis->cr[0x09] & 0x1F; /* starting row scan count */
2555 for(y = 0; y < height; y++) {
2556 addr = addr1;
2557 /* CGA/MDA compatibility. Note that these addresses are all
2558 * shifted left by two compared to VGA specs.
2559 */
2560 if (!(pThis->cr[0x17] & 1)) {
2561 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2562 }
2563 if (!(pThis->cr[0x17] & 2)) {
2564 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2565 }
2566 addr &= pThis->vga_addr_mask;
2567 page0 = addr & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2568 page1 = (addr + bwidth - 1) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2569 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2570 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2571 * anything wider than 2050 pixels @32bpp. Need to check all pages
2572 * between the first and last one. */
2573 bool update = full_update || vgaR3IsDirty(pThis, page0) || vgaR3IsDirty(pThis, page1);
2574 if (page1 - page0 > GUEST_PAGE_SIZE) {
2575 /* if wide line, can use another page */
2576 update |= vgaR3IsDirty(pThis, page0 + GUEST_PAGE_SIZE);
2577 }
2578 /* explicit invalidation for the hardware cursor */
2579 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2580 if (update) {
2581 if (y_start < 0)
2582 y_start = y;
2583 if (page0 < page_min)
2584 page_min = page0;
2585 if (page1 > page_max)
2586 page_max = page1;
2587 if (pThis->fRenderVRAM)
2588 pfnVgaDrawLine(pThis, pThisCC, d, pThisCC->pbVRam + addr, width);
2589 if (pThisCC->cursor_draw_line)
2590 pThisCC->cursor_draw_line(pThis, d, y);
2591 } else {
2592 if (y_start >= 0) {
2593 /* flush to display */
2594 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2595 y_start = -1;
2596 }
2597 }
2598 if (!multi_run) {
2599 y1++;
2600 multi_run = double_scan;
2601
2602 if (y2 == 0) {
2603 y2 = pThis->cr[0x09] & 0x1F;
2604 addr1 += line_offset;
2605 } else {
2606 --y2;
2607 }
2608 } else {
2609 multi_run--;
2610 }
2611 /* line compare acts on the displayed lines */
2612 if ((uint32_t)y == pThis->line_compare)
2613 addr1 = 0;
2614 d += linesize;
2615 }
2616 if (y_start >= 0) {
2617 /* flush to display */
2618 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2619 }
2620 /* reset modified pages */
2621 if (page_max != -1 && reset_dirty) {
2622 vgaR3ResetDirty(pThis, page_min, page_max + GUEST_PAGE_SIZE);
2623 }
2624 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2625 return VINF_SUCCESS;
2626}
2627
2628/**
2629 * blanked modes
2630 */
2631static int vgaR3DrawBlank(PVGASTATE pThis, PVGASTATER3 pThisCC, bool full_update,
2632 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv)
2633{
2634 int i, w, val;
2635 uint8_t *d;
2636 uint32_t cbScanline = pDrv->cbScanline;
2637 uint32_t page_min, page_max;
2638
2639 if (pThis->last_width != 0)
2640 {
2641 if (fFailOnResize)
2642 {
2643 /* The caller does not want to call the pfnResize. */
2644 return VERR_TRY_AGAIN;
2645 }
2646 pThis->last_width = 0;
2647 pThis->last_height = 0;
2648 /* For blanking signal width=0, height=0, bpp=0 and cbLine=0 here.
2649 * There is no screen content, which distinguishes it from text mode. */
2650 pDrv->pfnResize(pDrv, 0, NULL, 0, 0, 0);
2651 }
2652 /* reset modified pages, i.e. everything */
2653 if (reset_dirty && pThis->last_scr_height > 0)
2654 {
2655 page_min = (pThis->start_addr * 4) & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2656 /* round up page_max by one page, as otherwise this can be -GUEST_PAGE_SIZE,
2657 * which causes assertion trouble in vgaR3ResetDirty. */
2658 page_max = (pThis->start_addr * 4 + pThis->line_offset * pThis->last_scr_height - 1 + GUEST_PAGE_SIZE)
2659 & ~(uint32_t)GUEST_PAGE_OFFSET_MASK;
2660 vgaR3ResetDirty(pThis, page_min, page_max + GUEST_PAGE_SIZE);
2661 }
2662 if (pDrv->pbData == pThisCC->pbVRam) /* Do not clear the VRAM itself. */
2663 return VINF_SUCCESS;
2664 if (!full_update)
2665 return VINF_SUCCESS;
2666 if (pThis->last_scr_width <= 0 || pThis->last_scr_height <= 0)
2667 return VINF_SUCCESS;
2668 if (pDrv->cBits == 8)
2669 val = pThisCC->rgb_to_pixel(0, 0, 0);
2670 else
2671 val = 0;
2672 w = pThis->last_scr_width * ((pDrv->cBits + 7) >> 3);
2673 d = pDrv->pbData;
2674 if (pThis->fRenderVRAM)
2675 {
2676 for(i = 0; i < (int)pThis->last_scr_height; i++) {
2677 memset(d, val, w);
2678 d += cbScanline;
2679 }
2680 }
2681 pDrv->pfnUpdateRect(pDrv, 0, 0, pThis->last_scr_width, pThis->last_scr_height);
2682 return VINF_SUCCESS;
2683}
2684
2685
2686#define GMODE_TEXT 0
2687#define GMODE_GRAPH 1
2688#define GMODE_BLANK 2
2689#ifdef VBOX_WITH_VMSVGA
2690#define GMODE_SVGA 3
2691#endif
2692
2693/**
2694 * Worker for vgaR3PortUpdateDisplay(), vgaR3UpdateDisplayAllInternal() and
2695 * vgaR3PortTakeScreenshot().
2696 */
2697static int vgaR3UpdateDisplay(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATER3 pThisCC, bool fUpdateAll,
2698 bool fFailOnResize, bool reset_dirty, PDMIDISPLAYCONNECTOR *pDrv, int32_t *pcur_graphic_mode)
2699{
2700 int rc = VINF_SUCCESS;
2701 int graphic_mode;
2702
2703 if (pDrv->cBits == 0) {
2704 /* nothing to do */
2705 } else {
2706 switch(pDrv->cBits) {
2707 case 8:
2708 pThisCC->rgb_to_pixel = rgb_to_pixel8_dup;
2709 break;
2710 case 15:
2711 pThisCC->rgb_to_pixel = rgb_to_pixel15_dup;
2712 break;
2713 default:
2714 case 16:
2715 pThisCC->rgb_to_pixel = rgb_to_pixel16_dup;
2716 break;
2717 case 32:
2718 pThisCC->rgb_to_pixel = rgb_to_pixel32_dup;
2719 break;
2720 }
2721
2722#ifdef VBOX_WITH_VMSVGA
2723 if (pThis->svga.fEnabled) {
2724 graphic_mode = GMODE_SVGA;
2725 }
2726 else
2727#endif
2728 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2729 graphic_mode = GMODE_BLANK;
2730 } else {
2731 graphic_mode = pThis->gr[6] & 1 ? GMODE_GRAPH : GMODE_TEXT;
2732 }
2733 bool full_update = fUpdateAll || graphic_mode != *pcur_graphic_mode;
2734 if (full_update) {
2735 *pcur_graphic_mode = graphic_mode;
2736 }
2737 switch(graphic_mode) {
2738 case GMODE_TEXT:
2739 rc = vgaR3DrawText(pDevIns, pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2740 break;
2741 case GMODE_GRAPH:
2742 rc = vgaR3DrawGraphic(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2743 break;
2744#ifdef VBOX_WITH_VMSVGA
2745 case GMODE_SVGA:
2746 rc = vmsvgaR3DrawGraphic(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2747 break;
2748#endif
2749 case GMODE_BLANK:
2750 default:
2751 rc = vgaR3DrawBlank(pThis, pThisCC, full_update, fFailOnResize, reset_dirty, pDrv);
2752 break;
2753 }
2754 }
2755 return rc;
2756}
2757
2758/**
2759 * Worker for vgaR3SaveExec().
2760 */
2761static void vga_save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PVGASTATE pThis)
2762{
2763 int i;
2764
2765 pHlp->pfnSSMPutU32(pSSM, pThis->latch);
2766 pHlp->pfnSSMPutU8(pSSM, pThis->sr_index);
2767 pHlp->pfnSSMPutMem(pSSM, pThis->sr, 8);
2768 pHlp->pfnSSMPutU8(pSSM, pThis->gr_index);
2769 pHlp->pfnSSMPutMem(pSSM, pThis->gr, 16);
2770 pHlp->pfnSSMPutU8(pSSM, pThis->ar_index);
2771 pHlp->pfnSSMPutMem(pSSM, pThis->ar, 21);
2772 pHlp->pfnSSMPutU32(pSSM, pThis->ar_flip_flop);
2773 pHlp->pfnSSMPutU8(pSSM, pThis->cr_index);
2774 pHlp->pfnSSMPutMem(pSSM, pThis->cr, 256);
2775 pHlp->pfnSSMPutU8(pSSM, pThis->msr);
2776 pHlp->pfnSSMPutU8(pSSM, pThis->fcr);
2777 pHlp->pfnSSMPutU8(pSSM, pThis->st00);
2778 pHlp->pfnSSMPutU8(pSSM, pThis->st01);
2779
2780 pHlp->pfnSSMPutU8(pSSM, pThis->dac_state);
2781 pHlp->pfnSSMPutU8(pSSM, pThis->dac_sub_index);
2782 pHlp->pfnSSMPutU8(pSSM, pThis->dac_read_index);
2783 pHlp->pfnSSMPutU8(pSSM, pThis->dac_write_index);
2784 pHlp->pfnSSMPutMem(pSSM, pThis->dac_cache, 3);
2785 pHlp->pfnSSMPutMem(pSSM, pThis->palette, 768);
2786
2787 pHlp->pfnSSMPutU32(pSSM, pThis->bank_offset);
2788#ifdef CONFIG_BOCHS_VBE
2789 AssertCompile(RT_ELEMENTS(pThis->vbe_regs) < 256);
2790 pHlp->pfnSSMPutU8(pSSM, (uint8_t)RT_ELEMENTS(pThis->vbe_regs));
2791 pHlp->pfnSSMPutU16(pSSM, pThis->vbe_index);
2792 for(i = 0; i < (int)RT_ELEMENTS(pThis->vbe_regs); i++)
2793 pHlp->pfnSSMPutU16(pSSM, pThis->vbe_regs[i]);
2794 pHlp->pfnSSMPutU32(pSSM, pThis->vbe_start_addr);
2795 pHlp->pfnSSMPutU32(pSSM, pThis->vbe_line_offset);
2796#else
2797 pHlp->pfnSSMPutU8(pSSM, 0);
2798#endif
2799}
2800
2801
2802/**
2803 * Worker for vgaR3LoadExec().
2804 */
2805static int vga_load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PVGASTATE pThis, int version_id)
2806{
2807 int is_vbe, i;
2808 uint32_t u32Dummy;
2809 uint8_t u8;
2810
2811 pHlp->pfnSSMGetU32(pSSM, &pThis->latch);
2812 pHlp->pfnSSMGetU8(pSSM, &pThis->sr_index);
2813 pHlp->pfnSSMGetMem(pSSM, pThis->sr, 8);
2814 pHlp->pfnSSMGetU8(pSSM, &pThis->gr_index);
2815 pHlp->pfnSSMGetMem(pSSM, pThis->gr, 16);
2816 pHlp->pfnSSMGetU8(pSSM, &pThis->ar_index);
2817 pHlp->pfnSSMGetMem(pSSM, pThis->ar, 21);
2818 pHlp->pfnSSMGetS32(pSSM, &pThis->ar_flip_flop);
2819 pHlp->pfnSSMGetU8(pSSM, &pThis->cr_index);
2820 pHlp->pfnSSMGetMem(pSSM, pThis->cr, 256);
2821 pHlp->pfnSSMGetU8(pSSM, &pThis->msr);
2822 pHlp->pfnSSMGetU8(pSSM, &pThis->fcr);
2823 pHlp->pfnSSMGetU8(pSSM, &pThis->st00);
2824 pHlp->pfnSSMGetU8(pSSM, &pThis->st01);
2825
2826 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_state);
2827 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_sub_index);
2828 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_read_index);
2829 pHlp->pfnSSMGetU8(pSSM, &pThis->dac_write_index);
2830 pHlp->pfnSSMGetMem(pSSM, pThis->dac_cache, 3);
2831 pHlp->pfnSSMGetMem(pSSM, pThis->palette, 768);
2832
2833 pHlp->pfnSSMGetS32(pSSM, &pThis->bank_offset);
2834 pHlp->pfnSSMGetU8(pSSM, &u8);
2835 is_vbe = !!u8;
2836#ifdef CONFIG_BOCHS_VBE
2837 if (!is_vbe)
2838 {
2839 Log(("vga_load: !is_vbe !!\n"));
2840 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2841 }
2842
2843 if (u8 == 1)
2844 u8 = VBE_DISPI_INDEX_NB_SAVED; /* Used to save so many registers. */
2845 if (u8 > RT_ELEMENTS(pThis->vbe_regs))
2846 {
2847 Log(("vga_load: saved %d, expected %d!!\n", u8, RT_ELEMENTS(pThis->vbe_regs)));
2848 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2849 }
2850
2851 pHlp->pfnSSMGetU16(pSSM, &pThis->vbe_index);
2852 for(i = 0; i < (int)u8; i++)
2853 pHlp->pfnSSMGetU16(pSSM, &pThis->vbe_regs[i]);
2854 if (version_id <= VGA_SAVEDSTATE_VERSION_INV_VHEIGHT)
2855 recalculate_data(pThis); /* <- re-calculate the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] since it might be invalid */
2856 pHlp->pfnSSMGetU32(pSSM, &pThis->vbe_start_addr);
2857 pHlp->pfnSSMGetU32(pSSM, &pThis->vbe_line_offset);
2858 if (version_id < 2)
2859 pHlp->pfnSSMGetU32(pSSM, &u32Dummy);
2860 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
2861#else
2862 if (is_vbe)
2863 {
2864 Log(("vga_load: is_vbe !!\n"));
2865 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2866 }
2867#endif
2868
2869 /* force refresh */
2870 pThis->graphic_mode = -1;
2871 return 0;
2872}
2873
2874
2875/**
2876 * Worker for vgaR3Construct().
2877 */
2878static void vgaR3InitExpand(void)
2879{
2880 int i, j, v, b;
2881
2882 for(i = 0;i < 256; i++) {
2883 v = 0;
2884 for(j = 0; j < 8; j++) {
2885 v |= ((i >> j) & 1) << (j * 4);
2886 }
2887 expand4[i] = v;
2888
2889 v = 0;
2890 for(j = 0; j < 4; j++) {
2891 v |= ((i >> (2 * j)) & 3) << (j * 4);
2892 }
2893 expand2[i] = v;
2894 }
2895 for(i = 0; i < 16; i++) {
2896 v = 0;
2897 for(j = 0; j < 4; j++) {
2898 b = ((i >> j) & 1);
2899 v |= b << (2 * j);
2900 v |= b << (2 * j + 1);
2901 }
2902 expand4to8[i] = v;
2903 }
2904}
2905
2906#endif /* IN_RING3 */
2907
2908
2909
2910/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2911
2912#define VGA_IOPORT_WRITE_PLACEHOLDER(a_uPort, a_cPorts) do {\
2913 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); \
2914 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo))); \
2915 AssertCompile(RT_IS_POWER_OF_TWO(a_cPorts)); \
2916 Assert((unsigned)offPort - (unsigned)(a_uPort) < (unsigned)(a_cPorts)); \
2917 NOREF(pvUser); \
2918 if (cb == 1) \
2919 vga_ioport_write(pDevIns, pThis, offPort, u32); \
2920 else if (cb == 2) \
2921 { \
2922 vga_ioport_write(pDevIns, pThis, offPort, u32 & 0xff); \
2923 vga_ioport_write(pDevIns, pThis, offPort + 1, u32 >> 8); \
2924 } \
2925 return VINF_SUCCESS; \
2926 } while (0)
2927
2928#define VGA_IOPORT_READ_PLACEHOLDER(a_uPort, a_cPorts) do {\
2929 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE); \
2930 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo))); \
2931 AssertCompile(RT_IS_POWER_OF_TWO(a_cPorts)); \
2932 Assert((unsigned)offPort - (unsigned)(a_uPort) < (unsigned)(a_cPorts)); \
2933 NOREF(pvUser); \
2934 if (cb == 1) \
2935 *pu32 = vga_ioport_read(pDevIns, pThis, offPort); \
2936 else if (cb == 2) \
2937 { \
2938 uint32_t u32 = vga_ioport_read(pDevIns, pThis, offPort); \
2939 u32 |= vga_ioport_read(pDevIns, pThis, offPort + 1) << 8; \
2940 *pu32 = u32; \
2941 } \
2942 else \
2943 return VERR_IOM_IOPORT_UNUSED; \
2944 return VINF_SUCCESS; \
2945 } while (0)
2946
2947/**
2948 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c0-0x3c1 Attribute Controller.}
2949 */
2950static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortArWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2951{
2952 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c0, 2);
2953}
2954
2955/**
2956 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c0-0x3c1 Attribute Controller.}
2957 */
2958static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortArRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2959{
2960 VGA_IOPORT_READ_PLACEHOLDER(0x3c0, 2);
2961}
2962
2963
2964/**
2965 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c2 Miscellaneous Register.}
2966 */
2967static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMsrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2968{
2969 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c2, 1);
2970}
2971
2972/**
2973 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c2 Status register 0.}
2974 */
2975static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSt00Read(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2976{
2977 VGA_IOPORT_READ_PLACEHOLDER(0x3c2, 1);
2978}
2979
2980
2981/**
2982 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c3 Unused.}
2983 */
2984static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortUnusedWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
2985{
2986 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c3, 1);
2987}
2988
2989/**
2990 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c3 Unused.}
2991 */
2992static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortUnusedRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
2993{
2994 VGA_IOPORT_READ_PLACEHOLDER(0x3c3, 1);
2995}
2996
2997
2998/**
2999 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c4-0x3c5 Sequencer.}
3000 */
3001static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3002{
3003 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c4, 2);
3004}
3005
3006/**
3007 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c4-0x3c5 Sequencer.}
3008 */
3009static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortSrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3010{
3011 VGA_IOPORT_READ_PLACEHOLDER(0x3c4, 2);
3012}
3013
3014
3015/**
3016 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3c6-0x3c9 DAC.}
3017 */
3018static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortDacWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3019{
3020 VGA_IOPORT_WRITE_PLACEHOLDER(0x3c6, 4);
3021}
3022
3023/**
3024 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3c6-0x3c9 DAC.}
3025 */
3026static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortDacRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3027{
3028 VGA_IOPORT_READ_PLACEHOLDER(0x3c6, 4);
3029}
3030
3031
3032/**
3033 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ca-0x3cd Graphics Position?}
3034 */
3035static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortPosWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3036{
3037 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ca, 4);
3038}
3039
3040/**
3041 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ca-0x3cd Graphics Position?}
3042 */
3043static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortPosRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3044{
3045 VGA_IOPORT_READ_PLACEHOLDER(0x3ca, 4);
3046}
3047
3048
3049/**
3050 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ce-0x3cf Graphics Controller.}
3051 */
3052static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortGrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3053{
3054 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ce, 2);
3055}
3056
3057/**
3058 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ca-0x3cf Graphics Controller.}
3059 */
3060static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortGrRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3061{
3062 VGA_IOPORT_READ_PLACEHOLDER(0x3ce, 2);
3063}
3064
3065
3066/**
3067 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3b4-0x3b5 MDA CRT control.}
3068 */
3069static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaCrtWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3070{
3071 /** @todo do vga_ioport_invalid here */
3072 VGA_IOPORT_WRITE_PLACEHOLDER(0x3b4, 2);
3073}
3074
3075/**
3076 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3b4-0x3b5 MDA CRT control.}
3077 */
3078static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaCrtRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3079{
3080 /** @todo do vga_ioport_invalid here */
3081 VGA_IOPORT_READ_PLACEHOLDER(0x3b4, 2);
3082}
3083
3084
3085/**
3086 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3ba MDA feature/status.}
3087 */
3088static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaFcrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3089{
3090 /** @todo do vga_ioport_invalid here */
3091 VGA_IOPORT_WRITE_PLACEHOLDER(0x3ba, 1);
3092}
3093
3094/**
3095 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3ba MDA feature/status.}
3096 */
3097static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortMdaStRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3098{
3099 /** @todo do vga_ioport_invalid here */
3100 VGA_IOPORT_READ_PLACEHOLDER(0x3ba, 1);
3101}
3102
3103
3104/**
3105 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3d4-0x3d5 CGA CRT control.}
3106 */
3107static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaCrtWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3108{
3109 /** @todo do vga_ioport_invalid here */
3110 VGA_IOPORT_WRITE_PLACEHOLDER(0x3d4, 2);
3111}
3112
3113/**
3114 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3d4-0x3d5 CGA CRT control.}
3115 */
3116static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaCrtRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3117{
3118 /** @todo do vga_ioport_invalid here */
3119 VGA_IOPORT_READ_PLACEHOLDER(0x3d4, 2);
3120}
3121
3122
3123/**
3124 * @callback_method_impl{FNIOMIOPORTNEWOUT,0x3da CGA feature/status.}
3125 */
3126static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaFcrWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3127{
3128 /** @todo do vga_ioport_invalid here */
3129 VGA_IOPORT_WRITE_PLACEHOLDER(0x3da, 1);
3130}
3131
3132/**
3133 * @callback_method_impl{FNIOMIOPORTNEWIN,0x3da CGA feature/status.}
3134 */
3135static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortCgaStRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3136{
3137 /** @todo do vga_ioport_invalid here */
3138 VGA_IOPORT_READ_PLACEHOLDER(0x3da, 1);
3139}
3140
3141
3142/**
3143 * @callback_method_impl{FNIOMIOPORTNEWOUT,VBE Data Port OUT handler (0x1ce).}
3144 */
3145static DECLCALLBACK(VBOXSTRICTRC)
3146vgaIoPortWriteVbeData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3147{
3148 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3149 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3150 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3151
3152 NOREF(pvUser);
3153
3154#ifndef IN_RING3
3155 /*
3156 * This has to be done on the host in order to execute the connector callbacks.
3157 */
3158 if ( pThis->vbe_index == VBE_DISPI_INDEX_ENABLE
3159 || pThis->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
3160 {
3161 Log(("vgaIoPortWriteVbeData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
3162 return VINF_IOM_R3_IOPORT_WRITE;
3163 }
3164#endif
3165#ifdef VBE_BYTEWISE_IO
3166 if (cb == 1)
3167 {
3168 if (!pThis->fWriteVBEData)
3169 {
3170 if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
3171 && (u32 & VBE_DISPI_ENABLED))
3172 {
3173 pThis->fWriteVBEData = false;
3174 return vbe_ioport_write_data(pDevIns, pThis, pThisCC, offPort, u32 & 0xFF);
3175 }
3176
3177 pThis->cbWriteVBEData = u32 & 0xFF;
3178 pThis->fWriteVBEData = true;
3179 return VINF_SUCCESS;
3180 }
3181
3182 u32 = (pThis->cbWriteVBEData << 8) | (u32 & 0xFF);
3183 pThis->fWriteVBEData = false;
3184 cb = 2;
3185 }
3186#endif
3187 if (cb == 2 || cb == 4)
3188 return vbe_ioport_write_data(pDevIns, pThis, pThisCC, offPort, u32);
3189 ASSERT_GUEST_MSG_FAILED(("vgaIoPortWriteVbeData: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
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 ASSERT_GUEST_MSG_FAILED(("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 ASSERT_GUEST_MSG_FAILED(("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_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 /* pThisCC->IVBVACallbacks not used currently. */
4777 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4778 return NULL;
4779}
4780
4781
4782/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4783
4784/**
4785 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4786 */
4787static DECLCALLBACK(int) vgaR3PortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4788{
4789 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, ILeds);
4790 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4791 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4792 switch (iLUN)
4793 {
4794 /* LUN #0 is the only one for which we have a status LED. */
4795 case 0:
4796 {
4797 *ppLed = &pThis->Led3D;
4798 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4799 return VINF_SUCCESS;
4800 }
4801
4802 default:
4803 AssertMsgFailed(("Invalid LUN #%u\n", iLUN));
4804 return VERR_PDM_NO_SUCH_LUN;
4805 }
4806}
4807
4808
4809/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4810
4811/**
4812 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnResize}
4813 */
4814static DECLCALLBACK(int) vgaR3DummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t cBits, void *pvVRAM,
4815 uint32_t cbLine, uint32_t cx, uint32_t cy)
4816{
4817 RT_NOREF(pInterface, cBits, pvVRAM, cbLine, cx, cy);
4818 return VINF_SUCCESS;
4819}
4820
4821
4822/**
4823 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnUpdateRect}
4824 */
4825static DECLCALLBACK(void) vgaR3DummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4826{
4827 RT_NOREF(pInterface, x, y, cx, cy);
4828}
4829
4830
4831/**
4832 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnRefresh}
4833 */
4834static DECLCALLBACK(void) vgaR3DummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4835{
4836 NOREF(pInterface);
4837}
4838
4839
4840/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4841
4842/**
4843 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
4844 */
4845static DECLCALLBACK(int) vgaR3PortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4846{
4847 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4848 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4849 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4850
4851 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4852 AssertRCReturn(rc, rc);
4853
4854# ifdef VBOX_WITH_VMSVGA
4855 if ( pThis->svga.fEnabled
4856 && !pThis->svga.fTraces)
4857 {
4858 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4859 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4860 return VINF_SUCCESS;
4861 }
4862#endif
4863
4864# ifndef VBOX_WITH_HGSMI
4865 /* This should be called only in non VBVA mode. */
4866# else
4867 if (VBVAUpdateDisplay(pThis, pThisCC) == VINF_SUCCESS)
4868 {
4869 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4870 return VINF_SUCCESS;
4871 }
4872# endif /* VBOX_WITH_HGSMI */
4873
4874 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4875
4876 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4877 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4878
4879 if (pThis->bmPageRemappedVGA != 0)
4880 {
4881 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4882 STAM_COUNTER_INC(&pThis->StatMapReset);
4883 vgaResetRemapped(pThis);
4884 }
4885
4886 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, false /*fFailOnResize*/, true /*reset_dirty*/,
4887 pThisCC->pDrv, &pThis->graphic_mode);
4888 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4889 return rc;
4890}
4891
4892
4893/**
4894 * Internal vgaR3PortUpdateDisplayAll worker called under pThis->CritSect.
4895 */
4896static int vgaR3UpdateDisplayAllInternal(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool fFailOnResize)
4897{
4898# ifdef VBOX_WITH_VMSVGA
4899 if ( !pThis->svga.fEnabled
4900 || pThis->svga.fTraces)
4901# endif
4902 {
4903 /* Update the dirty bits. */
4904 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4905 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4906 }
4907
4908 if (pThis->bmPageRemappedVGA != 0)
4909 {
4910 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4911 STAM_COUNTER_INC(&pThis->StatMapReset);
4912 vgaResetRemapped(pThis);
4913 }
4914
4915 pThis->graphic_mode = -1; /* force full update */
4916
4917 return vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, true /*fUpdateAll*/, fFailOnResize,
4918 true /*reset_dirty*/, pThisCC->pDrv, &pThis->graphic_mode);
4919}
4920
4921
4922/**
4923 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
4924 */
4925static DECLCALLBACK(int) vgaR3PortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4926{
4927 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4928 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4929 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4930
4931 /* This is called both in VBVA mode and normal modes. */
4932
4933# ifdef DEBUG_sunlover
4934 LogFlow(("vgaR3PortUpdateDisplayAll\n"));
4935# endif /* DEBUG_sunlover */
4936
4937 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4938 AssertRCReturn(rc, rc);
4939
4940 rc = vgaR3UpdateDisplayAllInternal(pDevIns, pThis, pThisCC, fFailOnResize);
4941
4942 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4943 return rc;
4944}
4945
4946
4947/**
4948 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
4949 */
4950static DECLCALLBACK(int) vgaR3PortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4951{
4952 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4953 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4954 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4955
4956 /*
4957 * Update the interval, notify the VMSVGA FIFO thread if sleeping,
4958 * then restart or stop the timer.
4959 */
4960 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
4961
4962# ifdef VBOX_WITH_VMSVGA
4963 if (pThis->svga.fFIFOThreadSleeping)
4964 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
4965# endif
4966
4967 if (cMilliesInterval)
4968 return PDMDevHlpTimerSetMillies(pDevIns, pThis->hRefreshTimer, cMilliesInterval);
4969 return PDMDevHlpTimerStop(pDevIns, pThis->hRefreshTimer);
4970}
4971
4972
4973/**
4974 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
4975 */
4976static DECLCALLBACK(int) vgaR3PortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4977{
4978 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4979 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4980 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4981
4982 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
4983
4984 *pcBits = vgaR3GetBpp(pThis);
4985 if (pcx)
4986 *pcx = pThis->last_scr_width;
4987 if (pcy)
4988 *pcy = pThis->last_scr_height;
4989 return VINF_SUCCESS;
4990}
4991
4992
4993/**
4994 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
4995 */
4996static DECLCALLBACK(int) vgaR3PortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
4997 uint32_t *pcx, uint32_t *pcy)
4998{
4999 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5000 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5001 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5002 PDMDEV_ASSERT_EMT(pDevIns);
5003
5004 LogFlow(("vgaR3PortTakeScreenshot: ppbData=%p pcbData=%p pcx=%p pcy=%p\n", ppbData, pcbData, pcx, pcy));
5005
5006 /*
5007 * Validate input.
5008 */
5009 if (!RT_VALID_PTR(ppbData) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
5010 return VERR_INVALID_PARAMETER;
5011
5012 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5013 AssertRCReturn(rc, rc);
5014
5015 /*
5016 * Get screenshot. This function will fail if a resize is required.
5017 * So there is not need to do a 'vgaR3UpdateDisplayAllInternal' before taking screenshot.
5018 */
5019
5020 /*
5021 * Allocate the buffer for 32 bits per pixel bitmap
5022 *
5023 * Note! The size can't be zero or greater than the size of the VRAM.
5024 * Inconsistent VGA device state can cause the incorrect size values.
5025 */
5026 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
5027 if (cbRequired && cbRequired <= pThis->vram_size)
5028 {
5029 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbRequired);
5030 if (pbData != NULL)
5031 {
5032 /*
5033 * Only 3 methods, assigned below, will be called during the screenshot update.
5034 * All other are already set to NULL.
5035 */
5036 /* The display connector interface is temporarily replaced with the fake one. */
5037 PDMIDISPLAYCONNECTOR Connector;
5038 RT_ZERO(Connector);
5039 Connector.pbData = pbData;
5040 Connector.cBits = 32;
5041 Connector.cx = pThis->last_scr_width;
5042 Connector.cy = pThis->last_scr_height;
5043 Connector.cbScanline = Connector.cx * 4;
5044 Connector.pfnRefresh = vgaR3DummyRefresh;
5045 Connector.pfnResize = vgaR3DummyResize;
5046 Connector.pfnUpdateRect = vgaR3DummyUpdateRect;
5047
5048 int32_t cur_graphic_mode = -1;
5049
5050 bool fSavedRenderVRAM = pThis->fRenderVRAM;
5051 pThis->fRenderVRAM = true;
5052
5053 /*
5054 * Take the screenshot.
5055 *
5056 * The second parameter is 'false' because the current display state is being rendered to an
5057 * external buffer using a fake connector. That is if display is blanked, we expect a black
5058 * screen in the external buffer.
5059 * If there is a pending resize, the function will fail.
5060 */
5061 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, true /*fFailOnResize*/,
5062 false /*reset_dirty*/, &Connector, &cur_graphic_mode);
5063
5064 pThis->fRenderVRAM = fSavedRenderVRAM;
5065
5066 if (rc == VINF_SUCCESS)
5067 {
5068 /*
5069 * Return the result.
5070 */
5071 *ppbData = pbData;
5072 *pcbData = cbRequired;
5073 *pcx = Connector.cx;
5074 *pcy = Connector.cy;
5075 }
5076 else
5077 {
5078 /* If we do not return a success, then the data buffer must be freed. */
5079 RTMemFree(pbData);
5080 if (RT_SUCCESS_NP(rc))
5081 {
5082 AssertMsgFailed(("%Rrc\n", rc));
5083 rc = VERR_INTERNAL_ERROR_5;
5084 }
5085 }
5086 }
5087 else
5088 rc = VERR_NO_MEMORY;
5089 }
5090 else
5091 rc = VERR_NOT_SUPPORTED;
5092
5093 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5094
5095 LogFlow(("vgaR3PortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
5096 return rc;
5097}
5098
5099
5100/**
5101 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
5102 */
5103static DECLCALLBACK(void) vgaR3PortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
5104{
5105 NOREF(pInterface);
5106
5107 LogFlow(("vgaR3PortFreeScreenshot: pbData=%p\n", pbData));
5108
5109 RTMemFree(pbData);
5110}
5111
5112
5113/**
5114 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
5115 */
5116static DECLCALLBACK(int) vgaR3PortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
5117 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
5118{
5119 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5120 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5121 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5122 PDMDEV_ASSERT_EMT(pDevIns);
5123 LogFlow(("vgaR3PortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
5124
5125 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5126 AssertRCReturn(rc, rc);
5127
5128 /*
5129 * Validate input.
5130 */
5131 if ( pvData
5132 && x < pThisCC->pDrv->cx
5133 && cx <= pThisCC->pDrv->cx
5134 && cx + x <= pThisCC->pDrv->cx
5135 && y < pThisCC->pDrv->cy
5136 && cy <= pThisCC->pDrv->cy
5137 && cy + y <= pThisCC->pDrv->cy)
5138 {
5139 /*
5140 * Determine bytes per pixel in the destination buffer.
5141 */
5142 size_t cbPixelDst = 0;
5143 switch (pThisCC->pDrv->cBits)
5144 {
5145 case 8:
5146 cbPixelDst = 1;
5147 break;
5148 case 15:
5149 case 16:
5150 cbPixelDst = 2;
5151 break;
5152 case 24:
5153 cbPixelDst = 3;
5154 break;
5155 case 32:
5156 cbPixelDst = 4;
5157 break;
5158 default:
5159 rc = VERR_INVALID_PARAMETER;
5160 break;
5161 }
5162 if (RT_SUCCESS(rc))
5163 {
5164 /*
5165 * The blitting loop.
5166 */
5167 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
5168 uint8_t *pbSrc = (uint8_t *)pvData;
5169 size_t cbLineDst = pThisCC->pDrv->cbScanline;
5170 uint8_t *pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5171 uint32_t cyLeft = cy;
5172 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5173 Assert(pfnVgaDrawLine);
5174 while (cyLeft-- > 0)
5175 {
5176 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5177 pbDst += cbLineDst;
5178 pbSrc += cbLineSrc;
5179 }
5180
5181 /*
5182 * Invalidate the area.
5183 */
5184 pThisCC->pDrv->pfnUpdateRect(pThisCC->pDrv, x, y, cx, cy);
5185 }
5186 }
5187 else
5188 rc = VERR_INVALID_PARAMETER;
5189
5190 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5191
5192 LogFlow(("vgaR3PortDisplayBlt: returns %Rrc\n", rc));
5193 return rc;
5194}
5195
5196
5197/**
5198 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
5199 */
5200static DECLCALLBACK(void) vgaR3PortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
5201{
5202 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5203 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5204 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5205 uint32_t v;
5206
5207 uint32_t cbPixelDst;
5208 uint32_t cbLineDst;
5209 uint8_t *pbDst;
5210
5211 uint32_t cbPixelSrc;
5212 uint32_t cbLineSrc;
5213 uint8_t *pbSrc;
5214
5215
5216# ifdef DEBUG_sunlover
5217 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d\n", x, y, cx, cy));
5218# endif /* DEBUG_sunlover */
5219
5220 Assert(pInterface);
5221
5222 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5223 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
5224
5225 /* Check if there is something to do at all. */
5226 if (!pThis->fRenderVRAM)
5227 {
5228 /* The framebuffer uses the guest VRAM directly. */
5229# ifdef DEBUG_sunlover
5230 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do fRender is false.\n"));
5231# endif /* DEBUG_sunlover */
5232 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5233 return;
5234 }
5235
5236 Assert(pThisCC->pDrv);
5237 Assert(pThisCC->pDrv->pbData);
5238
5239 /* Correct negative x and y coordinates. */
5240 if (x < 0)
5241 {
5242 x += cx; /* Compute xRight which is also the new width. */
5243 cx = (x < 0) ? 0 : x;
5244 x = 0;
5245 }
5246
5247 if (y < 0)
5248 {
5249 y += cy; /* Compute yBottom, which is also the new height. */
5250 cy = (y < 0) ? 0 : y;
5251 y = 0;
5252 }
5253
5254 /* Also check if coords are greater than the display resolution. */
5255 if (x + cx > pThisCC->pDrv->cx)
5256 {
5257 // x < 0 is not possible here
5258 cx = pThisCC->pDrv->cx > (uint32_t)x? pThisCC->pDrv->cx - x: 0;
5259 }
5260
5261 if (y + cy > pThisCC->pDrv->cy)
5262 {
5263 // y < 0 is not possible here
5264 cy = pThisCC->pDrv->cy > (uint32_t)y? pThisCC->pDrv->cy - y: 0;
5265 }
5266
5267# ifdef DEBUG_sunlover
5268 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, cx, cy));
5269# endif
5270
5271 /* Check if there is something to do at all. */
5272 if (cx == 0 || cy == 0)
5273 {
5274 /* Empty rectangle. */
5275# ifdef DEBUG_sunlover
5276 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do: %dx%d\n", cx, cy));
5277#endif
5278 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5279 return;
5280 }
5281
5282 /** @todo This method should be made universal and not only for VBVA.
5283 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5284 * changed.
5285 */
5286
5287 /* Choose the rendering function. */
5288 switch(pThisCC->get_bpp(pThis))
5289 {
5290 default:
5291 case 0:
5292 /* A LFB mode is already disabled, but the callback is still called
5293 * by Display because VBVA buffer is being flushed.
5294 * Nothing to do, just return.
5295 */
5296 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5297 return;
5298 case 8:
5299 v = VGA_DRAW_LINE8;
5300 break;
5301 case 15:
5302 v = VGA_DRAW_LINE15;
5303 break;
5304 case 16:
5305 v = VGA_DRAW_LINE16;
5306 break;
5307 case 24:
5308 v = VGA_DRAW_LINE24;
5309 break;
5310 case 32:
5311 v = VGA_DRAW_LINE32;
5312 break;
5313 }
5314
5315 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5316
5317 /* Compute source and destination addresses and pitches. */
5318 cbPixelDst = (pThisCC->pDrv->cBits + 7) / 8;
5319 cbLineDst = pThisCC->pDrv->cbScanline;
5320 pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5321
5322 cbPixelSrc = (pThisCC->get_bpp(pThis) + 7) / 8;
5323 uint32_t offSrc, u32Dummy;
5324 pThisCC->get_offsets(pThis, &cbLineSrc, &offSrc, &u32Dummy);
5325
5326 /* Assume that rendering is performed only on visible part of VRAM.
5327 * This is true because coordinates were verified.
5328 */
5329 pbSrc = pThisCC->pbVRam;
5330 pbSrc += offSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5331
5332 /* Render VRAM to framebuffer. */
5333
5334# ifdef DEBUG_sunlover
5335 LogFlow(("vgaR3PortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDst, cbLineDst, cbPixelDst, pbSrc, cbLineSrc, cbPixelSrc));
5336# endif
5337
5338 while (cy-- > 0)
5339 {
5340 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5341 pbDst += cbLineDst;
5342 pbSrc += cbLineSrc;
5343 }
5344
5345 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5346# ifdef DEBUG_sunlover
5347 LogFlow(("vgaR3PortUpdateDisplayRect: completed.\n"));
5348# endif
5349}
5350
5351
5352/**
5353 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
5354 */
5355static DECLCALLBACK(int)
5356vgaR3PortCopyRect(PPDMIDISPLAYPORT pInterface,
5357 uint32_t cx, uint32_t cy,
5358 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
5359 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
5360 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
5361 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
5362{
5363 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5364 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5365 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5366 uint32_t v;
5367
5368# ifdef DEBUG_sunlover
5369 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, cx, cy, xDst, yDst));
5370# endif
5371
5372 Assert(pInterface);
5373 Assert(pThisCC->pDrv);
5374
5375 int32_t xSrcCorrected = xSrc;
5376 int32_t ySrcCorrected = ySrc;
5377 uint32_t cxCorrected = cx;
5378 uint32_t cyCorrected = cy;
5379
5380 /* Correct source coordinates to be within the source bitmap. */
5381 if (xSrcCorrected < 0)
5382 {
5383 xSrcCorrected += cxCorrected; /* Compute xRight which is also the new width. */
5384 cxCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5385 xSrcCorrected = 0;
5386 }
5387
5388 if (ySrcCorrected < 0)
5389 {
5390 ySrcCorrected += cyCorrected; /* Compute yBottom, which is also the new height. */
5391 cyCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5392 ySrcCorrected = 0;
5393 }
5394
5395 /* Also check if coords are greater than the display resolution. */
5396 if (xSrcCorrected + cxCorrected > cxSrc)
5397 {
5398 /* xSrcCorrected < 0 is not possible here */
5399 cxCorrected = cxSrc > (uint32_t)xSrcCorrected ? cxSrc - xSrcCorrected : 0;
5400 }
5401
5402 if (ySrcCorrected + cyCorrected > cySrc)
5403 {
5404 /* y < 0 is not possible here */
5405 cyCorrected = cySrc > (uint32_t)ySrcCorrected ? cySrc - ySrcCorrected : 0;
5406 }
5407
5408# ifdef DEBUG_sunlover
5409 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, cxCorrected, cyCorrected));
5410# endif
5411
5412 /* Check if there is something to do at all. */
5413 if (cxCorrected == 0 || cyCorrected == 0)
5414 {
5415 /* Empty rectangle. */
5416# ifdef DEBUG_sunlover
5417 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", cxCorrected, cyCorrected));
5418# endif
5419 return VINF_SUCCESS;
5420 }
5421
5422 /* Check that the corrected source rectangle is within the destination.
5423 * Note: source rectangle is adjusted, but the target must be large enough.
5424 */
5425 if ( xDst < 0
5426 || yDst < 0
5427 || xDst + cxCorrected > cxDst
5428 || yDst + cyCorrected > cyDst)
5429 {
5430 return VERR_INVALID_PARAMETER;
5431 }
5432
5433 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5434 AssertRCReturn(rc, rc);
5435
5436 /* This method only works if the VGA device is in a VBE mode or not paused VBVA mode.
5437 * VGA modes are reported to the caller by returning VERR_INVALID_STATE.
5438 *
5439 * If VBE_DISPI_ENABLED is set, then it is a VBE or VBE compatible VBVA mode. Both of them can be handled.
5440 *
5441 * If VBE_DISPI_ENABLED is clear, then it is either a VGA mode or a VBVA mode set by guest additions
5442 * which have VBVACAPS_USE_VBVA_ONLY capability.
5443 * When VBE_DISPI_ENABLED is being cleared and VBVACAPS_USE_VBVA_ONLY is not set (i.e. guest wants a VGA mode),
5444 * then VBVAOnVBEChanged makes sure that VBVA is paused.
5445 * That is a not paused VBVA means that the video mode can be handled even if VBE_DISPI_ENABLED is clear.
5446 */
5447 if ( (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0
5448 && VBVAIsPaused(pThisCC)
5449# ifdef VBOX_WITH_VMSVGA
5450 && !pThis->svga.fEnabled
5451# endif
5452 )
5453 {
5454 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5455 return VERR_INVALID_STATE;
5456 }
5457
5458 /* Choose the rendering function. */
5459 switch (cSrcBitsPerPixel)
5460 {
5461 default:
5462 case 0:
5463 /* Nothing to do, just return. */
5464 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5465 return VINF_SUCCESS;
5466 case 8:
5467 v = VGA_DRAW_LINE8;
5468 break;
5469 case 15:
5470 v = VGA_DRAW_LINE15;
5471 break;
5472 case 16:
5473 v = VGA_DRAW_LINE16;
5474 break;
5475 case 24:
5476 v = VGA_DRAW_LINE24;
5477 break;
5478 case 32:
5479 v = VGA_DRAW_LINE32;
5480 break;
5481 }
5482
5483 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(cDstBitsPerPixel)];
5484
5485 /* Compute source and destination addresses and pitches. */
5486 uint32_t cbPixelDst = (cDstBitsPerPixel + 7) / 8;
5487 uint32_t cbLineDst = cbDstLine;
5488 uint8_t *pbDstCur = pbDst + yDst * cbLineDst + xDst * cbPixelDst;
5489
5490 uint32_t cbPixelSrc = (cSrcBitsPerPixel + 7) / 8;
5491 uint32_t cbLineSrc = cbSrcLine;
5492 const uint8_t *pbSrcCur = pbSrc + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5493
5494# ifdef DEBUG_sunlover
5495 LogFlow(("vgaR3PortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDstCur, cbLineDst, cbPixelDst, pbSrcCur, cbLineSrc, cbPixelSrc));
5496# endif
5497
5498 while (cyCorrected-- > 0)
5499 {
5500 pfnVgaDrawLine(pThis, pThisCC, pbDstCur, pbSrcCur, cxCorrected);
5501 pbDstCur += cbLineDst;
5502 pbSrcCur += cbLineSrc;
5503 }
5504
5505 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5506# ifdef DEBUG_sunlover
5507 LogFlow(("vgaR3PortCopyRect: completed.\n"));
5508# endif
5509 return VINF_SUCCESS;
5510}
5511
5512
5513/**
5514 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
5515 */
5516static DECLCALLBACK(void) vgaR3PortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5517{
5518 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5519 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5520 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5521
5522 LogFlow(("vgaR3PortSetRenderVRAM: fRender = %d\n", fRender));
5523
5524 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5525 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
5526
5527 pThis->fRenderVRAM = fRender;
5528
5529 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5530}
5531
5532
5533/**
5534 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
5535 */
5536static DECLCALLBACK(void) vgaR3PortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
5537 bool fSupportsMoveCursor)
5538{
5539 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
5540}
5541
5542
5543/**
5544 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
5545 */
5546static DECLCALLBACK(void) vgaR3PortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
5547{
5548 RT_NOREF(pInterface, x, y, fOutOfRange);
5549}
5550
5551
5552/**
5553 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
5554 */
5555static DECLCALLBACK(void) vgaR3TimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
5556{
5557 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5558 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5559 RT_NOREF(pvUser);
5560
5561 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5562 VBVARaiseIrq(pDevIns, pThis, pThisCC, HGSMIHOSTFLAGS_VSYNC);
5563
5564 if (pThisCC->pDrv)
5565 pThisCC->pDrv->pfnRefresh(pThisCC->pDrv);
5566
5567 if (pThis->cMilliesRefreshInterval)
5568 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
5569
5570# ifdef VBOX_WITH_VMSVGA
5571 /*
5572 * Call the VMSVGA FIFO poller/watchdog so we can wake up the thread if
5573 * there is work to be done.
5574 */
5575 if (pThis->svga.fFIFOThreadSleeping && pThis->svga.fEnabled && pThis->svga.fConfigured)
5576 vmsvgaR3FifoWatchdogTimer(pDevIns, pThis, pThisCC);
5577# endif
5578}
5579
5580# ifdef VBOX_WITH_VMSVGA
5581
5582/**
5583 * Helper for VMSVGA.
5584 */
5585int vgaR3RegisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis, uint64_t cbFrameBuffer)
5586{
5587 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5588 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5589 RT_NOREF(cbFrameBuffer);
5590 AssertRC(rc);
5591 return rc;
5592}
5593
5594
5595/**
5596 * Helper for VMSVGA.
5597 */
5598int vgaR3UnregisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis)
5599{
5600 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5601 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, false /*fEnabled*/);
5602 AssertRC(rc);
5603 return rc;
5604}
5605
5606# endif /* VBOX_WITH_VMSVGA */
5607
5608
5609/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5610
5611/**
5612 * @callback_method_impl{FNPCIIOREGIONMAP, Mapping/unmapping the VRAM MMI2 region}
5613 */
5614static DECLCALLBACK(int) vgaR3PciIORegionVRamMapUnmap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5615 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
5616{
5617 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5618 Log(("vgaR3PciIORegionVRamMapUnmap: iRegion=%d GCPhysAddress=%RGp cb=%RGp enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5619 RT_NOREF(pPciDev, cb);
5620
5621# ifdef VBOX_WITH_VMSVGA
5622 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5623 && ( enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH
5624 || (enmType == PCI_ADDRESS_SPACE_MEM && pThis->fVMSVGAEnabled && pThis->fStateLoaded)), VERR_INTERNAL_ERROR);
5625# else
5626 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5627 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5628# endif
5629
5630 Assert(pPciDev == pDevIns->apPciDevs[0]);
5631
5632 /* Note! We cannot take the device lock here as that would create a lock order
5633 problem as the caller has taken the PDM lock prior to calling us. If
5634 we did, we will get trouble later when raising interrupts while owning
5635 the device lock (e.g. vmsvgaR3FifoLoop). */
5636
5637 int rc;
5638 if (GCPhysAddress != NIL_RTGCPHYS)
5639 {
5640 /*
5641 * Make sure the dirty page tracking state is up to date before mapping it.
5642 */
5643# ifdef VBOX_WITH_VMSVGA
5644 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, !pThis->svga.fEnabled || pThis->svga.fVRAMTracking);
5645# else
5646 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5647# endif
5648 AssertLogRelRC(rc);
5649
5650 /*
5651 * Map the VRAM.
5652 */
5653 rc = PDMDevHlpMmio2Map(pDevIns, pThis->hMmio2VRam, GCPhysAddress);
5654 AssertLogRelRC(rc);
5655 if (RT_SUCCESS(rc))
5656 {
5657 pThis->GCPhysVRAM = GCPhysAddress;
5658 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5659
5660 rc = VINF_PCI_MAPPING_DONE; /* caller doesn't care about any other status, so no problem overwriting error here */
5661 }
5662 }
5663 else
5664 {
5665 /*
5666 * Unmapping of the VRAM in progress (caller will do that).
5667 */
5668 Assert(pThis->GCPhysVRAM);
5669 pThis->GCPhysVRAM = 0;
5670 rc = VINF_SUCCESS;
5671 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5672 }
5673 return rc;
5674}
5675
5676
5677# ifdef VBOX_WITH_VMSVGA /* Currently not needed in the non-VMSVGA mode, but keeping it flexible for later. */
5678/**
5679 * @interface_method_impl{PDMPCIDEV,pfnRegionLoadChangeHookR3}
5680 */
5681static DECLCALLBACK(int) vgaR3PciRegionLoadChangeHook(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5682 uint64_t cbRegion, PCIADDRESSSPACE enmType,
5683 PFNPCIIOREGIONOLDSETTER pfnOldSetter, PFNPCIIOREGIONSWAP pfnSwapRegions)
5684{
5685 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5686
5687# ifdef VBOX_WITH_VMSVGA
5688 if (pThis->fVMSVGAEnabled)
5689 {
5690 /*
5691 * We messed up BAR order for the hybrid devices in 6.0 (see #9359).
5692 * It should have been compatible with the VBox VGA device and had the
5693 * VRAM region first and I/O second, but instead the I/O region ended
5694 * up first and VRAM second like the VMSVGA device.
5695 *
5696 * So, we have to detect that here and reconfigure the memory regions.
5697 * Region numbers are used in our (and the PCI bus') interfaction with
5698 * PGM, so PGM needs to be informed too.
5699 */
5700 if ( iRegion == 0
5701 && iRegion == pThis->pciRegions.iVRAM
5702 && (enmType & PCI_ADDRESS_SPACE_IO))
5703 {
5704 LogRel(("VGA: Detected old BAR config, making adjustments.\n"));
5705
5706 /* Update the entries. */
5707 pThis->pciRegions.iIO = 0;
5708 pThis->pciRegions.iVRAM = 1;
5709
5710 /* Update PGM on the region number change so it won't barf when restoring state. */
5711 AssertLogRelReturn(pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo, VERR_VERSION_MISMATCH);
5712 int rc = pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo(pDevIns, pThis->hMmio2VRam, 1);
5713 AssertLogRelRCReturn(rc, rc);
5714 /** @todo Update the I/O port too, only currently we don't give a hoot about
5715 * the region number in the I/O port registrations so it can wait...
5716 * (Only visible in the 'info ioport' output IIRC). */
5717
5718 /* Update the calling PCI device. */
5719 AssertLogRelReturn(pfnSwapRegions, VERR_INTERNAL_ERROR_2);
5720 rc = pfnSwapRegions(pPciDev, 0, 1);
5721 AssertLogRelRCReturn(rc, rc);
5722
5723 return rc;
5724 }
5725
5726 /*
5727 * The VMSVGA changed the default FIFO size from 128KB to 2MB after 5.1.
5728 */
5729 if (iRegion == pThis->pciRegions.iFIFO)
5730 {
5731 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5732 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5733 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5734
5735 /* If the size didn't change we're fine, so just return already. */
5736 if (cbRegion == pThis->svga.cbFIFO)
5737 return VINF_SUCCESS;
5738
5739 /* If the size is larger than the current configuration, refuse to load. */
5740 AssertLogRelMsgReturn(cbRegion <= pThis->svga.cbFIFOConfig,
5741 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x\n",
5742 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO),
5743 VERR_SSM_LOAD_CONFIG_MISMATCH);
5744
5745 /* Adjust the size down. */
5746 int rc = PDMDevHlpMmio2Reduce(pDevIns, pThis->hMmio2VmSvgaFifo, cbRegion);
5747 AssertLogRelMsgRCReturn(rc,
5748 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x: %Rrc\n",
5749 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO, rc),
5750 rc);
5751 pThis->svga.cbFIFO = cbRegion;
5752 return rc;
5753
5754 }
5755
5756 /*
5757 * VRAM used to be non-prefetchable till 6.1.0, so we end up here when restoring
5758 * states older than that with 6.1.0 and later. We just have to check that
5759 * the size and basic type matches, then return VINF_SUCCESS to ACK it.
5760 */
5761 if (iRegion == pThis->pciRegions.iVRAM)
5762 {
5763 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5764 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5765 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5766 /* The size must be the same. */
5767 AssertLogRelMsgReturn(cbRegion == pThis->vram_size,
5768 ("cbRegion=%#RGp vram_size=%#x\n", cbRegion, pThis->vram_size),
5769 VERR_SSM_LOAD_CONFIG_MISMATCH);
5770 return VINF_SUCCESS;
5771 }
5772
5773 /* Emulate callbacks for 5.1 and older saved states by recursion. */
5774 if (iRegion == UINT32_MAX)
5775 {
5776 int rc = vgaR3PciRegionLoadChangeHook(pDevIns, pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD,
5777 PCI_ADDRESS_SPACE_MEM, NULL, NULL);
5778 if (RT_SUCCESS(rc))
5779 rc = pfnOldSetter(pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM);
5780 return rc;
5781 }
5782 }
5783# endif /* VBOX_WITH_VMSVGA */
5784
5785 return VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE;
5786}
5787# endif /* VBOX_WITH_VMSVGA */
5788
5789
5790/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5791
5792/**
5793 * Saves a important bits of the VGA device config.
5794 *
5795 * @param pHlp The device helpers (for SSM functions).
5796 * @param pThis The shared VGA instance data.
5797 * @param pSSM The saved state handle.
5798 */
5799static void vgaR3SaveConfig(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PSSMHANDLE pSSM)
5800{
5801 pHlp->pfnSSMPutU32(pSSM, pThis->vram_size);
5802 pHlp->pfnSSMPutU32(pSSM, pThis->cMonitors);
5803}
5804
5805
5806/**
5807 * @callback_method_impl{FNSSMDEVLIVEEXEC}
5808 */
5809static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5810{
5811 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5812 Assert(uPass == 0); NOREF(uPass);
5813 vgaR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
5814 return VINF_SSM_DONT_CALL_AGAIN;
5815}
5816
5817
5818/**
5819 * @callback_method_impl{FNSSMDEVSAVEPREP}
5820 */
5821static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5822{
5823 RT_NOREF(pDevIns, pSSM);
5824 return VINF_SUCCESS;
5825}
5826
5827
5828/**
5829 * @callback_method_impl{FNSSMDEVSAVEDONE}
5830 */
5831static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5832{
5833 RT_NOREF(pDevIns, pSSM);
5834 return VINF_SUCCESS;
5835}
5836
5837
5838/**
5839 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5840 */
5841static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5842{
5843 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5844 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5845 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5846
5847# ifdef VBOX_WITH_VDMA
5848 vboxVDMASaveStateExecPrep(pThisCC->pVdma);
5849# endif
5850
5851 vgaR3SaveConfig(pHlp, pThis, pSSM);
5852 vga_save(pHlp, pSSM, PDMDEVINS_2_DATA(pDevIns, PVGASTATE));
5853
5854 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5855# ifdef VBOX_WITH_HGSMI
5856 pHlp->pfnSSMPutBool(pSSM, true);
5857 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5858# else
5859 int rc = pHlp->pfnSSMPutBool(pSSM, false);
5860# endif
5861
5862 AssertRCReturn(rc, rc);
5863
5864 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5865# ifdef VBOX_WITH_VDMA
5866 rc = pHlp->pfnSSMPutU32(pSSM, 1);
5867 AssertRCReturn(rc, rc);
5868 rc = vboxVDMASaveStateExecPerform(pHlp, pThisCC->pVdma, pSSM);
5869# else
5870 rc = pHlp->pfnSSMPutU32(pSSM, 0);
5871# endif
5872 AssertRCReturn(rc, rc);
5873
5874# ifdef VBOX_WITH_VDMA
5875 vboxVDMASaveStateExecDone(pThisCC->pVdma);
5876# endif
5877
5878 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5879# ifdef VBOX_WITH_VMSVGA
5880 if (pThis->fVMSVGAEnabled)
5881 {
5882 rc = vmsvgaR3SaveExec(pDevIns, pSSM);
5883 AssertRCReturn(rc, rc);
5884 }
5885# endif
5886 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5887
5888 return rc;
5889}
5890
5891
5892/**
5893 * @callback_method_impl{FNSSMDEVLOADPREP}
5894 */
5895static DECLCALLBACK(int) vgaR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5896{
5897 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5898 RT_NOREF(pSSM);
5899 pThis->fStateLoaded = true;
5900 return VINF_SUCCESS;
5901}
5902
5903
5904/**
5905 * @callback_method_impl{FNSSMDEVLOADEXEC}
5906 */
5907static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5908{
5909 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5910 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5911 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5912 int rc;
5913
5914 pThis->fStateLoaded = true;
5915
5916 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5917 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5918
5919 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5920 {
5921 /* Check the config */
5922 uint32_t cbVRam;
5923 rc = pHlp->pfnSSMGetU32(pSSM, &cbVRam);
5924 AssertRCReturn(rc, rc);
5925 if (pThis->vram_size != cbVRam)
5926 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5927
5928 uint32_t cMonitors;
5929 rc = pHlp->pfnSSMGetU32(pSSM, &cMonitors);
5930 AssertRCReturn(rc, rc);
5931 if (pThis->cMonitors != cMonitors)
5932 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5933 }
5934
5935 if (uPass == SSM_PASS_FINAL)
5936 {
5937 rc = vga_load(pHlp, pSSM, pThis, uVersion);
5938 if (RT_FAILURE(rc))
5939 return rc;
5940
5941 /*
5942 * Restore the HGSMI state, if present.
5943 */
5944 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5945 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5946 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5947 {
5948 rc = pHlp->pfnSSMGetBool(pSSM, &fWithHgsmi);
5949 AssertRCReturn(rc, rc);
5950 }
5951 if (fWithHgsmi)
5952 {
5953# ifdef VBOX_WITH_HGSMI
5954 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5955 AssertRCReturn(rc, rc);
5956# else
5957 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5958# endif
5959 }
5960
5961 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5962 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5963 {
5964 uint32_t u32;
5965 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
5966 AssertRCReturn(rc, rc);
5967
5968 if (u32)
5969 {
5970# ifdef VBOX_WITH_VDMA
5971 if (u32 == 1)
5972 {
5973 rc = vboxVDMASaveLoadExecPerform(pHlp, pThisCC->pVdma, pSSM, uVersion);
5974 AssertRCReturn(rc, rc);
5975 }
5976 else
5977# endif
5978 {
5979 LogRel(("invalid CmdVbva version info\n"));
5980 return VERR_VERSION_MISMATCH;
5981 }
5982 }
5983 }
5984
5985 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
5986# ifdef VBOX_WITH_VMSVGA
5987 if (pThis->fVMSVGAEnabled)
5988 {
5989 rc = vmsvgaR3LoadExec(pDevIns, pSSM, uVersion, uPass);
5990 AssertRCReturn(rc, rc);
5991 }
5992# endif
5993 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
5994 }
5995 return VINF_SUCCESS;
5996}
5997
5998
5999/**
6000 * @@callback_method_impl{FNSSMDEVLOADDONE}
6001 */
6002static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
6003{
6004 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6005 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6006 int rc;
6007 RT_NOREF(pThisCC, pThis, pSSM);
6008
6009# ifdef VBOX_WITH_HGSMI
6010 rc = vboxVBVALoadStateDone(pDevIns);
6011 AssertRCReturn(rc, rc);
6012# ifdef VBOX_WITH_VDMA
6013 rc = vboxVDMASaveLoadDone(pThisCC->pVdma);
6014 AssertRCReturn(rc, rc);
6015# endif
6016 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
6017 VBVAOnVBEChanged(pThis, pThisCC);
6018# endif
6019# ifdef VBOX_WITH_VMSVGA
6020 if (pThis->fVMSVGAEnabled)
6021 {
6022 rc = vmsvgaR3LoadDone(pDevIns);
6023 AssertRCReturn(rc, rc);
6024 }
6025# endif
6026 return VINF_SUCCESS;
6027}
6028
6029
6030/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
6031
6032/**
6033 * @interface_method_impl{PDMDEVREG,pfnResume}
6034 */
6035static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
6036{
6037 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6038 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6039 VBVAOnResume(pDevIns, pThis, pThisCC);
6040}
6041
6042
6043/**
6044 * @interface_method_impl{PDMDEVREG,pfnReset}
6045 */
6046static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
6047{
6048 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6049 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6050 char *pchStart;
6051 char *pchEnd;
6052 LogFlow(("vgaReset\n"));
6053
6054 if (pThisCC->pVdma)
6055 vboxVDMAReset(pThisCC->pVdma);
6056
6057# ifdef VBOX_WITH_VMSVGA
6058 if (pThis->fVMSVGAEnabled)
6059 vmsvgaR3Reset(pDevIns);
6060# endif
6061
6062# ifdef VBOX_WITH_HGSMI
6063 VBVAReset(pDevIns, pThis, pThisCC);
6064# endif
6065
6066
6067 /* Clear the VRAM ourselves. */
6068 if (pThisCC->pbVRam && pThis->vram_size)
6069 memset(pThisCC->pbVRam, 0, pThis->vram_size);
6070
6071 /*
6072 * Zero most of it.
6073 *
6074 * Unlike vga_reset we're leaving out a few members which we believe
6075 * must remain unchanged....
6076 */
6077 /* 1st part. */
6078 pchStart = (char *)&pThis->latch;
6079 pchEnd = (char *)&pThis->invalidated_y_table;
6080 memset(pchStart, 0, pchEnd - pchStart);
6081
6082 /* 2nd part. */
6083 pchStart = (char *)&pThis->last_palette;
6084 pchEnd = (char *)&pThis->u32Marker;
6085 memset(pchStart, 0, pchEnd - pchStart);
6086
6087
6088 /*
6089 * Restore and re-init some bits.
6090 */
6091 pThisCC->get_bpp = vgaR3GetBpp;
6092 pThisCC->get_offsets = vgaR3GetOffsets;
6093 pThisCC->get_resolution = vgaR3GetResolution;
6094 pThis->graphic_mode = -1; /* Force full update. */
6095# ifdef CONFIG_BOCHS_VBE
6096 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
6097 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
6098 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
6099 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
6100# endif /* CONFIG_BOCHS_VBE */
6101 pThis->st00 = 0x70; /* Static except for bit 4. */
6102
6103 /*
6104 * Reset the LFB mapping.
6105 */
6106 if ( ( pDevIns->fRCEnabled
6107 || pDevIns->fR0Enabled)
6108 && pThis->GCPhysVRAM != 0
6109 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
6110 {
6111 /** @todo r=bird: This used to be a PDMDevHlpPGMHandlerPhysicalReset call.
6112 * Not quite sure if it was/is needed. Besides, where do we reset the
6113 * dirty bitmap (bmDirtyBitmap)? */
6114 int rc = PDMDevHlpMmio2ResetDirtyBitmap(pDevIns, pThis->hMmio2VRam);
6115 AssertRC(rc);
6116 }
6117 if (pThis->bmPageRemappedVGA != 0)
6118 {
6119 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
6120 STAM_COUNTER_INC(&pThis->StatMapReset);
6121 vgaResetRemapped(pThis);
6122 }
6123
6124 /*
6125 * Reset the logo data.
6126 */
6127 pThisCC->LogoCommand = LOGO_CMD_NOP;
6128 pThisCC->offLogoData = 0;
6129
6130 /* notify port handler */
6131 if (pThisCC->pDrv)
6132 {
6133 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* hack around lock order issue. */
6134
6135 pThisCC->pDrv->pfnReset(pThisCC->pDrv);
6136 pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, false, false, 0, 0, 0, 0, NULL);
6137
6138 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
6139 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
6140 }
6141
6142 /* Reset latched access mask. */
6143 pThis->uMaskLatchAccess = 0x3ff;
6144 pThis->cLatchAccesses = 0;
6145 pThis->u64LastLatchedAccess = 0;
6146 pThis->iMask = 0;
6147
6148 /* Reset retrace emulation. */
6149 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
6150}
6151
6152
6153/**
6154 * @interface_method_impl{PDMDEVREG,pfnPowerOn}
6155 */
6156static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
6157{
6158 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6159 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6160# ifdef VBOX_WITH_VMSVGA
6161 if (pThis->fVMSVGAEnabled)
6162 vmsvgaR3PowerOn(pDevIns);
6163# endif
6164 VBVAOnResume(pDevIns, pThis, pThisCC);
6165}
6166
6167
6168/**
6169 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
6170 */
6171static DECLCALLBACK(void) vgaR3PowerOff(PPDMDEVINS pDevIns)
6172{
6173 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6174 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6175 RT_NOREF(pThis, pThisCC);
6176# ifdef VBOX_WITH_VMSVGA
6177 if (pThis->fVMSVGAEnabled)
6178 vmsvgaR3PowerOff(pDevIns);
6179# endif
6180}
6181
6182
6183/**
6184 * @interface_method_impl{PDMDEVREG,pfnRelocate}
6185 */
6186static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
6187{
6188# ifdef VBOX_WITH_RAW_MODE_KEEP
6189 if (offDelta)
6190 {
6191 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6192 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
6193
6194 pThisRC->pbVRam += offDelta;
6195 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6196 }
6197# else
6198 RT_NOREF(pDevIns, offDelta);
6199# endif
6200}
6201
6202
6203/**
6204 * @interface_method_impl{PDMDEVREG,pfnAttach}
6205 *
6206 * This is like plugging in the monitor after turning on the PC.
6207 */
6208static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6209{
6210 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6211 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6212
6213 RT_NOREF(pThis);
6214
6215 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
6216 ("VGA device does not support hotplugging\n"),
6217 VERR_INVALID_PARAMETER);
6218
6219 switch (iLUN)
6220 {
6221 /* LUN #0: Display port. */
6222 case 0:
6223 {
6224 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "Display Port");
6225 if (RT_SUCCESS(rc))
6226 {
6227 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIDISPLAYCONNECTOR);
6228 if (pThisCC->pDrv)
6229 {
6230 /* pThisCC->pDrv->pbData can be NULL when there is no framebuffer. */
6231 if ( pThisCC->pDrv->pfnRefresh
6232 && pThisCC->pDrv->pfnResize
6233 && pThisCC->pDrv->pfnUpdateRect)
6234 rc = VINF_SUCCESS;
6235 else
6236 {
6237 Assert(pThisCC->pDrv->pfnRefresh);
6238 Assert(pThisCC->pDrv->pfnResize);
6239 Assert(pThisCC->pDrv->pfnUpdateRect);
6240 pThisCC->pDrv = NULL;
6241 pThisCC->pDrvBase = NULL;
6242 rc = VERR_INTERNAL_ERROR;
6243 }
6244 }
6245 else
6246 {
6247 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
6248 pThisCC->pDrvBase = NULL;
6249 rc = VERR_PDM_MISSING_INTERFACE;
6250 }
6251 }
6252 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6253 {
6254 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6255 rc = VINF_SUCCESS;
6256 }
6257 else
6258 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
6259 return rc;
6260 }
6261
6262 default:
6263 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6264 return VERR_PDM_NO_SUCH_LUN;
6265 }
6266}
6267
6268
6269/**
6270 * @interface_method_impl{PDMDEVREG,pfnDetach}
6271 *
6272 * This is like unplugging the monitor while the PC is still running.
6273 */
6274static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6275{
6276 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6277 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
6278 RT_NOREF(fFlags);
6279
6280 /*
6281 * Reset the interfaces and update the controller state.
6282 */
6283 switch (iLUN)
6284 {
6285 /* LUN #0: Display port. */
6286 case 0:
6287 pThisCC->pDrv = NULL;
6288 pThisCC->pDrvBase = NULL;
6289 break;
6290
6291 default:
6292 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6293 break;
6294 }
6295}
6296
6297
6298/**
6299 * @interface_method_impl{PDMDEVREG,pfnDestruct}
6300 */
6301static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6302{
6303 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
6304 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6305 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6306 LogFlow(("vgaR3Destruct:\n"));
6307
6308# ifdef VBOX_WITH_VDMA
6309 if (pThisCC->pVdma)
6310 vboxVDMADestruct(pThisCC->pVdma);
6311# endif
6312
6313# ifdef VBOX_WITH_VMSVGA
6314 if (pThis->fVMSVGAEnabled)
6315 vmsvgaR3Destruct(pDevIns);
6316# endif
6317
6318# ifdef VBOX_WITH_HGSMI
6319 VBVADestroy(pThisCC);
6320# endif
6321
6322 /*
6323 * Free MM heap pointers.
6324 */
6325 if (pThisCC->pbVBEExtraData)
6326 {
6327 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVBEExtraData);
6328 pThisCC->pbVBEExtraData = NULL;
6329 }
6330 if (pThisCC->pbVgaBios)
6331 {
6332 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6333 pThisCC->pbVgaBios = NULL;
6334 }
6335
6336 if (pThisCC->pszVgaBiosFile)
6337 {
6338 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6339 pThisCC->pszVgaBiosFile = NULL;
6340 }
6341
6342 if (pThisCC->pszLogoFile)
6343 {
6344 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
6345 pThisCC->pszLogoFile = NULL;
6346 }
6347
6348 if (pThisCC->pbLogo)
6349 {
6350 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbLogo);
6351 pThisCC->pbLogo = NULL;
6352 }
6353
6354# if defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
6355 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSectIRQ);
6356# endif
6357 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
6358 return VINF_SUCCESS;
6359}
6360
6361
6362/**
6363 * Adjust VBE mode information
6364 *
6365 * Depending on the configured VRAM size, certain parts of VBE mode
6366 * information must be updated.
6367 *
6368 * @param pThis The device instance data.
6369 * @param pMode The mode information structure.
6370 */
6371static void vgaR3AdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6372{
6373 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6374 unsigned bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6375 /* The "number of image pages" is really the max page index... */
6376 unsigned maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6377 if (maxPage > 255)
6378 maxPage = 255; /* 8-bit value. */
6379 pMode->info.NumberOfImagePages = maxPage;
6380 pMode->info.LinNumberOfPages = maxPage;
6381}
6382
6383
6384/**
6385 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6386 */
6387static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6388{
6389 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6390 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6391 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6392 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
6393 int rc;
6394 unsigned i;
6395 uint32_t cCustomModes;
6396 uint32_t cyReduction;
6397 uint32_t cbPitch;
6398 PVBEHEADER pVBEDataHdr;
6399 ModeInfoListItem *pCurMode;
6400 unsigned cb;
6401
6402 Assert(iInstance == 0);
6403
6404 /*
6405 * Init static data.
6406 */
6407 static bool s_fExpandDone = false;
6408 if (!s_fExpandDone)
6409 {
6410 s_fExpandDone = true;
6411 vgaR3InitExpand();
6412 }
6413
6414 /*
6415 * Validate configuration.
6416 */
6417 static const char s_szMscWorkaround[] = "VRamSize"
6418 "|MonitorCount"
6419 "|FadeIn"
6420 "|FadeOut"
6421 "|LogoTime"
6422 "|LogoFile"
6423 "|ShowBootMenu"
6424 "|BiosRom"
6425 "|RealRetrace"
6426 "|CustomVideoModes"
6427 "|HeightReduction"
6428 "|CustomVideoMode1"
6429 "|CustomVideoMode2"
6430 "|CustomVideoMode3"
6431 "|CustomVideoMode4"
6432 "|CustomVideoMode5"
6433 "|CustomVideoMode6"
6434 "|CustomVideoMode7"
6435 "|CustomVideoMode8"
6436 "|CustomVideoMode9"
6437 "|CustomVideoMode10"
6438 "|CustomVideoMode11"
6439 "|CustomVideoMode12"
6440 "|CustomVideoMode13"
6441 "|CustomVideoMode14"
6442 "|CustomVideoMode15"
6443 "|CustomVideoMode16"
6444 "|MaxBiosXRes"
6445 "|MaxBiosYRes"
6446# ifdef VBOX_WITH_VMSVGA
6447 "|VMSVGAEnabled"
6448 "|VMSVGA10"
6449 "|VMSVGAPciId"
6450 "|VMSVGAPciBarLayout"
6451 "|VMSVGAFifoSize"
6452 "|VmSvga3"
6453 "|VmSvgaExposeLegacyVga"
6454# endif
6455# ifdef VBOX_WITH_VMSVGA3D
6456 "|VMSVGA3dEnabled"
6457 "|VMSVGA3dOverlayEnabled"
6458 "|VMSVGA3dMSAA"
6459 "|VMSVGA2dGBO"
6460# endif
6461 "|SuppressNewYearSplash"
6462 "|3DEnabled";
6463
6464 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, s_szMscWorkaround, "");
6465
6466 /*
6467 * Init state data.
6468 */
6469 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6470 AssertLogRelRCReturn(rc, rc);
6471 if (pThis->vram_size > VGA_VRAM_MAX)
6472 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6473 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6474 if (pThis->vram_size < VGA_VRAM_MIN)
6475 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6476 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6477 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6478 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6479 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6480
6481 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6482 AssertLogRelRCReturn(rc, rc);
6483
6484 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
6485
6486 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "3DEnabled", &pThis->f3DEnabled, false);
6487 AssertLogRelRCReturn(rc, rc);
6488 Log(("VGA: f3DEnabled=%RTbool\n", pThis->f3DEnabled));
6489
6490# ifdef VBOX_WITH_VMSVGA
6491 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6492 AssertLogRelRCReturn(rc, rc);
6493 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6494
6495 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA10", &pThis->fVMSVGA10, true);
6496 AssertLogRelRCReturn(rc, rc);
6497 Log(("VMSVGA: VMSVGA10 = %d\n", pThis->fVMSVGA10));
6498
6499 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciId", &pThis->fVMSVGAPciId, false);
6500 AssertLogRelRCReturn(rc, rc);
6501 Log(("VMSVGA: VMSVGAPciId = %d\n", pThis->fVMSVGAPciId));
6502
6503 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciBarLayout", &pThis->fVMSVGAPciBarLayout, pThis->fVMSVGAPciId);
6504 AssertLogRelRCReturn(rc, rc);
6505 Log(("VMSVGA: VMSVGAPciBarLayout = %d\n", pThis->fVMSVGAPciBarLayout));
6506
6507 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VmSvga3", &pThis->fVmSvga3, false);
6508 AssertLogRelRCReturn(rc, rc);
6509 Log(("VMSVGA: VmSvga3 = %RTbool\n", pThis->fVmSvga3));
6510
6511 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VmSvgaExposeLegacyVga", &pThis->fLegacyVgaEnabled, true);
6512 AssertLogRelRCReturn(rc, rc);
6513 Log(("VMSVGA: VmSvgaExposeLegacyVga = %RTbool\n", pThis->fLegacyVgaEnabled));
6514
6515 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6516 AssertLogRelRCReturn(rc, rc);
6517 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6518 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6519 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6520 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6521 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6522# endif
6523# ifdef VBOX_WITH_VMSVGA3D
6524 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6525 AssertLogRelRCReturn(rc, rc);
6526 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6527
6528 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dOverlayEnabled", &pThis->svga.f3DOverlayEnabled, false);
6529 AssertLogRelRCReturn(rc, rc);
6530 Log(("VMSVGA: VMSVGA3dOverlayEnabled = %d\n", pThis->svga.f3DOverlayEnabled));
6531
6532 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dMSAA", &pThis->svga.fVMSVGA3dMSAA, true);
6533 AssertLogRelRCReturn(rc, rc);
6534 Log(("VMSVGA: VMSVGA3dMSAA = %d\n", pThis->svga.fVMSVGA3dMSAA));
6535
6536 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA2dGBO", &pThis->svga.fVMSVGA2dGBO, true);
6537 AssertLogRelRCReturn(rc, rc);
6538 if (pThis->svga.f3DEnabled || pThis->fVMSVGAPciId == 0) /* The fVMSVGA2dGBO is for 2D mode of vmwgfx.ko only*/
6539 pThis->svga.fVMSVGA2dGBO = false;
6540 Log(("VMSVGA: fVMSVGA2dGBO = %d\n", pThis->svga.fVMSVGA2dGBO));
6541# endif
6542
6543# ifdef VBOX_WITH_VMSVGA
6544 if (pThis->fVMSVGAPciBarLayout)
6545 {
6546 if (pThis->fVmSvga3)
6547 {
6548 pThis->pciRegions.iIO = 0;
6549 pThis->pciRegions.iVRAM = 2;
6550 }
6551 else
6552 {
6553 pThis->pciRegions.iIO = 0;
6554 pThis->pciRegions.iVRAM = 1;
6555 }
6556 }
6557 else
6558 {
6559 pThis->pciRegions.iVRAM = 0;
6560 pThis->pciRegions.iIO = 1;
6561 }
6562 pThis->pciRegions.iFIFO = 2;
6563# else
6564 pThis->pciRegions.iVRAM = 0;
6565# endif
6566
6567 pThisCC->pDevIns = pDevIns;
6568
6569 vgaR3Reset(pDevIns);
6570
6571 /* The PCI devices configuration. */
6572 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
6573 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
6574
6575# ifdef VBOX_WITH_VMSVGA
6576 if (pThis->fVMSVGAEnabled)
6577 {
6578 /* Extend our VGA device with VMWare SVGA functionality. */
6579 if (pThis->fVMSVGAPciId)
6580 {
6581 PDMPciDevSetVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6582 if (pThis->fVmSvga3)
6583 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA3);
6584 else
6585 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6586 }
6587 else
6588 {
6589 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6590 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6591 }
6592 PDMPciDevSetSubSystemVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6593 if (pThis->fVmSvga3)
6594 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA3);
6595 else
6596 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6597 }
6598 else
6599# endif /* VBOX_WITH_VMSVGA */
6600 {
6601 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6602 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6603 }
6604 PDMPciDevSetClassSub(pPciDev, 0x00); /* VGA controller */
6605 PDMPciDevSetClassBase(pPciDev, 0x03);
6606 PDMPciDevSetHeaderType(pPciDev, 0x00);
6607# if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6608 PDMPciDevSetInterruptPin(pPciDev, 1);
6609# endif
6610
6611 /* the interfaces. */
6612 pThisCC->IBase.pfnQueryInterface = vgaR3PortQueryInterface;
6613
6614 pThisCC->IPort.pfnUpdateDisplay = vgaR3PortUpdateDisplay;
6615 pThisCC->IPort.pfnUpdateDisplayAll = vgaR3PortUpdateDisplayAll;
6616 pThisCC->IPort.pfnQueryVideoMode = vgaR3PortQueryVideoMode;
6617 pThisCC->IPort.pfnSetRefreshRate = vgaR3PortSetRefreshRate;
6618 pThisCC->IPort.pfnTakeScreenshot = vgaR3PortTakeScreenshot;
6619 pThisCC->IPort.pfnFreeScreenshot = vgaR3PortFreeScreenshot;
6620 pThisCC->IPort.pfnDisplayBlt = vgaR3PortDisplayBlt;
6621 pThisCC->IPort.pfnUpdateDisplayRect = vgaR3PortUpdateDisplayRect;
6622 pThisCC->IPort.pfnCopyRect = vgaR3PortCopyRect;
6623 pThisCC->IPort.pfnSetRenderVRAM = vgaR3PortSetRenderVRAM;
6624 pThisCC->IPort.pfnSetViewport = NULL;
6625 pThisCC->IPort.pfnReportMonitorPositions = NULL;
6626# ifdef VBOX_WITH_VMSVGA
6627 if (pThis->fVMSVGAEnabled)
6628 {
6629 pThisCC->IPort.pfnSetViewport = vmsvgaR3PortSetViewport;
6630 pThisCC->IPort.pfnReportMonitorPositions = vmsvgaR3PortReportMonitorPositions;
6631 }
6632# endif
6633 pThisCC->IPort.pfnSendModeHint = vbvaR3PortSendModeHint;
6634 pThisCC->IPort.pfnReportHostCursorCapabilities = vgaR3PortReportHostCursorCapabilities;
6635 pThisCC->IPort.pfnReportHostCursorPosition = vgaR3PortReportHostCursorPosition;
6636
6637 pThisCC->ILeds.pfnQueryStatusLed = vgaR3PortQueryStatusLed;
6638 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6639
6640 /*
6641 * We use our own critical section to avoid unncessary pointer indirections
6642 * in interface methods (as well as for historical reasons).
6643 */
6644 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6645 AssertRCReturn(rc, rc);
6646 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6647 AssertRCReturn(rc, rc);
6648
6649# ifdef VBOX_WITH_HGSMI
6650 /*
6651 * This critical section is used by vgaR3IOPortHgsmiWrite, VBVARaiseIrq and VBVAOnResume
6652 * for some IRQ related synchronization.
6653 */
6654 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6655 AssertRCReturn(rc, rc);
6656# endif
6657
6658 /*
6659 * PCI device registration.
6660 */
6661 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6662 if (RT_FAILURE(rc))
6663 return rc;
6664 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6665 if (pPciDev->uDevFn != 16 && iInstance == 0)
6666 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pPciDev->uDevFn));
6667
6668# ifdef VBOX_WITH_VMSVGA
6669 pThis->hIoPortVmSvga = NIL_IOMIOPORTHANDLE;
6670 pThis->hMmio2VmSvgaFifo = NIL_PGMMMIO2HANDLE;
6671 pThis->hMmioSvga3 = NIL_IOMMMIOHANDLE;
6672 if (pThis->fVMSVGAEnabled)
6673 {
6674 if (pThis->fVmSvga3)
6675 {
6676 /* Register the MMIO register region. */
6677 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, pThis->pciRegions.iIO, 4096 * 1024, PCI_ADDRESS_SPACE_MEM,
6678 vmsvga3MmioWrite, vmsvga3MmioRead, NULL /*pvUser*/,
6679 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED,
6680 "VMSVGA3-MMIO", &pThis->hMmioSvga3);
6681 AssertRCReturn(rc, rc);
6682 }
6683 else
6684 {
6685 /* Register the io command ports. */
6686 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, pThis->pciRegions.iIO, 0x10, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/,
6687 "VMSVGA", NULL /*paExtDescs*/, &pThis->hIoPortVmSvga);
6688 AssertRCReturn(rc, rc);
6689
6690 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iFIFO, pThis->svga.cbFIFO,
6691 PCI_ADDRESS_SPACE_MEM, 0 /*fFlags*/, vmsvgaR3PciIORegionFifoMapUnmap,
6692 "VMSVGA-FIFO", (void **)&pThisCC->svga.pau32FIFO, &pThis->hMmio2VmSvgaFifo);
6693 AssertRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6694 N_("Failed to create VMSVGA FIFO (%u bytes)"), pThis->svga.cbFIFO));
6695 }
6696
6697 pPciDev->pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6698 }
6699# endif /* VBOX_WITH_VMSVGA */
6700
6701 /*
6702 * Allocate VRAM and create a PCI region for it.
6703 */
6704 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iVRAM, pThis->vram_size,
6705 PCI_ADDRESS_SPACE_MEM_PREFETCH, PGMPHYS_MMIO2_FLAGS_TRACK_DIRTY_PAGES,
6706 vgaR3PciIORegionVRamMapUnmap, "VRam", (void **)&pThisCC->pbVRam, &pThis->hMmio2VRam);
6707 AssertLogRelRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6708 N_("Failed to allocate %u bytes of VRAM"), pThis->vram_size));
6709
6710 if (pThis->fLegacyVgaEnabled)
6711 {
6712 /*
6713 * Register I/O ports.
6714 */
6715# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_phIoPort) do { \
6716 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, a_uPort, a_cPorts, IOM_IOPORT_F_ABS, \
6717 a_pfnWrite, a_pfnRead, "VGA - " a_szDesc, NULL /*paExtDescs*/, a_phIoPort); \
6718 AssertRCReturn(rc, rc); \
6719 } while (0)
6720 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", &pThis->hIoPortAr);
6721 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", &pThis->hIoPortMsrSt00);
6722 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", &pThis->hIoPort3c3);
6723 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", &pThis->hIoPortSr);
6724 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", &pThis->hIoPortDac);
6725 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ &pThis->hIoPortPos);
6726 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", &pThis->hIoPortGr);
6727
6728 /* Note! Ralf Brown lists 0x3b0-0x3b1, 0x3b2-0x3b3 and 0x3b6-0x3b7 as "the same as" 0x3b4-0x3b5. */
6729 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", &pThis->hIoPortMdaCrt);
6730 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", &pThis->hIoPortMdaFcrSt);
6731 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", &pThis->hIoPortCgaCrt);
6732 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", &pThis->hIoPortCgaFcrSt);
6733
6734# ifdef CONFIG_BOCHS_VBE
6735 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", &pThis->hIoPortVbeIndex);
6736 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", &pThis->hIoPortVbeData);
6737# endif /* CONFIG_BOCHS_VBE */
6738
6739# ifdef VBOX_WITH_HGSMI
6740 /* Use reserved VGA IO ports for HGSMI. */
6741 REG_PORT(VGA_PORT_HGSMI_HOST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI host (3b0-3b3)", &pThis->hIoPortHgsmiHost);
6742 REG_PORT(VGA_PORT_HGSMI_GUEST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI guest (3d0-3d3)", &pThis->hIoPortHgsmiGuest);
6743# endif /* VBOX_WITH_HGSMI */
6744
6745# undef REG_PORT
6746
6747 /* vga bios */
6748 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_PRINTF_PORT, 1 /*cPorts*/, vgaIoPortWriteBios, vgaIoPortReadBios,
6749 "VGA BIOS debug/panic", NULL /*paExtDescs*/, &pThis->hIoPortBios);
6750 AssertRCReturn(rc, rc);
6751
6752 /*
6753 * The MDA/CGA/EGA/VGA/whatever fixed MMIO area.
6754 */
6755 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, 0x000a0000, 0x00020000,
6756 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
6757 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
6758 vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/,
6759 "VGA - VGA Video Buffer", &pThis->hMmioLegacy);
6760 AssertRCReturn(rc, rc);
6761
6762 /*
6763 * Get the VGA BIOS ROM file name.
6764 */
6765 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThisCC->pszVgaBiosFile);
6766 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6767 {
6768 pThisCC->pszVgaBiosFile = NULL;
6769 rc = VINF_SUCCESS;
6770 }
6771 else if (RT_FAILURE(rc))
6772 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6773 else if (!*pThisCC->pszVgaBiosFile)
6774 {
6775 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6776 pThisCC->pszVgaBiosFile = NULL;
6777 }
6778
6779 /*
6780 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6781 */
6782 RTFILE FileVgaBios = NIL_RTFILE;
6783 if (pThisCC->pszVgaBiosFile)
6784 {
6785 rc = RTFileOpen(&FileVgaBios, pThisCC->pszVgaBiosFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6786 if (RT_SUCCESS(rc))
6787 {
6788 rc = RTFileQuerySize(FileVgaBios, &pThisCC->cbVgaBios);
6789 if (RT_SUCCESS(rc))
6790 {
6791 if ( RT_ALIGN(pThisCC->cbVgaBios, _4K) != pThisCC->cbVgaBios
6792 || pThisCC->cbVgaBios > _64K
6793 || pThisCC->cbVgaBios < 16 * _1K)
6794 rc = VERR_TOO_MUCH_DATA;
6795 }
6796 }
6797 if (RT_FAILURE(rc))
6798 {
6799 /*
6800 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6801 */
6802 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThisCC->pszVgaBiosFile, rc));
6803 RTFileClose(FileVgaBios);
6804 FileVgaBios = NIL_RTFILE;
6805 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6806 pThisCC->pszVgaBiosFile = NULL;
6807 }
6808 }
6809
6810 /*
6811 * Attempt to get the VGA BIOS ROM data from file.
6812 */
6813 if (pThisCC->pszVgaBiosFile)
6814 {
6815 /*
6816 * Allocate buffer for the VGA BIOS ROM data.
6817 */
6818 pThisCC->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbVgaBios);
6819 if (pThisCC->pbVgaBios)
6820 {
6821 rc = RTFileRead(FileVgaBios, pThisCC->pbVgaBios, pThisCC->cbVgaBios, NULL);
6822 if (RT_FAILURE(rc))
6823 {
6824 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThisCC->cbVgaBios, rc));
6825 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6826 pThisCC->pbVgaBios = NULL;
6827 }
6828 rc = VINF_SUCCESS;
6829 }
6830 /* else: Out of memory condition is ignored, see below. */
6831 }
6832 else
6833 pThisCC->pbVgaBios = NULL;
6834
6835 /* cleanup */
6836 if (FileVgaBios != NIL_RTFILE)
6837 RTFileClose(FileVgaBios);
6838
6839 /* If we were unable to get the data from file for whatever reason, fall
6840 back to the built-in ROM image. */
6841 const uint8_t *pbVgaBiosBinary;
6842 uint64_t cbVgaBiosBinary;
6843 uint32_t fFlags = 0;
6844 if (pThisCC->pbVgaBios == NULL)
6845 {
6846 CPUMMICROARCH enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
6847 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6848 || enmMicroarch == kCpumMicroarch_Intel_80186
6849 || enmMicroarch == kCpumMicroarch_NEC_V20
6850 || enmMicroarch == kCpumMicroarch_NEC_V30)
6851 {
6852 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6853 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6854 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6855 }
6856 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6857 {
6858 pbVgaBiosBinary = g_abVgaBiosBinary286;
6859 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6860 LogRel(("VGA: Using the 286 BIOS image!\n"));
6861 }
6862 else
6863 {
6864 pbVgaBiosBinary = g_abVgaBiosBinary386;
6865 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6866 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6867 }
6868 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6869 }
6870 else
6871 {
6872 pbVgaBiosBinary = pThisCC->pbVgaBios;
6873 cbVgaBiosBinary = pThisCC->cbVgaBios;
6874 }
6875
6876 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6877 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, GUEST_PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6878 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6879 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6880 fFlags, "VGA BIOS");
6881 AssertRCReturn(rc, rc);
6882 }
6883
6884 /*
6885 * Saved state.
6886 */
6887 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6888 NULL, vgaR3LiveExec, NULL,
6889 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6890 vgaR3LoadPrep, vgaR3LoadExec, vgaR3LoadDone);
6891 AssertRCReturn(rc, rc);
6892
6893 /*
6894 * Create the refresh timer.
6895 */
6896 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, vgaR3TimerRefresh, NULL,
6897 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "VGA Refresh", &pThis->hRefreshTimer);
6898 AssertRCReturn(rc, rc);
6899
6900 /*
6901 * Attach to the display.
6902 */
6903 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6904 AssertRCReturn(rc, rc);
6905
6906 /*
6907 * Initialize the retrace flag.
6908 */
6909 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6910 AssertLogRelRCReturn(rc, rc);
6911
6912 uint16_t maxBiosXRes;
6913 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6914 AssertLogRelRCReturn(rc, rc);
6915 uint16_t maxBiosYRes;
6916 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6917 AssertLogRelRCReturn(rc, rc);
6918
6919 if (pThis->fLegacyVgaEnabled)
6920 {
6921 /*
6922 * Compute buffer size for the VBE BIOS Extra Data.
6923 */
6924 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6925
6926 rc = pHlp->pfnCFGMQueryU32(pCfg, "HeightReduction", &cyReduction);
6927 if (RT_SUCCESS(rc) && cyReduction)
6928 cb *= 2; /* Default mode list will be twice long */
6929 else
6930 cyReduction = 0;
6931
6932 rc = pHlp->pfnCFGMQueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6933 if (RT_SUCCESS(rc) && cCustomModes)
6934 cb += sizeof(ModeInfoListItem) * cCustomModes;
6935 else
6936 cCustomModes = 0;
6937
6938 /*
6939 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6940 */
6941 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6942 pThisCC->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6943 pThisCC->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThisCC->cbVBEExtraData);
6944 if (!pThisCC->pbVBEExtraData)
6945 return VERR_NO_MEMORY;
6946
6947 pVBEDataHdr = (PVBEHEADER)pThisCC->pbVBEExtraData;
6948 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6949 pVBEDataHdr->cbData = cb;
6950
6951 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6952 for (i = 0; i < MODE_INFO_SIZE; i++)
6953 {
6954 uint32_t pixelWidth, reqSize;
6955 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6956 pixelWidth = 2;
6957 else
6958 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6959 reqSize = mode_info_list[i].info.XResolution
6960 * mode_info_list[i].info.YResolution
6961 * pixelWidth;
6962 if (reqSize >= pThis->vram_size)
6963 continue;
6964 if (!reqSize)
6965 continue;
6966 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6967 || mode_info_list[i].info.YResolution > maxBiosYRes)
6968 continue;
6969 *pCurMode = mode_info_list[i];
6970 vgaR3AdjustModeInfo(pThis, pCurMode);
6971 pCurMode++;
6972 }
6973
6974 /*
6975 * Copy default modes with subtracted YResolution.
6976 */
6977 if (cyReduction)
6978 {
6979 ModeInfoListItem *pDefMode = mode_info_list;
6980 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6981 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6982 {
6983 uint32_t pixelWidth, reqSize;
6984 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6985 pixelWidth = 2;
6986 else
6987 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6988 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6989 if (reqSize >= pThis->vram_size)
6990 continue;
6991 if ( pDefMode->info.XResolution > maxBiosXRes
6992 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6993 continue;
6994 *pCurMode = *pDefMode;
6995 pCurMode->mode += 0x30;
6996 pCurMode->info.YResolution -= cyReduction;
6997 pCurMode++;
6998 }
6999 }
7000
7001
7002 /*
7003 * Add custom modes.
7004 */
7005 if (cCustomModes)
7006 {
7007 uint16_t u16CurMode = VBE_VBOX_MODE_CUSTOM1;
7008 for (i = 1; i <= cCustomModes; i++)
7009 {
7010 char szExtraDataKey[sizeof("CustomVideoModeXX")];
7011 char *pszExtraData = NULL;
7012
7013 /* query and decode the custom mode string. */
7014 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
7015 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
7016 if (RT_SUCCESS(rc))
7017 {
7018 ModeInfoListItem *pDefMode = mode_info_list;
7019 unsigned int cx, cy, cBits, cParams, j;
7020 uint16_t u16DefMode;
7021
7022 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
7023 if ( cParams != 3
7024 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
7025 {
7026 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
7027 return VERR_VGA_INVALID_CUSTOM_MODE;
7028 }
7029 if (!cx || !cy)
7030 {
7031 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cx=%u, cy=%u\n", pszExtraData, szExtraDataKey, cx, cy));
7032 return VERR_VGA_INVALID_CUSTOM_MODE;
7033 }
7034 cbPitch = calc_line_pitch(cBits, cx);
7035 if (cy * cbPitch >= pThis->vram_size)
7036 {
7037 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",
7038 cx, cy, cBits, pThis->vram_size / _1M));
7039 return VERR_VGA_INVALID_CUSTOM_MODE;
7040 }
7041 PDMDevHlpMMHeapFree(pDevIns, pszExtraData);
7042
7043 /* Use defaults from max@bpp mode. */
7044 switch (cBits)
7045 {
7046 case 8:
7047 u16DefMode = VBE_VESA_MODE_1024X768X8;
7048 break;
7049
7050 case 16:
7051 u16DefMode = VBE_VESA_MODE_1024X768X565;
7052 break;
7053
7054 case 24:
7055 u16DefMode = VBE_VESA_MODE_1024X768X888;
7056 break;
7057
7058 case 32:
7059 u16DefMode = VBE_OWN_MODE_1024X768X8888;
7060 break;
7061
7062 default: /* gcc, shut up! */
7063 AssertMsgFailed(("gone postal!\n"));
7064 continue;
7065 }
7066
7067 /* mode_info_list is not terminated */
7068 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
7069 pDefMode++;
7070 Assert(j < MODE_INFO_SIZE);
7071
7072 *pCurMode = *pDefMode;
7073 pCurMode->mode = u16CurMode++;
7074
7075 /* adjust defaults */
7076 pCurMode->info.XResolution = cx;
7077 pCurMode->info.YResolution = cy;
7078 pCurMode->info.BytesPerScanLine = cbPitch;
7079 pCurMode->info.LinBytesPerScanLine = cbPitch;
7080 vgaR3AdjustModeInfo(pThis, pCurMode);
7081
7082 /* commit it */
7083 pCurMode++;
7084 }
7085 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
7086 {
7087 AssertMsgFailed(("pHlp->pfnCFGMQueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
7088 return rc;
7089 }
7090 } /* foreach custom mode key */
7091 }
7092
7093 /*
7094 * Add the "End of list" mode.
7095 */
7096 memset(pCurMode, 0, sizeof(*pCurMode));
7097 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
7098
7099 /*
7100 * Register I/O Port for the VBE BIOS Extra Data.
7101 */
7102 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_EXTRA_PORT, 1 /*cPorts*/, vbeR3IOPortWriteVbeExtra, vbeR3IoPortReadVbeExtra,
7103 "VBE BIOS Extra Data", NULL /*paExtDesc*/, &pThis->hIoPortVbeExtra);
7104 AssertRCReturn(rc, rc);
7105
7106 /*
7107 * Register I/O Port for the BIOS Logo.
7108 */
7109 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, LOGO_IO_PORT, 1 /*cPorts*/, vbeR3IoPortWriteCmdLogo, vbeR3IoPortReadCmdLogo,
7110 "BIOS Logo", NULL /*paExtDesc*/, &pThis->hIoPortCmdLogo);
7111 AssertRCReturn(rc, rc);
7112
7113 /*
7114 * Register debugger info callbacks.
7115 */
7116 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaR3InfoState);
7117 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaR3InfoText);
7118 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaR3InfoCR);
7119 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaR3InfoGR);
7120 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaR3InfoSR);
7121 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaR3InfoAR);
7122 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaR3InfoPlanar);
7123 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaR3InfoDAC);
7124 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaR3InfoVBE);
7125 }
7126
7127 /*
7128 * Construct the logo header.
7129 */
7130 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
7131
7132 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
7133 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7134 LogoHdr.fu8FadeIn = 1;
7135 else if (RT_FAILURE(rc))
7136 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeIn\" as integer failed"));
7137
7138 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
7139 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7140 LogoHdr.fu8FadeOut = 1;
7141 else if (RT_FAILURE(rc))
7142 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeOut\" as integer failed"));
7143
7144 rc = pHlp->pfnCFGMQueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
7145 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7146 LogoHdr.u16LogoMillies = 0;
7147 else if (RT_FAILURE(rc))
7148 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"LogoTime\" as integer failed"));
7149
7150 rc = pHlp->pfnCFGMQueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
7151 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7152 LogoHdr.fu8ShowBootMenu = 0;
7153 else if (RT_FAILURE(rc))
7154 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
7155
7156# if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
7157 /* Disable the logo abd menu if all default settings. */
7158 if ( LogoHdr.fu8FadeIn
7159 && LogoHdr.fu8FadeOut
7160 && LogoHdr.u16LogoMillies == 0
7161 && LogoHdr.fu8ShowBootMenu == 2)
7162 {
7163 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
7164 LogoHdr.u16LogoMillies = 500;
7165 }
7166# endif
7167
7168 /* Delay the logo a little bit */
7169 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
7170 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
7171
7172 /*
7173 * Get the Logo file name.
7174 */
7175 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LogoFile", &pThisCC->pszLogoFile);
7176 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7177 pThisCC->pszLogoFile = NULL;
7178 else if (RT_FAILURE(rc))
7179 return PDMDEV_SET_ERROR(pDevIns, rc,
7180 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
7181 else if (!*pThisCC->pszLogoFile)
7182 {
7183 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7184 pThisCC->pszLogoFile = NULL;
7185 }
7186
7187 /*
7188 * Determine the logo size, open any specified logo file in the process.
7189 */
7190 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7191 RTFILE FileLogo = NIL_RTFILE;
7192 if (pThisCC->pszLogoFile)
7193 {
7194 rc = RTFileOpen(&FileLogo, pThisCC->pszLogoFile,
7195 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
7196 if (RT_SUCCESS(rc))
7197 {
7198 uint64_t cbFile;
7199 rc = RTFileQuerySize(FileLogo, &cbFile);
7200 if (RT_SUCCESS(rc))
7201 {
7202 if (cbFile > 0 && cbFile < 32*_1M)
7203 LogoHdr.cbLogo = (uint32_t)cbFile;
7204 else
7205 rc = VERR_TOO_MUCH_DATA;
7206 }
7207 }
7208 if (RT_FAILURE(rc))
7209 {
7210 /*
7211 * Ignore failure and fall back to the default logo.
7212 */
7213 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThisCC->pszLogoFile, rc));
7214 if (FileLogo != NIL_RTFILE)
7215 RTFileClose(FileLogo);
7216 FileLogo = NIL_RTFILE;
7217 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7218 pThisCC->pszLogoFile = NULL;
7219 }
7220 }
7221
7222 /*
7223 * Disable graphic splash screen if it doesn't fit into VRAM.
7224 */
7225 if (pThis->vram_size < LOGO_MAX_SIZE)
7226 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7227
7228 /*
7229 * Allocate buffer for the logo data.
7230 * Let us fall back to default logo on read failure.
7231 */
7232 pThisCC->cbLogo = LogoHdr.cbLogo;
7233 if (g_cbVgaDefBiosLogo)
7234 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogo);
7235 pThisCC->cbLogo += sizeof(LogoHdr);
7236
7237 pThisCC->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbLogo);
7238 if (pThisCC->pbLogo)
7239 {
7240 /*
7241 * Write the logo header.
7242 */
7243 PLOGOHDR pLogoHdr = (PLOGOHDR)pThisCC->pbLogo;
7244 *pLogoHdr = LogoHdr;
7245
7246 /*
7247 * Write the logo bitmap.
7248 */
7249 if (pThisCC->pszLogoFile)
7250 {
7251 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7252 if (RT_SUCCESS(rc))
7253 rc = vbeR3ParseBitmap(pThisCC);
7254 if (RT_FAILURE(rc))
7255 {
7256 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7257 rc, pThisCC->pszLogoFile));
7258 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7259 }
7260 }
7261 if ( !pThisCC->pszLogoFile
7262 || RT_FAILURE(rc))
7263 {
7264 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7265 rc = vbeR3ParseBitmap(pThisCC);
7266 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Parsing of internal bitmap failed! vbeR3ParseBitmap() -> %Rrc\n", rc), rc);
7267 }
7268
7269 rc = VINF_SUCCESS;
7270 }
7271 else
7272 rc = VERR_NO_MEMORY;
7273
7274 /*
7275 * Cleanup.
7276 */
7277 if (FileLogo != NIL_RTFILE)
7278 RTFileClose(FileLogo);
7279
7280# ifdef VBOX_WITH_HGSMI
7281 VBVAInit(pDevIns, pThis, pThisCC);
7282# endif
7283
7284# ifdef VBOX_WITH_VDMA
7285 if (rc == VINF_SUCCESS)
7286 {
7287 rc = vboxVDMAConstruct(pThis, pThisCC, 1024);
7288 AssertRC(rc);
7289 }
7290# endif
7291
7292# ifdef VBOX_WITH_VMSVGA
7293 if ( rc == VINF_SUCCESS
7294 && pThis->fVMSVGAEnabled)
7295 rc = vmsvgaR3Init(pDevIns);
7296# endif
7297
7298 /*
7299 * Statistics.
7300 */
7301# ifdef VBOX_WITH_STATISTICS
7302 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7303 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7304 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7305 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7306 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapPage, STAMTYPE_COUNTER, "MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMmioMapMmio2Page.");
7307 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapReset, STAMTYPE_COUNTER, "MapPageReset", STAMUNIT_OCCURENCES, "Calls to IOMMmioResetRegion.");
7308 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaR3PortUpdateDisplay().");
7309# endif
7310# ifdef VBOX_WITH_HGSMI
7311 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatHgsmiMdaCgaAccesses, STAMTYPE_COUNTER, "HgmsiMdaCgaAccesses", STAMUNIT_OCCURENCES, "Number of non-HGMSI accesses for 03b0-3b3 and 03d0-3d3.");
7312# endif
7313
7314 /* Init latched access mask. */
7315 pThis->uMaskLatchAccess = 0x3ff;
7316
7317 if (RT_SUCCESS(rc))
7318 {
7319 PPDMIBASE pBase;
7320 /*
7321 * Attach status driver (optional).
7322 */
7323 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
7324 if (RT_SUCCESS(rc))
7325 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7326 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7327 {
7328 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7329 rc = VINF_SUCCESS;
7330 }
7331 else
7332 {
7333 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7334 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7335 }
7336 }
7337 return rc;
7338}
7339
7340#else /* !IN_RING3 */
7341
7342/**
7343 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
7344 */
7345static DECLCALLBACK(int) vgaRZConstruct(PPDMDEVINS pDevIns)
7346{
7347 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
7348 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
7349 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
7350
7351 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
7352 AssertRCReturn(rc, rc);
7353
7354 if (pThis->fLegacyVgaEnabled)
7355 {
7356 /*
7357 * Set I/O port callbacks for this context.
7358 * We just copy the ring-3 registration bits and remove the '&' before the handle.
7359 */
7360# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_hIoPort) do { \
7361 rc = PDMDevHlpIoPortSetUpContext(pDevIns, a_hIoPort, a_pfnWrite, a_pfnRead, NULL /*pvUser*/); \
7362 AssertRCReturn(rc, rc); \
7363 } while (0)
7364
7365 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", pThis->hIoPortAr);
7366 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", pThis->hIoPortMsrSt00);
7367 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", pThis->hIoPort3c3);
7368 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", pThis->hIoPortSr);
7369 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", pThis->hIoPortDac);
7370 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ pThis->hIoPortPos);
7371 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", pThis->hIoPortGr);
7372
7373 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", pThis->hIoPortMdaCrt);
7374 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", pThis->hIoPortMdaFcrSt);
7375 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", pThis->hIoPortCgaCrt);
7376 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", pThis->hIoPortCgaFcrSt);
7377
7378# ifdef CONFIG_BOCHS_VBE
7379 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", pThis->hIoPortVbeIndex);
7380 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", pThis->hIoPortVbeData);
7381# endif /* CONFIG_BOCHS_VBE */
7382
7383# undef REG_PORT
7384
7385 /* BIOS port: */
7386 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortBios, vgaIoPortWriteBios, vgaIoPortReadBios, NULL /*pvUser*/);
7387 AssertRCReturn(rc, rc);
7388
7389 /*
7390 * MMIO.
7391 */
7392 rc = PDMDevHlpMmioSetUpContextEx(pDevIns, pThis->hMmioLegacy, vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/);
7393 AssertRCReturn(rc, rc);
7394 }
7395
7396# ifdef VBOX_WITH_VMSVGA
7397 if (pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE)
7398 {
7399 AssertReturn(pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7400 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortVmSvga, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/);
7401 AssertRCReturn(rc, rc);
7402 }
7403 else
7404 AssertReturn(!pThis->fVMSVGAEnabled || pThis->fVmSvga3, VERR_INVALID_STATE);
7405
7406 if (pThis->hMmioSvga3 != NIL_IOMMMIOHANDLE)
7407 {
7408 AssertReturn(pThis->fVMSVGAEnabled && pThis->fVmSvga3, VERR_INVALID_STATE);
7409 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioSvga3, vmsvga3MmioWrite, vmsvga3MmioRead, NULL /*pvUser*/);
7410 AssertRCReturn(rc, rc);
7411 }
7412 else
7413 AssertReturn(!pThis->fVmSvga3, VERR_INVALID_STATE);
7414# endif
7415
7416 /*
7417 * Map the start of the VRAM into this context.
7418 */
7419# if defined(VBOX_WITH_2X_4GB_ADDR_SPACE) || (defined(IN_RING0) && defined(VGA_WITH_PARTIAL_RING0_MAPPING))
7420 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VRam, 0 /* off */, VGA_MAPPING_SIZE, (void **)&pThisCC->pbVRam);
7421 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMmio2SetUpContext(,VRAM,0,%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
7422# endif
7423
7424 /*
7425 * Map the first page of the VMSVGA FIFO into this context (not raw-mode).
7426 * We currently only access SVGA_FIFO_MIN, SVGA_FIFO_PITCHLOCK, and SVGA_FIFO_BUSY.
7427 */
7428# if defined(VBOX_WITH_VMSVGA) && !defined(IN_RC)
7429 AssertCompile((RT_MAX(SVGA_FIFO_MIN, RT_MAX(SVGA_FIFO_PITCHLOCK, SVGA_FIFO_BUSY)) + 1) * sizeof(uint32_t) < GUEST_PAGE_SIZE);
7430 if ( pThis->fVMSVGAEnabled
7431 && !pThis->fVmSvga3)
7432 {
7433 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VmSvgaFifo, 0 /* off */, GUEST_PAGE_SIZE,
7434 (void **)&pThisCC->svga.pau32FIFO);
7435 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
7436 }
7437 else
7438 AssertReturn(pThis->hMmio2VmSvgaFifo == NIL_PGMMMIO2HANDLE, VERR_INVALID_STATE);
7439# endif
7440
7441 return VINF_SUCCESS;
7442}
7443
7444#endif /* !IN_RING3 */
7445
7446/**
7447 * The device registration structure.
7448 */
7449const PDMDEVREG g_DeviceVga =
7450{
7451 /* .u32Version = */ PDM_DEVREG_VERSION,
7452 /* .uReserved0 = */ 0,
7453 /* .szName = */ "vga",
7454 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
7455 /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS,
7456 /* .cMaxInstances = */ 1,
7457 /* .uSharedVersion = */ 42,
7458 /* .cbInstanceShared = */ sizeof(VGASTATE),
7459 /* .cbInstanceCC = */ sizeof(VGASTATECC),
7460 /* .cbInstanceRC = */ sizeof(VGASTATERC),
7461 /* .cMaxPciDevices = */ 1,
7462 /* .cMaxMsixVectors = */ 0,
7463 /* .pszDescription = */ "VGA Adaptor with VESA extensions.",
7464#if defined(IN_RING3)
7465 /* .pszRCMod = */ "VBoxDDRC.rc",
7466 /* .pszR0Mod = */ "VBoxDDR0.r0",
7467 /* .pfnConstruct = */ vgaR3Construct,
7468 /* .pfnDestruct = */ vgaR3Destruct,
7469 /* .pfnRelocate = */ vgaR3Relocate,
7470 /* .pfnMemSetup = */ NULL,
7471 /* .pfnPowerOn = */ vgaR3PowerOn,
7472 /* .pfnReset = */ vgaR3Reset,
7473 /* .pfnSuspend = */ NULL,
7474 /* .pfnResume = */ vgaR3Resume,
7475 /* .pfnAttach = */ vgaAttach,
7476 /* .pfnDetach = */ vgaDetach,
7477 /* .pfnQueryInterface = */ NULL,
7478 /* .pfnInitComplete = */ NULL,
7479 /* .pfnPowerOff = */ vgaR3PowerOff,
7480 /* .pfnSoftReset = */ NULL,
7481 /* .pfnReserved0 = */ NULL,
7482 /* .pfnReserved1 = */ NULL,
7483 /* .pfnReserved2 = */ NULL,
7484 /* .pfnReserved3 = */ NULL,
7485 /* .pfnReserved4 = */ NULL,
7486 /* .pfnReserved5 = */ NULL,
7487 /* .pfnReserved6 = */ NULL,
7488 /* .pfnReserved7 = */ NULL,
7489#elif defined(IN_RING0)
7490 /* .pfnEarlyConstruct = */ NULL,
7491 /* .pfnConstruct = */ vgaRZConstruct,
7492 /* .pfnDestruct = */ NULL,
7493 /* .pfnFinalDestruct = */ NULL,
7494 /* .pfnRequest = */ NULL,
7495 /* .pfnReserved0 = */ NULL,
7496 /* .pfnReserved1 = */ NULL,
7497 /* .pfnReserved2 = */ NULL,
7498 /* .pfnReserved3 = */ NULL,
7499 /* .pfnReserved4 = */ NULL,
7500 /* .pfnReserved5 = */ NULL,
7501 /* .pfnReserved6 = */ NULL,
7502 /* .pfnReserved7 = */ NULL,
7503#elif defined(IN_RC)
7504 /* .pfnConstruct = */ vgaRZConstruct,
7505 /* .pfnReserved0 = */ NULL,
7506 /* .pfnReserved1 = */ NULL,
7507 /* .pfnReserved2 = */ NULL,
7508 /* .pfnReserved3 = */ NULL,
7509 /* .pfnReserved4 = */ NULL,
7510 /* .pfnReserved5 = */ NULL,
7511 /* .pfnReserved6 = */ NULL,
7512 /* .pfnReserved7 = */ NULL,
7513#else
7514# error "Not in IN_RING3, IN_RING0 or IN_RC!"
7515#endif
7516 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
7517};
7518
7519#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7520
7521/*
7522 * Local Variables:
7523 * nuke-trailing-whitespace-p:nil
7524 * End:
7525 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette