VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 265.4 KB
Line 
1/* $Id: DevVGA.cpp 106061 2024-09-16 14:03:52Z 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 = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1067 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
1068 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
1069 else
1070 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] * ((pThis->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
1071 cb *= pThis->vbe_regs[VBE_DISPI_INDEX_YRES];
1072 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
1073 if (!cVirtWidth)
1074 cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1075 if ( !cVirtWidth
1076 || !pThis->vbe_regs[VBE_DISPI_INDEX_YRES]
1077 || cb > pThis->vram_size)
1078 {
1079 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:
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_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
3322 if (u32 == HGSMIOFFSET_VOID)
3323 {
3324 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSectIRQ, VERR_SEM_BUSY);
3325 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSectIRQ, rcLock);
3326
3327 if (pThis->fu32PendingGuestFlags == 0)
3328 {
3329 PDMDevHlpPCISetIrqNoWait(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
3330 HGSMIClearHostGuestFlags(pThisCC->pHGSMI,
3331 HGSMIHOSTFLAGS_IRQ
3332 | HGSMIHOSTFLAGS_VSYNC
3333 | HGSMIHOSTFLAGS_HOTPLUG
3334 | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES);
3335 }
3336 else
3337 {
3338 HGSMISetHostGuestFlags(pThisCC->pHGSMI, HGSMIHOSTFLAGS_IRQ | pThis->fu32PendingGuestFlags);
3339 pThis->fu32PendingGuestFlags = 0;
3340 /* Keep the IRQ unchanged. */
3341 }
3342
3343 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSectIRQ);
3344 }
3345 else
3346# endif
3347 {
3348 HGSMIHostWrite(pThisCC->pHGSMI, u32);
3349 }
3350 break;
3351 }
3352
3353 case VGA_PORT_HGSMI_GUEST: /* Guest */
3354 HGSMIGuestWrite(pThisCC->pHGSMI, u32);
3355 break;
3356
3357 default:
3358# ifdef DEBUG_sunlover
3359 AssertMsgFailed(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3360# endif
3361 break;
3362 }
3363 }
3364 else
3365 {
3366 /** @todo r=bird: According to Ralf Brown, one and two byte accesses to the
3367 * 0x3b0-0x3b1 and 0x3b2-0x3b3 I/O port pairs should work the same as
3368 * 0x3b4-0x3b5 (MDA CRT control). */
3369 Log(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x - possible valid MDA CRT access\n", offPort, cb, u32));
3370# ifdef DEBUG_sunlover
3371 AssertMsgFailed(("vgaR3IOPortHgsmiWrite: offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32));
3372# endif
3373 STAM_REL_COUNTER_INC(&pThis->StatHgsmiMdaCgaAccesses);
3374 }
3375
3376 return VINF_SUCCESS;
3377}
3378
3379
3380/**
3381 * @callback_method_impl{FNIOMIOPORTNEWOUT,HGSMI IN handler.}
3382 */
3383static DECLCALLBACK(VBOXSTRICTRC)
3384vgaR3IOPortHgmsiRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3385{
3386 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3387 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3388 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3389 LogFlowFunc(("offPort=0x%x cb=%d\n", offPort, cb));
3390
3391 NOREF(pvUser);
3392
3393 VBOXSTRICTRC rc = VINF_SUCCESS;
3394 if (cb == 4)
3395 {
3396 switch (offPort)
3397 {
3398 case VGA_PORT_HGSMI_HOST: /* Host */
3399 *pu32 = HGSMIHostRead(pThisCC->pHGSMI);
3400 break;
3401 case VGA_PORT_HGSMI_GUEST: /* Guest */
3402 *pu32 = HGSMIGuestRead(pThisCC->pHGSMI);
3403 break;
3404 default:
3405 rc = VERR_IOM_IOPORT_UNUSED;
3406 break;
3407 }
3408 }
3409 else
3410 {
3411 /** @todo r=bird: According to Ralf Brown, one and two byte accesses to the
3412 * 0x3b0-0x3b1 and 0x3b2-0x3b3 I/O port pairs should work the same as
3413 * 0x3b4-0x3b5 (MDA CRT control). */
3414 Log(("vgaR3IOPortHgmsiRead: offPort=%#x cb=%d - possible valid MDA CRT access\n", offPort, cb));
3415 STAM_REL_COUNTER_INC(&pThis->StatHgsmiMdaCgaAccesses);
3416 rc = VERR_IOM_IOPORT_UNUSED;
3417 }
3418
3419 return rc;
3420}
3421
3422#endif /* VBOX_WITH_HGSMI && IN_RING3*/
3423
3424
3425
3426
3427/* -=-=-=-=-=- All Contexts -=-=-=-=-=- */
3428
3429/**
3430 * @internal. For use inside VGAGCMemoryFillWrite only.
3431 * Macro for apply logical operation and bit mask.
3432 */
3433#define APPLY_LOGICAL_AND_MASK(pThis, val, bit_mask) \
3434 /* apply logical operation */ \
3435 switch (pThis->gr[3] >> 3)\
3436 { \
3437 case 0: \
3438 default:\
3439 /* nothing to do */ \
3440 break; \
3441 case 1: \
3442 /* and */ \
3443 val &= pThis->latch; \
3444 break; \
3445 case 2: \
3446 /* or */ \
3447 val |= pThis->latch; \
3448 break; \
3449 case 3: \
3450 /* xor */ \
3451 val ^= pThis->latch; \
3452 break; \
3453 } \
3454 /* apply bit mask */ \
3455 val = (val & bit_mask) | (pThis->latch & ~bit_mask)
3456
3457/**
3458 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3459 * This is the advanced version of vga_mem_writeb function.
3460 *
3461 * @returns VBox status code.
3462 * @param pThis The shared VGA instance data.
3463 * @param pThisCC The VGA instance data for the current context.
3464 * @param pvUser User argument - ignored.
3465 * @param GCPhysAddr Physical address of memory to write.
3466 * @param u32Item Data to write, up to 4 bytes.
3467 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3468 * @param cItems Number of data items to write.
3469 */
3470static int vgaInternalMMIOFill(PVGASTATE pThis, PVGASTATECC pThisCC, void *pvUser, RTGCPHYS GCPhysAddr,
3471 uint32_t u32Item, unsigned cbItem, unsigned cItems)
3472{
3473 uint32_t b;
3474 uint32_t write_mask, bit_mask, set_mask;
3475 uint32_t aVal[4];
3476 unsigned i;
3477 NOREF(pvUser);
3478
3479 for (i = 0; i < cbItem; i++)
3480 {
3481 aVal[i] = u32Item & 0xff;
3482 u32Item >>= 8;
3483 }
3484
3485 /* convert to VGA memory offset */
3486 /// @todo add check for the end of region
3487 GCPhysAddr &= 0x1ffff;
3488 switch((pThis->gr[6] >> 2) & 3) {
3489 case 0:
3490 break;
3491 case 1:
3492 if (GCPhysAddr >= 0x10000)
3493 return VINF_SUCCESS;
3494 GCPhysAddr += pThis->bank_offset;
3495 break;
3496 case 2:
3497 GCPhysAddr -= 0x10000;
3498 if (GCPhysAddr >= 0x8000)
3499 return VINF_SUCCESS;
3500 break;
3501 default:
3502 case 3:
3503 GCPhysAddr -= 0x18000;
3504 if (GCPhysAddr >= 0x8000)
3505 return VINF_SUCCESS;
3506 break;
3507 }
3508
3509 if (pThis->sr[4] & 0x08) {
3510 /* chain 4 mode : simplest access */
3511 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3512
3513 while (cItems-- > 0)
3514 for (i = 0; i < cbItem; i++)
3515 {
3516 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3517 {
3518 pThisCC->pbVRam[GCPhysAddr] = aVal[i];
3519 vgaR3MarkDirty(pThis, GCPhysAddr);
3520 }
3521 GCPhysAddr++;
3522 }
3523 } else if (pThis->gr[5] & 0x10) {
3524 /* odd/even mode (aka text mode mapping) */
3525 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3526 while (cItems-- > 0)
3527 for (i = 0; i < cbItem; i++)
3528 {
3529 unsigned plane = GCPhysAddr & 1;
3530 if (pThis->sr[2] & (1 << plane)) {
3531 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) * 4) | plane;
3532 pThisCC->pbVRam[PhysAddr2] = aVal[i];
3533 vgaR3MarkDirty(pThis, PhysAddr2);
3534 }
3535 GCPhysAddr++;
3536 }
3537 } else {
3538 /* standard VGA latched access */
3539 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3540
3541 switch(pThis->gr[5] & 3) {
3542 default:
3543 case 0:
3544 /* rotate */
3545 b = pThis->gr[3] & 7;
3546 bit_mask = pThis->gr[8];
3547 bit_mask |= bit_mask << 8;
3548 bit_mask |= bit_mask << 16;
3549 set_mask = mask16[pThis->gr[1]];
3550
3551 for (i = 0; i < cbItem; i++)
3552 {
3553 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3554 aVal[i] |= aVal[i] << 8;
3555 aVal[i] |= aVal[i] << 16;
3556
3557 /* apply set/reset mask */
3558 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3559
3560 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3561 }
3562 break;
3563 case 1:
3564 for (i = 0; i < cbItem; i++)
3565 aVal[i] = pThis->latch;
3566 break;
3567 case 2:
3568 bit_mask = pThis->gr[8];
3569 bit_mask |= bit_mask << 8;
3570 bit_mask |= bit_mask << 16;
3571 for (i = 0; i < cbItem; i++)
3572 {
3573 aVal[i] = mask16[aVal[i] & 0x0f];
3574
3575 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3576 }
3577 break;
3578 case 3:
3579 /* rotate */
3580 b = pThis->gr[3] & 7;
3581
3582 for (i = 0; i < cbItem; i++)
3583 {
3584 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3585 bit_mask = pThis->gr[8] & aVal[i];
3586 bit_mask |= bit_mask << 8;
3587 bit_mask |= bit_mask << 16;
3588 aVal[i] = mask16[pThis->gr[0]];
3589
3590 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3591 }
3592 break;
3593 }
3594
3595 /* mask data according to sr[2] */
3596 write_mask = mask16[pThis->sr[2]];
3597
3598 /* actually write data */
3599 if (cbItem == 1)
3600 {
3601 /* The most frequently case is 1 byte I/O. */
3602 while (cItems-- > 0)
3603 {
3604 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3605 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3606 GCPhysAddr++;
3607 }
3608 }
3609 else if (cbItem == 2)
3610 {
3611 /* The second case is 2 bytes I/O. */
3612 while (cItems-- > 0)
3613 {
3614 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3615 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3616 GCPhysAddr++;
3617
3618 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3619 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3620 GCPhysAddr++;
3621 }
3622 }
3623 else
3624 {
3625 /* And the rest is 4 bytes. */
3626 Assert(cbItem == 4);
3627 while (cItems-- > 0)
3628 for (i = 0; i < cbItem; i++)
3629 {
3630 ((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] = (((uint32_t *)pThisCC->pbVRam)[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3631 vgaR3MarkDirty(pThis, GCPhysAddr * 4);
3632 GCPhysAddr++;
3633 }
3634 }
3635 }
3636 return VINF_SUCCESS;
3637}
3638
3639#undef APPLY_LOGICAL_AND_MASK
3640
3641/**
3642 * @callback_method_impl{FNIOMMMIONEWFILL,
3643 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM and
3644 * from the inside of VGADeviceGC.cpp. This is the advanced version of
3645 * vga_mem_writeb function.}
3646 */
3647static DECLCALLBACK(VBOXSTRICTRC)
3648vgaMmioFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3649{
3650 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3651 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3652 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3653
3654 return vgaInternalMMIOFill(pThis, pThisCC, pvUser, off, u32Item, cbItem, cItems);
3655}
3656
3657
3658/**
3659 * @callback_method_impl{FNIOMMMIONEWREAD,
3660 * Legacy VGA memory (0xa0000 - 0xbffff) read hook\, to be called from IOM.}
3661 *
3662 * @note The @a off is an absolute address in the 0xa0000 - 0xbffff range, not
3663 * an offset.
3664 */
3665static DECLCALLBACK(VBOXSTRICTRC) vgaMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
3666{
3667 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3668 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3669 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3670 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3671 NOREF(pvUser);
3672
3673 int rc = VINF_SUCCESS;
3674 switch (cb)
3675 {
3676 case 1:
3677 *(uint8_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc);
3678 break;
3679 case 2:
3680/** @todo This and the wider accesses maybe misbehave when accessing bytes
3681 * crossing the 512KB VRAM boundrary if the access is handled in
3682 * ring-0 and operating in latched mode. */
3683 *(uint16_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3684 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8);
3685 break;
3686 case 4:
3687 *(uint32_t *)pv = vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3688 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8)
3689 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 2, &rc) << 16)
3690 | (vga_mem_readb(pDevIns, pThis, pThisCC, off + 3, &rc) << 24);
3691 break;
3692
3693 case 8:
3694 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off, &rc)
3695 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 1, &rc) << 8)
3696 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 2, &rc) << 16)
3697 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 3, &rc) << 24)
3698 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 4, &rc) << 32)
3699 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 5, &rc) << 40)
3700 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 6, &rc) << 48)
3701 | ((uint64_t)vga_mem_readb(pDevIns, pThis, pThisCC, off + 7, &rc) << 56);
3702 break;
3703
3704 default:
3705 {
3706 uint8_t *pbData = (uint8_t *)pv;
3707 while (cb-- > 0)
3708 {
3709 *pbData++ = vga_mem_readb(pDevIns, pThis, pThisCC, off++, &rc);
3710 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3711 break;
3712 }
3713 }
3714 }
3715
3716 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3717 return rc;
3718}
3719
3720/**
3721 * @callback_method_impl{FNIOMMMIONEWWRITE,
3722 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM.}
3723 *
3724 * @note The @a off is an absolute address in the 0xa0000 - 0xbffff range, not
3725 * an offset.
3726 */
3727static DECLCALLBACK(VBOXSTRICTRC) vgaMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
3728{
3729 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3730 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3731 uint8_t const *pbSrc = (uint8_t const *)pv;
3732 NOREF(pvUser);
3733 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3734 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3735
3736 VBOXSTRICTRC rc;
3737 switch (cb)
3738 {
3739 case 1:
3740 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off, *pbSrc);
3741 break;
3742#if 1
3743 case 2:
3744 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3745 if (RT_LIKELY(rc == VINF_SUCCESS))
3746 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3747 break;
3748 case 4:
3749 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3750 if (RT_LIKELY(rc == VINF_SUCCESS))
3751 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3752 if (RT_LIKELY(rc == VINF_SUCCESS))
3753 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 2, pbSrc[2]);
3754 if (RT_LIKELY(rc == VINF_SUCCESS))
3755 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 3, pbSrc[3]);
3756 break;
3757 case 8:
3758 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 0, pbSrc[0]);
3759 if (RT_LIKELY(rc == VINF_SUCCESS))
3760 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 1, pbSrc[1]);
3761 if (RT_LIKELY(rc == VINF_SUCCESS))
3762 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 2, pbSrc[2]);
3763 if (RT_LIKELY(rc == VINF_SUCCESS))
3764 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 3, pbSrc[3]);
3765 if (RT_LIKELY(rc == VINF_SUCCESS))
3766 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 4, pbSrc[4]);
3767 if (RT_LIKELY(rc == VINF_SUCCESS))
3768 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 5, pbSrc[5]);
3769 if (RT_LIKELY(rc == VINF_SUCCESS))
3770 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 6, pbSrc[6]);
3771 if (RT_LIKELY(rc == VINF_SUCCESS))
3772 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off + 7, pbSrc[7]);
3773 break;
3774#else
3775 case 2:
3776 rc = vgaMmioFill(pDevIns, off, *(uint16_t *)pv, 2, 1);
3777 break;
3778 case 4:
3779 rc = vgaMmioFill(pDevIns, off, *(uint32_t *)pv, 4, 1);
3780 break;
3781 case 8:
3782 rc = vgaMmioFill(pDevIns, off, *(uint64_t *)pv, 8, 1);
3783 break;
3784#endif
3785 default:
3786 rc = VINF_SUCCESS;
3787 while (cb-- > 0 && rc == VINF_SUCCESS)
3788 rc = vga_mem_writeb(pDevIns, pThis, pThisCC, off++, *pbSrc++);
3789 break;
3790
3791 }
3792 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3793 return rc;
3794}
3795
3796
3797/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3798
3799/**
3800 * @callback_method_impl{FNIOMIOPORTNEWIN,
3801 * Port I/O Handler for VGA BIOS IN operations.}
3802 */
3803static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortReadBios(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3804{
3805 RT_NOREF(pDevIns, pvUser, offPort, pu32, cb);
3806 return VERR_IOM_IOPORT_UNUSED;
3807}
3808
3809/**
3810 * @callback_method_impl{FNIOMIOPORTNEWOUT,
3811 * Port I/O Handler for VGA BIOS IN operations.}
3812 */
3813static DECLCALLBACK(VBOXSTRICTRC) vgaIoPortWriteBios(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3814{
3815 RT_NOREF2(pDevIns, pvUser);
3816 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3817 Assert(offPort == 0); RT_NOREF(offPort);
3818
3819 /*
3820 * VGA BIOS char printing.
3821 */
3822 if (cb == 1)
3823 {
3824#if 0
3825 switch (u32)
3826 {
3827 case '\r': Log(("vgabios: <return>\n")); break;
3828 case '\n': Log(("vgabios: <newline>\n")); break;
3829 case '\t': Log(("vgabios: <tab>\n")); break;
3830 default:
3831 Log(("vgabios: %c\n", u32));
3832 }
3833#else
3834 static int s_fLastWasNotNewline = 0; /* We are only called in a single-threaded way */
3835 if (s_fLastWasNotNewline == 0)
3836 Log(("vgabios: "));
3837 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3838 Log(("%c", u32));
3839 if (u32 == '\n')
3840 s_fLastWasNotNewline = 0;
3841 else
3842 s_fLastWasNotNewline = 1;
3843#endif
3844 return VINF_SUCCESS;
3845 }
3846
3847 /* not in use. */
3848 return VERR_IOM_IOPORT_UNUSED;
3849}
3850
3851
3852/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3853
3854#ifdef IN_RING3
3855
3856/**
3857 * @callback_method_impl{FNIOMIOPORTNEWOUT,
3858 * Port I/O Handler for VBE Extra OUT operations.}
3859 */
3860static DECLCALLBACK(VBOXSTRICTRC)
3861vbeR3IOPortWriteVbeExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
3862{
3863 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3864 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3865 RT_NOREF(offPort, pvUser);
3866
3867 if (cb == 2)
3868 {
3869 Log(("vbeR3IOPortWriteVbeExtra: addr=%#RX32\n", u32));
3870 pThisCC->u16VBEExtraAddress = u32;
3871 }
3872 else
3873 Log(("vbeR3IOPortWriteVbeExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3874
3875 return VINF_SUCCESS;
3876}
3877
3878
3879/**
3880 * @callback_method_impl{FNIOMIOPORTNEWIN,
3881 * Port I/O Handler for VBE Extra IN operations.}
3882 */
3883static DECLCALLBACK(VBOXSTRICTRC)
3884vbeR3IoPortReadVbeExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
3885{
3886 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
3887 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
3888 Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
3889 RT_NOREF(offPort, pvUser);
3890
3891 int rc = VINF_SUCCESS;
3892 if (pThisCC->u16VBEExtraAddress == 0xffff)
3893 {
3894 Log(("vbeR3IoPortReadVbeExtra: Requested number of 64k video banks\n"));
3895 *pu32 = pThis->vram_size / _64K;
3896 }
3897 else if ( pThisCC->u16VBEExtraAddress >= pThisCC->cbVBEExtraData
3898 || pThisCC->u16VBEExtraAddress + cb > pThisCC->cbVBEExtraData)
3899 {
3900 *pu32 = 0;
3901 Log(("vbeR3IoPortReadVbeExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3902 pThisCC->u16VBEExtraAddress, pThisCC->u16VBEExtraAddress, pThisCC->cbVBEExtraData, pThisCC->cbVBEExtraData));
3903 }
3904 else
3905 {
3906 RT_UNTRUSTED_VALIDATED_FENCE();
3907 if (cb == 1)
3908 {
3909 *pu32 = pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress] & 0xFF;
3910
3911 Log(("vbeR3IoPortReadVbeExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3912 }
3913 else if (cb == 2)
3914 {
3915 *pu32 = pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress]
3916 | (uint32_t)pThisCC->pbVBEExtraData[pThisCC->u16VBEExtraAddress + 1] << 8;
3917
3918 Log(("vbeR3IoPortReadVbeExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3919 }
3920 else
3921 {
3922 Log(("vbeR3IoPortReadVbeExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3923 rc = VERR_IOM_IOPORT_UNUSED;
3924 }
3925 }
3926
3927 return rc;
3928}
3929
3930
3931/**
3932 * Parse the logo bitmap data at init time.
3933 *
3934 * @returns VBox status code.
3935 *
3936 * @param pThisCC The VGA instance data for ring-3.
3937 */
3938static int vbeR3ParseBitmap(PVGASTATECC pThisCC)
3939{
3940 /*
3941 * Get bitmap header data
3942 */
3943 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
3944 PBMPFILEHDR pFileHdr = (PBMPFILEHDR)(pThisCC->pbLogo + sizeof(LOGOHDR));
3945 PBMPWIN3XINFOHDR pCoreHdr = (PBMPWIN3XINFOHDR)(pThisCC->pbLogo + sizeof(LOGOHDR) + sizeof(BMPFILEHDR));
3946
3947 if (pFileHdr->uType == BMP_HDR_MAGIC)
3948 {
3949 switch (pCoreHdr->cbSize)
3950 {
3951 case BMP_HDR_SIZE_OS21:
3952 {
3953 PBMPOS2COREHDR pOs2Hdr = (PBMPOS2COREHDR)pCoreHdr;
3954 pThisCC->cxLogo = pOs2Hdr->uWidth;
3955 pThisCC->cyLogo = pOs2Hdr->uHeight;
3956 pThisCC->cLogoPlanes = pOs2Hdr->cPlanes;
3957 pThisCC->cLogoBits = pOs2Hdr->cBits;
3958 pThisCC->LogoCompression = BMP_COMPRESSION_TYPE_NONE;
3959 pThisCC->cLogoUsedColors = 0;
3960 break;
3961 }
3962
3963 case BMP_HDR_SIZE_OS22:
3964 {
3965 PBMPOS2COREHDR2 pOs22Hdr = (PBMPOS2COREHDR2)pCoreHdr;
3966 pThisCC->cxLogo = pOs22Hdr->uWidth;
3967 pThisCC->cyLogo = pOs22Hdr->uHeight;
3968 pThisCC->cLogoPlanes = pOs22Hdr->cPlanes;
3969 pThisCC->cLogoBits = pOs22Hdr->cBits;
3970 pThisCC->LogoCompression = pOs22Hdr->enmCompression;
3971 pThisCC->cLogoUsedColors = pOs22Hdr->cClrUsed;
3972 break;
3973 }
3974
3975 case BMP_HDR_SIZE_WIN3X:
3976 pThisCC->cxLogo = pCoreHdr->uWidth;
3977 pThisCC->cyLogo = pCoreHdr->uHeight;
3978 pThisCC->cLogoPlanes = pCoreHdr->cPlanes;
3979 pThisCC->cLogoBits = pCoreHdr->cBits;
3980 pThisCC->LogoCompression = pCoreHdr->enmCompression;
3981 pThisCC->cLogoUsedColors = pCoreHdr->cClrUsed;
3982 break;
3983
3984 default:
3985 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pCoreHdr->cbSize),
3986 VERR_INVALID_PARAMETER);
3987 break;
3988 }
3989
3990 AssertLogRelMsgReturn(pThisCC->cxLogo <= LOGO_MAX_WIDTH && pThisCC->cyLogo <= LOGO_MAX_HEIGHT,
3991 ("Bitmap %ux%u is too big.\n", pThisCC->cxLogo, pThisCC->cyLogo),
3992 VERR_INVALID_PARAMETER);
3993
3994 AssertLogRelMsgReturn(pThisCC->cLogoPlanes == 1,
3995 ("Bitmap planes %u != 1.\n", pThisCC->cLogoPlanes),
3996 VERR_INVALID_PARAMETER);
3997
3998 AssertLogRelMsgReturn(pThisCC->cLogoBits == 4 || pThisCC->cLogoBits == 8 || pThisCC->cLogoBits == 24,
3999 ("Unsupported %u depth.\n", pThisCC->cLogoBits),
4000 VERR_INVALID_PARAMETER);
4001
4002 AssertLogRelMsgReturn(pThisCC->cLogoUsedColors <= 256,
4003 ("Unsupported %u colors.\n", pThisCC->cLogoUsedColors),
4004 VERR_INVALID_PARAMETER);
4005
4006 AssertLogRelMsgReturn(pThisCC->LogoCompression == BMP_COMPRESSION_TYPE_NONE,
4007 ("Unsupported %u compression.\n", pThisCC->LogoCompression),
4008 VERR_INVALID_PARAMETER);
4009
4010 AssertLogRelMsgReturn(pLogoHdr->cbLogo > pFileHdr->offBits,
4011 ("Wrong bitmap data offset %u, cbLogo=%u.\n", pFileHdr->offBits, pLogoHdr->cbLogo),
4012 VERR_INVALID_PARAMETER);
4013
4014 uint32_t const cbFileData = pLogoHdr->cbLogo - pFileHdr->offBits;
4015 uint32_t cbImageData = (uint32_t)pThisCC->cxLogo * pThisCC->cyLogo * pThisCC->cLogoPlanes;
4016 if (pThisCC->cLogoBits == 4)
4017 cbImageData /= 2;
4018 else if (pThisCC->cLogoBits == 24)
4019 cbImageData *= 3;
4020 AssertLogRelMsgReturn(cbImageData <= cbFileData,
4021 ("Wrong BMP header data %u (cbLogo=%u offBits=%u)\n", cbImageData, pFileHdr->offBits, pLogoHdr->cbLogo),
4022 VERR_INVALID_PARAMETER);
4023
4024 AssertLogRelMsgReturn(pLogoHdr->cbLogo == pFileHdr->cbFileSize,
4025 ("Wrong bitmap file size %u, cbLogo=%u.\n", pFileHdr->cbFileSize, pLogoHdr->cbLogo),
4026 VERR_INVALID_PARAMETER);
4027
4028 /*
4029 * Read bitmap palette
4030 */
4031 if (!pThisCC->cLogoUsedColors)
4032 pThisCC->cLogoPalEntries = 1 << (pThisCC->cLogoPlanes * pThisCC->cLogoBits);
4033 else
4034 pThisCC->cLogoPalEntries = pThisCC->cLogoUsedColors;
4035
4036 if (pThisCC->cLogoPalEntries)
4037 {
4038 const uint8_t *pbPal = pThisCC->pbLogo + sizeof(LOGOHDR) + sizeof(BMPFILEHDR) + pCoreHdr->cbSize; /* ASSUMES Size location (safe) */
4039
4040 for (uint16_t i = 0; i < pThisCC->cLogoPalEntries; i++)
4041 {
4042 uint16_t j;
4043 uint32_t u32Pal = 0;
4044
4045 for (j = 0; j < 3; j++)
4046 {
4047 uint8_t b = *pbPal++;
4048 u32Pal <<= 8;
4049 u32Pal |= b;
4050 }
4051
4052 pbPal++; /* skip unused byte */
4053 pThisCC->au32LogoPalette[i] = u32Pal;
4054 }
4055 }
4056
4057 /*
4058 * Bitmap data offset
4059 */
4060 pThisCC->pbLogoBitmap = pThisCC->pbLogo + sizeof(LOGOHDR) + pFileHdr->offBits;
4061 }
4062 else
4063 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
4064
4065 return VINF_SUCCESS;
4066}
4067
4068
4069/**
4070 * Show logo bitmap data.
4071 *
4072 * @param cBits Logo depth.
4073 * @param xLogo Logo X position.
4074 * @param yLogo Logo Y position.
4075 * @param cxLogo Logo width.
4076 * @param cyLogo Logo height.
4077 * @param fInverse True if the bitmask is black on white (only for 1bpp)
4078 * @param iStep Fade in/fade out step.
4079 * @param pu32Palette Palette data.
4080 * @param pbSrc Source buffer.
4081 * @param pbDst Destination buffer.
4082 */
4083static void vbeR3ShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo,
4084 bool fInverse, uint8_t iStep, const uint32_t *pu32Palette, const uint8_t *pbSrc, uint8_t *pbDst)
4085{
4086 uint16_t i;
4087 size_t cbPadBytes = 0;
4088 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
4089 uint16_t cyLeft = cyLogo;
4090
4091 pbDst += xLogo * 4 + yLogo * cbLineDst;
4092
4093 switch (cBits)
4094 {
4095 case 1:
4096 pbDst += cyLogo * cbLineDst;
4097 cbPadBytes = 0;
4098 break;
4099
4100 case 4:
4101 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
4102 cbPadBytes = 0;
4103 else if ((cxLogo % 8) <= 2)
4104 cbPadBytes = 3;
4105 else if ((cxLogo % 8) <= 4)
4106 cbPadBytes = 2;
4107 else
4108 cbPadBytes = 1;
4109 break;
4110
4111 case 8:
4112 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
4113 break;
4114
4115 case 24:
4116 cbPadBytes = cxLogo % 4;
4117 break;
4118 }
4119
4120 uint8_t j = 0, c = 0;
4121
4122 while (cyLeft-- > 0)
4123 {
4124 uint8_t *pbTmpDst = pbDst;
4125
4126 if (cBits != 1)
4127 j = 0;
4128
4129 for (i = 0; i < cxLogo; i++)
4130 {
4131 switch (cBits)
4132 {
4133 case 1:
4134 {
4135 if (!j)
4136 c = *pbSrc++;
4137
4138 if (c & 1)
4139 {
4140 if (fInverse)
4141 {
4142 *pbTmpDst++ = 0;
4143 *pbTmpDst++ = 0;
4144 *pbTmpDst++ = 0;
4145 pbTmpDst++;
4146 }
4147 else
4148 {
4149 uint8_t pix = 0xFF * iStep / LOGO_SHOW_STEPS;
4150 *pbTmpDst++ = pix;
4151 *pbTmpDst++ = pix;
4152 *pbTmpDst++ = pix;
4153 pbTmpDst++;
4154 }
4155 }
4156 else
4157 pbTmpDst += 4;
4158 c >>= 1;
4159 j = (j + 1) % 8;
4160 break;
4161 }
4162
4163 case 4:
4164 {
4165 if (!j)
4166 c = *pbSrc++;
4167
4168 uint8_t pix = (c >> 4) & 0xF;
4169 c <<= 4;
4170
4171 uint32_t u32Pal = pu32Palette[pix];
4172
4173 pix = (u32Pal >> 16) & 0xFF;
4174 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4175 pix = (u32Pal >> 8) & 0xFF;
4176 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4177 pix = u32Pal & 0xFF;
4178 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4179 pbTmpDst++;
4180
4181 j = (j + 1) % 2;
4182 break;
4183 }
4184
4185 case 8:
4186 {
4187 uint32_t u32Pal = pu32Palette[*pbSrc++];
4188
4189 uint8_t pix = (u32Pal >> 16) & 0xFF;
4190 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4191 pix = (u32Pal >> 8) & 0xFF;
4192 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4193 pix = u32Pal & 0xFF;
4194 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4195 pbTmpDst++;
4196 break;
4197 }
4198
4199 case 24:
4200 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4201 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4202 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4203 pbTmpDst++;
4204 break;
4205 }
4206 }
4207
4208 pbDst -= cbLineDst;
4209 pbSrc += cbPadBytes;
4210 }
4211}
4212
4213
4214/**
4215 * @callback_method_impl{FNIOMIOPORTNEWOUT,
4216 * Port I/O Handler for BIOS Logo OUT operations.}
4217 */
4218static DECLCALLBACK(VBOXSTRICTRC)
4219vbeR3IoPortWriteCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
4220{
4221 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4222 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4223 RT_NOREF(pvUser, offPort);
4224
4225 Log(("vbeR3IoPortWriteCmdLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
4226
4227 if (cb == 2)
4228 {
4229 /* Get the logo command */
4230 switch (u32 & 0xFF00)
4231 {
4232 case LOGO_CMD_SET_OFFSET:
4233 pThisCC->offLogoData = u32 & 0xFF;
4234 break;
4235
4236 case LOGO_CMD_SHOW_BMP:
4237 {
4238 uint8_t iStep = u32 & 0xFF;
4239 const uint8_t *pbSrc = pThisCC->pbLogoBitmap;
4240 uint8_t *pbDst;
4241 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
4242 uint32_t offDirty = 0;
4243 uint16_t xLogo = (LOGO_MAX_WIDTH - pThisCC->cxLogo) / 2;
4244 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThisCC->cyLogo) / 2;
4245
4246 /* Check VRAM size */
4247 if (pThis->vram_size < LOGO_MAX_SIZE)
4248 break;
4249
4250 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4251 pbDst = pThisCC->pbVRam + LOGO_MAX_SIZE;
4252 else
4253 pbDst = pThisCC->pbVRam;
4254
4255 /* Clear screen - except on power on... */
4256 if (!pThisCC->fLogoClearScreen)
4257 {
4258 /* Clear vram */
4259 uint32_t *pu32Dst = (uint32_t *)pbDst;
4260 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4261 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4262 *pu32Dst++ = 0;
4263 pThisCC->fLogoClearScreen = true;
4264 }
4265
4266 /* Show the bitmap. */
4267 vbeR3ShowBitmap(pThisCC->cLogoBits, xLogo, yLogo,
4268 pThisCC->cxLogo, pThisCC->cyLogo,
4269 false, iStep, &pThisCC->au32LogoPalette[0],
4270 pbSrc, pbDst);
4271
4272 /* Show the 'Press F12...' text. */
4273 if (pLogoHdr->fu8ShowBootMenu == 2)
4274 vbeR3ShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4275 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4276 pThisCC->fBootMenuInverse, iStep, &pThisCC->au32LogoPalette[0],
4277 &g_abLogoF12BootText[0], pbDst);
4278
4279 /* Blit the offscreen buffer. */
4280 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4281 {
4282 uint32_t *pu32TmpDst = (uint32_t *)pThisCC->pbVRam;
4283 uint32_t *pu32TmpSrc = (uint32_t *)(pThisCC->pbVRam + LOGO_MAX_SIZE);
4284 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4285 {
4286 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4287 *pu32TmpDst++ = *pu32TmpSrc++;
4288 }
4289 }
4290
4291 /* Set the dirty flags. */
4292 while (offDirty <= LOGO_MAX_SIZE)
4293 {
4294 vgaR3MarkDirty(pThis, offDirty);
4295 offDirty += GUEST_PAGE_SIZE;
4296 }
4297 break;
4298 }
4299
4300 default:
4301 Log(("vbeR3IoPortWriteCmdLogo: invalid command %d\n", u32));
4302 pThisCC->LogoCommand = LOGO_CMD_NOP;
4303 break;
4304 }
4305
4306 return VINF_SUCCESS;
4307 }
4308
4309 Log(("vbeR3IoPortWriteCmdLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4310 return VINF_SUCCESS;
4311}
4312
4313
4314/**
4315 * @callback_method_impl{FNIOMIOPORTIN,
4316 * Port I/O Handler for BIOS Logo IN operations.}
4317 */
4318static DECLCALLBACK(VBOXSTRICTRC)
4319vbeR3IoPortReadCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
4320{
4321 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4322 RT_NOREF(pvUser, offPort);
4323
4324 if (pThisCC->offLogoData + cb > pThisCC->cbLogo)
4325 {
4326 Log(("vbeR3IoPortReadCmdLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4327 pThisCC->offLogoData, pThisCC->offLogoData, pThisCC->cbLogo, pThisCC->cbLogo));
4328 return VINF_SUCCESS;
4329 }
4330 RT_UNTRUSTED_VALIDATED_FENCE();
4331
4332 PCRTUINT64U p = (PCRTUINT64U)&pThisCC->pbLogo[pThisCC->offLogoData];
4333 switch (cb)
4334 {
4335 case 1: *pu32 = p->au8[0]; break;
4336 case 2: *pu32 = p->au16[0]; break;
4337 case 4: *pu32 = p->au32[0]; break;
4338 //case 8: *pu32 = p->au64[0]; break;
4339 default: AssertFailed(); break;
4340 }
4341 Log(("vbeR3IoPortReadCmdLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThisCC->offLogoData, pThisCC->offLogoData, cb, cb, pu32));
4342
4343 pThisCC->LogoCommand = LOGO_CMD_NOP;
4344 pThisCC->offLogoData += cb;
4345
4346 return VINF_SUCCESS;
4347}
4348
4349
4350/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4351
4352/**
4353 * @callback_method_impl{FNDBGFHANDLERDEV,
4354 * Dumps several interesting bits of the VGA state that are difficult to
4355 * decode from the registers.}
4356 */
4357static DECLCALLBACK(void) vgaR3InfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4358{
4359 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4360 int is_graph, double_scan;
4361 int w, h, char_height, char_dots;
4362 int val, vfreq_hz, hfreq_hz;
4363 vga_retrace_s *r = &pThis->retrace_state;
4364 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4365 const char *mem_map[] = { "A000-BFFF", "A000-AFFF", "B000-B7FF", "B800-BFFF" };
4366 NOREF(pszArgs);
4367
4368 is_graph = pThis->gr[6] & 1;
4369 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4370 double_scan = pThis->cr[9] >> 7;
4371 pHlp->pfnPrintf(pHlp, "decoding memory at %s\n", mem_map[(pThis->gr[6] >> 2) & 3]);
4372 pHlp->pfnPrintf(pHlp, "Misc status reg. MSR:%02X\n", pThis->msr);
4373 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4374 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4375 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4376 val = pThis->cr[0] + 5;
4377 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4378 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4379 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4380 val = pThis->cr[1] + 1;
4381 w = val * char_dots;
4382 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4383 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4384 h = val;
4385 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4386 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4387 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4388 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4389 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4390 if (!is_graph)
4391 {
4392 uint8_t ch_stride;
4393
4394 ch_stride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4395 val = (pThis->cr[9] & 0x1f) + 1;
4396 char_height = val;
4397 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4398 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4399
4400 uint32_t cbLine;
4401 uint32_t offStart;
4402 uint32_t offCursr;
4403 uint32_t uLineCompareIgn;
4404 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4405 if (!cbLine)
4406 cbLine = 80 * ch_stride;
4407 offStart *= ch_stride;
4408 offCursr = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) * ch_stride;
4409 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4410 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4411 pHlp->pfnPrintf(pHlp, "offCursr: %#x\n", offCursr);
4412 }
4413 if (pThis->fRealRetrace)
4414 {
4415 val = r->hb_start;
4416 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4417 val = r->hb_end;
4418 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4419 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4420 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4421 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4422 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4423 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4424 {
4425 vfreq_hz = 1000000000 / r->frame_ns;
4426 hfreq_hz = 1000000000 / r->h_total_ns;
4427 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4428 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4429 }
4430 }
4431 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4432
4433# ifdef VBOX_WITH_VMSVGA
4434 if (pThis->svga.fEnabled)
4435 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4436 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4437# endif
4438}
4439
4440
4441/**
4442 * Prints a separator line.
4443 *
4444 * @param pHlp Callback functions for doing output.
4445 * @param cCols The number of columns.
4446 * @param pszTitle The title text, NULL if none.
4447 */
4448static void vgaR3InfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4449{
4450 if (pszTitle)
4451 {
4452 size_t cchTitle = strlen(pszTitle);
4453 if (cchTitle + 6 >= cCols)
4454 {
4455 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4456 cCols = 0;
4457 }
4458 else
4459 {
4460 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4461 cCols -= cchLeft + cchTitle + 2;
4462 while (cchLeft-- > 0)
4463 pHlp->pfnPrintf(pHlp, "-");
4464 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4465 }
4466 }
4467
4468 while (cCols-- > 0)
4469 pHlp->pfnPrintf(pHlp, "-");
4470 pHlp->pfnPrintf(pHlp, "\n");
4471}
4472
4473
4474/**
4475 * Worker for vgaR3InfoText.
4476 *
4477 * @param pThis The shared VGA state.
4478 * @param pThisCC The VGA state for ring-3.
4479 * @param pHlp Callback functions for doing output.
4480 * @param offStart Where to start dumping (relative to the VRAM).
4481 * @param cbLine The source line length (aka line_offset).
4482 * @param cCols The number of columns on the screen.
4483 * @param cRows The number of rows to dump.
4484 * @param iScrBegin The row at which the current screen output starts.
4485 * @param iScrEnd The row at which the current screen output end
4486 * (exclusive).
4487 */
4488static void vgaR3InfoTextWorker(PVGASTATE pThis, PVGASTATER3 pThisCC, PCDBGFINFOHLP pHlp,
4489 uint32_t offStart, uint32_t cbLine,
4490 uint32_t cCols, uint32_t cRows,
4491 uint32_t iScrBegin, uint32_t iScrEnd)
4492{
4493 /* Title, */
4494 char szTitle[32];
4495 if (iScrBegin || iScrEnd < cRows)
4496 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4497 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4498 else
4499 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4500
4501 /* Do the dumping. */
4502 uint8_t const *pbSrcOuter = pThisCC->pbVRam + offStart;
4503 uint8_t const cStride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4504 uint32_t iRow;
4505 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4506 {
4507 if ((uintptr_t)(pbSrcOuter + cbLine - pThisCC->pbVRam) > pThis->vram_size) {
4508 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4509 break;
4510 }
4511
4512 if (iRow == 0)
4513 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4514 else if (iRow == iScrBegin)
4515 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4516 else if (iRow == iScrEnd)
4517 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4518
4519 uint8_t const *pbSrc = pbSrcOuter;
4520 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4521 {
4522 if (RT_C_IS_PRINT(*pbSrc))
4523 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4524 else
4525 pHlp->pfnPrintf(pHlp, ".");
4526 pbSrc += cStride; /* chars are spaced 8 or sometimes 4 bytes apart */
4527 }
4528 pHlp->pfnPrintf(pHlp, "\n");
4529 }
4530
4531 /* Final separator. */
4532 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4533}
4534
4535
4536/**
4537 * @callback_method_impl{FNDBGFHANDLERDEV,
4538 * Dumps VGA memory formatted as ASCII text\, no attributes. Only looks at
4539 * the first page.}
4540 */
4541static DECLCALLBACK(void) vgaR3InfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4542{
4543 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4544 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4545
4546 /*
4547 * Parse args.
4548 */
4549 bool fAll = true;
4550 if (pszArgs && *pszArgs)
4551 {
4552 if (!strcmp(pszArgs, "all"))
4553 fAll = true;
4554 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4555 fAll = false;
4556 else
4557 {
4558 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4559 return;
4560 }
4561 }
4562
4563 /*
4564 * Check that we're in text mode and that the VRAM is accessible.
4565 */
4566 if (!(pThis->gr[6] & 1))
4567 {
4568 uint8_t *pbSrc = pThisCC->pbVRam;
4569 if (pbSrc)
4570 {
4571 /*
4572 * Figure out the display size and where the text is.
4573 *
4574 * Note! We're cutting quite a few corners here and this code could
4575 * do with some brushing up. Dumping from the start of the
4576 * frame buffer is done intentionally so that we're more
4577 * likely to obtain the full scrollback of a linux panic.
4578 * windbg> .printf "------ start -----\n"; .for (r $t0 = 0; @$t0 < 25; r $t0 = @$t0 + 1) { .for (r $t1 = 0; @$t1 < 80; r $t1 = @$t1 + 1) { .printf "%c", by( (@$t0 * 80 + @$t1) * 8 + 100f0000) }; .printf "\n" }; .printf "------ end -----\n";
4579 */
4580 uint32_t cbLine;
4581 uint32_t offStart;
4582 uint32_t uLineCompareIgn;
4583 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4584 if (!cbLine)
4585 cbLine = 80 * 8;
4586 offStart *= 8;
4587
4588 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4589 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4590 uint32_t uDblScan = pThis->cr[9] >> 7;
4591 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4592 if (cScrRows < 25)
4593 cScrRows = 25;
4594 uint32_t iScrBegin = offStart / cbLine;
4595 uint32_t cRows = iScrBegin + cScrRows;
4596 uint32_t cCols = cbLine / 8;
4597
4598 if (fAll)
4599 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart - iScrBegin * cbLine, cbLine,
4600 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4601 else
4602 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4603 }
4604 else
4605 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4606 }
4607 else
4608 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4609}
4610
4611
4612/**
4613 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4614 */
4615static DECLCALLBACK(void) vgaR3InfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4616{
4617 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4618 NOREF(pszArgs);
4619
4620 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4621 Assert(sizeof(pThis->sr) >= 8);
4622 for (unsigned i = 0; i < 8; ++i)
4623 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4624 pHlp->pfnPrintf(pHlp, "\n");
4625}
4626
4627
4628/**
4629 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4630 */
4631static DECLCALLBACK(void) vgaR3InfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4632{
4633 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4634 unsigned i;
4635 NOREF(pszArgs);
4636
4637 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4638 Assert(sizeof(pThis->cr) >= 24);
4639 for (i = 0; i < 10; ++i)
4640 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4641 pHlp->pfnPrintf(pHlp, "\n");
4642 for (i = 10; i < 20; ++i)
4643 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4644 pHlp->pfnPrintf(pHlp, "\n");
4645 for (i = 20; i < 25; ++i)
4646 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4647 pHlp->pfnPrintf(pHlp, "\n");
4648}
4649
4650
4651/**
4652 * @callback_method_impl{FNDBGFHANDLERDEV,
4653 * Dumps VGA Graphics Controller registers.}
4654 */
4655static DECLCALLBACK(void) vgaR3InfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4656{
4657 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4658 NOREF(pszArgs);
4659
4660 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4661 Assert(sizeof(pThis->gr) >= 9);
4662 for (unsigned i = 0; i < 9; ++i)
4663 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4664 pHlp->pfnPrintf(pHlp, "\n");
4665}
4666
4667
4668/**
4669 * @callback_method_impl{FNDBGFHANDLERDEV,
4670 * Dumps VGA Attribute Controller registers.}
4671 */
4672static DECLCALLBACK(void) vgaR3InfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4673{
4674 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4675 unsigned i;
4676 NOREF(pszArgs);
4677
4678 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4679 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4680 Assert(sizeof(pThis->ar) >= 0x14);
4681 pHlp->pfnPrintf(pHlp, " Palette:");
4682 for (i = 0; i < 0x10; ++i)
4683 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4684 pHlp->pfnPrintf(pHlp, "\n");
4685 for (i = 0x10; i <= 0x14; ++i)
4686 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4687 pHlp->pfnPrintf(pHlp, "\n");
4688}
4689
4690
4691/**
4692 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4693 */
4694static DECLCALLBACK(void) vgaR3InfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4695{
4696 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4697 NOREF(pszArgs);
4698
4699 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4700 for (unsigned i = 0; i < 0x100; ++i)
4701 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4702 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4703}
4704
4705
4706/**
4707 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4708 */
4709static DECLCALLBACK(void) vgaR3InfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4710{
4711 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4712 NOREF(pszArgs);
4713
4714 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4715 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4716 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4717 else
4718 {
4719 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4720 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4721 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4722 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4723 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4724 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4725 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4726 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4727 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4728 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4729 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4730 pHlp->pfnPrintf(pHlp, " DAC: %d-bit\n", pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_8BIT_DAC ? 8 : 6);
4731 }
4732}
4733
4734
4735/**
4736 * @callback_method_impl{FNDBGFHANDLERDEV,
4737 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4738 * in human-readable form.}
4739 */
4740static DECLCALLBACK(void) vgaR3InfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4741{
4742 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4743 NOREF(pszArgs);
4744
4745 unsigned val1 = (pThis->gr[5] >> 3) & 1;
4746 unsigned val2 = pThis->gr[5] & 3;
4747 pHlp->pfnPrintf(pHlp, "read mode : %u write mode: %u\n", val1, val2);
4748 val1 = pThis->gr[0];
4749 val2 = pThis->gr[1];
4750 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4751 val1 = pThis->gr[2];
4752 val2 = pThis->gr[4] & 3;
4753 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %u\n", val1, val2);
4754 val1 = pThis->gr[3] & 7;
4755 val2 = (pThis->gr[3] >> 3) & 3;
4756 pHlp->pfnPrintf(pHlp, "rotate : %u function : %u\n", val1, val2);
4757 val1 = pThis->gr[7];
4758 val2 = pThis->gr[8];
4759 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4760 val1 = pThis->sr[2];
4761 val2 = pThis->sr[4] & 8;
4762 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4763}
4764
4765
4766/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4767
4768/**
4769 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4770 */
4771static DECLCALLBACK(void *) vgaR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4772{
4773 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IBase);
4774 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4775 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThisCC->IPort);
4776# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
4777 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThisCC->IVBVACallbacks);
4778# endif
4779 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4780 return NULL;
4781}
4782
4783
4784/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4785
4786/**
4787 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4788 */
4789static DECLCALLBACK(int) vgaR3PortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4790{
4791 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, ILeds);
4792 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4793 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4794 switch (iLUN)
4795 {
4796 /* LUN #0 is the only one for which we have a status LED. */
4797 case 0:
4798 {
4799 *ppLed = &pThis->Led3D;
4800 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4801 return VINF_SUCCESS;
4802 }
4803
4804 default:
4805 AssertMsgFailed(("Invalid LUN #%u\n", iLUN));
4806 return VERR_PDM_NO_SUCH_LUN;
4807 }
4808}
4809
4810
4811/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4812
4813/**
4814 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnResize}
4815 */
4816static DECLCALLBACK(int) vgaR3DummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t cBits, void *pvVRAM,
4817 uint32_t cbLine, uint32_t cx, uint32_t cy)
4818{
4819 RT_NOREF(pInterface, cBits, pvVRAM, cbLine, cx, cy);
4820 return VINF_SUCCESS;
4821}
4822
4823
4824/**
4825 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnUpdateRect}
4826 */
4827static DECLCALLBACK(void) vgaR3DummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4828{
4829 RT_NOREF(pInterface, x, y, cx, cy);
4830}
4831
4832
4833/**
4834 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnRefresh}
4835 */
4836static DECLCALLBACK(void) vgaR3DummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4837{
4838 NOREF(pInterface);
4839}
4840
4841
4842/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4843
4844/**
4845 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
4846 */
4847static DECLCALLBACK(int) vgaR3PortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4848{
4849 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4850 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4851 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4852
4853 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4854 AssertRCReturn(rc, rc);
4855
4856# ifdef VBOX_WITH_VMSVGA
4857 if ( pThis->svga.fEnabled
4858 && !pThis->svga.fTraces)
4859 {
4860 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4861 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4862 return VINF_SUCCESS;
4863 }
4864#endif
4865
4866# ifndef VBOX_WITH_HGSMI
4867 /* This should be called only in non VBVA mode. */
4868# else
4869 if (VBVAUpdateDisplay(pThis, pThisCC) == VINF_SUCCESS)
4870 {
4871 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4872 return VINF_SUCCESS;
4873 }
4874# endif /* VBOX_WITH_HGSMI */
4875
4876 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4877
4878 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4879 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4880
4881 if (pThis->bmPageRemappedVGA != 0)
4882 {
4883 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4884 STAM_COUNTER_INC(&pThis->StatMapReset);
4885 vgaResetRemapped(pThis);
4886 }
4887
4888 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, false /*fFailOnResize*/, true /*reset_dirty*/,
4889 pThisCC->pDrv, &pThis->graphic_mode);
4890 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4891 return rc;
4892}
4893
4894
4895/**
4896 * Internal vgaR3PortUpdateDisplayAll worker called under pThis->CritSect.
4897 */
4898static int vgaR3UpdateDisplayAllInternal(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool fFailOnResize)
4899{
4900# ifdef VBOX_WITH_VMSVGA
4901 if ( !pThis->svga.fEnabled
4902 || pThis->svga.fTraces)
4903# endif
4904 {
4905 /* Update the dirty bits. */
4906 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4907 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4908 }
4909
4910 if (pThis->bmPageRemappedVGA != 0)
4911 {
4912 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4913 STAM_COUNTER_INC(&pThis->StatMapReset);
4914 vgaResetRemapped(pThis);
4915 }
4916
4917 pThis->graphic_mode = -1; /* force full update */
4918
4919 return vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, true /*fUpdateAll*/, fFailOnResize,
4920 true /*reset_dirty*/, pThisCC->pDrv, &pThis->graphic_mode);
4921}
4922
4923
4924/**
4925 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
4926 */
4927static DECLCALLBACK(int) vgaR3PortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4928{
4929 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4930 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4931 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4932
4933 /* This is called both in VBVA mode and normal modes. */
4934
4935# ifdef DEBUG_sunlover
4936 LogFlow(("vgaR3PortUpdateDisplayAll\n"));
4937# endif /* DEBUG_sunlover */
4938
4939 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4940 AssertRCReturn(rc, rc);
4941
4942 rc = vgaR3UpdateDisplayAllInternal(pDevIns, pThis, pThisCC, fFailOnResize);
4943
4944 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4945 return rc;
4946}
4947
4948
4949/**
4950 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
4951 */
4952static DECLCALLBACK(int) vgaR3PortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4953{
4954 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4955 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4956 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4957
4958 /*
4959 * Update the interval, notify the VMSVGA FIFO thread if sleeping,
4960 * then restart or stop the timer.
4961 */
4962 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
4963
4964# ifdef VBOX_WITH_VMSVGA
4965 if (pThis->svga.fFIFOThreadSleeping)
4966 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
4967# endif
4968
4969 if (cMilliesInterval)
4970 return PDMDevHlpTimerSetMillies(pDevIns, pThis->hRefreshTimer, cMilliesInterval);
4971 return PDMDevHlpTimerStop(pDevIns, pThis->hRefreshTimer);
4972}
4973
4974
4975/**
4976 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
4977 */
4978static DECLCALLBACK(int) vgaR3PortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4979{
4980 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4981 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4982 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4983
4984 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
4985
4986 *pcBits = vgaR3GetBpp(pThis);
4987 if (pcx)
4988 *pcx = pThis->last_scr_width;
4989 if (pcy)
4990 *pcy = pThis->last_scr_height;
4991 return VINF_SUCCESS;
4992}
4993
4994
4995/**
4996 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
4997 */
4998static DECLCALLBACK(int) vgaR3PortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
4999 uint32_t *pcx, uint32_t *pcy)
5000{
5001 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5002 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5003 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5004 PDMDEV_ASSERT_EMT(pDevIns);
5005
5006 LogFlow(("vgaR3PortTakeScreenshot: ppbData=%p pcbData=%p pcx=%p pcy=%p\n", ppbData, pcbData, pcx, pcy));
5007
5008 /*
5009 * Validate input.
5010 */
5011 if (!RT_VALID_PTR(ppbData) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
5012 return VERR_INVALID_PARAMETER;
5013
5014 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5015 AssertRCReturn(rc, rc);
5016
5017 /*
5018 * Get screenshot. This function will fail if a resize is required.
5019 * So there is not need to do a 'vgaR3UpdateDisplayAllInternal' before taking screenshot.
5020 */
5021
5022 /*
5023 * Allocate the buffer for 32 bits per pixel bitmap
5024 *
5025 * Note! The size can't be zero or greater than the size of the VRAM.
5026 * Inconsistent VGA device state can cause the incorrect size values.
5027 */
5028 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
5029 if (cbRequired && cbRequired <= pThis->vram_size)
5030 {
5031 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbRequired);
5032 if (pbData != NULL)
5033 {
5034 /*
5035 * Only 3 methods, assigned below, will be called during the screenshot update.
5036 * All other are already set to NULL.
5037 */
5038 /* The display connector interface is temporarily replaced with the fake one. */
5039 PDMIDISPLAYCONNECTOR Connector;
5040 RT_ZERO(Connector);
5041 Connector.pbData = pbData;
5042 Connector.cBits = 32;
5043 Connector.cx = pThis->last_scr_width;
5044 Connector.cy = pThis->last_scr_height;
5045 Connector.cbScanline = Connector.cx * 4;
5046 Connector.pfnRefresh = vgaR3DummyRefresh;
5047 Connector.pfnResize = vgaR3DummyResize;
5048 Connector.pfnUpdateRect = vgaR3DummyUpdateRect;
5049
5050 int32_t cur_graphic_mode = -1;
5051
5052 bool fSavedRenderVRAM = pThis->fRenderVRAM;
5053 pThis->fRenderVRAM = true;
5054
5055 /*
5056 * Take the screenshot.
5057 *
5058 * The second parameter is 'false' because the current display state is being rendered to an
5059 * external buffer using a fake connector. That is if display is blanked, we expect a black
5060 * screen in the external buffer.
5061 * If there is a pending resize, the function will fail.
5062 */
5063 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, true /*fFailOnResize*/,
5064 false /*reset_dirty*/, &Connector, &cur_graphic_mode);
5065
5066 pThis->fRenderVRAM = fSavedRenderVRAM;
5067
5068 if (rc == VINF_SUCCESS)
5069 {
5070 /*
5071 * Return the result.
5072 */
5073 *ppbData = pbData;
5074 *pcbData = cbRequired;
5075 *pcx = Connector.cx;
5076 *pcy = Connector.cy;
5077 }
5078 else
5079 {
5080 /* If we do not return a success, then the data buffer must be freed. */
5081 RTMemFree(pbData);
5082 if (RT_SUCCESS_NP(rc))
5083 {
5084 AssertMsgFailed(("%Rrc\n", rc));
5085 rc = VERR_INTERNAL_ERROR_5;
5086 }
5087 }
5088 }
5089 else
5090 rc = VERR_NO_MEMORY;
5091 }
5092 else
5093 rc = VERR_NOT_SUPPORTED;
5094
5095 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5096
5097 LogFlow(("vgaR3PortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
5098 return rc;
5099}
5100
5101
5102/**
5103 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
5104 */
5105static DECLCALLBACK(void) vgaR3PortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
5106{
5107 NOREF(pInterface);
5108
5109 LogFlow(("vgaR3PortFreeScreenshot: pbData=%p\n", pbData));
5110
5111 RTMemFree(pbData);
5112}
5113
5114
5115/**
5116 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
5117 */
5118static DECLCALLBACK(int) vgaR3PortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
5119 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
5120{
5121 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5122 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5123 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5124 PDMDEV_ASSERT_EMT(pDevIns);
5125 LogFlow(("vgaR3PortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
5126
5127 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5128 AssertRCReturn(rc, rc);
5129
5130 /*
5131 * Validate input.
5132 */
5133 if ( pvData
5134 && x < pThisCC->pDrv->cx
5135 && cx <= pThisCC->pDrv->cx
5136 && cx + x <= pThisCC->pDrv->cx
5137 && y < pThisCC->pDrv->cy
5138 && cy <= pThisCC->pDrv->cy
5139 && cy + y <= pThisCC->pDrv->cy)
5140 {
5141 /*
5142 * Determine bytes per pixel in the destination buffer.
5143 */
5144 size_t cbPixelDst = 0;
5145 switch (pThisCC->pDrv->cBits)
5146 {
5147 case 8:
5148 cbPixelDst = 1;
5149 break;
5150 case 15:
5151 case 16:
5152 cbPixelDst = 2;
5153 break;
5154 case 24:
5155 cbPixelDst = 3;
5156 break;
5157 case 32:
5158 cbPixelDst = 4;
5159 break;
5160 default:
5161 rc = VERR_INVALID_PARAMETER;
5162 break;
5163 }
5164 if (RT_SUCCESS(rc))
5165 {
5166 /*
5167 * The blitting loop.
5168 */
5169 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
5170 uint8_t *pbSrc = (uint8_t *)pvData;
5171 size_t cbLineDst = pThisCC->pDrv->cbScanline;
5172 uint8_t *pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5173 uint32_t cyLeft = cy;
5174 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5175 Assert(pfnVgaDrawLine);
5176 while (cyLeft-- > 0)
5177 {
5178 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5179 pbDst += cbLineDst;
5180 pbSrc += cbLineSrc;
5181 }
5182
5183 /*
5184 * Invalidate the area.
5185 */
5186 pThisCC->pDrv->pfnUpdateRect(pThisCC->pDrv, x, y, cx, cy);
5187 }
5188 }
5189 else
5190 rc = VERR_INVALID_PARAMETER;
5191
5192 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5193
5194 LogFlow(("vgaR3PortDisplayBlt: returns %Rrc\n", rc));
5195 return rc;
5196}
5197
5198
5199/**
5200 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
5201 */
5202static DECLCALLBACK(void) vgaR3PortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
5203{
5204 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5205 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5206 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5207 uint32_t v;
5208
5209 uint32_t cbPixelDst;
5210 uint32_t cbLineDst;
5211 uint8_t *pbDst;
5212
5213 uint32_t cbPixelSrc;
5214 uint32_t cbLineSrc;
5215 uint8_t *pbSrc;
5216
5217
5218# ifdef DEBUG_sunlover
5219 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d\n", x, y, cx, cy));
5220# endif /* DEBUG_sunlover */
5221
5222 Assert(pInterface);
5223
5224 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5225 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
5226
5227 /* Check if there is something to do at all. */
5228 if (!pThis->fRenderVRAM)
5229 {
5230 /* The framebuffer uses the guest VRAM directly. */
5231# ifdef DEBUG_sunlover
5232 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do fRender is false.\n"));
5233# endif /* DEBUG_sunlover */
5234 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5235 return;
5236 }
5237
5238 Assert(pThisCC->pDrv);
5239 Assert(pThisCC->pDrv->pbData);
5240
5241 /* Correct negative x and y coordinates. */
5242 if (x < 0)
5243 {
5244 x += cx; /* Compute xRight which is also the new width. */
5245 cx = (x < 0) ? 0 : x;
5246 x = 0;
5247 }
5248
5249 if (y < 0)
5250 {
5251 y += cy; /* Compute yBottom, which is also the new height. */
5252 cy = (y < 0) ? 0 : y;
5253 y = 0;
5254 }
5255
5256 /* Also check if coords are greater than the display resolution. */
5257 if (x + cx > pThisCC->pDrv->cx)
5258 {
5259 // x < 0 is not possible here
5260 cx = pThisCC->pDrv->cx > (uint32_t)x? pThisCC->pDrv->cx - x: 0;
5261 }
5262
5263 if (y + cy > pThisCC->pDrv->cy)
5264 {
5265 // y < 0 is not possible here
5266 cy = pThisCC->pDrv->cy > (uint32_t)y? pThisCC->pDrv->cy - y: 0;
5267 }
5268
5269# ifdef DEBUG_sunlover
5270 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, cx, cy));
5271# endif
5272
5273 /* Check if there is something to do at all. */
5274 if (cx == 0 || cy == 0)
5275 {
5276 /* Empty rectangle. */
5277# ifdef DEBUG_sunlover
5278 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do: %dx%d\n", cx, cy));
5279#endif
5280 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5281 return;
5282 }
5283
5284 /** @todo This method should be made universal and not only for VBVA.
5285 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5286 * changed.
5287 */
5288
5289 /* Choose the rendering function. */
5290 switch(pThisCC->get_bpp(pThis))
5291 {
5292 default:
5293 case 0:
5294 /* A LFB mode is already disabled, but the callback is still called
5295 * by Display because VBVA buffer is being flushed.
5296 * Nothing to do, just return.
5297 */
5298 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5299 return;
5300 case 8:
5301 v = VGA_DRAW_LINE8;
5302 break;
5303 case 15:
5304 v = VGA_DRAW_LINE15;
5305 break;
5306 case 16:
5307 v = VGA_DRAW_LINE16;
5308 break;
5309 case 24:
5310 v = VGA_DRAW_LINE24;
5311 break;
5312 case 32:
5313 v = VGA_DRAW_LINE32;
5314 break;
5315 }
5316
5317 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5318
5319 /* Compute source and destination addresses and pitches. */
5320 cbPixelDst = (pThisCC->pDrv->cBits + 7) / 8;
5321 cbLineDst = pThisCC->pDrv->cbScanline;
5322 pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5323
5324 cbPixelSrc = (pThisCC->get_bpp(pThis) + 7) / 8;
5325 uint32_t offSrc, u32Dummy;
5326 pThisCC->get_offsets(pThis, &cbLineSrc, &offSrc, &u32Dummy);
5327
5328 /* Assume that rendering is performed only on visible part of VRAM.
5329 * This is true because coordinates were verified.
5330 */
5331 pbSrc = pThisCC->pbVRam;
5332 pbSrc += offSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5333
5334 /* Render VRAM to framebuffer. */
5335
5336# ifdef DEBUG_sunlover
5337 LogFlow(("vgaR3PortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDst, cbLineDst, cbPixelDst, pbSrc, cbLineSrc, cbPixelSrc));
5338# endif
5339
5340 while (cy-- > 0)
5341 {
5342 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5343 pbDst += cbLineDst;
5344 pbSrc += cbLineSrc;
5345 }
5346
5347 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5348# ifdef DEBUG_sunlover
5349 LogFlow(("vgaR3PortUpdateDisplayRect: completed.\n"));
5350# endif
5351}
5352
5353
5354/**
5355 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
5356 */
5357static DECLCALLBACK(int)
5358vgaR3PortCopyRect(PPDMIDISPLAYPORT pInterface,
5359 uint32_t cx, uint32_t cy,
5360 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
5361 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
5362 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
5363 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
5364{
5365 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5366 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5367 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5368 uint32_t v;
5369
5370# ifdef DEBUG_sunlover
5371 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, cx, cy, xDst, yDst));
5372# endif
5373
5374 Assert(pInterface);
5375 Assert(pThisCC->pDrv);
5376
5377 int32_t xSrcCorrected = xSrc;
5378 int32_t ySrcCorrected = ySrc;
5379 uint32_t cxCorrected = cx;
5380 uint32_t cyCorrected = cy;
5381
5382 /* Correct source coordinates to be within the source bitmap. */
5383 if (xSrcCorrected < 0)
5384 {
5385 xSrcCorrected += cxCorrected; /* Compute xRight which is also the new width. */
5386 cxCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5387 xSrcCorrected = 0;
5388 }
5389
5390 if (ySrcCorrected < 0)
5391 {
5392 ySrcCorrected += cyCorrected; /* Compute yBottom, which is also the new height. */
5393 cyCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5394 ySrcCorrected = 0;
5395 }
5396
5397 /* Also check if coords are greater than the display resolution. */
5398 if (xSrcCorrected + cxCorrected > cxSrc)
5399 {
5400 /* xSrcCorrected < 0 is not possible here */
5401 cxCorrected = cxSrc > (uint32_t)xSrcCorrected ? cxSrc - xSrcCorrected : 0;
5402 }
5403
5404 if (ySrcCorrected + cyCorrected > cySrc)
5405 {
5406 /* y < 0 is not possible here */
5407 cyCorrected = cySrc > (uint32_t)ySrcCorrected ? cySrc - ySrcCorrected : 0;
5408 }
5409
5410# ifdef DEBUG_sunlover
5411 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, cxCorrected, cyCorrected));
5412# endif
5413
5414 /* Check if there is something to do at all. */
5415 if (cxCorrected == 0 || cyCorrected == 0)
5416 {
5417 /* Empty rectangle. */
5418# ifdef DEBUG_sunlover
5419 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", cxCorrected, cyCorrected));
5420# endif
5421 return VINF_SUCCESS;
5422 }
5423
5424 /* Check that the corrected source rectangle is within the destination.
5425 * Note: source rectangle is adjusted, but the target must be large enough.
5426 */
5427 if ( xDst < 0
5428 || yDst < 0
5429 || xDst + cxCorrected > cxDst
5430 || yDst + cyCorrected > cyDst)
5431 {
5432 return VERR_INVALID_PARAMETER;
5433 }
5434
5435 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5436 AssertRCReturn(rc, rc);
5437
5438 /* This method only works if the VGA device is in a VBE mode or not paused VBVA mode.
5439 * VGA modes are reported to the caller by returning VERR_INVALID_STATE.
5440 *
5441 * If VBE_DISPI_ENABLED is set, then it is a VBE or VBE compatible VBVA mode. Both of them can be handled.
5442 *
5443 * If VBE_DISPI_ENABLED is clear, then it is either a VGA mode or a VBVA mode set by guest additions
5444 * which have VBVACAPS_USE_VBVA_ONLY capability.
5445 * When VBE_DISPI_ENABLED is being cleared and VBVACAPS_USE_VBVA_ONLY is not set (i.e. guest wants a VGA mode),
5446 * then VBVAOnVBEChanged makes sure that VBVA is paused.
5447 * That is a not paused VBVA means that the video mode can be handled even if VBE_DISPI_ENABLED is clear.
5448 */
5449 if ( (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0
5450 && VBVAIsPaused(pThisCC)
5451# ifdef VBOX_WITH_VMSVGA
5452 && !pThis->svga.fEnabled
5453# endif
5454 )
5455 {
5456 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5457 return VERR_INVALID_STATE;
5458 }
5459
5460 /* Choose the rendering function. */
5461 switch (cSrcBitsPerPixel)
5462 {
5463 default:
5464 case 0:
5465 /* Nothing to do, just return. */
5466 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5467 return VINF_SUCCESS;
5468 case 8:
5469 v = VGA_DRAW_LINE8;
5470 break;
5471 case 15:
5472 v = VGA_DRAW_LINE15;
5473 break;
5474 case 16:
5475 v = VGA_DRAW_LINE16;
5476 break;
5477 case 24:
5478 v = VGA_DRAW_LINE24;
5479 break;
5480 case 32:
5481 v = VGA_DRAW_LINE32;
5482 break;
5483 }
5484
5485 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(cDstBitsPerPixel)];
5486
5487 /* Compute source and destination addresses and pitches. */
5488 uint32_t cbPixelDst = (cDstBitsPerPixel + 7) / 8;
5489 uint32_t cbLineDst = cbDstLine;
5490 uint8_t *pbDstCur = pbDst + yDst * cbLineDst + xDst * cbPixelDst;
5491
5492 uint32_t cbPixelSrc = (cSrcBitsPerPixel + 7) / 8;
5493 uint32_t cbLineSrc = cbSrcLine;
5494 const uint8_t *pbSrcCur = pbSrc + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5495
5496# ifdef DEBUG_sunlover
5497 LogFlow(("vgaR3PortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDstCur, cbLineDst, cbPixelDst, pbSrcCur, cbLineSrc, cbPixelSrc));
5498# endif
5499
5500 while (cyCorrected-- > 0)
5501 {
5502 pfnVgaDrawLine(pThis, pThisCC, pbDstCur, pbSrcCur, cxCorrected);
5503 pbDstCur += cbLineDst;
5504 pbSrcCur += cbLineSrc;
5505 }
5506
5507 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5508# ifdef DEBUG_sunlover
5509 LogFlow(("vgaR3PortCopyRect: completed.\n"));
5510# endif
5511 return VINF_SUCCESS;
5512}
5513
5514
5515/**
5516 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
5517 */
5518static DECLCALLBACK(void) vgaR3PortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5519{
5520 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5521 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5522 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5523
5524 LogFlow(("vgaR3PortSetRenderVRAM: fRender = %d\n", fRender));
5525
5526 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5527 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
5528
5529 pThis->fRenderVRAM = fRender;
5530
5531 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5532}
5533
5534
5535/**
5536 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
5537 */
5538static DECLCALLBACK(void) vgaR3PortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
5539 bool fSupportsMoveCursor)
5540{
5541 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
5542}
5543
5544
5545/**
5546 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
5547 */
5548static DECLCALLBACK(void) vgaR3PortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
5549{
5550 RT_NOREF(pInterface, x, y, fOutOfRange);
5551}
5552
5553
5554/**
5555 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
5556 */
5557static DECLCALLBACK(void) vgaR3TimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
5558{
5559 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5560 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5561 RT_NOREF(pvUser);
5562
5563 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5564 VBVARaiseIrq(pDevIns, pThis, pThisCC, HGSMIHOSTFLAGS_VSYNC);
5565
5566 if (pThisCC->pDrv)
5567 pThisCC->pDrv->pfnRefresh(pThisCC->pDrv);
5568
5569 if (pThis->cMilliesRefreshInterval)
5570 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
5571
5572# ifdef VBOX_WITH_VIDEOHWACCEL
5573 vbvaTimerCb(pDevIns, pThis, pThisCC);
5574# endif
5575
5576# ifdef VBOX_WITH_VMSVGA
5577 /*
5578 * Call the VMSVGA FIFO poller/watchdog so we can wake up the thread if
5579 * there is work to be done.
5580 */
5581 if (pThis->svga.fFIFOThreadSleeping && pThis->svga.fEnabled && pThis->svga.fConfigured)
5582 vmsvgaR3FifoWatchdogTimer(pDevIns, pThis, pThisCC);
5583# endif
5584}
5585
5586# ifdef VBOX_WITH_VMSVGA
5587
5588/**
5589 * Helper for VMSVGA.
5590 */
5591int vgaR3RegisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis, uint64_t cbFrameBuffer)
5592{
5593 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5594 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5595 RT_NOREF(cbFrameBuffer);
5596 AssertRC(rc);
5597 return rc;
5598}
5599
5600
5601/**
5602 * Helper for VMSVGA.
5603 */
5604int vgaR3UnregisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis)
5605{
5606 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5607 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, false /*fEnabled*/);
5608 AssertRC(rc);
5609 return rc;
5610}
5611
5612# endif /* VBOX_WITH_VMSVGA */
5613
5614
5615/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5616
5617/**
5618 * @callback_method_impl{FNPCIIOREGIONMAP, Mapping/unmapping the VRAM MMI2 region}
5619 */
5620static DECLCALLBACK(int) vgaR3PciIORegionVRamMapUnmap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5621 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
5622{
5623 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5624 Log(("vgaR3PciIORegionVRamMapUnmap: iRegion=%d GCPhysAddress=%RGp cb=%RGp enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5625 RT_NOREF(pPciDev, cb);
5626
5627# ifdef VBOX_WITH_VMSVGA
5628 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5629 && ( enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH
5630 || (enmType == PCI_ADDRESS_SPACE_MEM && pThis->fVMSVGAEnabled && pThis->fStateLoaded)), VERR_INTERNAL_ERROR);
5631# else
5632 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5633 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5634# endif
5635
5636 Assert(pPciDev == pDevIns->apPciDevs[0]);
5637
5638 /* Note! We cannot take the device lock here as that would create a lock order
5639 problem as the caller has taken the PDM lock prior to calling us. If
5640 we did, we will get trouble later when raising interrupts while owning
5641 the device lock (e.g. vmsvgaR3FifoLoop). */
5642
5643 int rc;
5644 if (GCPhysAddress != NIL_RTGCPHYS)
5645 {
5646 /*
5647 * Make sure the dirty page tracking state is up to date before mapping it.
5648 */
5649# ifdef VBOX_WITH_VMSVGA
5650 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam,
5651 !pThis->svga.fEnabled ||(pThis->svga.fEnabled && pThis->svga.fVRAMTracking));
5652# else
5653 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5654# endif
5655 AssertLogRelRC(rc);
5656
5657 /*
5658 * Map the VRAM.
5659 */
5660 rc = PDMDevHlpMmio2Map(pDevIns, pThis->hMmio2VRam, GCPhysAddress);
5661 AssertLogRelRC(rc);
5662 if (RT_SUCCESS(rc))
5663 {
5664 pThis->GCPhysVRAM = GCPhysAddress;
5665 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5666
5667 rc = VINF_PCI_MAPPING_DONE; /* caller doesn't care about any other status, so no problem overwriting error here */
5668 }
5669 }
5670 else
5671 {
5672 /*
5673 * Unmapping of the VRAM in progress (caller will do that).
5674 */
5675 Assert(pThis->GCPhysVRAM);
5676 pThis->GCPhysVRAM = 0;
5677 rc = VINF_SUCCESS;
5678 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5679 }
5680 return rc;
5681}
5682
5683
5684# ifdef VBOX_WITH_VMSVGA /* Currently not needed in the non-VMSVGA mode, but keeping it flexible for later. */
5685/**
5686 * @interface_method_impl{PDMPCIDEV,pfnRegionLoadChangeHookR3}
5687 */
5688static DECLCALLBACK(int) vgaR3PciRegionLoadChangeHook(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5689 uint64_t cbRegion, PCIADDRESSSPACE enmType,
5690 PFNPCIIOREGIONOLDSETTER pfnOldSetter, PFNPCIIOREGIONSWAP pfnSwapRegions)
5691{
5692 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5693
5694# ifdef VBOX_WITH_VMSVGA
5695 if (pThis->fVMSVGAEnabled)
5696 {
5697 /*
5698 * We messed up BAR order for the hybrid devices in 6.0 (see #9359).
5699 * It should have been compatible with the VBox VGA device and had the
5700 * VRAM region first and I/O second, but instead the I/O region ended
5701 * up first and VRAM second like the VMSVGA device.
5702 *
5703 * So, we have to detect that here and reconfigure the memory regions.
5704 * Region numbers are used in our (and the PCI bus') interfaction with
5705 * PGM, so PGM needs to be informed too.
5706 */
5707 if ( iRegion == 0
5708 && iRegion == pThis->pciRegions.iVRAM
5709 && (enmType & PCI_ADDRESS_SPACE_IO))
5710 {
5711 LogRel(("VGA: Detected old BAR config, making adjustments.\n"));
5712
5713 /* Update the entries. */
5714 pThis->pciRegions.iIO = 0;
5715 pThis->pciRegions.iVRAM = 1;
5716
5717 /* Update PGM on the region number change so it won't barf when restoring state. */
5718 AssertLogRelReturn(pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo, VERR_VERSION_MISMATCH);
5719 int rc = pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo(pDevIns, pThis->hMmio2VRam, 1);
5720 AssertLogRelRCReturn(rc, rc);
5721 /** @todo Update the I/O port too, only currently we don't give a hoot about
5722 * the region number in the I/O port registrations so it can wait...
5723 * (Only visible in the 'info ioport' output IIRC). */
5724
5725 /* Update the calling PCI device. */
5726 AssertLogRelReturn(pfnSwapRegions, VERR_INTERNAL_ERROR_2);
5727 rc = pfnSwapRegions(pPciDev, 0, 1);
5728 AssertLogRelRCReturn(rc, rc);
5729
5730 return rc;
5731 }
5732
5733 /*
5734 * The VMSVGA changed the default FIFO size from 128KB to 2MB after 5.1.
5735 */
5736 if (iRegion == pThis->pciRegions.iFIFO)
5737 {
5738 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5739 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5740 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5741
5742 /* If the size didn't change we're fine, so just return already. */
5743 if (cbRegion == pThis->svga.cbFIFO)
5744 return VINF_SUCCESS;
5745
5746 /* If the size is larger than the current configuration, refuse to load. */
5747 AssertLogRelMsgReturn(cbRegion <= pThis->svga.cbFIFOConfig,
5748 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x\n",
5749 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO),
5750 VERR_SSM_LOAD_CONFIG_MISMATCH);
5751
5752 /* Adjust the size down. */
5753 int rc = PDMDevHlpMmio2Reduce(pDevIns, pThis->hMmio2VmSvgaFifo, cbRegion);
5754 AssertLogRelMsgRCReturn(rc,
5755 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x: %Rrc\n",
5756 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO, rc),
5757 rc);
5758 pThis->svga.cbFIFO = cbRegion;
5759 return rc;
5760
5761 }
5762
5763 /*
5764 * VRAM used to be non-prefetchable till 6.1.0, so we end up here when restoring
5765 * states older than that with 6.1.0 and later. We just have to check that
5766 * the size and basic type matches, then return VINF_SUCCESS to ACK it.
5767 */
5768 if (iRegion == pThis->pciRegions.iVRAM)
5769 {
5770 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5771 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5772 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5773 /* The size must be the same. */
5774 AssertLogRelMsgReturn(cbRegion == pThis->vram_size,
5775 ("cbRegion=%#RGp vram_size=%#x\n", cbRegion, pThis->vram_size),
5776 VERR_SSM_LOAD_CONFIG_MISMATCH);
5777 return VINF_SUCCESS;
5778 }
5779
5780 /* Emulate callbacks for 5.1 and older saved states by recursion. */
5781 if (iRegion == UINT32_MAX)
5782 {
5783 int rc = vgaR3PciRegionLoadChangeHook(pDevIns, pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD,
5784 PCI_ADDRESS_SPACE_MEM, NULL, NULL);
5785 if (RT_SUCCESS(rc))
5786 rc = pfnOldSetter(pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM);
5787 return rc;
5788 }
5789 }
5790# endif /* VBOX_WITH_VMSVGA */
5791
5792 return VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE;
5793}
5794# endif /* VBOX_WITH_VMSVGA */
5795
5796
5797/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5798
5799/**
5800 * Saves a important bits of the VGA device config.
5801 *
5802 * @param pHlp The device helpers (for SSM functions).
5803 * @param pThis The shared VGA instance data.
5804 * @param pSSM The saved state handle.
5805 */
5806static void vgaR3SaveConfig(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PSSMHANDLE pSSM)
5807{
5808 pHlp->pfnSSMPutU32(pSSM, pThis->vram_size);
5809 pHlp->pfnSSMPutU32(pSSM, pThis->cMonitors);
5810}
5811
5812
5813/**
5814 * @callback_method_impl{FNSSMDEVLIVEEXEC}
5815 */
5816static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5817{
5818 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5819 Assert(uPass == 0); NOREF(uPass);
5820 vgaR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
5821 return VINF_SSM_DONT_CALL_AGAIN;
5822}
5823
5824
5825/**
5826 * @callback_method_impl{FNSSMDEVSAVEPREP}
5827 */
5828static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5829{
5830# ifdef VBOX_WITH_VIDEOHWACCEL
5831 RT_NOREF(pSSM);
5832 return vboxVBVASaveStatePrep(pDevIns);
5833# else
5834 RT_NOREF(pDevIns, pSSM);
5835 return VINF_SUCCESS;
5836# endif
5837}
5838
5839
5840/**
5841 * @callback_method_impl{FNSSMDEVSAVEDONE}
5842 */
5843static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5844{
5845# ifdef VBOX_WITH_VIDEOHWACCEL
5846 RT_NOREF(pSSM);
5847 return vboxVBVASaveStateDone(pDevIns);
5848# else
5849 RT_NOREF(pDevIns, pSSM);
5850 return VINF_SUCCESS;
5851# endif
5852}
5853
5854
5855/**
5856 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5857 */
5858static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5859{
5860 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5861 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5862 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5863
5864# ifdef VBOX_WITH_VDMA
5865 vboxVDMASaveStateExecPrep(pThisCC->pVdma);
5866# endif
5867
5868 vgaR3SaveConfig(pHlp, pThis, pSSM);
5869 vga_save(pHlp, pSSM, PDMDEVINS_2_DATA(pDevIns, PVGASTATE));
5870
5871 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5872# ifdef VBOX_WITH_HGSMI
5873 pHlp->pfnSSMPutBool(pSSM, true);
5874 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5875# else
5876 int rc = pHlp->pfnSSMPutBool(pSSM, false);
5877# endif
5878
5879 AssertRCReturn(rc, rc);
5880
5881 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5882# ifdef VBOX_WITH_VDMA
5883 rc = pHlp->pfnSSMPutU32(pSSM, 1);
5884 AssertRCReturn(rc, rc);
5885 rc = vboxVDMASaveStateExecPerform(pHlp, pThisCC->pVdma, pSSM);
5886# else
5887 rc = pHlp->pfnSSMPutU32(pSSM, 0);
5888# endif
5889 AssertRCReturn(rc, rc);
5890
5891# ifdef VBOX_WITH_VDMA
5892 vboxVDMASaveStateExecDone(pThisCC->pVdma);
5893# endif
5894
5895 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5896# ifdef VBOX_WITH_VMSVGA
5897 if (pThis->fVMSVGAEnabled)
5898 {
5899 rc = vmsvgaR3SaveExec(pDevIns, pSSM);
5900 AssertRCReturn(rc, rc);
5901 }
5902# endif
5903 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5904
5905 return rc;
5906}
5907
5908
5909/**
5910 * @callback_method_impl{FNSSMDEVLOADPREP}
5911 */
5912static DECLCALLBACK(int) vgaR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5913{
5914 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5915 RT_NOREF(pSSM);
5916 pThis->fStateLoaded = true;
5917 return VINF_SUCCESS;
5918}
5919
5920
5921/**
5922 * @callback_method_impl{FNSSMDEVLOADEXEC}
5923 */
5924static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5925{
5926 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5927 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5928 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5929 int rc;
5930
5931 pThis->fStateLoaded = true;
5932
5933 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5934 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5935
5936 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5937 {
5938 /* Check the config */
5939 uint32_t cbVRam;
5940 rc = pHlp->pfnSSMGetU32(pSSM, &cbVRam);
5941 AssertRCReturn(rc, rc);
5942 if (pThis->vram_size != cbVRam)
5943 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5944
5945 uint32_t cMonitors;
5946 rc = pHlp->pfnSSMGetU32(pSSM, &cMonitors);
5947 AssertRCReturn(rc, rc);
5948 if (pThis->cMonitors != cMonitors)
5949 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5950 }
5951
5952 if (uPass == SSM_PASS_FINAL)
5953 {
5954 rc = vga_load(pHlp, pSSM, pThis, uVersion);
5955 if (RT_FAILURE(rc))
5956 return rc;
5957
5958 /*
5959 * Restore the HGSMI state, if present.
5960 */
5961 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5962 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5963 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5964 {
5965 rc = pHlp->pfnSSMGetBool(pSSM, &fWithHgsmi);
5966 AssertRCReturn(rc, rc);
5967 }
5968 if (fWithHgsmi)
5969 {
5970# ifdef VBOX_WITH_HGSMI
5971 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5972 AssertRCReturn(rc, rc);
5973# else
5974 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5975# endif
5976 }
5977
5978 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5979 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5980 {
5981 uint32_t u32;
5982 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
5983 if (u32)
5984 {
5985# ifdef VBOX_WITH_VDMA
5986 if (u32 == 1)
5987 {
5988 rc = vboxVDMASaveLoadExecPerform(pHlp, pThisCC->pVdma, pSSM, uVersion);
5989 AssertRCReturn(rc, rc);
5990 }
5991 else
5992# endif
5993 {
5994 LogRel(("invalid CmdVbva version info\n"));
5995 return VERR_VERSION_MISMATCH;
5996 }
5997 }
5998 }
5999
6000 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
6001# ifdef VBOX_WITH_VMSVGA
6002 if (pThis->fVMSVGAEnabled)
6003 {
6004 rc = vmsvgaR3LoadExec(pDevIns, pSSM, uVersion, uPass);
6005 AssertRCReturn(rc, rc);
6006 }
6007# endif
6008 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
6009 }
6010 return VINF_SUCCESS;
6011}
6012
6013
6014/**
6015 * @@callback_method_impl{FNSSMDEVLOADDONE}
6016 */
6017static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
6018{
6019 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6020 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6021 int rc;
6022 RT_NOREF(pThisCC, pThis, pSSM);
6023
6024# ifdef VBOX_WITH_HGSMI
6025 rc = vboxVBVALoadStateDone(pDevIns);
6026 AssertRCReturn(rc, rc);
6027# ifdef VBOX_WITH_VDMA
6028 rc = vboxVDMASaveLoadDone(pThisCC->pVdma);
6029 AssertRCReturn(rc, rc);
6030# endif
6031 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
6032 VBVAOnVBEChanged(pThis, pThisCC);
6033# endif
6034# ifdef VBOX_WITH_VMSVGA
6035 if (pThis->fVMSVGAEnabled)
6036 {
6037 rc = vmsvgaR3LoadDone(pDevIns);
6038 AssertRCReturn(rc, rc);
6039 }
6040# endif
6041 return VINF_SUCCESS;
6042}
6043
6044
6045/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
6046
6047/**
6048 * @interface_method_impl{PDMDEVREG,pfnResume}
6049 */
6050static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
6051{
6052 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6053 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6054 VBVAOnResume(pDevIns, pThis, pThisCC);
6055}
6056
6057
6058/**
6059 * @interface_method_impl{PDMDEVREG,pfnReset}
6060 */
6061static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
6062{
6063 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6064 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6065 char *pchStart;
6066 char *pchEnd;
6067 LogFlow(("vgaReset\n"));
6068
6069 if (pThisCC->pVdma)
6070 vboxVDMAReset(pThisCC->pVdma);
6071
6072# ifdef VBOX_WITH_VMSVGA
6073 if (pThis->fVMSVGAEnabled)
6074 vmsvgaR3Reset(pDevIns);
6075# endif
6076
6077# ifdef VBOX_WITH_HGSMI
6078 VBVAReset(pDevIns, pThis, pThisCC);
6079# endif
6080
6081
6082 /* Clear the VRAM ourselves. */
6083 if (pThisCC->pbVRam && pThis->vram_size)
6084 memset(pThisCC->pbVRam, 0, pThis->vram_size);
6085
6086 /*
6087 * Zero most of it.
6088 *
6089 * Unlike vga_reset we're leaving out a few members which we believe
6090 * must remain unchanged....
6091 */
6092 /* 1st part. */
6093 pchStart = (char *)&pThis->latch;
6094 pchEnd = (char *)&pThis->invalidated_y_table;
6095 memset(pchStart, 0, pchEnd - pchStart);
6096
6097 /* 2nd part. */
6098 pchStart = (char *)&pThis->last_palette;
6099 pchEnd = (char *)&pThis->u32Marker;
6100 memset(pchStart, 0, pchEnd - pchStart);
6101
6102
6103 /*
6104 * Restore and re-init some bits.
6105 */
6106 pThisCC->get_bpp = vgaR3GetBpp;
6107 pThisCC->get_offsets = vgaR3GetOffsets;
6108 pThisCC->get_resolution = vgaR3GetResolution;
6109 pThis->graphic_mode = -1; /* Force full update. */
6110# ifdef CONFIG_BOCHS_VBE
6111 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
6112 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
6113 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
6114 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
6115# endif /* CONFIG_BOCHS_VBE */
6116 pThis->st00 = 0x70; /* Static except for bit 4. */
6117
6118 /*
6119 * Reset the LFB mapping.
6120 */
6121 if ( ( pDevIns->fRCEnabled
6122 || pDevIns->fR0Enabled)
6123 && pThis->GCPhysVRAM != 0
6124 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
6125 {
6126 /** @todo r=bird: This used to be a PDMDevHlpPGMHandlerPhysicalReset call.
6127 * Not quite sure if it was/is needed. Besides, where do we reset the
6128 * dirty bitmap (bmDirtyBitmap)? */
6129 int rc = PDMDevHlpMmio2ResetDirtyBitmap(pDevIns, pThis->hMmio2VRam);
6130 AssertRC(rc);
6131 }
6132 if (pThis->bmPageRemappedVGA != 0)
6133 {
6134 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
6135 STAM_COUNTER_INC(&pThis->StatMapReset);
6136 vgaResetRemapped(pThis);
6137 }
6138
6139 /*
6140 * Reset the logo data.
6141 */
6142 pThisCC->LogoCommand = LOGO_CMD_NOP;
6143 pThisCC->offLogoData = 0;
6144
6145 /* notify port handler */
6146 if (pThisCC->pDrv)
6147 {
6148 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* hack around lock order issue. */
6149
6150 pThisCC->pDrv->pfnReset(pThisCC->pDrv);
6151 pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, false, false, 0, 0, 0, 0, NULL);
6152
6153 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
6154 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
6155 }
6156
6157 /* Reset latched access mask. */
6158 pThis->uMaskLatchAccess = 0x3ff;
6159 pThis->cLatchAccesses = 0;
6160 pThis->u64LastLatchedAccess = 0;
6161 pThis->iMask = 0;
6162
6163 /* Reset retrace emulation. */
6164 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
6165}
6166
6167
6168/**
6169 * @interface_method_impl{PDMDEVREG,pfnPowerOn}
6170 */
6171static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
6172{
6173 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6174 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6175# ifdef VBOX_WITH_VMSVGA
6176 if (pThis->fVMSVGAEnabled)
6177 vmsvgaR3PowerOn(pDevIns);
6178# endif
6179 VBVAOnResume(pDevIns, pThis, pThisCC);
6180}
6181
6182
6183/**
6184 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
6185 */
6186static DECLCALLBACK(void) vgaR3PowerOff(PPDMDEVINS pDevIns)
6187{
6188 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6189 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6190 RT_NOREF(pThis, pThisCC);
6191# ifdef VBOX_WITH_VMSVGA
6192 if (pThis->fVMSVGAEnabled)
6193 vmsvgaR3PowerOff(pDevIns);
6194# endif
6195}
6196
6197
6198/**
6199 * @interface_method_impl{PDMDEVREG,pfnRelocate}
6200 */
6201static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
6202{
6203# ifdef VBOX_WITH_RAW_MODE_KEEP
6204 if (offDelta)
6205 {
6206 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6207 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
6208
6209 pThisRC->pbVRam += offDelta;
6210 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6211 }
6212# else
6213 RT_NOREF(pDevIns, offDelta);
6214# endif
6215}
6216
6217
6218/**
6219 * @interface_method_impl{PDMDEVREG,pfnAttach}
6220 *
6221 * This is like plugging in the monitor after turning on the PC.
6222 */
6223static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6224{
6225 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6226 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6227
6228 RT_NOREF(pThis);
6229
6230 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
6231 ("VGA device does not support hotplugging\n"),
6232 VERR_INVALID_PARAMETER);
6233
6234 switch (iLUN)
6235 {
6236 /* LUN #0: Display port. */
6237 case 0:
6238 {
6239 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "Display Port");
6240 if (RT_SUCCESS(rc))
6241 {
6242 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIDISPLAYCONNECTOR);
6243 if (pThisCC->pDrv)
6244 {
6245 /* pThisCC->pDrv->pbData can be NULL when there is no framebuffer. */
6246 if ( pThisCC->pDrv->pfnRefresh
6247 && pThisCC->pDrv->pfnResize
6248 && pThisCC->pDrv->pfnUpdateRect)
6249 rc = VINF_SUCCESS;
6250 else
6251 {
6252 Assert(pThisCC->pDrv->pfnRefresh);
6253 Assert(pThisCC->pDrv->pfnResize);
6254 Assert(pThisCC->pDrv->pfnUpdateRect);
6255 pThisCC->pDrv = NULL;
6256 pThisCC->pDrvBase = NULL;
6257 rc = VERR_INTERNAL_ERROR;
6258 }
6259# ifdef VBOX_WITH_VIDEOHWACCEL
6260 if(rc == VINF_SUCCESS)
6261 {
6262 rc = vbvaVHWAConstruct(pDevIns, pThis, pThisCC);
6263 if (rc != VERR_NOT_IMPLEMENTED)
6264 AssertRC(rc);
6265 }
6266# endif
6267 }
6268 else
6269 {
6270 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
6271 pThisCC->pDrvBase = NULL;
6272 rc = VERR_PDM_MISSING_INTERFACE;
6273 }
6274 }
6275 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6276 {
6277 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6278 rc = VINF_SUCCESS;
6279 }
6280 else
6281 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
6282 return rc;
6283 }
6284
6285 default:
6286 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6287 return VERR_PDM_NO_SUCH_LUN;
6288 }
6289}
6290
6291
6292/**
6293 * @interface_method_impl{PDMDEVREG,pfnDetach}
6294 *
6295 * This is like unplugging the monitor while the PC is still running.
6296 */
6297static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6298{
6299 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6300 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
6301 RT_NOREF(fFlags);
6302
6303 /*
6304 * Reset the interfaces and update the controller state.
6305 */
6306 switch (iLUN)
6307 {
6308 /* LUN #0: Display port. */
6309 case 0:
6310 pThisCC->pDrv = NULL;
6311 pThisCC->pDrvBase = NULL;
6312 break;
6313
6314 default:
6315 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6316 break;
6317 }
6318}
6319
6320
6321/**
6322 * @interface_method_impl{PDMDEVREG,pfnDestruct}
6323 */
6324static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6325{
6326 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
6327 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6328 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6329 LogFlow(("vgaR3Destruct:\n"));
6330
6331# ifdef VBOX_WITH_VDMA
6332 if (pThisCC->pVdma)
6333 vboxVDMADestruct(pThisCC->pVdma);
6334# endif
6335
6336# ifdef VBOX_WITH_VMSVGA
6337 if (pThis->fVMSVGAEnabled)
6338 vmsvgaR3Destruct(pDevIns);
6339# endif
6340
6341# ifdef VBOX_WITH_HGSMI
6342 VBVADestroy(pThisCC);
6343# endif
6344
6345 /*
6346 * Free MM heap pointers.
6347 */
6348 if (pThisCC->pbVBEExtraData)
6349 {
6350 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVBEExtraData);
6351 pThisCC->pbVBEExtraData = NULL;
6352 }
6353 if (pThisCC->pbVgaBios)
6354 {
6355 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6356 pThisCC->pbVgaBios = NULL;
6357 }
6358
6359 if (pThisCC->pszVgaBiosFile)
6360 {
6361 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6362 pThisCC->pszVgaBiosFile = NULL;
6363 }
6364
6365 if (pThisCC->pszLogoFile)
6366 {
6367 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
6368 pThisCC->pszLogoFile = NULL;
6369 }
6370
6371 if (pThisCC->pbLogo)
6372 {
6373 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbLogo);
6374 pThisCC->pbLogo = NULL;
6375 }
6376
6377# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
6378 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSectIRQ);
6379# endif
6380 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
6381 return VINF_SUCCESS;
6382}
6383
6384
6385/**
6386 * Adjust VBE mode information
6387 *
6388 * Depending on the configured VRAM size, certain parts of VBE mode
6389 * information must be updated.
6390 *
6391 * @param pThis The device instance data.
6392 * @param pMode The mode information structure.
6393 */
6394static void vgaR3AdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6395{
6396 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6397 unsigned bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6398 /* The "number of image pages" is really the max page index... */
6399 unsigned maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6400 if (maxPage > 255)
6401 maxPage = 255; /* 8-bit value. */
6402 pMode->info.NumberOfImagePages = maxPage;
6403 pMode->info.LinNumberOfPages = maxPage;
6404}
6405
6406
6407/**
6408 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6409 */
6410static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6411{
6412 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6413 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6414 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6415 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
6416 int rc;
6417 unsigned i;
6418 uint32_t cCustomModes;
6419 uint32_t cyReduction;
6420 uint32_t cbPitch;
6421 PVBEHEADER pVBEDataHdr;
6422 ModeInfoListItem *pCurMode;
6423 unsigned cb;
6424
6425 Assert(iInstance == 0);
6426
6427 /*
6428 * Init static data.
6429 */
6430 static bool s_fExpandDone = false;
6431 if (!s_fExpandDone)
6432 {
6433 s_fExpandDone = true;
6434 vgaR3InitExpand();
6435 }
6436
6437 /*
6438 * Validate configuration.
6439 */
6440 static const char s_szMscWorkaround[] = "VRamSize"
6441 "|MonitorCount"
6442 "|FadeIn"
6443 "|FadeOut"
6444 "|LogoTime"
6445 "|LogoFile"
6446 "|ShowBootMenu"
6447 "|BiosRom"
6448 "|RealRetrace"
6449 "|CustomVideoModes"
6450 "|HeightReduction"
6451 "|CustomVideoMode1"
6452 "|CustomVideoMode2"
6453 "|CustomVideoMode3"
6454 "|CustomVideoMode4"
6455 "|CustomVideoMode5"
6456 "|CustomVideoMode6"
6457 "|CustomVideoMode7"
6458 "|CustomVideoMode8"
6459 "|CustomVideoMode9"
6460 "|CustomVideoMode10"
6461 "|CustomVideoMode11"
6462 "|CustomVideoMode12"
6463 "|CustomVideoMode13"
6464 "|CustomVideoMode14"
6465 "|CustomVideoMode15"
6466 "|CustomVideoMode16"
6467 "|MaxBiosXRes"
6468 "|MaxBiosYRes"
6469# ifdef VBOX_WITH_VMSVGA
6470 "|VMSVGAEnabled"
6471 "|VMSVGA10"
6472 "|VMSVGAPciId"
6473 "|VMSVGAPciBarLayout"
6474 "|VMSVGAFifoSize"
6475 "|VmSvga3"
6476 "|VmSvgaExposeLegacyVga"
6477# endif
6478# ifdef VBOX_WITH_VMSVGA3D
6479 "|VMSVGA3dEnabled"
6480 "|VMSVGA3dOverlayEnabled"
6481 "|VMSVGA3dMSAA"
6482# endif
6483 "|SuppressNewYearSplash"
6484 "|3DEnabled";
6485
6486 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, s_szMscWorkaround, "");
6487
6488 /*
6489 * Init state data.
6490 */
6491 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6492 AssertLogRelRCReturn(rc, rc);
6493 if (pThis->vram_size > VGA_VRAM_MAX)
6494 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6495 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6496 if (pThis->vram_size < VGA_VRAM_MIN)
6497 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6498 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6499 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6500 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6501 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6502
6503 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6504 AssertLogRelRCReturn(rc, rc);
6505
6506 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
6507
6508 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "3DEnabled", &pThis->f3DEnabled, false);
6509 AssertLogRelRCReturn(rc, rc);
6510 Log(("VGA: f3DEnabled=%RTbool\n", pThis->f3DEnabled));
6511
6512# ifdef VBOX_WITH_VMSVGA
6513 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6514 AssertLogRelRCReturn(rc, rc);
6515 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6516
6517 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA10", &pThis->fVMSVGA10, true);
6518 AssertLogRelRCReturn(rc, rc);
6519 Log(("VMSVGA: VMSVGA10 = %d\n", pThis->fVMSVGA10));
6520
6521 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciId", &pThis->fVMSVGAPciId, false);
6522 AssertLogRelRCReturn(rc, rc);
6523 Log(("VMSVGA: VMSVGAPciId = %d\n", pThis->fVMSVGAPciId));
6524
6525 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciBarLayout", &pThis->fVMSVGAPciBarLayout, pThis->fVMSVGAPciId);
6526 AssertLogRelRCReturn(rc, rc);
6527 Log(("VMSVGA: VMSVGAPciBarLayout = %d\n", pThis->fVMSVGAPciBarLayout));
6528
6529 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VmSvga3", &pThis->fVmSvga3, false);
6530 AssertLogRelRCReturn(rc, rc);
6531 Log(("VMSVGA: VmSvga3 = %RTbool\n", pThis->fVmSvga3));
6532
6533 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VmSvgaExposeLegacyVga", &pThis->fLegacyVgaEnabled, true);
6534 AssertLogRelRCReturn(rc, rc);
6535 Log(("VMSVGA: VmSvgaExposeLegacyVga = %RTbool\n", pThis->fLegacyVgaEnabled));
6536
6537 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6538 AssertLogRelRCReturn(rc, rc);
6539 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6540 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6541 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6542 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6543 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6544# endif
6545# ifdef VBOX_WITH_VMSVGA3D
6546 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6547 AssertLogRelRCReturn(rc, rc);
6548 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6549
6550 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dOverlayEnabled", &pThis->svga.f3DOverlayEnabled, false);
6551 AssertLogRelRCReturn(rc, rc);
6552 Log(("VMSVGA: VMSVGA3dOverlayEnabled = %d\n", pThis->svga.f3DOverlayEnabled));
6553
6554 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dMSAA", &pThis->svga.fVMSVGA3dMSAA, true);
6555 AssertLogRelRCReturn(rc, rc);
6556 Log(("VMSVGA: VMSVGA3dMSAA = %d\n", pThis->svga.fVMSVGA3dMSAA));
6557# endif
6558
6559# ifdef VBOX_WITH_VMSVGA
6560 if (pThis->fVMSVGAPciBarLayout)
6561 {
6562 if (pThis->fVmSvga3)
6563 {
6564 pThis->pciRegions.iIO = 0;
6565 pThis->pciRegions.iVRAM = 2;
6566 }
6567 else
6568 {
6569 pThis->pciRegions.iIO = 0;
6570 pThis->pciRegions.iVRAM = 1;
6571 }
6572 }
6573 else
6574 {
6575 pThis->pciRegions.iVRAM = 0;
6576 pThis->pciRegions.iIO = 1;
6577 }
6578 pThis->pciRegions.iFIFO = 2;
6579# else
6580 pThis->pciRegions.iVRAM = 0;
6581# endif
6582
6583 pThisCC->pDevIns = pDevIns;
6584
6585 vgaR3Reset(pDevIns);
6586
6587 /* The PCI devices configuration. */
6588 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
6589 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
6590
6591# ifdef VBOX_WITH_VMSVGA
6592 if (pThis->fVMSVGAEnabled)
6593 {
6594 /* Extend our VGA device with VMWare SVGA functionality. */
6595 if (pThis->fVMSVGAPciId)
6596 {
6597 PDMPciDevSetVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6598 if (pThis->fVmSvga3)
6599 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA3);
6600 else
6601 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6602 }
6603 else
6604 {
6605 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6606 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6607 }
6608 PDMPciDevSetSubSystemVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6609 if (pThis->fVmSvga3)
6610 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA3);
6611 else
6612 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6613 }
6614 else
6615# endif /* VBOX_WITH_VMSVGA */
6616 {
6617 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6618 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6619 }
6620 PDMPciDevSetClassSub(pPciDev, 0x00); /* VGA controller */
6621 PDMPciDevSetClassBase(pPciDev, 0x03);
6622 PDMPciDevSetHeaderType(pPciDev, 0x00);
6623# if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6624 PDMPciDevSetInterruptPin(pPciDev, 1);
6625# endif
6626
6627 /* the interfaces. */
6628 pThisCC->IBase.pfnQueryInterface = vgaR3PortQueryInterface;
6629
6630 pThisCC->IPort.pfnUpdateDisplay = vgaR3PortUpdateDisplay;
6631 pThisCC->IPort.pfnUpdateDisplayAll = vgaR3PortUpdateDisplayAll;
6632 pThisCC->IPort.pfnQueryVideoMode = vgaR3PortQueryVideoMode;
6633 pThisCC->IPort.pfnSetRefreshRate = vgaR3PortSetRefreshRate;
6634 pThisCC->IPort.pfnTakeScreenshot = vgaR3PortTakeScreenshot;
6635 pThisCC->IPort.pfnFreeScreenshot = vgaR3PortFreeScreenshot;
6636 pThisCC->IPort.pfnDisplayBlt = vgaR3PortDisplayBlt;
6637 pThisCC->IPort.pfnUpdateDisplayRect = vgaR3PortUpdateDisplayRect;
6638 pThisCC->IPort.pfnCopyRect = vgaR3PortCopyRect;
6639 pThisCC->IPort.pfnSetRenderVRAM = vgaR3PortSetRenderVRAM;
6640 pThisCC->IPort.pfnSetViewport = NULL;
6641 pThisCC->IPort.pfnReportMonitorPositions = NULL;
6642# ifdef VBOX_WITH_VMSVGA
6643 if (pThis->fVMSVGAEnabled)
6644 {
6645 pThisCC->IPort.pfnSetViewport = vmsvgaR3PortSetViewport;
6646 pThisCC->IPort.pfnReportMonitorPositions = vmsvgaR3PortReportMonitorPositions;
6647 }
6648# endif
6649 pThisCC->IPort.pfnSendModeHint = vbvaR3PortSendModeHint;
6650 pThisCC->IPort.pfnReportHostCursorCapabilities = vgaR3PortReportHostCursorCapabilities;
6651 pThisCC->IPort.pfnReportHostCursorPosition = vgaR3PortReportHostCursorPosition;
6652
6653# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
6654 pThisCC->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaR3VHWACommandCompleteAsync;
6655# endif
6656
6657 pThisCC->ILeds.pfnQueryStatusLed = vgaR3PortQueryStatusLed;
6658 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6659
6660 /*
6661 * We use our own critical section to avoid unncessary pointer indirections
6662 * in interface methods (as well as for historical reasons).
6663 */
6664 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6665 AssertRCReturn(rc, rc);
6666 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6667 AssertRCReturn(rc, rc);
6668
6669# ifdef VBOX_WITH_HGSMI
6670 /*
6671 * This critical section is used by vgaR3IOPortHgsmiWrite, VBVARaiseIrq and VBVAOnResume
6672 * for some IRQ related synchronization.
6673 */
6674 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6675 AssertRCReturn(rc, rc);
6676# endif
6677
6678 /*
6679 * PCI device registration.
6680 */
6681 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6682 if (RT_FAILURE(rc))
6683 return rc;
6684 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6685 if (pPciDev->uDevFn != 16 && iInstance == 0)
6686 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pPciDev->uDevFn));
6687
6688# ifdef VBOX_WITH_VMSVGA
6689 pThis->hIoPortVmSvga = NIL_IOMIOPORTHANDLE;
6690 pThis->hMmio2VmSvgaFifo = NIL_PGMMMIO2HANDLE;
6691 pThis->hMmioSvga3 = NIL_IOMMMIOHANDLE;
6692 if (pThis->fVMSVGAEnabled)
6693 {
6694 if (pThis->fVmSvga3)
6695 {
6696 /* Register the MMIO register region. */
6697 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, pThis->pciRegions.iIO, 4096 * 1024, PCI_ADDRESS_SPACE_MEM,
6698 vmsvga3MmioWrite, vmsvga3MmioRead, NULL /*pvUser*/,
6699 IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED,
6700 "VMSVGA3-MMIO", &pThis->hMmioSvga3);
6701 AssertRCReturn(rc, rc);
6702 }
6703 else
6704 {
6705 /* Register the io command ports. */
6706 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, pThis->pciRegions.iIO, 0x10, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/,
6707 "VMSVGA", NULL /*paExtDescs*/, &pThis->hIoPortVmSvga);
6708 AssertRCReturn(rc, rc);
6709
6710 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iFIFO, pThis->svga.cbFIFO,
6711 PCI_ADDRESS_SPACE_MEM, 0 /*fFlags*/, vmsvgaR3PciIORegionFifoMapUnmap,
6712 "VMSVGA-FIFO", (void **)&pThisCC->svga.pau32FIFO, &pThis->hMmio2VmSvgaFifo);
6713 AssertRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6714 N_("Failed to create VMSVGA FIFO (%u bytes)"), pThis->svga.cbFIFO));
6715 }
6716
6717 pPciDev->pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6718 }
6719# endif /* VBOX_WITH_VMSVGA */
6720
6721 /*
6722 * Allocate VRAM and create a PCI region for it.
6723 */
6724 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iVRAM, pThis->vram_size,
6725 PCI_ADDRESS_SPACE_MEM_PREFETCH, PGMPHYS_MMIO2_FLAGS_TRACK_DIRTY_PAGES,
6726 vgaR3PciIORegionVRamMapUnmap, "VRam", (void **)&pThisCC->pbVRam, &pThis->hMmio2VRam);
6727 AssertLogRelRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6728 N_("Failed to allocate %u bytes of VRAM"), pThis->vram_size));
6729
6730 if (pThis->fLegacyVgaEnabled)
6731 {
6732 /*
6733 * Register I/O ports.
6734 */
6735# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_phIoPort) do { \
6736 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, a_uPort, a_cPorts, IOM_IOPORT_F_ABS, \
6737 a_pfnWrite, a_pfnRead, "VGA - " a_szDesc, NULL /*paExtDescs*/, a_phIoPort); \
6738 AssertRCReturn(rc, rc); \
6739 } while (0)
6740 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", &pThis->hIoPortAr);
6741 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", &pThis->hIoPortMsrSt00);
6742 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", &pThis->hIoPort3c3);
6743 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", &pThis->hIoPortSr);
6744 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", &pThis->hIoPortDac);
6745 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ &pThis->hIoPortPos);
6746 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", &pThis->hIoPortGr);
6747
6748 /* Note! Ralf Brown lists 0x3b0-0x3b1, 0x3b2-0x3b3 and 0x3b6-0x3b7 as "the same as" 0x3b4-0x3b5. */
6749 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", &pThis->hIoPortMdaCrt);
6750 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", &pThis->hIoPortMdaFcrSt);
6751 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", &pThis->hIoPortCgaCrt);
6752 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", &pThis->hIoPortCgaFcrSt);
6753
6754# ifdef CONFIG_BOCHS_VBE
6755 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", &pThis->hIoPortVbeIndex);
6756 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", &pThis->hIoPortVbeData);
6757# endif /* CONFIG_BOCHS_VBE */
6758
6759# ifdef VBOX_WITH_HGSMI
6760 /* Use reserved VGA IO ports for HGSMI. */
6761 REG_PORT(VGA_PORT_HGSMI_HOST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI host (3b0-3b3)", &pThis->hIoPortHgsmiHost);
6762 REG_PORT(VGA_PORT_HGSMI_GUEST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI guest (3d0-3d3)", &pThis->hIoPortHgsmiGuest);
6763# endif /* VBOX_WITH_HGSMI */
6764
6765# undef REG_PORT
6766
6767 /* vga bios */
6768 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_PRINTF_PORT, 1 /*cPorts*/, vgaIoPortWriteBios, vgaIoPortReadBios,
6769 "VGA BIOS debug/panic", NULL /*paExtDescs*/, &pThis->hIoPortBios);
6770 AssertRCReturn(rc, rc);
6771
6772 /*
6773 * The MDA/CGA/EGA/VGA/whatever fixed MMIO area.
6774 */
6775 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, 0x000a0000, 0x00020000,
6776 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
6777 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
6778 vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/,
6779 "VGA - VGA Video Buffer", &pThis->hMmioLegacy);
6780 AssertRCReturn(rc, rc);
6781
6782 /*
6783 * Get the VGA BIOS ROM file name.
6784 */
6785 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThisCC->pszVgaBiosFile);
6786 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6787 {
6788 pThisCC->pszVgaBiosFile = NULL;
6789 rc = VINF_SUCCESS;
6790 }
6791 else if (RT_FAILURE(rc))
6792 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6793 else if (!*pThisCC->pszVgaBiosFile)
6794 {
6795 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6796 pThisCC->pszVgaBiosFile = NULL;
6797 }
6798
6799 /*
6800 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6801 */
6802 RTFILE FileVgaBios = NIL_RTFILE;
6803 if (pThisCC->pszVgaBiosFile)
6804 {
6805 rc = RTFileOpen(&FileVgaBios, pThisCC->pszVgaBiosFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6806 if (RT_SUCCESS(rc))
6807 {
6808 rc = RTFileQuerySize(FileVgaBios, &pThisCC->cbVgaBios);
6809 if (RT_SUCCESS(rc))
6810 {
6811 if ( RT_ALIGN(pThisCC->cbVgaBios, _4K) != pThisCC->cbVgaBios
6812 || pThisCC->cbVgaBios > _64K
6813 || pThisCC->cbVgaBios < 16 * _1K)
6814 rc = VERR_TOO_MUCH_DATA;
6815 }
6816 }
6817 if (RT_FAILURE(rc))
6818 {
6819 /*
6820 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6821 */
6822 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThisCC->pszVgaBiosFile, rc));
6823 RTFileClose(FileVgaBios);
6824 FileVgaBios = NIL_RTFILE;
6825 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6826 pThisCC->pszVgaBiosFile = NULL;
6827 }
6828 }
6829
6830 /*
6831 * Attempt to get the VGA BIOS ROM data from file.
6832 */
6833 if (pThisCC->pszVgaBiosFile)
6834 {
6835 /*
6836 * Allocate buffer for the VGA BIOS ROM data.
6837 */
6838 pThisCC->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbVgaBios);
6839 if (pThisCC->pbVgaBios)
6840 {
6841 rc = RTFileRead(FileVgaBios, pThisCC->pbVgaBios, pThisCC->cbVgaBios, NULL);
6842 if (RT_FAILURE(rc))
6843 {
6844 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThisCC->cbVgaBios, rc));
6845 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6846 pThisCC->pbVgaBios = NULL;
6847 }
6848 rc = VINF_SUCCESS;
6849 }
6850 else
6851 rc = VERR_NO_MEMORY;
6852 }
6853 else
6854 pThisCC->pbVgaBios = NULL;
6855
6856 /* cleanup */
6857 if (FileVgaBios != NIL_RTFILE)
6858 RTFileClose(FileVgaBios);
6859
6860 /* If we were unable to get the data from file for whatever reason, fall
6861 back to the built-in ROM image. */
6862 const uint8_t *pbVgaBiosBinary;
6863 uint64_t cbVgaBiosBinary;
6864 uint32_t fFlags = 0;
6865 if (pThisCC->pbVgaBios == NULL)
6866 {
6867 CPUMMICROARCH enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
6868 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6869 || enmMicroarch == kCpumMicroarch_Intel_80186
6870 || enmMicroarch == kCpumMicroarch_NEC_V20
6871 || enmMicroarch == kCpumMicroarch_NEC_V30)
6872 {
6873 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6874 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6875 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6876 }
6877 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6878 {
6879 pbVgaBiosBinary = g_abVgaBiosBinary286;
6880 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6881 LogRel(("VGA: Using the 286 BIOS image!\n"));
6882 }
6883 else
6884 {
6885 pbVgaBiosBinary = g_abVgaBiosBinary386;
6886 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6887 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6888 }
6889 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6890 }
6891 else
6892 {
6893 pbVgaBiosBinary = pThisCC->pbVgaBios;
6894 cbVgaBiosBinary = pThisCC->cbVgaBios;
6895 }
6896
6897 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6898 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, GUEST_PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6899 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6900 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6901 fFlags, "VGA BIOS");
6902 AssertRCReturn(rc, rc);
6903 }
6904
6905 /*
6906 * Saved state.
6907 */
6908 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6909 NULL, vgaR3LiveExec, NULL,
6910 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6911 vgaR3LoadPrep, vgaR3LoadExec, vgaR3LoadDone);
6912 AssertRCReturn(rc, rc);
6913
6914 /*
6915 * Create the refresh timer.
6916 */
6917 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, vgaR3TimerRefresh, NULL,
6918 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "VGA Refresh", &pThis->hRefreshTimer);
6919 AssertRCReturn(rc, rc);
6920
6921 /*
6922 * Attach to the display.
6923 */
6924 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6925 AssertRCReturn(rc, rc);
6926
6927 /*
6928 * Initialize the retrace flag.
6929 */
6930 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6931 AssertLogRelRCReturn(rc, rc);
6932
6933 uint16_t maxBiosXRes;
6934 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6935 AssertLogRelRCReturn(rc, rc);
6936 uint16_t maxBiosYRes;
6937 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6938 AssertLogRelRCReturn(rc, rc);
6939
6940 if (pThis->fLegacyVgaEnabled)
6941 {
6942 /*
6943 * Compute buffer size for the VBE BIOS Extra Data.
6944 */
6945 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6946
6947 rc = pHlp->pfnCFGMQueryU32(pCfg, "HeightReduction", &cyReduction);
6948 if (RT_SUCCESS(rc) && cyReduction)
6949 cb *= 2; /* Default mode list will be twice long */
6950 else
6951 cyReduction = 0;
6952
6953 rc = pHlp->pfnCFGMQueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6954 if (RT_SUCCESS(rc) && cCustomModes)
6955 cb += sizeof(ModeInfoListItem) * cCustomModes;
6956 else
6957 cCustomModes = 0;
6958
6959 /*
6960 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6961 */
6962 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6963 pThisCC->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6964 pThisCC->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThisCC->cbVBEExtraData);
6965 if (!pThisCC->pbVBEExtraData)
6966 return VERR_NO_MEMORY;
6967
6968 pVBEDataHdr = (PVBEHEADER)pThisCC->pbVBEExtraData;
6969 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6970 pVBEDataHdr->cbData = cb;
6971
6972 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6973 for (i = 0; i < MODE_INFO_SIZE; i++)
6974 {
6975 uint32_t pixelWidth, reqSize;
6976 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6977 pixelWidth = 2;
6978 else
6979 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6980 reqSize = mode_info_list[i].info.XResolution
6981 * mode_info_list[i].info.YResolution
6982 * pixelWidth;
6983 if (reqSize >= pThis->vram_size)
6984 continue;
6985 if (!reqSize)
6986 continue;
6987 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6988 || mode_info_list[i].info.YResolution > maxBiosYRes)
6989 continue;
6990 *pCurMode = mode_info_list[i];
6991 vgaR3AdjustModeInfo(pThis, pCurMode);
6992 pCurMode++;
6993 }
6994
6995 /*
6996 * Copy default modes with subtracted YResolution.
6997 */
6998 if (cyReduction)
6999 {
7000 ModeInfoListItem *pDefMode = mode_info_list;
7001 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
7002 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
7003 {
7004 uint32_t pixelWidth, reqSize;
7005 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
7006 pixelWidth = 2;
7007 else
7008 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
7009 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
7010 if (reqSize >= pThis->vram_size)
7011 continue;
7012 if ( pDefMode->info.XResolution > maxBiosXRes
7013 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
7014 continue;
7015 *pCurMode = *pDefMode;
7016 pCurMode->mode += 0x30;
7017 pCurMode->info.YResolution -= cyReduction;
7018 pCurMode++;
7019 }
7020 }
7021
7022
7023 /*
7024 * Add custom modes.
7025 */
7026 if (cCustomModes)
7027 {
7028 uint16_t u16CurMode = VBE_VBOX_MODE_CUSTOM1;
7029 for (i = 1; i <= cCustomModes; i++)
7030 {
7031 char szExtraDataKey[sizeof("CustomVideoModeXX")];
7032 char *pszExtraData = NULL;
7033
7034 /* query and decode the custom mode string. */
7035 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
7036 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
7037 if (RT_SUCCESS(rc))
7038 {
7039 ModeInfoListItem *pDefMode = mode_info_list;
7040 unsigned int cx, cy, cBits, cParams, j;
7041 uint16_t u16DefMode;
7042
7043 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
7044 if ( cParams != 3
7045 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
7046 {
7047 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
7048 return VERR_VGA_INVALID_CUSTOM_MODE;
7049 }
7050 if (!cx || !cy)
7051 {
7052 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cx=%u, cy=%u\n", pszExtraData, szExtraDataKey, cx, cy));
7053 return VERR_VGA_INVALID_CUSTOM_MODE;
7054 }
7055 cbPitch = calc_line_pitch(cBits, cx);
7056 if (cy * cbPitch >= pThis->vram_size)
7057 {
7058 AssertMsgFailed(("Configuration error: custom video mode %dx%dx%dbits is too large for the virtual video memory of %dMb. Please increase the video memory size.\n",
7059 cx, cy, cBits, pThis->vram_size / _1M));
7060 return VERR_VGA_INVALID_CUSTOM_MODE;
7061 }
7062 PDMDevHlpMMHeapFree(pDevIns, pszExtraData);
7063
7064 /* Use defaults from max@bpp mode. */
7065 switch (cBits)
7066 {
7067 case 8:
7068 u16DefMode = VBE_VESA_MODE_1024X768X8;
7069 break;
7070
7071 case 16:
7072 u16DefMode = VBE_VESA_MODE_1024X768X565;
7073 break;
7074
7075 case 24:
7076 u16DefMode = VBE_VESA_MODE_1024X768X888;
7077 break;
7078
7079 case 32:
7080 u16DefMode = VBE_OWN_MODE_1024X768X8888;
7081 break;
7082
7083 default: /* gcc, shut up! */
7084 AssertMsgFailed(("gone postal!\n"));
7085 continue;
7086 }
7087
7088 /* mode_info_list is not terminated */
7089 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
7090 pDefMode++;
7091 Assert(j < MODE_INFO_SIZE);
7092
7093 *pCurMode = *pDefMode;
7094 pCurMode->mode = u16CurMode++;
7095
7096 /* adjust defaults */
7097 pCurMode->info.XResolution = cx;
7098 pCurMode->info.YResolution = cy;
7099 pCurMode->info.BytesPerScanLine = cbPitch;
7100 pCurMode->info.LinBytesPerScanLine = cbPitch;
7101 vgaR3AdjustModeInfo(pThis, pCurMode);
7102
7103 /* commit it */
7104 pCurMode++;
7105 }
7106 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
7107 {
7108 AssertMsgFailed(("pHlp->pfnCFGMQueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
7109 return rc;
7110 }
7111 } /* foreach custom mode key */
7112 }
7113
7114 /*
7115 * Add the "End of list" mode.
7116 */
7117 memset(pCurMode, 0, sizeof(*pCurMode));
7118 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
7119
7120 /*
7121 * Register I/O Port for the VBE BIOS Extra Data.
7122 */
7123 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_EXTRA_PORT, 1 /*cPorts*/, vbeR3IOPortWriteVbeExtra, vbeR3IoPortReadVbeExtra,
7124 "VBE BIOS Extra Data", NULL /*paExtDesc*/, &pThis->hIoPortVbeExtra);
7125 AssertRCReturn(rc, rc);
7126
7127 /*
7128 * Register I/O Port for the BIOS Logo.
7129 */
7130 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, LOGO_IO_PORT, 1 /*cPorts*/, vbeR3IoPortWriteCmdLogo, vbeR3IoPortReadCmdLogo,
7131 "BIOS Logo", NULL /*paExtDesc*/, &pThis->hIoPortCmdLogo);
7132 AssertRCReturn(rc, rc);
7133
7134 /*
7135 * Register debugger info callbacks.
7136 */
7137 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaR3InfoState);
7138 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaR3InfoText);
7139 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaR3InfoCR);
7140 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaR3InfoGR);
7141 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaR3InfoSR);
7142 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaR3InfoAR);
7143 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaR3InfoPlanar);
7144 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaR3InfoDAC);
7145 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaR3InfoVBE);
7146 }
7147
7148 /*
7149 * Construct the logo header.
7150 */
7151 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
7152
7153 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
7154 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7155 LogoHdr.fu8FadeIn = 1;
7156 else if (RT_FAILURE(rc))
7157 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeIn\" as integer failed"));
7158
7159 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
7160 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7161 LogoHdr.fu8FadeOut = 1;
7162 else if (RT_FAILURE(rc))
7163 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeOut\" as integer failed"));
7164
7165 rc = pHlp->pfnCFGMQueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
7166 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7167 LogoHdr.u16LogoMillies = 0;
7168 else if (RT_FAILURE(rc))
7169 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"LogoTime\" as integer failed"));
7170
7171 rc = pHlp->pfnCFGMQueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
7172 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7173 LogoHdr.fu8ShowBootMenu = 0;
7174 else if (RT_FAILURE(rc))
7175 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
7176
7177# if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
7178 /* Disable the logo abd menu if all default settings. */
7179 if ( LogoHdr.fu8FadeIn
7180 && LogoHdr.fu8FadeOut
7181 && LogoHdr.u16LogoMillies == 0
7182 && LogoHdr.fu8ShowBootMenu == 2)
7183 {
7184 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
7185 LogoHdr.u16LogoMillies = 500;
7186 }
7187# endif
7188
7189 /* Delay the logo a little bit */
7190 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
7191 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
7192
7193 /*
7194 * Get the Logo file name.
7195 */
7196 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LogoFile", &pThisCC->pszLogoFile);
7197 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7198 pThisCC->pszLogoFile = NULL;
7199 else if (RT_FAILURE(rc))
7200 return PDMDEV_SET_ERROR(pDevIns, rc,
7201 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
7202 else if (!*pThisCC->pszLogoFile)
7203 {
7204 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7205 pThisCC->pszLogoFile = NULL;
7206 }
7207
7208 /*
7209 * Determine the logo size, open any specified logo file in the process.
7210 */
7211 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7212 RTFILE FileLogo = NIL_RTFILE;
7213 if (pThisCC->pszLogoFile)
7214 {
7215 rc = RTFileOpen(&FileLogo, pThisCC->pszLogoFile,
7216 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
7217 if (RT_SUCCESS(rc))
7218 {
7219 uint64_t cbFile;
7220 rc = RTFileQuerySize(FileLogo, &cbFile);
7221 if (RT_SUCCESS(rc))
7222 {
7223 if (cbFile > 0 && cbFile < 32*_1M)
7224 LogoHdr.cbLogo = (uint32_t)cbFile;
7225 else
7226 rc = VERR_TOO_MUCH_DATA;
7227 }
7228 }
7229 if (RT_FAILURE(rc))
7230 {
7231 /*
7232 * Ignore failure and fall back to the default logo.
7233 */
7234 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThisCC->pszLogoFile, rc));
7235 if (FileLogo != NIL_RTFILE)
7236 RTFileClose(FileLogo);
7237 FileLogo = NIL_RTFILE;
7238 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7239 pThisCC->pszLogoFile = NULL;
7240 }
7241 }
7242
7243 /*
7244 * Disable graphic splash screen if it doesn't fit into VRAM.
7245 */
7246 if (pThis->vram_size < LOGO_MAX_SIZE)
7247 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7248
7249 /*
7250 * Allocate buffer for the logo data.
7251 * Let us fall back to default logo on read failure.
7252 */
7253 pThisCC->cbLogo = LogoHdr.cbLogo;
7254 if (g_cbVgaDefBiosLogo)
7255 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogo);
7256 pThisCC->cbLogo += sizeof(LogoHdr);
7257
7258 pThisCC->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbLogo);
7259 if (pThisCC->pbLogo)
7260 {
7261 /*
7262 * Write the logo header.
7263 */
7264 PLOGOHDR pLogoHdr = (PLOGOHDR)pThisCC->pbLogo;
7265 *pLogoHdr = LogoHdr;
7266
7267 /*
7268 * Write the logo bitmap.
7269 */
7270 if (pThisCC->pszLogoFile)
7271 {
7272 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7273 if (RT_SUCCESS(rc))
7274 rc = vbeR3ParseBitmap(pThisCC);
7275 if (RT_FAILURE(rc))
7276 {
7277 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7278 rc, pThisCC->pszLogoFile));
7279 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7280 }
7281 }
7282 if ( !pThisCC->pszLogoFile
7283 || RT_FAILURE(rc))
7284 {
7285 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7286 rc = vbeR3ParseBitmap(pThisCC);
7287 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Parsing of internal bitmap failed! vbeR3ParseBitmap() -> %Rrc\n", rc), rc);
7288 }
7289
7290 rc = VINF_SUCCESS;
7291 }
7292 else
7293 rc = VERR_NO_MEMORY;
7294
7295 /*
7296 * Cleanup.
7297 */
7298 if (FileLogo != NIL_RTFILE)
7299 RTFileClose(FileLogo);
7300
7301# ifdef VBOX_WITH_HGSMI
7302 VBVAInit(pDevIns, pThis, pThisCC);
7303# endif
7304
7305# ifdef VBOX_WITH_VDMA
7306 if (rc == VINF_SUCCESS)
7307 {
7308 rc = vboxVDMAConstruct(pThis, pThisCC, 1024);
7309 AssertRC(rc);
7310 }
7311# endif
7312
7313# ifdef VBOX_WITH_VMSVGA
7314 if ( rc == VINF_SUCCESS
7315 && pThis->fVMSVGAEnabled)
7316 rc = vmsvgaR3Init(pDevIns);
7317# endif
7318
7319 /*
7320 * Statistics.
7321 */
7322# ifdef VBOX_WITH_STATISTICS
7323 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7324 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7325 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7326 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7327 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapPage, STAMTYPE_COUNTER, "MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMmioMapMmio2Page.");
7328 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapReset, STAMTYPE_COUNTER, "MapPageReset", STAMUNIT_OCCURENCES, "Calls to IOMMmioResetRegion.");
7329 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaR3PortUpdateDisplay().");
7330# endif
7331# ifdef VBOX_WITH_HGSMI
7332 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatHgsmiMdaCgaAccesses, STAMTYPE_COUNTER, "HgmsiMdaCgaAccesses", STAMUNIT_OCCURENCES, "Number of non-HGMSI accesses for 03b0-3b3 and 03d0-3d3.");
7333# endif
7334
7335 /* Init latched access mask. */
7336 pThis->uMaskLatchAccess = 0x3ff;
7337
7338 if (RT_SUCCESS(rc))
7339 {
7340 PPDMIBASE pBase;
7341 /*
7342 * Attach status driver (optional).
7343 */
7344 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
7345 if (RT_SUCCESS(rc))
7346 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7347 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7348 {
7349 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7350 rc = VINF_SUCCESS;
7351 }
7352 else
7353 {
7354 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7355 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7356 }
7357 }
7358 return rc;
7359}
7360
7361#else /* !IN_RING3 */
7362
7363/**
7364 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
7365 */
7366static DECLCALLBACK(int) vgaRZConstruct(PPDMDEVINS pDevIns)
7367{
7368 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
7369 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
7370 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
7371
7372 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
7373 AssertRCReturn(rc, rc);
7374
7375 if (pThis->fLegacyVgaEnabled)
7376 {
7377 /*
7378 * Set I/O port callbacks for this context.
7379 * We just copy the ring-3 registration bits and remove the '&' before the handle.
7380 */
7381# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_hIoPort) do { \
7382 rc = PDMDevHlpIoPortSetUpContext(pDevIns, a_hIoPort, a_pfnWrite, a_pfnRead, NULL /*pvUser*/); \
7383 AssertRCReturn(rc, rc); \
7384 } while (0)
7385
7386 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", pThis->hIoPortAr);
7387 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", pThis->hIoPortMsrSt00);
7388 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", pThis->hIoPort3c3);
7389 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", pThis->hIoPortSr);
7390 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", pThis->hIoPortDac);
7391 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ pThis->hIoPortPos);
7392 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", pThis->hIoPortGr);
7393
7394 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", pThis->hIoPortMdaCrt);
7395 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", pThis->hIoPortMdaFcrSt);
7396 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", pThis->hIoPortCgaCrt);
7397 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", pThis->hIoPortCgaFcrSt);
7398
7399# ifdef CONFIG_BOCHS_VBE
7400 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", pThis->hIoPortVbeIndex);
7401 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", pThis->hIoPortVbeData);
7402# endif /* CONFIG_BOCHS_VBE */
7403
7404# undef REG_PORT
7405
7406 /* BIOS port: */
7407 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortBios, vgaIoPortWriteBios, vgaIoPortReadBios, NULL /*pvUser*/);
7408 AssertRCReturn(rc, rc);
7409
7410 /*
7411 * MMIO.
7412 */
7413 rc = PDMDevHlpMmioSetUpContextEx(pDevIns, pThis->hMmioLegacy, vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/);
7414 AssertRCReturn(rc, rc);
7415 }
7416
7417# ifdef VBOX_WITH_VMSVGA
7418 if (pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE)
7419 {
7420 AssertReturn(pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7421 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortVmSvga, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/);
7422 AssertRCReturn(rc, rc);
7423 }
7424 else
7425 AssertReturn(!pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7426
7427 if (pThis->hMmioSvga3 != NIL_IOMMMIOHANDLE)
7428 {
7429 AssertReturn(pThis->fVMSVGAEnabled && pThis->fVmSvga3, VERR_INVALID_STATE);
7430 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioSvga3, vmsvga3MmioWrite, vmsvga3MmioRead, NULL /*pvUser*/);
7431 AssertRCReturn(rc, rc);
7432 }
7433 else
7434 AssertReturn(!pThis->fVmSvga3, VERR_INVALID_STATE);
7435# endif
7436
7437 /*
7438 * Map the start of the VRAM into this context.
7439 */
7440# if defined(VBOX_WITH_2X_4GB_ADDR_SPACE) || (defined(IN_RING0) && defined(VGA_WITH_PARTIAL_RING0_MAPPING))
7441 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VRam, 0 /* off */, VGA_MAPPING_SIZE, (void **)&pThisCC->pbVRam);
7442 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMmio2SetUpContext(,VRAM,0,%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
7443# endif
7444
7445 /*
7446 * Map the first page of the VMSVGA FIFO into this context (not raw-mode).
7447 * We currently only access SVGA_FIFO_MIN, SVGA_FIFO_PITCHLOCK, and SVGA_FIFO_BUSY.
7448 */
7449# if defined(VBOX_WITH_VMSVGA) && !defined(IN_RC)
7450 AssertCompile((RT_MAX(SVGA_FIFO_MIN, RT_MAX(SVGA_FIFO_PITCHLOCK, SVGA_FIFO_BUSY)) + 1) * sizeof(uint32_t) < GUEST_PAGE_SIZE);
7451 if ( pThis->fVMSVGAEnabled
7452 && !pThis->fVmSvga3)
7453 {
7454 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VmSvgaFifo, 0 /* off */, GUEST_PAGE_SIZE,
7455 (void **)&pThisCC->svga.pau32FIFO);
7456 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
7457 }
7458 else
7459 AssertReturn(pThis->hMmio2VmSvgaFifo == NIL_PGMMMIO2HANDLE, VERR_INVALID_STATE);
7460# endif
7461
7462 return VINF_SUCCESS;
7463}
7464
7465#endif /* !IN_RING3 */
7466
7467/**
7468 * The device registration structure.
7469 */
7470const PDMDEVREG g_DeviceVga =
7471{
7472 /* .u32Version = */ PDM_DEVREG_VERSION,
7473 /* .uReserved0 = */ 0,
7474 /* .szName = */ "vga",
7475 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
7476 /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS,
7477 /* .cMaxInstances = */ 1,
7478 /* .uSharedVersion = */ 42,
7479 /* .cbInstanceShared = */ sizeof(VGASTATE),
7480 /* .cbInstanceCC = */ sizeof(VGASTATECC),
7481 /* .cbInstanceRC = */ sizeof(VGASTATERC),
7482 /* .cMaxPciDevices = */ 1,
7483 /* .cMaxMsixVectors = */ 0,
7484 /* .pszDescription = */ "VGA Adaptor with VESA extensions.",
7485#if defined(IN_RING3)
7486 /* .pszRCMod = */ "VBoxDDRC.rc",
7487 /* .pszR0Mod = */ "VBoxDDR0.r0",
7488 /* .pfnConstruct = */ vgaR3Construct,
7489 /* .pfnDestruct = */ vgaR3Destruct,
7490 /* .pfnRelocate = */ vgaR3Relocate,
7491 /* .pfnMemSetup = */ NULL,
7492 /* .pfnPowerOn = */ vgaR3PowerOn,
7493 /* .pfnReset = */ vgaR3Reset,
7494 /* .pfnSuspend = */ NULL,
7495 /* .pfnResume = */ vgaR3Resume,
7496 /* .pfnAttach = */ vgaAttach,
7497 /* .pfnDetach = */ vgaDetach,
7498 /* .pfnQueryInterface = */ NULL,
7499 /* .pfnInitComplete = */ NULL,
7500 /* .pfnPowerOff = */ vgaR3PowerOff,
7501 /* .pfnSoftReset = */ NULL,
7502 /* .pfnReserved0 = */ NULL,
7503 /* .pfnReserved1 = */ NULL,
7504 /* .pfnReserved2 = */ NULL,
7505 /* .pfnReserved3 = */ NULL,
7506 /* .pfnReserved4 = */ NULL,
7507 /* .pfnReserved5 = */ NULL,
7508 /* .pfnReserved6 = */ NULL,
7509 /* .pfnReserved7 = */ NULL,
7510#elif defined(IN_RING0)
7511 /* .pfnEarlyConstruct = */ NULL,
7512 /* .pfnConstruct = */ vgaRZConstruct,
7513 /* .pfnDestruct = */ NULL,
7514 /* .pfnFinalDestruct = */ NULL,
7515 /* .pfnRequest = */ NULL,
7516 /* .pfnReserved0 = */ NULL,
7517 /* .pfnReserved1 = */ NULL,
7518 /* .pfnReserved2 = */ NULL,
7519 /* .pfnReserved3 = */ NULL,
7520 /* .pfnReserved4 = */ NULL,
7521 /* .pfnReserved5 = */ NULL,
7522 /* .pfnReserved6 = */ NULL,
7523 /* .pfnReserved7 = */ NULL,
7524#elif defined(IN_RC)
7525 /* .pfnConstruct = */ vgaRZConstruct,
7526 /* .pfnReserved0 = */ NULL,
7527 /* .pfnReserved1 = */ NULL,
7528 /* .pfnReserved2 = */ NULL,
7529 /* .pfnReserved3 = */ NULL,
7530 /* .pfnReserved4 = */ NULL,
7531 /* .pfnReserved5 = */ NULL,
7532 /* .pfnReserved6 = */ NULL,
7533 /* .pfnReserved7 = */ NULL,
7534#else
7535# error "Not in IN_RING3, IN_RING0 or IN_RC!"
7536#endif
7537 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
7538};
7539
7540#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7541
7542/*
7543 * Local Variables:
7544 * nuke-trailing-whitespace-p:nil
7545 * End:
7546 */
Note: See TracBrowser for help on using the repository browser.

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