VirtualBox

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

Last change on this file since 84747 was 84722, checked in by vboxsync, 5 years ago

bugref:9637. setting vmsvga relevant functions in DevVGA only when vmsvga is used.

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