VirtualBox

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

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

Devices/Graphics: VBE_DISPI_CFG_ID_VMSVGA_DX

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