VirtualBox

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

Last change on this file since 55896 was 55896, checked in by vboxsync, 10 years ago

PGM: Renamed the ring-0 and raw-mode context physical page access handler callbacks to 'PfHandler' to indicate that these are for page-fault callbacks. Will add non-PF handlers similar to the ring-3 one later.

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