VirtualBox

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

Last change on this file since 104175 was 104175, checked in by vboxsync, 6 weeks ago

Dev/VGA: Spec considers vbe_start_addr as DWORD offset. bugref:10636

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

© 2023 Oracle
ContactPrivacy policyTerms of Use