VirtualBox

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

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

s/VMMGC.gc/VMMRC.rc/g s/VBoxDDGC.gc/VBoxDDRC.rc/g s/VBoxDD2GC.gc/VBoxDD2RC.rc/g

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 236.4 KB
Line 
1/* $Id: DevVGA.cpp 56284 2015-06-09 10:46:34Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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.uHeight == VMSVGA_VAL_UNINITIALIZED
2229 || pThis->svga.uBpp == VMSVGA_VAL_UNINITIALIZED)
2230 {
2231 /* Intermediate state; skip redraws. */
2232 return VINF_SUCCESS;
2233 }
2234
2235 width = pThis->svga.uWidth;
2236 height = pThis->svga.uHeight;
2237
2238 disp_width = width;
2239
2240 switch(pThis->svga.uBpp) {
2241 default:
2242 case 0:
2243 case 8:
2244 AssertFailed();
2245 return VERR_NOT_IMPLEMENTED;
2246 case 15:
2247 v = VGA_DRAW_LINE15;
2248 bits = 16;
2249 break;
2250 case 16:
2251 v = VGA_DRAW_LINE16;
2252 bits = 16;
2253 break;
2254 case 24:
2255 v = VGA_DRAW_LINE24;
2256 bits = 24;
2257 break;
2258 case 32:
2259 v = VGA_DRAW_LINE32;
2260 bits = 32;
2261 break;
2262 }
2263 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pDrv->cBits)];
2264
2265 if (pThis->cursor_invalidate)
2266 pThis->cursor_invalidate(pThis);
2267
2268 addr1 = 0; /* always start at the beginning of the framebuffer */
2269 bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
2270 y_start = -1;
2271 page_min = 0x7fffffff;
2272 page_max = -1;
2273 d = pDrv->pu8Data;
2274 linesize = pDrv->cbScanline;
2275
2276 for(y = 0; y < height; y++)
2277 {
2278 addr = addr1 + y * bwidth;
2279
2280 page0 = addr & ~PAGE_OFFSET_MASK;
2281 page1 = (addr + bwidth - 1) & ~PAGE_OFFSET_MASK;
2282 bool update = full_update | vga_is_dirty(pThis, page0) | vga_is_dirty(pThis, page1);
2283 if (page1 - page0 > PAGE_SIZE)
2284 /* if wide line, can use another page */
2285 update |= vga_is_dirty(pThis, page0 + PAGE_SIZE);
2286 /* explicit invalidation for the hardware cursor */
2287 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2288 if (update)
2289 {
2290 if (y_start < 0)
2291 y_start = y;
2292 if (page0 < page_min)
2293 page_min = page0;
2294 if (page1 > page_max)
2295 page_max = page1;
2296 if (pThis->fRenderVRAM)
2297 vga_draw_line(pThis, d, pThis->CTX_SUFF(vram_ptr) + addr, width);
2298 if (pThis->cursor_draw_line)
2299 pThis->cursor_draw_line(pThis, d, y);
2300 } else
2301 {
2302 if (y_start >= 0)
2303 {
2304 /* flush to display */
2305 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, y_start, disp_width, y - y_start));
2306 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2307 y_start = -1;
2308 }
2309 }
2310 d += linesize;
2311 }
2312 if (y_start >= 0)
2313 {
2314 /* flush to display */
2315 Log(("Flush to display (%d,%d)(%d,%d)\n", 0, y_start, disp_width, y - y_start));
2316 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2317 }
2318 /* reset modified pages */
2319 if (page_max != -1 && reset_dirty)
2320 vga_reset_dirty(pThis, page_min, page_max + PAGE_SIZE);
2321 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2322 return VINF_SUCCESS;
2323}
2324#endif /* VBOX_WITH_VMSVGA */
2325
2326/*
2327 * graphic modes
2328 */
2329static int vga_draw_graphic(PVGASTATE pThis, bool full_update, bool fFailOnResize, bool reset_dirty,
2330 PDMIDISPLAYCONNECTOR *pDrv)
2331{
2332 int y1, y2, y, page_min, page_max, linesize, y_start, double_scan;
2333 int width, height, shift_control, line_offset, page0, page1, bwidth, bits;
2334 int disp_width, multi_run;
2335 uint8_t *d;
2336 uint32_t v, addr1, addr;
2337 vga_draw_line_func *vga_draw_line;
2338
2339 bool offsets_changed = update_basic_params(pThis);
2340
2341 full_update |= offsets_changed;
2342
2343 pThis->get_resolution(pThis, &width, &height);
2344 disp_width = width;
2345
2346 shift_control = (pThis->gr[0x05] >> 5) & 3;
2347 double_scan = (pThis->cr[0x09] >> 7);
2348 multi_run = double_scan;
2349 if (shift_control != pThis->shift_control ||
2350 double_scan != pThis->double_scan) {
2351 full_update = true;
2352 pThis->shift_control = shift_control;
2353 pThis->double_scan = double_scan;
2354 }
2355
2356 if (shift_control == 0) {
2357 full_update |= update_palette16(pThis);
2358 if (pThis->sr[0x01] & 8) {
2359 v = VGA_DRAW_LINE4D2;
2360 disp_width <<= 1;
2361 } else {
2362 v = VGA_DRAW_LINE4;
2363 }
2364 bits = 4;
2365 } else if (shift_control == 1) {
2366 full_update |= update_palette16(pThis);
2367 if (pThis->sr[0x01] & 8) {
2368 v = VGA_DRAW_LINE2D2;
2369 disp_width <<= 1;
2370 } else {
2371 v = VGA_DRAW_LINE2;
2372 }
2373 bits = 4;
2374 } else {
2375 switch(pThis->get_bpp(pThis)) {
2376 default:
2377 case 0:
2378 full_update |= update_palette256(pThis);
2379 v = VGA_DRAW_LINE8D2;
2380 bits = 4;
2381 break;
2382 case 8:
2383 full_update |= update_palette256(pThis);
2384 v = VGA_DRAW_LINE8;
2385 bits = 8;
2386 break;
2387 case 15:
2388 v = VGA_DRAW_LINE15;
2389 bits = 16;
2390 break;
2391 case 16:
2392 v = VGA_DRAW_LINE16;
2393 bits = 16;
2394 break;
2395 case 24:
2396 v = VGA_DRAW_LINE24;
2397 bits = 24;
2398 break;
2399 case 32:
2400 v = VGA_DRAW_LINE32;
2401 bits = 32;
2402 break;
2403 }
2404 }
2405 if ( disp_width != (int)pThis->last_width
2406 || height != (int)pThis->last_height
2407 || pThis->get_bpp(pThis) != (int)pThis->last_bpp
2408 || (offsets_changed && !pThis->fRenderVRAM))
2409 {
2410 if (fFailOnResize)
2411 {
2412 /* The caller does not want to call the pfnResize. */
2413 return VERR_TRY_AGAIN;
2414 }
2415 int rc = vga_resize_graphic(pThis, disp_width, height, pDrv);
2416 if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
2417 return rc;
2418 full_update = true;
2419 }
2420
2421 if (pThis->fRenderVRAM)
2422 {
2423 /* Do not update the destination buffer if it is not big enough.
2424 * Can happen if the resize request was ignored by the driver.
2425 */
2426 if ( pDrv->cx != (uint32_t)width
2427 || pDrv->cy != (uint32_t)height)
2428 {
2429 LogRel(("Framebuffer mismatch: vga %dx%d, drv %dx%d!!!\n",
2430 width, height,
2431 pDrv->cx, pDrv->cy));
2432 return VINF_SUCCESS;
2433 }
2434 }
2435
2436 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pDrv->cBits)];
2437
2438 if (pThis->cursor_invalidate)
2439 pThis->cursor_invalidate(pThis);
2440
2441 line_offset = pThis->line_offset;
2442#if 0
2443 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",
2444 width, height, v, line_offset, pThis->cr[9], pThis->cr[0x17], pThis->line_compare, pThis->sr[0x01]));
2445#endif
2446 addr1 = (pThis->start_addr * 4);
2447 bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
2448 y_start = -1;
2449 page_min = 0x7fffffff;
2450 page_max = -1;
2451 d = pDrv->pu8Data;
2452 linesize = pDrv->cbScanline;
2453
2454 y1 = 0;
2455 y2 = pThis->cr[0x09] & 0x1F; /* starting row scan count */
2456 for(y = 0; y < height; y++) {
2457 addr = addr1;
2458 /* CGA/MDA compatibility. Note that these addresses are all
2459 * shifted left by two compared to VGA specs.
2460 */
2461 if (!(pThis->cr[0x17] & 1)) {
2462 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2463 }
2464 if (!(pThis->cr[0x17] & 2)) {
2465 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2466 }
2467 page0 = addr & ~PAGE_OFFSET_MASK;
2468 page1 = (addr + bwidth - 1) & ~PAGE_OFFSET_MASK;
2469 bool update = full_update | vga_is_dirty(pThis, page0) | vga_is_dirty(pThis, page1);
2470 if (page1 - page0 > PAGE_SIZE) {
2471 /* if wide line, can use another page */
2472 update |= vga_is_dirty(pThis, page0 + PAGE_SIZE);
2473 }
2474 /* explicit invalidation for the hardware cursor */
2475 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2476 if (update) {
2477 if (y_start < 0)
2478 y_start = y;
2479 if (page0 < page_min)
2480 page_min = page0;
2481 if (page1 > page_max)
2482 page_max = page1;
2483 if (pThis->fRenderVRAM)
2484 vga_draw_line(pThis, d, pThis->CTX_SUFF(vram_ptr) + addr, width);
2485 if (pThis->cursor_draw_line)
2486 pThis->cursor_draw_line(pThis, d, y);
2487 } else {
2488 if (y_start >= 0) {
2489 /* flush to display */
2490 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2491 y_start = -1;
2492 }
2493 }
2494 if (!multi_run) {
2495 y1++;
2496 multi_run = double_scan;
2497
2498 if (y2 == 0) {
2499 y2 = pThis->cr[0x09] & 0x1F;
2500 addr1 += line_offset;
2501 } else {
2502 --y2;
2503 }
2504 } else {
2505 multi_run--;
2506 }
2507 /* line compare acts on the displayed lines */
2508 if ((uint32_t)y == pThis->line_compare)
2509 addr1 = 0;
2510 d += linesize;
2511 }
2512 if (y_start >= 0) {
2513 /* flush to display */
2514 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2515 }
2516 /* reset modified pages */
2517 if (page_max != -1 && reset_dirty) {
2518 vga_reset_dirty(pThis, page_min, page_max + PAGE_SIZE);
2519 }
2520 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2521 return VINF_SUCCESS;
2522}
2523
2524static void vga_draw_blank(PVGASTATE pThis, int full_update, PDMIDISPLAYCONNECTOR *pDrv)
2525{
2526 int i, w, val;
2527 uint8_t *d;
2528 uint32_t cbScanline = pDrv->cbScanline;
2529
2530 if (pDrv->pu8Data == pThis->vram_ptrR3) /* Do not clear the VRAM itself. */
2531 return;
2532 if (!full_update)
2533 return;
2534 if (pThis->last_scr_width <= 0 || pThis->last_scr_height <= 0)
2535 return;
2536 if (pDrv->cBits == 8)
2537 val = pThis->rgb_to_pixel(0, 0, 0);
2538 else
2539 val = 0;
2540 w = pThis->last_scr_width * ((pDrv->cBits + 7) >> 3);
2541 d = pDrv->pu8Data;
2542 if (pThis->fRenderVRAM)
2543 {
2544 for(i = 0; i < (int)pThis->last_scr_height; i++) {
2545 memset(d, val, w);
2546 d += cbScanline;
2547 }
2548 }
2549 pDrv->pfnUpdateRect(pDrv, 0, 0, pThis->last_scr_width, pThis->last_scr_height);
2550}
2551
2552static DECLCALLBACK(void) voidUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2553{
2554 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(cx); NOREF(cy);
2555}
2556
2557
2558#define GMODE_TEXT 0
2559#define GMODE_GRAPH 1
2560#define GMODE_BLANK 2
2561#ifdef VBOX_WITH_VMSVGA
2562#define GMODE_SVGA 3
2563#endif
2564
2565static int vga_update_display(PVGASTATE pThis, bool fUpdateAll, bool fFailOnResize, bool reset_dirty,
2566 PDMIDISPLAYCONNECTOR *pDrv, int32_t *pcur_graphic_mode)
2567{
2568 int rc = VINF_SUCCESS;
2569 int graphic_mode;
2570
2571 if (pDrv->cBits == 0) {
2572 /* nothing to do */
2573 } else {
2574 switch(pDrv->cBits) {
2575 case 8:
2576 pThis->rgb_to_pixel = rgb_to_pixel8_dup;
2577 break;
2578 case 15:
2579 pThis->rgb_to_pixel = rgb_to_pixel15_dup;
2580 break;
2581 default:
2582 case 16:
2583 pThis->rgb_to_pixel = rgb_to_pixel16_dup;
2584 break;
2585 case 32:
2586 pThis->rgb_to_pixel = rgb_to_pixel32_dup;
2587 break;
2588 }
2589
2590 if (fUpdateAll) {
2591 /* A full update is requested. Special processing for a "blank" mode is required, because
2592 * the request must process all pending resolution changes.
2593 *
2594 * Appropriate vga_draw_graphic or vga_draw_text function, which checks the resolution change,
2595 * must be called even if the screen has been blanked, but then the function should do no actual
2596 * screen update. To do this, pfnUpdateRect is replaced with a nop.
2597 */
2598 typedef DECLCALLBACK(void) FNUPDATERECT(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy);
2599 typedef FNUPDATERECT *PFNUPDATERECT;
2600
2601 PFNUPDATERECT pfnUpdateRect = NULL;
2602
2603 /* Detect the "screen blank" conditions. */
2604 int fBlank = 0;
2605 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2606 fBlank = 1;
2607 }
2608
2609 if (fBlank) {
2610 /* Provide a void pfnUpdateRect callback. */
2611 if (pDrv) {
2612 pfnUpdateRect = pDrv->pfnUpdateRect;
2613 pDrv->pfnUpdateRect = voidUpdateRect;
2614 }
2615 }
2616
2617 /* Do a complete redraw, which will pick up a new screen resolution. */
2618#ifdef VBOX_WITH_VMSVGA
2619 if (pThis->svga.fEnabled) {
2620 *pcur_graphic_mode = GMODE_SVGA;
2621 rc = vmsvga_draw_graphic(pThis, 1, fFailOnResize, reset_dirty, pDrv);
2622 }
2623 else
2624#endif
2625 if (pThis->gr[6] & 1) {
2626 *pcur_graphic_mode = GMODE_GRAPH;
2627 rc = vga_draw_graphic(pThis, 1, fFailOnResize, reset_dirty, pDrv);
2628 } else {
2629 *pcur_graphic_mode = GMODE_TEXT;
2630 rc = vga_draw_text(pThis, 1, fFailOnResize, reset_dirty, pDrv);
2631 }
2632
2633 if (fBlank) {
2634 /* Set the current mode and restore the callback. */
2635 *pcur_graphic_mode = GMODE_BLANK;
2636 if (pDrv) {
2637 pDrv->pfnUpdateRect = pfnUpdateRect;
2638 }
2639 }
2640 return rc;
2641 }
2642
2643#ifdef VBOX_WITH_VMSVGA
2644 if (pThis->svga.fEnabled) {
2645 graphic_mode = GMODE_SVGA;
2646 }
2647 else
2648#endif
2649 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2650 graphic_mode = GMODE_BLANK;
2651 } else {
2652 graphic_mode = pThis->gr[6] & 1;
2653 }
2654 bool full_update = graphic_mode != *pcur_graphic_mode;
2655 if (full_update) {
2656 *pcur_graphic_mode = graphic_mode;
2657 }
2658 switch(graphic_mode) {
2659 case GMODE_TEXT:
2660 rc = vga_draw_text(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2661 break;
2662 case GMODE_GRAPH:
2663 rc = vga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2664 break;
2665#ifdef VBOX_WITH_VMSVGA
2666 case GMODE_SVGA:
2667 rc = vmsvga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2668 break;
2669#endif
2670 case GMODE_BLANK:
2671 default:
2672 vga_draw_blank(pThis, full_update, pDrv);
2673 break;
2674 }
2675 }
2676 return rc;
2677}
2678
2679static void vga_save(PSSMHANDLE pSSM, PVGASTATE pThis)
2680{
2681 int i;
2682
2683 SSMR3PutU32(pSSM, pThis->latch);
2684 SSMR3PutU8(pSSM, pThis->sr_index);
2685 SSMR3PutMem(pSSM, pThis->sr, 8);
2686 SSMR3PutU8(pSSM, pThis->gr_index);
2687 SSMR3PutMem(pSSM, pThis->gr, 16);
2688 SSMR3PutU8(pSSM, pThis->ar_index);
2689 SSMR3PutMem(pSSM, pThis->ar, 21);
2690 SSMR3PutU32(pSSM, pThis->ar_flip_flop);
2691 SSMR3PutU8(pSSM, pThis->cr_index);
2692 SSMR3PutMem(pSSM, pThis->cr, 256);
2693 SSMR3PutU8(pSSM, pThis->msr);
2694 SSMR3PutU8(pSSM, pThis->fcr);
2695 SSMR3PutU8(pSSM, pThis->st00);
2696 SSMR3PutU8(pSSM, pThis->st01);
2697
2698 SSMR3PutU8(pSSM, pThis->dac_state);
2699 SSMR3PutU8(pSSM, pThis->dac_sub_index);
2700 SSMR3PutU8(pSSM, pThis->dac_read_index);
2701 SSMR3PutU8(pSSM, pThis->dac_write_index);
2702 SSMR3PutMem(pSSM, pThis->dac_cache, 3);
2703 SSMR3PutMem(pSSM, pThis->palette, 768);
2704
2705 SSMR3PutU32(pSSM, pThis->bank_offset);
2706#ifdef CONFIG_BOCHS_VBE
2707 SSMR3PutU8(pSSM, 1);
2708 SSMR3PutU16(pSSM, pThis->vbe_index);
2709 for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
2710 SSMR3PutU16(pSSM, pThis->vbe_regs[i]);
2711 SSMR3PutU32(pSSM, pThis->vbe_start_addr);
2712 SSMR3PutU32(pSSM, pThis->vbe_line_offset);
2713#else
2714 SSMR3PutU8(pSSM, 0);
2715#endif
2716}
2717
2718static int vga_load(PSSMHANDLE pSSM, PVGASTATE pThis, int version_id)
2719{
2720 int is_vbe, i;
2721 uint32_t u32Dummy;
2722 uint8_t u8;
2723
2724 SSMR3GetU32(pSSM, &pThis->latch);
2725 SSMR3GetU8(pSSM, &pThis->sr_index);
2726 SSMR3GetMem(pSSM, pThis->sr, 8);
2727 SSMR3GetU8(pSSM, &pThis->gr_index);
2728 SSMR3GetMem(pSSM, pThis->gr, 16);
2729 SSMR3GetU8(pSSM, &pThis->ar_index);
2730 SSMR3GetMem(pSSM, pThis->ar, 21);
2731 SSMR3GetU32(pSSM, (uint32_t *)&pThis->ar_flip_flop);
2732 SSMR3GetU8(pSSM, &pThis->cr_index);
2733 SSMR3GetMem(pSSM, pThis->cr, 256);
2734 SSMR3GetU8(pSSM, &pThis->msr);
2735 SSMR3GetU8(pSSM, &pThis->fcr);
2736 SSMR3GetU8(pSSM, &pThis->st00);
2737 SSMR3GetU8(pSSM, &pThis->st01);
2738
2739 SSMR3GetU8(pSSM, &pThis->dac_state);
2740 SSMR3GetU8(pSSM, &pThis->dac_sub_index);
2741 SSMR3GetU8(pSSM, &pThis->dac_read_index);
2742 SSMR3GetU8(pSSM, &pThis->dac_write_index);
2743 SSMR3GetMem(pSSM, pThis->dac_cache, 3);
2744 SSMR3GetMem(pSSM, pThis->palette, 768);
2745
2746 SSMR3GetU32(pSSM, (uint32_t *)&pThis->bank_offset);
2747 SSMR3GetU8(pSSM, &u8);
2748 is_vbe = !!u8;
2749#ifdef CONFIG_BOCHS_VBE
2750 if (!is_vbe)
2751 {
2752 Log(("vga_load: !is_vbe !!\n"));
2753 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2754 }
2755 SSMR3GetU16(pSSM, &pThis->vbe_index);
2756 for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
2757 SSMR3GetU16(pSSM, &pThis->vbe_regs[i]);
2758 if (version_id <= VGA_SAVEDSTATE_VERSION_INV_VHEIGHT)
2759 recalculate_data(pThis, false); /* <- re-calculate the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] since it might be invalid */
2760 SSMR3GetU32(pSSM, &pThis->vbe_start_addr);
2761 SSMR3GetU32(pSSM, &pThis->vbe_line_offset);
2762 if (version_id < 2)
2763 SSMR3GetU32(pSSM, &u32Dummy);
2764 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
2765#else
2766 if (is_vbe)
2767 {
2768 Log(("vga_load: is_vbe !!\n"));
2769 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2770 }
2771#endif
2772
2773 /* force refresh */
2774 pThis->graphic_mode = -1;
2775 return 0;
2776}
2777
2778/* see vgaR3Construct */
2779static void vga_init_expand(void)
2780{
2781 int i, j, v, b;
2782
2783 for(i = 0;i < 256; i++) {
2784 v = 0;
2785 for(j = 0; j < 8; j++) {
2786 v |= ((i >> j) & 1) << (j * 4);
2787 }
2788 expand4[i] = v;
2789
2790 v = 0;
2791 for(j = 0; j < 4; j++) {
2792 v |= ((i >> (2 * j)) & 3) << (j * 4);
2793 }
2794 expand2[i] = v;
2795 }
2796 for(i = 0; i < 16; i++) {
2797 v = 0;
2798 for(j = 0; j < 4; j++) {
2799 b = ((i >> j) & 1);
2800 v |= b << (2 * j);
2801 v |= b << (2 * j + 1);
2802 }
2803 expand4to8[i] = v;
2804 }
2805}
2806
2807#endif /* !IN_RING0 */
2808
2809
2810
2811/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2812
2813/**
2814 * @callback_method_impl{FNIOMIOPORTOUT,Generic VGA OUT dispatcher.}
2815 */
2816PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2817{
2818 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2819 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2820
2821 NOREF(pvUser);
2822 if (cb == 1)
2823 vga_ioport_write(pThis, Port, u32);
2824 else if (cb == 2)
2825 {
2826 vga_ioport_write(pThis, Port, u32 & 0xff);
2827 vga_ioport_write(pThis, Port + 1, u32 >> 8);
2828 }
2829 return VINF_SUCCESS;
2830}
2831
2832
2833/**
2834 * @callback_method_impl{FNIOMIOPORTOUT,Generic VGA IN dispatcher.}
2835 */
2836PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2837{
2838 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2839 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2840 NOREF(pvUser);
2841
2842 int rc = VINF_SUCCESS;
2843 if (cb == 1)
2844 *pu32 = vga_ioport_read(pThis, Port);
2845 else if (cb == 2)
2846 *pu32 = vga_ioport_read(pThis, Port)
2847 | (vga_ioport_read(pThis, Port + 1) << 8);
2848 else
2849 rc = VERR_IOM_IOPORT_UNUSED;
2850 return rc;
2851}
2852
2853
2854/**
2855 * @callback_method_impl{FNIOMIOPORTOUT,VBE Data Port OUT handler.}
2856 */
2857PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2858{
2859 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2860 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2861
2862 NOREF(pvUser);
2863
2864#ifndef IN_RING3
2865 /*
2866 * This has to be done on the host in order to execute the connector callbacks.
2867 */
2868 if ( pThis->vbe_index == VBE_DISPI_INDEX_ENABLE
2869 || pThis->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
2870 {
2871 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2872 return VINF_IOM_R3_IOPORT_WRITE;
2873 }
2874#endif
2875#ifdef VBE_BYTEWISE_IO
2876 if (cb == 1)
2877 {
2878 if (!pThis->fWriteVBEData)
2879 {
2880 if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
2881 && (u32 & VBE_DISPI_ENABLED))
2882 {
2883 pThis->fWriteVBEData = false;
2884 return vbe_ioport_write_data(pThis, Port, u32 & 0xFF);
2885 }
2886
2887 pThis->cbWriteVBEData = u32 & 0xFF;
2888 pThis->fWriteVBEData = true;
2889 return VINF_SUCCESS;
2890 }
2891
2892 u32 = (pThis->cbWriteVBEData << 8) | (u32 & 0xFF);
2893 pThis->fWriteVBEData = false;
2894 cb = 2;
2895 }
2896#endif
2897 if (cb == 2 || cb == 4)
2898 {
2899//#ifdef IN_RC
2900// /*
2901// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2902// * Since we're not mapping the entire framebuffer any longer that
2903// * has to be done on the host.
2904// */
2905// if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
2906// && (u32 & VBE_DISPI_ENABLED))
2907// {
2908// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2909// return VINF_IOM_R3_IOPORT_WRITE;
2910// }
2911//#endif
2912 return vbe_ioport_write_data(pThis, Port, u32);
2913 }
2914 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2915
2916 return VINF_SUCCESS;
2917}
2918
2919
2920/**
2921 * @callback_method_impl{FNIOMIOPORTOUT,VBE Index Port OUT handler.}
2922 */
2923PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2924{
2925 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
2926 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2927
2928#ifdef VBE_BYTEWISE_IO
2929 if (cb == 1)
2930 {
2931 if (!pThis->fWriteVBEIndex)
2932 {
2933 pThis->cbWriteVBEIndex = u32 & 0x00FF;
2934 pThis->fWriteVBEIndex = true;
2935 return VINF_SUCCESS;
2936 }
2937 pThis->fWriteVBEIndex = false;
2938 vbe_ioport_write_index(pThis, Port, (pThis->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2939 return VINF_SUCCESS;
2940 }
2941#endif
2942
2943 if (cb == 2)
2944 vbe_ioport_write_index(pThis, Port, u32);
2945 else
2946 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2947 return VINF_SUCCESS;
2948}
2949
2950
2951/**
2952 * @callback_method_impl{FNIOMIOPORTOUT,VBE Data Port IN handler.}
2953 */
2954PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2955{
2956 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
2957 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2958
2959
2960#ifdef VBE_BYTEWISE_IO
2961 if (cb == 1)
2962 {
2963 if (!pThis->fReadVBEData)
2964 {
2965 *pu32 = (vbe_ioport_read_data(pThis, Port) >> 8) & 0xFF;
2966 pThis->fReadVBEData = true;
2967 return VINF_SUCCESS;
2968 }
2969 *pu32 = vbe_ioport_read_data(pThis, Port) & 0xFF;
2970 pThis->fReadVBEData = false;
2971 return VINF_SUCCESS;
2972 }
2973#endif
2974 if (cb == 2)
2975 {
2976 *pu32 = vbe_ioport_read_data(pThis, Port);
2977 return VINF_SUCCESS;
2978 }
2979 if (cb == 4)
2980 {
2981 /* Quick hack for getting the vram size. */
2982 *pu32 = pThis->vram_size;
2983 return VINF_SUCCESS;
2984 }
2985 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2986 return VERR_IOM_IOPORT_UNUSED;
2987}
2988
2989
2990/**
2991 * @callback_method_impl{FNIOMIOPORTOUT,VBE Index Port IN handler.}
2992 */
2993PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2994{
2995 NOREF(pvUser);
2996 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2997 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2998
2999#ifdef VBE_BYTEWISE_IO
3000 if (cb == 1)
3001 {
3002 if (!pThis->fReadVBEIndex)
3003 {
3004 *pu32 = (vbe_ioport_read_index(pThis, Port) >> 8) & 0xFF;
3005 pThis->fReadVBEIndex = true;
3006 return VINF_SUCCESS;
3007 }
3008 *pu32 = vbe_ioport_read_index(pThis, Port) & 0xFF;
3009 pThis->fReadVBEIndex = false;
3010 return VINF_SUCCESS;
3011 }
3012#endif
3013 if (cb == 2)
3014 {
3015 *pu32 = vbe_ioport_read_index(pThis, Port);
3016 return VINF_SUCCESS;
3017 }
3018 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
3019 return VERR_IOM_IOPORT_UNUSED;
3020}
3021
3022#ifdef VBOX_WITH_HGSMI
3023# ifdef IN_RING3
3024/**
3025 * @callback_method_impl{FNIOMIOPORTOUT,HGSMI OUT handler.}
3026 */
3027static DECLCALLBACK(int) vgaR3IOPortHGSMIWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3028{
3029 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3030 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3031 LogFlowFunc(("Port 0x%x, u32 0x%x, cb %d\n", Port, u32, cb));
3032
3033
3034 NOREF(pvUser);
3035
3036 if (cb == 4)
3037 {
3038 switch (Port)
3039 {
3040 case VGA_PORT_HGSMI_HOST: /* Host */
3041 {
3042# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
3043 if (u32 == HGSMIOFFSET_VOID)
3044 {
3045 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
3046 HGSMIClearHostGuestFlags(pThis->pHGSMI,
3047 HGSMIHOSTFLAGS_IRQ
3048# ifdef VBOX_VDMA_WITH_WATCHDOG
3049 | HGSMIHOSTFLAGS_WATCHDOG
3050# endif
3051 | HGSMIHOSTFLAGS_VSYNC
3052 );
3053 }
3054 else
3055# endif
3056 {
3057 HGSMIHostWrite(pThis->pHGSMI, u32);
3058 }
3059 break;
3060 }
3061
3062 case VGA_PORT_HGSMI_GUEST: /* Guest */
3063 HGSMIGuestWrite(pThis->pHGSMI, u32);
3064 break;
3065
3066 default:
3067# ifdef DEBUG_sunlover
3068 AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3069# endif
3070 break;
3071 }
3072 }
3073 else
3074 {
3075# ifdef DEBUG_sunlover
3076 AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3077# endif
3078 }
3079
3080 return VINF_SUCCESS;
3081}
3082
3083
3084/**
3085 * @callback_method_impl{FNIOMIOPORTOUT,HGSMI IN handler.}
3086 */
3087static DECLCALLBACK(int) vgaR3IOPortHGSMIRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3088{
3089 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3090 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3091 LogFlowFunc(("Port 0x%x, cb %d\n", Port, cb));
3092
3093 NOREF(pvUser);
3094
3095 int rc = VINF_SUCCESS;
3096 if (cb == 4)
3097 {
3098 switch (Port)
3099 {
3100 case VGA_PORT_HGSMI_HOST: /* Host */
3101 *pu32 = HGSMIHostRead(pThis->pHGSMI);
3102 break;
3103 case VGA_PORT_HGSMI_GUEST: /* Guest */
3104 *pu32 = HGSMIGuestRead(pThis->pHGSMI);
3105 break;
3106 default:
3107# ifdef DEBUG_sunlover
3108 AssertMsgFailed(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
3109# endif
3110 rc = VERR_IOM_IOPORT_UNUSED;
3111 break;
3112 }
3113 }
3114 else
3115 {
3116# ifdef DEBUG_sunlover
3117 Log(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
3118# endif
3119 rc = VERR_IOM_IOPORT_UNUSED;
3120 }
3121
3122 return rc;
3123}
3124# endif /* IN_RING3 */
3125#endif /* VBOX_WITH_HGSMI */
3126
3127
3128
3129
3130/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
3131
3132/**
3133 * @internal. For use inside VGAGCMemoryFillWrite only.
3134 * Macro for apply logical operation and bit mask.
3135 */
3136#define APPLY_LOGICAL_AND_MASK(pThis, val, bit_mask) \
3137 /* apply logical operation */ \
3138 switch (pThis->gr[3] >> 3) \
3139 { \
3140 case 0: \
3141 default: \
3142 /* nothing to do */ \
3143 break; \
3144 case 1: \
3145 /* and */ \
3146 val &= pThis->latch; \
3147 break; \
3148 case 2: \
3149 /* or */ \
3150 val |= pThis->latch; \
3151 break; \
3152 case 3: \
3153 /* xor */ \
3154 val ^= pThis->latch; \
3155 break; \
3156 } \
3157 /* apply bit mask */ \
3158 val = (val & bit_mask) | (pThis->latch & ~bit_mask)
3159
3160/**
3161 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3162 * This is the advanced version of vga_mem_writeb function.
3163 *
3164 * @returns VBox status code.
3165 * @param pThis VGA device structure
3166 * @param pvUser User argument - ignored.
3167 * @param GCPhysAddr Physical address of memory to write.
3168 * @param u32Item Data to write, up to 4 bytes.
3169 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3170 * @param cItems Number of data items to write.
3171 */
3172static int vgaInternalMMIOFill(PVGASTATE pThis, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3173{
3174 uint32_t b;
3175 uint32_t write_mask, bit_mask, set_mask;
3176 uint32_t aVal[4];
3177 unsigned i;
3178 NOREF(pvUser);
3179
3180 for (i = 0; i < cbItem; i++)
3181 {
3182 aVal[i] = u32Item & 0xff;
3183 u32Item >>= 8;
3184 }
3185
3186 /* convert to VGA memory offset */
3187 /// @todo add check for the end of region
3188 GCPhysAddr &= 0x1ffff;
3189 switch((pThis->gr[6] >> 2) & 3) {
3190 case 0:
3191 break;
3192 case 1:
3193 if (GCPhysAddr >= 0x10000)
3194 return VINF_SUCCESS;
3195 GCPhysAddr += pThis->bank_offset;
3196 break;
3197 case 2:
3198 GCPhysAddr -= 0x10000;
3199 if (GCPhysAddr >= 0x8000)
3200 return VINF_SUCCESS;
3201 break;
3202 default:
3203 case 3:
3204 GCPhysAddr -= 0x18000;
3205 if (GCPhysAddr >= 0x8000)
3206 return VINF_SUCCESS;
3207 break;
3208 }
3209
3210 if (pThis->sr[4] & 0x08) {
3211 /* chain 4 mode : simplest access */
3212 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3213
3214 while (cItems-- > 0)
3215 for (i = 0; i < cbItem; i++)
3216 {
3217 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3218 {
3219 pThis->CTX_SUFF(vram_ptr)[GCPhysAddr] = aVal[i];
3220 vga_set_dirty(pThis, GCPhysAddr);
3221 }
3222 GCPhysAddr++;
3223 }
3224 } else if (pThis->gr[5] & 0x10) {
3225 /* odd/even mode (aka text mode mapping) */
3226 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3227 while (cItems-- > 0)
3228 for (i = 0; i < cbItem; i++)
3229 {
3230 unsigned plane = (pThis->gr[4] & 2) | (GCPhysAddr & 1);
3231 if (pThis->sr[2] & (1 << plane)) {
3232 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
3233 pThis->CTX_SUFF(vram_ptr)[PhysAddr2] = aVal[i];
3234 vga_set_dirty(pThis, PhysAddr2);
3235 }
3236 GCPhysAddr++;
3237 }
3238 } else {
3239 /* standard VGA latched access */
3240 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3241
3242 switch(pThis->gr[5] & 3) {
3243 default:
3244 case 0:
3245 /* rotate */
3246 b = pThis->gr[3] & 7;
3247 bit_mask = pThis->gr[8];
3248 bit_mask |= bit_mask << 8;
3249 bit_mask |= bit_mask << 16;
3250 set_mask = mask16[pThis->gr[1]];
3251
3252 for (i = 0; i < cbItem; i++)
3253 {
3254 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3255 aVal[i] |= aVal[i] << 8;
3256 aVal[i] |= aVal[i] << 16;
3257
3258 /* apply set/reset mask */
3259 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3260
3261 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3262 }
3263 break;
3264 case 1:
3265 for (i = 0; i < cbItem; i++)
3266 aVal[i] = pThis->latch;
3267 break;
3268 case 2:
3269 bit_mask = pThis->gr[8];
3270 bit_mask |= bit_mask << 8;
3271 bit_mask |= bit_mask << 16;
3272 for (i = 0; i < cbItem; i++)
3273 {
3274 aVal[i] = mask16[aVal[i] & 0x0f];
3275
3276 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3277 }
3278 break;
3279 case 3:
3280 /* rotate */
3281 b = pThis->gr[3] & 7;
3282
3283 for (i = 0; i < cbItem; i++)
3284 {
3285 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3286 bit_mask = pThis->gr[8] & aVal[i];
3287 bit_mask |= bit_mask << 8;
3288 bit_mask |= bit_mask << 16;
3289 aVal[i] = mask16[pThis->gr[0]];
3290
3291 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3292 }
3293 break;
3294 }
3295
3296 /* mask data according to sr[2] */
3297 write_mask = mask16[pThis->sr[2]];
3298
3299 /* actually write data */
3300 if (cbItem == 1)
3301 {
3302 /* The most frequently case is 1 byte I/O. */
3303 while (cItems-- > 0)
3304 {
3305 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3306 vga_set_dirty(pThis, GCPhysAddr << 2);
3307 GCPhysAddr++;
3308 }
3309 }
3310 else if (cbItem == 2)
3311 {
3312 /* The second case is 2 bytes I/O. */
3313 while (cItems-- > 0)
3314 {
3315 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3316 vga_set_dirty(pThis, GCPhysAddr << 2);
3317 GCPhysAddr++;
3318
3319 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3320 vga_set_dirty(pThis, GCPhysAddr << 2);
3321 GCPhysAddr++;
3322 }
3323 }
3324 else
3325 {
3326 /* And the rest is 4 bytes. */
3327 Assert(cbItem == 4);
3328 while (cItems-- > 0)
3329 for (i = 0; i < cbItem; i++)
3330 {
3331 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3332 vga_set_dirty(pThis, GCPhysAddr << 2);
3333 GCPhysAddr++;
3334 }
3335 }
3336 }
3337 return VINF_SUCCESS;
3338}
3339
3340
3341/**
3342 * @callback_method_impl{FNIOMMMIOFILL,
3343 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and
3344 * from the inside of VGADeviceGC.cpp. This is the advanced version of
3345 * vga_mem_writeb function.}
3346 */
3347PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3348{
3349 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3350 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3351
3352 return vgaInternalMMIOFill(pThis, pvUser, GCPhysAddr, u32Item, cbItem, cItems);
3353}
3354#undef APPLY_LOGICAL_AND_MASK
3355
3356
3357/**
3358 * @callback_method_impl{FNIOMMMIOREAD, Legacy VGA memory (0xa0000 - 0xbffff)
3359 * read hook, to be called from IOM.}
3360 */
3361PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3362{
3363 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3364 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3365 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3366 NOREF(pvUser);
3367
3368 int rc = VINF_SUCCESS;
3369 switch (cb)
3370 {
3371 case 1:
3372 *(uint8_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc);
3373 break;
3374 case 2:
3375 *(uint16_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3376 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8);
3377 break;
3378 case 4:
3379 *(uint32_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3380 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3381 | (vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3382 | (vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24);
3383 break;
3384
3385 case 8:
3386 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pThis, GCPhysAddr, &rc)
3387 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3388 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3389 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24)
3390 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 4, &rc) << 32)
3391 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 5, &rc) << 40)
3392 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 6, &rc) << 48)
3393 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 7, &rc) << 56);
3394 break;
3395
3396 default:
3397 {
3398 uint8_t *pu8Data = (uint8_t *)pv;
3399 while (cb-- > 0)
3400 {
3401 *pu8Data++ = vga_mem_readb(pThis, GCPhysAddr++, &rc);
3402 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3403 break;
3404 }
3405 }
3406 }
3407
3408 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3409 return rc;
3410}
3411
3412/**
3413 * @callback_method_impl{FNIOMMMIOWRITE, Legacy VGA memory (0xa0000 - 0xbffff)
3414 * write hook, to be called from IOM.}
3415 */
3416PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
3417{
3418 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3419 uint8_t *pu8 = (uint8_t *)pv;
3420 NOREF(pvUser);
3421 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3422 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3423
3424 int rc;
3425 switch (cb)
3426 {
3427 case 1:
3428 rc = vga_mem_writeb(pThis, GCPhysAddr, *pu8);
3429 break;
3430#if 1
3431 case 2:
3432 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3433 if (RT_LIKELY(rc == VINF_SUCCESS))
3434 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3435 break;
3436 case 4:
3437 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3438 if (RT_LIKELY(rc == VINF_SUCCESS))
3439 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3440 if (RT_LIKELY(rc == VINF_SUCCESS))
3441 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
3442 if (RT_LIKELY(rc == VINF_SUCCESS))
3443 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
3444 break;
3445 case 8:
3446 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3447 if (RT_LIKELY(rc == VINF_SUCCESS))
3448 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3449 if (RT_LIKELY(rc == VINF_SUCCESS))
3450 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
3451 if (RT_LIKELY(rc == VINF_SUCCESS))
3452 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
3453 if (RT_LIKELY(rc == VINF_SUCCESS))
3454 rc = vga_mem_writeb(pThis, GCPhysAddr + 4, pu8[4]);
3455 if (RT_LIKELY(rc == VINF_SUCCESS))
3456 rc = vga_mem_writeb(pThis, GCPhysAddr + 5, pu8[5]);
3457 if (RT_LIKELY(rc == VINF_SUCCESS))
3458 rc = vga_mem_writeb(pThis, GCPhysAddr + 6, pu8[6]);
3459 if (RT_LIKELY(rc == VINF_SUCCESS))
3460 rc = vga_mem_writeb(pThis, GCPhysAddr + 7, pu8[7]);
3461 break;
3462#else
3463 case 2:
3464 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3465 break;
3466 case 4:
3467 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3468 break;
3469 case 8:
3470 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint64_t *)pv, 8, 1);
3471 break;
3472#endif
3473 default:
3474 rc = VINF_SUCCESS;
3475 while (cb-- > 0 && rc == VINF_SUCCESS)
3476 rc = vga_mem_writeb(pThis, GCPhysAddr++, *pu8++);
3477 break;
3478
3479 }
3480 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3481 return rc;
3482}
3483
3484
3485/**
3486 * Handle LFB access.
3487 * @returns VBox status code.
3488 * @param pVM VM handle.
3489 * @param pThis VGA device instance data.
3490 * @param GCPhys The access physical address.
3491 * @param GCPtr The access virtual address (only GC).
3492 */
3493static int vgaLFBAccess(PVM pVM, PVGASTATE pThis, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3494{
3495 int rc = PDMCritSectEnter(&pThis->CritSect, VINF_EM_RAW_EMULATE_INSTR);
3496 if (rc != VINF_SUCCESS)
3497 return rc;
3498
3499 /*
3500 * Set page dirty bit.
3501 */
3502 vga_set_dirty(pThis, GCPhys - pThis->GCPhysVRAM);
3503 pThis->fLFBUpdated = true;
3504
3505 /*
3506 * Turn of the write handler for this particular page and make it R/W.
3507 * Then return telling the caller to restart the guest instruction.
3508 * ASSUME: the guest always maps video memory RW.
3509 */
3510 rc = PGMHandlerPhysicalPageTempOff(pVM, pThis->GCPhysVRAM, GCPhys);
3511 if (RT_SUCCESS(rc))
3512 {
3513#ifndef IN_RING3
3514 rc = PGMShwMakePageWritable(PDMDevHlpGetVMCPU(pThis->CTX_SUFF(pDevIns)), GCPtr,
3515 PGM_MK_PG_IS_MMIO2 | PGM_MK_PG_IS_WRITE_FAULT);
3516 PDMCritSectLeave(&pThis->CritSect);
3517 AssertMsgReturn( rc == VINF_SUCCESS
3518 /* In the SMP case the page table might be removed while we wait for the PGM lock in the trap handler. */
3519 || rc == VERR_PAGE_TABLE_NOT_PRESENT
3520 || rc == VERR_PAGE_NOT_PRESENT,
3521 ("PGMShwModifyPage -> GCPtr=%RGv rc=%d\n", GCPtr, rc),
3522 rc);
3523#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3524 PDMCritSectLeave(&pThis->CritSect);
3525 Assert(GCPtr == 0);
3526#endif
3527 return VINF_SUCCESS;
3528 }
3529
3530 PDMCritSectLeave(&pThis->CritSect);
3531 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3532 return rc;
3533}
3534
3535
3536#ifndef IN_RING3
3537/**
3538 * @callback_method_impl{FNPGMRCPHYSHANDLER, \#PF Handler for VBE LFB access.}
3539 */
3540PDMBOTHCBDECL(VBOXSTRICTRC) vgaLbfAccessPfHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame,
3541 RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3542{
3543 PVGASTATE pThis = (PVGASTATE)pvUser;
3544 AssertPtr(pThis);
3545 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3546 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3547 NOREF(pRegFrame);
3548
3549 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3550}
3551#endif /* !IN_RING3 */
3552
3553
3554/**
3555 * @callback_method_impl{FNPGMPHYSHANDLER,
3556 * VBE LFB write access handler for the dirty tracking.}
3557 */
3558PGM_ALL_CB_DECL(VBOXSTRICTRC) vgaLFBAccessHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf,
3559 PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
3560{
3561 PVGASTATE pThis = (PVGASTATE)pvUser;
3562 int rc;
3563 Assert(pThis);
3564 Assert(GCPhys >= pThis->GCPhysVRAM);
3565 NOREF(pVCpu); NOREF(pvPhys); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmAccessType); NOREF(enmOrigin);
3566
3567 rc = vgaLFBAccess(pVM, pThis, GCPhys, 0);
3568 if (RT_SUCCESS(rc))
3569 return VINF_PGM_HANDLER_DO_DEFAULT;
3570 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
3571 return rc;
3572}
3573
3574
3575/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3576
3577/**
3578 * @callback_method_impl{FNIOMIOPORTIN,
3579 * Port I/O Handler for VGA BIOS IN operations.}
3580 */
3581PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3582{
3583 NOREF(pDevIns);
3584 NOREF(pvUser);
3585 NOREF(Port);
3586 NOREF(pu32);
3587 NOREF(cb);
3588 return VERR_IOM_IOPORT_UNUSED;
3589}
3590
3591/**
3592 * @callback_method_impl{FNIOMIOPORTOUT,
3593 * Port I/O Handler for VGA BIOS IN operations.}
3594 */
3595PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3596{
3597 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3598 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3599 NOREF(pvUser);
3600 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3601
3602 /*
3603 * VGA BIOS char printing.
3604 */
3605 if ( cb == 1
3606 && Port == VBE_PRINTF_PORT)
3607 {
3608#if 0
3609 switch (u32)
3610 {
3611 case '\r': Log(("vgabios: <return>\n")); break;
3612 case '\n': Log(("vgabios: <newline>\n")); break;
3613 case '\t': Log(("vgabios: <tab>\n")); break;
3614 default:
3615 Log(("vgabios: %c\n", u32));
3616 }
3617#else
3618 if (lastWasNotNewline == 0)
3619 Log(("vgabios: "));
3620 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3621 Log(("%c", u32));
3622 if (u32 == '\n')
3623 lastWasNotNewline = 0;
3624 else
3625 lastWasNotNewline = 1;
3626#endif
3627 return VINF_SUCCESS;
3628 }
3629
3630 /* not in use. */
3631 return VERR_IOM_IOPORT_UNUSED;
3632}
3633
3634
3635/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3636
3637#ifdef IN_RING3
3638
3639# ifdef VBE_NEW_DYN_LIST
3640/**
3641 * @callback_method_impl{FNIOMIOPORTOUT,
3642 * Port I/O Handler for VBE Extra OUT operations.}
3643 */
3644PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3645{
3646 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3647 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3648 NOREF(pvUser); NOREF(Port);
3649
3650 if (cb == 2)
3651 {
3652 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3653 pThis->u16VBEExtraAddress = u32;
3654 }
3655 else
3656 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3657
3658 return VINF_SUCCESS;
3659}
3660
3661
3662/**
3663 * @callback_method_impl{FNIOMIOPORTIN,
3664 * Port I/O Handler for VBE Extra IN operations.}
3665 */
3666PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3667{
3668 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3669 NOREF(pvUser); NOREF(Port);
3670 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3671
3672 int rc = VINF_SUCCESS;
3673 if (pThis->u16VBEExtraAddress == 0xffff)
3674 {
3675 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3676 *pu32 = pThis->vram_size / _64K;
3677 }
3678 else if ( pThis->u16VBEExtraAddress >= pThis->cbVBEExtraData
3679 || pThis->u16VBEExtraAddress + cb > pThis->cbVBEExtraData)
3680 {
3681 *pu32 = 0;
3682 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3683 pThis->u16VBEExtraAddress, pThis->u16VBEExtraAddress, pThis->cbVBEExtraData, pThis->cbVBEExtraData));
3684 }
3685 else if (cb == 1)
3686 {
3687 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress] & 0xFF;
3688
3689 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3690 }
3691 else if (cb == 2)
3692 {
3693 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress]
3694 | pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress + 1] << 8;
3695
3696 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3697 }
3698 else
3699 {
3700 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3701 rc = VERR_IOM_IOPORT_UNUSED;
3702 }
3703
3704 return rc;
3705}
3706# endif /* VBE_NEW_DYN_LIST */
3707
3708
3709/**
3710 * Parse the logo bitmap data at init time.
3711 *
3712 * @returns VBox status code.
3713 *
3714 * @param pThis The VGA instance data.
3715 */
3716static int vbeParseBitmap(PVGASTATE pThis)
3717{
3718 uint16_t i;
3719 PBMPINFO bmpInfo;
3720 POS2HDR pOs2Hdr;
3721 POS22HDR pOs22Hdr;
3722 PWINHDR pWinHdr;
3723
3724 /*
3725 * Get bitmap header data
3726 */
3727 bmpInfo = (PBMPINFO)(pThis->pu8Logo + sizeof(LOGOHDR));
3728 pWinHdr = (PWINHDR)(pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO));
3729
3730 if (bmpInfo->Type == BMP_ID)
3731 {
3732 switch (pWinHdr->Size)
3733 {
3734 case BMP_HEADER_OS21:
3735 pOs2Hdr = (POS2HDR)pWinHdr;
3736 pThis->cxLogo = pOs2Hdr->Width;
3737 pThis->cyLogo = pOs2Hdr->Height;
3738 pThis->cLogoPlanes = pOs2Hdr->Planes;
3739 pThis->cLogoBits = pOs2Hdr->BitCount;
3740 pThis->LogoCompression = BMP_COMPRESS_NONE;
3741 pThis->cLogoUsedColors = 0;
3742 break;
3743
3744 case BMP_HEADER_OS22:
3745 pOs22Hdr = (POS22HDR)pWinHdr;
3746 pThis->cxLogo = pOs22Hdr->Width;
3747 pThis->cyLogo = pOs22Hdr->Height;
3748 pThis->cLogoPlanes = pOs22Hdr->Planes;
3749 pThis->cLogoBits = pOs22Hdr->BitCount;
3750 pThis->LogoCompression = pOs22Hdr->Compression;
3751 pThis->cLogoUsedColors = pOs22Hdr->ClrUsed;
3752 break;
3753
3754 case BMP_HEADER_WIN3:
3755 pThis->cxLogo = pWinHdr->Width;
3756 pThis->cyLogo = pWinHdr->Height;
3757 pThis->cLogoPlanes = pWinHdr->Planes;
3758 pThis->cLogoBits = pWinHdr->BitCount;
3759 pThis->LogoCompression = pWinHdr->Compression;
3760 pThis->cLogoUsedColors = pWinHdr->ClrUsed;
3761 break;
3762
3763 default:
3764 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pWinHdr->Size),
3765 VERR_INVALID_PARAMETER);
3766 break;
3767 }
3768
3769 AssertLogRelMsgReturn(pThis->cxLogo <= LOGO_MAX_WIDTH && pThis->cyLogo <= LOGO_MAX_HEIGHT,
3770 ("Bitmap %ux%u is too big.\n", pThis->cxLogo, pThis->cyLogo),
3771 VERR_INVALID_PARAMETER);
3772
3773 AssertLogRelMsgReturn(pThis->cLogoPlanes == 1,
3774 ("Bitmap planes %u != 1.\n", pThis->cLogoPlanes),
3775 VERR_INVALID_PARAMETER);
3776
3777 AssertLogRelMsgReturn(pThis->cLogoBits == 4 || pThis->cLogoBits == 8 || pThis->cLogoBits == 24,
3778 ("Unsupported %u depth.\n", pThis->cLogoBits),
3779 VERR_INVALID_PARAMETER);
3780
3781 AssertLogRelMsgReturn(pThis->cLogoUsedColors <= 256,
3782 ("Unsupported %u colors.\n", pThis->cLogoUsedColors),
3783 VERR_INVALID_PARAMETER);
3784
3785 AssertLogRelMsgReturn(pThis->LogoCompression == BMP_COMPRESS_NONE,
3786 ("Unsupported %u compression.\n", pThis->LogoCompression),
3787 VERR_INVALID_PARAMETER);
3788
3789 /*
3790 * Read bitmap palette
3791 */
3792 if (!pThis->cLogoUsedColors)
3793 pThis->cLogoPalEntries = 1 << (pThis->cLogoPlanes * pThis->cLogoBits);
3794 else
3795 pThis->cLogoPalEntries = pThis->cLogoUsedColors;
3796
3797 if (pThis->cLogoPalEntries)
3798 {
3799 const uint8_t *pu8Pal = pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO) + pWinHdr->Size; /* ASSUMES Size location (safe) */
3800
3801 for (i = 0; i < pThis->cLogoPalEntries; i++)
3802 {
3803 uint16_t j;
3804 uint32_t u32Pal = 0;
3805
3806 for (j = 0; j < 3; j++)
3807 {
3808 uint8_t b = *pu8Pal++;
3809 u32Pal <<= 8;
3810 u32Pal |= b;
3811 }
3812
3813 pu8Pal++; /* skip unused byte */
3814 pThis->au32LogoPalette[i] = u32Pal;
3815 }
3816 }
3817
3818 /*
3819 * Bitmap data offset
3820 */
3821 pThis->pu8LogoBitmap = pThis->pu8Logo + sizeof(LOGOHDR) + bmpInfo->Offset;
3822 }
3823 else
3824 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
3825
3826 return VINF_SUCCESS;
3827}
3828
3829
3830/**
3831 * Show logo bitmap data.
3832 *
3833 * @returns VBox status code.
3834 *
3835 * @param cbDepth Logo depth.
3836 * @param xLogo Logo X position.
3837 * @param yLogo Logo Y position.
3838 * @param cxLogo Logo width.
3839 * @param cyLogo Logo height.
3840 * @param iStep Fade in/fade out step.
3841 * @param pu32Palette Palette data.
3842 * @param pu8Src Source buffer.
3843 * @param pu8Dst Destination buffer.
3844 */
3845static void vbeShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo, uint8_t iStep,
3846 const uint32_t *pu32Palette, const uint8_t *pu8Src, uint8_t *pu8Dst)
3847{
3848 uint16_t i;
3849 size_t cbPadBytes = 0;
3850 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
3851 uint16_t cyLeft = cyLogo;
3852
3853 pu8Dst += xLogo * 4 + yLogo * cbLineDst;
3854
3855 switch (cBits)
3856 {
3857 case 1:
3858 pu8Dst += cyLogo * cbLineDst;
3859 cbPadBytes = 0;
3860 break;
3861
3862 case 4:
3863 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
3864 cbPadBytes = 0;
3865 else if ((cxLogo % 8) <= 2)
3866 cbPadBytes = 3;
3867 else if ((cxLogo % 8) <= 4)
3868 cbPadBytes = 2;
3869 else
3870 cbPadBytes = 1;
3871 break;
3872
3873 case 8:
3874 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
3875 break;
3876
3877 case 24:
3878 cbPadBytes = cxLogo % 4;
3879 break;
3880 }
3881
3882 uint8_t j = 0, c = 0;
3883
3884 while (cyLeft-- > 0)
3885 {
3886 uint8_t *pu8TmpPtr = pu8Dst;
3887
3888 if (cBits != 1)
3889 j = 0;
3890
3891 for (i = 0; i < cxLogo; i++)
3892 {
3893 uint8_t pix;
3894
3895 switch (cBits)
3896 {
3897 case 1:
3898 {
3899 if (!j)
3900 c = *pu8Src++;
3901
3902 pix = (c & 1) ? 0xFF : 0;
3903 c >>= 1;
3904
3905 if (pix)
3906 {
3907 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3908 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3909 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3910 pu8TmpPtr++;
3911 }
3912 else
3913 {
3914 pu8TmpPtr += 4;
3915 }
3916
3917 j = (j + 1) % 8;
3918 break;
3919 }
3920
3921 case 4:
3922 {
3923 if (!j)
3924 c = *pu8Src++;
3925
3926 pix = (c >> 4) & 0xF;
3927 c <<= 4;
3928
3929 uint32_t u32Pal = pu32Palette[pix];
3930
3931 pix = (u32Pal >> 16) & 0xFF;
3932 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3933 pix = (u32Pal >> 8) & 0xFF;
3934 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3935 pix = u32Pal & 0xFF;
3936 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3937 pu8TmpPtr++;
3938
3939 j = (j + 1) % 2;
3940 break;
3941 }
3942
3943 case 8:
3944 {
3945 uint32_t u32Pal = pu32Palette[*pu8Src++];
3946
3947 pix = (u32Pal >> 16) & 0xFF;
3948 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3949 pix = (u32Pal >> 8) & 0xFF;
3950 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3951 pix = u32Pal & 0xFF;
3952 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3953 pu8TmpPtr++;
3954 break;
3955 }
3956
3957 case 24:
3958 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3959 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3960 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3961 pu8TmpPtr++;
3962 break;
3963 }
3964 }
3965
3966 pu8Dst -= cbLineDst;
3967 pu8Src += cbPadBytes;
3968 }
3969}
3970
3971
3972/**
3973 * @callback_method_impl{FNIOMIOPORTOUT,
3974 * Port I/O Handler for BIOS Logo OUT operations.}
3975 */
3976PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3977{
3978 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3979 NOREF(pvUser);
3980 NOREF(Port);
3981
3982 Log(("vbeIOPortWriteCMDLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
3983
3984 if (cb == 2)
3985 {
3986 /* Get the logo command */
3987 switch (u32 & 0xFF00)
3988 {
3989 case LOGO_CMD_SET_OFFSET:
3990 pThis->offLogoData = u32 & 0xFF;
3991 break;
3992
3993 case LOGO_CMD_SHOW_BMP:
3994 {
3995 uint8_t iStep = u32 & 0xFF;
3996 const uint8_t *pu8Src = pThis->pu8LogoBitmap;
3997 uint8_t *pu8Dst;
3998 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
3999 uint32_t offDirty = 0;
4000 uint16_t xLogo = (LOGO_MAX_WIDTH - pThis->cxLogo) / 2;
4001 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThis->cyLogo) / 2;
4002
4003 /* Check VRAM size */
4004 if (pThis->vram_size < LOGO_MAX_SIZE)
4005 break;
4006
4007 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4008 pu8Dst = pThis->vram_ptrR3 + LOGO_MAX_SIZE;
4009 else
4010 pu8Dst = pThis->vram_ptrR3;
4011
4012 /* Clear screen - except on power on... */
4013 if (!pThis->fLogoClearScreen)
4014 {
4015 uint32_t *pu32TmpPtr = (uint32_t *)pu8Dst;
4016
4017 /* Clear vram */
4018 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4019 {
4020 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4021 *pu32TmpPtr++ = 0;
4022 }
4023 pThis->fLogoClearScreen = true;
4024 }
4025
4026 /* Show the bitmap. */
4027 vbeShowBitmap(pThis->cLogoBits, xLogo, yLogo,
4028 pThis->cxLogo, pThis->cyLogo,
4029 iStep, &pThis->au32LogoPalette[0],
4030 pu8Src, pu8Dst);
4031
4032 /* Show the 'Press F12...' text. */
4033 if (pLogoHdr->fu8ShowBootMenu == 2)
4034 vbeShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4035 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4036 iStep, &pThis->au32LogoPalette[0],
4037 &g_abLogoF12BootText[0], pu8Dst);
4038
4039 /* Blit the offscreen buffer. */
4040 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4041 {
4042 uint32_t *pu32TmpDst = (uint32_t *)pThis->vram_ptrR3;
4043 uint32_t *pu32TmpSrc = (uint32_t *)(pThis->vram_ptrR3 + LOGO_MAX_SIZE);
4044 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4045 {
4046 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4047 *pu32TmpDst++ = *pu32TmpSrc++;
4048 }
4049 }
4050
4051 /* Set the dirty flags. */
4052 while (offDirty <= LOGO_MAX_SIZE)
4053 {
4054 vga_set_dirty(pThis, offDirty);
4055 offDirty += PAGE_SIZE;
4056 }
4057 break;
4058 }
4059
4060 default:
4061 Log(("vbeIOPortWriteCMDLogo: invalid command %d\n", u32));
4062 pThis->LogoCommand = LOGO_CMD_NOP;
4063 break;
4064 }
4065
4066 return VINF_SUCCESS;
4067 }
4068
4069 Log(("vbeIOPortWriteCMDLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4070 return VINF_SUCCESS;
4071}
4072
4073
4074/**
4075 * @callback_method_impl{FNIOMIOPORTIN,
4076 * Port I/O Handler for BIOS Logo IN operations.}
4077 */
4078PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
4079{
4080 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4081 NOREF(pvUser);
4082 NOREF(Port);
4083
4084 PRTUINT64U p;
4085
4086 if (pThis->offLogoData + cb > pThis->cbLogo)
4087 {
4088 Log(("vbeIOPortReadCMDLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4089 pThis->offLogoData, pThis->offLogoData, pThis->cbLogo, pThis->cbLogo));
4090 return VINF_SUCCESS;
4091 }
4092 p = (PRTUINT64U)&pThis->pu8Logo[pThis->offLogoData];
4093
4094 switch (cb)
4095 {
4096 case 1: *pu32 = p->au8[0]; break;
4097 case 2: *pu32 = p->au16[0]; break;
4098 case 4: *pu32 = p->au32[0]; break;
4099 //case 8: *pu32 = p->au64[0]; break;
4100 default: AssertFailed(); break;
4101 }
4102 Log(("vbeIOPortReadCMDLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThis->offLogoData, pThis->offLogoData, cb, cb, pu32));
4103
4104 pThis->LogoCommand = LOGO_CMD_NOP;
4105 pThis->offLogoData += cb;
4106
4107 return VINF_SUCCESS;
4108}
4109
4110
4111/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4112
4113/**
4114 * @callback_method_impl{FNDBGFHANDLERDEV,
4115 * Dumps several interesting bits of the VGA state that are difficult to
4116 * decode from the registers.}
4117 */
4118static DECLCALLBACK(void) vgaInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4119{
4120 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4121 int is_graph, double_scan;
4122 int w, h, char_height, char_dots;
4123 int val, vfreq_hz, hfreq_hz;
4124 vga_retrace_s *r = &pThis->retrace_state;
4125 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4126 NOREF(pszArgs);
4127
4128 is_graph = pThis->gr[6] & 1;
4129 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4130 double_scan = pThis->cr[9] >> 7;
4131 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4132 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4133 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4134 val = pThis->cr[0] + 5;
4135 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4136 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4137 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4138 val = pThis->cr[1] + 1;
4139 w = val * char_dots;
4140 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4141 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4142 h = val;
4143 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4144 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4145 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4146 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4147 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4148 if (!is_graph)
4149 {
4150 val = (pThis->cr[9] & 0x1f) + 1;
4151 char_height = val;
4152 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4153 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4154
4155 uint32_t cbLine;
4156 uint32_t offStart;
4157 uint32_t uLineCompareIgn;
4158 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4159 if (!cbLine)
4160 cbLine = 80 * 8;
4161 offStart *= 8;
4162 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4163 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4164 }
4165 if (pThis->fRealRetrace)
4166 {
4167 val = r->hb_start;
4168 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4169 val = r->hb_end;
4170 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4171 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4172 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4173 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4174 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4175 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4176 {
4177 vfreq_hz = 1000000000 / r->frame_ns;
4178 hfreq_hz = 1000000000 / r->h_total_ns;
4179 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4180 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4181 }
4182 }
4183 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4184
4185#ifdef VBOX_WITH_VMSVGA
4186 if (pThis->svga.fEnabled) {
4187 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4188 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4189 }
4190#endif
4191}
4192
4193
4194/**
4195 * Prints a separator line.
4196 *
4197 * @param pHlp Callback functions for doing output.
4198 * @param cCols The number of columns.
4199 * @param pszTitle The title text, NULL if none.
4200 */
4201static void vgaInfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4202{
4203 if (pszTitle)
4204 {
4205 size_t cchTitle = strlen(pszTitle);
4206 if (cchTitle + 6 >= cCols)
4207 {
4208 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4209 cCols = 0;
4210 }
4211 else
4212 {
4213 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4214 cCols -= cchLeft + cchTitle + 2;
4215 while (cchLeft-- > 0)
4216 pHlp->pfnPrintf(pHlp, "-");
4217 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4218 }
4219 }
4220
4221 while (cCols-- > 0)
4222 pHlp->pfnPrintf(pHlp, "-");
4223 pHlp->pfnPrintf(pHlp, "\n");
4224}
4225
4226
4227/**
4228 * Worker for vgaInfoText.
4229 *
4230 * @param pThis The vga state.
4231 * @param pHlp Callback functions for doing output.
4232 * @param offStart Where to start dumping (relative to the VRAM).
4233 * @param cbLine The source line length (aka line_offset).
4234 * @param cCols The number of columns on the screen.
4235 * @param cRows The number of rows to dump.
4236 * @param iScrBegin The row at which the current screen output starts.
4237 * @param iScrEnd The row at which the current screen output end
4238 * (exclusive).
4239 */
4240static void vgaInfoTextWorker(PVGASTATE pThis, PCDBGFINFOHLP pHlp,
4241 uint32_t offStart, uint32_t cbLine,
4242 uint32_t cCols, uint32_t cRows,
4243 uint32_t iScrBegin, uint32_t iScrEnd)
4244{
4245 /* Title, */
4246 char szTitle[32];
4247 if (iScrBegin || iScrEnd < cRows)
4248 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4249 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4250 else
4251 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4252
4253 /* Do the dumping. */
4254 uint8_t const *pbSrcOuter = pThis->CTX_SUFF(vram_ptr) + offStart;
4255 uint32_t iRow;
4256 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4257 {
4258 if ((uintptr_t)(pbSrcOuter + cbLine - pThis->CTX_SUFF(vram_ptr)) > pThis->vram_size) {
4259 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4260 break;
4261 }
4262
4263 if (iRow == 0)
4264 vgaInfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4265 else if (iRow == iScrBegin)
4266 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4267 else if (iRow == iScrEnd)
4268 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4269
4270 uint8_t const *pbSrc = pbSrcOuter;
4271 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4272 {
4273 if (RT_C_IS_PRINT(*pbSrc))
4274 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4275 else
4276 pHlp->pfnPrintf(pHlp, ".");
4277 pbSrc += 8; /* chars are spaced 8 bytes apart */
4278 }
4279 pHlp->pfnPrintf(pHlp, "\n");
4280 }
4281
4282 /* Final separator. */
4283 vgaInfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4284}
4285
4286
4287/**
4288 * @callback_method_impl{FNDBGFHANDLERDEV,
4289 * Dumps VGA memory formatted as ASCII text, no attributes. Only looks at the
4290 * first page.}
4291 */
4292static DECLCALLBACK(void) vgaInfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4293{
4294 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4295
4296 /*
4297 * Parse args.
4298 */
4299 bool fAll = true;
4300 if (pszArgs && *pszArgs)
4301 {
4302 if (!strcmp(pszArgs, "all"))
4303 fAll = true;
4304 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4305 fAll = false;
4306 else
4307 {
4308 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4309 return;
4310 }
4311 }
4312
4313 /*
4314 * Check that we're in text mode and that the VRAM is accessible.
4315 */
4316 if (!(pThis->gr[6] & 1))
4317 {
4318 uint8_t *pbSrc = pThis->vram_ptrR3;
4319 if (pbSrc)
4320 {
4321 /*
4322 * Figure out the display size and where the text is.
4323 *
4324 * Note! We're cutting quite a few corners here and this code could
4325 * do with some brushing up. Dumping from the start of the
4326 * frame buffer is done intentionally so that we're more
4327 * likely to obtain the full scrollback of a linux panic.
4328 * 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";
4329 */
4330 uint32_t cbLine;
4331 uint32_t offStart;
4332 uint32_t uLineCompareIgn;
4333 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4334 if (!cbLine)
4335 cbLine = 80 * 8;
4336 offStart *= 8;
4337
4338 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4339 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4340 uint32_t uDblScan = pThis->cr[9] >> 7;
4341 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4342 if (cScrRows < 25)
4343 cScrRows = 25;
4344 uint32_t iScrBegin = offStart / cbLine;
4345 uint32_t cRows = iScrBegin + cScrRows;
4346 uint32_t cCols = cbLine / 8;
4347
4348 if (fAll) {
4349 vgaInfoTextWorker(pThis, pHlp, offStart - iScrBegin * cbLine, cbLine,
4350 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4351 } else {
4352 vgaInfoTextWorker(pThis, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4353 }
4354 }
4355 else
4356 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4357 }
4358 else
4359 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4360}
4361
4362
4363/**
4364 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4365 */
4366static DECLCALLBACK(void) vgaInfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4367{
4368 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4369 unsigned i;
4370 NOREF(pszArgs);
4371
4372 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4373 Assert(sizeof(pThis->sr) >= 8);
4374 for (i = 0; i < 5; ++i)
4375 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4376 pHlp->pfnPrintf(pHlp, "\n");
4377}
4378
4379
4380/**
4381 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4382 */
4383static DECLCALLBACK(void) vgaInfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4384{
4385 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4386 unsigned i;
4387 NOREF(pszArgs);
4388
4389 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4390 Assert(sizeof(pThis->cr) >= 24);
4391 for (i = 0; i < 10; ++i)
4392 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4393 pHlp->pfnPrintf(pHlp, "\n");
4394 for (i = 10; i < 20; ++i)
4395 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4396 pHlp->pfnPrintf(pHlp, "\n");
4397 for (i = 20; i < 25; ++i)
4398 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4399 pHlp->pfnPrintf(pHlp, "\n");
4400}
4401
4402
4403/**
4404 * @callback_method_impl{FNDBGFHANDLERDEV,
4405 * Dumps VGA Graphics Controller registers.}
4406 */
4407static DECLCALLBACK(void) vgaInfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4408{
4409 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4410 unsigned i;
4411 NOREF(pszArgs);
4412
4413 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4414 Assert(sizeof(pThis->gr) >= 9);
4415 for (i = 0; i < 9; ++i)
4416 {
4417 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4418 }
4419 pHlp->pfnPrintf(pHlp, "\n");
4420}
4421
4422
4423/**
4424 * @callback_method_impl{FNDBGFHANDLERDEV,
4425 * Dumps VGA Attribute Controller registers.}
4426 */
4427static DECLCALLBACK(void) vgaInfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4428{
4429 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4430 unsigned i;
4431 NOREF(pszArgs);
4432
4433 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4434 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4435 Assert(sizeof(pThis->ar) >= 0x14);
4436 pHlp->pfnPrintf(pHlp, " Palette:");
4437 for (i = 0; i < 0x10; ++i)
4438 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4439 pHlp->pfnPrintf(pHlp, "\n");
4440 for (i = 0x10; i <= 0x14; ++i)
4441 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4442 pHlp->pfnPrintf(pHlp, "\n");
4443}
4444
4445
4446/**
4447 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4448 */
4449static DECLCALLBACK(void) vgaInfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4450{
4451 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4452 unsigned i;
4453 NOREF(pszArgs);
4454
4455 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4456 for (i = 0; i < 0x100; ++i)
4457 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4458 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4459}
4460
4461
4462/**
4463 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4464 */
4465static DECLCALLBACK(void) vgaInfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4466{
4467 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4468 NOREF(pszArgs);
4469
4470 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4471
4472 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4473 {
4474 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4475 return;
4476 }
4477
4478 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4479 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4480 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4481 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4482 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4483 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4484 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4485 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4486 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4487 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4488 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4489}
4490
4491
4492/**
4493 * @callback_method_impl{FNDBGFHANDLERDEV,
4494 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4495 * in human-readable form.}
4496 */
4497static DECLCALLBACK(void) vgaInfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4498{
4499 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4500 int val1, val2;
4501 NOREF(pszArgs);
4502
4503 val1 = (pThis->gr[5] >> 3) & 1;
4504 val2 = pThis->gr[5] & 3;
4505 pHlp->pfnPrintf(pHlp, "read mode : %d write mode: %d\n", val1, val2);
4506 val1 = pThis->gr[0];
4507 val2 = pThis->gr[1];
4508 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4509 val1 = pThis->gr[2];
4510 val2 = pThis->gr[4] & 3;
4511 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %d\n", val1, val2);
4512 val1 = pThis->gr[3] & 7;
4513 val2 = (pThis->gr[3] >> 3) & 3;
4514 pHlp->pfnPrintf(pHlp, "rotate : %d function : %d\n", val1, val2);
4515 val1 = pThis->gr[7];
4516 val2 = pThis->gr[8];
4517 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4518 val1 = pThis->sr[2];
4519 val2 = pThis->sr[4] & 8;
4520 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4521}
4522
4523
4524/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4525
4526/**
4527 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4528 */
4529static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4530{
4531 PVGASTATE pThis = RT_FROM_MEMBER(pInterface, VGASTATE, IBase);
4532 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4533 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPort);
4534#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI))
4535 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThis->IVBVACallbacks);
4536#endif
4537 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4538 return NULL;
4539}
4540
4541/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4542#define ILEDPORTS_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, ILeds)) )
4543
4544/**
4545 * Gets the pointer to the status LED of a unit.
4546 *
4547 * @returns VBox status code.
4548 * @param pInterface Pointer to the interface structure containing the called function pointer.
4549 * @param iLUN The unit which status LED we desire.
4550 * @param ppLed Where to store the LED pointer.
4551 */
4552static DECLCALLBACK(int) vgaPortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4553{
4554 PVGASTATE pThis = ILEDPORTS_2_VGASTATE(pInterface);
4555 switch (iLUN)
4556 {
4557 /* LUN #0: Display port. */
4558 case 0:
4559 {
4560 *ppLed = &pThis->Led3D;
4561 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4562 return VINF_SUCCESS;
4563 }
4564
4565 default:
4566 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4567 return VERR_PDM_NO_SUCH_LUN;
4568 }
4569
4570 return VERR_PDM_LUN_NOT_FOUND;
4571}
4572
4573/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4574
4575/**
4576 * Resize the display.
4577 * This is called when the resolution changes. This usually happens on
4578 * request from the guest os, but may also happen as the result of a reset.
4579 *
4580 * @param pInterface Pointer to this interface.
4581 * @param cx New display width.
4582 * @param cy New display height
4583 * @thread The emulation thread.
4584 */
4585static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM,
4586 uint32_t cbLine, uint32_t cx, uint32_t cy)
4587{
4588 NOREF(pInterface); NOREF(bpp); NOREF(pvVRAM); NOREF(cbLine); NOREF(cx); NOREF(cy);
4589 return VINF_SUCCESS;
4590}
4591
4592
4593/**
4594 * Update a rectangle of the display.
4595 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
4596 *
4597 * @param pInterface Pointer to this interface.
4598 * @param x The upper left corner x coordinate of the rectangle.
4599 * @param y The upper left corner y coordinate of the rectangle.
4600 * @param cx The width of the rectangle.
4601 * @param cy The height of the rectangle.
4602 * @thread The emulation thread.
4603 */
4604static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4605{
4606 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(cx); NOREF(cy);
4607}
4608
4609
4610/**
4611 * Refresh the display.
4612 *
4613 * The interval between these calls is set by
4614 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
4615 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
4616 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
4617 * the changed rectangles.
4618 *
4619 * @param pInterface Pointer to this interface.
4620 * @thread The emulation thread.
4621 */
4622static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4623{
4624 NOREF(pInterface);
4625}
4626
4627
4628/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4629
4630/** Converts a display port interface pointer to a vga state pointer. */
4631#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, IPort)) )
4632
4633
4634/**
4635 * Update the display with any changed regions.
4636 *
4637 * @param pInterface Pointer to this interface.
4638 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
4639 */
4640static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4641{
4642 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4643 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4644 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4645
4646 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4647 AssertRC(rc);
4648
4649#ifdef VBOX_WITH_VMSVGA
4650 if ( pThis->svga.fEnabled
4651 && !pThis->svga.fTraces)
4652 {
4653 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4654 PDMCritSectLeave(&pThis->CritSect);
4655 return VINF_SUCCESS;
4656 }
4657#endif
4658
4659#ifndef VBOX_WITH_HGSMI
4660 /* This should be called only in non VBVA mode. */
4661#else
4662 if (VBVAUpdateDisplay (pThis) == VINF_SUCCESS)
4663 {
4664 PDMCritSectLeave(&pThis->CritSect);
4665 return VINF_SUCCESS;
4666 }
4667#endif /* VBOX_WITH_HGSMI */
4668
4669 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4670 if (pThis->fHasDirtyBits && pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4671 {
4672 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4673 pThis->fHasDirtyBits = false;
4674 }
4675 if (pThis->fRemappedVGA)
4676 {
4677 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4678 pThis->fRemappedVGA = false;
4679 }
4680
4681 rc = vga_update_display(pThis, false, false, true,
4682 pThis->pDrv, &pThis->graphic_mode);
4683 PDMCritSectLeave(&pThis->CritSect);
4684 return rc;
4685}
4686
4687
4688/**
4689 * Internal vgaPortUpdateDisplayAll worker called under pThis->CritSect.
4690 */
4691static int updateDisplayAll(PVGASTATE pThis, bool fFailOnResize)
4692{
4693 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4694
4695#ifdef VBOX_WITH_VMSVGA
4696 if ( !pThis->svga.fEnabled
4697 || pThis->svga.fTraces)
4698 {
4699#endif
4700 /* The dirty bits array has been just cleared, reset handlers as well. */
4701 if (pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4702 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4703#ifdef VBOX_WITH_VMSVGA
4704 }
4705#endif
4706 if (pThis->fRemappedVGA)
4707 {
4708 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4709 pThis->fRemappedVGA = false;
4710 }
4711
4712 pThis->graphic_mode = -1; /* force full update */
4713
4714 return vga_update_display(pThis, true, fFailOnResize, true,
4715 pThis->pDrv, &pThis->graphic_mode);
4716}
4717
4718
4719DECLCALLBACK(int) vgaUpdateDisplayAll(PVGASTATE pThis, bool fFailOnResize)
4720{
4721#ifdef DEBUG_sunlover
4722 LogFlow(("vgaPortUpdateDisplayAll\n"));
4723#endif /* DEBUG_sunlover */
4724
4725 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4726 AssertRC(rc);
4727
4728 rc = updateDisplayAll(pThis, fFailOnResize);
4729
4730 PDMCritSectLeave(&pThis->CritSect);
4731 return rc;
4732}
4733
4734/**
4735 * Update the entire display.
4736 *
4737 * @param pInterface Pointer to this interface.
4738 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
4739 */
4740static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4741{
4742 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4743 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4744
4745 /* This is called both in VBVA mode and normal modes. */
4746
4747 return vgaUpdateDisplayAll(pThis, fFailOnResize);
4748}
4749
4750
4751/**
4752 * Sets the refresh rate and restart the timer.
4753 *
4754 * @returns VBox status code.
4755 * @param pInterface Pointer to this interface.
4756 * @param cMilliesInterval Number of millis between two refreshes.
4757 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
4758 */
4759static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4760{
4761 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4762
4763 pThis->cMilliesRefreshInterval = cMilliesInterval;
4764 if (cMilliesInterval)
4765 return TMTimerSetMillies(pThis->RefreshTimer, cMilliesInterval);
4766 return TMTimerStop(pThis->RefreshTimer);
4767}
4768
4769
4770/** @copydoc PDMIDISPLAYPORT::pfnQueryVideoMode */
4771static DECLCALLBACK(int) vgaPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4772{
4773 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4774
4775 if (!pcBits)
4776 return VERR_INVALID_PARAMETER;
4777 *pcBits = vga_get_bpp(pThis);
4778 if (pcx)
4779 *pcx = pThis->last_scr_width;
4780 if (pcy)
4781 *pcy = pThis->last_scr_height;
4782 return VINF_SUCCESS;
4783}
4784
4785
4786/**
4787 * Create a 32-bbp screenshot of the display. Size of the bitmap scanline in bytes is 4*width.
4788 *
4789 * @param pInterface Pointer to this interface.
4790 * @param ppu8Data Where to store the pointer to the allocated buffer.
4791 * @param pcbData Where to store the actual size of the bitmap.
4792 * @param pcx Where to store the width of the bitmap.
4793 * @param pcy Where to store the height of the bitmap.
4794 * @see PDMIDISPLAYPORT::pfnTakeScreenshot() for details.
4795 */
4796static DECLCALLBACK(int) vgaPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pcx, uint32_t *pcy)
4797{
4798 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4799 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4800
4801 LogFlow(("vgaPortTakeScreenshot: ppu8Data=%p pcbData=%p pcx=%p pcy=%p\n", ppu8Data, pcbData, pcx, pcy));
4802
4803 /*
4804 * Validate input.
4805 */
4806 if (!RT_VALID_PTR(ppu8Data) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
4807 return VERR_INVALID_PARAMETER;
4808
4809 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4810 AssertRCReturn(rc, rc);
4811
4812 /*
4813 * Get screenshot. This function will fail if a resize is required.
4814 * So there is not need to do a 'updateDisplayAll' before taking screenshot.
4815 */
4816
4817 /*
4818 * Allocate the buffer for 32 bits per pixel bitmap
4819 *
4820 * Note! The size can't be zero or greater than the size of the VRAM.
4821 * Inconsistent VGA device state can cause the incorrect size values.
4822 */
4823 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
4824 if (cbRequired && cbRequired <= pThis->vram_size)
4825 {
4826 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
4827 if (pu8Data != NULL)
4828 {
4829 /*
4830 * Only 3 methods, assigned below, will be called during the screenshot update.
4831 * All other are already set to NULL.
4832 */
4833 /* The display connector interface is temporarily replaced with the fake one. */
4834 PDMIDISPLAYCONNECTOR Connector;
4835 RT_ZERO(Connector);
4836 Connector.pu8Data = pu8Data;
4837 Connector.cBits = 32;
4838 Connector.cx = pThis->last_scr_width;
4839 Connector.cy = pThis->last_scr_height;
4840 Connector.cbScanline = Connector.cx * 4;
4841 Connector.pfnRefresh = vgaDummyRefresh;
4842 Connector.pfnResize = vgaDummyResize;
4843 Connector.pfnUpdateRect = vgaDummyUpdateRect;
4844
4845 int32_t cur_graphic_mode = -1;
4846
4847 bool fSavedRenderVRAM = pThis->fRenderVRAM;
4848 pThis->fRenderVRAM = true;
4849
4850 /*
4851 * Make the screenshot.
4852 *
4853 * The second parameter is 'false' because the current display state is being rendered to an
4854 * external buffer using a fake connector. That is if display is blanked, we expect a black
4855 * screen in the external buffer.
4856 * If there is a pending resize, the function will fail.
4857 */
4858 rc = vga_update_display(pThis, false, true, false,
4859 &Connector, &cur_graphic_mode);
4860
4861 pThis->fRenderVRAM = fSavedRenderVRAM;
4862
4863 if (rc == VINF_SUCCESS)
4864 {
4865 /*
4866 * Return the result.
4867 */
4868 *ppu8Data = pu8Data;
4869 *pcbData = cbRequired;
4870 *pcx = Connector.cx;
4871 *pcy = Connector.cy;
4872 }
4873 else
4874 {
4875 /* If we do not return a success, then the data buffer must be freed. */
4876 RTMemFree(pu8Data);
4877 if (RT_SUCCESS_NP(rc))
4878 {
4879 AssertMsgFailed(("%Rrc\n", rc));
4880 rc = VERR_INTERNAL_ERROR_5;
4881 }
4882 }
4883 }
4884 else
4885 rc = VERR_NO_MEMORY;
4886 }
4887 else
4888 rc = VERR_NOT_SUPPORTED;
4889
4890 PDMCritSectLeave(&pThis->CritSect);
4891
4892 LogFlow(("vgaPortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
4893 return rc;
4894}
4895
4896/**
4897 * Free a screenshot buffer allocated in vgaPortTakeScreenshot.
4898 *
4899 * @param pInterface Pointer to this interface.
4900 * @param pu8Data Pointer returned by vgaPortTakeScreenshot.
4901 * @see PDMIDISPLAYPORT::pfnFreeScreenshot() for details.
4902 */
4903static DECLCALLBACK(void) vgaPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pu8Data)
4904{
4905 NOREF(pInterface);
4906
4907 LogFlow(("vgaPortFreeScreenshot: pu8Data=%p\n", pu8Data));
4908
4909 RTMemFree(pu8Data);
4910}
4911
4912/**
4913 * Copy bitmap to the display.
4914 *
4915 * @param pInterface Pointer to this interface.
4916 * @param pvData Pointer to the bitmap bits.
4917 * @param x The upper left corner x coordinate of the destination rectangle.
4918 * @param y The upper left corner y coordinate of the destination rectangle.
4919 * @param cx The width of the source and destination rectangles.
4920 * @param cy The height of the source and destination rectangles.
4921 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
4922 */
4923static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4924{
4925 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4926 int rc = VINF_SUCCESS;
4927 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4928 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
4929
4930 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4931 AssertRC(rc);
4932
4933 /*
4934 * Validate input.
4935 */
4936 if ( pvData
4937 && x < pThis->pDrv->cx
4938 && cx <= pThis->pDrv->cx
4939 && cx + x <= pThis->pDrv->cx
4940 && y < pThis->pDrv->cy
4941 && cy <= pThis->pDrv->cy
4942 && cy + y <= pThis->pDrv->cy)
4943 {
4944 /*
4945 * Determine bytes per pixel in the destination buffer.
4946 */
4947 size_t cbPixelDst = 0;
4948 switch (pThis->pDrv->cBits)
4949 {
4950 case 8:
4951 cbPixelDst = 1;
4952 break;
4953 case 15:
4954 case 16:
4955 cbPixelDst = 2;
4956 break;
4957 case 24:
4958 cbPixelDst = 3;
4959 break;
4960 case 32:
4961 cbPixelDst = 4;
4962 break;
4963 default:
4964 rc = VERR_INVALID_PARAMETER;
4965 break;
4966 }
4967 if (RT_SUCCESS(rc))
4968 {
4969 /*
4970 * The blitting loop.
4971 */
4972 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
4973 uint8_t *pu8Src = (uint8_t *)pvData;
4974 size_t cbLineDst = pThis->pDrv->cbScanline;
4975 uint8_t *pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4976 uint32_t cyLeft = cy;
4977 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
4978 Assert(pfnVgaDrawLine);
4979 while (cyLeft-- > 0)
4980 {
4981 pfnVgaDrawLine(pThis, pu8Dst, pu8Src, cx);
4982 pu8Dst += cbLineDst;
4983 pu8Src += cbLineSrc;
4984 }
4985
4986 /*
4987 * Invalidate the area.
4988 */
4989 pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
4990 }
4991 }
4992 else
4993 rc = VERR_INVALID_PARAMETER;
4994
4995 PDMCritSectLeave(&pThis->CritSect);
4996
4997 LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
4998 return rc;
4999}
5000
5001static DECLCALLBACK(void) vgaPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
5002{
5003 uint32_t v;
5004 vga_draw_line_func *vga_draw_line;
5005
5006 uint32_t cbPixelDst;
5007 uint32_t cbLineDst;
5008 uint8_t *pu8Dst;
5009
5010 uint32_t cbPixelSrc;
5011 uint32_t cbLineSrc;
5012 uint8_t *pu8Src;
5013
5014 uint32_t u32OffsetSrc, u32Dummy;
5015
5016 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5017
5018#ifdef DEBUG_sunlover
5019 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
5020#endif /* DEBUG_sunlover */
5021
5022 Assert(pInterface);
5023
5024 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5025 AssertRC(rc);
5026
5027 /* Check if there is something to do at all. */
5028 if (!pThis->fRenderVRAM)
5029 {
5030 /* The framebuffer uses the guest VRAM directly. */
5031#ifdef DEBUG_sunlover
5032 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
5033#endif /* DEBUG_sunlover */
5034 PDMCritSectLeave(&pThis->CritSect);
5035 return;
5036 }
5037
5038 Assert(pThis->pDrv);
5039 Assert(pThis->pDrv->pu8Data);
5040
5041 /* Correct negative x and y coordinates. */
5042 if (x < 0)
5043 {
5044 x += w; /* Compute xRight which is also the new width. */
5045 w = (x < 0) ? 0 : x;
5046 x = 0;
5047 }
5048
5049 if (y < 0)
5050 {
5051 y += h; /* Compute yBottom, which is also the new height. */
5052 h = (y < 0) ? 0 : y;
5053 y = 0;
5054 }
5055
5056 /* Also check if coords are greater than the display resolution. */
5057 if (x + w > pThis->pDrv->cx)
5058 {
5059 // x < 0 is not possible here
5060 w = pThis->pDrv->cx > (uint32_t)x? pThis->pDrv->cx - x: 0;
5061 }
5062
5063 if (y + h > pThis->pDrv->cy)
5064 {
5065 // y < 0 is not possible here
5066 h = pThis->pDrv->cy > (uint32_t)y? pThis->pDrv->cy - y: 0;
5067 }
5068
5069#ifdef DEBUG_sunlover
5070 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
5071#endif /* DEBUG_sunlover */
5072
5073 /* Check if there is something to do at all. */
5074 if (w == 0 || h == 0)
5075 {
5076 /* Empty rectangle. */
5077#ifdef DEBUG_sunlover
5078 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
5079#endif /* DEBUG_sunlover */
5080 PDMCritSectLeave(&pThis->CritSect);
5081 return;
5082 }
5083
5084 /** @todo This method should be made universal and not only for VBVA.
5085 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5086 * changed.
5087 */
5088
5089 /* Choose the rendering function. */
5090 switch(pThis->get_bpp(pThis))
5091 {
5092 default:
5093 case 0:
5094 /* A LFB mode is already disabled, but the callback is still called
5095 * by Display because VBVA buffer is being flushed.
5096 * Nothing to do, just return.
5097 */
5098 PDMCritSectLeave(&pThis->CritSect);
5099 return;
5100 case 8:
5101 v = VGA_DRAW_LINE8;
5102 break;
5103 case 15:
5104 v = VGA_DRAW_LINE15;
5105 break;
5106 case 16:
5107 v = VGA_DRAW_LINE16;
5108 break;
5109 case 24:
5110 v = VGA_DRAW_LINE24;
5111 break;
5112 case 32:
5113 v = VGA_DRAW_LINE32;
5114 break;
5115 }
5116
5117 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pThis->pDrv->cBits)];
5118
5119 /* Compute source and destination addresses and pitches. */
5120 cbPixelDst = (pThis->pDrv->cBits + 7) / 8;
5121 cbLineDst = pThis->pDrv->cbScanline;
5122 pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
5123
5124 cbPixelSrc = (pThis->get_bpp(pThis) + 7) / 8;
5125 pThis->get_offsets(pThis, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
5126
5127 /* Assume that rendering is performed only on visible part of VRAM.
5128 * This is true because coordinates were verified.
5129 */
5130 pu8Src = pThis->vram_ptrR3;
5131 pu8Src += u32OffsetSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5132
5133 /* Render VRAM to framebuffer. */
5134
5135#ifdef DEBUG_sunlover
5136 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
5137#endif /* DEBUG_sunlover */
5138
5139 while (h-- > 0)
5140 {
5141 vga_draw_line (pThis, pu8Dst, pu8Src, w);
5142 pu8Dst += cbLineDst;
5143 pu8Src += cbLineSrc;
5144 }
5145
5146 PDMCritSectLeave(&pThis->CritSect);
5147#ifdef DEBUG_sunlover
5148 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
5149#endif /* DEBUG_sunlover */
5150}
5151
5152static DECLCALLBACK(int) vgaPortCopyRect (PPDMIDISPLAYPORT pInterface,
5153 uint32_t w,
5154 uint32_t h,
5155 const uint8_t *pu8Src,
5156 int32_t xSrc,
5157 int32_t ySrc,
5158 uint32_t u32SrcWidth,
5159 uint32_t u32SrcHeight,
5160 uint32_t u32SrcLineSize,
5161 uint32_t u32SrcBitsPerPixel,
5162 uint8_t *pu8Dst,
5163 int32_t xDst,
5164 int32_t yDst,
5165 uint32_t u32DstWidth,
5166 uint32_t u32DstHeight,
5167 uint32_t u32DstLineSize,
5168 uint32_t u32DstBitsPerPixel)
5169{
5170 uint32_t v;
5171 vga_draw_line_func *vga_draw_line;
5172
5173 uint32_t cbPixelDst;
5174 uint32_t cbLineDst;
5175 uint8_t *pu8DstPtr;
5176
5177 uint32_t cbPixelSrc;
5178 uint32_t cbLineSrc;
5179 const uint8_t *pu8SrcPtr;
5180
5181#ifdef DEBUG_sunlover
5182 LogFlow(("vgaPortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, w, h, xDst, yDst));
5183#endif /* DEBUG_sunlover */
5184
5185 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5186
5187 Assert(pInterface);
5188 Assert(pThis->pDrv);
5189
5190 int32_t xSrcCorrected = xSrc;
5191 int32_t ySrcCorrected = ySrc;
5192 uint32_t wCorrected = w;
5193 uint32_t hCorrected = h;
5194
5195 /* Correct source coordinates to be within the source bitmap. */
5196 if (xSrcCorrected < 0)
5197 {
5198 xSrcCorrected += wCorrected; /* Compute xRight which is also the new width. */
5199 wCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5200 xSrcCorrected = 0;
5201 }
5202
5203 if (ySrcCorrected < 0)
5204 {
5205 ySrcCorrected += hCorrected; /* Compute yBottom, which is also the new height. */
5206 hCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5207 ySrcCorrected = 0;
5208 }
5209
5210 /* Also check if coords are greater than the display resolution. */
5211 if (xSrcCorrected + wCorrected > u32SrcWidth)
5212 {
5213 /* xSrcCorrected < 0 is not possible here */
5214 wCorrected = u32SrcWidth > (uint32_t)xSrcCorrected? u32SrcWidth - xSrcCorrected: 0;
5215 }
5216
5217 if (ySrcCorrected + hCorrected > u32SrcHeight)
5218 {
5219 /* y < 0 is not possible here */
5220 hCorrected = u32SrcHeight > (uint32_t)ySrcCorrected? u32SrcHeight - ySrcCorrected: 0;
5221 }
5222
5223#ifdef DEBUG_sunlover
5224 LogFlow(("vgaPortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, wCorrected, hCorrected));
5225#endif /* DEBUG_sunlover */
5226
5227 /* Check if there is something to do at all. */
5228 if (wCorrected == 0 || hCorrected == 0)
5229 {
5230 /* Empty rectangle. */
5231#ifdef DEBUG_sunlover
5232 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", wCorrected, hCorrected));
5233#endif /* DEBUG_sunlover */
5234 return VINF_SUCCESS;
5235 }
5236
5237 /* Check that the corrected source rectangle is within the destination.
5238 * Note: source rectangle is adjusted, but the target must be large enough.
5239 */
5240 if ( xDst < 0
5241 || yDst < 0
5242 || xDst + wCorrected > u32DstWidth
5243 || yDst + hCorrected > u32DstHeight)
5244 {
5245 return VERR_INVALID_PARAMETER;
5246 }
5247
5248 /* Choose the rendering function. */
5249 switch(u32SrcBitsPerPixel)
5250 {
5251 default:
5252 case 0:
5253 /* Nothing to do, just return. */
5254 return VINF_SUCCESS;
5255 case 8:
5256 v = VGA_DRAW_LINE8;
5257 break;
5258 case 15:
5259 v = VGA_DRAW_LINE15;
5260 break;
5261 case 16:
5262 v = VGA_DRAW_LINE16;
5263 break;
5264 case 24:
5265 v = VGA_DRAW_LINE24;
5266 break;
5267 case 32:
5268 v = VGA_DRAW_LINE32;
5269 break;
5270 }
5271
5272 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5273 AssertRC(rc);
5274
5275 /* This method only works if the VGA device is in a VBE mode. */
5276 if ((pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0)
5277 {
5278 PDMCritSectLeave(&pThis->CritSect);
5279 return VERR_INVALID_STATE;
5280 }
5281
5282 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(u32DstBitsPerPixel)];
5283
5284 /* Compute source and destination addresses and pitches. */
5285 cbPixelDst = (u32DstBitsPerPixel + 7) / 8;
5286 cbLineDst = u32DstLineSize;
5287 pu8DstPtr = pu8Dst + yDst * cbLineDst + xDst * cbPixelDst;
5288
5289 cbPixelSrc = (u32SrcBitsPerPixel + 7) / 8;
5290 cbLineSrc = u32SrcLineSize;
5291 pu8SrcPtr = pu8Src + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5292
5293#ifdef DEBUG_sunlover
5294 LogFlow(("vgaPortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8DstPtr, cbLineDst, cbPixelDst, pu8SrcPtr, cbLineSrc, cbPixelSrc));
5295#endif /* DEBUG_sunlover */
5296
5297 while (hCorrected-- > 0)
5298 {
5299 vga_draw_line (pThis, pu8DstPtr, pu8SrcPtr, wCorrected);
5300 pu8DstPtr += cbLineDst;
5301 pu8SrcPtr += cbLineSrc;
5302 }
5303
5304 PDMCritSectLeave(&pThis->CritSect);
5305#ifdef DEBUG_sunlover
5306 LogFlow(("vgaPortCopyRect: completed.\n"));
5307#endif /* DEBUG_sunlover */
5308
5309 return VINF_SUCCESS;
5310}
5311
5312static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5313{
5314 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5315
5316 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
5317
5318 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5319 AssertRC(rc);
5320
5321 pThis->fRenderVRAM = fRender;
5322
5323 PDMCritSectLeave(&pThis->CritSect);
5324}
5325
5326
5327static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5328{
5329 PVGASTATE pThis = (PVGASTATE)pvUser;
5330 NOREF(pDevIns);
5331
5332 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5333 {
5334 VBVARaiseIrq(pThis, HGSMIHOSTFLAGS_VSYNC);
5335 }
5336
5337 if (pThis->pDrv)
5338 pThis->pDrv->pfnRefresh(pThis->pDrv);
5339
5340 if (pThis->cMilliesRefreshInterval)
5341 TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
5342
5343#ifdef VBOX_WITH_VIDEOHWACCEL
5344 vbvaTimerCb(pThis);
5345#endif
5346
5347#ifdef VBOX_WITH_CRHGSMI
5348 vboxCmdVBVACmdTimer(pThis);
5349#endif
5350}
5351
5352#ifdef VBOX_WITH_VMSVGA
5353int vgaR3RegisterVRAMHandler(PVGASTATE pVGAState, uint64_t cbFrameBuffer)
5354{
5355 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5356 Assert(pVGAState->GCPhysVRAM);
5357
5358 int rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5359 pVGAState->GCPhysVRAM, pVGAState->GCPhysVRAM + (cbFrameBuffer - 1),
5360 pVGAState->hLfbAccessHandlerType, pVGAState, pDevIns->pvInstanceDataR0,
5361 pDevIns->pvInstanceDataRC, "VGA LFB");
5362
5363 AssertRC(rc);
5364 return rc;
5365}
5366
5367int vgaR3UnregisterVRAMHandler(PVGASTATE pVGAState)
5368{
5369 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5370
5371 Assert(pVGAState->GCPhysVRAM);
5372 int rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pVGAState->GCPhysVRAM);
5373 AssertRC(rc);
5374 return rc;
5375}
5376#endif
5377
5378/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5379
5380/**
5381 * Callback function for unmapping and/or mapping the VRAM MMIO2 region (called by the PCI bus).
5382 *
5383 * @return VBox status code.
5384 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
5385 * @param iRegion The region number.
5386 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
5387 * I/O port, else it's a physical address.
5388 * This address is *NOT* relative to pci_mem_base like earlier!
5389 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
5390 */
5391static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5392{
5393 int rc;
5394 PPDMDEVINS pDevIns = pPciDev->pDevIns;
5395 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5396 Log(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5397#ifdef VBOX_WITH_VMSVGA
5398 AssertReturn((iRegion == ((pThis->fVMSVGAEnabled) ? 1 : 0)) && (enmType == ((pThis->fVMSVGAEnabled) ? PCI_ADDRESS_SPACE_MEM : PCI_ADDRESS_SPACE_MEM_PREFETCH)), VERR_INTERNAL_ERROR);
5399#else
5400 AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5401#endif
5402
5403 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5404 AssertRC(rc);
5405
5406 if (GCPhysAddress != NIL_RTGCPHYS)
5407 {
5408 /*
5409 * Mapping the VRAM.
5410 */
5411 rc = PDMDevHlpMMIO2Map(pDevIns, iRegion, GCPhysAddress);
5412 AssertRC(rc);
5413 if (RT_SUCCESS(rc))
5414 {
5415 rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns), GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5416 pThis->hLfbAccessHandlerType, pThis, pDevIns->pvInstanceDataR0,
5417 pDevIns->pvInstanceDataRC, "VGA LFB");
5418 AssertRC(rc);
5419 if (RT_SUCCESS(rc))
5420 {
5421 pThis->GCPhysVRAM = GCPhysAddress;
5422 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5423 }
5424 }
5425 }
5426 else
5427 {
5428 /*
5429 * Unmapping of the VRAM in progress.
5430 * Deregister the access handler so PGM doesn't get upset.
5431 */
5432 Assert(pThis->GCPhysVRAM);
5433#ifdef VBOX_WITH_VMSVGA
5434 Assert(!pThis->svga.fEnabled || !pThis->svga.fVRAMTracking);
5435 if ( !pThis->svga.fEnabled
5436 || ( pThis->svga.fEnabled
5437 && pThis->svga.fVRAMTracking
5438 )
5439 )
5440 {
5441#endif
5442 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5443 AssertRC(rc);
5444#ifdef VBOX_WITH_VMSVGA
5445 }
5446 else
5447 rc = VINF_SUCCESS;
5448#endif
5449 pThis->GCPhysVRAM = 0;
5450 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5451 }
5452 PDMCritSectLeave(&pThis->CritSect);
5453 return rc;
5454}
5455
5456
5457/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5458
5459/**
5460 * Saves a important bits of the VGA device config.
5461 *
5462 * @param pThis The VGA instance data.
5463 * @param pSSM The saved state handle.
5464 */
5465static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
5466{
5467 SSMR3PutU32(pSSM, pThis->vram_size);
5468 SSMR3PutU32(pSSM, pThis->cMonitors);
5469}
5470
5471
5472/**
5473 * @copydoc FNSSMDEVLIVEEXEC
5474 */
5475static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5476{
5477 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5478 Assert(uPass == 0); NOREF(uPass);
5479 vgaR3SaveConfig(pThis, pSSM);
5480 return VINF_SSM_DONT_CALL_AGAIN;
5481}
5482
5483
5484/**
5485 * @copydoc FNSSMDEVSAVEPREP
5486 */
5487static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5488{
5489#ifdef VBOX_WITH_VIDEOHWACCEL
5490 return vboxVBVASaveStatePrep(pDevIns, pSSM);
5491#else
5492 return VINF_SUCCESS;
5493#endif
5494}
5495
5496static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5497{
5498#ifdef VBOX_WITH_VIDEOHWACCEL
5499 return vboxVBVASaveStateDone(pDevIns, pSSM);
5500#else
5501 return VINF_SUCCESS;
5502#endif
5503}
5504
5505/**
5506 * @copydoc FNSSMDEVSAVEEXEC
5507 */
5508static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5509{
5510 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5511
5512#ifdef VBOX_WITH_VDMA
5513 vboxVDMASaveStateExecPrep(pThis->pVdma, pSSM);
5514#endif
5515
5516 vgaR3SaveConfig(pThis, pSSM);
5517 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5518
5519#ifdef VBOX_WITH_HGSMI
5520 SSMR3PutBool(pSSM, true);
5521 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5522#else
5523 int rc = SSMR3PutBool(pSSM, false);
5524#endif
5525
5526 AssertRCReturn(rc, rc);
5527
5528#ifdef VBOX_WITH_VDMA
5529 rc = SSMR3PutU32(pSSM, 1);
5530 AssertRCReturn(rc, rc);
5531 rc = vboxVDMASaveStateExecPerform(pThis->pVdma, pSSM);
5532#else
5533 rc = SSMR3PutU32(pSSM, 0);
5534#endif
5535 AssertRCReturn(rc, rc);
5536
5537#ifdef VBOX_WITH_VDMA
5538 vboxVDMASaveStateExecDone(pThis->pVdma, pSSM);
5539#endif
5540
5541#ifdef VBOX_WITH_VMSVGA
5542 if (pThis->fVMSVGAEnabled)
5543 {
5544 rc = vmsvgaSaveExec(pDevIns, pSSM);
5545 AssertRCReturn(rc, rc);
5546 }
5547#endif
5548
5549 return rc;
5550}
5551
5552
5553/**
5554 * @copydoc FNSSMDEVSAVEEXEC
5555 */
5556static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5557{
5558 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5559 int rc;
5560
5561 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5562 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5563
5564 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5565 {
5566 /* Check the config */
5567 uint32_t cbVRam;
5568 rc = SSMR3GetU32(pSSM, &cbVRam);
5569 AssertRCReturn(rc, rc);
5570 if (pThis->vram_size != cbVRam)
5571 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5572
5573 uint32_t cMonitors;
5574 rc = SSMR3GetU32(pSSM, &cMonitors);
5575 AssertRCReturn(rc, rc);
5576 if (pThis->cMonitors != cMonitors)
5577 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5578 }
5579
5580 if (uPass == SSM_PASS_FINAL)
5581 {
5582 rc = vga_load(pSSM, pThis, uVersion);
5583 if (RT_FAILURE(rc))
5584 return rc;
5585 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5586 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5587 {
5588 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5589 AssertRCReturn(rc, rc);
5590 }
5591 if (fWithHgsmi)
5592 {
5593#ifdef VBOX_WITH_HGSMI
5594 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5595 AssertRCReturn(rc, rc);
5596#else
5597 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5598#endif
5599 }
5600
5601 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5602 {
5603 uint32_t u32;
5604 rc = SSMR3GetU32(pSSM, &u32);
5605 if (u32)
5606 {
5607#ifdef VBOX_WITH_VDMA
5608 if (u32 == 1)
5609 {
5610 rc = vboxVDMASaveLoadExecPerform(pThis->pVdma, pSSM, uVersion);
5611 AssertRCReturn(rc, rc);
5612 }
5613 else
5614#endif
5615 {
5616 LogRel(("invalid CmdVbva version info\n"));
5617 return VERR_VERSION_MISMATCH;
5618 }
5619 }
5620 }
5621
5622#ifdef VBOX_WITH_VMSVGA
5623 if (pThis->fVMSVGAEnabled)
5624 {
5625 rc = vmsvgaLoadExec(pDevIns, pSSM, uVersion, uPass);
5626 AssertRCReturn(rc, rc);
5627 }
5628#endif
5629 }
5630 return VINF_SUCCESS;
5631}
5632
5633
5634/**
5635 * @copydoc FNSSMDEVLOADDONE
5636 */
5637static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5638{
5639 int rc = VINF_SUCCESS;
5640
5641#ifdef VBOX_WITH_HGSMI
5642 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5643 VBVAPause(pThis, (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0);
5644 rc = vboxVBVALoadStateDone(pDevIns, pSSM);
5645 AssertRCReturn(rc, rc);
5646# ifdef VBOX_WITH_VDMA
5647 rc = vboxVDMASaveLoadDone(pThis->pVdma);
5648 AssertRCReturn(rc, rc);
5649# endif
5650#endif
5651#ifdef VBOX_WITH_VMSVGA
5652 if (pThis->fVMSVGAEnabled)
5653 {
5654 rc = vmsvgaLoadDone(pDevIns);
5655 AssertRCReturn(rc, rc);
5656 }
5657#endif
5658 return rc;
5659}
5660
5661
5662/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5663
5664/**
5665 * @interface_method_impl{PDMDEVREG,pfnReset}
5666 */
5667static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5668{
5669 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5670 char *pchStart;
5671 char *pchEnd;
5672 LogFlow(("vgaReset\n"));
5673
5674 if (pThis->pVdma)
5675 vboxVDMAReset(pThis->pVdma);
5676
5677#ifdef VBOX_WITH_VMSVGA
5678 if (pThis->fVMSVGAEnabled)
5679 vmsvgaReset(pDevIns);
5680#endif
5681
5682#ifdef VBOX_WITH_HGSMI
5683 VBVAReset(pThis);
5684#endif /* VBOX_WITH_HGSMI */
5685
5686
5687 /* Clear the VRAM ourselves. */
5688 if (pThis->vram_ptrR3 && pThis->vram_size)
5689 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5690
5691 /*
5692 * Zero most of it.
5693 *
5694 * Unlike vga_reset we're leaving out a few members which we believe
5695 * must remain unchanged....
5696 */
5697 /* 1st part. */
5698 pchStart = (char *)&pThis->latch;
5699 pchEnd = (char *)&pThis->invalidated_y_table;
5700 memset(pchStart, 0, pchEnd - pchStart);
5701
5702 /* 2nd part. */
5703 pchStart = (char *)&pThis->last_palette;
5704 pchEnd = (char *)&pThis->u32Marker;
5705 memset(pchStart, 0, pchEnd - pchStart);
5706
5707
5708 /*
5709 * Restore and re-init some bits.
5710 */
5711 pThis->get_bpp = vga_get_bpp;
5712 pThis->get_offsets = vga_get_offsets;
5713 pThis->get_resolution = vga_get_resolution;
5714 pThis->graphic_mode = -1; /* Force full update. */
5715#ifdef CONFIG_BOCHS_VBE
5716 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5717 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5718 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
5719 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5720#endif /* CONFIG_BOCHS_VBE */
5721
5722 /*
5723 * Reset the LBF mapping.
5724 */
5725 pThis->fLFBUpdated = false;
5726 if ( ( pThis->fGCEnabled
5727 || pThis->fR0Enabled)
5728 && pThis->GCPhysVRAM
5729 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
5730 {
5731 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5732 AssertRC(rc);
5733 }
5734 if (pThis->fRemappedVGA)
5735 {
5736 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5737 pThis->fRemappedVGA = false;
5738 }
5739
5740 /*
5741 * Reset the logo data.
5742 */
5743 pThis->LogoCommand = LOGO_CMD_NOP;
5744 pThis->offLogoData = 0;
5745
5746 /* notify port handler */
5747 if (pThis->pDrv)
5748 {
5749 PDMCritSectLeave(&pThis->CritSect); /* hack around lock order issue. */
5750 pThis->pDrv->pfnReset(pThis->pDrv);
5751 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
5752 }
5753
5754 /* Reset latched access mask. */
5755 pThis->uMaskLatchAccess = 0x3ff;
5756 pThis->cLatchAccesses = 0;
5757 pThis->u64LastLatchedAccess = 0;
5758 pThis->iMask = 0;
5759
5760 /* Reset retrace emulation. */
5761 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5762}
5763
5764
5765/**
5766 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5767 */
5768static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5769{
5770 if (offDelta)
5771 {
5772 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5773 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5774
5775 pThis->vram_ptrRC += offDelta;
5776 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5777 }
5778}
5779
5780
5781/**
5782 * @interface_method_impl{PDMDEVREG,pfnAttach}
5783 *
5784 * This is like plugging in the monitor after turning on the PC.
5785 */
5786static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5787{
5788 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5789
5790 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5791 ("VGA device does not support hotplugging\n"),
5792 VERR_INVALID_PARAMETER);
5793
5794 switch (iLUN)
5795 {
5796 /* LUN #0: Display port. */
5797 case 0:
5798 {
5799 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5800 if (RT_SUCCESS(rc))
5801 {
5802 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5803 if (pThis->pDrv)
5804 {
5805 /* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
5806 if ( pThis->pDrv->pfnRefresh
5807 && pThis->pDrv->pfnResize
5808 && pThis->pDrv->pfnUpdateRect)
5809 rc = VINF_SUCCESS;
5810 else
5811 {
5812 Assert(pThis->pDrv->pfnRefresh);
5813 Assert(pThis->pDrv->pfnResize);
5814 Assert(pThis->pDrv->pfnUpdateRect);
5815 pThis->pDrv = NULL;
5816 pThis->pDrvBase = NULL;
5817 rc = VERR_INTERNAL_ERROR;
5818 }
5819#ifdef VBOX_WITH_VIDEOHWACCEL
5820 if(rc == VINF_SUCCESS)
5821 {
5822 rc = vbvaVHWAConstruct(pThis);
5823 if (rc != VERR_NOT_IMPLEMENTED)
5824 AssertRC(rc);
5825 }
5826#endif
5827 }
5828 else
5829 {
5830 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5831 pThis->pDrvBase = NULL;
5832 rc = VERR_PDM_MISSING_INTERFACE;
5833 }
5834 }
5835 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5836 {
5837 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5838 rc = VINF_SUCCESS;
5839 }
5840 else
5841 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5842 return rc;
5843 }
5844
5845 default:
5846 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5847 return VERR_PDM_NO_SUCH_LUN;
5848 }
5849}
5850
5851
5852/**
5853 * @interface_method_impl{PDMDEVREG,pfnDetach}
5854 *
5855 * This is like unplugging the monitor while the PC is still running.
5856 */
5857static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5858{
5859 /*
5860 * Reset the interfaces and update the controller state.
5861 */
5862 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5863
5864 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5865 ("VGA device does not support hotplugging\n"));
5866
5867 switch (iLUN)
5868 {
5869 /* LUN #0: Display port. */
5870 case 0:
5871 pThis->pDrv = NULL;
5872 pThis->pDrvBase = NULL;
5873 break;
5874
5875 default:
5876 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5877 break;
5878 }
5879}
5880
5881
5882/**
5883 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5884 */
5885static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5886{
5887 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5888
5889#ifdef VBE_NEW_DYN_LIST
5890 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5891 LogFlow(("vgaR3Destruct:\n"));
5892
5893# ifdef VBOX_WITH_VDMA
5894 if (pThis->pVdma)
5895 vboxVDMADestruct(pThis->pVdma);
5896# endif
5897
5898#ifdef VBOX_WITH_VMSVGA
5899 if (pThis->fVMSVGAEnabled)
5900 vmsvgaDestruct(pDevIns);
5901#endif
5902
5903 /*
5904 * Free MM heap pointers.
5905 */
5906 if (pThis->pu8VBEExtraData)
5907 {
5908 MMR3HeapFree(pThis->pu8VBEExtraData);
5909 pThis->pu8VBEExtraData = NULL;
5910 }
5911#endif /* VBE_NEW_DYN_LIST */
5912 if (pThis->pu8VgaBios)
5913 {
5914 MMR3HeapFree(pThis->pu8VgaBios);
5915 pThis->pu8VgaBios = NULL;
5916 }
5917
5918 if (pThis->pszVgaBiosFile)
5919 {
5920 MMR3HeapFree(pThis->pszVgaBiosFile);
5921 pThis->pszVgaBiosFile = NULL;
5922 }
5923
5924 if (pThis->pszLogoFile)
5925 {
5926 MMR3HeapFree(pThis->pszLogoFile);
5927 pThis->pszLogoFile = NULL;
5928 }
5929
5930 PDMR3CritSectDelete(&pThis->CritSect);
5931 return VINF_SUCCESS;
5932}
5933
5934
5935/**
5936 * Adjust VBE mode information
5937 *
5938 * Depending on the configured VRAM size, certain parts of VBE mode
5939 * information must be updated.
5940 *
5941 * @param pThis The device instance data.
5942 * @param pMode The mode information structure.
5943 */
5944static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
5945{
5946 int maxPage;
5947 int bpl;
5948
5949
5950 /* For 4bpp modes, the planes are "stacked" on top of each other. */
5951 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
5952 /* The "number of image pages" is really the max page index... */
5953 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
5954 Assert(maxPage >= 0);
5955 if (maxPage > 255)
5956 maxPage = 255; /* 8-bit value. */
5957 pMode->info.NumberOfImagePages = maxPage;
5958 pMode->info.LinNumberOfPages = maxPage;
5959}
5960
5961
5962/**
5963 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5964 */
5965static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5966{
5967
5968 static bool s_fExpandDone = false;
5969 int rc;
5970 unsigned i;
5971#ifdef VBE_NEW_DYN_LIST
5972 uint32_t cCustomModes;
5973 uint32_t cyReduction;
5974 uint32_t cbPitch;
5975 PVBEHEADER pVBEDataHdr;
5976 ModeInfoListItem *pCurMode;
5977 unsigned cb;
5978#endif
5979 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5980 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5981 PVM pVM = PDMDevHlpGetVM(pDevIns);
5982
5983 Assert(iInstance == 0);
5984 Assert(pVM);
5985
5986 /*
5987 * Init static data.
5988 */
5989 if (!s_fExpandDone)
5990 {
5991 s_fExpandDone = true;
5992 vga_init_expand();
5993 }
5994
5995 /*
5996 * Validate configuration.
5997 */
5998 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
5999 "MonitorCount\0"
6000 "GCEnabled\0"
6001 "R0Enabled\0"
6002 "FadeIn\0"
6003 "FadeOut\0"
6004 "LogoTime\0"
6005 "LogoFile\0"
6006 "ShowBootMenu\0"
6007 "BiosRom\0"
6008 "RealRetrace\0"
6009 "CustomVideoModes\0"
6010 "HeightReduction\0"
6011 "CustomVideoMode1\0"
6012 "CustomVideoMode2\0"
6013 "CustomVideoMode3\0"
6014 "CustomVideoMode4\0"
6015 "CustomVideoMode5\0"
6016 "CustomVideoMode6\0"
6017 "CustomVideoMode7\0"
6018 "CustomVideoMode8\0"
6019 "CustomVideoMode9\0"
6020 "CustomVideoMode10\0"
6021 "CustomVideoMode11\0"
6022 "CustomVideoMode12\0"
6023 "CustomVideoMode13\0"
6024 "CustomVideoMode14\0"
6025 "CustomVideoMode15\0"
6026 "CustomVideoMode16\0"
6027 "MaxBiosXRes\0"
6028 "MaxBiosYRes\0"
6029#ifdef VBOX_WITH_VMSVGA
6030 "VMSVGAEnabled\0"
6031#endif
6032#ifdef VBOX_WITH_VMSVGA3D
6033 "VMSVGA3dEnabled\0"
6034 "HostWindowId\0"
6035#endif
6036 ))
6037 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
6038 N_("Invalid configuration for vga device"));
6039
6040 /*
6041 * Init state data.
6042 */
6043 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6044 AssertLogRelRCReturn(rc, rc);
6045 if (pThis->vram_size > VGA_VRAM_MAX)
6046 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6047 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6048 if (pThis->vram_size < VGA_VRAM_MIN)
6049 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6050 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6051 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6052 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6053 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6054
6055 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6056 AssertLogRelRCReturn(rc, rc);
6057
6058 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
6059 AssertLogRelRCReturn(rc, rc);
6060
6061 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
6062 AssertLogRelRCReturn(rc, rc);
6063 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
6064
6065#ifdef VBOX_WITH_VMSVGA
6066 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6067 AssertLogRelRCReturn(rc, rc);
6068 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6069#endif
6070#ifdef VBOX_WITH_VMSVGA3D
6071 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6072 AssertLogRelRCReturn(rc, rc);
6073 rc = CFGMR3QueryU64Def(pCfg, "HostWindowId", &pThis->svga.u64HostWindowId, 0);
6074 AssertLogRelRCReturn(rc, rc);
6075 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6076 Log(("VMSVGA: HostWindowId = 0x%x\n", pThis->svga.u64HostWindowId));
6077#endif
6078
6079 pThis->pDevInsR3 = pDevIns;
6080 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
6081 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6082
6083 vgaR3Reset(pDevIns);
6084
6085 /* The PCI devices configuration. */
6086#ifdef VBOX_WITH_VMSVGA
6087 if (pThis->fVMSVGAEnabled)
6088 {
6089 /* Extend our VGA device with VMWare SVGA functionality. */
6090 PCIDevSetVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6091 PCIDevSetDeviceId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6092 PCIDevSetSubSystemVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6093 PCIDevSetSubSystemId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6094 }
6095 else
6096 {
6097#endif /* VBOX_WITH_VMSVGA */
6098 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
6099 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
6100#ifdef VBOX_WITH_VMSVGA
6101 }
6102#endif
6103 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
6104 PCIDevSetClassBase( &pThis->Dev, 0x03);
6105 PCIDevSetHeaderType(&pThis->Dev, 0x00);
6106#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6107 PCIDevSetInterruptPin(&pThis->Dev, 1);
6108#endif
6109
6110 /* the interfaces. */
6111 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
6112
6113 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
6114 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
6115 pThis->IPort.pfnQueryVideoMode = vgaPortQueryVideoMode;
6116 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
6117 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
6118 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
6119 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
6120 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
6121 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
6122 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
6123#ifdef VBOX_WITH_VMSVGA
6124 pThis->IPort.pfnSetViewPort = vmsvgaPortSetViewPort;
6125#endif
6126 pThis->IPort.pfnSendModeHint = vbvaPortSendModeHint;
6127 pThis->IPort.pfnReportHostCursorCapabilities
6128 = vbvaPortReportHostCursorCapabilities;
6129 pThis->IPort.pfnReportHostCursorPosition
6130 = vbvaPortReportHostCursorPosition;
6131
6132#if defined(VBOX_WITH_HGSMI)
6133# if defined(VBOX_WITH_VIDEOHWACCEL)
6134 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaVHWACommandCompleteAsync;
6135# endif
6136#if defined(VBOX_WITH_CRHGSMI)
6137 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
6138 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
6139
6140 pThis->IVBVACallbacks.pfnCrCtlSubmit = vboxCmdVBVACmdHostCtl;
6141 pThis->IVBVACallbacks.pfnCrCtlSubmitSync = vboxCmdVBVACmdHostCtlSync;
6142# endif
6143#endif
6144
6145 pThis->ILeds.pfnQueryStatusLed = vgaPortQueryStatusLed;
6146
6147 RT_ZERO(pThis->Led3D);
6148 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6149
6150 /*
6151 * We use our own critical section to avoid unncessary pointer indirections
6152 * in interface methods (as well as for historical reasons).
6153 */
6154 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6155 AssertRCReturn(rc, rc);
6156 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6157 AssertRCReturn(rc, rc);
6158
6159 /*
6160 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
6161 */
6162#ifdef VBOX_WITH_VMSVGA
6163 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
6164
6165 if (pThis->fVMSVGAEnabled)
6166 {
6167 /*
6168 * Allocate and initialize the FIFO MMIO2 memory.
6169 */
6170 rc = PDMDevHlpMMIO2Register(pDevIns, 2 /*iRegion*/, VMSVGA_FIFO_SIZE, 0 /*fFlags*/, (void **)&pThis->svga.pFIFOR3, "VMSVGA-FIFO");
6171 if (RT_FAILURE(rc))
6172 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6173 N_("Failed to allocate %u bytes of memory for the VMSVGA device"), VMSVGA_FIFO_SIZE);
6174 pThis->svga.pFIFOR0 = (RTR0PTR)pThis->svga.pFIFOR3;
6175 pThis->svga.cbFIFO = VMSVGA_FIFO_SIZE;
6176 }
6177#else
6178 int iPCIRegionVRAM = 0;
6179#endif
6180 rc = PDMDevHlpMMIO2Register(pDevIns, iPCIRegionVRAM, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
6181 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
6182 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo @bugref{1865} Map parts into R0 or just use PGM access (Mac only). */
6183
6184 if (pThis->fGCEnabled)
6185 {
6186 RTRCPTR pRCMapping = 0;
6187 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
6188 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6189 pThis->vram_ptrRC = pRCMapping;
6190# ifdef VBOX_WITH_VMSVGA
6191 /* Don't need a mapping in RC */
6192# endif
6193 }
6194
6195#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
6196 if (pThis->fR0Enabled)
6197 {
6198 RTR0PTR pR0Mapping = 0;
6199 rc = PDMDevHlpMMIO2MapKernel(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
6200 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6201 pThis->vram_ptrR0 = pR0Mapping;
6202# ifdef VBOX_WITH_VMSVGA
6203 if (pThis->fVMSVGAEnabled)
6204 {
6205 RTR0PTR pR0Mapping = 0;
6206 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 2 /* iRegion */, 0 /* off */, VMSVGA_FIFO_SIZE, "VMSVGA-FIFO", &pR0Mapping);
6207 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VMSVGA_FIFO_SIZE, rc), rc);
6208 pThis->svga.pFIFOR0 = pR0Mapping;
6209 }
6210# endif
6211 }
6212#endif
6213
6214 /*
6215 * Register access handler types.
6216 */
6217 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
6218 vgaLFBAccessHandler,
6219 g_DeviceVga.szR0Mod, NULL, "vgaLbfAccessPfHandler",
6220 g_DeviceVga.szRCMod, NULL, "vgaLbfAccessPfHandler",
6221 "VGA LFB", &pThis->hLfbAccessHandlerType);
6222 AssertRCReturn(rc, rc);
6223
6224
6225 /*
6226 * Register I/O ports.
6227 */
6228 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
6229 if (RT_FAILURE(rc))
6230 return rc;
6231 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
6232 if (RT_FAILURE(rc))
6233 return rc;
6234 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
6235 if (RT_FAILURE(rc))
6236 return rc;
6237 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
6238 if (RT_FAILURE(rc))
6239 return rc;
6240 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
6241 if (RT_FAILURE(rc))
6242 return rc;
6243#ifdef VBOX_WITH_HGSMI
6244 /* Use reserved VGA IO ports for HGSMI. */
6245 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
6246 if (RT_FAILURE(rc))
6247 return rc;
6248 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
6249 if (RT_FAILURE(rc))
6250 return rc;
6251#endif /* VBOX_WITH_HGSMI */
6252
6253#ifdef CONFIG_BOCHS_VBE
6254 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
6255 if (RT_FAILURE(rc))
6256 return rc;
6257 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
6258 if (RT_FAILURE(rc))
6259 return rc;
6260#endif /* CONFIG_BOCHS_VBE */
6261
6262 /* guest context extension */
6263 if (pThis->fGCEnabled)
6264 {
6265 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6266 if (RT_FAILURE(rc))
6267 return rc;
6268 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6269 if (RT_FAILURE(rc))
6270 return rc;
6271 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6272 if (RT_FAILURE(rc))
6273 return rc;
6274 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6275 if (RT_FAILURE(rc))
6276 return rc;
6277 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6278 if (RT_FAILURE(rc))
6279 return rc;
6280#ifdef CONFIG_BOCHS_VBE
6281 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6282 if (RT_FAILURE(rc))
6283 return rc;
6284 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6285 if (RT_FAILURE(rc))
6286 return rc;
6287#endif /* CONFIG_BOCHS_VBE */
6288 }
6289
6290 /* R0 context extension */
6291 if (pThis->fR0Enabled)
6292 {
6293 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6294 if (RT_FAILURE(rc))
6295 return rc;
6296 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6297 if (RT_FAILURE(rc))
6298 return rc;
6299 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6300 if (RT_FAILURE(rc))
6301 return rc;
6302 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6303 if (RT_FAILURE(rc))
6304 return rc;
6305 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6306 if (RT_FAILURE(rc))
6307 return rc;
6308#ifdef CONFIG_BOCHS_VBE
6309 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6310 if (RT_FAILURE(rc))
6311 return rc;
6312 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6313 if (RT_FAILURE(rc))
6314 return rc;
6315#endif /* CONFIG_BOCHS_VBE */
6316 }
6317
6318 /* vga mmio */
6319 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
6320 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
6321 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
6322 if (RT_FAILURE(rc))
6323 return rc;
6324 if (pThis->fGCEnabled)
6325 {
6326 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6327 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6328 if (RT_FAILURE(rc))
6329 return rc;
6330 }
6331 if (pThis->fR0Enabled)
6332 {
6333 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6334 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6335 if (RT_FAILURE(rc))
6336 return rc;
6337 }
6338
6339 /* vga bios */
6340 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6341 if (RT_FAILURE(rc))
6342 return rc;
6343 if (pThis->fR0Enabled)
6344 {
6345 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6346 if (RT_FAILURE(rc))
6347 return rc;
6348 }
6349
6350 /*
6351 * Get the VGA BIOS ROM file name.
6352 */
6353 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6354 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6355 {
6356 pThis->pszVgaBiosFile = NULL;
6357 rc = VINF_SUCCESS;
6358 }
6359 else if (RT_FAILURE(rc))
6360 return PDMDEV_SET_ERROR(pDevIns, rc,
6361 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6362 else if (!*pThis->pszVgaBiosFile)
6363 {
6364 MMR3HeapFree(pThis->pszVgaBiosFile);
6365 pThis->pszVgaBiosFile = NULL;
6366 }
6367
6368 /*
6369 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6370 */
6371 RTFILE FileVgaBios = NIL_RTFILE;
6372 if (pThis->pszVgaBiosFile)
6373 {
6374 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6375 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6376 if (RT_SUCCESS(rc))
6377 {
6378 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6379 if (RT_SUCCESS(rc))
6380 {
6381 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6382 || pThis->cbVgaBios > _64K
6383 || pThis->cbVgaBios < 16 * _1K)
6384 rc = VERR_TOO_MUCH_DATA;
6385 }
6386 }
6387 if (RT_FAILURE(rc))
6388 {
6389 /*
6390 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6391 */
6392 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6393 RTFileClose(FileVgaBios);
6394 FileVgaBios = NIL_RTFILE;
6395 MMR3HeapFree(pThis->pszVgaBiosFile);
6396 pThis->pszVgaBiosFile = NULL;
6397 }
6398 }
6399
6400 /*
6401 * Attempt to get the VGA BIOS ROM data from file.
6402 */
6403 if (pThis->pszVgaBiosFile)
6404 {
6405 /*
6406 * Allocate buffer for the VGA BIOS ROM data.
6407 */
6408 pThis->pu8VgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6409 if (pThis->pu8VgaBios)
6410 {
6411 rc = RTFileRead(FileVgaBios, pThis->pu8VgaBios, pThis->cbVgaBios, NULL);
6412 if (RT_FAILURE(rc))
6413 {
6414 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6415 MMR3HeapFree(pThis->pu8VgaBios);
6416 pThis->pu8VgaBios = NULL;
6417 }
6418 rc = VINF_SUCCESS;
6419 }
6420 else
6421 rc = VERR_NO_MEMORY;
6422 }
6423 else
6424 pThis->pu8VgaBios = NULL;
6425
6426 /* cleanup */
6427 if (FileVgaBios != NIL_RTFILE)
6428 RTFileClose(FileVgaBios);
6429
6430 /* If we were unable to get the data from file for whatever reason, fall
6431 back to the built-in ROM image. */
6432 const uint8_t *pu8VgaBiosBinary;
6433 uint64_t cbVgaBiosBinary;
6434 uint32_t fFlags = 0;
6435 if (pThis->pu8VgaBios == NULL)
6436 {
6437 pu8VgaBiosBinary = g_abVgaBiosBinary;
6438 cbVgaBiosBinary = g_cbVgaBiosBinary;
6439 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6440 }
6441 else
6442 {
6443 pu8VgaBiosBinary = pThis->pu8VgaBios;
6444 cbVgaBiosBinary = pThis->cbVgaBios;
6445 }
6446
6447 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6448 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6449 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6450 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pu8VgaBiosBinary, cbVgaBiosBinary,
6451 fFlags, "VGA BIOS");
6452 if (RT_FAILURE(rc))
6453 return rc;
6454
6455 /*
6456 * Saved state.
6457 */
6458 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6459 NULL, vgaR3LiveExec, NULL,
6460 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6461 NULL, vgaR3LoadExec, vgaR3LoadDone);
6462 if (RT_FAILURE(rc))
6463 return rc;
6464
6465 /*
6466 * PCI device registration.
6467 */
6468 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6469 if (RT_FAILURE(rc))
6470 return rc;
6471 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
6472 if (pThis->Dev.devfn != 16 && iInstance == 0)
6473 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
6474
6475#ifdef VBOX_WITH_VMSVGA
6476 if (pThis->fVMSVGAEnabled)
6477 {
6478 /* Register the io command ports. */
6479 rc = PDMDevHlpPCIIORegionRegister (pDevIns, 0 /* iRegion */, 0x10, PCI_ADDRESS_SPACE_IO, vmsvgaR3IORegionMap);
6480 if (RT_FAILURE (rc))
6481 return rc;
6482 /* VMware's MetalKit doesn't like PCI_ADDRESS_SPACE_MEM_PREFETCH */
6483 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vgaR3IORegionMap);
6484 if (RT_FAILURE(rc))
6485 return rc;
6486 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2 /* iRegion */, VMSVGA_FIFO_SIZE, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vmsvgaR3IORegionMap);
6487 if (RT_FAILURE(rc))
6488 return rc;
6489 }
6490 else
6491#endif /* VBOX_WITH_VMSVGA */
6492 rc = PDMDevHlpPCIIORegionRegister(pDevIns, iPCIRegionVRAM, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6493 if (RT_FAILURE(rc))
6494 return rc;
6495
6496 /*
6497 * Create the refresh timer.
6498 */
6499 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6500 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6501 "VGA Refresh Timer", &pThis->RefreshTimer);
6502 if (RT_FAILURE(rc))
6503 return rc;
6504
6505 /*
6506 * Attach to the display.
6507 */
6508 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6509 if (RT_FAILURE(rc))
6510 return rc;
6511
6512 /*
6513 * Initialize the retrace flag.
6514 */
6515 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6516 AssertLogRelRCReturn(rc, rc);
6517
6518#ifdef VBE_NEW_DYN_LIST
6519
6520 uint16_t maxBiosXRes;
6521 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6522 AssertLogRelRCReturn(rc, rc);
6523 uint16_t maxBiosYRes;
6524 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6525 AssertLogRelRCReturn(rc, rc);
6526
6527 /*
6528 * Compute buffer size for the VBE BIOS Extra Data.
6529 */
6530 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6531
6532 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6533 if (RT_SUCCESS(rc) && cyReduction)
6534 cb *= 2; /* Default mode list will be twice long */
6535 else
6536 cyReduction = 0;
6537
6538 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6539 if (RT_SUCCESS(rc) && cCustomModes)
6540 cb += sizeof(ModeInfoListItem) * cCustomModes;
6541 else
6542 cCustomModes = 0;
6543
6544 /*
6545 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6546 */
6547 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6548 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6549 pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6550 if (!pThis->pu8VBEExtraData)
6551 return VERR_NO_MEMORY;
6552
6553 pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
6554 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6555 pVBEDataHdr->cbData = cb;
6556
6557# ifndef VRAM_SIZE_FIX
6558 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6559 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6560# else /* VRAM_SIZE_FIX defined */
6561 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6562 for (i = 0; i < MODE_INFO_SIZE; i++)
6563 {
6564 uint32_t pixelWidth, reqSize;
6565 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6566 pixelWidth = 2;
6567 else
6568 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6569 reqSize = mode_info_list[i].info.XResolution
6570 * mode_info_list[i].info.YResolution
6571 * pixelWidth;
6572 if (reqSize >= pThis->vram_size)
6573 continue;
6574 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6575 || mode_info_list[i].info.YResolution > maxBiosYRes)
6576 continue;
6577 *pCurMode = mode_info_list[i];
6578 vgaAdjustModeInfo(pThis, pCurMode);
6579 pCurMode++;
6580 }
6581# endif /* VRAM_SIZE_FIX defined */
6582
6583 /*
6584 * Copy default modes with subtracted YResolution.
6585 */
6586 if (cyReduction)
6587 {
6588 ModeInfoListItem *pDefMode = mode_info_list;
6589 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6590# ifndef VRAM_SIZE_FIX
6591 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6592 {
6593 *pCurMode = *pDefMode;
6594 pCurMode->mode += 0x30;
6595 pCurMode->info.YResolution -= cyReduction;
6596 }
6597# else /* VRAM_SIZE_FIX defined */
6598 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6599 {
6600 uint32_t pixelWidth, reqSize;
6601 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6602 pixelWidth = 2;
6603 else
6604 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6605 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6606 if (reqSize >= pThis->vram_size)
6607 continue;
6608 if ( pDefMode->info.XResolution > maxBiosXRes
6609 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6610 continue;
6611 *pCurMode = *pDefMode;
6612 pCurMode->mode += 0x30;
6613 pCurMode->info.YResolution -= cyReduction;
6614 pCurMode++;
6615 }
6616# endif /* VRAM_SIZE_FIX defined */
6617 }
6618
6619
6620 /*
6621 * Add custom modes.
6622 */
6623 if (cCustomModes)
6624 {
6625 uint16_t u16CurMode = 0x160;
6626 for (i = 1; i <= cCustomModes; i++)
6627 {
6628 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6629 char *pszExtraData = NULL;
6630
6631 /* query and decode the custom mode string. */
6632 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6633 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6634 if (RT_SUCCESS(rc))
6635 {
6636 ModeInfoListItem *pDefMode = mode_info_list;
6637 unsigned int cx, cy, cBits, cParams, j;
6638 uint16_t u16DefMode;
6639
6640 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6641 if ( cParams != 3
6642 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6643 {
6644 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6645 return VERR_VGA_INVALID_CUSTOM_MODE;
6646 }
6647 cbPitch = calc_line_pitch(cBits, cx);
6648# ifdef VRAM_SIZE_FIX
6649 if (cy * cbPitch >= pThis->vram_size)
6650 {
6651 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",
6652 cx, cy, cBits, pThis->vram_size / _1M));
6653 return VERR_VGA_INVALID_CUSTOM_MODE;
6654 }
6655# endif /* VRAM_SIZE_FIX defined */
6656 MMR3HeapFree(pszExtraData);
6657
6658 /* Use defaults from max@bpp mode. */
6659 switch (cBits)
6660 {
6661 case 8:
6662 u16DefMode = VBE_VESA_MODE_1024X768X8;
6663 break;
6664
6665 case 16:
6666 u16DefMode = VBE_VESA_MODE_1024X768X565;
6667 break;
6668
6669 case 24:
6670 u16DefMode = VBE_VESA_MODE_1024X768X888;
6671 break;
6672
6673 case 32:
6674 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6675 break;
6676
6677 default: /* gcc, shut up! */
6678 AssertMsgFailed(("gone postal!\n"));
6679 continue;
6680 }
6681
6682 /* mode_info_list is not terminated */
6683 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6684 pDefMode++;
6685 Assert(j < MODE_INFO_SIZE);
6686
6687 *pCurMode = *pDefMode;
6688 pCurMode->mode = u16CurMode++;
6689
6690 /* adjust defaults */
6691 pCurMode->info.XResolution = cx;
6692 pCurMode->info.YResolution = cy;
6693 pCurMode->info.BytesPerScanLine = cbPitch;
6694 pCurMode->info.LinBytesPerScanLine = cbPitch;
6695 vgaAdjustModeInfo(pThis, pCurMode);
6696
6697 /* commit it */
6698 pCurMode++;
6699 }
6700 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6701 {
6702 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6703 return rc;
6704 }
6705 } /* foreach custom mode key */
6706 }
6707
6708 /*
6709 * Add the "End of list" mode.
6710 */
6711 memset(pCurMode, 0, sizeof(*pCurMode));
6712 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6713
6714 /*
6715 * Register I/O Port for the VBE BIOS Extra Data.
6716 */
6717 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6718 if (RT_FAILURE(rc))
6719 return rc;
6720#endif /* VBE_NEW_DYN_LIST */
6721
6722 /*
6723 * Register I/O Port for the BIOS Logo.
6724 */
6725 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6726 if (RT_FAILURE(rc))
6727 return rc;
6728
6729 /*
6730 * Register debugger info callbacks.
6731 */
6732 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6733 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6734 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6735 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6736 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6737 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6738 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaInfoPlanar);
6739 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6740 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6741
6742 /*
6743 * Construct the logo header.
6744 */
6745 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6746
6747 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6748 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6749 LogoHdr.fu8FadeIn = 1;
6750 else if (RT_FAILURE(rc))
6751 return PDMDEV_SET_ERROR(pDevIns, rc,
6752 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6753
6754 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6755 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6756 LogoHdr.fu8FadeOut = 1;
6757 else if (RT_FAILURE(rc))
6758 return PDMDEV_SET_ERROR(pDevIns, rc,
6759 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6760
6761 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6762 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6763 LogoHdr.u16LogoMillies = 0;
6764 else if (RT_FAILURE(rc))
6765 return PDMDEV_SET_ERROR(pDevIns, rc,
6766 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6767
6768 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6769 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6770 LogoHdr.fu8ShowBootMenu = 0;
6771 else if (RT_FAILURE(rc))
6772 return PDMDEV_SET_ERROR(pDevIns, rc,
6773 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6774
6775#if defined(DEBUG) && !defined(DEBUG_sunlover)
6776 /* Disable the logo abd menu if all default settings. */
6777 if ( LogoHdr.fu8FadeIn
6778 && LogoHdr.fu8FadeOut
6779 && LogoHdr.u16LogoMillies == 0
6780 && LogoHdr.fu8ShowBootMenu == 2)
6781 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.fu8ShowBootMenu = 0;
6782#endif
6783
6784 /* Delay the logo a little bit */
6785 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6786 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6787
6788 /*
6789 * Get the Logo file name.
6790 */
6791 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6792 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6793 pThis->pszLogoFile = NULL;
6794 else if (RT_FAILURE(rc))
6795 return PDMDEV_SET_ERROR(pDevIns, rc,
6796 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6797 else if (!*pThis->pszLogoFile)
6798 {
6799 MMR3HeapFree(pThis->pszLogoFile);
6800 pThis->pszLogoFile = NULL;
6801 }
6802
6803 /*
6804 * Determine the logo size, open any specified logo file in the process.
6805 */
6806 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6807 RTFILE FileLogo = NIL_RTFILE;
6808 if (pThis->pszLogoFile)
6809 {
6810 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6811 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6812 if (RT_SUCCESS(rc))
6813 {
6814 uint64_t cbFile;
6815 rc = RTFileGetSize(FileLogo, &cbFile);
6816 if (RT_SUCCESS(rc))
6817 {
6818 if (cbFile > 0 && cbFile < 32*_1M)
6819 LogoHdr.cbLogo = (uint32_t)cbFile;
6820 else
6821 rc = VERR_TOO_MUCH_DATA;
6822 }
6823 }
6824 if (RT_FAILURE(rc))
6825 {
6826 /*
6827 * Ignore failure and fall back to the default logo.
6828 */
6829 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6830 if (FileLogo != NIL_RTFILE)
6831 RTFileClose(FileLogo);
6832 FileLogo = NIL_RTFILE;
6833 MMR3HeapFree(pThis->pszLogoFile);
6834 pThis->pszLogoFile = NULL;
6835 }
6836 }
6837
6838 /*
6839 * Disable graphic splash screen if it doesn't fit into VRAM.
6840 */
6841 if (pThis->vram_size < LOGO_MAX_SIZE)
6842 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6843
6844 /*
6845 * Allocate buffer for the logo data.
6846 * RT_MAX() is applied to let us fall back to default logo on read failure.
6847 */
6848 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6849 pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6850 if (pThis->pu8Logo)
6851 {
6852 /*
6853 * Write the logo header.
6854 */
6855 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
6856 *pLogoHdr = LogoHdr;
6857
6858 /*
6859 * Write the logo bitmap.
6860 */
6861 if (pThis->pszLogoFile)
6862 {
6863 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6864 if (RT_SUCCESS(rc))
6865 rc = vbeParseBitmap(pThis);
6866 if (RT_FAILURE(rc))
6867 {
6868 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
6869 rc, pThis->pszLogoFile));
6870 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6871 }
6872 }
6873 if ( !pThis->pszLogoFile
6874 || RT_FAILURE(rc))
6875 {
6876 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6877 rc = vbeParseBitmap(pThis);
6878 if (RT_FAILURE(rc))
6879 AssertReleaseMsgFailed(("Parsing of internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6880 }
6881
6882 rc = VINF_SUCCESS;
6883 }
6884 else
6885 rc = VERR_NO_MEMORY;
6886
6887 /*
6888 * Cleanup.
6889 */
6890 if (FileLogo != NIL_RTFILE)
6891 RTFileClose(FileLogo);
6892
6893#ifdef VBOX_WITH_HGSMI
6894 VBVAInit (pThis);
6895#endif /* VBOX_WITH_HGSMI */
6896
6897#ifdef VBOX_WITH_VDMA
6898 if (rc == VINF_SUCCESS)
6899 {
6900 rc = vboxVDMAConstruct(pThis, 1024);
6901 AssertRC(rc);
6902 }
6903#endif
6904
6905#ifdef VBOX_WITH_VMSVGA
6906 if ( rc == VINF_SUCCESS
6907 && pThis->fVMSVGAEnabled)
6908 {
6909 rc = vmsvgaInit(pDevIns);
6910 }
6911#endif
6912
6913 /*
6914 * Statistics.
6915 */
6916 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6917 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6918 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6919 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6920 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6921 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
6922
6923 /* Init latched access mask. */
6924 pThis->uMaskLatchAccess = 0x3ff;
6925
6926 if (RT_SUCCESS(rc))
6927 {
6928 PPDMIBASE pBase;
6929 /*
6930 * Attach status driver (optional).
6931 */
6932 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
6933 AssertRC(rc);
6934 if (RT_SUCCESS(rc))
6935 {
6936 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
6937 pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
6938 }
6939 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6940 {
6941 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6942 rc = VINF_SUCCESS;
6943 }
6944 else
6945 {
6946 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
6947 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
6948 }
6949 }
6950 return rc;
6951}
6952
6953
6954/**
6955 * The device registration structure.
6956 */
6957const PDMDEVREG g_DeviceVga =
6958{
6959 /* u32Version */
6960 PDM_DEVREG_VERSION,
6961 /* szName */
6962 "vga",
6963 /* szRCMod */
6964 "VBoxDDRC.rc",
6965 /* szR0Mod */
6966 "VBoxDDR0.r0",
6967 /* pszDescription */
6968 "VGA Adaptor with VESA extensions.",
6969 /* fFlags */
6970 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6971 /* fClass */
6972 PDM_DEVREG_CLASS_GRAPHICS,
6973 /* cMaxInstances */
6974 1,
6975 /* cbInstance */
6976 sizeof(VGASTATE),
6977 /* pfnConstruct */
6978 vgaR3Construct,
6979 /* pfnDestruct */
6980 vgaR3Destruct,
6981 /* pfnRelocate */
6982 vgaR3Relocate,
6983 /* pfnMemSetup */
6984 NULL,
6985 /* pfnPowerOn */
6986#ifdef VBOX_WITH_VMSVGA
6987 vmsvgaR3PowerOn,
6988#else
6989 NULL,
6990#endif
6991 /* pfnReset */
6992 vgaR3Reset,
6993 /* pfnSuspend */
6994 NULL,
6995 /* pfnResume */
6996 NULL,
6997 /* pfnAttach */
6998 vgaAttach,
6999 /* pfnDetach */
7000 vgaDetach,
7001 /* pfnQueryInterface */
7002 NULL,
7003 /* pfnInitComplete */
7004 NULL,
7005 /* pfnPowerOff */
7006 NULL,
7007 /* pfnSoftReset */
7008 NULL,
7009 /* u32VersionEnd */
7010 PDM_DEVREG_VERSION
7011};
7012
7013#endif /* !IN_RING3 */
7014#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7015
7016/*
7017 * Local Variables:
7018 * nuke-trailing-whitespace-p:nil
7019 * End:
7020 */
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