VirtualBox

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

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

scm copyright and license note update

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