VirtualBox

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

Last change on this file since 100108 was 100108, checked in by vboxsync, 18 months ago

*: Fix build issues when setting VBOX_WITH_WARNINGS_AS_ERRORS=1 on darwin.arm64 and make it a default, bugref:10469

  • 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 100108 2023-06-07 20:05:13Z 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
578static int 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 * 4 + 3 < 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 * @param cBits Logo depth.
4072 * @param xLogo Logo X position.
4073 * @param yLogo Logo Y position.
4074 * @param cxLogo Logo width.
4075 * @param cyLogo Logo height.
4076 * @param fInverse True if the bitmask is black on white (only for 1bpp)
4077 * @param iStep Fade in/fade out step.
4078 * @param pu32Palette Palette data.
4079 * @param pbSrc Source buffer.
4080 * @param pbDst Destination buffer.
4081 */
4082static void vbeR3ShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo,
4083 bool fInverse, uint8_t iStep, const uint32_t *pu32Palette, const uint8_t *pbSrc, uint8_t *pbDst)
4084{
4085 uint16_t i;
4086 size_t cbPadBytes = 0;
4087 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
4088 uint16_t cyLeft = cyLogo;
4089
4090 pbDst += xLogo * 4 + yLogo * cbLineDst;
4091
4092 switch (cBits)
4093 {
4094 case 1:
4095 pbDst += cyLogo * cbLineDst;
4096 cbPadBytes = 0;
4097 break;
4098
4099 case 4:
4100 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
4101 cbPadBytes = 0;
4102 else if ((cxLogo % 8) <= 2)
4103 cbPadBytes = 3;
4104 else if ((cxLogo % 8) <= 4)
4105 cbPadBytes = 2;
4106 else
4107 cbPadBytes = 1;
4108 break;
4109
4110 case 8:
4111 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
4112 break;
4113
4114 case 24:
4115 cbPadBytes = cxLogo % 4;
4116 break;
4117 }
4118
4119 uint8_t j = 0, c = 0;
4120
4121 while (cyLeft-- > 0)
4122 {
4123 uint8_t *pbTmpDst = pbDst;
4124
4125 if (cBits != 1)
4126 j = 0;
4127
4128 for (i = 0; i < cxLogo; i++)
4129 {
4130 switch (cBits)
4131 {
4132 case 1:
4133 {
4134 if (!j)
4135 c = *pbSrc++;
4136
4137 if (c & 1)
4138 {
4139 if (fInverse)
4140 {
4141 *pbTmpDst++ = 0;
4142 *pbTmpDst++ = 0;
4143 *pbTmpDst++ = 0;
4144 pbTmpDst++;
4145 }
4146 else
4147 {
4148 uint8_t pix = 0xFF * iStep / LOGO_SHOW_STEPS;
4149 *pbTmpDst++ = pix;
4150 *pbTmpDst++ = pix;
4151 *pbTmpDst++ = pix;
4152 pbTmpDst++;
4153 }
4154 }
4155 else
4156 pbTmpDst += 4;
4157 c >>= 1;
4158 j = (j + 1) % 8;
4159 break;
4160 }
4161
4162 case 4:
4163 {
4164 if (!j)
4165 c = *pbSrc++;
4166
4167 uint8_t pix = (c >> 4) & 0xF;
4168 c <<= 4;
4169
4170 uint32_t u32Pal = pu32Palette[pix];
4171
4172 pix = (u32Pal >> 16) & 0xFF;
4173 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4174 pix = (u32Pal >> 8) & 0xFF;
4175 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4176 pix = u32Pal & 0xFF;
4177 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4178 pbTmpDst++;
4179
4180 j = (j + 1) % 2;
4181 break;
4182 }
4183
4184 case 8:
4185 {
4186 uint32_t u32Pal = pu32Palette[*pbSrc++];
4187
4188 uint8_t pix = (u32Pal >> 16) & 0xFF;
4189 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4190 pix = (u32Pal >> 8) & 0xFF;
4191 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4192 pix = u32Pal & 0xFF;
4193 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
4194 pbTmpDst++;
4195 break;
4196 }
4197
4198 case 24:
4199 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4200 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4201 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
4202 pbTmpDst++;
4203 break;
4204 }
4205 }
4206
4207 pbDst -= cbLineDst;
4208 pbSrc += cbPadBytes;
4209 }
4210}
4211
4212
4213/**
4214 * @callback_method_impl{FNIOMIOPORTNEWOUT,
4215 * Port I/O Handler for BIOS Logo OUT operations.}
4216 */
4217static DECLCALLBACK(VBOXSTRICTRC)
4218vbeR3IoPortWriteCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
4219{
4220 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4221 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4222 RT_NOREF(pvUser, offPort);
4223
4224 Log(("vbeR3IoPortWriteCmdLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
4225
4226 if (cb == 2)
4227 {
4228 /* Get the logo command */
4229 switch (u32 & 0xFF00)
4230 {
4231 case LOGO_CMD_SET_OFFSET:
4232 pThisCC->offLogoData = u32 & 0xFF;
4233 break;
4234
4235 case LOGO_CMD_SHOW_BMP:
4236 {
4237 uint8_t iStep = u32 & 0xFF;
4238 const uint8_t *pbSrc = pThisCC->pbLogoBitmap;
4239 uint8_t *pbDst;
4240 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThisCC->pbLogo;
4241 uint32_t offDirty = 0;
4242 uint16_t xLogo = (LOGO_MAX_WIDTH - pThisCC->cxLogo) / 2;
4243 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThisCC->cyLogo) / 2;
4244
4245 /* Check VRAM size */
4246 if (pThis->vram_size < LOGO_MAX_SIZE)
4247 break;
4248
4249 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4250 pbDst = pThisCC->pbVRam + LOGO_MAX_SIZE;
4251 else
4252 pbDst = pThisCC->pbVRam;
4253
4254 /* Clear screen - except on power on... */
4255 if (!pThisCC->fLogoClearScreen)
4256 {
4257 /* Clear vram */
4258 uint32_t *pu32Dst = (uint32_t *)pbDst;
4259 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4260 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4261 *pu32Dst++ = 0;
4262 pThisCC->fLogoClearScreen = true;
4263 }
4264
4265 /* Show the bitmap. */
4266 vbeR3ShowBitmap(pThisCC->cLogoBits, xLogo, yLogo,
4267 pThisCC->cxLogo, pThisCC->cyLogo,
4268 false, iStep, &pThisCC->au32LogoPalette[0],
4269 pbSrc, pbDst);
4270
4271 /* Show the 'Press F12...' text. */
4272 if (pLogoHdr->fu8ShowBootMenu == 2)
4273 vbeR3ShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4274 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4275 pThisCC->fBootMenuInverse, iStep, &pThisCC->au32LogoPalette[0],
4276 &g_abLogoF12BootText[0], pbDst);
4277
4278 /* Blit the offscreen buffer. */
4279 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4280 {
4281 uint32_t *pu32TmpDst = (uint32_t *)pThisCC->pbVRam;
4282 uint32_t *pu32TmpSrc = (uint32_t *)(pThisCC->pbVRam + LOGO_MAX_SIZE);
4283 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4284 {
4285 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4286 *pu32TmpDst++ = *pu32TmpSrc++;
4287 }
4288 }
4289
4290 /* Set the dirty flags. */
4291 while (offDirty <= LOGO_MAX_SIZE)
4292 {
4293 vgaR3MarkDirty(pThis, offDirty);
4294 offDirty += GUEST_PAGE_SIZE;
4295 }
4296 break;
4297 }
4298
4299 default:
4300 Log(("vbeR3IoPortWriteCmdLogo: invalid command %d\n", u32));
4301 pThisCC->LogoCommand = LOGO_CMD_NOP;
4302 break;
4303 }
4304
4305 return VINF_SUCCESS;
4306 }
4307
4308 Log(("vbeR3IoPortWriteCmdLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4309 return VINF_SUCCESS;
4310}
4311
4312
4313/**
4314 * @callback_method_impl{FNIOMIOPORTIN,
4315 * Port I/O Handler for BIOS Logo IN operations.}
4316 */
4317static DECLCALLBACK(VBOXSTRICTRC)
4318vbeR3IoPortReadCmdLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
4319{
4320 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4321 RT_NOREF(pvUser, offPort);
4322
4323 if (pThisCC->offLogoData + cb > pThisCC->cbLogo)
4324 {
4325 Log(("vbeR3IoPortReadCmdLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4326 pThisCC->offLogoData, pThisCC->offLogoData, pThisCC->cbLogo, pThisCC->cbLogo));
4327 return VINF_SUCCESS;
4328 }
4329 RT_UNTRUSTED_VALIDATED_FENCE();
4330
4331 PCRTUINT64U p = (PCRTUINT64U)&pThisCC->pbLogo[pThisCC->offLogoData];
4332 switch (cb)
4333 {
4334 case 1: *pu32 = p->au8[0]; break;
4335 case 2: *pu32 = p->au16[0]; break;
4336 case 4: *pu32 = p->au32[0]; break;
4337 //case 8: *pu32 = p->au64[0]; break;
4338 default: AssertFailed(); break;
4339 }
4340 Log(("vbeR3IoPortReadCmdLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThisCC->offLogoData, pThisCC->offLogoData, cb, cb, pu32));
4341
4342 pThisCC->LogoCommand = LOGO_CMD_NOP;
4343 pThisCC->offLogoData += cb;
4344
4345 return VINF_SUCCESS;
4346}
4347
4348
4349/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4350
4351/**
4352 * @callback_method_impl{FNDBGFHANDLERDEV,
4353 * Dumps several interesting bits of the VGA state that are difficult to
4354 * decode from the registers.}
4355 */
4356static DECLCALLBACK(void) vgaR3InfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4357{
4358 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4359 int is_graph, double_scan;
4360 int w, h, char_height, char_dots;
4361 int val, vfreq_hz, hfreq_hz;
4362 vga_retrace_s *r = &pThis->retrace_state;
4363 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4364 const char *mem_map[] = { "A000-BFFF", "A000-AFFF", "B000-B7FF", "B800-BFFF" };
4365 NOREF(pszArgs);
4366
4367 is_graph = pThis->gr[6] & 1;
4368 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4369 double_scan = pThis->cr[9] >> 7;
4370 pHlp->pfnPrintf(pHlp, "decoding memory at %s\n", mem_map[(pThis->gr[6] >> 2) & 3]);
4371 pHlp->pfnPrintf(pHlp, "Misc status reg. MSR:%02X\n", pThis->msr);
4372 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4373 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4374 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4375 val = pThis->cr[0] + 5;
4376 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4377 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4378 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4379 val = pThis->cr[1] + 1;
4380 w = val * char_dots;
4381 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4382 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4383 h = val;
4384 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4385 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4386 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4387 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4388 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4389 if (!is_graph)
4390 {
4391 uint8_t ch_stride;
4392
4393 ch_stride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4394 val = (pThis->cr[9] & 0x1f) + 1;
4395 char_height = val;
4396 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4397 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4398
4399 uint32_t cbLine;
4400 uint32_t offStart;
4401 uint32_t offCursr;
4402 uint32_t uLineCompareIgn;
4403 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4404 if (!cbLine)
4405 cbLine = 80 * ch_stride;
4406 offStart *= ch_stride;
4407 offCursr = ((pThis->cr[0x0e] << 8) | pThis->cr[0x0f]) * ch_stride;
4408 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4409 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4410 pHlp->pfnPrintf(pHlp, "offCursr: %#x\n", offCursr);
4411 }
4412 if (pThis->fRealRetrace)
4413 {
4414 val = r->hb_start;
4415 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4416 val = r->hb_end;
4417 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4418 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4419 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4420 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4421 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4422 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4423 {
4424 vfreq_hz = 1000000000 / r->frame_ns;
4425 hfreq_hz = 1000000000 / r->h_total_ns;
4426 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4427 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4428 }
4429 }
4430 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4431
4432# ifdef VBOX_WITH_VMSVGA
4433 if (pThis->svga.fEnabled)
4434 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4435 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4436# endif
4437}
4438
4439
4440/**
4441 * Prints a separator line.
4442 *
4443 * @param pHlp Callback functions for doing output.
4444 * @param cCols The number of columns.
4445 * @param pszTitle The title text, NULL if none.
4446 */
4447static void vgaR3InfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4448{
4449 if (pszTitle)
4450 {
4451 size_t cchTitle = strlen(pszTitle);
4452 if (cchTitle + 6 >= cCols)
4453 {
4454 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4455 cCols = 0;
4456 }
4457 else
4458 {
4459 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4460 cCols -= cchLeft + cchTitle + 2;
4461 while (cchLeft-- > 0)
4462 pHlp->pfnPrintf(pHlp, "-");
4463 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4464 }
4465 }
4466
4467 while (cCols-- > 0)
4468 pHlp->pfnPrintf(pHlp, "-");
4469 pHlp->pfnPrintf(pHlp, "\n");
4470}
4471
4472
4473/**
4474 * Worker for vgaR3InfoText.
4475 *
4476 * @param pThis The shared VGA state.
4477 * @param pThisCC The VGA state for ring-3.
4478 * @param pHlp Callback functions for doing output.
4479 * @param offStart Where to start dumping (relative to the VRAM).
4480 * @param cbLine The source line length (aka line_offset).
4481 * @param cCols The number of columns on the screen.
4482 * @param cRows The number of rows to dump.
4483 * @param iScrBegin The row at which the current screen output starts.
4484 * @param iScrEnd The row at which the current screen output end
4485 * (exclusive).
4486 */
4487static void vgaR3InfoTextWorker(PVGASTATE pThis, PVGASTATER3 pThisCC, PCDBGFINFOHLP pHlp,
4488 uint32_t offStart, uint32_t cbLine,
4489 uint32_t cCols, uint32_t cRows,
4490 uint32_t iScrBegin, uint32_t iScrEnd)
4491{
4492 /* Title, */
4493 char szTitle[32];
4494 if (iScrBegin || iScrEnd < cRows)
4495 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4496 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4497 else
4498 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4499
4500 /* Do the dumping. */
4501 uint8_t const *pbSrcOuter = pThisCC->pbVRam + offStart;
4502 uint8_t const cStride = pThis->cr[0x17] & 0x40 ? 4 : 8;
4503 uint32_t iRow;
4504 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4505 {
4506 if ((uintptr_t)(pbSrcOuter + cbLine - pThisCC->pbVRam) > pThis->vram_size) {
4507 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4508 break;
4509 }
4510
4511 if (iRow == 0)
4512 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4513 else if (iRow == iScrBegin)
4514 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4515 else if (iRow == iScrEnd)
4516 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4517
4518 uint8_t const *pbSrc = pbSrcOuter;
4519 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4520 {
4521 if (RT_C_IS_PRINT(*pbSrc))
4522 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4523 else
4524 pHlp->pfnPrintf(pHlp, ".");
4525 pbSrc += cStride; /* chars are spaced 8 or sometimes 4 bytes apart */
4526 }
4527 pHlp->pfnPrintf(pHlp, "\n");
4528 }
4529
4530 /* Final separator. */
4531 vgaR3InfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4532}
4533
4534
4535/**
4536 * @callback_method_impl{FNDBGFHANDLERDEV,
4537 * Dumps VGA memory formatted as ASCII text\, no attributes. Only looks at
4538 * the first page.}
4539 */
4540static DECLCALLBACK(void) vgaR3InfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4541{
4542 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4543 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
4544
4545 /*
4546 * Parse args.
4547 */
4548 bool fAll = true;
4549 if (pszArgs && *pszArgs)
4550 {
4551 if (!strcmp(pszArgs, "all"))
4552 fAll = true;
4553 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4554 fAll = false;
4555 else
4556 {
4557 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4558 return;
4559 }
4560 }
4561
4562 /*
4563 * Check that we're in text mode and that the VRAM is accessible.
4564 */
4565 if (!(pThis->gr[6] & 1))
4566 {
4567 uint8_t *pbSrc = pThisCC->pbVRam;
4568 if (pbSrc)
4569 {
4570 /*
4571 * Figure out the display size and where the text is.
4572 *
4573 * Note! We're cutting quite a few corners here and this code could
4574 * do with some brushing up. Dumping from the start of the
4575 * frame buffer is done intentionally so that we're more
4576 * likely to obtain the full scrollback of a linux panic.
4577 * 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";
4578 */
4579 uint32_t cbLine;
4580 uint32_t offStart;
4581 uint32_t uLineCompareIgn;
4582 vgaR3GetOffsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4583 if (!cbLine)
4584 cbLine = 80 * 8;
4585 offStart *= 8;
4586
4587 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4588 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4589 uint32_t uDblScan = pThis->cr[9] >> 7;
4590 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4591 if (cScrRows < 25)
4592 cScrRows = 25;
4593 uint32_t iScrBegin = offStart / cbLine;
4594 uint32_t cRows = iScrBegin + cScrRows;
4595 uint32_t cCols = cbLine / 8;
4596
4597 if (fAll)
4598 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart - iScrBegin * cbLine, cbLine,
4599 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4600 else
4601 vgaR3InfoTextWorker(pThis, pThisCC, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4602 }
4603 else
4604 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4605 }
4606 else
4607 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4608}
4609
4610
4611/**
4612 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4613 */
4614static DECLCALLBACK(void) vgaR3InfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4615{
4616 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4617 NOREF(pszArgs);
4618
4619 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4620 Assert(sizeof(pThis->sr) >= 8);
4621 for (unsigned i = 0; i < 8; ++i)
4622 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4623 pHlp->pfnPrintf(pHlp, "\n");
4624}
4625
4626
4627/**
4628 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4629 */
4630static DECLCALLBACK(void) vgaR3InfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4631{
4632 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4633 unsigned i;
4634 NOREF(pszArgs);
4635
4636 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4637 Assert(sizeof(pThis->cr) >= 24);
4638 for (i = 0; i < 10; ++i)
4639 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4640 pHlp->pfnPrintf(pHlp, "\n");
4641 for (i = 10; i < 20; ++i)
4642 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4643 pHlp->pfnPrintf(pHlp, "\n");
4644 for (i = 20; i < 25; ++i)
4645 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4646 pHlp->pfnPrintf(pHlp, "\n");
4647}
4648
4649
4650/**
4651 * @callback_method_impl{FNDBGFHANDLERDEV,
4652 * Dumps VGA Graphics Controller registers.}
4653 */
4654static DECLCALLBACK(void) vgaR3InfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4655{
4656 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4657 NOREF(pszArgs);
4658
4659 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4660 Assert(sizeof(pThis->gr) >= 9);
4661 for (unsigned i = 0; i < 9; ++i)
4662 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4663 pHlp->pfnPrintf(pHlp, "\n");
4664}
4665
4666
4667/**
4668 * @callback_method_impl{FNDBGFHANDLERDEV,
4669 * Dumps VGA Attribute Controller registers.}
4670 */
4671static DECLCALLBACK(void) vgaR3InfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4672{
4673 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4674 unsigned i;
4675 NOREF(pszArgs);
4676
4677 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4678 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4679 Assert(sizeof(pThis->ar) >= 0x14);
4680 pHlp->pfnPrintf(pHlp, " Palette:");
4681 for (i = 0; i < 0x10; ++i)
4682 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4683 pHlp->pfnPrintf(pHlp, "\n");
4684 for (i = 0x10; i <= 0x14; ++i)
4685 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4686 pHlp->pfnPrintf(pHlp, "\n");
4687}
4688
4689
4690/**
4691 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4692 */
4693static DECLCALLBACK(void) vgaR3InfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4694{
4695 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4696 NOREF(pszArgs);
4697
4698 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4699 for (unsigned i = 0; i < 0x100; ++i)
4700 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4701 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4702}
4703
4704
4705/**
4706 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4707 */
4708static DECLCALLBACK(void) vgaR3InfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4709{
4710 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4711 NOREF(pszArgs);
4712
4713 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4714 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4715 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4716 else
4717 {
4718 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4719 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4720 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4721 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4722 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4723 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4724 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4725 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4726 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4727 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4728 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4729 pHlp->pfnPrintf(pHlp, " DAC: %d-bit\n", pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_8BIT_DAC ? 8 : 6);
4730 }
4731}
4732
4733
4734/**
4735 * @callback_method_impl{FNDBGFHANDLERDEV,
4736 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4737 * in human-readable form.}
4738 */
4739static DECLCALLBACK(void) vgaR3InfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4740{
4741 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4742 NOREF(pszArgs);
4743
4744 unsigned val1 = (pThis->gr[5] >> 3) & 1;
4745 unsigned val2 = pThis->gr[5] & 3;
4746 pHlp->pfnPrintf(pHlp, "read mode : %u write mode: %u\n", val1, val2);
4747 val1 = pThis->gr[0];
4748 val2 = pThis->gr[1];
4749 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4750 val1 = pThis->gr[2];
4751 val2 = pThis->gr[4] & 3;
4752 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %u\n", val1, val2);
4753 val1 = pThis->gr[3] & 7;
4754 val2 = (pThis->gr[3] >> 3) & 3;
4755 pHlp->pfnPrintf(pHlp, "rotate : %u function : %u\n", val1, val2);
4756 val1 = pThis->gr[7];
4757 val2 = pThis->gr[8];
4758 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4759 val1 = pThis->sr[2];
4760 val2 = pThis->sr[4] & 8;
4761 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4762}
4763
4764
4765/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4766
4767/**
4768 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4769 */
4770static DECLCALLBACK(void *) vgaR3PortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4771{
4772 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IBase);
4773 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
4774 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThisCC->IPort);
4775# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
4776 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThisCC->IVBVACallbacks);
4777# endif
4778 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
4779 return NULL;
4780}
4781
4782
4783/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4784
4785/**
4786 * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
4787 */
4788static DECLCALLBACK(int) vgaR3PortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4789{
4790 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, ILeds);
4791 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4792 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4793 switch (iLUN)
4794 {
4795 /* LUN #0 is the only one for which we have a status LED. */
4796 case 0:
4797 {
4798 *ppLed = &pThis->Led3D;
4799 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4800 return VINF_SUCCESS;
4801 }
4802
4803 default:
4804 AssertMsgFailed(("Invalid LUN #%u\n", iLUN));
4805 return VERR_PDM_NO_SUCH_LUN;
4806 }
4807}
4808
4809
4810/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4811
4812/**
4813 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnResize}
4814 */
4815static DECLCALLBACK(int) vgaR3DummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t cBits, void *pvVRAM,
4816 uint32_t cbLine, uint32_t cx, uint32_t cy)
4817{
4818 RT_NOREF(pInterface, cBits, pvVRAM, cbLine, cx, cy);
4819 return VINF_SUCCESS;
4820}
4821
4822
4823/**
4824 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnUpdateRect}
4825 */
4826static DECLCALLBACK(void) vgaR3DummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4827{
4828 RT_NOREF(pInterface, x, y, cx, cy);
4829}
4830
4831
4832/**
4833 * @interface_method_impl{PDMIDISPLAYCONNECTOR,pfnRefresh}
4834 */
4835static DECLCALLBACK(void) vgaR3DummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4836{
4837 NOREF(pInterface);
4838}
4839
4840
4841/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4842
4843/**
4844 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplay}
4845 */
4846static DECLCALLBACK(int) vgaR3PortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4847{
4848 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4849 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4850 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4851
4852 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4853 AssertRCReturn(rc, rc);
4854
4855# ifdef VBOX_WITH_VMSVGA
4856 if ( pThis->svga.fEnabled
4857 && !pThis->svga.fTraces)
4858 {
4859 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4860 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4861 return VINF_SUCCESS;
4862 }
4863#endif
4864
4865# ifndef VBOX_WITH_HGSMI
4866 /* This should be called only in non VBVA mode. */
4867# else
4868 if (VBVAUpdateDisplay(pThis, pThisCC) == VINF_SUCCESS)
4869 {
4870 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4871 return VINF_SUCCESS;
4872 }
4873# endif /* VBOX_WITH_HGSMI */
4874
4875 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4876
4877 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4878 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4879
4880 if (pThis->bmPageRemappedVGA != 0)
4881 {
4882 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4883 STAM_COUNTER_INC(&pThis->StatMapReset);
4884 vgaResetRemapped(pThis);
4885 }
4886
4887 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, false /*fFailOnResize*/, true /*reset_dirty*/,
4888 pThisCC->pDrv, &pThis->graphic_mode);
4889 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4890 return rc;
4891}
4892
4893
4894/**
4895 * Internal vgaR3PortUpdateDisplayAll worker called under pThis->CritSect.
4896 */
4897static int vgaR3UpdateDisplayAllInternal(PPDMDEVINS pDevIns, PVGASTATE pThis, PVGASTATECC pThisCC, bool fFailOnResize)
4898{
4899# ifdef VBOX_WITH_VMSVGA
4900 if ( !pThis->svga.fEnabled
4901 || pThis->svga.fTraces)
4902# endif
4903 {
4904 /* Update the dirty bits. */
4905 if (pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4906 vgaR3UpdateDirtyBitsAndResetMonitoring(pDevIns, pThis);
4907 }
4908
4909 if (pThis->bmPageRemappedVGA != 0)
4910 {
4911 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
4912 STAM_COUNTER_INC(&pThis->StatMapReset);
4913 vgaResetRemapped(pThis);
4914 }
4915
4916 pThis->graphic_mode = -1; /* force full update */
4917
4918 return vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, true /*fUpdateAll*/, fFailOnResize,
4919 true /*reset_dirty*/, pThisCC->pDrv, &pThis->graphic_mode);
4920}
4921
4922
4923/**
4924 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayAll}
4925 */
4926static DECLCALLBACK(int) vgaR3PortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4927{
4928 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4929 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4930 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4931
4932 /* This is called both in VBVA mode and normal modes. */
4933
4934# ifdef DEBUG_sunlover
4935 LogFlow(("vgaR3PortUpdateDisplayAll\n"));
4936# endif /* DEBUG_sunlover */
4937
4938 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
4939 AssertRCReturn(rc, rc);
4940
4941 rc = vgaR3UpdateDisplayAllInternal(pDevIns, pThis, pThisCC, fFailOnResize);
4942
4943 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
4944 return rc;
4945}
4946
4947
4948/**
4949 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRefreshRate}
4950 */
4951static DECLCALLBACK(int) vgaR3PortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4952{
4953 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4954 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4955 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4956
4957 /*
4958 * Update the interval, notify the VMSVGA FIFO thread if sleeping,
4959 * then restart or stop the timer.
4960 */
4961 ASMAtomicWriteU32(&pThis->cMilliesRefreshInterval, cMilliesInterval);
4962
4963# ifdef VBOX_WITH_VMSVGA
4964 if (pThis->svga.fFIFOThreadSleeping)
4965 PDMDevHlpSUPSemEventSignal(pDevIns, pThis->svga.hFIFORequestSem);
4966# endif
4967
4968 if (cMilliesInterval)
4969 return PDMDevHlpTimerSetMillies(pDevIns, pThis->hRefreshTimer, cMilliesInterval);
4970 return PDMDevHlpTimerStop(pDevIns, pThis->hRefreshTimer);
4971}
4972
4973
4974/**
4975 * @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode}
4976 */
4977static DECLCALLBACK(int) vgaR3PortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4978{
4979 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
4980 PPDMDEVINS pDevIns = pThisCC->pDevIns;
4981 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
4982
4983 AssertReturn(pcBits, VERR_INVALID_PARAMETER);
4984
4985 *pcBits = vgaR3GetBpp(pThis);
4986 if (pcx)
4987 *pcx = pThis->last_scr_width;
4988 if (pcy)
4989 *pcy = pThis->last_scr_height;
4990 return VINF_SUCCESS;
4991}
4992
4993
4994/**
4995 * @interface_method_impl{PDMIDISPLAYPORT,pfnTakeScreenshot}
4996 */
4997static DECLCALLBACK(int) vgaR3PortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData,
4998 uint32_t *pcx, uint32_t *pcy)
4999{
5000 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5001 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5002 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5003 PDMDEV_ASSERT_EMT(pDevIns);
5004
5005 LogFlow(("vgaR3PortTakeScreenshot: ppbData=%p pcbData=%p pcx=%p pcy=%p\n", ppbData, pcbData, pcx, pcy));
5006
5007 /*
5008 * Validate input.
5009 */
5010 if (!RT_VALID_PTR(ppbData) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
5011 return VERR_INVALID_PARAMETER;
5012
5013 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5014 AssertRCReturn(rc, rc);
5015
5016 /*
5017 * Get screenshot. This function will fail if a resize is required.
5018 * So there is not need to do a 'vgaR3UpdateDisplayAllInternal' before taking screenshot.
5019 */
5020
5021 /*
5022 * Allocate the buffer for 32 bits per pixel bitmap
5023 *
5024 * Note! The size can't be zero or greater than the size of the VRAM.
5025 * Inconsistent VGA device state can cause the incorrect size values.
5026 */
5027 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
5028 if (cbRequired && cbRequired <= pThis->vram_size)
5029 {
5030 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbRequired);
5031 if (pbData != NULL)
5032 {
5033 /*
5034 * Only 3 methods, assigned below, will be called during the screenshot update.
5035 * All other are already set to NULL.
5036 */
5037 /* The display connector interface is temporarily replaced with the fake one. */
5038 PDMIDISPLAYCONNECTOR Connector;
5039 RT_ZERO(Connector);
5040 Connector.pbData = pbData;
5041 Connector.cBits = 32;
5042 Connector.cx = pThis->last_scr_width;
5043 Connector.cy = pThis->last_scr_height;
5044 Connector.cbScanline = Connector.cx * 4;
5045 Connector.pfnRefresh = vgaR3DummyRefresh;
5046 Connector.pfnResize = vgaR3DummyResize;
5047 Connector.pfnUpdateRect = vgaR3DummyUpdateRect;
5048
5049 int32_t cur_graphic_mode = -1;
5050
5051 bool fSavedRenderVRAM = pThis->fRenderVRAM;
5052 pThis->fRenderVRAM = true;
5053
5054 /*
5055 * Take the screenshot.
5056 *
5057 * The second parameter is 'false' because the current display state is being rendered to an
5058 * external buffer using a fake connector. That is if display is blanked, we expect a black
5059 * screen in the external buffer.
5060 * If there is a pending resize, the function will fail.
5061 */
5062 rc = vgaR3UpdateDisplay(pDevIns, pThis, pThisCC, false /*fUpdateAll*/, true /*fFailOnResize*/,
5063 false /*reset_dirty*/, &Connector, &cur_graphic_mode);
5064
5065 pThis->fRenderVRAM = fSavedRenderVRAM;
5066
5067 if (rc == VINF_SUCCESS)
5068 {
5069 /*
5070 * Return the result.
5071 */
5072 *ppbData = pbData;
5073 *pcbData = cbRequired;
5074 *pcx = Connector.cx;
5075 *pcy = Connector.cy;
5076 }
5077 else
5078 {
5079 /* If we do not return a success, then the data buffer must be freed. */
5080 RTMemFree(pbData);
5081 if (RT_SUCCESS_NP(rc))
5082 {
5083 AssertMsgFailed(("%Rrc\n", rc));
5084 rc = VERR_INTERNAL_ERROR_5;
5085 }
5086 }
5087 }
5088 else
5089 rc = VERR_NO_MEMORY;
5090 }
5091 else
5092 rc = VERR_NOT_SUPPORTED;
5093
5094 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5095
5096 LogFlow(("vgaR3PortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
5097 return rc;
5098}
5099
5100
5101/**
5102 * @interface_method_impl{PDMIDISPLAYPORT,pfnFreeScreenshot}
5103 */
5104static DECLCALLBACK(void) vgaR3PortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
5105{
5106 NOREF(pInterface);
5107
5108 LogFlow(("vgaR3PortFreeScreenshot: pbData=%p\n", pbData));
5109
5110 RTMemFree(pbData);
5111}
5112
5113
5114/**
5115 * @interface_method_impl{PDMIDISPLAYPORT,pfnDisplayBlt}
5116 */
5117static DECLCALLBACK(int) vgaR3PortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData,
5118 uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
5119{
5120 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5121 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5122 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5123 PDMDEV_ASSERT_EMT(pDevIns);
5124 LogFlow(("vgaR3PortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
5125
5126 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5127 AssertRCReturn(rc, rc);
5128
5129 /*
5130 * Validate input.
5131 */
5132 if ( pvData
5133 && x < pThisCC->pDrv->cx
5134 && cx <= pThisCC->pDrv->cx
5135 && cx + x <= pThisCC->pDrv->cx
5136 && y < pThisCC->pDrv->cy
5137 && cy <= pThisCC->pDrv->cy
5138 && cy + y <= pThisCC->pDrv->cy)
5139 {
5140 /*
5141 * Determine bytes per pixel in the destination buffer.
5142 */
5143 size_t cbPixelDst = 0;
5144 switch (pThisCC->pDrv->cBits)
5145 {
5146 case 8:
5147 cbPixelDst = 1;
5148 break;
5149 case 15:
5150 case 16:
5151 cbPixelDst = 2;
5152 break;
5153 case 24:
5154 cbPixelDst = 3;
5155 break;
5156 case 32:
5157 cbPixelDst = 4;
5158 break;
5159 default:
5160 rc = VERR_INVALID_PARAMETER;
5161 break;
5162 }
5163 if (RT_SUCCESS(rc))
5164 {
5165 /*
5166 * The blitting loop.
5167 */
5168 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
5169 uint8_t *pbSrc = (uint8_t *)pvData;
5170 size_t cbLineDst = pThisCC->pDrv->cbScanline;
5171 uint8_t *pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5172 uint32_t cyLeft = cy;
5173 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5174 Assert(pfnVgaDrawLine);
5175 while (cyLeft-- > 0)
5176 {
5177 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5178 pbDst += cbLineDst;
5179 pbSrc += cbLineSrc;
5180 }
5181
5182 /*
5183 * Invalidate the area.
5184 */
5185 pThisCC->pDrv->pfnUpdateRect(pThisCC->pDrv, x, y, cx, cy);
5186 }
5187 }
5188 else
5189 rc = VERR_INVALID_PARAMETER;
5190
5191 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5192
5193 LogFlow(("vgaR3PortDisplayBlt: returns %Rrc\n", rc));
5194 return rc;
5195}
5196
5197
5198/**
5199 * @interface_method_impl{PDMIDISPLAYPORT,pfnUpdateDisplayRect}
5200 */
5201static DECLCALLBACK(void) vgaR3PortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t cx, uint32_t cy)
5202{
5203 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5204 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5205 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5206 uint32_t v;
5207
5208 uint32_t cbPixelDst;
5209 uint32_t cbLineDst;
5210 uint8_t *pbDst;
5211
5212 uint32_t cbPixelSrc;
5213 uint32_t cbLineSrc;
5214 uint8_t *pbSrc;
5215
5216
5217# ifdef DEBUG_sunlover
5218 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d\n", x, y, cx, cy));
5219# endif /* DEBUG_sunlover */
5220
5221 Assert(pInterface);
5222
5223 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5224 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
5225
5226 /* Check if there is something to do at all. */
5227 if (!pThis->fRenderVRAM)
5228 {
5229 /* The framebuffer uses the guest VRAM directly. */
5230# ifdef DEBUG_sunlover
5231 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do fRender is false.\n"));
5232# endif /* DEBUG_sunlover */
5233 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5234 return;
5235 }
5236
5237 Assert(pThisCC->pDrv);
5238 Assert(pThisCC->pDrv->pbData);
5239
5240 /* Correct negative x and y coordinates. */
5241 if (x < 0)
5242 {
5243 x += cx; /* Compute xRight which is also the new width. */
5244 cx = (x < 0) ? 0 : x;
5245 x = 0;
5246 }
5247
5248 if (y < 0)
5249 {
5250 y += cy; /* Compute yBottom, which is also the new height. */
5251 cy = (y < 0) ? 0 : y;
5252 y = 0;
5253 }
5254
5255 /* Also check if coords are greater than the display resolution. */
5256 if (x + cx > pThisCC->pDrv->cx)
5257 {
5258 // x < 0 is not possible here
5259 cx = pThisCC->pDrv->cx > (uint32_t)x? pThisCC->pDrv->cx - x: 0;
5260 }
5261
5262 if (y + cy > pThisCC->pDrv->cy)
5263 {
5264 // y < 0 is not possible here
5265 cy = pThisCC->pDrv->cy > (uint32_t)y? pThisCC->pDrv->cy - y: 0;
5266 }
5267
5268# ifdef DEBUG_sunlover
5269 LogFlow(("vgaR3PortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, cx, cy));
5270# endif
5271
5272 /* Check if there is something to do at all. */
5273 if (cx == 0 || cy == 0)
5274 {
5275 /* Empty rectangle. */
5276# ifdef DEBUG_sunlover
5277 LogFlow(("vgaR3PortUpdateDisplayRect: nothing to do: %dx%d\n", cx, cy));
5278#endif
5279 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5280 return;
5281 }
5282
5283 /** @todo This method should be made universal and not only for VBVA.
5284 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5285 * changed.
5286 */
5287
5288 /* Choose the rendering function. */
5289 switch(pThisCC->get_bpp(pThis))
5290 {
5291 default:
5292 case 0:
5293 /* A LFB mode is already disabled, but the callback is still called
5294 * by Display because VBVA buffer is being flushed.
5295 * Nothing to do, just return.
5296 */
5297 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5298 return;
5299 case 8:
5300 v = VGA_DRAW_LINE8;
5301 break;
5302 case 15:
5303 v = VGA_DRAW_LINE15;
5304 break;
5305 case 16:
5306 v = VGA_DRAW_LINE16;
5307 break;
5308 case 24:
5309 v = VGA_DRAW_LINE24;
5310 break;
5311 case 32:
5312 v = VGA_DRAW_LINE32;
5313 break;
5314 }
5315
5316 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(pThisCC->pDrv->cBits)];
5317
5318 /* Compute source and destination addresses and pitches. */
5319 cbPixelDst = (pThisCC->pDrv->cBits + 7) / 8;
5320 cbLineDst = pThisCC->pDrv->cbScanline;
5321 pbDst = pThisCC->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5322
5323 cbPixelSrc = (pThisCC->get_bpp(pThis) + 7) / 8;
5324 uint32_t offSrc, u32Dummy;
5325 pThisCC->get_offsets(pThis, &cbLineSrc, &offSrc, &u32Dummy);
5326
5327 /* Assume that rendering is performed only on visible part of VRAM.
5328 * This is true because coordinates were verified.
5329 */
5330 pbSrc = pThisCC->pbVRam;
5331 pbSrc += offSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5332
5333 /* Render VRAM to framebuffer. */
5334
5335# ifdef DEBUG_sunlover
5336 LogFlow(("vgaR3PortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDst, cbLineDst, cbPixelDst, pbSrc, cbLineSrc, cbPixelSrc));
5337# endif
5338
5339 while (cy-- > 0)
5340 {
5341 pfnVgaDrawLine(pThis, pThisCC, pbDst, pbSrc, cx);
5342 pbDst += cbLineDst;
5343 pbSrc += cbLineSrc;
5344 }
5345
5346 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5347# ifdef DEBUG_sunlover
5348 LogFlow(("vgaR3PortUpdateDisplayRect: completed.\n"));
5349# endif
5350}
5351
5352
5353/**
5354 * @interface_method_impl{PDMIDISPLAYPORT,pfnCopyRect}
5355 */
5356static DECLCALLBACK(int)
5357vgaR3PortCopyRect(PPDMIDISPLAYPORT pInterface,
5358 uint32_t cx, uint32_t cy,
5359 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
5360 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
5361 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
5362 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
5363{
5364 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5365 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5366 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5367 uint32_t v;
5368
5369# ifdef DEBUG_sunlover
5370 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, cx, cy, xDst, yDst));
5371# endif
5372
5373 Assert(pInterface);
5374 Assert(pThisCC->pDrv);
5375
5376 int32_t xSrcCorrected = xSrc;
5377 int32_t ySrcCorrected = ySrc;
5378 uint32_t cxCorrected = cx;
5379 uint32_t cyCorrected = cy;
5380
5381 /* Correct source coordinates to be within the source bitmap. */
5382 if (xSrcCorrected < 0)
5383 {
5384 xSrcCorrected += cxCorrected; /* Compute xRight which is also the new width. */
5385 cxCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5386 xSrcCorrected = 0;
5387 }
5388
5389 if (ySrcCorrected < 0)
5390 {
5391 ySrcCorrected += cyCorrected; /* Compute yBottom, which is also the new height. */
5392 cyCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5393 ySrcCorrected = 0;
5394 }
5395
5396 /* Also check if coords are greater than the display resolution. */
5397 if (xSrcCorrected + cxCorrected > cxSrc)
5398 {
5399 /* xSrcCorrected < 0 is not possible here */
5400 cxCorrected = cxSrc > (uint32_t)xSrcCorrected ? cxSrc - xSrcCorrected : 0;
5401 }
5402
5403 if (ySrcCorrected + cyCorrected > cySrc)
5404 {
5405 /* y < 0 is not possible here */
5406 cyCorrected = cySrc > (uint32_t)ySrcCorrected ? cySrc - ySrcCorrected : 0;
5407 }
5408
5409# ifdef DEBUG_sunlover
5410 LogFlow(("vgaR3PortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, cxCorrected, cyCorrected));
5411# endif
5412
5413 /* Check if there is something to do at all. */
5414 if (cxCorrected == 0 || cyCorrected == 0)
5415 {
5416 /* Empty rectangle. */
5417# ifdef DEBUG_sunlover
5418 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", cxCorrected, cyCorrected));
5419# endif
5420 return VINF_SUCCESS;
5421 }
5422
5423 /* Check that the corrected source rectangle is within the destination.
5424 * Note: source rectangle is adjusted, but the target must be large enough.
5425 */
5426 if ( xDst < 0
5427 || yDst < 0
5428 || xDst + cxCorrected > cxDst
5429 || yDst + cyCorrected > cyDst)
5430 {
5431 return VERR_INVALID_PARAMETER;
5432 }
5433
5434 int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5435 AssertRCReturn(rc, rc);
5436
5437 /* This method only works if the VGA device is in a VBE mode or not paused VBVA mode.
5438 * VGA modes are reported to the caller by returning VERR_INVALID_STATE.
5439 *
5440 * If VBE_DISPI_ENABLED is set, then it is a VBE or VBE compatible VBVA mode. Both of them can be handled.
5441 *
5442 * If VBE_DISPI_ENABLED is clear, then it is either a VGA mode or a VBVA mode set by guest additions
5443 * which have VBVACAPS_USE_VBVA_ONLY capability.
5444 * When VBE_DISPI_ENABLED is being cleared and VBVACAPS_USE_VBVA_ONLY is not set (i.e. guest wants a VGA mode),
5445 * then VBVAOnVBEChanged makes sure that VBVA is paused.
5446 * That is a not paused VBVA means that the video mode can be handled even if VBE_DISPI_ENABLED is clear.
5447 */
5448 if ( (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0
5449 && VBVAIsPaused(pThisCC)
5450# ifdef VBOX_WITH_VMSVGA
5451 && !pThis->svga.fEnabled
5452# endif
5453 )
5454 {
5455 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5456 return VERR_INVALID_STATE;
5457 }
5458
5459 /* Choose the rendering function. */
5460 switch (cSrcBitsPerPixel)
5461 {
5462 default:
5463 case 0:
5464 /* Nothing to do, just return. */
5465 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5466 return VINF_SUCCESS;
5467 case 8:
5468 v = VGA_DRAW_LINE8;
5469 break;
5470 case 15:
5471 v = VGA_DRAW_LINE15;
5472 break;
5473 case 16:
5474 v = VGA_DRAW_LINE16;
5475 break;
5476 case 24:
5477 v = VGA_DRAW_LINE24;
5478 break;
5479 case 32:
5480 v = VGA_DRAW_LINE32;
5481 break;
5482 }
5483
5484 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[v * 4 + vgaR3GetDepthIndex(cDstBitsPerPixel)];
5485
5486 /* Compute source and destination addresses and pitches. */
5487 uint32_t cbPixelDst = (cDstBitsPerPixel + 7) / 8;
5488 uint32_t cbLineDst = cbDstLine;
5489 uint8_t *pbDstCur = pbDst + yDst * cbLineDst + xDst * cbPixelDst;
5490
5491 uint32_t cbPixelSrc = (cSrcBitsPerPixel + 7) / 8;
5492 uint32_t cbLineSrc = cbSrcLine;
5493 const uint8_t *pbSrcCur = pbSrc + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5494
5495# ifdef DEBUG_sunlover
5496 LogFlow(("vgaR3PortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDstCur, cbLineDst, cbPixelDst, pbSrcCur, cbLineSrc, cbPixelSrc));
5497# endif
5498
5499 while (cyCorrected-- > 0)
5500 {
5501 pfnVgaDrawLine(pThis, pThisCC, pbDstCur, pbSrcCur, cxCorrected);
5502 pbDstCur += cbLineDst;
5503 pbSrcCur += cbLineSrc;
5504 }
5505
5506 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5507# ifdef DEBUG_sunlover
5508 LogFlow(("vgaR3PortCopyRect: completed.\n"));
5509# endif
5510 return VINF_SUCCESS;
5511}
5512
5513
5514/**
5515 * @interface_method_impl{PDMIDISPLAYPORT,pfnSetRenderVRAM}
5516 */
5517static DECLCALLBACK(void) vgaR3PortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5518{
5519 PVGASTATECC pThisCC = RT_FROM_MEMBER(pInterface, VGASTATECC, IPort);
5520 PPDMDEVINS pDevIns = pThisCC->pDevIns;
5521 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5522
5523 LogFlow(("vgaR3PortSetRenderVRAM: fRender = %d\n", fRender));
5524
5525 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
5526 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
5527
5528 pThis->fRenderVRAM = fRender;
5529
5530 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
5531}
5532
5533
5534/**
5535 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorCapabilities}
5536 */
5537static DECLCALLBACK(void) vgaR3PortReportHostCursorCapabilities(PPDMIDISPLAYPORT pInterface, bool fSupportsRenderCursor,
5538 bool fSupportsMoveCursor)
5539{
5540 RT_NOREF(pInterface, fSupportsRenderCursor, fSupportsMoveCursor);
5541}
5542
5543
5544/**
5545 * @interface_method_impl{PDMIDISPLAYPORT,pfnReportHostCursorPosition}
5546 */
5547static DECLCALLBACK(void) vgaR3PortReportHostCursorPosition(PPDMIDISPLAYPORT pInterface, uint32_t x, uint32_t y, bool fOutOfRange)
5548{
5549 RT_NOREF(pInterface, x, y, fOutOfRange);
5550}
5551
5552
5553/**
5554 * @callback_method_impl{FNTMTIMERDEV, VGA Refresh Timer}
5555 */
5556static DECLCALLBACK(void) vgaR3TimerRefresh(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
5557{
5558 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5559 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5560 RT_NOREF(pvUser);
5561
5562 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5563 VBVARaiseIrq(pDevIns, pThis, pThisCC, HGSMIHOSTFLAGS_VSYNC);
5564
5565 if (pThisCC->pDrv)
5566 pThisCC->pDrv->pfnRefresh(pThisCC->pDrv);
5567
5568 if (pThis->cMilliesRefreshInterval)
5569 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cMilliesRefreshInterval);
5570
5571# ifdef VBOX_WITH_VIDEOHWACCEL
5572 vbvaTimerCb(pDevIns, pThis, pThisCC);
5573# endif
5574
5575# ifdef VBOX_WITH_VMSVGA
5576 /*
5577 * Call the VMSVGA FIFO poller/watchdog so we can wake up the thread if
5578 * there is work to be done.
5579 */
5580 if (pThis->svga.fFIFOThreadSleeping && pThis->svga.fEnabled && pThis->svga.fConfigured)
5581 vmsvgaR3FifoWatchdogTimer(pDevIns, pThis, pThisCC);
5582# endif
5583}
5584
5585# ifdef VBOX_WITH_VMSVGA
5586
5587/**
5588 * Helper for VMSVGA.
5589 */
5590int vgaR3RegisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis, uint64_t cbFrameBuffer)
5591{
5592 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5593 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5594 RT_NOREF(cbFrameBuffer);
5595 AssertRC(rc);
5596 return rc;
5597}
5598
5599
5600/**
5601 * Helper for VMSVGA.
5602 */
5603int vgaR3UnregisterVRAMHandler(PPDMDEVINS pDevIns, PVGASTATE pThis)
5604{
5605 Assert(pThis->GCPhysVRAM != 0 && pThis->GCPhysVRAM != NIL_RTGCPHYS);
5606 int rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, false /*fEnabled*/);
5607 AssertRC(rc);
5608 return rc;
5609}
5610
5611# endif /* VBOX_WITH_VMSVGA */
5612
5613
5614/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5615
5616/**
5617 * @callback_method_impl{FNPCIIOREGIONMAP, Mapping/unmapping the VRAM MMI2 region}
5618 */
5619static DECLCALLBACK(int) vgaR3PciIORegionVRamMapUnmap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5620 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
5621{
5622 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5623 Log(("vgaR3PciIORegionVRamMapUnmap: iRegion=%d GCPhysAddress=%RGp cb=%RGp enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5624 RT_NOREF(pPciDev, cb);
5625
5626# ifdef VBOX_WITH_VMSVGA
5627 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5628 && ( enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH
5629 || (enmType == PCI_ADDRESS_SPACE_MEM && pThis->fVMSVGAEnabled && pThis->fStateLoaded)), VERR_INTERNAL_ERROR);
5630# else
5631 AssertReturn( iRegion == pThis->pciRegions.iVRAM
5632 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5633# endif
5634
5635 Assert(pPciDev == pDevIns->apPciDevs[0]);
5636
5637 /* Note! We cannot take the device lock here as that would create a lock order
5638 problem as the caller has taken the PDM lock prior to calling us. If
5639 we did, we will get trouble later when raising interrupts while owning
5640 the device lock (e.g. vmsvgaR3FifoLoop). */
5641
5642 int rc;
5643 if (GCPhysAddress != NIL_RTGCPHYS)
5644 {
5645 /*
5646 * Make sure the dirty page tracking state is up to date before mapping it.
5647 */
5648# ifdef VBOX_WITH_VMSVGA
5649 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam,
5650 !pThis->svga.fEnabled ||(pThis->svga.fEnabled && pThis->svga.fVRAMTracking));
5651# else
5652 rc = PDMDevHlpMmio2ControlDirtyPageTracking(pDevIns, pThis->hMmio2VRam, true /*fEnabled*/);
5653# endif
5654 AssertLogRelRC(rc);
5655
5656 /*
5657 * Map the VRAM.
5658 */
5659 rc = PDMDevHlpMmio2Map(pDevIns, pThis->hMmio2VRam, GCPhysAddress);
5660 AssertLogRelRC(rc);
5661 if (RT_SUCCESS(rc))
5662 {
5663 pThis->GCPhysVRAM = GCPhysAddress;
5664 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5665
5666 rc = VINF_PCI_MAPPING_DONE; /* caller doesn't care about any other status, so no problem overwriting error here */
5667 }
5668 }
5669 else
5670 {
5671 /*
5672 * Unmapping of the VRAM in progress (caller will do that).
5673 */
5674 Assert(pThis->GCPhysVRAM);
5675 pThis->GCPhysVRAM = 0;
5676 rc = VINF_SUCCESS;
5677 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5678 }
5679 return rc;
5680}
5681
5682
5683# ifdef VBOX_WITH_VMSVGA /* Currently not needed in the non-VMSVGA mode, but keeping it flexible for later. */
5684/**
5685 * @interface_method_impl{PDMPCIDEV,pfnRegionLoadChangeHookR3}
5686 */
5687static DECLCALLBACK(int) vgaR3PciRegionLoadChangeHook(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5688 uint64_t cbRegion, PCIADDRESSSPACE enmType,
5689 PFNPCIIOREGIONOLDSETTER pfnOldSetter, PFNPCIIOREGIONSWAP pfnSwapRegions)
5690{
5691 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5692
5693# ifdef VBOX_WITH_VMSVGA
5694 if (pThis->fVMSVGAEnabled)
5695 {
5696 /*
5697 * We messed up BAR order for the hybrid devices in 6.0 (see #9359).
5698 * It should have been compatible with the VBox VGA device and had the
5699 * VRAM region first and I/O second, but instead the I/O region ended
5700 * up first and VRAM second like the VMSVGA device.
5701 *
5702 * So, we have to detect that here and reconfigure the memory regions.
5703 * Region numbers are used in our (and the PCI bus') interfaction with
5704 * PGM, so PGM needs to be informed too.
5705 */
5706 if ( iRegion == 0
5707 && iRegion == pThis->pciRegions.iVRAM
5708 && (enmType & PCI_ADDRESS_SPACE_IO))
5709 {
5710 LogRel(("VGA: Detected old BAR config, making adjustments.\n"));
5711
5712 /* Update the entries. */
5713 pThis->pciRegions.iIO = 0;
5714 pThis->pciRegions.iVRAM = 1;
5715
5716 /* Update PGM on the region number change so it won't barf when restoring state. */
5717 AssertLogRelReturn(pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo, VERR_VERSION_MISMATCH);
5718 int rc = pDevIns->CTX_SUFF(pHlp)->pfnMmio2ChangeRegionNo(pDevIns, pThis->hMmio2VRam, 1);
5719 AssertLogRelRCReturn(rc, rc);
5720 /** @todo Update the I/O port too, only currently we don't give a hoot about
5721 * the region number in the I/O port registrations so it can wait...
5722 * (Only visible in the 'info ioport' output IIRC). */
5723
5724 /* Update the calling PCI device. */
5725 AssertLogRelReturn(pfnSwapRegions, VERR_INTERNAL_ERROR_2);
5726 rc = pfnSwapRegions(pPciDev, 0, 1);
5727 AssertLogRelRCReturn(rc, rc);
5728
5729 return rc;
5730 }
5731
5732 /*
5733 * The VMSVGA changed the default FIFO size from 128KB to 2MB after 5.1.
5734 */
5735 if (iRegion == pThis->pciRegions.iFIFO)
5736 {
5737 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5738 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5739 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5740
5741 /* If the size didn't change we're fine, so just return already. */
5742 if (cbRegion == pThis->svga.cbFIFO)
5743 return VINF_SUCCESS;
5744
5745 /* If the size is larger than the current configuration, refuse to load. */
5746 AssertLogRelMsgReturn(cbRegion <= pThis->svga.cbFIFOConfig,
5747 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x\n",
5748 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO),
5749 VERR_SSM_LOAD_CONFIG_MISMATCH);
5750
5751 /* Adjust the size down. */
5752 int rc = PDMDevHlpMmio2Reduce(pDevIns, pThis->hMmio2VmSvgaFifo, cbRegion);
5753 AssertLogRelMsgRCReturn(rc,
5754 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x: %Rrc\n",
5755 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO, rc),
5756 rc);
5757 pThis->svga.cbFIFO = cbRegion;
5758 return rc;
5759
5760 }
5761
5762 /*
5763 * VRAM used to be non-prefetchable till 6.1.0, so we end up here when restoring
5764 * states older than that with 6.1.0 and later. We just have to check that
5765 * the size and basic type matches, then return VINF_SUCCESS to ACK it.
5766 */
5767 if (iRegion == pThis->pciRegions.iVRAM)
5768 {
5769 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag. */
5770 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5771 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5772 /* The size must be the same. */
5773 AssertLogRelMsgReturn(cbRegion == pThis->vram_size,
5774 ("cbRegion=%#RGp vram_size=%#x\n", cbRegion, pThis->vram_size),
5775 VERR_SSM_LOAD_CONFIG_MISMATCH);
5776 return VINF_SUCCESS;
5777 }
5778
5779 /* Emulate callbacks for 5.1 and older saved states by recursion. */
5780 if (iRegion == UINT32_MAX)
5781 {
5782 int rc = vgaR3PciRegionLoadChangeHook(pDevIns, pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD,
5783 PCI_ADDRESS_SPACE_MEM, NULL, NULL);
5784 if (RT_SUCCESS(rc))
5785 rc = pfnOldSetter(pPciDev, pThis->pciRegions.iFIFO, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM);
5786 return rc;
5787 }
5788 }
5789# endif /* VBOX_WITH_VMSVGA */
5790
5791 return VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE;
5792}
5793# endif /* VBOX_WITH_VMSVGA */
5794
5795
5796/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5797
5798/**
5799 * Saves a important bits of the VGA device config.
5800 *
5801 * @param pHlp The device helpers (for SSM functions).
5802 * @param pThis The shared VGA instance data.
5803 * @param pSSM The saved state handle.
5804 */
5805static void vgaR3SaveConfig(PCPDMDEVHLPR3 pHlp, PVGASTATE pThis, PSSMHANDLE pSSM)
5806{
5807 pHlp->pfnSSMPutU32(pSSM, pThis->vram_size);
5808 pHlp->pfnSSMPutU32(pSSM, pThis->cMonitors);
5809}
5810
5811
5812/**
5813 * @callback_method_impl{FNSSMDEVLIVEEXEC}
5814 */
5815static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5816{
5817 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5818 Assert(uPass == 0); NOREF(uPass);
5819 vgaR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
5820 return VINF_SSM_DONT_CALL_AGAIN;
5821}
5822
5823
5824/**
5825 * @callback_method_impl{FNSSMDEVSAVEPREP}
5826 */
5827static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5828{
5829# ifdef VBOX_WITH_VIDEOHWACCEL
5830 RT_NOREF(pSSM);
5831 return vboxVBVASaveStatePrep(pDevIns);
5832# else
5833 RT_NOREF(pDevIns, pSSM);
5834 return VINF_SUCCESS;
5835# endif
5836}
5837
5838
5839/**
5840 * @callback_method_impl{FNSSMDEVSAVEDONE}
5841 */
5842static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5843{
5844# ifdef VBOX_WITH_VIDEOHWACCEL
5845 RT_NOREF(pSSM);
5846 return vboxVBVASaveStateDone(pDevIns);
5847# else
5848 RT_NOREF(pDevIns, pSSM);
5849 return VINF_SUCCESS;
5850# endif
5851}
5852
5853
5854/**
5855 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5856 */
5857static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5858{
5859 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5860 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5861 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5862
5863# ifdef VBOX_WITH_VDMA
5864 vboxVDMASaveStateExecPrep(pThisCC->pVdma);
5865# endif
5866
5867 vgaR3SaveConfig(pHlp, pThis, pSSM);
5868 vga_save(pHlp, pSSM, PDMDEVINS_2_DATA(pDevIns, PVGASTATE));
5869
5870 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5871# ifdef VBOX_WITH_HGSMI
5872 pHlp->pfnSSMPutBool(pSSM, true);
5873 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5874# else
5875 int rc = pHlp->pfnSSMPutBool(pSSM, false);
5876# endif
5877
5878 AssertRCReturn(rc, rc);
5879
5880 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5881# ifdef VBOX_WITH_VDMA
5882 rc = pHlp->pfnSSMPutU32(pSSM, 1);
5883 AssertRCReturn(rc, rc);
5884 rc = vboxVDMASaveStateExecPerform(pHlp, pThisCC->pVdma, pSSM);
5885# else
5886 rc = pHlp->pfnSSMPutU32(pSSM, 0);
5887# endif
5888 AssertRCReturn(rc, rc);
5889
5890# ifdef VBOX_WITH_VDMA
5891 vboxVDMASaveStateExecDone(pThisCC->pVdma);
5892# endif
5893
5894 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5895# ifdef VBOX_WITH_VMSVGA
5896 if (pThis->fVMSVGAEnabled)
5897 {
5898 rc = vmsvgaR3SaveExec(pDevIns, pSSM);
5899 AssertRCReturn(rc, rc);
5900 }
5901# endif
5902 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5903
5904 return rc;
5905}
5906
5907
5908/**
5909 * @callback_method_impl{FNSSMDEVLOADPREP}
5910 */
5911static DECLCALLBACK(int) vgaR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5912{
5913 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5914 RT_NOREF(pSSM);
5915 pThis->fStateLoaded = true;
5916 return VINF_SUCCESS;
5917}
5918
5919
5920/**
5921 * @callback_method_impl{FNSSMDEVLOADEXEC}
5922 */
5923static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5924{
5925 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
5926 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
5927 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
5928 int rc;
5929
5930 pThis->fStateLoaded = true;
5931
5932 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5933 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5934
5935 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5936 {
5937 /* Check the config */
5938 uint32_t cbVRam;
5939 rc = pHlp->pfnSSMGetU32(pSSM, &cbVRam);
5940 AssertRCReturn(rc, rc);
5941 if (pThis->vram_size != cbVRam)
5942 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5943
5944 uint32_t cMonitors;
5945 rc = pHlp->pfnSSMGetU32(pSSM, &cMonitors);
5946 AssertRCReturn(rc, rc);
5947 if (pThis->cMonitors != cMonitors)
5948 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5949 }
5950
5951 if (uPass == SSM_PASS_FINAL)
5952 {
5953 rc = vga_load(pHlp, pSSM, pThis, uVersion);
5954 if (RT_FAILURE(rc))
5955 return rc;
5956
5957 /*
5958 * Restore the HGSMI state, if present.
5959 */
5960 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5961 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5962 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5963 {
5964 rc = pHlp->pfnSSMGetBool(pSSM, &fWithHgsmi);
5965 AssertRCReturn(rc, rc);
5966 }
5967 if (fWithHgsmi)
5968 {
5969# ifdef VBOX_WITH_HGSMI
5970 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5971 AssertRCReturn(rc, rc);
5972# else
5973 return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5974# endif
5975 }
5976
5977 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5978 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5979 {
5980 uint32_t u32;
5981 rc = pHlp->pfnSSMGetU32(pSSM, &u32);
5982 if (u32)
5983 {
5984# ifdef VBOX_WITH_VDMA
5985 if (u32 == 1)
5986 {
5987 rc = vboxVDMASaveLoadExecPerform(pHlp, pThisCC->pVdma, pSSM, uVersion);
5988 AssertRCReturn(rc, rc);
5989 }
5990 else
5991# endif
5992 {
5993 LogRel(("invalid CmdVbva version info\n"));
5994 return VERR_VERSION_MISMATCH;
5995 }
5996 }
5997 }
5998
5999 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
6000# ifdef VBOX_WITH_VMSVGA
6001 if (pThis->fVMSVGAEnabled)
6002 {
6003 rc = vmsvgaR3LoadExec(pDevIns, pSSM, uVersion, uPass);
6004 AssertRCReturn(rc, rc);
6005 }
6006# endif
6007 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
6008 }
6009 return VINF_SUCCESS;
6010}
6011
6012
6013/**
6014 * @@callback_method_impl{FNSSMDEVLOADDONE}
6015 */
6016static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
6017{
6018 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6019 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6020 int rc;
6021 RT_NOREF(pThisCC, pThis, pSSM);
6022
6023# ifdef VBOX_WITH_HGSMI
6024 rc = vboxVBVALoadStateDone(pDevIns);
6025 AssertRCReturn(rc, rc);
6026# ifdef VBOX_WITH_VDMA
6027 rc = vboxVDMASaveLoadDone(pThisCC->pVdma);
6028 AssertRCReturn(rc, rc);
6029# endif
6030 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
6031 VBVAOnVBEChanged(pThis, pThisCC);
6032# endif
6033# ifdef VBOX_WITH_VMSVGA
6034 if (pThis->fVMSVGAEnabled)
6035 {
6036 rc = vmsvgaR3LoadDone(pDevIns);
6037 AssertRCReturn(rc, rc);
6038 }
6039# endif
6040 return VINF_SUCCESS;
6041}
6042
6043
6044/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
6045
6046/**
6047 * @interface_method_impl{PDMDEVREG,pfnResume}
6048 */
6049static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
6050{
6051 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6052 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6053 VBVAOnResume(pDevIns, pThis, pThisCC);
6054}
6055
6056
6057/**
6058 * @interface_method_impl{PDMDEVREG,pfnReset}
6059 */
6060static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
6061{
6062 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6063 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6064 char *pchStart;
6065 char *pchEnd;
6066 LogFlow(("vgaReset\n"));
6067
6068 if (pThisCC->pVdma)
6069 vboxVDMAReset(pThisCC->pVdma);
6070
6071# ifdef VBOX_WITH_VMSVGA
6072 if (pThis->fVMSVGAEnabled)
6073 vmsvgaR3Reset(pDevIns);
6074# endif
6075
6076# ifdef VBOX_WITH_HGSMI
6077 VBVAReset(pDevIns, pThis, pThisCC);
6078# endif
6079
6080
6081 /* Clear the VRAM ourselves. */
6082 if (pThisCC->pbVRam && pThis->vram_size)
6083 memset(pThisCC->pbVRam, 0, pThis->vram_size);
6084
6085 /*
6086 * Zero most of it.
6087 *
6088 * Unlike vga_reset we're leaving out a few members which we believe
6089 * must remain unchanged....
6090 */
6091 /* 1st part. */
6092 pchStart = (char *)&pThis->latch;
6093 pchEnd = (char *)&pThis->invalidated_y_table;
6094 memset(pchStart, 0, pchEnd - pchStart);
6095
6096 /* 2nd part. */
6097 pchStart = (char *)&pThis->last_palette;
6098 pchEnd = (char *)&pThis->u32Marker;
6099 memset(pchStart, 0, pchEnd - pchStart);
6100
6101
6102 /*
6103 * Restore and re-init some bits.
6104 */
6105 pThisCC->get_bpp = vgaR3GetBpp;
6106 pThisCC->get_offsets = vgaR3GetOffsets;
6107 pThisCC->get_resolution = vgaR3GetResolution;
6108 pThis->graphic_mode = -1; /* Force full update. */
6109# ifdef CONFIG_BOCHS_VBE
6110 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
6111 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
6112 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
6113 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
6114# endif /* CONFIG_BOCHS_VBE */
6115 pThis->st00 = 0x70; /* Static except for bit 4. */
6116
6117 /*
6118 * Reset the LFB mapping.
6119 */
6120 if ( ( pDevIns->fRCEnabled
6121 || pDevIns->fR0Enabled)
6122 && pThis->GCPhysVRAM != 0
6123 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
6124 {
6125 /** @todo r=bird: This used to be a PDMDevHlpPGMHandlerPhysicalReset call.
6126 * Not quite sure if it was/is needed. Besides, where do we reset the
6127 * dirty bitmap (bmDirtyBitmap)? */
6128 int rc = PDMDevHlpMmio2ResetDirtyBitmap(pDevIns, pThis->hMmio2VRam);
6129 AssertRC(rc);
6130 }
6131 if (pThis->bmPageRemappedVGA != 0)
6132 {
6133 PDMDevHlpMmioResetRegion(pDevIns, pThis->hMmioLegacy);
6134 STAM_COUNTER_INC(&pThis->StatMapReset);
6135 vgaResetRemapped(pThis);
6136 }
6137
6138 /*
6139 * Reset the logo data.
6140 */
6141 pThisCC->LogoCommand = LOGO_CMD_NOP;
6142 pThisCC->offLogoData = 0;
6143
6144 /* notify port handler */
6145 if (pThisCC->pDrv)
6146 {
6147 PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* hack around lock order issue. */
6148
6149 pThisCC->pDrv->pfnReset(pThisCC->pDrv);
6150 pThisCC->pDrv->pfnVBVAMousePointerShape(pThisCC->pDrv, false, false, 0, 0, 0, 0, NULL);
6151
6152 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
6153 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
6154 }
6155
6156 /* Reset latched access mask. */
6157 pThis->uMaskLatchAccess = 0x3ff;
6158 pThis->cLatchAccesses = 0;
6159 pThis->u64LastLatchedAccess = 0;
6160 pThis->iMask = 0;
6161
6162 /* Reset retrace emulation. */
6163 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
6164}
6165
6166
6167/**
6168 * @interface_method_impl{PDMDEVREG,pfnPowerOn}
6169 */
6170static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
6171{
6172 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6173 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6174# ifdef VBOX_WITH_VMSVGA
6175 if (pThis->fVMSVGAEnabled)
6176 vmsvgaR3PowerOn(pDevIns);
6177# endif
6178 VBVAOnResume(pDevIns, pThis, pThisCC);
6179}
6180
6181
6182/**
6183 * @interface_method_impl{PDMDEVREG,pfnPowerOff}
6184 */
6185static DECLCALLBACK(void) vgaR3PowerOff(PPDMDEVINS pDevIns)
6186{
6187 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6188 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6189 RT_NOREF(pThis, pThisCC);
6190# ifdef VBOX_WITH_VMSVGA
6191 if (pThis->fVMSVGAEnabled)
6192 vmsvgaR3PowerOff(pDevIns);
6193# endif
6194}
6195
6196
6197/**
6198 * @interface_method_impl{PDMDEVREG,pfnRelocate}
6199 */
6200static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
6201{
6202# ifdef VBOX_WITH_RAW_MODE_KEEP
6203 if (offDelta)
6204 {
6205 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6206 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
6207
6208 pThisRC->pbVRam += offDelta;
6209 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6210 }
6211# else
6212 RT_NOREF(pDevIns, offDelta);
6213# endif
6214}
6215
6216
6217/**
6218 * @interface_method_impl{PDMDEVREG,pfnAttach}
6219 *
6220 * This is like plugging in the monitor after turning on the PC.
6221 */
6222static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6223{
6224 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6225 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6226
6227 RT_NOREF(pThis);
6228
6229 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
6230 ("VGA device does not support hotplugging\n"),
6231 VERR_INVALID_PARAMETER);
6232
6233 switch (iLUN)
6234 {
6235 /* LUN #0: Display port. */
6236 case 0:
6237 {
6238 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->IBase, &pThisCC->pDrvBase, "Display Port");
6239 if (RT_SUCCESS(rc))
6240 {
6241 pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIDISPLAYCONNECTOR);
6242 if (pThisCC->pDrv)
6243 {
6244 /* pThisCC->pDrv->pbData can be NULL when there is no framebuffer. */
6245 if ( pThisCC->pDrv->pfnRefresh
6246 && pThisCC->pDrv->pfnResize
6247 && pThisCC->pDrv->pfnUpdateRect)
6248 rc = VINF_SUCCESS;
6249 else
6250 {
6251 Assert(pThisCC->pDrv->pfnRefresh);
6252 Assert(pThisCC->pDrv->pfnResize);
6253 Assert(pThisCC->pDrv->pfnUpdateRect);
6254 pThisCC->pDrv = NULL;
6255 pThisCC->pDrvBase = NULL;
6256 rc = VERR_INTERNAL_ERROR;
6257 }
6258# ifdef VBOX_WITH_VIDEOHWACCEL
6259 if(rc == VINF_SUCCESS)
6260 {
6261 rc = vbvaVHWAConstruct(pDevIns, pThis, pThisCC);
6262 if (rc != VERR_NOT_IMPLEMENTED)
6263 AssertRC(rc);
6264 }
6265# endif
6266 }
6267 else
6268 {
6269 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
6270 pThisCC->pDrvBase = NULL;
6271 rc = VERR_PDM_MISSING_INTERFACE;
6272 }
6273 }
6274 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6275 {
6276 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6277 rc = VINF_SUCCESS;
6278 }
6279 else
6280 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
6281 return rc;
6282 }
6283
6284 default:
6285 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6286 return VERR_PDM_NO_SUCH_LUN;
6287 }
6288}
6289
6290
6291/**
6292 * @interface_method_impl{PDMDEVREG,pfnDetach}
6293 *
6294 * This is like unplugging the monitor while the PC is still running.
6295 */
6296static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
6297{
6298 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6299 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
6300 RT_NOREF(fFlags);
6301
6302 /*
6303 * Reset the interfaces and update the controller state.
6304 */
6305 switch (iLUN)
6306 {
6307 /* LUN #0: Display port. */
6308 case 0:
6309 pThisCC->pDrv = NULL;
6310 pThisCC->pDrvBase = NULL;
6311 break;
6312
6313 default:
6314 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
6315 break;
6316 }
6317}
6318
6319
6320/**
6321 * @interface_method_impl{PDMDEVREG,pfnDestruct}
6322 */
6323static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
6324{
6325 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
6326 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6327 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6328 LogFlow(("vgaR3Destruct:\n"));
6329
6330# ifdef VBOX_WITH_VDMA
6331 if (pThisCC->pVdma)
6332 vboxVDMADestruct(pThisCC->pVdma);
6333# endif
6334
6335# ifdef VBOX_WITH_VMSVGA
6336 if (pThis->fVMSVGAEnabled)
6337 vmsvgaR3Destruct(pDevIns);
6338# endif
6339
6340# ifdef VBOX_WITH_HGSMI
6341 VBVADestroy(pThisCC);
6342# endif
6343
6344 /*
6345 * Free MM heap pointers.
6346 */
6347 if (pThisCC->pbVBEExtraData)
6348 {
6349 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVBEExtraData);
6350 pThisCC->pbVBEExtraData = NULL;
6351 }
6352 if (pThisCC->pbVgaBios)
6353 {
6354 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6355 pThisCC->pbVgaBios = NULL;
6356 }
6357
6358 if (pThisCC->pszVgaBiosFile)
6359 {
6360 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6361 pThisCC->pszVgaBiosFile = NULL;
6362 }
6363
6364 if (pThisCC->pszLogoFile)
6365 {
6366 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
6367 pThisCC->pszLogoFile = NULL;
6368 }
6369
6370 if (pThisCC->pbLogo)
6371 {
6372 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbLogo);
6373 pThisCC->pbLogo = NULL;
6374 }
6375
6376# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
6377 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSectIRQ);
6378# endif
6379 PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
6380 return VINF_SUCCESS;
6381}
6382
6383
6384/**
6385 * Adjust VBE mode information
6386 *
6387 * Depending on the configured VRAM size, certain parts of VBE mode
6388 * information must be updated.
6389 *
6390 * @param pThis The device instance data.
6391 * @param pMode The mode information structure.
6392 */
6393static void vgaR3AdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6394{
6395 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6396 unsigned bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6397 /* The "number of image pages" is really the max page index... */
6398 unsigned maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6399 if (maxPage > 255)
6400 maxPage = 255; /* 8-bit value. */
6401 pMode->info.NumberOfImagePages = maxPage;
6402 pMode->info.LinNumberOfPages = maxPage;
6403}
6404
6405
6406/**
6407 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6408 */
6409static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6410{
6411 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6412 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
6413 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
6414 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
6415 int rc;
6416 unsigned i;
6417 uint32_t cCustomModes;
6418 uint32_t cyReduction;
6419 uint32_t cbPitch;
6420 PVBEHEADER pVBEDataHdr;
6421 ModeInfoListItem *pCurMode;
6422 unsigned cb;
6423
6424 Assert(iInstance == 0);
6425
6426 /*
6427 * Init static data.
6428 */
6429 static bool s_fExpandDone = false;
6430 if (!s_fExpandDone)
6431 {
6432 s_fExpandDone = true;
6433 vgaR3InitExpand();
6434 }
6435
6436 /*
6437 * Validate configuration.
6438 */
6439 static const char s_szMscWorkaround[] = "VRamSize"
6440 "|MonitorCount"
6441 "|FadeIn"
6442 "|FadeOut"
6443 "|LogoTime"
6444 "|LogoFile"
6445 "|ShowBootMenu"
6446 "|BiosRom"
6447 "|RealRetrace"
6448 "|CustomVideoModes"
6449 "|HeightReduction"
6450 "|CustomVideoMode1"
6451 "|CustomVideoMode2"
6452 "|CustomVideoMode3"
6453 "|CustomVideoMode4"
6454 "|CustomVideoMode5"
6455 "|CustomVideoMode6"
6456 "|CustomVideoMode7"
6457 "|CustomVideoMode8"
6458 "|CustomVideoMode9"
6459 "|CustomVideoMode10"
6460 "|CustomVideoMode11"
6461 "|CustomVideoMode12"
6462 "|CustomVideoMode13"
6463 "|CustomVideoMode14"
6464 "|CustomVideoMode15"
6465 "|CustomVideoMode16"
6466 "|MaxBiosXRes"
6467 "|MaxBiosYRes"
6468# ifdef VBOX_WITH_VMSVGA
6469 "|VMSVGAEnabled"
6470 "|VMSVGA10"
6471 "|VMSVGAPciId"
6472 "|VMSVGAPciBarLayout"
6473 "|VMSVGAFifoSize"
6474# endif
6475# ifdef VBOX_WITH_VMSVGA3D
6476 "|VMSVGA3dEnabled"
6477 "|VMSVGA3dOverlayEnabled"
6478# endif
6479 "|SuppressNewYearSplash"
6480 "|3DEnabled";
6481
6482 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, s_szMscWorkaround, "");
6483
6484 /*
6485 * Init state data.
6486 */
6487 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6488 AssertLogRelRCReturn(rc, rc);
6489 if (pThis->vram_size > VGA_VRAM_MAX)
6490 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6491 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6492 if (pThis->vram_size < VGA_VRAM_MIN)
6493 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6494 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6495 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6496 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6497 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6498
6499 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6500 AssertLogRelRCReturn(rc, rc);
6501
6502 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
6503
6504 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "3DEnabled", &pThis->f3DEnabled, false);
6505 AssertLogRelRCReturn(rc, rc);
6506 Log(("VGA: f3DEnabled=%RTbool\n", pThis->f3DEnabled));
6507
6508# ifdef VBOX_WITH_VMSVGA
6509 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6510 AssertLogRelRCReturn(rc, rc);
6511 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6512
6513 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA10", &pThis->fVMSVGA10, true);
6514 AssertLogRelRCReturn(rc, rc);
6515 Log(("VMSVGA: VMSVGA10 = %d\n", pThis->fVMSVGA10));
6516
6517 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciId", &pThis->fVMSVGAPciId, false);
6518 AssertLogRelRCReturn(rc, rc);
6519 Log(("VMSVGA: VMSVGAPciId = %d\n", pThis->fVMSVGAPciId));
6520
6521 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGAPciBarLayout", &pThis->fVMSVGAPciBarLayout, pThis->fVMSVGAPciId);
6522 AssertLogRelRCReturn(rc, rc);
6523 Log(("VMSVGA: VMSVGAPciBarLayout = %d\n", pThis->fVMSVGAPciBarLayout));
6524
6525 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6526 AssertLogRelRCReturn(rc, rc);
6527 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6528 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6529 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6530 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6531 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6532# endif
6533# ifdef VBOX_WITH_VMSVGA3D
6534 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6535 AssertLogRelRCReturn(rc, rc);
6536 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6537
6538 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "VMSVGA3dOverlayEnabled", &pThis->svga.f3DOverlayEnabled, false);
6539 AssertLogRelRCReturn(rc, rc);
6540 Log(("VMSVGA: VMSVGA3dOverlayEnabled = %d\n", pThis->svga.f3DOverlayEnabled));
6541# endif
6542
6543# ifdef VBOX_WITH_VMSVGA
6544 if (pThis->fVMSVGAPciBarLayout)
6545 {
6546 pThis->pciRegions.iIO = 0;
6547 pThis->pciRegions.iVRAM = 1;
6548 }
6549 else
6550 {
6551 pThis->pciRegions.iVRAM = 0;
6552 pThis->pciRegions.iIO = 1;
6553 }
6554 pThis->pciRegions.iFIFO = 2;
6555# else
6556 pThis->pciRegions.iVRAM = 0;
6557# endif
6558
6559 pThisCC->pDevIns = pDevIns;
6560
6561 vgaR3Reset(pDevIns);
6562
6563 /* The PCI devices configuration. */
6564 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
6565 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
6566
6567# ifdef VBOX_WITH_VMSVGA
6568 if (pThis->fVMSVGAEnabled)
6569 {
6570 /* Extend our VGA device with VMWare SVGA functionality. */
6571 if (pThis->fVMSVGAPciId)
6572 {
6573 PDMPciDevSetVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6574 PDMPciDevSetDeviceId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6575 }
6576 else
6577 {
6578 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6579 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6580 }
6581 PDMPciDevSetSubSystemVendorId(pPciDev, PCI_VENDOR_ID_VMWARE);
6582 PDMPciDevSetSubSystemId(pPciDev, PCI_DEVICE_ID_VMWARE_SVGA2);
6583 }
6584 else
6585# endif /* VBOX_WITH_VMSVGA */
6586 {
6587 PDMPciDevSetVendorId(pPciDev, 0x80ee); /* PCI vendor, just a free bogus value */
6588 PDMPciDevSetDeviceId(pPciDev, 0xbeef);
6589 }
6590 PDMPciDevSetClassSub(pPciDev, 0x00); /* VGA controller */
6591 PDMPciDevSetClassBase(pPciDev, 0x03);
6592 PDMPciDevSetHeaderType(pPciDev, 0x00);
6593# if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6594 PDMPciDevSetInterruptPin(pPciDev, 1);
6595# endif
6596
6597 /* the interfaces. */
6598 pThisCC->IBase.pfnQueryInterface = vgaR3PortQueryInterface;
6599
6600 pThisCC->IPort.pfnUpdateDisplay = vgaR3PortUpdateDisplay;
6601 pThisCC->IPort.pfnUpdateDisplayAll = vgaR3PortUpdateDisplayAll;
6602 pThisCC->IPort.pfnQueryVideoMode = vgaR3PortQueryVideoMode;
6603 pThisCC->IPort.pfnSetRefreshRate = vgaR3PortSetRefreshRate;
6604 pThisCC->IPort.pfnTakeScreenshot = vgaR3PortTakeScreenshot;
6605 pThisCC->IPort.pfnFreeScreenshot = vgaR3PortFreeScreenshot;
6606 pThisCC->IPort.pfnDisplayBlt = vgaR3PortDisplayBlt;
6607 pThisCC->IPort.pfnUpdateDisplayRect = vgaR3PortUpdateDisplayRect;
6608 pThisCC->IPort.pfnCopyRect = vgaR3PortCopyRect;
6609 pThisCC->IPort.pfnSetRenderVRAM = vgaR3PortSetRenderVRAM;
6610 pThisCC->IPort.pfnSetViewport = NULL;
6611 pThisCC->IPort.pfnReportMonitorPositions = NULL;
6612# ifdef VBOX_WITH_VMSVGA
6613 if (pThis->fVMSVGAEnabled)
6614 {
6615 pThisCC->IPort.pfnSetViewport = vmsvgaR3PortSetViewport;
6616 pThisCC->IPort.pfnReportMonitorPositions = vmsvgaR3PortReportMonitorPositions;
6617 }
6618# endif
6619 pThisCC->IPort.pfnSendModeHint = vbvaR3PortSendModeHint;
6620 pThisCC->IPort.pfnReportHostCursorCapabilities = vgaR3PortReportHostCursorCapabilities;
6621 pThisCC->IPort.pfnReportHostCursorPosition = vgaR3PortReportHostCursorPosition;
6622
6623# if defined(VBOX_WITH_HGSMI) && defined(VBOX_WITH_VIDEOHWACCEL)
6624 pThisCC->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaR3VHWACommandCompleteAsync;
6625# endif
6626
6627 pThisCC->ILeds.pfnQueryStatusLed = vgaR3PortQueryStatusLed;
6628 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6629
6630 /*
6631 * We use our own critical section to avoid unncessary pointer indirections
6632 * in interface methods (as well as for historical reasons).
6633 */
6634 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6635 AssertRCReturn(rc, rc);
6636 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6637 AssertRCReturn(rc, rc);
6638
6639# ifdef VBOX_WITH_HGSMI
6640 /*
6641 * This critical section is used by vgaR3IOPortHgsmiWrite, VBVARaiseIrq and VBVAOnResume
6642 * for some IRQ related synchronization.
6643 */
6644 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6645 AssertRCReturn(rc, rc);
6646# endif
6647
6648 /*
6649 * PCI device registration.
6650 */
6651 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
6652 if (RT_FAILURE(rc))
6653 return rc;
6654 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6655 if (pPciDev->uDevFn != 16 && iInstance == 0)
6656 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pPciDev->uDevFn));
6657
6658# ifdef VBOX_WITH_VMSVGA
6659 pThis->hIoPortVmSvga = NIL_IOMIOPORTHANDLE;
6660 pThis->hMmio2VmSvgaFifo = NIL_PGMMMIO2HANDLE;
6661 if (pThis->fVMSVGAEnabled)
6662 {
6663 /* Register the io command ports. */
6664 rc = PDMDevHlpPCIIORegionCreateIo(pDevIns, pThis->pciRegions.iIO, 0x10, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/,
6665 "VMSVGA", NULL /*paExtDescs*/, &pThis->hIoPortVmSvga);
6666 AssertRCReturn(rc, rc);
6667
6668 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iFIFO, pThis->svga.cbFIFO,
6669 PCI_ADDRESS_SPACE_MEM, 0 /*fFlags*/, vmsvgaR3PciIORegionFifoMapUnmap,
6670 "VMSVGA-FIFO", (void **)&pThisCC->svga.pau32FIFO, &pThis->hMmio2VmSvgaFifo);
6671 AssertRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6672 N_("Failed to create VMSVGA FIFO (%u bytes)"), pThis->svga.cbFIFO));
6673
6674 pPciDev->pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6675 }
6676# endif /* VBOX_WITH_VMSVGA */
6677
6678 /*
6679 * Allocate VRAM and create a PCI region for it.
6680 */
6681 rc = PDMDevHlpPCIIORegionCreateMmio2Ex(pDevIns, pThis->pciRegions.iVRAM, pThis->vram_size,
6682 PCI_ADDRESS_SPACE_MEM_PREFETCH, PGMPHYS_MMIO2_FLAGS_TRACK_DIRTY_PAGES,
6683 vgaR3PciIORegionVRamMapUnmap, "VRam", (void **)&pThisCC->pbVRam, &pThis->hMmio2VRam);
6684 AssertLogRelRCReturn(rc, PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6685 N_("Failed to allocate %u bytes of VRAM"), pThis->vram_size));
6686
6687 /*
6688 * Register I/O ports.
6689 */
6690# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_phIoPort) do { \
6691 rc = PDMDevHlpIoPortCreateFlagsAndMap(pDevIns, a_uPort, a_cPorts, IOM_IOPORT_F_ABS, \
6692 a_pfnWrite, a_pfnRead, "VGA - " a_szDesc, NULL /*paExtDescs*/, a_phIoPort); \
6693 AssertRCReturn(rc, rc); \
6694 } while (0)
6695 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", &pThis->hIoPortAr);
6696 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", &pThis->hIoPortMsrSt00);
6697 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", &pThis->hIoPort3c3);
6698 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", &pThis->hIoPortSr);
6699 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", &pThis->hIoPortDac);
6700 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ &pThis->hIoPortPos);
6701 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", &pThis->hIoPortGr);
6702
6703 /* Note! Ralf Brown lists 0x3b0-0x3b1, 0x3b2-0x3b3 and 0x3b6-0x3b7 as "the same as" 0x3b4-0x3b5. */
6704 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", &pThis->hIoPortMdaCrt);
6705 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", &pThis->hIoPortMdaFcrSt);
6706 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", &pThis->hIoPortCgaCrt);
6707 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", &pThis->hIoPortCgaFcrSt);
6708
6709# ifdef CONFIG_BOCHS_VBE
6710 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", &pThis->hIoPortVbeIndex);
6711 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", &pThis->hIoPortVbeData);
6712# endif /* CONFIG_BOCHS_VBE */
6713
6714# ifdef VBOX_WITH_HGSMI
6715 /* Use reserved VGA IO ports for HGSMI. */
6716 REG_PORT(VGA_PORT_HGSMI_HOST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI host (3b0-3b3)", &pThis->hIoPortHgsmiHost);
6717 REG_PORT(VGA_PORT_HGSMI_GUEST, 4, vgaR3IOPortHgsmiWrite, vgaR3IOPortHgmsiRead, "HGSMI guest (3d0-3d3)", &pThis->hIoPortHgsmiGuest);
6718# endif /* VBOX_WITH_HGSMI */
6719
6720# undef REG_PORT
6721
6722 /* vga bios */
6723 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_PRINTF_PORT, 1 /*cPorts*/, vgaIoPortWriteBios, vgaIoPortReadBios,
6724 "VGA BIOS debug/panic", NULL /*paExtDescs*/, &pThis->hIoPortBios);
6725 AssertRCReturn(rc, rc);
6726
6727 /*
6728 * The MDA/CGA/EGA/VGA/whatever fixed MMIO area.
6729 */
6730 rc = PDMDevHlpMmioCreateExAndMap(pDevIns, 0x000a0000, 0x00020000,
6731 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
6732 NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
6733 vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/,
6734 "VGA - VGA Video Buffer", &pThis->hMmioLegacy);
6735 AssertRCReturn(rc, rc);
6736
6737 /*
6738 * Get the VGA BIOS ROM file name.
6739 */
6740 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThisCC->pszVgaBiosFile);
6741 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6742 {
6743 pThisCC->pszVgaBiosFile = NULL;
6744 rc = VINF_SUCCESS;
6745 }
6746 else if (RT_FAILURE(rc))
6747 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6748 else if (!*pThisCC->pszVgaBiosFile)
6749 {
6750 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6751 pThisCC->pszVgaBiosFile = NULL;
6752 }
6753
6754 /*
6755 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6756 */
6757 RTFILE FileVgaBios = NIL_RTFILE;
6758 if (pThisCC->pszVgaBiosFile)
6759 {
6760 rc = RTFileOpen(&FileVgaBios, pThisCC->pszVgaBiosFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6761 if (RT_SUCCESS(rc))
6762 {
6763 rc = RTFileQuerySize(FileVgaBios, &pThisCC->cbVgaBios);
6764 if (RT_SUCCESS(rc))
6765 {
6766 if ( RT_ALIGN(pThisCC->cbVgaBios, _4K) != pThisCC->cbVgaBios
6767 || pThisCC->cbVgaBios > _64K
6768 || pThisCC->cbVgaBios < 16 * _1K)
6769 rc = VERR_TOO_MUCH_DATA;
6770 }
6771 }
6772 if (RT_FAILURE(rc))
6773 {
6774 /*
6775 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6776 */
6777 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThisCC->pszVgaBiosFile, rc));
6778 RTFileClose(FileVgaBios);
6779 FileVgaBios = NIL_RTFILE;
6780 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszVgaBiosFile);
6781 pThisCC->pszVgaBiosFile = NULL;
6782 }
6783 }
6784
6785 /*
6786 * Attempt to get the VGA BIOS ROM data from file.
6787 */
6788 if (pThisCC->pszVgaBiosFile)
6789 {
6790 /*
6791 * Allocate buffer for the VGA BIOS ROM data.
6792 */
6793 pThisCC->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbVgaBios);
6794 if (pThisCC->pbVgaBios)
6795 {
6796 rc = RTFileRead(FileVgaBios, pThisCC->pbVgaBios, pThisCC->cbVgaBios, NULL);
6797 if (RT_FAILURE(rc))
6798 {
6799 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThisCC->cbVgaBios, rc));
6800 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pbVgaBios);
6801 pThisCC->pbVgaBios = NULL;
6802 }
6803 rc = VINF_SUCCESS;
6804 }
6805 else
6806 rc = VERR_NO_MEMORY;
6807 }
6808 else
6809 pThisCC->pbVgaBios = NULL;
6810
6811 /* cleanup */
6812 if (FileVgaBios != NIL_RTFILE)
6813 RTFileClose(FileVgaBios);
6814
6815 /* If we were unable to get the data from file for whatever reason, fall
6816 back to the built-in ROM image. */
6817 const uint8_t *pbVgaBiosBinary;
6818 uint64_t cbVgaBiosBinary;
6819 uint32_t fFlags = 0;
6820 if (pThisCC->pbVgaBios == NULL)
6821 {
6822 CPUMMICROARCH enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
6823 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6824 || enmMicroarch == kCpumMicroarch_Intel_80186
6825 || enmMicroarch == kCpumMicroarch_NEC_V20
6826 || enmMicroarch == kCpumMicroarch_NEC_V30)
6827 {
6828 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6829 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6830 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6831 }
6832 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6833 {
6834 pbVgaBiosBinary = g_abVgaBiosBinary286;
6835 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6836 LogRel(("VGA: Using the 286 BIOS image!\n"));
6837 }
6838 else
6839 {
6840 pbVgaBiosBinary = g_abVgaBiosBinary386;
6841 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6842 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6843 }
6844 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6845 }
6846 else
6847 {
6848 pbVgaBiosBinary = pThisCC->pbVgaBios;
6849 cbVgaBiosBinary = pThisCC->cbVgaBios;
6850 }
6851
6852 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6853 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, GUEST_PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6854 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6855 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6856 fFlags, "VGA BIOS");
6857 AssertRCReturn(rc, rc);
6858
6859 /*
6860 * Saved state.
6861 */
6862 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6863 NULL, vgaR3LiveExec, NULL,
6864 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6865 vgaR3LoadPrep, vgaR3LoadExec, vgaR3LoadDone);
6866 AssertRCReturn(rc, rc);
6867
6868 /*
6869 * Create the refresh timer.
6870 */
6871 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_REAL, vgaR3TimerRefresh, NULL,
6872 TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "VGA Refresh", &pThis->hRefreshTimer);
6873 AssertRCReturn(rc, rc);
6874
6875 /*
6876 * Attach to the display.
6877 */
6878 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6879 AssertRCReturn(rc, rc);
6880
6881 /*
6882 * Initialize the retrace flag.
6883 */
6884 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6885 AssertLogRelRCReturn(rc, rc);
6886
6887 uint16_t maxBiosXRes;
6888 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6889 AssertLogRelRCReturn(rc, rc);
6890 uint16_t maxBiosYRes;
6891 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6892 AssertLogRelRCReturn(rc, rc);
6893
6894 /*
6895 * Compute buffer size for the VBE BIOS Extra Data.
6896 */
6897 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6898
6899 rc = pHlp->pfnCFGMQueryU32(pCfg, "HeightReduction", &cyReduction);
6900 if (RT_SUCCESS(rc) && cyReduction)
6901 cb *= 2; /* Default mode list will be twice long */
6902 else
6903 cyReduction = 0;
6904
6905 rc = pHlp->pfnCFGMQueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6906 if (RT_SUCCESS(rc) && cCustomModes)
6907 cb += sizeof(ModeInfoListItem) * cCustomModes;
6908 else
6909 cCustomModes = 0;
6910
6911 /*
6912 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6913 */
6914 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6915 pThisCC->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6916 pThisCC->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThisCC->cbVBEExtraData);
6917 if (!pThisCC->pbVBEExtraData)
6918 return VERR_NO_MEMORY;
6919
6920 pVBEDataHdr = (PVBEHEADER)pThisCC->pbVBEExtraData;
6921 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6922 pVBEDataHdr->cbData = cb;
6923
6924 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6925 for (i = 0; i < MODE_INFO_SIZE; i++)
6926 {
6927 uint32_t pixelWidth, reqSize;
6928 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6929 pixelWidth = 2;
6930 else
6931 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6932 reqSize = mode_info_list[i].info.XResolution
6933 * mode_info_list[i].info.YResolution
6934 * pixelWidth;
6935 if (reqSize >= pThis->vram_size)
6936 continue;
6937 if (!reqSize)
6938 continue;
6939 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6940 || mode_info_list[i].info.YResolution > maxBiosYRes)
6941 continue;
6942 *pCurMode = mode_info_list[i];
6943 vgaR3AdjustModeInfo(pThis, pCurMode);
6944 pCurMode++;
6945 }
6946
6947 /*
6948 * Copy default modes with subtracted YResolution.
6949 */
6950 if (cyReduction)
6951 {
6952 ModeInfoListItem *pDefMode = mode_info_list;
6953 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6954 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6955 {
6956 uint32_t pixelWidth, reqSize;
6957 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6958 pixelWidth = 2;
6959 else
6960 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6961 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6962 if (reqSize >= pThis->vram_size)
6963 continue;
6964 if ( pDefMode->info.XResolution > maxBiosXRes
6965 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6966 continue;
6967 *pCurMode = *pDefMode;
6968 pCurMode->mode += 0x30;
6969 pCurMode->info.YResolution -= cyReduction;
6970 pCurMode++;
6971 }
6972 }
6973
6974
6975 /*
6976 * Add custom modes.
6977 */
6978 if (cCustomModes)
6979 {
6980 uint16_t u16CurMode = VBE_VBOX_MODE_CUSTOM1;
6981 for (i = 1; i <= cCustomModes; i++)
6982 {
6983 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6984 char *pszExtraData = NULL;
6985
6986 /* query and decode the custom mode string. */
6987 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6988 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6989 if (RT_SUCCESS(rc))
6990 {
6991 ModeInfoListItem *pDefMode = mode_info_list;
6992 unsigned int cx, cy, cBits, cParams, j;
6993 uint16_t u16DefMode;
6994
6995 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6996 if ( cParams != 3
6997 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6998 {
6999 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
7000 return VERR_VGA_INVALID_CUSTOM_MODE;
7001 }
7002 if (!cx || !cy)
7003 {
7004 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cx=%u, cy=%u\n", pszExtraData, szExtraDataKey, cx, cy));
7005 return VERR_VGA_INVALID_CUSTOM_MODE;
7006 }
7007 cbPitch = calc_line_pitch(cBits, cx);
7008 if (cy * cbPitch >= pThis->vram_size)
7009 {
7010 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",
7011 cx, cy, cBits, pThis->vram_size / _1M));
7012 return VERR_VGA_INVALID_CUSTOM_MODE;
7013 }
7014 PDMDevHlpMMHeapFree(pDevIns, pszExtraData);
7015
7016 /* Use defaults from max@bpp mode. */
7017 switch (cBits)
7018 {
7019 case 8:
7020 u16DefMode = VBE_VESA_MODE_1024X768X8;
7021 break;
7022
7023 case 16:
7024 u16DefMode = VBE_VESA_MODE_1024X768X565;
7025 break;
7026
7027 case 24:
7028 u16DefMode = VBE_VESA_MODE_1024X768X888;
7029 break;
7030
7031 case 32:
7032 u16DefMode = VBE_OWN_MODE_1024X768X8888;
7033 break;
7034
7035 default: /* gcc, shut up! */
7036 AssertMsgFailed(("gone postal!\n"));
7037 continue;
7038 }
7039
7040 /* mode_info_list is not terminated */
7041 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
7042 pDefMode++;
7043 Assert(j < MODE_INFO_SIZE);
7044
7045 *pCurMode = *pDefMode;
7046 pCurMode->mode = u16CurMode++;
7047
7048 /* adjust defaults */
7049 pCurMode->info.XResolution = cx;
7050 pCurMode->info.YResolution = cy;
7051 pCurMode->info.BytesPerScanLine = cbPitch;
7052 pCurMode->info.LinBytesPerScanLine = cbPitch;
7053 vgaR3AdjustModeInfo(pThis, pCurMode);
7054
7055 /* commit it */
7056 pCurMode++;
7057 }
7058 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
7059 {
7060 AssertMsgFailed(("pHlp->pfnCFGMQueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
7061 return rc;
7062 }
7063 } /* foreach custom mode key */
7064 }
7065
7066 /*
7067 * Add the "End of list" mode.
7068 */
7069 memset(pCurMode, 0, sizeof(*pCurMode));
7070 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
7071
7072 /*
7073 * Register I/O Port for the VBE BIOS Extra Data.
7074 */
7075 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, VBE_EXTRA_PORT, 1 /*cPorts*/, vbeR3IOPortWriteVbeExtra, vbeR3IoPortReadVbeExtra,
7076 "VBE BIOS Extra Data", NULL /*paExtDesc*/, &pThis->hIoPortVbeExtra);
7077 AssertRCReturn(rc, rc);
7078
7079 /*
7080 * Register I/O Port for the BIOS Logo.
7081 */
7082 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, LOGO_IO_PORT, 1 /*cPorts*/, vbeR3IoPortWriteCmdLogo, vbeR3IoPortReadCmdLogo,
7083 "BIOS Logo", NULL /*paExtDesc*/, &pThis->hIoPortCmdLogo);
7084 AssertRCReturn(rc, rc);
7085
7086 /*
7087 * Register debugger info callbacks.
7088 */
7089 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaR3InfoState);
7090 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaR3InfoText);
7091 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaR3InfoCR);
7092 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaR3InfoGR);
7093 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaR3InfoSR);
7094 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaR3InfoAR);
7095 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaR3InfoPlanar);
7096 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaR3InfoDAC);
7097 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaR3InfoVBE);
7098
7099 /*
7100 * Construct the logo header.
7101 */
7102 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
7103
7104 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
7105 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7106 LogoHdr.fu8FadeIn = 1;
7107 else if (RT_FAILURE(rc))
7108 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeIn\" as integer failed"));
7109
7110 rc = pHlp->pfnCFGMQueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
7111 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7112 LogoHdr.fu8FadeOut = 1;
7113 else if (RT_FAILURE(rc))
7114 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FadeOut\" as integer failed"));
7115
7116 rc = pHlp->pfnCFGMQueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
7117 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7118 LogoHdr.u16LogoMillies = 0;
7119 else if (RT_FAILURE(rc))
7120 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"LogoTime\" as integer failed"));
7121
7122 rc = pHlp->pfnCFGMQueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
7123 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7124 LogoHdr.fu8ShowBootMenu = 0;
7125 else if (RT_FAILURE(rc))
7126 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
7127
7128# if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
7129 /* Disable the logo abd menu if all default settings. */
7130 if ( LogoHdr.fu8FadeIn
7131 && LogoHdr.fu8FadeOut
7132 && LogoHdr.u16LogoMillies == 0
7133 && LogoHdr.fu8ShowBootMenu == 2)
7134 {
7135 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
7136 LogoHdr.u16LogoMillies = 500;
7137 }
7138# endif
7139
7140 /* Delay the logo a little bit */
7141 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
7142 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
7143
7144 /*
7145 * Get the Logo file name.
7146 */
7147 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LogoFile", &pThisCC->pszLogoFile);
7148 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
7149 pThisCC->pszLogoFile = NULL;
7150 else if (RT_FAILURE(rc))
7151 return PDMDEV_SET_ERROR(pDevIns, rc,
7152 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
7153 else if (!*pThisCC->pszLogoFile)
7154 {
7155 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7156 pThisCC->pszLogoFile = NULL;
7157 }
7158
7159 /*
7160 * Determine the logo size, open any specified logo file in the process.
7161 */
7162 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7163 RTFILE FileLogo = NIL_RTFILE;
7164 if (pThisCC->pszLogoFile)
7165 {
7166 rc = RTFileOpen(&FileLogo, pThisCC->pszLogoFile,
7167 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
7168 if (RT_SUCCESS(rc))
7169 {
7170 uint64_t cbFile;
7171 rc = RTFileQuerySize(FileLogo, &cbFile);
7172 if (RT_SUCCESS(rc))
7173 {
7174 if (cbFile > 0 && cbFile < 32*_1M)
7175 LogoHdr.cbLogo = (uint32_t)cbFile;
7176 else
7177 rc = VERR_TOO_MUCH_DATA;
7178 }
7179 }
7180 if (RT_FAILURE(rc))
7181 {
7182 /*
7183 * Ignore failure and fall back to the default logo.
7184 */
7185 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThisCC->pszLogoFile, rc));
7186 if (FileLogo != NIL_RTFILE)
7187 RTFileClose(FileLogo);
7188 FileLogo = NIL_RTFILE;
7189 PDMDevHlpMMHeapFree(pDevIns, pThisCC->pszLogoFile);
7190 pThisCC->pszLogoFile = NULL;
7191 }
7192 }
7193
7194 /*
7195 * Disable graphic splash screen if it doesn't fit into VRAM.
7196 */
7197 if (pThis->vram_size < LOGO_MAX_SIZE)
7198 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7199
7200 /*
7201 * Allocate buffer for the logo data.
7202 * Let us fall back to default logo on read failure.
7203 */
7204 pThisCC->cbLogo = LogoHdr.cbLogo;
7205 if (g_cbVgaDefBiosLogo)
7206 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogo);
7207# ifndef VBOX_OSE
7208 if (g_cbVgaDefBiosLogoNY)
7209 pThisCC->cbLogo = RT_MAX(pThisCC->cbLogo, g_cbVgaDefBiosLogoNY);
7210# endif
7211 pThisCC->cbLogo += sizeof(LogoHdr);
7212
7213 pThisCC->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThisCC->cbLogo);
7214 if (pThisCC->pbLogo)
7215 {
7216 /*
7217 * Write the logo header.
7218 */
7219 PLOGOHDR pLogoHdr = (PLOGOHDR)pThisCC->pbLogo;
7220 *pLogoHdr = LogoHdr;
7221
7222 /*
7223 * Write the logo bitmap.
7224 */
7225 if (pThisCC->pszLogoFile)
7226 {
7227 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7228 if (RT_SUCCESS(rc))
7229 rc = vbeR3ParseBitmap(pThisCC);
7230 if (RT_FAILURE(rc))
7231 {
7232 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7233 rc, pThisCC->pszLogoFile));
7234 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7235 }
7236 }
7237 if ( !pThisCC->pszLogoFile
7238 || RT_FAILURE(rc))
7239 {
7240# ifndef VBOX_OSE
7241 RTTIMESPEC Now;
7242 RTTimeLocalNow(&Now);
7243 RTTIME T;
7244 RTTimeLocalExplode(&T, &Now);
7245 bool fSuppressNewYearSplash = false;
7246 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SuppressNewYearSplash", &fSuppressNewYearSplash, true);
7247 if ( !fSuppressNewYearSplash
7248 && (T.u16YearDay > 353 || T.u16YearDay < 10))
7249 {
7250 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogoNY;
7251 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogoNY, LogoHdr.cbLogo);
7252 pThisCC->fBootMenuInverse = true;
7253 }
7254 else
7255# endif
7256 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7257 rc = vbeR3ParseBitmap(pThisCC);
7258 AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Parsing of internal bitmap failed! vbeR3ParseBitmap() -> %Rrc\n", rc), rc);
7259 }
7260
7261 rc = VINF_SUCCESS;
7262 }
7263 else
7264 rc = VERR_NO_MEMORY;
7265
7266 /*
7267 * Cleanup.
7268 */
7269 if (FileLogo != NIL_RTFILE)
7270 RTFileClose(FileLogo);
7271
7272# ifdef VBOX_WITH_HGSMI
7273 VBVAInit(pDevIns, pThis, pThisCC);
7274# endif
7275
7276# ifdef VBOX_WITH_VDMA
7277 if (rc == VINF_SUCCESS)
7278 {
7279 rc = vboxVDMAConstruct(pThis, pThisCC, 1024);
7280 AssertRC(rc);
7281 }
7282# endif
7283
7284# ifdef VBOX_WITH_VMSVGA
7285 if ( rc == VINF_SUCCESS
7286 && pThis->fVMSVGAEnabled)
7287 rc = vmsvgaR3Init(pDevIns);
7288# endif
7289
7290 /*
7291 * Statistics.
7292 */
7293# ifdef VBOX_WITH_STATISTICS
7294 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7295 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7296 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7297 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7298 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapPage, STAMTYPE_COUNTER, "MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMmioMapMmio2Page.");
7299 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMapReset, STAMTYPE_COUNTER, "MapPageReset", STAMUNIT_OCCURENCES, "Calls to IOMMmioResetRegion.");
7300 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaR3PortUpdateDisplay().");
7301# endif
7302# ifdef VBOX_WITH_HGSMI
7303 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatHgsmiMdaCgaAccesses, STAMTYPE_COUNTER, "HgmsiMdaCgaAccesses", STAMUNIT_OCCURENCES, "Number of non-HGMSI accesses for 03b0-3b3 and 03d0-3d3.");
7304# endif
7305
7306 /* Init latched access mask. */
7307 pThis->uMaskLatchAccess = 0x3ff;
7308
7309 if (RT_SUCCESS(rc))
7310 {
7311 PPDMIBASE pBase;
7312 /*
7313 * Attach status driver (optional).
7314 */
7315 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
7316 if (RT_SUCCESS(rc))
7317 pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7318 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7319 {
7320 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7321 rc = VINF_SUCCESS;
7322 }
7323 else
7324 {
7325 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7326 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7327 }
7328 }
7329 return rc;
7330}
7331
7332#else /* !IN_RING3 */
7333
7334/**
7335 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
7336 */
7337static DECLCALLBACK(int) vgaRZConstruct(PPDMDEVINS pDevIns)
7338{
7339 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
7340 PVGASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PVGASTATE);
7341 PVGASTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVGASTATECC);
7342
7343 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
7344 AssertRCReturn(rc, rc);
7345
7346 /*
7347 * Set I/O port callbacks for this context.
7348 * We just copy the ring-3 registration bits and remove the '&' before the handle.
7349 */
7350# define REG_PORT(a_uPort, a_cPorts, a_pfnWrite, a_pfnRead, a_szDesc, a_hIoPort) do { \
7351 rc = PDMDevHlpIoPortSetUpContext(pDevIns, a_hIoPort, a_pfnWrite, a_pfnRead, NULL /*pvUser*/); \
7352 AssertRCReturn(rc, rc); \
7353 } while (0)
7354
7355 REG_PORT(0x3c0, 2, vgaIoPortArWrite, vgaIoPortArRead, "Attribute Controller", pThis->hIoPortAr);
7356 REG_PORT(0x3c2, 1, vgaIoPortMsrWrite, vgaIoPortSt00Read, "MSR / ST00", pThis->hIoPortMsrSt00);
7357 REG_PORT(0x3c3, 1, vgaIoPortUnusedWrite, vgaIoPortUnusedRead, "0x3c3", pThis->hIoPort3c3);
7358 REG_PORT(0x3c4, 2, vgaIoPortSrWrite, vgaIoPortSrRead, "Sequencer", pThis->hIoPortSr);
7359 REG_PORT(0x3c6, 4, vgaIoPortDacWrite, vgaIoPortDacRead, "DAC", pThis->hIoPortDac);
7360 REG_PORT(0x3ca, 4, vgaIoPortPosWrite, vgaIoPortPosRead, "Graphics Position", /*?*/ pThis->hIoPortPos);
7361 REG_PORT(0x3ce, 2, vgaIoPortGrWrite, vgaIoPortGrRead, "Graphics Controller", pThis->hIoPortGr);
7362
7363 REG_PORT(0x3b4, 2, vgaIoPortMdaCrtWrite, vgaIoPortMdaCrtRead, "MDA CRT control", pThis->hIoPortMdaCrt);
7364 REG_PORT(0x3ba, 1, vgaIoPortMdaFcrWrite, vgaIoPortMdaStRead, "MDA feature/status", pThis->hIoPortMdaFcrSt);
7365 REG_PORT(0x3d4, 2, vgaIoPortCgaCrtWrite, vgaIoPortCgaCrtRead, "CGA CRT control", pThis->hIoPortCgaCrt);
7366 REG_PORT(0x3da, 1, vgaIoPortCgaFcrWrite, vgaIoPortCgaStRead, "CGA Feature / status", pThis->hIoPortCgaFcrSt);
7367
7368# ifdef CONFIG_BOCHS_VBE
7369 REG_PORT(0x1ce, 1, vgaIoPortWriteVbeIndex, vgaIoPortReadVbeIndex, "VBE Index", pThis->hIoPortVbeIndex);
7370 REG_PORT(0x1cf, 1, vgaIoPortWriteVbeData, vgaIoPortReadVbeData, "VBE Data", pThis->hIoPortVbeData);
7371# endif /* CONFIG_BOCHS_VBE */
7372
7373# undef REG_PORT
7374
7375 /* BIOS port: */
7376 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortBios, vgaIoPortWriteBios, vgaIoPortReadBios, NULL /*pvUser*/);
7377 AssertRCReturn(rc, rc);
7378
7379# ifdef VBOX_WITH_VMSVGA
7380 if (pThis->hIoPortVmSvga != NIL_IOMIOPORTHANDLE)
7381 {
7382 AssertReturn(pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7383 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortVmSvga, vmsvgaIOWrite, vmsvgaIORead, NULL /*pvUser*/);
7384 AssertRCReturn(rc, rc);
7385 }
7386 else
7387 AssertReturn(!pThis->fVMSVGAEnabled, VERR_INVALID_STATE);
7388# endif
7389
7390 /*
7391 * MMIO.
7392 */
7393 rc = PDMDevHlpMmioSetUpContextEx(pDevIns, pThis->hMmioLegacy, vgaMmioWrite, vgaMmioRead, vgaMmioFill, NULL /*pvUser*/);
7394 AssertRCReturn(rc, rc);
7395
7396 /*
7397 * Map the start of the VRAM into this context.
7398 */
7399# if defined(VBOX_WITH_2X_4GB_ADDR_SPACE) || (defined(IN_RING0) && defined(VGA_WITH_PARTIAL_RING0_MAPPING))
7400 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VRam, 0 /* off */, VGA_MAPPING_SIZE, (void **)&pThisCC->pbVRam);
7401 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMmio2SetUpContext(,VRAM,0,%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
7402# endif
7403
7404 /*
7405 * Map the first page of the VMSVGA FIFO into this context (not raw-mode).
7406 * We currently only access SVGA_FIFO_MIN, SVGA_FIFO_PITCHLOCK, and SVGA_FIFO_BUSY.
7407 */
7408# if defined(VBOX_WITH_VMSVGA) && !defined(IN_RC)
7409 AssertCompile((RT_MAX(SVGA_FIFO_MIN, RT_MAX(SVGA_FIFO_PITCHLOCK, SVGA_FIFO_BUSY)) + 1) * sizeof(uint32_t) < GUEST_PAGE_SIZE);
7410 if (pThis->fVMSVGAEnabled)
7411 {
7412 rc = PDMDevHlpMmio2SetUpContext(pDevIns, pThis->hMmio2VmSvgaFifo, 0 /* off */, GUEST_PAGE_SIZE,
7413 (void **)&pThisCC->svga.pau32FIFO);
7414 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
7415 }
7416 else
7417 AssertReturn(pThis->hMmio2VmSvgaFifo == NIL_PGMMMIO2HANDLE, VERR_INVALID_STATE);
7418# endif
7419
7420 return VINF_SUCCESS;
7421}
7422
7423#endif /* !IN_RING3 */
7424
7425/**
7426 * The device registration structure.
7427 */
7428const PDMDEVREG g_DeviceVga =
7429{
7430 /* .u32Version = */ PDM_DEVREG_VERSION,
7431 /* .uReserved0 = */ 0,
7432 /* .szName = */ "vga",
7433 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
7434 /* .fClass = */ PDM_DEVREG_CLASS_GRAPHICS,
7435 /* .cMaxInstances = */ 1,
7436 /* .uSharedVersion = */ 42,
7437 /* .cbInstanceShared = */ sizeof(VGASTATE),
7438 /* .cbInstanceCC = */ sizeof(VGASTATECC),
7439 /* .cbInstanceRC = */ sizeof(VGASTATERC),
7440 /* .cMaxPciDevices = */ 1,
7441 /* .cMaxMsixVectors = */ 0,
7442 /* .pszDescription = */ "VGA Adaptor with VESA extensions.",
7443#if defined(IN_RING3)
7444 /* .pszRCMod = */ "VBoxDDRC.rc",
7445 /* .pszR0Mod = */ "VBoxDDR0.r0",
7446 /* .pfnConstruct = */ vgaR3Construct,
7447 /* .pfnDestruct = */ vgaR3Destruct,
7448 /* .pfnRelocate = */ vgaR3Relocate,
7449 /* .pfnMemSetup = */ NULL,
7450 /* .pfnPowerOn = */ vgaR3PowerOn,
7451 /* .pfnReset = */ vgaR3Reset,
7452 /* .pfnSuspend = */ NULL,
7453 /* .pfnResume = */ vgaR3Resume,
7454 /* .pfnAttach = */ vgaAttach,
7455 /* .pfnDetach = */ vgaDetach,
7456 /* .pfnQueryInterface = */ NULL,
7457 /* .pfnInitComplete = */ NULL,
7458 /* .pfnPowerOff = */ vgaR3PowerOff,
7459 /* .pfnSoftReset = */ NULL,
7460 /* .pfnReserved0 = */ NULL,
7461 /* .pfnReserved1 = */ NULL,
7462 /* .pfnReserved2 = */ NULL,
7463 /* .pfnReserved3 = */ NULL,
7464 /* .pfnReserved4 = */ NULL,
7465 /* .pfnReserved5 = */ NULL,
7466 /* .pfnReserved6 = */ NULL,
7467 /* .pfnReserved7 = */ NULL,
7468#elif defined(IN_RING0)
7469 /* .pfnEarlyConstruct = */ NULL,
7470 /* .pfnConstruct = */ vgaRZConstruct,
7471 /* .pfnDestruct = */ NULL,
7472 /* .pfnFinalDestruct = */ NULL,
7473 /* .pfnRequest = */ NULL,
7474 /* .pfnReserved0 = */ NULL,
7475 /* .pfnReserved1 = */ NULL,
7476 /* .pfnReserved2 = */ NULL,
7477 /* .pfnReserved3 = */ NULL,
7478 /* .pfnReserved4 = */ NULL,
7479 /* .pfnReserved5 = */ NULL,
7480 /* .pfnReserved6 = */ NULL,
7481 /* .pfnReserved7 = */ NULL,
7482#elif defined(IN_RC)
7483 /* .pfnConstruct = */ vgaRZConstruct,
7484 /* .pfnReserved0 = */ NULL,
7485 /* .pfnReserved1 = */ NULL,
7486 /* .pfnReserved2 = */ NULL,
7487 /* .pfnReserved3 = */ NULL,
7488 /* .pfnReserved4 = */ NULL,
7489 /* .pfnReserved5 = */ NULL,
7490 /* .pfnReserved6 = */ NULL,
7491 /* .pfnReserved7 = */ NULL,
7492#else
7493# error "Not in IN_RING3, IN_RING0 or IN_RC!"
7494#endif
7495 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
7496};
7497
7498#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7499
7500/*
7501 * Local Variables:
7502 * nuke-trailing-whitespace-p:nil
7503 * End:
7504 */
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