VirtualBox

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

Last change on this file since 39135 was 39135, checked in by vboxsync, 13 years ago

Changed PDMDevHlpMMIORegister to take flags and drop pfnFill. Added PDMDevHlpMMIORegisterEx for the one user of pfnFill.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette