VirtualBox

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

Last change on this file since 95602 was 95602, checked in by vboxsync, 2 years ago

Devices/Graphics: changed device initialization to take saved state into account (fixed crash with VBoxVGA). bugref:9830

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

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