VirtualBox

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

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

Devices/Graphics/DevVGA.cpp: Fix for unreferenced variable.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 260.7 KB
Line 
1/* $Id: DevVGA.cpp 82607 2019-12-17 14:56:09Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5631 AssertRC(rc);
5632
5633 if (GCPhysAddress != NIL_RTGCPHYS)
5634 {
5635 /*
5636 * Mapping the VRAM.
5637 */
5638 rc = PDMDevHlpMmio2Map(pDevIns, pThis->hMmio2VRam, GCPhysAddress);
5639 AssertLogRelRC(rc);
5640 if (RT_SUCCESS(rc))
5641 {
5642 rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns), GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5643 pThis->hLfbAccessHandlerType, pDevIns, pDevIns->pDevInsR0RemoveMe,
5644 pDevIns->pDevInsForRC, "VGA LFB");
5645 AssertLogRelRC(rc);
5646 if (RT_SUCCESS(rc))
5647 {
5648 pThis->GCPhysVRAM = GCPhysAddress;
5649 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5650 }
5651 rc = VINF_PCI_MAPPING_DONE; /* caller doesn't care about any other status, so no problem overwriting error here */
5652 }
5653 }
5654 else
5655 {
5656 /*
5657 * Unmapping of the VRAM in progress (caller will do that).
5658 * Deregister the access handler so PGM doesn't get upset.
5659 */
5660 Assert(pThis->GCPhysVRAM);
5661# ifdef VBOX_WITH_VMSVGA
5662 Assert(!pThis->svga.fEnabled || !pThis->svga.fVRAMTracking);
5663 if ( !pThis->svga.fEnabled
5664 || ( pThis->svga.fEnabled
5665 && pThis->svga.fVRAMTracking
5666 )
5667 )
5668# endif
5669 {
5670 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5671 AssertRC(rc);
5672 }
5673# ifdef VBOX_WITH_VMSVGA
5674 else
5675 rc = VINF_SUCCESS;
5676# endif
5677 pThis->GCPhysVRAM = 0;
5678 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5679 }
5680 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5681 return rc;
5682}
5683
5684
5685# ifdef VBOX_WITH_VMSVGA /* Currently not needed in the non-VMSVGA mode, but keeping it flexible for later. */
5686/**
5687 * @interface_method_impl{PDMPCIDEV,pfnRegionLoadChangeHookR3}
5688 */
5689static DECLCALLBACK(int) vgaR3PciRegionLoadChangeHook(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5690 uint64_t cbRegion, PCIADDRESSSPACE enmType,
5691 PFNPCIIOREGIONOLDSETTER pfnOldSetter, PFNPCIIOREGIONSWAP pfnSwapRegions)
5692{
5693 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5694
5695# ifdef VBOX_WITH_VMSVGA
5696 if (pThis->fVMSVGAEnabled)
5697 {
5698 /*
5699 * We messed up BAR order for the hybrid devices in 6.0 (see #9359).
5700 * It should have been compatible with the VBox VGA device and had the
5701 * VRAM region first and I/O second, but instead the I/O region ended
5702 * up first and VRAM second like the VMSVGA device.
5703 *
5704 * So, we have to detect that here and reconfigure the memory regions.
5705 * Region numbers are used in our (and the PCI bus') interfaction with
5706 * PGM, so PGM needs to be informed too.
5707 */
5708 if ( iRegion == 0
5709 && iRegion == pThis->pciRegions.iVRAM
5710 && (enmType & PCI_ADDRESS_SPACE_IO))
5711 {
5712 LogRel(("VGA: Detected old BAR config, making adjustments.\n"));
5713
5714 /* Update the entries. */
5715 pThis->pciRegions.iIO = 0;
5716 pThis->pciRegions.iVRAM = 1;
5717
5718 /* Update PGM on the region number change so it won't barf when restoring state. */
5719 AssertLogRelReturn(pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo, VERR_VERSION_MISMATCH);
5720 int rc = pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo(pDevIns, pThis->hMmio2VRam, 1);
5721 AssertLogRelRCReturn(rc, rc);
5722 /** @todo Update the I/O port too, only currently we don't give a hoot about
5723 * the region number in the I/O port registrations so it can wait...
5724 * (Only visible in the 'info ioport' output IIRC). */
5725
5726 /* Update the calling PCI device. */
5727 AssertLogRelReturn(pfnSwapRegions, VERR_INTERNAL_ERROR_2);
5728 rc = pfnSwapRegions(pPciDev, 0, 1);
5729 AssertLogRelRCReturn(rc, rc);
5730
5731 return rc;
5732 }
5733
5734 /*
5735 * The VMSVGA changed the default FIFO size from 128KB to 2MB after 5.1.
5736 */
5737 if (iRegion == pThis->pciRegions.iFIFO)
5738 {
5739 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5740 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5741 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5742
5743 /* If the size didn't change we're fine, so just return already. */
5744 if (cbRegion == pThis->svga.cbFIFO)
5745 return VINF_SUCCESS;
5746
5747 /* If the size is larger than the current configuration, refuse to load. */
5748 AssertLogRelMsgReturn(cbRegion <= pThis->svga.cbFIFOConfig,
5749 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x\n",
5750 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO),
5751 VERR_SSM_LOAD_CONFIG_MISMATCH);
5752
5753 /* Adjust the size down. */
5754 int rc = PDMDevHlpMmio2Reduce(pDevIns, pThis->hMmio2VmSvgaFifo, cbRegion);
5755 AssertLogRelMsgRCReturn(rc,
5756 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x: %Rrc\n",
5757 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO, rc),
5758 rc);
5759 pThis->svga.cbFIFO = cbRegion;
5760 return rc;
5761
5762 }
5763
5764 /*
5765 * VRAM used to be non-prefetchable till 6.1.0, so we end up here when restoring
5766 * states older than that with 6.1.0 and later. We just have to check that
5767 * the size and basic type matches, then return VINF_SUCCESS to ACK it.
5768 */
5769 if (iRegion == pThis->pciRegions.iVRAM)
5770 {
5771 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5772 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5773 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5774 /* The size must be the same. */
5775 AssertLogRelMsgReturn(cbRegion == pThis->vram_size,
5776 ("cbRegion=%#RGp vram_size=%#x\n", cbRegion, pThis->vram_size),
5777 VERR_SSM_LOAD_CONFIG_MISMATCH);
5778 return VINF_SUCCESS;
5779 }
5780
5781 /* Emulate callbacks for 5.1 and older saved states by recursion. */
5782 if (iRegion == UINT32_MAX)
5783 {
5784 int rc = vgaR3PciRegionLoadChangeHook(pDevIns, pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD,
5785 PCI_ADDRESS_SPACE_MEM, NULL, NULL);
5786 if (RT_SUCCESS(rc))
5787 rc = pfnOldSetter(pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM);
5788 return rc;
5789 }
5790 }
5791# endif /* VBOX_WITH_VMSVGA */
5792
5793 return VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE;
5794}
5795# endif /* VBOX_WITH_VMSVGA */
5796
5797
5798/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5799
5800/**
5801 * Saves a important bits of the VGA device config.
5802 *
5803 * @param pHlp The device helpers (for SSM functions).
5804 * @param pThis The shared VGA instance data.
5805 * @param pSSM The saved state handle.
5806 */
5807static void vgaR3SaveConfig(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PSSMHANDLE pSSM)
5808{
5809 pHlp->pfnSSMPutU32(pSSM, pThis->vram_size);
5810 pHlp->pfnSSMPutU32(pSSM, pThis->cMonitors);
5811}
5812
5813
5814/**
5815 * @callback_method_impl{FNSSMDEVLIVEEXEC}
5816 */
5817static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5818{
5819 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5820 Assert(uPass == 0); NOREF(uPass);
5821 vgaR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
5822 return VINF_SSM_DONT_CALL_AGAIN;
5823}
5824
5825
5826/**
5827 * @callback_method_impl{FNSSMDEVSAVEPREP}
5828 */
5829static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5830{
5831# ifdef VBOX_WITH_VIDEOHWACCEL
5832 RT_NOREF(pSSM);
5833 return vboxVBVASaveStatePrep(pDevIns);
5834# else
5835 RT_NOREF(pDevIns, pSSM);
5836 return VINF_SUCCESS;
5837# endif
5838}
5839
5840
5841/**
5842 * @callback_method_impl{FNSSMDEVSAVEDONE}
5843 */
5844static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5845{
5846# ifdef VBOX_WITH_VIDEOHWACCEL
5847 RT_NOREF(pSSM);
5848 return vboxVBVASaveStateDone(pDevIns);
5849# else
5850 RT_NOREF(pDevIns, pSSM);
5851 return VINF_SUCCESS;
5852# endif
5853}
5854
5855
5856/**
5857 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5858 */
5859static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5860{
5861 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5862 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5863 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5864
5865# ifdef VBOX_WITH_VDMA
5866 vboxVDMASaveStateExecPrep(pThisCC->pVdma);
5867# endif
5868
5869 vgaR3SaveConfig(pHlp, pThis, pSSM);
5870 vga_save(pHlp, pSSM, PDMDEVINS_2_DATA(pDevIns, PVGASTATE));
5871
5872 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5873# ifdef VBOX_WITH_HGSMI
5874 pHlp->pfnSSMPutBool(pSSM, true);
5875 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5876# else
5877 int rc = pHlp->pfnSSMPutBool(pSSM, false);
5878# endif
5879
5880 AssertRCReturn(rc, rc);
5881
5882 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5883# ifdef VBOX_WITH_VDMA
5884 rc = pHlp->pfnSSMPutU32(pSSM, 1);
5885 AssertRCReturn(rc, rc);
5886 rc = vboxVDMASaveStateExecPerform(pHlp, pThisCC->pVdma, pSSM);
5887# else
5888 rc = pHlp->pfnSSMPutU32(pSSM, 0);
5889# endif
5890 AssertRCReturn(rc, rc);
5891
5892# ifdef VBOX_WITH_VDMA
5893 vboxVDMASaveStateExecDone(pThisCC->pVdma);
5894# endif
5895
5896 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5897# ifdef VBOX_WITH_VMSVGA
5898 if (pThis->fVMSVGAEnabled)
5899 {
5900 rc = vmsvgaR3SaveExec(pDevIns, pSSM);
5901 AssertRCReturn(rc, rc);
5902 }
5903# endif
5904 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5905
5906 return rc;
5907}
5908
5909
5910/**
5911 * @callback_method_impl{FNSSMDEVLOADPREP}
5912 */
5913static DECLCALLBACK(int) vgaR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5914{
5915 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5916 RT_NOREF(pSSM);
5917 pThis->fStateLoaded = true;
5918 return VINF_SUCCESS;
5919}
5920
5921
5922/**
5923 * @callback_method_impl{FNSSMDEVLOADEXEC}
5924 */
5925static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5926{
5927 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5928 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5929 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5930 int rc;
5931
5932 pThis->fStateLoaded = true;
5933
5934 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5935 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5936
5937 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5938 {
5939 /* Check the config */
5940 uint32_t cbVRam;
5941 rc = pHlp->pfnSSMGetU32(pSSM, &cbVRam);
5942 AssertRCReturn(rc, rc);
5943 if (pThis->vram_size != cbVRam)
5944 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5945
5946 uint32_t cMonitors;
5947 rc = pHlp->pfnSSMGetU32(pSSM, &cMonitors);
5948 AssertRCReturn(rc, rc);
5949 if (pThis->cMonitors != cMonitors)
5950 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5951 }
5952
5953 if (uPass == SSM_PASS_FINAL)
5954 {
5955 rc = vga_load(pHlp, pSSM, pThis, uVersion);
5956 if (RT_FAILURE(rc))
5957 return rc;
5958
5959 /*
5960 * Restore the HGSMI state, if present.
5961 */
5962 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5963 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5964 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5965 {
5966 rc = pHlp->pfnSSMGetBool(pSSM, &fWithHgsmi);
5967 AssertRCReturn(rc, rc);
5968 }
5969 if (fWithHgsmi)
5970 {
5971# ifdef VBOX_WITH_HGSMI
5972 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5973 AssertRCReturn(rc, rc);
5974# else
5975 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5976# endif
5977 }
5978
5979 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5980 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5981 {
5982 uint32_t u32;
5983 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
5984 if (u32)
5985 {
5986# ifdef VBOX_WITH_VDMA
5987 if (u32 == 1)
5988 {
5989 rc = vboxVDMASaveLoadExecPerform(pHlp, pThisCC->pVdma, pSSM, uVersion);
5990 AssertRCReturn(rc, rc);
5991 }
5992 else
5993# endif
5994 {
5995 LogRel(("invalid CmdVbva version info\n"));
5996 return VERR_VERSION_MISMATCH;
5997 }
5998 }
5999 }
6000
6001 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
6002# ifdef VBOX_WITH_VMSVGA
6003 if (pThis->fVMSVGAEnabled)
6004 {
6005 rc = vmsvgaR3LoadExec(pDevIns, pSSM, uVersion, uPass);
6006 AssertRCReturn(rc, rc);
6007 }
6008# endif
6009 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
6010 }
6011 return VINF_SUCCESS;
6012}
6013
6014
6015/**
6016 * @@callback_method_impl{FNSSMDEVLOADDONE}
6017 */
6018static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
6019{
6020 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6021 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6022 int rc;
6023 RT_NOREF(pThisCC, pThis, pSSM);
6024
6025# ifdef VBOX_WITH_HGSMI
6026 rc = vboxVBVALoadStateDone(pDevIns);
6027 AssertRCReturn(rc, rc);
6028# ifdef VBOX_WITH_VDMA
6029 rc = vboxVDMASaveLoadDone(pThisCC->pVdma);
6030 AssertRCReturn(rc, rc);
6031# endif
6032 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
6033 VBVAOnVBEChanged(pThis, pThisCC);
6034# endif
6035# ifdef VBOX_WITH_VMSVGA
6036 if (pThis->fVMSVGAEnabled)
6037 {
6038 rc = vmsvgaR3LoadDone(pDevIns);
6039 AssertRCReturn(rc, rc);
6040 }
6041# endif
6042 return VINF_SUCCESS;
6043}
6044
6045
6046/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
6047
6048/**
6049 * @interface_method_impl{PDMDEVREG,pfnResume}
6050 */
6051static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
6052{
6053 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6054 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6055 VBVAOnResume(pDevIns, pThis, pThisCC);
6056}
6057
6058
6059/**
6060 * @interface_method_impl{PDMDEVREG,pfnReset}
6061 */
6062static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
6063{
6064 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6065 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6066 char *pchStart;
6067 char *pchEnd;
6068 LogFlow(("vgaReset\n"));
6069
6070 if (pThisCC->pVdma)
6071 vboxVDMAReset(pThisCC->pVdma);
6072
6073# ifdef VBOX_WITH_VMSVGA
6074 if (pThis->fVMSVGAEnabled)
6075 vmsvgaR3Reset(pDevIns);
6076# endif
6077
6078# ifdef VBOX_WITH_HGSMI
6079 VBVAReset(pDevIns, pThis, pThisCC);
6080# endif
6081
6082
6083 /* Clear the VRAM ourselves. */
6084 if (pThisCC->pbVRam && pThis->vram_size)
6085 memset(pThisCC->pbVRam, 0, pThis->vram_size);
6086
6087 /*
6088 * Zero most of it.
6089 *
6090 * Unlike vga_reset we're leaving out a few members which we believe
6091 * must remain unchanged....
6092 */
6093 /* 1st part. */
6094 pchStart = (char *)&pThis->latch;
6095 pchEnd = (char *)&pThis->invalidated_y_table;
6096 memset(pchStart, 0, pchEnd - pchStart);
6097
6098 /* 2nd part. */
6099 pchStart = (char *)&pThis->last_palette;
6100 pchEnd = (char *)&pThis->u32Marker;
6101 memset(pchStart, 0, pchEnd - pchStart);
6102
6103
6104 /*
6105 * Restore and re-init some bits.
6106 */
6107 pThisCC->get_bpp = vgaR3GetBpp;
6108 pThisCC->get_offsets = vgaR3GetOffsets;
6109 pThisCC->get_resolution = vgaR3GetResolution;
6110 pThis->graphic_mode = -1; /* Force full update. */
6111# ifdef CONFIG_BOCHS_VBE
6112 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
6113 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
6114 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
6115 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
6116# endif /* CONFIG_BOCHS_VBE */
6117 pThis->st00 = 0x70; /* Static except for bit 4. */
6118
6119 /*
6120 * Reset the LFB mapping.
6121 */
6122 pThis->fLFBUpdated = false;
6123 if ( ( pDevIns->fRCEnabled
6124 || pDevIns->fR0Enabled)
6125 && pThis->GCPhysVRAM
6126 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
6127 {
6128 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
6129 AssertRC(rc);
6130 }
6131 if (pThis->fRemappedVGA)
6132 {
6133 IOMMmioResetRegion(PDMDevHlpGetVM(pDevIns), pDevIns, pThis->hMmioLegacy);
6134 pThis->fRemappedVGA = false;
6135 }
6136
6137 /*
6138 * Reset the logo data.
6139 */
6140 pThisCC->LogoCommand = LOGO_CMD_NOP;
6141 pThisCC->offLogoData = 0;
6142
6143 /* notify port handler */
6144 if (pThisCC->pDrv)
6145 {
6146 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* hack around lock order issue. */
6147 pThisCC->pDrv->pfnReset(pThisCC->pDrv);
6148 pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, false, false, 0, 0, 0, 0, NULL);
6149 PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
6150 }
6151
6152 /* Reset latched access mask. */
6153 pThis->uMaskLatchAccess = 0x3ff;
6154 pThis->cLatchAccesses = 0;
6155 pThis->u64LastLatchedAccess = 0;
6156 pThis->iMask = 0;
6157
6158 /* Reset retrace emulation. */
6159 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
6160}
6161
6162
6163/**
6164 * @interface_method_impl{PDMDEVREG,pfnPowerOn}
6165 */
6166static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
6167{
6168 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6169 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6170# ifdef VBOX_WITH_VMSVGA
6171 vmsvgaR3PowerOn(pDevIns);
6172# endif
6173 VBVAOnResume(pDevIns, pThis, pThisCC);
6174}
6175
6176
6177/**
6178 * @interface_method_impl{PDMDEVREG,pfnRelocate}
6179 */
6180static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
6181{
6182# ifdef VBOX_WITH_RAW_MODE_KEEP
6183 if (offDelta)
6184 {
6185 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6186 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
6187
6188 pThisRC->pbVRam += offDelta;
6189 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6190 }
6191# else
6192 RT_NOREF(pDevIns, offDelta);
6193# endif
6194}
6195
6196
6197/**
6198 * @interface_method_impl{PDMDEVREG,pfnAttach}
6199 *
6200 * This is like plugging in the monitor after turning on the PC.
6201 */
6202static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6203{
6204 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6205 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6206
6207 RT_NOREF(pThis);
6208
6209 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
6210 ("VGA device does not support hotplugging\n"),
6211 VERR_INVALID_PARAMETER);
6212
6213 switch (iLUN)
6214 {
6215 /* LUN #0: Display port. */
6216 case 0:
6217 {
6218 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "Display Port");
6219 if (RT_SUCCESS(rc))
6220 {
6221 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIDISPLAYCONNECTOR);
6222 if (pThisCC->pDrv)
6223 {
6224 /* pThisCC->pDrv->pbData can be NULL when there is no framebuffer. */
6225 if ( pThisCC->pDrv->pfnRefresh
6226 && pThisCC->pDrv->pfnResize
6227 && pThisCC->pDrv->pfnUpdateRect)
6228 rc = VINF_SUCCESS;
6229 else
6230 {
6231 Assert(pThisCC->pDrv->pfnRefresh);
6232 Assert(pThisCC->pDrv->pfnResize);
6233 Assert(pThisCC->pDrv->pfnUpdateRect);
6234 pThisCC->pDrv = NULL;
6235 pThisCC->pDrvBase = NULL;
6236 rc = VERR_INTERNAL_ERROR;
6237 }
6238# ifdef VBOX_WITH_VIDEOHWACCEL
6239 if(rc == VINF_SUCCESS)
6240 {
6241 rc = vbvaVHWAConstruct(pDevIns, pThis, pThisCC);
6242 if (rc != VERR_NOT_IMPLEMENTED)
6243 AssertRC(rc);
6244 }
6245# endif
6246 }
6247 else
6248 {
6249 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
6250 pThisCC->pDrvBase = NULL;
6251 rc = VERR_PDM_MISSING_INTERFACE;
6252 }
6253 }
6254 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6255 {
6256 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6257 rc = VINF_SUCCESS;
6258 }
6259 else
6260 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
6261 return rc;
6262 }
6263
6264 default:
6265 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6266 return VERR_PDM_NO_SUCH_LUN;
6267 }
6268}
6269
6270
6271/**
6272 * @interface_method_impl{PDMDEVREG,pfnDetach}
6273 *
6274 * This is like unplugging the monitor while the PC is still running.
6275 */
6276static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6277{
6278 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6279 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
6280 RT_NOREF(fFlags);
6281
6282 /*
6283 * Reset the interfaces and update the controller state.
6284 */
6285 switch (iLUN)
6286 {
6287 /* LUN #0: Display port. */
6288 case 0:
6289 pThisCC->pDrv = NULL;
6290 pThisCC->pDrvBase = NULL;
6291 break;
6292
6293 default:
6294 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6295 break;
6296 }
6297}
6298
6299
6300/**
6301 * @interface_method_impl{PDMDEVREG,pfnDestruct}
6302 */
6303static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6304{
6305 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
6306 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6307 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6308 LogFlow(("vgaR3Destruct:\n"));
6309
6310# ifdef VBOX_WITH_VDMA
6311 if (pThisCC->pVdma)
6312 vboxVDMADestruct(pThisCC->pVdma);
6313# endif
6314
6315# ifdef VBOX_WITH_VMSVGA
6316 if (pThis->fVMSVGAEnabled)
6317 vmsvgaR3Destruct(pDevIns);
6318# endif
6319
6320# ifdef VBOX_WITH_HGSMI
6321 VBVADestroy(pThisCC);
6322# endif
6323
6324 /*
6325 * Free MM heap pointers.
6326 */
6327 if (pThisCC->pbVBEExtraData)
6328 {
6329 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVBEExtraData);
6330 pThisCC->pbVBEExtraData = NULL;
6331 }
6332 if (pThisCC->pbVgaBios)
6333 {
6334 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6335 pThisCC->pbVgaBios = NULL;
6336 }
6337
6338 if (pThisCC->pszVgaBiosFile)
6339 {
6340 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6341 pThisCC->pszVgaBiosFile = NULL;
6342 }
6343
6344 if (pThisCC->pszLogoFile)
6345 {
6346 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
6347 pThisCC->pszLogoFile = NULL;
6348 }
6349
6350 if (pThisCC->pbLogo)
6351 {
6352 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbLogo);
6353 pThisCC->pbLogo = NULL;
6354 }
6355
6356# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
6357 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSectIRQ);
6358# endif
6359 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
6360 return VINF_SUCCESS;
6361}
6362
6363
6364/**
6365 * Adjust VBE mode information
6366 *
6367 * Depending on the configured VRAM size, certain parts of VBE mode
6368 * information must be updated.
6369 *
6370 * @param pThis The device instance data.
6371 * @param pMode The mode information structure.
6372 */
6373static void vgaR3AdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6374{
6375 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6376 unsigned bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6377 /* The "number of image pages" is really the max page index... */
6378 unsigned maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6379 if (maxPage > 255)
6380 maxPage = 255; /* 8-bit value. */
6381 pMode->info.NumberOfImagePages = maxPage;
6382 pMode->info.LinNumberOfPages = maxPage;
6383}
6384
6385
6386/**
6387 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6388 */
6389static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6390{
6391 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6392 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6393 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6394 PVM pVM = PDMDevHlpGetVM(pDevIns);
6395 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
6396 int rc;
6397 unsigned i;
6398 uint32_t cCustomModes;
6399 uint32_t cyReduction;
6400 uint32_t cbPitch;
6401 PVBEHEADER pVBEDataHdr;
6402 ModeInfoListItem *pCurMode;
6403 unsigned cb;
6404
6405 Assert(iInstance == 0);
6406 Assert(pVM);
6407
6408 /*
6409 * Init static data.
6410 */
6411 static bool s_fExpandDone = false;
6412 if (!s_fExpandDone)
6413 {
6414 s_fExpandDone = true;
6415 vgaR3InitExpand();
6416 }
6417
6418 /*
6419 * Validate configuration.
6420 */
6421 static const char s_szMscWorkaround[] = "VRamSize"
6422 "|MonitorCount"
6423 "|FadeIn"
6424 "|FadeOut"
6425 "|LogoTime"
6426 "|LogoFile"
6427 "|ShowBootMenu"
6428 "|BiosRom"
6429 "|RealRetrace"
6430 "|CustomVideoModes"
6431 "|HeightReduction"
6432 "|CustomVideoMode1"
6433 "|CustomVideoMode2"
6434 "|CustomVideoMode3"
6435 "|CustomVideoMode4"
6436 "|CustomVideoMode5"
6437 "|CustomVideoMode6"
6438 "|CustomVideoMode7"
6439 "|CustomVideoMode8"
6440 "|CustomVideoMode9"
6441 "|CustomVideoMode10"
6442 "|CustomVideoMode11"
6443 "|CustomVideoMode12"
6444 "|CustomVideoMode13"
6445 "|CustomVideoMode14"
6446 "|CustomVideoMode15"
6447 "|CustomVideoMode16"
6448 "|MaxBiosXRes"
6449 "|MaxBiosYRes"
6450# ifdef VBOX_WITH_VMSVGA
6451 "|VMSVGAEnabled"
6452 "|VMSVGAPciId"
6453 "|VMSVGAPciBarLayout"
6454 "|VMSVGAFifoSize"
6455# endif
6456# ifdef VBOX_WITH_VMSVGA3D
6457 "|VMSVGA3dEnabled"
6458# endif
6459 "|SuppressNewYearSplash"
6460 "|3DEnabled";
6461
6462 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, s_szMscWorkaround, "");
6463
6464 /*
6465 * Init state data.
6466 */
6467 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6468 AssertLogRelRCReturn(rc, rc);
6469 if (pThis->vram_size > VGA_VRAM_MAX)
6470 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6471 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6472 if (pThis->vram_size < VGA_VRAM_MIN)
6473 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6474 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6475 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6476 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6477 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6478
6479 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6480 AssertLogRelRCReturn(rc, rc);
6481
6482 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
6483
6484 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "3DEnabled", &pThis->f3DEnabled, false);
6485 AssertLogRelRCReturn(rc, rc);
6486 Log(("VGA: f3DEnabled=%RTbool\n", pThis->f3DEnabled));
6487
6488# ifdef VBOX_WITH_VMSVGA
6489 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6490 AssertLogRelRCReturn(rc, rc);
6491 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6492
6493 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciId", &pThis->fVMSVGAPciId, false);
6494 AssertLogRelRCReturn(rc, rc);
6495 Log(("VMSVGA: VMSVGAPciId = %d\n", pThis->fVMSVGAPciId));
6496
6497 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciBarLayout", &pThis->fVMSVGAPciBarLayout, pThis->fVMSVGAPciId);
6498 AssertLogRelRCReturn(rc, rc);
6499 Log(("VMSVGA: VMSVGAPciBarLayout = %d\n", pThis->fVMSVGAPciBarLayout));
6500
6501 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6502 AssertLogRelRCReturn(rc, rc);
6503 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6504 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6505 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6506 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6507 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6508# endif
6509# ifdef VBOX_WITH_VMSVGA3D
6510 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6511 AssertLogRelRCReturn(rc, rc);
6512 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6513# endif
6514
6515# ifdef VBOX_WITH_VMSVGA
6516 if (pThis->fVMSVGAPciBarLayout)
6517 {
6518 pThis->pciRegions.iIO = 0;
6519 pThis->pciRegions.iVRAM = 1;
6520 }
6521 else
6522 {
6523 pThis->pciRegions.iVRAM = 0;
6524 pThis->pciRegions.iIO = 1;
6525 }
6526 pThis->pciRegions.iFIFO = 2;
6527# else
6528 pThis->pciRegions.iVRAM = 0;
6529# endif
6530
6531 pThisCC->pDevIns = pDevIns;
6532
6533 vgaR3Reset(pDevIns);
6534
6535 /* The PCI devices configuration. */
6536 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
6537 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
6538
6539# ifdef VBOX_WITH_VMSVGA
6540 if (pThis->fVMSVGAEnabled)
6541 {
6542 /* Extend our VGA device with VMWare SVGA functionality. */
6543 if (pThis->fVMSVGAPciId)
6544 {
6545 PDMPciDevSetVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6546 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6547 }
6548 else
6549 {
6550 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6551 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6552 }
6553 PDMPciDevSetSubSystemVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6554 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6555 }
6556 else
6557# endif /* VBOX_WITH_VMSVGA */
6558 {
6559 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6560 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6561 }
6562 PDMPciDevSetClassSub(pPciDev, 0x00); /* VGA controller */
6563 PDMPciDevSetClassBase(pPciDev, 0x03);
6564 PDMPciDevSetHeaderType(pPciDev, 0x00);
6565# if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6566 PDMPciDevSetInterruptPin(pPciDev, 1);
6567# endif
6568
6569 /* the interfaces. */
6570 pThisCC->IBase.pfnQueryInterface = vgaR3PortQueryInterface;
6571
6572 pThisCC->IPort.pfnUpdateDisplay = vgaR3PortUpdateDisplay;
6573 pThisCC->IPort.pfnUpdateDisplayAll = vgaR3PortUpdateDisplayAll;
6574 pThisCC->IPort.pfnQueryVideoMode = vgaR3PortQueryVideoMode;
6575 pThisCC->IPort.pfnSetRefreshRate = vgaR3PortSetRefreshRate;
6576 pThisCC->IPort.pfnTakeScreenshot = vgaR3PortTakeScreenshot;
6577 pThisCC->IPort.pfnFreeScreenshot = vgaR3PortFreeScreenshot;
6578 pThisCC->IPort.pfnDisplayBlt = vgaR3PortDisplayBlt;
6579 pThisCC->IPort.pfnUpdateDisplayRect = vgaR3PortUpdateDisplayRect;
6580 pThisCC->IPort.pfnCopyRect = vgaR3PortCopyRect;
6581 pThisCC->IPort.pfnSetRenderVRAM = vgaR3PortSetRenderVRAM;
6582# ifdef VBOX_WITH_VMSVGA
6583 pThisCC->IPort.pfnSetViewport = vmsvgaR3PortSetViewport;
6584# else
6585 pThisCC->IPort.pfnSetViewport = NULL;
6586# endif
6587 pThisCC->IPort.pfnSendModeHint = vbvaR3PortSendModeHint;
6588 pThisCC->IPort.pfnReportHostCursorCapabilities = vgaR3PortReportHostCursorCapabilities;
6589 pThisCC->IPort.pfnReportHostCursorPosition = vgaR3PortReportHostCursorPosition;
6590
6591# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
6592 pThisCC->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaR3VHWACommandCompleteAsync;
6593# endif
6594
6595 pThisCC->ILeds.pfnQueryStatusLed = vgaR3PortQueryStatusLed;
6596 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6597
6598 /*
6599 * We use our own critical section to avoid unncessary pointer indirections
6600 * in interface methods (as well as for historical reasons).
6601 */
6602 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6603 AssertRCReturn(rc, rc);
6604 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6605 AssertRCReturn(rc, rc);
6606
6607# ifdef VBOX_WITH_HGSMI
6608 /*
6609 * This critical section is used by vgaR3IOPortHgsmiWrite, VBVARaiseIrq and VBVAOnResume
6610 * for some IRQ related synchronization.
6611 */
6612 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6613 AssertRCReturn(rc, rc);
6614# endif
6615
6616 /*
6617 * PCI device registration.
6618 */
6619 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6620 if (RT_FAILURE(rc))
6621 return rc;
6622 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6623 if (pPciDev->uDevFn != 16 && iInstance == 0)
6624 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pPciDev->uDevFn));
6625
6626# ifdef VBOX_WITH_VMSVGA
6627 pThis->hIoPortVmSvga = NIL_IOMIOPORTHANDLE;
6628 pThis->hMmio2VmSvgaFifo = NIL_PGMMMIO2HANDLE;
6629 if (pThis->fVMSVGAEnabled)
6630 {
6631 /* Register the io command ports. */
6632 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, pThis->pciRegions.iIO, 0x10, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/,
6633 "VMSVGA", NULL /*paExtDescs*/, &pThis->hIoPortVmSvga);
6634 AssertRCReturn(rc, rc);
6635
6636 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iFIFO, pThis->svga.cbFIFO,
6637 PCI_ADDRESS_SPACE_MEM, 0 /*fFlags*/, vmsvgaR3PciIORegionFifoMapUnmap,
6638 "VMSVGA-FIFO", (void **)&pThisCC->svga.pau32FIFO, &pThis->hMmio2VmSvgaFifo);
6639 AssertRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6640 N_("Failed to create VMSVGA FIFO (%u bytes)"), pThis->svga.cbFIFO));
6641
6642 pPciDev->pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6643 }
6644# endif /* VBOX_WITH_VMSVGA */
6645
6646 /*
6647 * Allocate VRAM and create a PCI region for it.
6648 */
6649 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iVRAM, pThis->vram_size,
6650 PCI_ADDRESS_SPACE_MEM_PREFETCH, 0 /*fFlags*/, vgaR3PciIORegionVRamMapUnmap,
6651 "VRam", (void **)&pThisCC->pbVRam, &pThis->hMmio2VRam);
6652 AssertLogRelRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6653 N_("Failed to allocate %u bytes of VRAM"), pThis->vram_size));
6654# ifndef VGA_WITH_PARTIAL_RING0_MAPPING
6655 pThis->vram_ptrR0 = (RTR0PTR)pThisCC->pbVRam;
6656# endif
6657
6658 /*
6659 * Register access handler types for tracking dirty VRAM pages.
6660 */
6661 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
6662 vgaLFBAccessHandler,
6663 g_DeviceVga.pszR0Mod, "vgaLFBAccessHandler", "vgaLbfAccessPfHandler",
6664 g_DeviceVga.pszRCMod, "vgaLFBAccessHandler", "vgaLbfAccessPfHandler",
6665 "VGA LFB", &pThis->hLfbAccessHandlerType);
6666 AssertRCReturn(rc, rc);
6667
6668 /*
6669 * Register I/O ports.
6670 */
6671# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_phIoPort) do { \
6672 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, a_uPort, a_cPorts, IOM_IOPORT_F_ABS, \
6673 a_pfnWrite, a_pfnRead, "VGA - " a_szDesc, NULL /*paExtDescs*/, a_phIoPort); \
6674 AssertRCReturn(rc, rc); \
6675 } while (0)
6676 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", &pThis->hIoPortAr);
6677 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", &pThis->hIoPortMsrSt00);
6678 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", &pThis->hIoPort3c3);
6679 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", &pThis->hIoPortSr);
6680 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", &pThis->hIoPortDac);
6681 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ &pThis->hIoPortPos);
6682 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", &pThis->hIoPortGr);
6683
6684 /* Note! Ralf Brown lists 0x3b0-0x3b1, 0x3b2-0x3b3 and 0x3b6-0x3b7 as "the same as" 0x3b4-0x3b5. */
6685 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", &pThis->hIoPortMdaCrt);
6686 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", &pThis->hIoPortMdaFcrSt);
6687 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", &pThis->hIoPortCgaCrt);
6688 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", &pThis->hIoPortCgaFcrSt);
6689
6690# ifdef CONFIG_BOCHS_VBE
6691 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", &pThis->hIoPortVbeIndex);
6692 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", &pThis->hIoPortVbeData);
6693# endif /* CONFIG_BOCHS_VBE */
6694
6695# ifdef VBOX_WITH_HGSMI
6696 /* Use reserved VGA IO ports for HGSMI. */
6697 REG_PORT(VGA_PORT_HGSMI_HOST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI host (3b0-3b3)", &pThis->hIoPortHgsmiHost);
6698 REG_PORT(VGA_PORT_HGSMI_GUEST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI guest (3d0-3d3)", &pThis->hIoPortHgsmiGuest);
6699# endif /* VBOX_WITH_HGSMI */
6700
6701# undef REG_PORT
6702
6703 /* vga bios */
6704 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_PRINTF_PORT, 1 /*cPorts*/, vgaIoPortWriteBios, vgaIoPortReadBios,
6705 "VGA BIOS debug/panic", NULL /*paExtDescs*/, &pThis->hIoPortBios);
6706 AssertRCReturn(rc, rc);
6707
6708 /*
6709 * The MDA/CGA/EGA/VGA/whatever fixed MMIO area.
6710 */
6711 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, 0x000a0000, 0x00020000,
6712 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
6713 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
6714 vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/,
6715 "VGA - VGA Video Buffer", &pThis->hMmioLegacy);
6716 AssertRCReturn(rc, rc);
6717
6718 /*
6719 * Get the VGA BIOS ROM file name.
6720 */
6721 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThisCC->pszVgaBiosFile);
6722 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6723 {
6724 pThisCC->pszVgaBiosFile = NULL;
6725 rc = VINF_SUCCESS;
6726 }
6727 else if (RT_FAILURE(rc))
6728 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6729 else if (!*pThisCC->pszVgaBiosFile)
6730 {
6731 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6732 pThisCC->pszVgaBiosFile = NULL;
6733 }
6734
6735 /*
6736 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6737 */
6738 RTFILE FileVgaBios = NIL_RTFILE;
6739 if (pThisCC->pszVgaBiosFile)
6740 {
6741 rc = RTFileOpen(&FileVgaBios, pThisCC->pszVgaBiosFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6742 if (RT_SUCCESS(rc))
6743 {
6744 rc = RTFileQuerySize(FileVgaBios, &pThisCC->cbVgaBios);
6745 if (RT_SUCCESS(rc))
6746 {
6747 if ( RT_ALIGN(pThisCC->cbVgaBios, _4K) != pThisCC->cbVgaBios
6748 || pThisCC->cbVgaBios > _64K
6749 || pThisCC->cbVgaBios < 16 * _1K)
6750 rc = VERR_TOO_MUCH_DATA;
6751 }
6752 }
6753 if (RT_FAILURE(rc))
6754 {
6755 /*
6756 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6757 */
6758 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThisCC->pszVgaBiosFile, rc));
6759 RTFileClose(FileVgaBios);
6760 FileVgaBios = NIL_RTFILE;
6761 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6762 pThisCC->pszVgaBiosFile = NULL;
6763 }
6764 }
6765
6766 /*
6767 * Attempt to get the VGA BIOS ROM data from file.
6768 */
6769 if (pThisCC->pszVgaBiosFile)
6770 {
6771 /*
6772 * Allocate buffer for the VGA BIOS ROM data.
6773 */
6774 pThisCC->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbVgaBios);
6775 if (pThisCC->pbVgaBios)
6776 {
6777 rc = RTFileRead(FileVgaBios, pThisCC->pbVgaBios, pThisCC->cbVgaBios, NULL);
6778 if (RT_FAILURE(rc))
6779 {
6780 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThisCC->cbVgaBios, rc));
6781 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6782 pThisCC->pbVgaBios = NULL;
6783 }
6784 rc = VINF_SUCCESS;
6785 }
6786 else
6787 rc = VERR_NO_MEMORY;
6788 }
6789 else
6790 pThisCC->pbVgaBios = NULL;
6791
6792 /* cleanup */
6793 if (FileVgaBios != NIL_RTFILE)
6794 RTFileClose(FileVgaBios);
6795
6796 /* If we were unable to get the data from file for whatever reason, fall
6797 back to the built-in ROM image. */
6798 const uint8_t *pbVgaBiosBinary;
6799 uint64_t cbVgaBiosBinary;
6800 uint32_t fFlags = 0;
6801 if (pThisCC->pbVgaBios == NULL)
6802 {
6803 CPUMMICROARCH enmMicroarch = pVM ? CPUMGetGuestMicroarch(pVM) : kCpumMicroarch_Intel_P6;
6804 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6805 || enmMicroarch == kCpumMicroarch_Intel_80186
6806 || enmMicroarch == kCpumMicroarch_NEC_V20
6807 || enmMicroarch == kCpumMicroarch_NEC_V30)
6808 {
6809 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6810 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6811 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6812 }
6813 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6814 {
6815 pbVgaBiosBinary = g_abVgaBiosBinary286;
6816 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6817 LogRel(("VGA: Using the 286 BIOS image!\n"));
6818 }
6819 else
6820 {
6821 pbVgaBiosBinary = g_abVgaBiosBinary386;
6822 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6823 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6824 }
6825 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6826 }
6827 else
6828 {
6829 pbVgaBiosBinary = pThisCC->pbVgaBios;
6830 cbVgaBiosBinary = pThisCC->cbVgaBios;
6831 }
6832
6833 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6834 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6835 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6836 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6837 fFlags, "VGA BIOS");
6838 AssertRCReturn(rc, rc);
6839
6840 /*
6841 * Saved state.
6842 */
6843 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6844 NULL, vgaR3LiveExec, NULL,
6845 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6846 vgaR3LoadPrep, vgaR3LoadExec, vgaR3LoadDone);
6847 AssertRCReturn(rc, rc);
6848
6849 /*
6850 * Create the refresh timer.
6851 */
6852 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh, NULL, TMTIMER_FLAGS_NO_CRIT_SECT,
6853 "VGA Refresh Timer", &pThis->hRefreshTimer);
6854 AssertRCReturn(rc, rc);
6855
6856 /*
6857 * Attach to the display.
6858 */
6859 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6860 AssertRCReturn(rc, rc);
6861
6862 /*
6863 * Initialize the retrace flag.
6864 */
6865 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6866 AssertLogRelRCReturn(rc, rc);
6867
6868 uint16_t maxBiosXRes;
6869 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6870 AssertLogRelRCReturn(rc, rc);
6871 uint16_t maxBiosYRes;
6872 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6873 AssertLogRelRCReturn(rc, rc);
6874
6875 /*
6876 * Compute buffer size for the VBE BIOS Extra Data.
6877 */
6878 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6879
6880 rc = pHlp->pfnCFGMQueryU32(pCfg, "HeightReduction", &cyReduction);
6881 if (RT_SUCCESS(rc) && cyReduction)
6882 cb *= 2; /* Default mode list will be twice long */
6883 else
6884 cyReduction = 0;
6885
6886 rc = pHlp->pfnCFGMQueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6887 if (RT_SUCCESS(rc) && cCustomModes)
6888 cb += sizeof(ModeInfoListItem) * cCustomModes;
6889 else
6890 cCustomModes = 0;
6891
6892 /*
6893 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6894 */
6895 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6896 pThisCC->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6897 pThisCC->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThisCC->cbVBEExtraData);
6898 if (!pThisCC->pbVBEExtraData)
6899 return VERR_NO_MEMORY;
6900
6901 pVBEDataHdr = (PVBEHEADER)pThisCC->pbVBEExtraData;
6902 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6903 pVBEDataHdr->cbData = cb;
6904
6905 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6906 for (i = 0; i < MODE_INFO_SIZE; i++)
6907 {
6908 uint32_t pixelWidth, reqSize;
6909 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6910 pixelWidth = 2;
6911 else
6912 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6913 reqSize = mode_info_list[i].info.XResolution
6914 * mode_info_list[i].info.YResolution
6915 * pixelWidth;
6916 if (reqSize >= pThis->vram_size)
6917 continue;
6918 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6919 || mode_info_list[i].info.YResolution > maxBiosYRes)
6920 continue;
6921 *pCurMode = mode_info_list[i];
6922 vgaR3AdjustModeInfo(pThis, pCurMode);
6923 pCurMode++;
6924 }
6925
6926 /*
6927 * Copy default modes with subtracted YResolution.
6928 */
6929 if (cyReduction)
6930 {
6931 ModeInfoListItem *pDefMode = mode_info_list;
6932 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6933 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6934 {
6935 uint32_t pixelWidth, reqSize;
6936 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6937 pixelWidth = 2;
6938 else
6939 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6940 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6941 if (reqSize >= pThis->vram_size)
6942 continue;
6943 if ( pDefMode->info.XResolution > maxBiosXRes
6944 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6945 continue;
6946 *pCurMode = *pDefMode;
6947 pCurMode->mode += 0x30;
6948 pCurMode->info.YResolution -= cyReduction;
6949 pCurMode++;
6950 }
6951 }
6952
6953
6954 /*
6955 * Add custom modes.
6956 */
6957 if (cCustomModes)
6958 {
6959 uint16_t u16CurMode = VBE_VBOX_MODE_CUSTOM1;
6960 for (i = 1; i <= cCustomModes; i++)
6961 {
6962 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6963 char *pszExtraData = NULL;
6964
6965 /* query and decode the custom mode string. */
6966 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6967 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6968 if (RT_SUCCESS(rc))
6969 {
6970 ModeInfoListItem *pDefMode = mode_info_list;
6971 unsigned int cx, cy, cBits, cParams, j;
6972 uint16_t u16DefMode;
6973
6974 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6975 if ( cParams != 3
6976 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6977 {
6978 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6979 return VERR_VGA_INVALID_CUSTOM_MODE;
6980 }
6981 cbPitch = calc_line_pitch(cBits, cx);
6982 if (cy * cbPitch >= pThis->vram_size)
6983 {
6984 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",
6985 cx, cy, cBits, pThis->vram_size / _1M));
6986 return VERR_VGA_INVALID_CUSTOM_MODE;
6987 }
6988 PDMDevHlpMMHeapFree(pDevIns, pszExtraData);
6989
6990 /* Use defaults from max@bpp mode. */
6991 switch (cBits)
6992 {
6993 case 8:
6994 u16DefMode = VBE_VESA_MODE_1024X768X8;
6995 break;
6996
6997 case 16:
6998 u16DefMode = VBE_VESA_MODE_1024X768X565;
6999 break;
7000
7001 case 24:
7002 u16DefMode = VBE_VESA_MODE_1024X768X888;
7003 break;
7004
7005 case 32:
7006 u16DefMode = VBE_OWN_MODE_1024X768X8888;
7007 break;
7008
7009 default: /* gcc, shut up! */
7010 AssertMsgFailed(("gone postal!\n"));
7011 continue;
7012 }
7013
7014 /* mode_info_list is not terminated */
7015 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
7016 pDefMode++;
7017 Assert(j < MODE_INFO_SIZE);
7018
7019 *pCurMode = *pDefMode;
7020 pCurMode->mode = u16CurMode++;
7021
7022 /* adjust defaults */
7023 pCurMode->info.XResolution = cx;
7024 pCurMode->info.YResolution = cy;
7025 pCurMode->info.BytesPerScanLine = cbPitch;
7026 pCurMode->info.LinBytesPerScanLine = cbPitch;
7027 vgaR3AdjustModeInfo(pThis, pCurMode);
7028
7029 /* commit it */
7030 pCurMode++;
7031 }
7032 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
7033 {
7034 AssertMsgFailed(("pHlp->pfnCFGMQueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
7035 return rc;
7036 }
7037 } /* foreach custom mode key */
7038 }
7039
7040 /*
7041 * Add the "End of list" mode.
7042 */
7043 memset(pCurMode, 0, sizeof(*pCurMode));
7044 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
7045
7046 /*
7047 * Register I/O Port for the VBE BIOS Extra Data.
7048 */
7049 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_EXTRA_PORT, 1 /*cPorts*/, vbeR3IOPortWriteVbeExtra, vbeR3IoPortReadVbeExtra,
7050 "VBE BIOS Extra Data", NULL /*paExtDesc*/, &pThis->hIoPortVbeExtra);
7051 AssertRCReturn(rc, rc);
7052
7053 /*
7054 * Register I/O Port for the BIOS Logo.
7055 */
7056 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, LOGO_IO_PORT, 1 /*cPorts*/, vbeR3IoPortWriteCmdLogo, vbeR3IoPortReadCmdLogo,
7057 "BIOS Logo", NULL /*paExtDesc*/, &pThis->hIoPortCmdLogo);
7058 AssertRCReturn(rc, rc);
7059
7060 /*
7061 * Register debugger info callbacks.
7062 */
7063 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaR3InfoState);
7064 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaR3InfoText);
7065 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaR3InfoCR);
7066 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaR3InfoGR);
7067 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaR3InfoSR);
7068 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaR3InfoAR);
7069 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaR3InfoPlanar);
7070 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaR3InfoDAC);
7071 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaR3InfoVBE);
7072
7073 /*
7074 * Construct the logo header.
7075 */
7076 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
7077
7078 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
7079 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7080 LogoHdr.fu8FadeIn = 1;
7081 else if (RT_FAILURE(rc))
7082 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeIn\" as integer failed"));
7083
7084 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
7085 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7086 LogoHdr.fu8FadeOut = 1;
7087 else if (RT_FAILURE(rc))
7088 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeOut\" as integer failed"));
7089
7090 rc = pHlp->pfnCFGMQueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
7091 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7092 LogoHdr.u16LogoMillies = 0;
7093 else if (RT_FAILURE(rc))
7094 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"LogoTime\" as integer failed"));
7095
7096 rc = pHlp->pfnCFGMQueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
7097 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7098 LogoHdr.fu8ShowBootMenu = 0;
7099 else if (RT_FAILURE(rc))
7100 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
7101
7102# if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
7103 /* Disable the logo abd menu if all default settings. */
7104 if ( LogoHdr.fu8FadeIn
7105 && LogoHdr.fu8FadeOut
7106 && LogoHdr.u16LogoMillies == 0
7107 && LogoHdr.fu8ShowBootMenu == 2)
7108 {
7109 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
7110 LogoHdr.u16LogoMillies = 500;
7111 }
7112# endif
7113
7114 /* Delay the logo a little bit */
7115 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
7116 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
7117
7118 /*
7119 * Get the Logo file name.
7120 */
7121 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LogoFile", &pThisCC->pszLogoFile);
7122 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7123 pThisCC->pszLogoFile = NULL;
7124 else if (RT_FAILURE(rc))
7125 return PDMDEV_SET_ERROR(pDevIns, rc,
7126 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
7127 else if (!*pThisCC->pszLogoFile)
7128 {
7129 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7130 pThisCC->pszLogoFile = NULL;
7131 }
7132
7133 /*
7134 * Determine the logo size, open any specified logo file in the process.
7135 */
7136 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7137 RTFILE FileLogo = NIL_RTFILE;
7138 if (pThisCC->pszLogoFile)
7139 {
7140 rc = RTFileOpen(&FileLogo, pThisCC->pszLogoFile,
7141 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
7142 if (RT_SUCCESS(rc))
7143 {
7144 uint64_t cbFile;
7145 rc = RTFileQuerySize(FileLogo, &cbFile);
7146 if (RT_SUCCESS(rc))
7147 {
7148 if (cbFile > 0 && cbFile < 32*_1M)
7149 LogoHdr.cbLogo = (uint32_t)cbFile;
7150 else
7151 rc = VERR_TOO_MUCH_DATA;
7152 }
7153 }
7154 if (RT_FAILURE(rc))
7155 {
7156 /*
7157 * Ignore failure and fall back to the default logo.
7158 */
7159 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThisCC->pszLogoFile, rc));
7160 if (FileLogo != NIL_RTFILE)
7161 RTFileClose(FileLogo);
7162 FileLogo = NIL_RTFILE;
7163 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7164 pThisCC->pszLogoFile = NULL;
7165 }
7166 }
7167
7168 /*
7169 * Disable graphic splash screen if it doesn't fit into VRAM.
7170 */
7171 if (pThis->vram_size < LOGO_MAX_SIZE)
7172 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7173
7174 /*
7175 * Allocate buffer for the logo data.
7176 * Let us fall back to default logo on read failure.
7177 */
7178 pThisCC->cbLogo = LogoHdr.cbLogo;
7179 if (g_cbVgaDefBiosLogo)
7180 pThisCC->cbLogo = g_cbVgaDefBiosLogo;
7181# ifndef VBOX_OSE
7182 if (g_cbVgaDefBiosLogoNY)
7183 pThisCC->cbLogo = g_cbVgaDefBiosLogoNY;
7184# endif
7185 pThisCC->cbLogo += sizeof(LogoHdr);
7186
7187 pThisCC->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbLogo);
7188 if (pThisCC->pbLogo)
7189 {
7190 /*
7191 * Write the logo header.
7192 */
7193 PLOGOHDR pLogoHdr = (PLOGOHDR)pThisCC->pbLogo;
7194 *pLogoHdr = LogoHdr;
7195
7196 /*
7197 * Write the logo bitmap.
7198 */
7199 if (pThisCC->pszLogoFile)
7200 {
7201 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7202 if (RT_SUCCESS(rc))
7203 rc = vbeR3ParseBitmap(pThisCC);
7204 if (RT_FAILURE(rc))
7205 {
7206 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7207 rc, pThisCC->pszLogoFile));
7208 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7209 }
7210 }
7211 if ( !pThisCC->pszLogoFile
7212 || RT_FAILURE(rc))
7213 {
7214# ifndef VBOX_OSE
7215 RTTIMESPEC Now;
7216 RTTimeLocalNow(&Now);
7217 RTTIME T;
7218 RTTimeLocalExplode(&T, &Now);
7219 bool fSuppressNewYearSplash = false;
7220 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SuppressNewYearSplash", &fSuppressNewYearSplash, true);
7221 if ( !fSuppressNewYearSplash
7222 && (T.u16YearDay > 353 || T.u16YearDay < 10))
7223 {
7224 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogoNY;
7225 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogoNY, LogoHdr.cbLogo);
7226 pThisCC->fBootMenuInverse = true;
7227 }
7228 else
7229# endif
7230 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7231 rc = vbeR3ParseBitmap(pThisCC);
7232 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Parsing of internal bitmap failed! vbeR3ParseBitmap() -> %Rrc\n", rc), rc);
7233 }
7234
7235 rc = VINF_SUCCESS;
7236 }
7237 else
7238 rc = VERR_NO_MEMORY;
7239
7240 /*
7241 * Cleanup.
7242 */
7243 if (FileLogo != NIL_RTFILE)
7244 RTFileClose(FileLogo);
7245
7246# ifdef VBOX_WITH_HGSMI
7247 VBVAInit(pDevIns, pThis, pThisCC);
7248# endif
7249
7250# ifdef VBOX_WITH_VDMA
7251 if (rc == VINF_SUCCESS)
7252 {
7253 rc = vboxVDMAConstruct(pThis, pThisCC, 1024);
7254 AssertRC(rc);
7255 }
7256# endif
7257
7258# ifdef VBOX_WITH_VMSVGA
7259 if ( rc == VINF_SUCCESS
7260 && pThis->fVMSVGAEnabled)
7261 rc = vmsvgaR3Init(pDevIns);
7262# endif
7263
7264 /*
7265 * Statistics.
7266 */
7267# ifdef VBOX_WITH_STATISTICS
7268 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7269 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7270 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7271 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7272 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapPage, STAMTYPE_COUNTER, "MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMmioMapMmio2Page.");
7273 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaR3PortUpdateDisplay().");
7274# endif
7275# ifdef VBOX_WITH_HGSMI
7276 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatHgsmiMdaCgaAccesses, STAMTYPE_COUNTER, "HgmsiMdaCgaAccesses", STAMUNIT_OCCURENCES, "Number of non-HGMSI accesses for 03b0-3b3 and 03d0-3d3.");
7277# endif
7278
7279 /* Init latched access mask. */
7280 pThis->uMaskLatchAccess = 0x3ff;
7281
7282 if (RT_SUCCESS(rc))
7283 {
7284 PPDMIBASE pBase;
7285 /*
7286 * Attach status driver (optional).
7287 */
7288 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
7289 if (RT_SUCCESS(rc))
7290 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7291 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7292 {
7293 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7294 rc = VINF_SUCCESS;
7295 }
7296 else
7297 {
7298 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7299 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7300 }
7301 }
7302 return rc;
7303}
7304
7305#else /* !IN_RING3 */
7306
7307/**
7308 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
7309 */
7310static DECLCALLBACK(int) vgaRZConstruct(PPDMDEVINS pDevIns)
7311{
7312 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
7313 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
7314 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
7315
7316 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
7317 AssertRCReturn(rc, rc);
7318
7319 /*
7320 * Set I/O port callbacks for this context.
7321 * We just copy the ring-3 registration bits and remove the '&' before the handle.
7322 */
7323# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_hIoPort) do { \
7324 rc = PDMDevHlpIoPortSetUpContext(pDevIns, a_hIoPort, a_pfnWrite, a_pfnRead, NULL /*pvUser*/); \
7325 AssertRCReturn(rc, rc); \
7326 } while (0)
7327
7328 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", pThis->hIoPortAr);
7329 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", pThis->hIoPortMsrSt00);
7330 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", pThis->hIoPort3c3);
7331 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", pThis->hIoPortSr);
7332 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", pThis->hIoPortDac);
7333 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ pThis->hIoPortPos);
7334 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", pThis->hIoPortGr);
7335
7336 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", pThis->hIoPortMdaCrt);
7337 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", pThis->hIoPortMdaFcrSt);
7338 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", pThis->hIoPortCgaCrt);
7339 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", pThis->hIoPortCgaFcrSt);
7340
7341# ifdef CONFIG_BOCHS_VBE
7342 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", pThis->hIoPortVbeIndex);
7343 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", pThis->hIoPortVbeData);
7344# endif /* CONFIG_BOCHS_VBE */
7345
7346# undef REG_PORT
7347
7348 /* BIOS port: */
7349 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortBios, vgaIoPortWriteBios, vgaIoPortReadBios, NULL /*pvUser*/);
7350 AssertRCReturn(rc, rc);
7351
7352# ifdef VBOX_WITH_VMSVGA
7353 if (pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE)
7354 {
7355 AssertReturn(pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7356 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortVmSvga, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/);
7357 AssertRCReturn(rc, rc);
7358 }
7359 else
7360 AssertReturn(!pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7361# endif
7362
7363 /*
7364 * MMIO.
7365 */
7366 rc = PDMDevHlpMmioSetUpContextEx(pDevIns, pThis->hMmioLegacy, vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/);
7367 AssertRCReturn(rc, rc);
7368
7369 /*
7370 * Map the start of the VRAM into this context.
7371 */
7372# if defined(VBOX_WITH_2X_4GB_ADDR_SPACE) || (defined(IN_RING0) && defined(VGA_WITH_PARTIAL_RING0_MAPPING))
7373 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VRam, 0 /* off */, VGA_MAPPING_SIZE, (void **)&pThisCC->pbVRam);
7374 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMmio2SetUpContext(,VRAM,0,%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
7375# endif
7376
7377 /*
7378 * Map the first page of the VMSVGA FIFO into this context (not raw-mode).
7379 * We currently only access SVGA_FIFO_MIN, SVGA_FIFO_PITCHLOCK, and SVGA_FIFO_BUSY.
7380 */
7381 AssertCompile((RT_MAX(SVGA_FIFO_MIN, RT_MAX(SVGA_FIFO_PITCHLOCK, SVGA_FIFO_BUSY)) + 1) * sizeof(uint32_t) < PAGE_SIZE);
7382# if defined(VBOX_WITH_VMSVGA) && !defined(IN_RC)
7383 if (pThis->fVMSVGAEnabled)
7384 {
7385 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VmSvgaFifo, 0 /* off */, PAGE_SIZE,
7386 (void **)&pThisCC->svga.pau32FIFO);
7387 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
7388 }
7389 else
7390 AssertReturn(pThis->hMmio2VmSvgaFifo == NIL_PGMMMIO2HANDLE, VERR_INVALID_STATE);
7391# endif
7392
7393 return VINF_SUCCESS;
7394}
7395
7396#endif /* !IN_RING3 */
7397
7398/**
7399 * The device registration structure.
7400 */
7401const PDMDEVREG g_DeviceVga =
7402{
7403 /* .u32Version = */ PDM_DEVREG_VERSION,
7404 /* .uReserved0 = */ 0,
7405 /* .szName = */ "vga",
7406 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
7407 /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS,
7408 /* .cMaxInstances = */ 1,
7409 /* .uSharedVersion = */ 42,
7410 /* .cbInstanceShared = */ sizeof(VGASTATE),
7411 /* .cbInstanceCC = */ sizeof(VGASTATECC),
7412 /* .cbInstanceRC = */ sizeof(VGASTATERC),
7413 /* .cMaxPciDevices = */ 1,
7414 /* .cMaxMsixVectors = */ 0,
7415 /* .pszDescription = */ "VGA Adaptor with VESA extensions.",
7416#if defined(IN_RING3)
7417 /* .pszRCMod = */ "VBoxDDRC.rc",
7418 /* .pszR0Mod = */ "VBoxDDR0.r0",
7419 /* .pfnConstruct = */ vgaR3Construct,
7420 /* .pfnDestruct = */ vgaR3Destruct,
7421 /* .pfnRelocate = */ vgaR3Relocate,
7422 /* .pfnMemSetup = */ NULL,
7423 /* .pfnPowerOn = */ vgaR3PowerOn,
7424 /* .pfnReset = */ vgaR3Reset,
7425 /* .pfnSuspend = */ NULL,
7426 /* .pfnResume = */ vgaR3Resume,
7427 /* .pfnAttach = */ vgaAttach,
7428 /* .pfnDetach = */ vgaDetach,
7429 /* .pfnQueryInterface = */ NULL,
7430 /* .pfnInitComplete = */ NULL,
7431 /* .pfnPowerOff = */ NULL,
7432 /* .pfnSoftReset = */ NULL,
7433 /* .pfnReserved0 = */ NULL,
7434 /* .pfnReserved1 = */ NULL,
7435 /* .pfnReserved2 = */ NULL,
7436 /* .pfnReserved3 = */ NULL,
7437 /* .pfnReserved4 = */ NULL,
7438 /* .pfnReserved5 = */ NULL,
7439 /* .pfnReserved6 = */ NULL,
7440 /* .pfnReserved7 = */ NULL,
7441#elif defined(IN_RING0)
7442 /* .pfnEarlyConstruct = */ NULL,
7443 /* .pfnConstruct = */ vgaRZConstruct,
7444 /* .pfnDestruct = */ NULL,
7445 /* .pfnFinalDestruct = */ NULL,
7446 /* .pfnRequest = */ NULL,
7447 /* .pfnReserved0 = */ NULL,
7448 /* .pfnReserved1 = */ NULL,
7449 /* .pfnReserved2 = */ NULL,
7450 /* .pfnReserved3 = */ NULL,
7451 /* .pfnReserved4 = */ NULL,
7452 /* .pfnReserved5 = */ NULL,
7453 /* .pfnReserved6 = */ NULL,
7454 /* .pfnReserved7 = */ NULL,
7455#elif defined(IN_RC)
7456 /* .pfnConstruct = */ vgaRZConstruct,
7457 /* .pfnReserved0 = */ NULL,
7458 /* .pfnReserved1 = */ NULL,
7459 /* .pfnReserved2 = */ NULL,
7460 /* .pfnReserved3 = */ NULL,
7461 /* .pfnReserved4 = */ NULL,
7462 /* .pfnReserved5 = */ NULL,
7463 /* .pfnReserved6 = */ NULL,
7464 /* .pfnReserved7 = */ NULL,
7465#else
7466# error "Not in IN_RING3, IN_RING0 or IN_RC!"
7467#endif
7468 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
7469};
7470
7471#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7472
7473/*
7474 * Local Variables:
7475 * nuke-trailing-whitespace-p:nil
7476 * End:
7477 */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use