VirtualBox

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

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

VGA: Do not hardcode LFB base in BIOS.

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