VirtualBox

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

Last change on this file since 82254 was 82245, checked in by vboxsync, 5 years ago

DevVGA: Mostly restored and properly documented Input Status Register 0 handling.

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

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