VirtualBox

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

Last change on this file since 94377 was 94360, checked in by vboxsync, 3 years ago

DevVGA: Cursor position is subject to CRTC byte/word mode, too.

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

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