VirtualBox

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

最後變更 在這個檔案從91928是 91928,由 vboxsync 提交於 3 年 前

VMM,Devices: Eliminate direct calls to PGMHandlerPhysical* APIs and introduce callbacks in the device helper callback table, bugref:10074

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

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