VirtualBox

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

Last change on this file since 37841 was 37770, checked in by vboxsync, 14 years ago

vga: workaround invalid vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]: re-calculate its value on saved state restore

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

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