VirtualBox

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

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

DevVGA: Register GR4 (read map select), as the name suggests, does not affect writes.

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