VirtualBox

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

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

VGA: Bring GCPhysVRAM into the 21st century.

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