VirtualBox

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

Last change on this file since 95463 was 95463, checked in by vboxsync, 3 years ago

DevVGA: vgaIsDirty is now used in R3 only.

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

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