VirtualBox

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

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

DevVGA.cpp: Fixed warnings on windows.

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

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