VirtualBox

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

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

DevVGA: Fixed problem with VBE banking destroying MMIO2 remapping optimization (see bugref:10251).

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