VirtualBox

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

Last change on this file since 80701 was 80701, checked in by vboxsync, 5 years ago

PDM,Devices: Redefined uReserved1 to cMaxPciDevices in the PDMDEVREG structures and added PDM_DEVREG_FLAGS_MSI_X for indicating possible need for MSI-X. bugref:9218

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