VirtualBox

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

Last change on this file since 57064 was 56969, checked in by vboxsync, 9 years ago

DevVGA: Added markers to the saved state that separates various parts so we can more easily pin down load troubles when reading the log file.

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