VirtualBox

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

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

Devices/Graphics: enabled VGPU10 by default. bugref:9830

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 261.1 KB
Line 
1/* $Id: DevVGA.cpp 95601 2022-07-12 04:56:46Z 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 vmsvgaR3PowerOn(pDevIns);
6147# endif
6148 VBVAOnResume(pDevIns, pThis, pThisCC);
6149}
6150
6151
6152/**
6153 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
6154 */
6155static DECLCALLBACK(void) vgaR3PowerOff(PPDMDEVINS pDevIns)
6156{
6157 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6158 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6159 RT_NOREF(pThis, pThisCC);
6160# ifdef VBOX_WITH_VMSVGA
6161 vmsvgaR3PowerOff(pDevIns);
6162# endif
6163}
6164
6165
6166/**
6167 * @interface_method_impl{PDMDEVREG,pfnRelocate}
6168 */
6169static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
6170{
6171# ifdef VBOX_WITH_RAW_MODE_KEEP
6172 if (offDelta)
6173 {
6174 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6175 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
6176
6177 pThisRC->pbVRam += offDelta;
6178 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6179 }
6180# else
6181 RT_NOREF(pDevIns, offDelta);
6182# endif
6183}
6184
6185
6186/**
6187 * @interface_method_impl{PDMDEVREG,pfnAttach}
6188 *
6189 * This is like plugging in the monitor after turning on the PC.
6190 */
6191static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6192{
6193 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6194 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6195
6196 RT_NOREF(pThis);
6197
6198 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
6199 ("VGA device does not support hotplugging\n"),
6200 VERR_INVALID_PARAMETER);
6201
6202 switch (iLUN)
6203 {
6204 /* LUN #0: Display port. */
6205 case 0:
6206 {
6207 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "Display Port");
6208 if (RT_SUCCESS(rc))
6209 {
6210 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIDISPLAYCONNECTOR);
6211 if (pThisCC->pDrv)
6212 {
6213 /* pThisCC->pDrv->pbData can be NULL when there is no framebuffer. */
6214 if ( pThisCC->pDrv->pfnRefresh
6215 && pThisCC->pDrv->pfnResize
6216 && pThisCC->pDrv->pfnUpdateRect)
6217 rc = VINF_SUCCESS;
6218 else
6219 {
6220 Assert(pThisCC->pDrv->pfnRefresh);
6221 Assert(pThisCC->pDrv->pfnResize);
6222 Assert(pThisCC->pDrv->pfnUpdateRect);
6223 pThisCC->pDrv = NULL;
6224 pThisCC->pDrvBase = NULL;
6225 rc = VERR_INTERNAL_ERROR;
6226 }
6227# ifdef VBOX_WITH_VIDEOHWACCEL
6228 if(rc == VINF_SUCCESS)
6229 {
6230 rc = vbvaVHWAConstruct(pDevIns, pThis, pThisCC);
6231 if (rc != VERR_NOT_IMPLEMENTED)
6232 AssertRC(rc);
6233 }
6234# endif
6235 }
6236 else
6237 {
6238 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
6239 pThisCC->pDrvBase = NULL;
6240 rc = VERR_PDM_MISSING_INTERFACE;
6241 }
6242 }
6243 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6244 {
6245 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6246 rc = VINF_SUCCESS;
6247 }
6248 else
6249 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
6250 return rc;
6251 }
6252
6253 default:
6254 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6255 return VERR_PDM_NO_SUCH_LUN;
6256 }
6257}
6258
6259
6260/**
6261 * @interface_method_impl{PDMDEVREG,pfnDetach}
6262 *
6263 * This is like unplugging the monitor while the PC is still running.
6264 */
6265static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6266{
6267 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6268 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
6269 RT_NOREF(fFlags);
6270
6271 /*
6272 * Reset the interfaces and update the controller state.
6273 */
6274 switch (iLUN)
6275 {
6276 /* LUN #0: Display port. */
6277 case 0:
6278 pThisCC->pDrv = NULL;
6279 pThisCC->pDrvBase = NULL;
6280 break;
6281
6282 default:
6283 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6284 break;
6285 }
6286}
6287
6288
6289/**
6290 * @interface_method_impl{PDMDEVREG,pfnDestruct}
6291 */
6292static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6293{
6294 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
6295 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6296 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6297 LogFlow(("vgaR3Destruct:\n"));
6298
6299# ifdef VBOX_WITH_VDMA
6300 if (pThisCC->pVdma)
6301 vboxVDMADestruct(pThisCC->pVdma);
6302# endif
6303
6304# ifdef VBOX_WITH_VMSVGA
6305 if (pThis->fVMSVGAEnabled)
6306 vmsvgaR3Destruct(pDevIns);
6307# endif
6308
6309# ifdef VBOX_WITH_HGSMI
6310 VBVADestroy(pThisCC);
6311# endif
6312
6313 /*
6314 * Free MM heap pointers.
6315 */
6316 if (pThisCC->pbVBEExtraData)
6317 {
6318 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVBEExtraData);
6319 pThisCC->pbVBEExtraData = NULL;
6320 }
6321 if (pThisCC->pbVgaBios)
6322 {
6323 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6324 pThisCC->pbVgaBios = NULL;
6325 }
6326
6327 if (pThisCC->pszVgaBiosFile)
6328 {
6329 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6330 pThisCC->pszVgaBiosFile = NULL;
6331 }
6332
6333 if (pThisCC->pszLogoFile)
6334 {
6335 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
6336 pThisCC->pszLogoFile = NULL;
6337 }
6338
6339 if (pThisCC->pbLogo)
6340 {
6341 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbLogo);
6342 pThisCC->pbLogo = NULL;
6343 }
6344
6345# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
6346 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSectIRQ);
6347# endif
6348 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
6349 return VINF_SUCCESS;
6350}
6351
6352
6353/**
6354 * Adjust VBE mode information
6355 *
6356 * Depending on the configured VRAM size, certain parts of VBE mode
6357 * information must be updated.
6358 *
6359 * @param pThis The device instance data.
6360 * @param pMode The mode information structure.
6361 */
6362static void vgaR3AdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6363{
6364 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6365 unsigned bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6366 /* The "number of image pages" is really the max page index... */
6367 unsigned maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6368 if (maxPage > 255)
6369 maxPage = 255; /* 8-bit value. */
6370 pMode->info.NumberOfImagePages = maxPage;
6371 pMode->info.LinNumberOfPages = maxPage;
6372}
6373
6374
6375/**
6376 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6377 */
6378static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6379{
6380 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6381 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6382 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6383 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
6384 int rc;
6385 unsigned i;
6386 uint32_t cCustomModes;
6387 uint32_t cyReduction;
6388 uint32_t cbPitch;
6389 PVBEHEADER pVBEDataHdr;
6390 ModeInfoListItem *pCurMode;
6391 unsigned cb;
6392
6393 Assert(iInstance == 0);
6394
6395 /*
6396 * Init static data.
6397 */
6398 static bool s_fExpandDone = false;
6399 if (!s_fExpandDone)
6400 {
6401 s_fExpandDone = true;
6402 vgaR3InitExpand();
6403 }
6404
6405 /*
6406 * Validate configuration.
6407 */
6408 static const char s_szMscWorkaround[] = "VRamSize"
6409 "|MonitorCount"
6410 "|FadeIn"
6411 "|FadeOut"
6412 "|LogoTime"
6413 "|LogoFile"
6414 "|ShowBootMenu"
6415 "|BiosRom"
6416 "|RealRetrace"
6417 "|CustomVideoModes"
6418 "|HeightReduction"
6419 "|CustomVideoMode1"
6420 "|CustomVideoMode2"
6421 "|CustomVideoMode3"
6422 "|CustomVideoMode4"
6423 "|CustomVideoMode5"
6424 "|CustomVideoMode6"
6425 "|CustomVideoMode7"
6426 "|CustomVideoMode8"
6427 "|CustomVideoMode9"
6428 "|CustomVideoMode10"
6429 "|CustomVideoMode11"
6430 "|CustomVideoMode12"
6431 "|CustomVideoMode13"
6432 "|CustomVideoMode14"
6433 "|CustomVideoMode15"
6434 "|CustomVideoMode16"
6435 "|MaxBiosXRes"
6436 "|MaxBiosYRes"
6437# ifdef VBOX_WITH_VMSVGA
6438 "|VMSVGAEnabled"
6439 "|VMSVGA10"
6440 "|VMSVGAPciId"
6441 "|VMSVGAPciBarLayout"
6442 "|VMSVGAFifoSize"
6443# endif
6444# ifdef VBOX_WITH_VMSVGA3D
6445 "|VMSVGA3dEnabled"
6446 "|VMSVGA3dOverlayEnabled"
6447# endif
6448 "|SuppressNewYearSplash"
6449 "|3DEnabled";
6450
6451 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, s_szMscWorkaround, "");
6452
6453 /*
6454 * Init state data.
6455 */
6456 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6457 AssertLogRelRCReturn(rc, rc);
6458 if (pThis->vram_size > VGA_VRAM_MAX)
6459 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6460 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6461 if (pThis->vram_size < VGA_VRAM_MIN)
6462 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6463 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6464 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6465 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6466 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6467
6468 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6469 AssertLogRelRCReturn(rc, rc);
6470
6471 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
6472
6473 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "3DEnabled", &pThis->f3DEnabled, false);
6474 AssertLogRelRCReturn(rc, rc);
6475 Log(("VGA: f3DEnabled=%RTbool\n", pThis->f3DEnabled));
6476
6477# ifdef VBOX_WITH_VMSVGA
6478 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6479 AssertLogRelRCReturn(rc, rc);
6480 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6481
6482 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA10", &pThis->fVMSVGA10, true);
6483 AssertLogRelRCReturn(rc, rc);
6484 Log(("VMSVGA: VMSVGA10 = %d\n", pThis->fVMSVGA10));
6485
6486 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciId", &pThis->fVMSVGAPciId, false);
6487 AssertLogRelRCReturn(rc, rc);
6488 Log(("VMSVGA: VMSVGAPciId = %d\n", pThis->fVMSVGAPciId));
6489
6490 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciBarLayout", &pThis->fVMSVGAPciBarLayout, pThis->fVMSVGAPciId);
6491 AssertLogRelRCReturn(rc, rc);
6492 Log(("VMSVGA: VMSVGAPciBarLayout = %d\n", pThis->fVMSVGAPciBarLayout));
6493
6494 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6495 AssertLogRelRCReturn(rc, rc);
6496 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6497 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6498 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6499 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6500 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6501# endif
6502# ifdef VBOX_WITH_VMSVGA3D
6503 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6504 AssertLogRelRCReturn(rc, rc);
6505 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6506
6507 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dOverlayEnabled", &pThis->svga.f3DOverlayEnabled, false);
6508 AssertLogRelRCReturn(rc, rc);
6509 Log(("VMSVGA: VMSVGA3dOverlayEnabled = %d\n", pThis->svga.f3DOverlayEnabled));
6510# endif
6511
6512# ifdef VBOX_WITH_VMSVGA
6513 if (pThis->fVMSVGAPciBarLayout)
6514 {
6515 pThis->pciRegions.iIO = 0;
6516 pThis->pciRegions.iVRAM = 1;
6517 }
6518 else
6519 {
6520 pThis->pciRegions.iVRAM = 0;
6521 pThis->pciRegions.iIO = 1;
6522 }
6523 pThis->pciRegions.iFIFO = 2;
6524# else
6525 pThis->pciRegions.iVRAM = 0;
6526# endif
6527
6528 pThisCC->pDevIns = pDevIns;
6529
6530 vgaR3Reset(pDevIns);
6531
6532 /* The PCI devices configuration. */
6533 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
6534 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
6535
6536# ifdef VBOX_WITH_VMSVGA
6537 if (pThis->fVMSVGAEnabled)
6538 {
6539 /* Extend our VGA device with VMWare SVGA functionality. */
6540 if (pThis->fVMSVGAPciId)
6541 {
6542 PDMPciDevSetVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6543 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6544 }
6545 else
6546 {
6547 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6548 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6549 }
6550 PDMPciDevSetSubSystemVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6551 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6552 }
6553 else
6554# endif /* VBOX_WITH_VMSVGA */
6555 {
6556 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6557 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6558 }
6559 PDMPciDevSetClassSub(pPciDev, 0x00); /* VGA controller */
6560 PDMPciDevSetClassBase(pPciDev, 0x03);
6561 PDMPciDevSetHeaderType(pPciDev, 0x00);
6562# if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6563 PDMPciDevSetInterruptPin(pPciDev, 1);
6564# endif
6565
6566 /* the interfaces. */
6567 pThisCC->IBase.pfnQueryInterface = vgaR3PortQueryInterface;
6568
6569 pThisCC->IPort.pfnUpdateDisplay = vgaR3PortUpdateDisplay;
6570 pThisCC->IPort.pfnUpdateDisplayAll = vgaR3PortUpdateDisplayAll;
6571 pThisCC->IPort.pfnQueryVideoMode = vgaR3PortQueryVideoMode;
6572 pThisCC->IPort.pfnSetRefreshRate = vgaR3PortSetRefreshRate;
6573 pThisCC->IPort.pfnTakeScreenshot = vgaR3PortTakeScreenshot;
6574 pThisCC->IPort.pfnFreeScreenshot = vgaR3PortFreeScreenshot;
6575 pThisCC->IPort.pfnDisplayBlt = vgaR3PortDisplayBlt;
6576 pThisCC->IPort.pfnUpdateDisplayRect = vgaR3PortUpdateDisplayRect;
6577 pThisCC->IPort.pfnCopyRect = vgaR3PortCopyRect;
6578 pThisCC->IPort.pfnSetRenderVRAM = vgaR3PortSetRenderVRAM;
6579 pThisCC->IPort.pfnSetViewport = NULL;
6580 pThisCC->IPort.pfnReportMonitorPositions = NULL;
6581# ifdef VBOX_WITH_VMSVGA
6582 if (pThis->fVMSVGAEnabled)
6583 {
6584 pThisCC->IPort.pfnSetViewport = vmsvgaR3PortSetViewport;
6585 pThisCC->IPort.pfnReportMonitorPositions = vmsvgaR3PortReportMonitorPositions;
6586 }
6587# endif
6588 pThisCC->IPort.pfnSendModeHint = vbvaR3PortSendModeHint;
6589 pThisCC->IPort.pfnReportHostCursorCapabilities = vgaR3PortReportHostCursorCapabilities;
6590 pThisCC->IPort.pfnReportHostCursorPosition = vgaR3PortReportHostCursorPosition;
6591
6592# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
6593 pThisCC->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaR3VHWACommandCompleteAsync;
6594# endif
6595
6596 pThisCC->ILeds.pfnQueryStatusLed = vgaR3PortQueryStatusLed;
6597 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6598
6599 /*
6600 * We use our own critical section to avoid unncessary pointer indirections
6601 * in interface methods (as well as for historical reasons).
6602 */
6603 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6604 AssertRCReturn(rc, rc);
6605 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6606 AssertRCReturn(rc, rc);
6607
6608# ifdef VBOX_WITH_HGSMI
6609 /*
6610 * This critical section is used by vgaR3IOPortHgsmiWrite, VBVARaiseIrq and VBVAOnResume
6611 * for some IRQ related synchronization.
6612 */
6613 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6614 AssertRCReturn(rc, rc);
6615# endif
6616
6617 /*
6618 * PCI device registration.
6619 */
6620 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6621 if (RT_FAILURE(rc))
6622 return rc;
6623 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6624 if (pPciDev->uDevFn != 16 && iInstance == 0)
6625 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pPciDev->uDevFn));
6626
6627# ifdef VBOX_WITH_VMSVGA
6628 pThis->hIoPortVmSvga = NIL_IOMIOPORTHANDLE;
6629 pThis->hMmio2VmSvgaFifo = NIL_PGMMMIO2HANDLE;
6630 if (pThis->fVMSVGAEnabled)
6631 {
6632 /* Register the io command ports. */
6633 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, pThis->pciRegions.iIO, 0x10, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/,
6634 "VMSVGA", NULL /*paExtDescs*/, &pThis->hIoPortVmSvga);
6635 AssertRCReturn(rc, rc);
6636
6637 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iFIFO, pThis->svga.cbFIFO,
6638 PCI_ADDRESS_SPACE_MEM, 0 /*fFlags*/, vmsvgaR3PciIORegionFifoMapUnmap,
6639 "VMSVGA-FIFO", (void **)&pThisCC->svga.pau32FIFO, &pThis->hMmio2VmSvgaFifo);
6640 AssertRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6641 N_("Failed to create VMSVGA FIFO (%u bytes)"), pThis->svga.cbFIFO));
6642
6643 pPciDev->pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6644 }
6645# endif /* VBOX_WITH_VMSVGA */
6646
6647 /*
6648 * Allocate VRAM and create a PCI region for it.
6649 */
6650 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iVRAM, pThis->vram_size,
6651 PCI_ADDRESS_SPACE_MEM_PREFETCH, PGMPHYS_MMIO2_FLAGS_TRACK_DIRTY_PAGES,
6652 vgaR3PciIORegionVRamMapUnmap, "VRam", (void **)&pThisCC->pbVRam, &pThis->hMmio2VRam);
6653 AssertLogRelRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6654 N_("Failed to allocate %u bytes of VRAM"), pThis->vram_size));
6655
6656 /*
6657 * Register I/O ports.
6658 */
6659# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_phIoPort) do { \
6660 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, a_uPort, a_cPorts, IOM_IOPORT_F_ABS, \
6661 a_pfnWrite, a_pfnRead, "VGA - " a_szDesc, NULL /*paExtDescs*/, a_phIoPort); \
6662 AssertRCReturn(rc, rc); \
6663 } while (0)
6664 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", &pThis->hIoPortAr);
6665 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", &pThis->hIoPortMsrSt00);
6666 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", &pThis->hIoPort3c3);
6667 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", &pThis->hIoPortSr);
6668 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", &pThis->hIoPortDac);
6669 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ &pThis->hIoPortPos);
6670 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", &pThis->hIoPortGr);
6671
6672 /* Note! Ralf Brown lists 0x3b0-0x3b1, 0x3b2-0x3b3 and 0x3b6-0x3b7 as "the same as" 0x3b4-0x3b5. */
6673 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", &pThis->hIoPortMdaCrt);
6674 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", &pThis->hIoPortMdaFcrSt);
6675 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", &pThis->hIoPortCgaCrt);
6676 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", &pThis->hIoPortCgaFcrSt);
6677
6678# ifdef CONFIG_BOCHS_VBE
6679 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", &pThis->hIoPortVbeIndex);
6680 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", &pThis->hIoPortVbeData);
6681# endif /* CONFIG_BOCHS_VBE */
6682
6683# ifdef VBOX_WITH_HGSMI
6684 /* Use reserved VGA IO ports for HGSMI. */
6685 REG_PORT(VGA_PORT_HGSMI_HOST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI host (3b0-3b3)", &pThis->hIoPortHgsmiHost);
6686 REG_PORT(VGA_PORT_HGSMI_GUEST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI guest (3d0-3d3)", &pThis->hIoPortHgsmiGuest);
6687# endif /* VBOX_WITH_HGSMI */
6688
6689# undef REG_PORT
6690
6691 /* vga bios */
6692 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_PRINTF_PORT, 1 /*cPorts*/, vgaIoPortWriteBios, vgaIoPortReadBios,
6693 "VGA BIOS debug/panic", NULL /*paExtDescs*/, &pThis->hIoPortBios);
6694 AssertRCReturn(rc, rc);
6695
6696 /*
6697 * The MDA/CGA/EGA/VGA/whatever fixed MMIO area.
6698 */
6699 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, 0x000a0000, 0x00020000,
6700 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
6701 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
6702 vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/,
6703 "VGA - VGA Video Buffer", &pThis->hMmioLegacy);
6704 AssertRCReturn(rc, rc);
6705
6706 /*
6707 * Get the VGA BIOS ROM file name.
6708 */
6709 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThisCC->pszVgaBiosFile);
6710 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6711 {
6712 pThisCC->pszVgaBiosFile = NULL;
6713 rc = VINF_SUCCESS;
6714 }
6715 else if (RT_FAILURE(rc))
6716 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6717 else if (!*pThisCC->pszVgaBiosFile)
6718 {
6719 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6720 pThisCC->pszVgaBiosFile = NULL;
6721 }
6722
6723 /*
6724 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6725 */
6726 RTFILE FileVgaBios = NIL_RTFILE;
6727 if (pThisCC->pszVgaBiosFile)
6728 {
6729 rc = RTFileOpen(&FileVgaBios, pThisCC->pszVgaBiosFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6730 if (RT_SUCCESS(rc))
6731 {
6732 rc = RTFileQuerySize(FileVgaBios, &pThisCC->cbVgaBios);
6733 if (RT_SUCCESS(rc))
6734 {
6735 if ( RT_ALIGN(pThisCC->cbVgaBios, _4K) != pThisCC->cbVgaBios
6736 || pThisCC->cbVgaBios > _64K
6737 || pThisCC->cbVgaBios < 16 * _1K)
6738 rc = VERR_TOO_MUCH_DATA;
6739 }
6740 }
6741 if (RT_FAILURE(rc))
6742 {
6743 /*
6744 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6745 */
6746 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThisCC->pszVgaBiosFile, rc));
6747 RTFileClose(FileVgaBios);
6748 FileVgaBios = NIL_RTFILE;
6749 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6750 pThisCC->pszVgaBiosFile = NULL;
6751 }
6752 }
6753
6754 /*
6755 * Attempt to get the VGA BIOS ROM data from file.
6756 */
6757 if (pThisCC->pszVgaBiosFile)
6758 {
6759 /*
6760 * Allocate buffer for the VGA BIOS ROM data.
6761 */
6762 pThisCC->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbVgaBios);
6763 if (pThisCC->pbVgaBios)
6764 {
6765 rc = RTFileRead(FileVgaBios, pThisCC->pbVgaBios, pThisCC->cbVgaBios, NULL);
6766 if (RT_FAILURE(rc))
6767 {
6768 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThisCC->cbVgaBios, rc));
6769 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6770 pThisCC->pbVgaBios = NULL;
6771 }
6772 rc = VINF_SUCCESS;
6773 }
6774 else
6775 rc = VERR_NO_MEMORY;
6776 }
6777 else
6778 pThisCC->pbVgaBios = NULL;
6779
6780 /* cleanup */
6781 if (FileVgaBios != NIL_RTFILE)
6782 RTFileClose(FileVgaBios);
6783
6784 /* If we were unable to get the data from file for whatever reason, fall
6785 back to the built-in ROM image. */
6786 const uint8_t *pbVgaBiosBinary;
6787 uint64_t cbVgaBiosBinary;
6788 uint32_t fFlags = 0;
6789 if (pThisCC->pbVgaBios == NULL)
6790 {
6791 CPUMMICROARCH enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
6792 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6793 || enmMicroarch == kCpumMicroarch_Intel_80186
6794 || enmMicroarch == kCpumMicroarch_NEC_V20
6795 || enmMicroarch == kCpumMicroarch_NEC_V30)
6796 {
6797 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6798 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6799 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6800 }
6801 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6802 {
6803 pbVgaBiosBinary = g_abVgaBiosBinary286;
6804 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6805 LogRel(("VGA: Using the 286 BIOS image!\n"));
6806 }
6807 else
6808 {
6809 pbVgaBiosBinary = g_abVgaBiosBinary386;
6810 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6811 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6812 }
6813 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6814 }
6815 else
6816 {
6817 pbVgaBiosBinary = pThisCC->pbVgaBios;
6818 cbVgaBiosBinary = pThisCC->cbVgaBios;
6819 }
6820
6821 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6822 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, GUEST_PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6823 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6824 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6825 fFlags, "VGA BIOS");
6826 AssertRCReturn(rc, rc);
6827
6828 /*
6829 * Saved state.
6830 */
6831 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6832 NULL, vgaR3LiveExec, NULL,
6833 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6834 vgaR3LoadPrep, vgaR3LoadExec, vgaR3LoadDone);
6835 AssertRCReturn(rc, rc);
6836
6837 /*
6838 * Create the refresh timer.
6839 */
6840 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, vgaR3TimerRefresh, NULL,
6841 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "VGA Refresh", &pThis->hRefreshTimer);
6842 AssertRCReturn(rc, rc);
6843
6844 /*
6845 * Attach to the display.
6846 */
6847 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6848 AssertRCReturn(rc, rc);
6849
6850 /*
6851 * Initialize the retrace flag.
6852 */
6853 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6854 AssertLogRelRCReturn(rc, rc);
6855
6856 uint16_t maxBiosXRes;
6857 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6858 AssertLogRelRCReturn(rc, rc);
6859 uint16_t maxBiosYRes;
6860 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6861 AssertLogRelRCReturn(rc, rc);
6862
6863 /*
6864 * Compute buffer size for the VBE BIOS Extra Data.
6865 */
6866 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6867
6868 rc = pHlp->pfnCFGMQueryU32(pCfg, "HeightReduction", &cyReduction);
6869 if (RT_SUCCESS(rc) && cyReduction)
6870 cb *= 2; /* Default mode list will be twice long */
6871 else
6872 cyReduction = 0;
6873
6874 rc = pHlp->pfnCFGMQueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6875 if (RT_SUCCESS(rc) && cCustomModes)
6876 cb += sizeof(ModeInfoListItem) * cCustomModes;
6877 else
6878 cCustomModes = 0;
6879
6880 /*
6881 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6882 */
6883 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6884 pThisCC->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6885 pThisCC->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThisCC->cbVBEExtraData);
6886 if (!pThisCC->pbVBEExtraData)
6887 return VERR_NO_MEMORY;
6888
6889 pVBEDataHdr = (PVBEHEADER)pThisCC->pbVBEExtraData;
6890 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6891 pVBEDataHdr->cbData = cb;
6892
6893 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6894 for (i = 0; i < MODE_INFO_SIZE; i++)
6895 {
6896 uint32_t pixelWidth, reqSize;
6897 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6898 pixelWidth = 2;
6899 else
6900 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6901 reqSize = mode_info_list[i].info.XResolution
6902 * mode_info_list[i].info.YResolution
6903 * pixelWidth;
6904 if (reqSize >= pThis->vram_size)
6905 continue;
6906 if (!reqSize)
6907 continue;
6908 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6909 || mode_info_list[i].info.YResolution > maxBiosYRes)
6910 continue;
6911 *pCurMode = mode_info_list[i];
6912 vgaR3AdjustModeInfo(pThis, pCurMode);
6913 pCurMode++;
6914 }
6915
6916 /*
6917 * Copy default modes with subtracted YResolution.
6918 */
6919 if (cyReduction)
6920 {
6921 ModeInfoListItem *pDefMode = mode_info_list;
6922 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6923 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6924 {
6925 uint32_t pixelWidth, reqSize;
6926 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6927 pixelWidth = 2;
6928 else
6929 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6930 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6931 if (reqSize >= pThis->vram_size)
6932 continue;
6933 if ( pDefMode->info.XResolution > maxBiosXRes
6934 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6935 continue;
6936 *pCurMode = *pDefMode;
6937 pCurMode->mode += 0x30;
6938 pCurMode->info.YResolution -= cyReduction;
6939 pCurMode++;
6940 }
6941 }
6942
6943
6944 /*
6945 * Add custom modes.
6946 */
6947 if (cCustomModes)
6948 {
6949 uint16_t u16CurMode = VBE_VBOX_MODE_CUSTOM1;
6950 for (i = 1; i <= cCustomModes; i++)
6951 {
6952 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6953 char *pszExtraData = NULL;
6954
6955 /* query and decode the custom mode string. */
6956 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6957 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6958 if (RT_SUCCESS(rc))
6959 {
6960 ModeInfoListItem *pDefMode = mode_info_list;
6961 unsigned int cx, cy, cBits, cParams, j;
6962 uint16_t u16DefMode;
6963
6964 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6965 if ( cParams != 3
6966 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6967 {
6968 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6969 return VERR_VGA_INVALID_CUSTOM_MODE;
6970 }
6971 if (!cx || !cy)
6972 {
6973 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cx=%u, cy=%u\n", pszExtraData, szExtraDataKey, cx, cy));
6974 return VERR_VGA_INVALID_CUSTOM_MODE;
6975 }
6976 cbPitch = calc_line_pitch(cBits, cx);
6977 if (cy * cbPitch >= pThis->vram_size)
6978 {
6979 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",
6980 cx, cy, cBits, pThis->vram_size / _1M));
6981 return VERR_VGA_INVALID_CUSTOM_MODE;
6982 }
6983 PDMDevHlpMMHeapFree(pDevIns, pszExtraData);
6984
6985 /* Use defaults from max@bpp mode. */
6986 switch (cBits)
6987 {
6988 case 8:
6989 u16DefMode = VBE_VESA_MODE_1024X768X8;
6990 break;
6991
6992 case 16:
6993 u16DefMode = VBE_VESA_MODE_1024X768X565;
6994 break;
6995
6996 case 24:
6997 u16DefMode = VBE_VESA_MODE_1024X768X888;
6998 break;
6999
7000 case 32:
7001 u16DefMode = VBE_OWN_MODE_1024X768X8888;
7002 break;
7003
7004 default: /* gcc, shut up! */
7005 AssertMsgFailed(("gone postal!\n"));
7006 continue;
7007 }
7008
7009 /* mode_info_list is not terminated */
7010 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
7011 pDefMode++;
7012 Assert(j < MODE_INFO_SIZE);
7013
7014 *pCurMode = *pDefMode;
7015 pCurMode->mode = u16CurMode++;
7016
7017 /* adjust defaults */
7018 pCurMode->info.XResolution = cx;
7019 pCurMode->info.YResolution = cy;
7020 pCurMode->info.BytesPerScanLine = cbPitch;
7021 pCurMode->info.LinBytesPerScanLine = cbPitch;
7022 vgaR3AdjustModeInfo(pThis, pCurMode);
7023
7024 /* commit it */
7025 pCurMode++;
7026 }
7027 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
7028 {
7029 AssertMsgFailed(("pHlp->pfnCFGMQueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
7030 return rc;
7031 }
7032 } /* foreach custom mode key */
7033 }
7034
7035 /*
7036 * Add the "End of list" mode.
7037 */
7038 memset(pCurMode, 0, sizeof(*pCurMode));
7039 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
7040
7041 /*
7042 * Register I/O Port for the VBE BIOS Extra Data.
7043 */
7044 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_EXTRA_PORT, 1 /*cPorts*/, vbeR3IOPortWriteVbeExtra, vbeR3IoPortReadVbeExtra,
7045 "VBE BIOS Extra Data", NULL /*paExtDesc*/, &pThis->hIoPortVbeExtra);
7046 AssertRCReturn(rc, rc);
7047
7048 /*
7049 * Register I/O Port for the BIOS Logo.
7050 */
7051 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, LOGO_IO_PORT, 1 /*cPorts*/, vbeR3IoPortWriteCmdLogo, vbeR3IoPortReadCmdLogo,
7052 "BIOS Logo", NULL /*paExtDesc*/, &pThis->hIoPortCmdLogo);
7053 AssertRCReturn(rc, rc);
7054
7055 /*
7056 * Register debugger info callbacks.
7057 */
7058 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaR3InfoState);
7059 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaR3InfoText);
7060 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaR3InfoCR);
7061 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaR3InfoGR);
7062 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaR3InfoSR);
7063 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaR3InfoAR);
7064 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaR3InfoPlanar);
7065 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaR3InfoDAC);
7066 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaR3InfoVBE);
7067
7068 /*
7069 * Construct the logo header.
7070 */
7071 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
7072
7073 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
7074 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7075 LogoHdr.fu8FadeIn = 1;
7076 else if (RT_FAILURE(rc))
7077 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeIn\" as integer failed"));
7078
7079 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
7080 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7081 LogoHdr.fu8FadeOut = 1;
7082 else if (RT_FAILURE(rc))
7083 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeOut\" as integer failed"));
7084
7085 rc = pHlp->pfnCFGMQueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
7086 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7087 LogoHdr.u16LogoMillies = 0;
7088 else if (RT_FAILURE(rc))
7089 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"LogoTime\" as integer failed"));
7090
7091 rc = pHlp->pfnCFGMQueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
7092 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7093 LogoHdr.fu8ShowBootMenu = 0;
7094 else if (RT_FAILURE(rc))
7095 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
7096
7097# if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
7098 /* Disable the logo abd menu if all default settings. */
7099 if ( LogoHdr.fu8FadeIn
7100 && LogoHdr.fu8FadeOut
7101 && LogoHdr.u16LogoMillies == 0
7102 && LogoHdr.fu8ShowBootMenu == 2)
7103 {
7104 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
7105 LogoHdr.u16LogoMillies = 500;
7106 }
7107# endif
7108
7109 /* Delay the logo a little bit */
7110 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
7111 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
7112
7113 /*
7114 * Get the Logo file name.
7115 */
7116 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LogoFile", &pThisCC->pszLogoFile);
7117 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7118 pThisCC->pszLogoFile = NULL;
7119 else if (RT_FAILURE(rc))
7120 return PDMDEV_SET_ERROR(pDevIns, rc,
7121 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
7122 else if (!*pThisCC->pszLogoFile)
7123 {
7124 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7125 pThisCC->pszLogoFile = NULL;
7126 }
7127
7128 /*
7129 * Determine the logo size, open any specified logo file in the process.
7130 */
7131 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7132 RTFILE FileLogo = NIL_RTFILE;
7133 if (pThisCC->pszLogoFile)
7134 {
7135 rc = RTFileOpen(&FileLogo, pThisCC->pszLogoFile,
7136 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
7137 if (RT_SUCCESS(rc))
7138 {
7139 uint64_t cbFile;
7140 rc = RTFileQuerySize(FileLogo, &cbFile);
7141 if (RT_SUCCESS(rc))
7142 {
7143 if (cbFile > 0 && cbFile < 32*_1M)
7144 LogoHdr.cbLogo = (uint32_t)cbFile;
7145 else
7146 rc = VERR_TOO_MUCH_DATA;
7147 }
7148 }
7149 if (RT_FAILURE(rc))
7150 {
7151 /*
7152 * Ignore failure and fall back to the default logo.
7153 */
7154 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThisCC->pszLogoFile, rc));
7155 if (FileLogo != NIL_RTFILE)
7156 RTFileClose(FileLogo);
7157 FileLogo = NIL_RTFILE;
7158 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7159 pThisCC->pszLogoFile = NULL;
7160 }
7161 }
7162
7163 /*
7164 * Disable graphic splash screen if it doesn't fit into VRAM.
7165 */
7166 if (pThis->vram_size < LOGO_MAX_SIZE)
7167 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7168
7169 /*
7170 * Allocate buffer for the logo data.
7171 * Let us fall back to default logo on read failure.
7172 */
7173 pThisCC->cbLogo = LogoHdr.cbLogo;
7174 if (g_cbVgaDefBiosLogo)
7175 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogo);
7176# ifndef VBOX_OSE
7177 if (g_cbVgaDefBiosLogoNY)
7178 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogoNY);
7179# endif
7180 pThisCC->cbLogo += sizeof(LogoHdr);
7181
7182 pThisCC->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbLogo);
7183 if (pThisCC->pbLogo)
7184 {
7185 /*
7186 * Write the logo header.
7187 */
7188 PLOGOHDR pLogoHdr = (PLOGOHDR)pThisCC->pbLogo;
7189 *pLogoHdr = LogoHdr;
7190
7191 /*
7192 * Write the logo bitmap.
7193 */
7194 if (pThisCC->pszLogoFile)
7195 {
7196 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7197 if (RT_SUCCESS(rc))
7198 rc = vbeR3ParseBitmap(pThisCC);
7199 if (RT_FAILURE(rc))
7200 {
7201 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7202 rc, pThisCC->pszLogoFile));
7203 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7204 }
7205 }
7206 if ( !pThisCC->pszLogoFile
7207 || RT_FAILURE(rc))
7208 {
7209# ifndef VBOX_OSE
7210 RTTIMESPEC Now;
7211 RTTimeLocalNow(&Now);
7212 RTTIME T;
7213 RTTimeLocalExplode(&T, &Now);
7214 bool fSuppressNewYearSplash = false;
7215 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SuppressNewYearSplash", &fSuppressNewYearSplash, true);
7216 if ( !fSuppressNewYearSplash
7217 && (T.u16YearDay > 353 || T.u16YearDay < 10))
7218 {
7219 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogoNY;
7220 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogoNY, LogoHdr.cbLogo);
7221 pThisCC->fBootMenuInverse = true;
7222 }
7223 else
7224# endif
7225 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7226 rc = vbeR3ParseBitmap(pThisCC);
7227 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Parsing of internal bitmap failed! vbeR3ParseBitmap() -> %Rrc\n", rc), rc);
7228 }
7229
7230 rc = VINF_SUCCESS;
7231 }
7232 else
7233 rc = VERR_NO_MEMORY;
7234
7235 /*
7236 * Cleanup.
7237 */
7238 if (FileLogo != NIL_RTFILE)
7239 RTFileClose(FileLogo);
7240
7241# ifdef VBOX_WITH_HGSMI
7242 VBVAInit(pDevIns, pThis, pThisCC);
7243# endif
7244
7245# ifdef VBOX_WITH_VDMA
7246 if (rc == VINF_SUCCESS)
7247 {
7248 rc = vboxVDMAConstruct(pThis, pThisCC, 1024);
7249 AssertRC(rc);
7250 }
7251# endif
7252
7253# ifdef VBOX_WITH_VMSVGA
7254 if ( rc == VINF_SUCCESS
7255 && pThis->fVMSVGAEnabled)
7256 rc = vmsvgaR3Init(pDevIns);
7257# endif
7258
7259 /*
7260 * Statistics.
7261 */
7262# ifdef VBOX_WITH_STATISTICS
7263 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7264 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7265 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7266 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7267 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapPage, STAMTYPE_COUNTER, "MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMmioMapMmio2Page.");
7268 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapReset, STAMTYPE_COUNTER, "MapPageReset", STAMUNIT_OCCURENCES, "Calls to IOMMmioResetRegion.");
7269 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaR3PortUpdateDisplay().");
7270# endif
7271# ifdef VBOX_WITH_HGSMI
7272 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatHgsmiMdaCgaAccesses, STAMTYPE_COUNTER, "HgmsiMdaCgaAccesses", STAMUNIT_OCCURENCES, "Number of non-HGMSI accesses for 03b0-3b3 and 03d0-3d3.");
7273# endif
7274
7275 /* Init latched access mask. */
7276 pThis->uMaskLatchAccess = 0x3ff;
7277
7278 if (RT_SUCCESS(rc))
7279 {
7280 PPDMIBASE pBase;
7281 /*
7282 * Attach status driver (optional).
7283 */
7284 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
7285 if (RT_SUCCESS(rc))
7286 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7287 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7288 {
7289 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7290 rc = VINF_SUCCESS;
7291 }
7292 else
7293 {
7294 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7295 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7296 }
7297 }
7298 return rc;
7299}
7300
7301#else /* !IN_RING3 */
7302
7303/**
7304 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
7305 */
7306static DECLCALLBACK(int) vgaRZConstruct(PPDMDEVINS pDevIns)
7307{
7308 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
7309 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
7310 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
7311
7312 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
7313 AssertRCReturn(rc, rc);
7314
7315 /*
7316 * Set I/O port callbacks for this context.
7317 * We just copy the ring-3 registration bits and remove the '&' before the handle.
7318 */
7319# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_hIoPort) do { \
7320 rc = PDMDevHlpIoPortSetUpContext(pDevIns, a_hIoPort, a_pfnWrite, a_pfnRead, NULL /*pvUser*/); \
7321 AssertRCReturn(rc, rc); \
7322 } while (0)
7323
7324 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", pThis->hIoPortAr);
7325 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", pThis->hIoPortMsrSt00);
7326 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", pThis->hIoPort3c3);
7327 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", pThis->hIoPortSr);
7328 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", pThis->hIoPortDac);
7329 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ pThis->hIoPortPos);
7330 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", pThis->hIoPortGr);
7331
7332 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", pThis->hIoPortMdaCrt);
7333 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", pThis->hIoPortMdaFcrSt);
7334 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", pThis->hIoPortCgaCrt);
7335 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", pThis->hIoPortCgaFcrSt);
7336
7337# ifdef CONFIG_BOCHS_VBE
7338 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", pThis->hIoPortVbeIndex);
7339 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", pThis->hIoPortVbeData);
7340# endif /* CONFIG_BOCHS_VBE */
7341
7342# undef REG_PORT
7343
7344 /* BIOS port: */
7345 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortBios, vgaIoPortWriteBios, vgaIoPortReadBios, NULL /*pvUser*/);
7346 AssertRCReturn(rc, rc);
7347
7348# ifdef VBOX_WITH_VMSVGA
7349 if (pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE)
7350 {
7351 AssertReturn(pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7352 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortVmSvga, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/);
7353 AssertRCReturn(rc, rc);
7354 }
7355 else
7356 AssertReturn(!pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7357# endif
7358
7359 /*
7360 * MMIO.
7361 */
7362 rc = PDMDevHlpMmioSetUpContextEx(pDevIns, pThis->hMmioLegacy, vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/);
7363 AssertRCReturn(rc, rc);
7364
7365 /*
7366 * Map the start of the VRAM into this context.
7367 */
7368# if defined(VBOX_WITH_2X_4GB_ADDR_SPACE) || (defined(IN_RING0) && defined(VGA_WITH_PARTIAL_RING0_MAPPING))
7369 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VRam, 0 /* off */, VGA_MAPPING_SIZE, (void **)&pThisCC->pbVRam);
7370 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMmio2SetUpContext(,VRAM,0,%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
7371# endif
7372
7373 /*
7374 * Map the first page of the VMSVGA FIFO into this context (not raw-mode).
7375 * We currently only access SVGA_FIFO_MIN, SVGA_FIFO_PITCHLOCK, and SVGA_FIFO_BUSY.
7376 */
7377# if defined(VBOX_WITH_VMSVGA) && !defined(IN_RC)
7378 AssertCompile((RT_MAX(SVGA_FIFO_MIN, RT_MAX(SVGA_FIFO_PITCHLOCK, SVGA_FIFO_BUSY)) + 1) * sizeof(uint32_t) < GUEST_PAGE_SIZE);
7379 if (pThis->fVMSVGAEnabled)
7380 {
7381 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VmSvgaFifo, 0 /* off */, GUEST_PAGE_SIZE,
7382 (void **)&pThisCC->svga.pau32FIFO);
7383 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
7384 }
7385 else
7386 AssertReturn(pThis->hMmio2VmSvgaFifo == NIL_PGMMMIO2HANDLE, VERR_INVALID_STATE);
7387# endif
7388
7389 return VINF_SUCCESS;
7390}
7391
7392#endif /* !IN_RING3 */
7393
7394/**
7395 * The device registration structure.
7396 */
7397const PDMDEVREG g_DeviceVga =
7398{
7399 /* .u32Version = */ PDM_DEVREG_VERSION,
7400 /* .uReserved0 = */ 0,
7401 /* .szName = */ "vga",
7402 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
7403 /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS,
7404 /* .cMaxInstances = */ 1,
7405 /* .uSharedVersion = */ 42,
7406 /* .cbInstanceShared = */ sizeof(VGASTATE),
7407 /* .cbInstanceCC = */ sizeof(VGASTATECC),
7408 /* .cbInstanceRC = */ sizeof(VGASTATERC),
7409 /* .cMaxPciDevices = */ 1,
7410 /* .cMaxMsixVectors = */ 0,
7411 /* .pszDescription = */ "VGA Adaptor with VESA extensions.",
7412#if defined(IN_RING3)
7413 /* .pszRCMod = */ "VBoxDDRC.rc",
7414 /* .pszR0Mod = */ "VBoxDDR0.r0",
7415 /* .pfnConstruct = */ vgaR3Construct,
7416 /* .pfnDestruct = */ vgaR3Destruct,
7417 /* .pfnRelocate = */ vgaR3Relocate,
7418 /* .pfnMemSetup = */ NULL,
7419 /* .pfnPowerOn = */ vgaR3PowerOn,
7420 /* .pfnReset = */ vgaR3Reset,
7421 /* .pfnSuspend = */ NULL,
7422 /* .pfnResume = */ vgaR3Resume,
7423 /* .pfnAttach = */ vgaAttach,
7424 /* .pfnDetach = */ vgaDetach,
7425 /* .pfnQueryInterface = */ NULL,
7426 /* .pfnInitComplete = */ NULL,
7427 /* .pfnPowerOff = */ vgaR3PowerOff,
7428 /* .pfnSoftReset = */ NULL,
7429 /* .pfnReserved0 = */ NULL,
7430 /* .pfnReserved1 = */ NULL,
7431 /* .pfnReserved2 = */ NULL,
7432 /* .pfnReserved3 = */ NULL,
7433 /* .pfnReserved4 = */ NULL,
7434 /* .pfnReserved5 = */ NULL,
7435 /* .pfnReserved6 = */ NULL,
7436 /* .pfnReserved7 = */ NULL,
7437#elif defined(IN_RING0)
7438 /* .pfnEarlyConstruct = */ NULL,
7439 /* .pfnConstruct = */ vgaRZConstruct,
7440 /* .pfnDestruct = */ NULL,
7441 /* .pfnFinalDestruct = */ NULL,
7442 /* .pfnRequest = */ NULL,
7443 /* .pfnReserved0 = */ NULL,
7444 /* .pfnReserved1 = */ NULL,
7445 /* .pfnReserved2 = */ NULL,
7446 /* .pfnReserved3 = */ NULL,
7447 /* .pfnReserved4 = */ NULL,
7448 /* .pfnReserved5 = */ NULL,
7449 /* .pfnReserved6 = */ NULL,
7450 /* .pfnReserved7 = */ NULL,
7451#elif defined(IN_RC)
7452 /* .pfnConstruct = */ vgaRZConstruct,
7453 /* .pfnReserved0 = */ NULL,
7454 /* .pfnReserved1 = */ NULL,
7455 /* .pfnReserved2 = */ NULL,
7456 /* .pfnReserved3 = */ NULL,
7457 /* .pfnReserved4 = */ NULL,
7458 /* .pfnReserved5 = */ NULL,
7459 /* .pfnReserved6 = */ NULL,
7460 /* .pfnReserved7 = */ NULL,
7461#else
7462# error "Not in IN_RING3, IN_RING0 or IN_RC!"
7463#endif
7464 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
7465};
7466
7467#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7468
7469/*
7470 * Local Variables:
7471 * nuke-trailing-whitespace-p:nil
7472 * End:
7473 */
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