VirtualBox

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

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

Let VGA BIOS get out of VBE mode only through VGA registers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 220.1 KB
Line 
1/* $Id: DevVGA.cpp 40148 2012-02-16 13:33:48Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * QEMU VGA Emulator.
21 *
22 * Copyright (c) 2003 Fabrice Bellard
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46
47/* WARNING!!! All defines that affect VGAState should be placed to DevVGA.h !!!
48 * NEVER place them here as this would lead to VGAState inconsistency
49 * across different .cpp files !!!
50 */
51/** The size of the VGA GC mapping.
52 * This is supposed to be all the VGA memory accessible to the guest.
53 * The initial value was 256KB but NTAllInOne.iso appears to access more
54 * thus the limit was upped to 512KB.
55 *
56 * @todo Someone with some VGA knowhow should make a better guess at this value.
57 */
58#define VGA_MAPPING_SIZE _512K
59
60#ifdef VBOX_WITH_HGSMI
61#define PCIDEV_2_VGASTATE(pPciDev) ((VGAState *)((uintptr_t)pPciDev - RT_OFFSETOF(VGAState, Dev)))
62#endif /* VBOX_WITH_HGSMI */
63/** Converts a vga adaptor state pointer to a device instance pointer. */
64#define VGASTATE2DEVINS(pVgaState) ((pVgaState)->CTX_SUFF(pDevIns))
65
66/** Check that the video modes fit into virtual video memory.
67 * Only works when VBE_NEW_DYN_LIST is defined! */
68#define VRAM_SIZE_FIX
69
70/** Check buffer if an VRAM offset is within the right range or not. */
71#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
72# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
73 do { \
74 if ((off) >= VGA_MAPPING_SIZE) \
75 { \
76 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS); \
77 Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
78 return VINF_IOM_HC_MMIO_WRITE; \
79 } \
80 } while (0)
81#else
82# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
83 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS)
84#endif
85
86/** Check buffer if an VRAM offset is within the right range or not. */
87#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
88# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
89 do { \
90 if ((off) >= VGA_MAPPING_SIZE) \
91 { \
92 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff); \
93 Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
94 (rcVar) = VINF_IOM_HC_MMIO_READ; \
95 return 0; \
96 } \
97 } while (0)
98#else
99# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
100 do { \
101 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff); \
102 NOREF(rcVar); \
103 } while (0)
104#endif
105
106
107/*******************************************************************************
108* Header Files *
109*******************************************************************************/
110#define LOG_GROUP LOG_GROUP_DEV_VGA
111#include <VBox/vmm/pdmdev.h>
112#include <VBox/vmm/pgm.h>
113#ifdef IN_RING3
114# include <iprt/alloc.h>
115# include <iprt/ctype.h>
116#endif /* IN_RING3 */
117#include <iprt/assert.h>
118#include <iprt/asm.h>
119#include <iprt/file.h>
120#include <iprt/time.h>
121#include <iprt/string.h>
122#include <iprt/uuid.h>
123
124#include <VBox/VMMDev.h>
125#include <VBox/VBoxVideo.h>
126#include <VBox/bioslogo.h>
127
128/* should go BEFORE any other DevVGA include to make all DevVGA.h config defines be visible */
129#include "DevVGA.h"
130
131#if defined(VBE_NEW_DYN_LIST) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
132# include "DevVGAModes.h"
133# include <stdio.h> /* sscan */
134#endif
135
136#include "vl_vbox.h"
137#include "VBoxDD.h"
138#include "VBoxDD2.h"
139
140
141/*******************************************************************************
142* Structures and Typedefs *
143*******************************************************************************/
144#pragma pack(1)
145
146/** BMP File Format Bitmap Header. */
147typedef struct
148{
149 uint16_t Type; /* File Type Identifier */
150 uint32_t FileSize; /* Size of File */
151 uint16_t Reserved1; /* Reserved (should be 0) */
152 uint16_t Reserved2; /* Reserved (should be 0) */
153 uint32_t Offset; /* Offset to bitmap data */
154} BMPINFO;
155
156/** Pointer to a bitmap header*/
157typedef BMPINFO *PBMPINFO;
158
159/** OS/2 1.x Information Header Format. */
160typedef struct
161{
162 uint32_t Size; /* Size of Remaining Header */
163 uint16_t Width; /* Width of Bitmap in Pixels */
164 uint16_t Height; /* Height of Bitmap in Pixels */
165 uint16_t Planes; /* Number of Planes */
166 uint16_t BitCount; /* Color Bits Per Pixel */
167} OS2HDR;
168
169/** Pointer to a OS/2 1.x header format */
170typedef OS2HDR *POS2HDR;
171
172/** OS/2 2.0 Information Header Format. */
173typedef struct
174{
175 uint32_t Size; /* Size of Remaining Header */
176 uint32_t Width; /* Width of Bitmap in Pixels */
177 uint32_t Height; /* Height of Bitmap in Pixels */
178 uint16_t Planes; /* Number of Planes */
179 uint16_t BitCount; /* Color Bits Per Pixel */
180 uint32_t Compression; /* Compression Scheme (0=none) */
181 uint32_t SizeImage; /* Size of bitmap in bytes */
182 uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
183 uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
184 uint32_t ClrUsed; /* Number of Colors in Color Table */
185 uint32_t ClrImportant; /* Number of Important Colors */
186 uint16_t Units; /* Resolution Measurement Used */
187 uint16_t Reserved; /* Reserved FIelds (always 0) */
188 uint16_t Recording; /* Orientation of Bitmap */
189 uint16_t Rendering; /* Halftone Algorithm Used on Image */
190 uint32_t Size1; /* Halftone Algorithm Data */
191 uint32_t Size2; /* Halftone Algorithm Data */
192 uint32_t ColorEncoding; /* Color Table Format (always 0) */
193 uint32_t Identifier; /* Misc. Field for Application Use */
194} OS22HDR;
195
196/** Pointer to a OS/2 2.0 header format */
197typedef OS22HDR *POS22HDR;
198
199/** Windows 3.x Information Header Format. */
200typedef struct
201{
202 uint32_t Size; /* Size of Remaining Header */
203 uint32_t Width; /* Width of Bitmap in Pixels */
204 uint32_t Height; /* Height of Bitmap in Pixels */
205 uint16_t Planes; /* Number of Planes */
206 uint16_t BitCount; /* Bits Per Pixel */
207 uint32_t Compression; /* Compression Scheme (0=none) */
208 uint32_t SizeImage; /* Size of bitmap in bytes */
209 uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
210 uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
211 uint32_t ClrUsed; /* Number of Colors in Color Table */
212 uint32_t ClrImportant; /* Number of Important Colors */
213} WINHDR;
214
215/** Pointer to a Windows 3.x header format */
216typedef WINHDR *PWINHDR;
217
218#pragma pack()
219
220#define BMP_ID 0x4D42
221
222/** @name BMP compressions.
223 * @{ */
224#define BMP_COMPRESS_NONE 0
225#define BMP_COMPRESS_RLE8 1
226#define BMP_COMPRESS_RLE4 2
227/** @} */
228
229/** @name BMP header sizes.
230 * @{ */
231#define BMP_HEADER_OS21 12
232#define BMP_HEADER_OS22 64
233#define BMP_HEADER_WIN3 40
234/** @} */
235
236/** The BIOS boot menu text position, X. */
237#define LOGO_F12TEXT_X 304
238/** The BIOS boot menu text position, Y. */
239#define LOGO_F12TEXT_Y 464
240
241/** Width of the "Press F12 to select boot device." bitmap.
242 Anything that exceeds the limit of F12BootText below is filled with
243 background. */
244#define LOGO_F12TEXT_WIDTH 286
245/** Height of the boot device selection bitmap, see LOGO_F12TEXT_WIDTH. */
246#define LOGO_F12TEXT_HEIGHT 12
247
248/** The BIOS logo delay time (msec). */
249#define LOGO_DELAY_TIME 2000
250
251#define LOGO_MAX_WIDTH 640
252#define LOGO_MAX_HEIGHT 480
253#define LOGO_MAX_SIZE LOGO_MAX_WIDTH * LOGO_MAX_HEIGHT * 4
254
255
256/*******************************************************************************
257* Global Variables *
258*******************************************************************************/
259/* "Press F12 to select boot device." bitmap. */
260static const uint8_t g_abLogoF12BootText[] =
261{
262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x0F, 0x7C,
265 0xF8, 0xF0, 0x01, 0xE0, 0x81, 0x9F, 0x3F, 0x00, 0x70, 0xF8, 0x00, 0xE0, 0xC3,
266 0x07, 0x0F, 0x1F, 0x3E, 0x70, 0x00, 0xF0, 0xE1, 0xC3, 0x07, 0x0E, 0x00, 0x6E,
267 0x7C, 0x60, 0xE0, 0xE1, 0xC3, 0x07, 0xC6, 0x80, 0x81, 0x31, 0x63, 0xC6, 0x00,
268 0x30, 0x80, 0x61, 0x0C, 0x00, 0x36, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
269 0x18, 0x36, 0x00, 0xCC, 0x8C, 0x19, 0xC3, 0x06, 0xC0, 0x8C, 0x31, 0x3C, 0x30,
270 0x8C, 0x19, 0x83, 0x31, 0x60, 0x60, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x60, 0x18,
271 0x00, 0x80, 0xC1, 0x18, 0x00, 0x30, 0x06, 0x60, 0x18, 0x30, 0x80, 0x01, 0x00,
272 0x33, 0x63, 0xC6, 0x30, 0x00, 0x30, 0x63, 0x80, 0x19, 0x0C, 0x03, 0x06, 0x00,
273 0x0C, 0x18, 0x18, 0xC0, 0x81, 0x03, 0x00, 0x03, 0x18, 0x0C, 0x00, 0x60, 0x30,
274 0x06, 0x00, 0x87, 0x01, 0x18, 0x06, 0x0C, 0x60, 0x00, 0xC0, 0xCC, 0x98, 0x31,
275 0x0C, 0x00, 0xCC, 0x18, 0x30, 0x0C, 0xC3, 0x80, 0x01, 0x00, 0x03, 0x66, 0xFE,
276 0x18, 0x30, 0x00, 0xC0, 0x02, 0x06, 0x06, 0x00, 0x18, 0x8C, 0x01, 0x60, 0xE0,
277 0x0F, 0x86, 0x3F, 0x03, 0x18, 0x00, 0x30, 0x33, 0x66, 0x0C, 0x03, 0x00, 0x33,
278 0xFE, 0x0C, 0xC3, 0x30, 0xE0, 0x0F, 0xC0, 0x87, 0x9B, 0x31, 0x63, 0xC6, 0x00,
279 0xF0, 0x80, 0x01, 0x03, 0x00, 0x06, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
280 0x18, 0x06, 0x00, 0x6C, 0x8C, 0x19, 0xC3, 0x00, 0x80, 0x8D, 0x31, 0xC3, 0x30,
281 0x8C, 0x19, 0x03, 0x30, 0xB3, 0xC3, 0x87, 0x0F, 0x1F, 0x00, 0x2C, 0x60, 0x80,
282 0x01, 0xE0, 0x87, 0x0F, 0x00, 0x3E, 0x7C, 0x60, 0xF0, 0xE1, 0xE3, 0x07, 0x00,
283 0x0F, 0x3E, 0x7C, 0xFC, 0x00, 0xC0, 0xC3, 0xC7, 0x30, 0x0E, 0x3E, 0x7C, 0x00,
284 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1E, 0xC0, 0x00, 0x60, 0x00,
285 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
286 0x0C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x87, 0x31, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30,
289 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
290 0xF8, 0x83, 0xC1, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
291 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30,
292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
294 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
295};
296
297
298#ifndef VBOX_DEVICE_STRUCT_TESTCASE
299
300
301/**
302 * Set a VRAM page dirty.
303 *
304 * @param pThis VGA instance data.
305 * @param offVRAM The VRAM offset of the page to set.
306 */
307DECLINLINE(void) vga_set_dirty(VGAState *pThis, RTGCPHYS offVRAM)
308{
309 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
310 ASMBitSet(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
311 pThis->fHasDirtyBits = true;
312}
313
314/**
315 * Tests if a VRAM page is dirty.
316 *
317 * @returns true if dirty.
318 * @returns false if clean.
319 * @param pThis VGA instance data.
320 * @param offVRAM The VRAM offset of the page to check.
321 */
322DECLINLINE(bool) vga_is_dirty(VGAState *pThis, RTGCPHYS offVRAM)
323{
324 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
325 return ASMBitTest(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
326}
327
328/**
329 * Reset dirty flags in a give range.
330 *
331 * @param pThis VGA instance data.
332 * @param offVRAMStart Offset into the VRAM buffer of the first page.
333 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
334 */
335DECLINLINE(void) vga_reset_dirty(VGAState *pThis, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
336{
337 Assert(offVRAMStart < pThis->vram_size);
338 Assert(offVRAMEnd <= pThis->vram_size);
339 Assert(offVRAMStart < offVRAMEnd);
340 ASMBitClearRange(&pThis->au32DirtyBitmap[0], offVRAMStart >> PAGE_SHIFT, offVRAMEnd >> PAGE_SHIFT);
341}
342
343/* force some bits to zero */
344static const uint8_t sr_mask[8] = {
345 (uint8_t)~0xfc,
346 (uint8_t)~0xc2,
347 (uint8_t)~0xf0,
348 (uint8_t)~0xc0,
349 (uint8_t)~0xf1,
350 (uint8_t)~0xff,
351 (uint8_t)~0xff,
352 (uint8_t)~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_SAVED);
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_SAVED);
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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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_HC_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 pThis->GCPhysVRAM = GCPhysAddress;
5268 }
5269 }
5270 else
5271 {
5272 /*
5273 * Unmapping of the VRAM in progress.
5274 * Deregister the access handler so PGM doesn't get upset.
5275 */
5276 Assert(pThis->GCPhysVRAM);
5277 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5278 AssertRC(rc);
5279 pThis->GCPhysVRAM = 0;
5280 }
5281 return rc;
5282}
5283
5284
5285/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5286
5287/**
5288 * Saves a important bits of the VGA device config.
5289 *
5290 * @param pThis The VGA instance data.
5291 * @param pSSM The saved state handle.
5292 */
5293static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
5294{
5295 SSMR3PutU32(pSSM, pThis->vram_size);
5296 SSMR3PutU32(pSSM, pThis->cMonitors);
5297}
5298
5299
5300/**
5301 * @copydoc FNSSMDEVLIVEEXEC
5302 */
5303static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5304{
5305 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5306 Assert(uPass == 0); NOREF(uPass);
5307 vgaR3SaveConfig(pThis, pSSM);
5308 return VINF_SSM_DONT_CALL_AGAIN;
5309}
5310
5311
5312/**
5313 * @copydoc FNSSMDEVSAVEPREP
5314 */
5315static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5316{
5317#ifdef VBOX_WITH_VIDEOHWACCEL
5318 return vboxVBVASaveStatePrep(pDevIns, pSSM);
5319#else
5320 return VINF_SUCCESS;
5321#endif
5322}
5323
5324static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5325{
5326#ifdef VBOX_WITH_VIDEOHWACCEL
5327 return vboxVBVASaveStateDone(pDevIns, pSSM);
5328#else
5329 return VINF_SUCCESS;
5330#endif
5331}
5332
5333/**
5334 * @copydoc FNSSMDEVSAVEEXEC
5335 */
5336static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5337{
5338 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5339#ifdef VBOX_WITH_VDMA
5340 vboxVDMASaveStateExecPrep(pThis->pVdma, pSSM);
5341#endif
5342 vgaR3SaveConfig(pThis, pSSM);
5343 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5344#ifdef VBOX_WITH_HGSMI
5345 SSMR3PutBool(pSSM, true);
5346 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5347# ifdef VBOX_WITH_VDMA
5348 vboxVDMASaveStateExecDone(pThis->pVdma, pSSM);
5349# endif
5350 return rc;
5351#else
5352 SSMR3PutBool(pSSM, false);
5353 return VINF_SUCCESS;
5354#endif
5355}
5356
5357
5358/**
5359 * @copydoc FNSSMDEVSAVEEXEC
5360 */
5361static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5362{
5363 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5364 int rc;
5365
5366 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5367 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5368
5369 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5370 {
5371 /* Check the config */
5372 uint32_t cbVRam;
5373 rc = SSMR3GetU32(pSSM, &cbVRam);
5374 AssertRCReturn(rc, rc);
5375 if (pThis->vram_size != cbVRam)
5376 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5377
5378 uint32_t cMonitors;
5379 rc = SSMR3GetU32(pSSM, &cMonitors);
5380 AssertRCReturn(rc, rc);
5381 if (pThis->cMonitors != cMonitors)
5382 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5383 }
5384
5385 if (uPass == SSM_PASS_FINAL)
5386 {
5387 rc = vga_load(pSSM, pThis, uVersion);
5388 if (RT_FAILURE(rc))
5389 return rc;
5390 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5391 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5392 {
5393 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5394 AssertRCReturn(rc, rc);
5395 }
5396 if (fWithHgsmi)
5397 {
5398#ifdef VBOX_WITH_HGSMI
5399 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5400 AssertRCReturn(rc, rc);
5401#else
5402 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5403#endif
5404 }
5405 }
5406 return VINF_SUCCESS;
5407}
5408
5409
5410/**
5411 * @copydoc FNSSMDEVLOADDONE
5412 */
5413static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5414{
5415#ifdef VBOX_WITH_HGSMI
5416 return vboxVBVALoadStateDone(pDevIns, pSSM);
5417#else
5418 return VINF_SUCCESS;
5419#endif
5420}
5421
5422
5423/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5424
5425/**
5426 * Reset notification.
5427 *
5428 * @returns VBox status.
5429 * @param pDevIns The device instance data.
5430 */
5431static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5432{
5433 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5434 char *pchStart;
5435 char *pchEnd;
5436 LogFlow(("vgaReset\n"));
5437
5438#ifdef VBOX_WITH_HGSMI
5439 VBVAReset(pThis);
5440#endif /* VBOX_WITH_HGSMI */
5441
5442
5443 /* Clear the VRAM ourselves. */
5444 if (pThis->vram_ptrR3 && pThis->vram_size)
5445 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5446
5447 /*
5448 * Zero most of it.
5449 *
5450 * Unlike vga_reset we're leaving out a few members which we believe
5451 * must remain unchanged....
5452 */
5453 /* 1st part. */
5454 pchStart = (char *)&pThis->latch;
5455 pchEnd = (char *)&pThis->invalidated_y_table;
5456 memset(pchStart, 0, pchEnd - pchStart);
5457
5458 /* 2nd part. */
5459 pchStart = (char *)&pThis->last_palette;
5460 pchEnd = (char *)&pThis->u32Marker;
5461 memset(pchStart, 0, pchEnd - pchStart);
5462
5463
5464 /*
5465 * Restore and re-init some bits.
5466 */
5467 pThis->get_bpp = vga_get_bpp;
5468 pThis->get_offsets = vga_get_offsets;
5469 pThis->get_resolution = vga_get_resolution;
5470 pThis->graphic_mode = -1; /* Force full update. */
5471#ifdef CONFIG_BOCHS_VBE
5472 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5473 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5474 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5475#endif /* CONFIG_BOCHS_VBE */
5476
5477 /*
5478 * Reset the LBF mapping.
5479 */
5480 pThis->fLFBUpdated = false;
5481 if ( ( pThis->fGCEnabled
5482 || pThis->fR0Enabled)
5483 && pThis->GCPhysVRAM
5484 && pThis->GCPhysVRAM != NIL_RTGCPHYS32)
5485 {
5486 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5487 AssertRC(rc);
5488 }
5489 if (pThis->fRemappedVGA)
5490 {
5491 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5492 pThis->fRemappedVGA = false;
5493 }
5494
5495 /*
5496 * Reset the logo data.
5497 */
5498 pThis->LogoCommand = LOGO_CMD_NOP;
5499 pThis->offLogoData = 0;
5500
5501 /* notify port handler */
5502 if (pThis->pDrv)
5503 pThis->pDrv->pfnReset(pThis->pDrv);
5504
5505 /* Reset latched access mask. */
5506 pThis->uMaskLatchAccess = 0x3ff;
5507 pThis->cLatchAccesses = 0;
5508 pThis->u64LastLatchedAccess = 0;
5509 pThis->iMask = 0;
5510
5511 /* Reset retrace emulation. */
5512 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5513}
5514
5515
5516/**
5517 * Device relocation callback.
5518 *
5519 * @param pDevIns Pointer to the device instance.
5520 * @param offDelta The relocation delta relative to the old location.
5521 *
5522 * @see FNPDMDEVRELOCATE for details.
5523 */
5524static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5525{
5526 if (offDelta)
5527 {
5528 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5529 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5530
5531 pThis->RCPtrLFBHandler += offDelta;
5532 pThis->vram_ptrRC += offDelta;
5533 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5534 }
5535}
5536
5537
5538/**
5539 * Attach command.
5540 *
5541 * This is called to let the device attach to a driver for a specified LUN
5542 * during runtime. This is not called during VM construction, the device
5543 * constructor have to attach to all the available drivers.
5544 *
5545 * This is like plugging in the monitor after turning on the PC.
5546 *
5547 * @returns VBox status code.
5548 * @param pDevIns The device instance.
5549 * @param iLUN The logical unit which is being detached.
5550 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
5551 */
5552static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5553{
5554 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5555
5556 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5557 ("VGA device does not support hotplugging\n"),
5558 VERR_INVALID_PARAMETER);
5559
5560 switch (iLUN)
5561 {
5562 /* LUN #0: Display port. */
5563 case 0:
5564 {
5565 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5566 if (RT_SUCCESS(rc))
5567 {
5568 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5569 if (pThis->pDrv)
5570 {
5571 /* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
5572 if ( pThis->pDrv->pfnRefresh
5573 && pThis->pDrv->pfnResize
5574 && pThis->pDrv->pfnUpdateRect)
5575 rc = VINF_SUCCESS;
5576 else
5577 {
5578 Assert(pThis->pDrv->pfnRefresh);
5579 Assert(pThis->pDrv->pfnResize);
5580 Assert(pThis->pDrv->pfnUpdateRect);
5581 pThis->pDrv = NULL;
5582 pThis->pDrvBase = NULL;
5583 rc = VERR_INTERNAL_ERROR;
5584 }
5585#ifdef VBOX_WITH_VIDEOHWACCEL
5586 if(rc == VINF_SUCCESS)
5587 {
5588 rc = vbvaVHWAConstruct(pThis);
5589 if (rc != VERR_NOT_IMPLEMENTED)
5590 AssertRC(rc);
5591 }
5592#endif
5593 }
5594 else
5595 {
5596 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5597 pThis->pDrvBase = NULL;
5598 rc = VERR_PDM_MISSING_INTERFACE;
5599 }
5600 }
5601 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5602 {
5603 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5604 rc = VINF_SUCCESS;
5605 }
5606 else
5607 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5608 return rc;
5609 }
5610
5611 default:
5612 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5613 return VERR_PDM_NO_SUCH_LUN;
5614 }
5615}
5616
5617
5618/**
5619 * Detach notification.
5620 *
5621 * This is called when a driver is detaching itself from a LUN of the device.
5622 * The device should adjust it's state to reflect this.
5623 *
5624 * This is like unplugging the monitor while the PC is still running.
5625 *
5626 * @param pDevIns The device instance.
5627 * @param iLUN The logical unit which is being detached.
5628 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
5629 */
5630static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5631{
5632 /*
5633 * Reset the interfaces and update the controller state.
5634 */
5635 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5636
5637 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5638 ("VGA device does not support hotplugging\n"));
5639
5640 switch (iLUN)
5641 {
5642 /* LUN #0: Display port. */
5643 case 0:
5644 pThis->pDrv = NULL;
5645 pThis->pDrvBase = NULL;
5646 break;
5647
5648 default:
5649 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5650 break;
5651 }
5652}
5653
5654
5655/**
5656 * Destruct a device instance.
5657 *
5658 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5659 * resources can be freed correctly.
5660 *
5661 * @param pDevIns The device instance data.
5662 */
5663static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5664{
5665 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5666
5667#ifdef VBE_NEW_DYN_LIST
5668 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5669 LogFlow(("vgaR3Destruct:\n"));
5670
5671#ifdef VBOX_WITH_VDMA
5672 vboxVDMADestruct(pThis->pVdma);
5673#endif
5674
5675 /*
5676 * Free MM heap pointers.
5677 */
5678 if (pThis->pu8VBEExtraData)
5679 {
5680 MMR3HeapFree(pThis->pu8VBEExtraData);
5681 pThis->pu8VBEExtraData = NULL;
5682 }
5683#endif
5684 if (pThis->pu8VgaBios)
5685 {
5686 MMR3HeapFree(pThis->pu8VgaBios);
5687 pThis->pu8VgaBios = NULL;
5688 }
5689
5690 if (pThis->pszVgaBiosFile)
5691 {
5692 MMR3HeapFree(pThis->pszVgaBiosFile);
5693 pThis->pszVgaBiosFile = NULL;
5694 }
5695
5696 if (pThis->pszLogoFile)
5697 {
5698 MMR3HeapFree(pThis->pszLogoFile);
5699 pThis->pszLogoFile = NULL;
5700 }
5701
5702 PDMR3CritSectDelete(&pThis->lock);
5703 return VINF_SUCCESS;
5704}
5705
5706/**
5707 * Adjust VBE mode information
5708 *
5709 * Depending on the configured VRAM size, certain parts of VBE mode
5710 * information must be updated.
5711 *
5712 * @param pThis The device instance data.
5713 * @param pMode The mode information structure.
5714 */
5715static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
5716{
5717 int maxPage;
5718 int bpl;
5719
5720
5721 /* For 4bpp modes, the planes are "stacked" on top of each other. */
5722 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
5723 /* The "number of image pages" is really the max page index... */
5724 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
5725 Assert(maxPage >= 0);
5726 if (maxPage > 255)
5727 maxPage = 255; /* 8-bit value. */
5728 pMode->info.NumberOfImagePages = maxPage;
5729 pMode->info.LinNumberOfPages = maxPage;
5730}
5731
5732/**
5733 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5734 */
5735static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5736{
5737
5738 static bool s_fExpandDone = false;
5739 int rc;
5740 unsigned i;
5741#ifdef VBE_NEW_DYN_LIST
5742 uint32_t cCustomModes;
5743 uint32_t cyReduction;
5744 uint32_t cbPitch;
5745 PVBEHEADER pVBEDataHdr;
5746 ModeInfoListItem *pCurMode;
5747 unsigned cb;
5748#endif
5749 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5750 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5751 PVM pVM = PDMDevHlpGetVM(pDevIns);
5752
5753 Assert(iInstance == 0);
5754 Assert(pVM);
5755
5756 /*
5757 * Init static data.
5758 */
5759 if (!s_fExpandDone)
5760 {
5761 s_fExpandDone = true;
5762 vga_init_expand();
5763 }
5764
5765 /*
5766 * Validate configuration.
5767 */
5768 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
5769 "MonitorCount\0"
5770 "GCEnabled\0"
5771 "R0Enabled\0"
5772 "FadeIn\0"
5773 "FadeOut\0"
5774 "LogoTime\0"
5775 "LogoFile\0"
5776 "ShowBootMenu\0"
5777 "BiosRom\0"
5778 "RealRetrace\0"
5779 "CustomVideoModes\0"
5780 "HeightReduction\0"
5781 "CustomVideoMode1\0"
5782 "CustomVideoMode2\0"
5783 "CustomVideoMode3\0"
5784 "CustomVideoMode4\0"
5785 "CustomVideoMode5\0"
5786 "CustomVideoMode6\0"
5787 "CustomVideoMode7\0"
5788 "CustomVideoMode8\0"
5789 "CustomVideoMode9\0"
5790 "CustomVideoMode10\0"
5791 "CustomVideoMode11\0"
5792 "CustomVideoMode12\0"
5793 "CustomVideoMode13\0"
5794 "CustomVideoMode14\0"
5795 "CustomVideoMode15\0"
5796 "CustomVideoMode16\0"
5797 "MaxBiosXRes\0"
5798 "MaxBiosYRes\0"))
5799 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5800 N_("Invalid configuration for vga device"));
5801
5802 /*
5803 * Init state data.
5804 */
5805 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
5806 AssertLogRelRCReturn(rc, rc);
5807 if (pThis->vram_size > VGA_VRAM_MAX)
5808 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5809 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
5810 if (pThis->vram_size < VGA_VRAM_MIN)
5811 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5812 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
5813 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
5814 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5815 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
5816
5817 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
5818 AssertLogRelRCReturn(rc, rc);
5819
5820 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
5821 AssertLogRelRCReturn(rc, rc);
5822
5823 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
5824 AssertLogRelRCReturn(rc, rc);
5825 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
5826
5827 pThis->pDevInsR3 = pDevIns;
5828 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5829 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5830
5831 vgaR3Reset(pDevIns);
5832
5833 /* The PCI devices configuration. */
5834 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
5835 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
5836 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
5837 PCIDevSetClassBase( &pThis->Dev, 0x03);
5838 PCIDevSetHeaderType(&pThis->Dev, 0x00);
5839#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
5840 PCIDevSetInterruptPin(&pThis->Dev, 1);
5841#endif
5842
5843 /* The LBF access handler - error handling is better here than in the map function. */
5844 rc = PDMR3LdrGetSymbolRCLazy(pVM, pDevIns->pReg->szRCMod, NULL, "vgaGCLFBAccessHandler", &pThis->RCPtrLFBHandler);
5845 if (RT_FAILURE(rc))
5846 {
5847 AssertReleaseMsgFailed(("PDMR3LdrGetSymbolRC(, %s, \"vgaGCLFBAccessHandler\",) -> %Rrc\n", pDevIns->pReg->szRCMod, rc));
5848 return rc;
5849 }
5850
5851 /* the interfaces. */
5852 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
5853
5854 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
5855 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
5856 pThis->IPort.pfnQueryColorDepth = vgaPortQueryColorDepth;
5857 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
5858 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
5859 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
5860 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
5861 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
5862 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
5863 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
5864
5865#if defined(VBOX_WITH_HGSMI)
5866# if defined(VBOX_WITH_VIDEOHWACCEL)
5867 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsynch = vbvaVHWACommandCompleteAsynch;
5868# endif
5869#if defined(VBOX_WITH_CRHGSMI)
5870 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
5871 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
5872# endif
5873#endif
5874
5875 /*
5876 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
5877 */
5878 rc = PDMDevHlpMMIO2Register(pDevIns, 0 /* iRegion */, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
5879 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
5880 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo #1865 Map parts into R0 or just use PGM access (Mac only). */
5881
5882 if (pThis->fGCEnabled)
5883 {
5884 RTRCPTR pRCMapping = 0;
5885 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, 0 /* iRegion */, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
5886 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
5887 pThis->vram_ptrRC = pRCMapping;
5888 }
5889
5890#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
5891 if (pThis->fR0Enabled)
5892 {
5893 RTR0PTR pR0Mapping = 0;
5894 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 0 /* iRegion */, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
5895 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
5896 pThis->vram_ptrR0 = pR0Mapping;
5897 }
5898#endif
5899
5900 /*
5901 * Register I/O ports, ROM and save state.
5902 */
5903 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
5904 if (RT_FAILURE(rc))
5905 return rc;
5906 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
5907 if (RT_FAILURE(rc))
5908 return rc;
5909 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
5910 if (RT_FAILURE(rc))
5911 return rc;
5912 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
5913 if (RT_FAILURE(rc))
5914 return rc;
5915 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
5916 if (RT_FAILURE(rc))
5917 return rc;
5918#ifdef VBOX_WITH_HGSMI
5919 /* Use reserved VGA IO ports for HGSMI. */
5920 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
5921 if (RT_FAILURE(rc))
5922 return rc;
5923 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
5924 if (RT_FAILURE(rc))
5925 return rc;
5926#endif /* VBOX_WITH_HGSMI */
5927
5928#ifdef CONFIG_BOCHS_VBE
5929 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
5930 if (RT_FAILURE(rc))
5931 return rc;
5932 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
5933 if (RT_FAILURE(rc))
5934 return rc;
5935#endif /* CONFIG_BOCHS_VBE */
5936
5937 /* guest context extension */
5938 if (pThis->fGCEnabled)
5939 {
5940 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
5941 if (RT_FAILURE(rc))
5942 return rc;
5943 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
5944 if (RT_FAILURE(rc))
5945 return rc;
5946 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
5947 if (RT_FAILURE(rc))
5948 return rc;
5949 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
5950 if (RT_FAILURE(rc))
5951 return rc;
5952 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
5953 if (RT_FAILURE(rc))
5954 return rc;
5955#ifdef CONFIG_BOCHS_VBE
5956 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
5957 if (RT_FAILURE(rc))
5958 return rc;
5959 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
5960 if (RT_FAILURE(rc))
5961 return rc;
5962#endif /* CONFIG_BOCHS_VBE */
5963 }
5964
5965 /* R0 context extension */
5966 if (pThis->fR0Enabled)
5967 {
5968 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
5969 if (RT_FAILURE(rc))
5970 return rc;
5971 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
5972 if (RT_FAILURE(rc))
5973 return rc;
5974 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
5975 if (RT_FAILURE(rc))
5976 return rc;
5977 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
5978 if (RT_FAILURE(rc))
5979 return rc;
5980 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
5981 if (RT_FAILURE(rc))
5982 return rc;
5983#ifdef CONFIG_BOCHS_VBE
5984 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
5985 if (RT_FAILURE(rc))
5986 return rc;
5987 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
5988 if (RT_FAILURE(rc))
5989 return rc;
5990#endif /* CONFIG_BOCHS_VBE */
5991 }
5992
5993 /* vga mmio */
5994 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
5995 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
5996 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
5997 if (RT_FAILURE(rc))
5998 return rc;
5999 if (pThis->fGCEnabled)
6000 {
6001 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6002 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6003 if (RT_FAILURE(rc))
6004 return rc;
6005 }
6006 if (pThis->fR0Enabled)
6007 {
6008 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6009 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6010 if (RT_FAILURE(rc))
6011 return rc;
6012 }
6013
6014 /* vga bios */
6015 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6016 if (RT_FAILURE(rc))
6017 return rc;
6018 if (pThis->fR0Enabled)
6019 {
6020 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6021 if (RT_FAILURE(rc))
6022 return rc;
6023 }
6024
6025 /*
6026 * Get the VGA BIOS ROM file name.
6027 */
6028 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6029 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6030 {
6031 pThis->pszVgaBiosFile = NULL;
6032 rc = VINF_SUCCESS;
6033 }
6034 else if (RT_FAILURE(rc))
6035 return PDMDEV_SET_ERROR(pDevIns, rc,
6036 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6037 else if (!*pThis->pszVgaBiosFile)
6038 {
6039 MMR3HeapFree(pThis->pszVgaBiosFile);
6040 pThis->pszVgaBiosFile = NULL;
6041 }
6042
6043 const uint8_t *pu8VgaBiosBinary = NULL;
6044 uint64_t cbVgaBiosBinary;
6045 /*
6046 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6047 */
6048 RTFILE FileVgaBios = NIL_RTFILE;
6049 if (pThis->pszVgaBiosFile)
6050 {
6051 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6052 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6053 if (RT_SUCCESS(rc))
6054 {
6055 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6056 if (RT_SUCCESS(rc))
6057 {
6058 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6059 || pThis->cbVgaBios > _64K
6060 || pThis->cbVgaBios < 16 * _1K)
6061 rc = VERR_TOO_MUCH_DATA;
6062 }
6063 }
6064 if (RT_FAILURE(rc))
6065 {
6066 /*
6067 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6068 */
6069 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6070 RTFileClose(FileVgaBios);
6071 FileVgaBios = NIL_RTFILE;
6072 MMR3HeapFree(pThis->pszVgaBiosFile);
6073 pThis->pszVgaBiosFile = NULL;
6074 }
6075 }
6076
6077 /*
6078 * Attempt to get the VGA BIOS ROM data from file.
6079 */
6080 if (pThis->pszVgaBiosFile)
6081 {
6082 /*
6083 * Allocate buffer for the VGA BIOS ROM data.
6084 */
6085 pThis->pu8VgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6086 if (pThis->pu8VgaBios)
6087 {
6088 rc = RTFileRead(FileVgaBios, pThis->pu8VgaBios, pThis->cbVgaBios, NULL);
6089 if (RT_FAILURE(rc))
6090 {
6091 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6092 MMR3HeapFree(pThis->pu8VgaBios);
6093 pThis->pu8VgaBios = NULL;
6094 }
6095 rc = VINF_SUCCESS;
6096 }
6097 else
6098 rc = VERR_NO_MEMORY;
6099 }
6100 else
6101 pThis->pu8VgaBios = NULL;
6102
6103 /* cleanup */
6104 if (FileVgaBios != NIL_RTFILE)
6105 RTFileClose(FileVgaBios);
6106
6107 /* If we were unable to get the data from file for whatever reason, fall
6108 back to the built-in ROM image. */
6109 uint32_t fFlags = 0;
6110 if (pThis->pu8VgaBios == NULL)
6111 {
6112 pu8VgaBiosBinary = g_abVgaBiosBinary;
6113 cbVgaBiosBinary = g_cbVgaBiosBinary;
6114 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6115 }
6116 else
6117 {
6118 pu8VgaBiosBinary = pThis->pu8VgaBios;
6119 cbVgaBiosBinary = pThis->cbVgaBios;
6120 }
6121
6122 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6123 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6124 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, cbVgaBiosBinary, pu8VgaBiosBinary, cbVgaBiosBinary,
6125 fFlags, "VGA BIOS");
6126 if (RT_FAILURE(rc))
6127 return rc;
6128
6129 /* save */
6130 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6131 NULL, vgaR3LiveExec, NULL,
6132 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6133 NULL, vgaR3LoadExec, vgaR3LoadDone);
6134 if (RT_FAILURE(rc))
6135 return rc;
6136
6137 /* PCI */
6138 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6139 if (RT_FAILURE(rc))
6140 return rc;
6141 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
6142 if (pThis->Dev.devfn != 16 && iInstance == 0)
6143 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
6144
6145 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6146 if (RT_FAILURE(rc))
6147 return rc;
6148
6149 /* Initialize the PDM lock. */
6150 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->lock, RT_SRC_POS, "VGA#u", iInstance);
6151 if (RT_FAILURE(rc))
6152 {
6153 Log(("%s: Failed to create critical section.\n", __FUNCTION__));
6154 return rc;
6155 }
6156
6157 /*
6158 * Create the refresh timer.
6159 */
6160 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6161 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6162 "VGA Refresh Timer", &pThis->RefreshTimer);
6163 if (RT_FAILURE(rc))
6164 return rc;
6165
6166 /*
6167 * Attach to the display.
6168 */
6169 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6170 if (RT_FAILURE(rc))
6171 return rc;
6172
6173 /*
6174 * Initialize the retrace flag.
6175 */
6176 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6177 AssertLogRelRCReturn(rc, rc);
6178
6179#ifdef VBE_NEW_DYN_LIST
6180
6181 uint16_t maxBiosXRes;
6182 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6183 AssertLogRelRCReturn(rc, rc);
6184 uint16_t maxBiosYRes;
6185 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6186 AssertLogRelRCReturn(rc, rc);
6187
6188 /*
6189 * Compute buffer size for the VBE BIOS Extra Data.
6190 */
6191 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6192
6193 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6194 if (RT_SUCCESS(rc) && cyReduction)
6195 cb *= 2; /* Default mode list will be twice long */
6196 else
6197 cyReduction = 0;
6198
6199 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6200 if (RT_SUCCESS(rc) && cCustomModes)
6201 cb += sizeof(ModeInfoListItem) * cCustomModes;
6202 else
6203 cCustomModes = 0;
6204
6205 /*
6206 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6207 */
6208 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6209 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6210 pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6211 if (!pThis->pu8VBEExtraData)
6212 return VERR_NO_MEMORY;
6213
6214 pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
6215 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6216 pVBEDataHdr->cbData = cb;
6217
6218# ifndef VRAM_SIZE_FIX
6219 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6220 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6221# else /* VRAM_SIZE_FIX defined */
6222 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6223 for (i = 0; i < MODE_INFO_SIZE; i++)
6224 {
6225 uint32_t pixelWidth, reqSize;
6226 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6227 pixelWidth = 2;
6228 else
6229 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6230 reqSize = mode_info_list[i].info.XResolution
6231 * mode_info_list[i].info.YResolution
6232 * pixelWidth;
6233 if (reqSize >= pThis->vram_size)
6234 continue;
6235 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6236 || mode_info_list[i].info.YResolution > maxBiosYRes)
6237 continue;
6238 *pCurMode = mode_info_list[i];
6239 vgaAdjustModeInfo(pThis, pCurMode);
6240 pCurMode++;
6241 }
6242# endif /* VRAM_SIZE_FIX defined */
6243
6244 /*
6245 * Copy default modes with subtracted YResolution.
6246 */
6247 if (cyReduction)
6248 {
6249 ModeInfoListItem *pDefMode = mode_info_list;
6250 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6251# ifndef VRAM_SIZE_FIX
6252 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6253 {
6254 *pCurMode = *pDefMode;
6255 pCurMode->mode += 0x30;
6256 pCurMode->info.YResolution -= cyReduction;
6257 }
6258# else /* VRAM_SIZE_FIX defined */
6259 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6260 {
6261 uint32_t pixelWidth, reqSize;
6262 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6263 pixelWidth = 2;
6264 else
6265 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6266 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6267 if (reqSize >= pThis->vram_size)
6268 continue;
6269 if ( pDefMode->info.XResolution > maxBiosXRes
6270 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6271 continue;
6272 *pCurMode = *pDefMode;
6273 pCurMode->mode += 0x30;
6274 pCurMode->info.YResolution -= cyReduction;
6275 pCurMode++;
6276 }
6277# endif /* VRAM_SIZE_FIX defined */
6278 }
6279
6280
6281 /*
6282 * Add custom modes.
6283 */
6284 if (cCustomModes)
6285 {
6286 uint16_t u16CurMode = 0x160;
6287 for (i = 1; i <= cCustomModes; i++)
6288 {
6289 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6290 char *pszExtraData = NULL;
6291
6292 /* query and decode the custom mode string. */
6293 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6294 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6295 if (RT_SUCCESS(rc))
6296 {
6297 ModeInfoListItem *pDefMode = mode_info_list;
6298 unsigned int cx, cy, cBits, cParams, j;
6299 uint16_t u16DefMode;
6300
6301 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6302 if ( cParams != 3
6303 || (cBits != 16 && cBits != 24 && cBits != 32))
6304 {
6305 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6306 return VERR_VGA_INVALID_CUSTOM_MODE;
6307 }
6308 cbPitch = calc_line_pitch(cBits, cx);
6309# ifdef VRAM_SIZE_FIX
6310 if (cy * cbPitch >= pThis->vram_size)
6311 {
6312 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",
6313 cx, cy, cBits, pThis->vram_size / _1M));
6314 return VERR_VGA_INVALID_CUSTOM_MODE;
6315 }
6316# endif /* VRAM_SIZE_FIX defined */
6317 MMR3HeapFree(pszExtraData);
6318
6319 /* Use defaults from max@bpp mode. */
6320 switch (cBits)
6321 {
6322 case 16:
6323 u16DefMode = VBE_VESA_MODE_1024X768X565;
6324 break;
6325
6326 case 24:
6327 u16DefMode = VBE_VESA_MODE_1024X768X888;
6328 break;
6329
6330 case 32:
6331 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6332 break;
6333
6334 default: /* gcc, shut up! */
6335 AssertMsgFailed(("gone postal!\n"));
6336 continue;
6337 }
6338
6339 /* mode_info_list is not terminated */
6340 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6341 pDefMode++;
6342 Assert(j < MODE_INFO_SIZE);
6343
6344 *pCurMode = *pDefMode;
6345 pCurMode->mode = u16CurMode++;
6346
6347 /* adjust defaults */
6348 pCurMode->info.XResolution = cx;
6349 pCurMode->info.YResolution = cy;
6350 pCurMode->info.BytesPerScanLine = cbPitch;
6351 pCurMode->info.LinBytesPerScanLine = cbPitch;
6352 vgaAdjustModeInfo(pThis, pCurMode);
6353
6354 /* commit it */
6355 pCurMode++;
6356 }
6357 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6358 {
6359 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6360 return rc;
6361 }
6362 } /* foreach custom mode key */
6363 }
6364
6365 /*
6366 * Add the "End of list" mode.
6367 */
6368 memset(pCurMode, 0, sizeof(*pCurMode));
6369 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6370
6371 /*
6372 * Register I/O Port for the VBE BIOS Extra Data.
6373 */
6374 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6375 if (RT_FAILURE(rc))
6376 return rc;
6377#endif /* VBE_NEW_DYN_LIST */
6378
6379 /*
6380 * Register I/O Port for the BIOS Logo.
6381 */
6382 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6383 if (RT_FAILURE(rc))
6384 return rc;
6385
6386 /*
6387 * Register debugger info callbacks.
6388 */
6389 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6390 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6391 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6392 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6393 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6394 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6395 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6396 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6397
6398 /*
6399 * Construct the logo header.
6400 */
6401 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6402
6403 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6404 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6405 LogoHdr.fu8FadeIn = 1;
6406 else if (RT_FAILURE(rc))
6407 return PDMDEV_SET_ERROR(pDevIns, rc,
6408 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6409
6410 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6411 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6412 LogoHdr.fu8FadeOut = 1;
6413 else if (RT_FAILURE(rc))
6414 return PDMDEV_SET_ERROR(pDevIns, rc,
6415 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6416
6417 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6418 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6419 LogoHdr.u16LogoMillies = 0;
6420 else if (RT_FAILURE(rc))
6421 return PDMDEV_SET_ERROR(pDevIns, rc,
6422 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6423
6424 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6425 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6426 LogoHdr.fu8ShowBootMenu = 0;
6427 else if (RT_FAILURE(rc))
6428 return PDMDEV_SET_ERROR(pDevIns, rc,
6429 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6430
6431#if defined(DEBUG) && !defined(DEBUG_sunlover)
6432 /* Disable the logo abd menu if all default settings. */
6433 if ( LogoHdr.fu8FadeIn
6434 && LogoHdr.fu8FadeOut
6435 && LogoHdr.u16LogoMillies == 0
6436 && LogoHdr.fu8ShowBootMenu == 2)
6437 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.fu8ShowBootMenu = 0;
6438#endif
6439
6440 /* Delay the logo a little bit */
6441 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6442 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6443
6444 /*
6445 * Get the Logo file name.
6446 */
6447 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6448 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6449 pThis->pszLogoFile = NULL;
6450 else if (RT_FAILURE(rc))
6451 return PDMDEV_SET_ERROR(pDevIns, rc,
6452 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6453 else if (!*pThis->pszLogoFile)
6454 {
6455 MMR3HeapFree(pThis->pszLogoFile);
6456 pThis->pszLogoFile = NULL;
6457 }
6458
6459 /*
6460 * Determine the logo size, open any specified logo file in the process.
6461 */
6462 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6463 RTFILE FileLogo = NIL_RTFILE;
6464 if (pThis->pszLogoFile)
6465 {
6466 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6467 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6468 if (RT_SUCCESS(rc))
6469 {
6470 uint64_t cbFile;
6471 rc = RTFileGetSize(FileLogo, &cbFile);
6472 if (RT_SUCCESS(rc))
6473 {
6474 if (cbFile > 0 && cbFile < 32*_1M)
6475 LogoHdr.cbLogo = (uint32_t)cbFile;
6476 else
6477 rc = VERR_TOO_MUCH_DATA;
6478 }
6479 }
6480 if (RT_FAILURE(rc))
6481 {
6482 /*
6483 * Ignore failure and fall back to the default logo.
6484 */
6485 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6486 if (FileLogo != NIL_RTFILE)
6487 RTFileClose(FileLogo);
6488 FileLogo = NIL_RTFILE;
6489 MMR3HeapFree(pThis->pszLogoFile);
6490 pThis->pszLogoFile = NULL;
6491 }
6492 }
6493
6494 /*
6495 * Disable graphic splash screen if it doesn't fit into VRAM.
6496 */
6497 if (pThis->vram_size < LOGO_MAX_SIZE)
6498 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6499
6500 /*
6501 * Allocate buffer for the logo data.
6502 * RT_MAX() is applied to let us fall back to default logo on read failure.
6503 */
6504 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6505 pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6506 if (pThis->pu8Logo)
6507 {
6508 /*
6509 * Write the logo header.
6510 */
6511 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
6512 *pLogoHdr = LogoHdr;
6513
6514 /*
6515 * Write the logo bitmap.
6516 */
6517 if (pThis->pszLogoFile)
6518 {
6519 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6520 if (RT_FAILURE(rc))
6521 {
6522 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", LogoHdr.cbLogo, rc));
6523 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6524 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6525 }
6526 }
6527 else
6528 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6529
6530 rc = vbeParseBitmap(pThis);
6531 if (RT_FAILURE(rc))
6532 {
6533 AssertMsgFailed(("vbeParseBitmap() -> %Rrc\n", rc));
6534 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6535 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6536 }
6537
6538 rc = vbeParseBitmap(pThis);
6539 if (RT_FAILURE(rc))
6540 AssertReleaseMsgFailed(("Internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6541
6542 rc = VINF_SUCCESS;
6543 }
6544 else
6545 rc = VERR_NO_MEMORY;
6546
6547 /*
6548 * Cleanup.
6549 */
6550 if (FileLogo != NIL_RTFILE)
6551 RTFileClose(FileLogo);
6552
6553#ifdef VBOX_WITH_HGSMI
6554 VBVAInit (pThis);
6555#endif /* VBOX_WITH_HGSMI */
6556
6557#ifdef VBOX_WITH_VDMA
6558 if(rc == VINF_SUCCESS)
6559 {
6560 rc = vboxVDMAConstruct(pThis, 1024);
6561 AssertRC(rc);
6562 }
6563#endif
6564 /*
6565 * Statistics.
6566 */
6567 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6568 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6569 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6570 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6571 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6572 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
6573
6574 /* Init latched access mask. */
6575 pThis->uMaskLatchAccess = 0x3ff;
6576 return rc;
6577}
6578
6579
6580/**
6581 * The device registration structure.
6582 */
6583const PDMDEVREG g_DeviceVga =
6584{
6585 /* u32Version */
6586 PDM_DEVREG_VERSION,
6587 /* szName */
6588 "vga",
6589 /* szRCMod */
6590 "VBoxDDGC.gc",
6591 /* szR0Mod */
6592 "VBoxDDR0.r0",
6593 /* pszDescription */
6594 "VGA Adaptor with VESA extensions.",
6595 /* fFlags */
6596 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6597 /* fClass */
6598 PDM_DEVREG_CLASS_GRAPHICS,
6599 /* cMaxInstances */
6600 1,
6601 /* cbInstance */
6602 sizeof(VGASTATE),
6603 /* pfnConstruct */
6604 vgaR3Construct,
6605 /* pfnDestruct */
6606 vgaR3Destruct,
6607 /* pfnRelocate */
6608 vgaR3Relocate,
6609 /* pfnIOCtl */
6610 NULL,
6611 /* pfnPowerOn */
6612 NULL,
6613 /* pfnReset */
6614 vgaR3Reset,
6615 /* pfnSuspend */
6616 NULL,
6617 /* pfnResume */
6618 NULL,
6619 /* pfnAttach */
6620 vgaAttach,
6621 /* pfnDetach */
6622 vgaDetach,
6623 /* pfnQueryInterface */
6624 NULL,
6625 /* pfnInitComplete */
6626 NULL,
6627 /* pfnPowerOff */
6628 NULL,
6629 /* pfnSoftReset */
6630 NULL,
6631 /* u32VersionEnd */
6632 PDM_DEVREG_VERSION
6633};
6634
6635#endif /* !IN_RING3 */
6636#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
6637
6638/*
6639 * Local Variables:
6640 * nuke-trailing-whitespace-p:nil
6641 * End:
6642 */
Note: See TracBrowser for help on using the repository browser.

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