VirtualBox

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

Last change on this file since 98523 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

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