VirtualBox

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

Last change on this file since 39073 was 39059, checked in by vboxsync, 14 years ago

unused variable warnings.

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