VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use