VirtualBox

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

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

DevVGA: Implemented text mode underline.

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

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