VirtualBox

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

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

Devices/Graphics: CFGM parameter to enable new capabilities.

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