VirtualBox

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

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

Devices: Must not use PAGE_SIZE, PAGE_SHIFT, PAGE_OFFSET_MASK, PAGE_ADDRESS or PHYS_PAGE_ADDRESS here either. bugref:9898

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