VirtualBox

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

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

DevVGA: pfnQueryColorDepth -> pfnQueryVideoMode

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 236.3 KB
Line 
1/* $Id: DevVGA.cpp 55838 2015-05-13 06:56:40Z 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.215389.xyz. 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#ifdef IN_RC
3527/**
3528 * @callback_method_impl{FNPGMRCPHYSHANDLER, \#PF Handler for VBE LFB access.}
3529 */
3530PDMBOTHCBDECL(int) vgaRCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3531{
3532 PVGASTATE pThis = (PVGASTATE)pvUser;
3533 AssertPtr(pThis);
3534 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3535 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3536 NOREF(pRegFrame);
3537
3538 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3539}
3540
3541#elif IN_RING0
3542
3543/**
3544 * @callback_method_impl{FNPGMR0PHYSHANDLER, \#PF Handler for VBE LFB access.}
3545 */
3546PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3547{
3548 PVGASTATE pThis = (PVGASTATE)pvUser;
3549 Assert(pThis);
3550 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3551 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3552 NOREF(pRegFrame);
3553
3554 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3555}
3556
3557#else /* IN_RING3 */
3558
3559/**
3560 * @callback_method_impl{FNPGMR3PHYSHANDLER, HC access handler for the LFB.}
3561 */
3562static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3563{
3564 PVGASTATE pThis = (PVGASTATE)pvUser;
3565 int rc;
3566 Assert(pThis);
3567 Assert(GCPhys >= pThis->GCPhysVRAM);
3568 NOREF(pvPhys); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmAccessType);
3569
3570 rc = vgaLFBAccess(pVM, pThis, GCPhys, 0);
3571 if (RT_SUCCESS(rc))
3572 return VINF_PGM_HANDLER_DO_DEFAULT;
3573 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
3574 return rc;
3575}
3576#endif /* IN_RING3 */
3577
3578/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3579
3580/**
3581 * @callback_method_impl{FNIOMIOPORTIN,
3582 * Port I/O Handler for VGA BIOS IN operations.}
3583 */
3584PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3585{
3586 NOREF(pDevIns);
3587 NOREF(pvUser);
3588 NOREF(Port);
3589 NOREF(pu32);
3590 NOREF(cb);
3591 return VERR_IOM_IOPORT_UNUSED;
3592}
3593
3594/**
3595 * @callback_method_impl{FNIOMIOPORTOUT,
3596 * Port I/O Handler for VGA BIOS IN operations.}
3597 */
3598PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3599{
3600 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3601 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3602 NOREF(pvUser);
3603 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3604
3605 /*
3606 * VGA BIOS char printing.
3607 */
3608 if ( cb == 1
3609 && Port == VBE_PRINTF_PORT)
3610 {
3611#if 0
3612 switch (u32)
3613 {
3614 case '\r': Log(("vgabios: <return>\n")); break;
3615 case '\n': Log(("vgabios: <newline>\n")); break;
3616 case '\t': Log(("vgabios: <tab>\n")); break;
3617 default:
3618 Log(("vgabios: %c\n", u32));
3619 }
3620#else
3621 if (lastWasNotNewline == 0)
3622 Log(("vgabios: "));
3623 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3624 Log(("%c", u32));
3625 if (u32 == '\n')
3626 lastWasNotNewline = 0;
3627 else
3628 lastWasNotNewline = 1;
3629#endif
3630 return VINF_SUCCESS;
3631 }
3632
3633 /* not in use. */
3634 return VERR_IOM_IOPORT_UNUSED;
3635}
3636
3637
3638/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3639
3640#ifdef IN_RING3
3641
3642# ifdef VBE_NEW_DYN_LIST
3643/**
3644 * @callback_method_impl{FNIOMIOPORTOUT,
3645 * Port I/O Handler for VBE Extra OUT operations.}
3646 */
3647PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3648{
3649 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3650 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3651 NOREF(pvUser); NOREF(Port);
3652
3653 if (cb == 2)
3654 {
3655 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3656 pThis->u16VBEExtraAddress = u32;
3657 }
3658 else
3659 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3660
3661 return VINF_SUCCESS;
3662}
3663
3664
3665/**
3666 * @callback_method_impl{FNIOMIOPORTIN,
3667 * Port I/O Handler for VBE Extra IN operations.}
3668 */
3669PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3670{
3671 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3672 NOREF(pvUser); NOREF(Port);
3673 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3674
3675 int rc = VINF_SUCCESS;
3676 if (pThis->u16VBEExtraAddress == 0xffff)
3677 {
3678 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3679 *pu32 = pThis->vram_size / _64K;
3680 }
3681 else if ( pThis->u16VBEExtraAddress >= pThis->cbVBEExtraData
3682 || pThis->u16VBEExtraAddress + cb > pThis->cbVBEExtraData)
3683 {
3684 *pu32 = 0;
3685 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3686 pThis->u16VBEExtraAddress, pThis->u16VBEExtraAddress, pThis->cbVBEExtraData, pThis->cbVBEExtraData));
3687 }
3688 else if (cb == 1)
3689 {
3690 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress] & 0xFF;
3691
3692 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3693 }
3694 else if (cb == 2)
3695 {
3696 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress]
3697 | pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress + 1] << 8;
3698
3699 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3700 }
3701 else
3702 {
3703 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3704 rc = VERR_IOM_IOPORT_UNUSED;
3705 }
3706
3707 return rc;
3708}
3709# endif /* VBE_NEW_DYN_LIST */
3710
3711
3712/**
3713 * Parse the logo bitmap data at init time.
3714 *
3715 * @returns VBox status code.
3716 *
3717 * @param pThis The VGA instance data.
3718 */
3719static int vbeParseBitmap(PVGASTATE pThis)
3720{
3721 uint16_t i;
3722 PBMPINFO bmpInfo;
3723 POS2HDR pOs2Hdr;
3724 POS22HDR pOs22Hdr;
3725 PWINHDR pWinHdr;
3726
3727 /*
3728 * Get bitmap header data
3729 */
3730 bmpInfo = (PBMPINFO)(pThis->pu8Logo + sizeof(LOGOHDR));
3731 pWinHdr = (PWINHDR)(pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO));
3732
3733 if (bmpInfo->Type == BMP_ID)
3734 {
3735 switch (pWinHdr->Size)
3736 {
3737 case BMP_HEADER_OS21:
3738 pOs2Hdr = (POS2HDR)pWinHdr;
3739 pThis->cxLogo = pOs2Hdr->Width;
3740 pThis->cyLogo = pOs2Hdr->Height;
3741 pThis->cLogoPlanes = pOs2Hdr->Planes;
3742 pThis->cLogoBits = pOs2Hdr->BitCount;
3743 pThis->LogoCompression = BMP_COMPRESS_NONE;
3744 pThis->cLogoUsedColors = 0;
3745 break;
3746
3747 case BMP_HEADER_OS22:
3748 pOs22Hdr = (POS22HDR)pWinHdr;
3749 pThis->cxLogo = pOs22Hdr->Width;
3750 pThis->cyLogo = pOs22Hdr->Height;
3751 pThis->cLogoPlanes = pOs22Hdr->Planes;
3752 pThis->cLogoBits = pOs22Hdr->BitCount;
3753 pThis->LogoCompression = pOs22Hdr->Compression;
3754 pThis->cLogoUsedColors = pOs22Hdr->ClrUsed;
3755 break;
3756
3757 case BMP_HEADER_WIN3:
3758 pThis->cxLogo = pWinHdr->Width;
3759 pThis->cyLogo = pWinHdr->Height;
3760 pThis->cLogoPlanes = pWinHdr->Planes;
3761 pThis->cLogoBits = pWinHdr->BitCount;
3762 pThis->LogoCompression = pWinHdr->Compression;
3763 pThis->cLogoUsedColors = pWinHdr->ClrUsed;
3764 break;
3765
3766 default:
3767 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pWinHdr->Size),
3768 VERR_INVALID_PARAMETER);
3769 break;
3770 }
3771
3772 AssertLogRelMsgReturn(pThis->cxLogo <= LOGO_MAX_WIDTH && pThis->cyLogo <= LOGO_MAX_HEIGHT,
3773 ("Bitmap %ux%u is too big.\n", pThis->cxLogo, pThis->cyLogo),
3774 VERR_INVALID_PARAMETER);
3775
3776 AssertLogRelMsgReturn(pThis->cLogoPlanes == 1,
3777 ("Bitmap planes %u != 1.\n", pThis->cLogoPlanes),
3778 VERR_INVALID_PARAMETER);
3779
3780 AssertLogRelMsgReturn(pThis->cLogoBits == 4 || pThis->cLogoBits == 8 || pThis->cLogoBits == 24,
3781 ("Unsupported %u depth.\n", pThis->cLogoBits),
3782 VERR_INVALID_PARAMETER);
3783
3784 AssertLogRelMsgReturn(pThis->cLogoUsedColors <= 256,
3785 ("Unsupported %u colors.\n", pThis->cLogoUsedColors),
3786 VERR_INVALID_PARAMETER);
3787
3788 AssertLogRelMsgReturn(pThis->LogoCompression == BMP_COMPRESS_NONE,
3789 ("Unsupported %u compression.\n", pThis->LogoCompression),
3790 VERR_INVALID_PARAMETER);
3791
3792 /*
3793 * Read bitmap palette
3794 */
3795 if (!pThis->cLogoUsedColors)
3796 pThis->cLogoPalEntries = 1 << (pThis->cLogoPlanes * pThis->cLogoBits);
3797 else
3798 pThis->cLogoPalEntries = pThis->cLogoUsedColors;
3799
3800 if (pThis->cLogoPalEntries)
3801 {
3802 const uint8_t *pu8Pal = pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO) + pWinHdr->Size; /* ASSUMES Size location (safe) */
3803
3804 for (i = 0; i < pThis->cLogoPalEntries; i++)
3805 {
3806 uint16_t j;
3807 uint32_t u32Pal = 0;
3808
3809 for (j = 0; j < 3; j++)
3810 {
3811 uint8_t b = *pu8Pal++;
3812 u32Pal <<= 8;
3813 u32Pal |= b;
3814 }
3815
3816 pu8Pal++; /* skip unused byte */
3817 pThis->au32LogoPalette[i] = u32Pal;
3818 }
3819 }
3820
3821 /*
3822 * Bitmap data offset
3823 */
3824 pThis->pu8LogoBitmap = pThis->pu8Logo + sizeof(LOGOHDR) + bmpInfo->Offset;
3825 }
3826 else
3827 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
3828
3829 return VINF_SUCCESS;
3830}
3831
3832
3833/**
3834 * Show logo bitmap data.
3835 *
3836 * @returns VBox status code.
3837 *
3838 * @param cbDepth Logo depth.
3839 * @param xLogo Logo X position.
3840 * @param yLogo Logo Y position.
3841 * @param cxLogo Logo width.
3842 * @param cyLogo Logo height.
3843 * @param iStep Fade in/fade out step.
3844 * @param pu32Palette Palette data.
3845 * @param pu8Src Source buffer.
3846 * @param pu8Dst Destination buffer.
3847 */
3848static void vbeShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo, uint8_t iStep,
3849 const uint32_t *pu32Palette, const uint8_t *pu8Src, uint8_t *pu8Dst)
3850{
3851 uint16_t i;
3852 size_t cbPadBytes = 0;
3853 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
3854 uint16_t cyLeft = cyLogo;
3855
3856 pu8Dst += xLogo * 4 + yLogo * cbLineDst;
3857
3858 switch (cBits)
3859 {
3860 case 1:
3861 pu8Dst += cyLogo * cbLineDst;
3862 cbPadBytes = 0;
3863 break;
3864
3865 case 4:
3866 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
3867 cbPadBytes = 0;
3868 else if ((cxLogo % 8) <= 2)
3869 cbPadBytes = 3;
3870 else if ((cxLogo % 8) <= 4)
3871 cbPadBytes = 2;
3872 else
3873 cbPadBytes = 1;
3874 break;
3875
3876 case 8:
3877 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
3878 break;
3879
3880 case 24:
3881 cbPadBytes = cxLogo % 4;
3882 break;
3883 }
3884
3885 uint8_t j = 0, c = 0;
3886
3887 while (cyLeft-- > 0)
3888 {
3889 uint8_t *pu8TmpPtr = pu8Dst;
3890
3891 if (cBits != 1)
3892 j = 0;
3893
3894 for (i = 0; i < cxLogo; i++)
3895 {
3896 uint8_t pix;
3897
3898 switch (cBits)
3899 {
3900 case 1:
3901 {
3902 if (!j)
3903 c = *pu8Src++;
3904
3905 pix = (c & 1) ? 0xFF : 0;
3906 c >>= 1;
3907
3908 if (pix)
3909 {
3910 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3911 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3912 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3913 pu8TmpPtr++;
3914 }
3915 else
3916 {
3917 pu8TmpPtr += 4;
3918 }
3919
3920 j = (j + 1) % 8;
3921 break;
3922 }
3923
3924 case 4:
3925 {
3926 if (!j)
3927 c = *pu8Src++;
3928
3929 pix = (c >> 4) & 0xF;
3930 c <<= 4;
3931
3932 uint32_t u32Pal = pu32Palette[pix];
3933
3934 pix = (u32Pal >> 16) & 0xFF;
3935 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3936 pix = (u32Pal >> 8) & 0xFF;
3937 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3938 pix = u32Pal & 0xFF;
3939 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3940 pu8TmpPtr++;
3941
3942 j = (j + 1) % 2;
3943 break;
3944 }
3945
3946 case 8:
3947 {
3948 uint32_t u32Pal = pu32Palette[*pu8Src++];
3949
3950 pix = (u32Pal >> 16) & 0xFF;
3951 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3952 pix = (u32Pal >> 8) & 0xFF;
3953 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3954 pix = u32Pal & 0xFF;
3955 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3956 pu8TmpPtr++;
3957 break;
3958 }
3959
3960 case 24:
3961 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3962 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3963 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3964 pu8TmpPtr++;
3965 break;
3966 }
3967 }
3968
3969 pu8Dst -= cbLineDst;
3970 pu8Src += cbPadBytes;
3971 }
3972}
3973
3974
3975/**
3976 * @callback_method_impl{FNIOMIOPORTOUT,
3977 * Port I/O Handler for BIOS Logo OUT operations.}
3978 */
3979PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3980{
3981 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3982 NOREF(pvUser);
3983 NOREF(Port);
3984
3985 Log(("vbeIOPortWriteCMDLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
3986
3987 if (cb == 2)
3988 {
3989 /* Get the logo command */
3990 switch (u32 & 0xFF00)
3991 {
3992 case LOGO_CMD_SET_OFFSET:
3993 pThis->offLogoData = u32 & 0xFF;
3994 break;
3995
3996 case LOGO_CMD_SHOW_BMP:
3997 {
3998 uint8_t iStep = u32 & 0xFF;
3999 const uint8_t *pu8Src = pThis->pu8LogoBitmap;
4000 uint8_t *pu8Dst;
4001 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
4002 uint32_t offDirty = 0;
4003 uint16_t xLogo = (LOGO_MAX_WIDTH - pThis->cxLogo) / 2;
4004 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThis->cyLogo) / 2;
4005
4006 /* Check VRAM size */
4007 if (pThis->vram_size < LOGO_MAX_SIZE)
4008 break;
4009
4010 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4011 pu8Dst = pThis->vram_ptrR3 + LOGO_MAX_SIZE;
4012 else
4013 pu8Dst = pThis->vram_ptrR3;
4014
4015 /* Clear screen - except on power on... */
4016 if (!pThis->fLogoClearScreen)
4017 {
4018 uint32_t *pu32TmpPtr = (uint32_t *)pu8Dst;
4019
4020 /* Clear vram */
4021 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4022 {
4023 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4024 *pu32TmpPtr++ = 0;
4025 }
4026 pThis->fLogoClearScreen = true;
4027 }
4028
4029 /* Show the bitmap. */
4030 vbeShowBitmap(pThis->cLogoBits, xLogo, yLogo,
4031 pThis->cxLogo, pThis->cyLogo,
4032 iStep, &pThis->au32LogoPalette[0],
4033 pu8Src, pu8Dst);
4034
4035 /* Show the 'Press F12...' text. */
4036 if (pLogoHdr->fu8ShowBootMenu == 2)
4037 vbeShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4038 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4039 iStep, &pThis->au32LogoPalette[0],
4040 &g_abLogoF12BootText[0], pu8Dst);
4041
4042 /* Blit the offscreen buffer. */
4043 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4044 {
4045 uint32_t *pu32TmpDst = (uint32_t *)pThis->vram_ptrR3;
4046 uint32_t *pu32TmpSrc = (uint32_t *)(pThis->vram_ptrR3 + LOGO_MAX_SIZE);
4047 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4048 {
4049 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4050 *pu32TmpDst++ = *pu32TmpSrc++;
4051 }
4052 }
4053
4054 /* Set the dirty flags. */
4055 while (offDirty <= LOGO_MAX_SIZE)
4056 {
4057 vga_set_dirty(pThis, offDirty);
4058 offDirty += PAGE_SIZE;
4059 }
4060 break;
4061 }
4062
4063 default:
4064 Log(("vbeIOPortWriteCMDLogo: invalid command %d\n", u32));
4065 pThis->LogoCommand = LOGO_CMD_NOP;
4066 break;
4067 }
4068
4069 return VINF_SUCCESS;
4070 }
4071
4072 Log(("vbeIOPortWriteCMDLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4073 return VINF_SUCCESS;
4074}
4075
4076
4077/**
4078 * @callback_method_impl{FNIOMIOPORTIN,
4079 * Port I/O Handler for BIOS Logo IN operations.}
4080 */
4081PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
4082{
4083 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4084 NOREF(pvUser);
4085 NOREF(Port);
4086
4087 PRTUINT64U p;
4088
4089 if (pThis->offLogoData + cb > pThis->cbLogo)
4090 {
4091 Log(("vbeIOPortReadCMDLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4092 pThis->offLogoData, pThis->offLogoData, pThis->cbLogo, pThis->cbLogo));
4093 return VINF_SUCCESS;
4094 }
4095 p = (PRTUINT64U)&pThis->pu8Logo[pThis->offLogoData];
4096
4097 switch (cb)
4098 {
4099 case 1: *pu32 = p->au8[0]; break;
4100 case 2: *pu32 = p->au16[0]; break;
4101 case 4: *pu32 = p->au32[0]; break;
4102 //case 8: *pu32 = p->au64[0]; break;
4103 default: AssertFailed(); break;
4104 }
4105 Log(("vbeIOPortReadCMDLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThis->offLogoData, pThis->offLogoData, cb, cb, pu32));
4106
4107 pThis->LogoCommand = LOGO_CMD_NOP;
4108 pThis->offLogoData += cb;
4109
4110 return VINF_SUCCESS;
4111}
4112
4113
4114/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4115
4116/**
4117 * @callback_method_impl{FNDBGFHANDLERDEV,
4118 * Dumps several interesting bits of the VGA state that are difficult to
4119 * decode from the registers.}
4120 */
4121static DECLCALLBACK(void) vgaInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4122{
4123 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4124 int is_graph, double_scan;
4125 int w, h, char_height, char_dots;
4126 int val, vfreq_hz, hfreq_hz;
4127 vga_retrace_s *r = &pThis->retrace_state;
4128 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4129 NOREF(pszArgs);
4130
4131 is_graph = pThis->gr[6] & 1;
4132 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4133 double_scan = pThis->cr[9] >> 7;
4134 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4135 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4136 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4137 val = pThis->cr[0] + 5;
4138 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4139 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4140 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4141 val = pThis->cr[1] + 1;
4142 w = val * char_dots;
4143 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4144 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4145 h = val;
4146 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4147 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4148 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4149 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4150 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4151 if (!is_graph)
4152 {
4153 val = (pThis->cr[9] & 0x1f) + 1;
4154 char_height = val;
4155 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4156 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4157
4158 uint32_t cbLine;
4159 uint32_t offStart;
4160 uint32_t uLineCompareIgn;
4161 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4162 if (!cbLine)
4163 cbLine = 80 * 8;
4164 offStart *= 8;
4165 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4166 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4167 }
4168 if (pThis->fRealRetrace)
4169 {
4170 val = r->hb_start;
4171 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4172 val = r->hb_end;
4173 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4174 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4175 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4176 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4177 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4178 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4179 {
4180 vfreq_hz = 1000000000 / r->frame_ns;
4181 hfreq_hz = 1000000000 / r->h_total_ns;
4182 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4183 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4184 }
4185 }
4186 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4187
4188#ifdef VBOX_WITH_VMSVGA
4189 if (pThis->svga.fEnabled) {
4190 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4191 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4192 }
4193#endif
4194}
4195
4196
4197/**
4198 * Prints a separator line.
4199 *
4200 * @param pHlp Callback functions for doing output.
4201 * @param cCols The number of columns.
4202 * @param pszTitle The title text, NULL if none.
4203 */
4204static void vgaInfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4205{
4206 if (pszTitle)
4207 {
4208 size_t cchTitle = strlen(pszTitle);
4209 if (cchTitle + 6 >= cCols)
4210 {
4211 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4212 cCols = 0;
4213 }
4214 else
4215 {
4216 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4217 cCols -= cchLeft + cchTitle + 2;
4218 while (cchLeft-- > 0)
4219 pHlp->pfnPrintf(pHlp, "-");
4220 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4221 }
4222 }
4223
4224 while (cCols-- > 0)
4225 pHlp->pfnPrintf(pHlp, "-");
4226 pHlp->pfnPrintf(pHlp, "\n");
4227}
4228
4229
4230/**
4231 * Worker for vgaInfoText.
4232 *
4233 * @param pThis The vga state.
4234 * @param pHlp Callback functions for doing output.
4235 * @param offStart Where to start dumping (relative to the VRAM).
4236 * @param cbLine The source line length (aka line_offset).
4237 * @param cCols The number of columns on the screen.
4238 * @param cRows The number of rows to dump.
4239 * @param iScrBegin The row at which the current screen output starts.
4240 * @param iScrEnd The row at which the current screen output end
4241 * (exclusive).
4242 */
4243static void vgaInfoTextWorker(PVGASTATE pThis, PCDBGFINFOHLP pHlp,
4244 uint32_t offStart, uint32_t cbLine,
4245 uint32_t cCols, uint32_t cRows,
4246 uint32_t iScrBegin, uint32_t iScrEnd)
4247{
4248 /* Title, */
4249 char szTitle[32];
4250 if (iScrBegin || iScrEnd < cRows)
4251 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4252 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4253 else
4254 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4255
4256 /* Do the dumping. */
4257 uint8_t const *pbSrcOuter = pThis->CTX_SUFF(vram_ptr) + offStart;
4258 uint32_t iRow;
4259 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4260 {
4261 if ((uintptr_t)(pbSrcOuter + cbLine - pThis->CTX_SUFF(vram_ptr)) > pThis->vram_size) {
4262 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4263 break;
4264 }
4265
4266 if (iRow == 0)
4267 vgaInfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4268 else if (iRow == iScrBegin)
4269 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4270 else if (iRow == iScrEnd)
4271 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4272
4273 uint8_t const *pbSrc = pbSrcOuter;
4274 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4275 {
4276 if (RT_C_IS_PRINT(*pbSrc))
4277 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4278 else
4279 pHlp->pfnPrintf(pHlp, ".");
4280 pbSrc += 8; /* chars are spaced 8 bytes apart */
4281 }
4282 pHlp->pfnPrintf(pHlp, "\n");
4283 }
4284
4285 /* Final separator. */
4286 vgaInfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4287}
4288
4289
4290/**
4291 * @callback_method_impl{FNDBGFHANDLERDEV,
4292 * Dumps VGA memory formatted as ASCII text, no attributes. Only looks at the
4293 * first page.}
4294 */
4295static DECLCALLBACK(void) vgaInfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4296{
4297 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4298
4299 /*
4300 * Parse args.
4301 */
4302 bool fAll = true;
4303 if (pszArgs && *pszArgs)
4304 {
4305 if (!strcmp(pszArgs, "all"))
4306 fAll = true;
4307 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4308 fAll = false;
4309 else
4310 {
4311 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4312 return;
4313 }
4314 }
4315
4316 /*
4317 * Check that we're in text mode and that the VRAM is accessible.
4318 */
4319 if (!(pThis->gr[6] & 1))
4320 {
4321 uint8_t *pbSrc = pThis->vram_ptrR3;
4322 if (pbSrc)
4323 {
4324 /*
4325 * Figure out the display size and where the text is.
4326 *
4327 * Note! We're cutting quite a few corners here and this code could
4328 * do with some brushing up. Dumping from the start of the
4329 * frame buffer is done intentionally so that we're more
4330 * likely to obtain the full scrollback of a linux panic.
4331 * 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";
4332 */
4333 uint32_t cbLine;
4334 uint32_t offStart;
4335 uint32_t uLineCompareIgn;
4336 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4337 if (!cbLine)
4338 cbLine = 80 * 8;
4339 offStart *= 8;
4340
4341 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4342 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4343 uint32_t uDblScan = pThis->cr[9] >> 7;
4344 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4345 if (cScrRows < 25)
4346 cScrRows = 25;
4347 uint32_t iScrBegin = offStart / cbLine;
4348 uint32_t cRows = iScrBegin + cScrRows;
4349 uint32_t cCols = cbLine / 8;
4350
4351 if (fAll) {
4352 vgaInfoTextWorker(pThis, pHlp, offStart - iScrBegin * cbLine, cbLine,
4353 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4354 } else {
4355 vgaInfoTextWorker(pThis, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4356 }
4357 }
4358 else
4359 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4360 }
4361 else
4362 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4363}
4364
4365
4366/**
4367 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4368 */
4369static DECLCALLBACK(void) vgaInfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4370{
4371 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4372 unsigned i;
4373 NOREF(pszArgs);
4374
4375 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4376 Assert(sizeof(pThis->sr) >= 8);
4377 for (i = 0; i < 5; ++i)
4378 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4379 pHlp->pfnPrintf(pHlp, "\n");
4380}
4381
4382
4383/**
4384 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4385 */
4386static DECLCALLBACK(void) vgaInfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4387{
4388 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4389 unsigned i;
4390 NOREF(pszArgs);
4391
4392 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4393 Assert(sizeof(pThis->cr) >= 24);
4394 for (i = 0; i < 10; ++i)
4395 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4396 pHlp->pfnPrintf(pHlp, "\n");
4397 for (i = 10; i < 20; ++i)
4398 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4399 pHlp->pfnPrintf(pHlp, "\n");
4400 for (i = 20; i < 25; ++i)
4401 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4402 pHlp->pfnPrintf(pHlp, "\n");
4403}
4404
4405
4406/**
4407 * @callback_method_impl{FNDBGFHANDLERDEV,
4408 * Dumps VGA Graphics Controller registers.}
4409 */
4410static DECLCALLBACK(void) vgaInfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4411{
4412 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4413 unsigned i;
4414 NOREF(pszArgs);
4415
4416 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4417 Assert(sizeof(pThis->gr) >= 9);
4418 for (i = 0; i < 9; ++i)
4419 {
4420 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4421 }
4422 pHlp->pfnPrintf(pHlp, "\n");
4423}
4424
4425
4426/**
4427 * @callback_method_impl{FNDBGFHANDLERDEV,
4428 * Dumps VGA Attribute Controller registers.}
4429 */
4430static DECLCALLBACK(void) vgaInfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4431{
4432 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4433 unsigned i;
4434 NOREF(pszArgs);
4435
4436 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4437 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4438 Assert(sizeof(pThis->ar) >= 0x14);
4439 pHlp->pfnPrintf(pHlp, " Palette:");
4440 for (i = 0; i < 0x10; ++i)
4441 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4442 pHlp->pfnPrintf(pHlp, "\n");
4443 for (i = 0x10; i <= 0x14; ++i)
4444 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4445 pHlp->pfnPrintf(pHlp, "\n");
4446}
4447
4448
4449/**
4450 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4451 */
4452static DECLCALLBACK(void) vgaInfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4453{
4454 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4455 unsigned i;
4456 NOREF(pszArgs);
4457
4458 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4459 for (i = 0; i < 0x100; ++i)
4460 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4461 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4462}
4463
4464
4465/**
4466 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4467 */
4468static DECLCALLBACK(void) vgaInfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4469{
4470 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4471 NOREF(pszArgs);
4472
4473 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4474
4475 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4476 {
4477 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4478 return;
4479 }
4480
4481 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4482 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4483 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4484 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4485 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4486 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4487 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4488 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4489 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4490 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4491 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4492}
4493
4494
4495/**
4496 * @callback_method_impl{FNDBGFHANDLERDEV,
4497 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4498 * in human-readable form.}
4499 */
4500static DECLCALLBACK(void) vgaInfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4501{
4502 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4503 int val1, val2;
4504 NOREF(pszArgs);
4505
4506 val1 = (pThis->gr[5] >> 3) & 1;
4507 val2 = pThis->gr[5] & 3;
4508 pHlp->pfnPrintf(pHlp, "read mode : %d write mode: %d\n", val1, val2);
4509 val1 = pThis->gr[0];
4510 val2 = pThis->gr[1];
4511 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4512 val1 = pThis->gr[2];
4513 val2 = pThis->gr[4] & 3;
4514 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %d\n", val1, val2);
4515 val1 = pThis->gr[3] & 7;
4516 val2 = (pThis->gr[3] >> 3) & 3;
4517 pHlp->pfnPrintf(pHlp, "rotate : %d function : %d\n", val1, val2);
4518 val1 = pThis->gr[7];
4519 val2 = pThis->gr[8];
4520 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4521 val1 = pThis->sr[2];
4522 val2 = pThis->sr[4] & 8;
4523 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4524}
4525
4526
4527/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4528
4529/**
4530 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4531 */
4532static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4533{
4534 PVGASTATE pThis = RT_FROM_MEMBER(pInterface, VGASTATE, IBase);
4535 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4536 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPort);
4537#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI))
4538 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThis->IVBVACallbacks);
4539#endif
4540 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4541 return NULL;
4542}
4543
4544/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4545#define ILEDPORTS_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, ILeds)) )
4546
4547/**
4548 * Gets the pointer to the status LED of a unit.
4549 *
4550 * @returns VBox status code.
4551 * @param pInterface Pointer to the interface structure containing the called function pointer.
4552 * @param iLUN The unit which status LED we desire.
4553 * @param ppLed Where to store the LED pointer.
4554 */
4555static DECLCALLBACK(int) vgaPortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4556{
4557 PVGASTATE pThis = ILEDPORTS_2_VGASTATE(pInterface);
4558 switch (iLUN)
4559 {
4560 /* LUN #0: Display port. */
4561 case 0:
4562 {
4563 *ppLed = &pThis->Led3D;
4564 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4565 return VINF_SUCCESS;
4566 }
4567
4568 default:
4569 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4570 return VERR_PDM_NO_SUCH_LUN;
4571 }
4572
4573 return VERR_PDM_LUN_NOT_FOUND;
4574}
4575
4576/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4577
4578/**
4579 * Resize the display.
4580 * This is called when the resolution changes. This usually happens on
4581 * request from the guest os, but may also happen as the result of a reset.
4582 *
4583 * @param pInterface Pointer to this interface.
4584 * @param cx New display width.
4585 * @param cy New display height
4586 * @thread The emulation thread.
4587 */
4588static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM,
4589 uint32_t cbLine, uint32_t cx, uint32_t cy)
4590{
4591 NOREF(pInterface); NOREF(bpp); NOREF(pvVRAM); NOREF(cbLine); NOREF(cx); NOREF(cy);
4592 return VINF_SUCCESS;
4593}
4594
4595
4596/**
4597 * Update a rectangle of the display.
4598 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
4599 *
4600 * @param pInterface Pointer to this interface.
4601 * @param x The upper left corner x coordinate of the rectangle.
4602 * @param y The upper left corner y coordinate of the rectangle.
4603 * @param cx The width of the rectangle.
4604 * @param cy The height of the rectangle.
4605 * @thread The emulation thread.
4606 */
4607static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4608{
4609 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(cx); NOREF(cy);
4610}
4611
4612
4613/**
4614 * Refresh the display.
4615 *
4616 * The interval between these calls is set by
4617 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
4618 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
4619 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
4620 * the changed rectangles.
4621 *
4622 * @param pInterface Pointer to this interface.
4623 * @thread The emulation thread.
4624 */
4625static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4626{
4627 NOREF(pInterface);
4628}
4629
4630
4631/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4632
4633/** Converts a display port interface pointer to a vga state pointer. */
4634#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, IPort)) )
4635
4636
4637/**
4638 * Update the display with any changed regions.
4639 *
4640 * @param pInterface Pointer to this interface.
4641 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
4642 */
4643static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4644{
4645 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4646 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4647 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4648
4649 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4650 AssertRC(rc);
4651
4652#ifdef VBOX_WITH_VMSVGA
4653 if ( pThis->svga.fEnabled
4654 && !pThis->svga.fTraces)
4655 {
4656 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4657 PDMCritSectLeave(&pThis->CritSect);
4658 return VINF_SUCCESS;
4659 }
4660#endif
4661
4662#ifndef VBOX_WITH_HGSMI
4663 /* This should be called only in non VBVA mode. */
4664#else
4665 if (VBVAUpdateDisplay (pThis) == VINF_SUCCESS)
4666 {
4667 PDMCritSectLeave(&pThis->CritSect);
4668 return VINF_SUCCESS;
4669 }
4670#endif /* VBOX_WITH_HGSMI */
4671
4672 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4673 if (pThis->fHasDirtyBits && pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4674 {
4675 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4676 pThis->fHasDirtyBits = false;
4677 }
4678 if (pThis->fRemappedVGA)
4679 {
4680 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4681 pThis->fRemappedVGA = false;
4682 }
4683
4684 rc = vga_update_display(pThis, false, false, true,
4685 pThis->pDrv, &pThis->graphic_mode);
4686 PDMCritSectLeave(&pThis->CritSect);
4687 return rc;
4688}
4689
4690
4691/**
4692 * Internal vgaPortUpdateDisplayAll worker called under pThis->CritSect.
4693 */
4694static int updateDisplayAll(PVGASTATE pThis, bool fFailOnResize)
4695{
4696 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4697
4698#ifdef VBOX_WITH_VMSVGA
4699 if ( !pThis->svga.fEnabled
4700 || pThis->svga.fTraces)
4701 {
4702#endif
4703 /* The dirty bits array has been just cleared, reset handlers as well. */
4704 if (pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4705 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4706#ifdef VBOX_WITH_VMSVGA
4707 }
4708#endif
4709 if (pThis->fRemappedVGA)
4710 {
4711 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4712 pThis->fRemappedVGA = false;
4713 }
4714
4715 pThis->graphic_mode = -1; /* force full update */
4716
4717 return vga_update_display(pThis, true, fFailOnResize, true,
4718 pThis->pDrv, &pThis->graphic_mode);
4719}
4720
4721
4722DECLCALLBACK(int) vgaUpdateDisplayAll(PVGASTATE pThis, bool fFailOnResize)
4723{
4724#ifdef DEBUG_sunlover
4725 LogFlow(("vgaPortUpdateDisplayAll\n"));
4726#endif /* DEBUG_sunlover */
4727
4728 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4729 AssertRC(rc);
4730
4731 rc = updateDisplayAll(pThis, fFailOnResize);
4732
4733 PDMCritSectLeave(&pThis->CritSect);
4734 return rc;
4735}
4736
4737/**
4738 * Update the entire display.
4739 *
4740 * @param pInterface Pointer to this interface.
4741 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
4742 */
4743static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4744{
4745 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4746 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4747
4748 /* This is called both in VBVA mode and normal modes. */
4749
4750 return vgaUpdateDisplayAll(pThis, fFailOnResize);
4751}
4752
4753
4754/**
4755 * Sets the refresh rate and restart the timer.
4756 *
4757 * @returns VBox status code.
4758 * @param pInterface Pointer to this interface.
4759 * @param cMilliesInterval Number of millis between two refreshes.
4760 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
4761 */
4762static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4763{
4764 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4765
4766 pThis->cMilliesRefreshInterval = cMilliesInterval;
4767 if (cMilliesInterval)
4768 return TMTimerSetMillies(pThis->RefreshTimer, cMilliesInterval);
4769 return TMTimerStop(pThis->RefreshTimer);
4770}
4771
4772
4773/** @copydoc PDMIDISPLAYPORT::pfnQueryVideoMode */
4774static DECLCALLBACK(int) vgaPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4775{
4776 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4777
4778 if (!pcBits)
4779 return VERR_INVALID_PARAMETER;
4780 *pcBits = vga_get_bpp(pThis);
4781 if (pcx)
4782 *pcx = pThis->last_scr_width;
4783 if (pcy)
4784 *pcy = pThis->last_scr_height;
4785 return VINF_SUCCESS;
4786}
4787
4788
4789/**
4790 * Create a 32-bbp screenshot of the display. Size of the bitmap scanline in bytes is 4*width.
4791 *
4792 * @param pInterface Pointer to this interface.
4793 * @param ppu8Data Where to store the pointer to the allocated buffer.
4794 * @param pcbData Where to store the actual size of the bitmap.
4795 * @param pcx Where to store the width of the bitmap.
4796 * @param pcy Where to store the height of the bitmap.
4797 * @see PDMIDISPLAYPORT::pfnTakeScreenshot() for details.
4798 */
4799static DECLCALLBACK(int) vgaPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pcx, uint32_t *pcy)
4800{
4801 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4802 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4803
4804 LogFlow(("vgaPortTakeScreenshot: ppu8Data=%p pcbData=%p pcx=%p pcy=%p\n", ppu8Data, pcbData, pcx, pcy));
4805
4806 /*
4807 * Validate input.
4808 */
4809 if (!RT_VALID_PTR(ppu8Data) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
4810 return VERR_INVALID_PARAMETER;
4811
4812 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4813 AssertRCReturn(rc, rc);
4814
4815 /*
4816 * Get screenshot. This function will fail if a resize is required.
4817 * So there is not need to do a 'updateDisplayAll' before taking screenshot.
4818 */
4819
4820 /*
4821 * Allocate the buffer for 32 bits per pixel bitmap
4822 *
4823 * Note! The size can't be zero or greater than the size of the VRAM.
4824 * Inconsistent VGA device state can cause the incorrect size values.
4825 */
4826 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
4827 if (cbRequired && cbRequired <= pThis->vram_size)
4828 {
4829 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
4830 if (pu8Data != NULL)
4831 {
4832 /*
4833 * Only 3 methods, assigned below, will be called during the screenshot update.
4834 * All other are already set to NULL.
4835 */
4836 /* The display connector interface is temporarily replaced with the fake one. */
4837 PDMIDISPLAYCONNECTOR Connector;
4838 RT_ZERO(Connector);
4839 Connector.pu8Data = pu8Data;
4840 Connector.cBits = 32;
4841 Connector.cx = pThis->last_scr_width;
4842 Connector.cy = pThis->last_scr_height;
4843 Connector.cbScanline = Connector.cx * 4;
4844 Connector.pfnRefresh = vgaDummyRefresh;
4845 Connector.pfnResize = vgaDummyResize;
4846 Connector.pfnUpdateRect = vgaDummyUpdateRect;
4847
4848 int32_t cur_graphic_mode = -1;
4849
4850 bool fSavedRenderVRAM = pThis->fRenderVRAM;
4851 pThis->fRenderVRAM = true;
4852
4853 /*
4854 * Make the screenshot.
4855 *
4856 * The second parameter is 'false' because the current display state is being rendered to an
4857 * external buffer using a fake connector. That is if display is blanked, we expect a black
4858 * screen in the external buffer.
4859 * If there is a pending resize, the function will fail.
4860 */
4861 rc = vga_update_display(pThis, false, true, false,
4862 &Connector, &cur_graphic_mode);
4863
4864 pThis->fRenderVRAM = fSavedRenderVRAM;
4865
4866 if (rc == VINF_SUCCESS)
4867 {
4868 /*
4869 * Return the result.
4870 */
4871 *ppu8Data = pu8Data;
4872 *pcbData = cbRequired;
4873 *pcx = Connector.cx;
4874 *pcy = Connector.cy;
4875 }
4876 else
4877 {
4878 /* If we do not return a success, then the data buffer must be freed. */
4879 RTMemFree(pu8Data);
4880 if (RT_SUCCESS_NP(rc))
4881 {
4882 AssertMsgFailed(("%Rrc\n", rc));
4883 rc = VERR_INTERNAL_ERROR_5;
4884 }
4885 }
4886 }
4887 else
4888 rc = VERR_NO_MEMORY;
4889 }
4890 else
4891 rc = VERR_NOT_SUPPORTED;
4892
4893 PDMCritSectLeave(&pThis->CritSect);
4894
4895 LogFlow(("vgaPortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
4896 return rc;
4897}
4898
4899/**
4900 * Free a screenshot buffer allocated in vgaPortTakeScreenshot.
4901 *
4902 * @param pInterface Pointer to this interface.
4903 * @param pu8Data Pointer returned by vgaPortTakeScreenshot.
4904 * @see PDMIDISPLAYPORT::pfnFreeScreenshot() for details.
4905 */
4906static DECLCALLBACK(void) vgaPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pu8Data)
4907{
4908 NOREF(pInterface);
4909
4910 LogFlow(("vgaPortFreeScreenshot: pu8Data=%p\n", pu8Data));
4911
4912 RTMemFree(pu8Data);
4913}
4914
4915/**
4916 * Copy bitmap to the display.
4917 *
4918 * @param pInterface Pointer to this interface.
4919 * @param pvData Pointer to the bitmap bits.
4920 * @param x The upper left corner x coordinate of the destination rectangle.
4921 * @param y The upper left corner y coordinate of the destination rectangle.
4922 * @param cx The width of the source and destination rectangles.
4923 * @param cy The height of the source and destination rectangles.
4924 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
4925 */
4926static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4927{
4928 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4929 int rc = VINF_SUCCESS;
4930 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4931 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
4932
4933 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4934 AssertRC(rc);
4935
4936 /*
4937 * Validate input.
4938 */
4939 if ( pvData
4940 && x < pThis->pDrv->cx
4941 && cx <= pThis->pDrv->cx
4942 && cx + x <= pThis->pDrv->cx
4943 && y < pThis->pDrv->cy
4944 && cy <= pThis->pDrv->cy
4945 && cy + y <= pThis->pDrv->cy)
4946 {
4947 /*
4948 * Determine bytes per pixel in the destination buffer.
4949 */
4950 size_t cbPixelDst = 0;
4951 switch (pThis->pDrv->cBits)
4952 {
4953 case 8:
4954 cbPixelDst = 1;
4955 break;
4956 case 15:
4957 case 16:
4958 cbPixelDst = 2;
4959 break;
4960 case 24:
4961 cbPixelDst = 3;
4962 break;
4963 case 32:
4964 cbPixelDst = 4;
4965 break;
4966 default:
4967 rc = VERR_INVALID_PARAMETER;
4968 break;
4969 }
4970 if (RT_SUCCESS(rc))
4971 {
4972 /*
4973 * The blitting loop.
4974 */
4975 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
4976 uint8_t *pu8Src = (uint8_t *)pvData;
4977 size_t cbLineDst = pThis->pDrv->cbScanline;
4978 uint8_t *pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4979 uint32_t cyLeft = cy;
4980 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
4981 Assert(pfnVgaDrawLine);
4982 while (cyLeft-- > 0)
4983 {
4984 pfnVgaDrawLine(pThis, pu8Dst, pu8Src, cx);
4985 pu8Dst += cbLineDst;
4986 pu8Src += cbLineSrc;
4987 }
4988
4989 /*
4990 * Invalidate the area.
4991 */
4992 pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
4993 }
4994 }
4995 else
4996 rc = VERR_INVALID_PARAMETER;
4997
4998 PDMCritSectLeave(&pThis->CritSect);
4999
5000 LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
5001 return rc;
5002}
5003
5004static DECLCALLBACK(void) vgaPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
5005{
5006 uint32_t v;
5007 vga_draw_line_func *vga_draw_line;
5008
5009 uint32_t cbPixelDst;
5010 uint32_t cbLineDst;
5011 uint8_t *pu8Dst;
5012
5013 uint32_t cbPixelSrc;
5014 uint32_t cbLineSrc;
5015 uint8_t *pu8Src;
5016
5017 uint32_t u32OffsetSrc, u32Dummy;
5018
5019 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5020
5021#ifdef DEBUG_sunlover
5022 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
5023#endif /* DEBUG_sunlover */
5024
5025 Assert(pInterface);
5026
5027 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5028 AssertRC(rc);
5029
5030 /* Check if there is something to do at all. */
5031 if (!pThis->fRenderVRAM)
5032 {
5033 /* The framebuffer uses the guest VRAM directly. */
5034#ifdef DEBUG_sunlover
5035 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
5036#endif /* DEBUG_sunlover */
5037 PDMCritSectLeave(&pThis->CritSect);
5038 return;
5039 }
5040
5041 Assert(pThis->pDrv);
5042 Assert(pThis->pDrv->pu8Data);
5043
5044 /* Correct negative x and y coordinates. */
5045 if (x < 0)
5046 {
5047 x += w; /* Compute xRight which is also the new width. */
5048 w = (x < 0) ? 0 : x;
5049 x = 0;
5050 }
5051
5052 if (y < 0)
5053 {
5054 y += h; /* Compute yBottom, which is also the new height. */
5055 h = (y < 0) ? 0 : y;
5056 y = 0;
5057 }
5058
5059 /* Also check if coords are greater than the display resolution. */
5060 if (x + w > pThis->pDrv->cx)
5061 {
5062 // x < 0 is not possible here
5063 w = pThis->pDrv->cx > (uint32_t)x? pThis->pDrv->cx - x: 0;
5064 }
5065
5066 if (y + h > pThis->pDrv->cy)
5067 {
5068 // y < 0 is not possible here
5069 h = pThis->pDrv->cy > (uint32_t)y? pThis->pDrv->cy - y: 0;
5070 }
5071
5072#ifdef DEBUG_sunlover
5073 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
5074#endif /* DEBUG_sunlover */
5075
5076 /* Check if there is something to do at all. */
5077 if (w == 0 || h == 0)
5078 {
5079 /* Empty rectangle. */
5080#ifdef DEBUG_sunlover
5081 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
5082#endif /* DEBUG_sunlover */
5083 PDMCritSectLeave(&pThis->CritSect);
5084 return;
5085 }
5086
5087 /** @todo This method should be made universal and not only for VBVA.
5088 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5089 * changed.
5090 */
5091
5092 /* Choose the rendering function. */
5093 switch(pThis->get_bpp(pThis))
5094 {
5095 default:
5096 case 0:
5097 /* A LFB mode is already disabled, but the callback is still called
5098 * by Display because VBVA buffer is being flushed.
5099 * Nothing to do, just return.
5100 */
5101 PDMCritSectLeave(&pThis->CritSect);
5102 return;
5103 case 8:
5104 v = VGA_DRAW_LINE8;
5105 break;
5106 case 15:
5107 v = VGA_DRAW_LINE15;
5108 break;
5109 case 16:
5110 v = VGA_DRAW_LINE16;
5111 break;
5112 case 24:
5113 v = VGA_DRAW_LINE24;
5114 break;
5115 case 32:
5116 v = VGA_DRAW_LINE32;
5117 break;
5118 }
5119
5120 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pThis->pDrv->cBits)];
5121
5122 /* Compute source and destination addresses and pitches. */
5123 cbPixelDst = (pThis->pDrv->cBits + 7) / 8;
5124 cbLineDst = pThis->pDrv->cbScanline;
5125 pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
5126
5127 cbPixelSrc = (pThis->get_bpp(pThis) + 7) / 8;
5128 pThis->get_offsets(pThis, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
5129
5130 /* Assume that rendering is performed only on visible part of VRAM.
5131 * This is true because coordinates were verified.
5132 */
5133 pu8Src = pThis->vram_ptrR3;
5134 pu8Src += u32OffsetSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5135
5136 /* Render VRAM to framebuffer. */
5137
5138#ifdef DEBUG_sunlover
5139 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
5140#endif /* DEBUG_sunlover */
5141
5142 while (h-- > 0)
5143 {
5144 vga_draw_line (pThis, pu8Dst, pu8Src, w);
5145 pu8Dst += cbLineDst;
5146 pu8Src += cbLineSrc;
5147 }
5148
5149 PDMCritSectLeave(&pThis->CritSect);
5150#ifdef DEBUG_sunlover
5151 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
5152#endif /* DEBUG_sunlover */
5153}
5154
5155static DECLCALLBACK(int) vgaPortCopyRect (PPDMIDISPLAYPORT pInterface,
5156 uint32_t w,
5157 uint32_t h,
5158 const uint8_t *pu8Src,
5159 int32_t xSrc,
5160 int32_t ySrc,
5161 uint32_t u32SrcWidth,
5162 uint32_t u32SrcHeight,
5163 uint32_t u32SrcLineSize,
5164 uint32_t u32SrcBitsPerPixel,
5165 uint8_t *pu8Dst,
5166 int32_t xDst,
5167 int32_t yDst,
5168 uint32_t u32DstWidth,
5169 uint32_t u32DstHeight,
5170 uint32_t u32DstLineSize,
5171 uint32_t u32DstBitsPerPixel)
5172{
5173 uint32_t v;
5174 vga_draw_line_func *vga_draw_line;
5175
5176 uint32_t cbPixelDst;
5177 uint32_t cbLineDst;
5178 uint8_t *pu8DstPtr;
5179
5180 uint32_t cbPixelSrc;
5181 uint32_t cbLineSrc;
5182 const uint8_t *pu8SrcPtr;
5183
5184#ifdef DEBUG_sunlover
5185 LogFlow(("vgaPortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, w, h, xDst, yDst));
5186#endif /* DEBUG_sunlover */
5187
5188 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5189
5190 Assert(pInterface);
5191 Assert(pThis->pDrv);
5192
5193 int32_t xSrcCorrected = xSrc;
5194 int32_t ySrcCorrected = ySrc;
5195 uint32_t wCorrected = w;
5196 uint32_t hCorrected = h;
5197
5198 /* Correct source coordinates to be within the source bitmap. */
5199 if (xSrcCorrected < 0)
5200 {
5201 xSrcCorrected += wCorrected; /* Compute xRight which is also the new width. */
5202 wCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5203 xSrcCorrected = 0;
5204 }
5205
5206 if (ySrcCorrected < 0)
5207 {
5208 ySrcCorrected += hCorrected; /* Compute yBottom, which is also the new height. */
5209 hCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5210 ySrcCorrected = 0;
5211 }
5212
5213 /* Also check if coords are greater than the display resolution. */
5214 if (xSrcCorrected + wCorrected > u32SrcWidth)
5215 {
5216 /* xSrcCorrected < 0 is not possible here */
5217 wCorrected = u32SrcWidth > (uint32_t)xSrcCorrected? u32SrcWidth - xSrcCorrected: 0;
5218 }
5219
5220 if (ySrcCorrected + hCorrected > u32SrcHeight)
5221 {
5222 /* y < 0 is not possible here */
5223 hCorrected = u32SrcHeight > (uint32_t)ySrcCorrected? u32SrcHeight - ySrcCorrected: 0;
5224 }
5225
5226#ifdef DEBUG_sunlover
5227 LogFlow(("vgaPortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, wCorrected, hCorrected));
5228#endif /* DEBUG_sunlover */
5229
5230 /* Check if there is something to do at all. */
5231 if (wCorrected == 0 || hCorrected == 0)
5232 {
5233 /* Empty rectangle. */
5234#ifdef DEBUG_sunlover
5235 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", wCorrected, hCorrected));
5236#endif /* DEBUG_sunlover */
5237 return VINF_SUCCESS;
5238 }
5239
5240 /* Check that the corrected source rectangle is within the destination.
5241 * Note: source rectangle is adjusted, but the target must be large enough.
5242 */
5243 if ( xDst < 0
5244 || yDst < 0
5245 || xDst + wCorrected > u32DstWidth
5246 || yDst + hCorrected > u32DstHeight)
5247 {
5248 return VERR_INVALID_PARAMETER;
5249 }
5250
5251 /* Choose the rendering function. */
5252 switch(u32SrcBitsPerPixel)
5253 {
5254 default:
5255 case 0:
5256 /* Nothing to do, just return. */
5257 return VINF_SUCCESS;
5258 case 8:
5259 v = VGA_DRAW_LINE8;
5260 break;
5261 case 15:
5262 v = VGA_DRAW_LINE15;
5263 break;
5264 case 16:
5265 v = VGA_DRAW_LINE16;
5266 break;
5267 case 24:
5268 v = VGA_DRAW_LINE24;
5269 break;
5270 case 32:
5271 v = VGA_DRAW_LINE32;
5272 break;
5273 }
5274
5275 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5276 AssertRC(rc);
5277
5278 /* This method only works if the VGA device is in a VBE mode. */
5279 if ((pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0)
5280 {
5281 PDMCritSectLeave(&pThis->CritSect);
5282 return VERR_INVALID_STATE;
5283 }
5284
5285 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(u32DstBitsPerPixel)];
5286
5287 /* Compute source and destination addresses and pitches. */
5288 cbPixelDst = (u32DstBitsPerPixel + 7) / 8;
5289 cbLineDst = u32DstLineSize;
5290 pu8DstPtr = pu8Dst + yDst * cbLineDst + xDst * cbPixelDst;
5291
5292 cbPixelSrc = (u32SrcBitsPerPixel + 7) / 8;
5293 cbLineSrc = u32SrcLineSize;
5294 pu8SrcPtr = pu8Src + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5295
5296#ifdef DEBUG_sunlover
5297 LogFlow(("vgaPortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8DstPtr, cbLineDst, cbPixelDst, pu8SrcPtr, cbLineSrc, cbPixelSrc));
5298#endif /* DEBUG_sunlover */
5299
5300 while (hCorrected-- > 0)
5301 {
5302 vga_draw_line (pThis, pu8DstPtr, pu8SrcPtr, wCorrected);
5303 pu8DstPtr += cbLineDst;
5304 pu8SrcPtr += cbLineSrc;
5305 }
5306
5307 PDMCritSectLeave(&pThis->CritSect);
5308#ifdef DEBUG_sunlover
5309 LogFlow(("vgaPortCopyRect: completed.\n"));
5310#endif /* DEBUG_sunlover */
5311
5312 return VINF_SUCCESS;
5313}
5314
5315static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5316{
5317 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5318
5319 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
5320
5321 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5322 AssertRC(rc);
5323
5324 pThis->fRenderVRAM = fRender;
5325
5326 PDMCritSectLeave(&pThis->CritSect);
5327}
5328
5329
5330static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5331{
5332 PVGASTATE pThis = (PVGASTATE)pvUser;
5333 NOREF(pDevIns);
5334
5335 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5336 {
5337 VBVARaiseIrq(pThis, HGSMIHOSTFLAGS_VSYNC);
5338 }
5339
5340 if (pThis->pDrv)
5341 pThis->pDrv->pfnRefresh(pThis->pDrv);
5342
5343 if (pThis->cMilliesRefreshInterval)
5344 TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
5345
5346#ifdef VBOX_WITH_VIDEOHWACCEL
5347 vbvaTimerCb(pThis);
5348#endif
5349
5350#ifdef VBOX_WITH_CRHGSMI
5351 vboxCmdVBVACmdTimer(pThis);
5352#endif
5353}
5354
5355#ifdef VBOX_WITH_VMSVGA
5356int vgaR3RegisterVRAMHandler(PVGASTATE pVGAState, uint64_t cbFrameBuffer)
5357{
5358 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5359 Assert(pVGAState->GCPhysVRAM);
5360
5361 int rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5362 pVGAState->GCPhysVRAM, pVGAState->GCPhysVRAM + (cbFrameBuffer - 1),
5363 pVGAState->hLfbAccessHandlerType, pVGAState, pDevIns->pvInstanceDataR0,
5364 pDevIns->pvInstanceDataRC, "VGA LFB");
5365
5366 AssertRC(rc);
5367 return rc;
5368}
5369
5370int vgaR3UnregisterVRAMHandler(PVGASTATE pVGAState)
5371{
5372 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5373
5374 Assert(pVGAState->GCPhysVRAM);
5375 int rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pVGAState->GCPhysVRAM);
5376 AssertRC(rc);
5377 return rc;
5378}
5379#endif
5380
5381/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5382
5383/**
5384 * Callback function for unmapping and/or mapping the VRAM MMIO2 region (called by the PCI bus).
5385 *
5386 * @return VBox status code.
5387 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
5388 * @param iRegion The region number.
5389 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
5390 * I/O port, else it's a physical address.
5391 * This address is *NOT* relative to pci_mem_base like earlier!
5392 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
5393 */
5394static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5395{
5396 int rc;
5397 PPDMDEVINS pDevIns = pPciDev->pDevIns;
5398 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5399 Log(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5400#ifdef VBOX_WITH_VMSVGA
5401 AssertReturn((iRegion == ((pThis->fVMSVGAEnabled) ? 1 : 0)) && (enmType == ((pThis->fVMSVGAEnabled) ? PCI_ADDRESS_SPACE_MEM : PCI_ADDRESS_SPACE_MEM_PREFETCH)), VERR_INTERNAL_ERROR);
5402#else
5403 AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5404#endif
5405
5406 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5407 AssertRC(rc);
5408
5409 if (GCPhysAddress != NIL_RTGCPHYS)
5410 {
5411 /*
5412 * Mapping the VRAM.
5413 */
5414 rc = PDMDevHlpMMIO2Map(pDevIns, iRegion, GCPhysAddress);
5415 AssertRC(rc);
5416 if (RT_SUCCESS(rc))
5417 {
5418 rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns), GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5419 pThis->hLfbAccessHandlerType, pThis, pDevIns->pvInstanceDataR0,
5420 pDevIns->pvInstanceDataRC, "VGA LFB");
5421 AssertRC(rc);
5422 if (RT_SUCCESS(rc))
5423 {
5424 pThis->GCPhysVRAM = GCPhysAddress;
5425 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5426 }
5427 }
5428 }
5429 else
5430 {
5431 /*
5432 * Unmapping of the VRAM in progress.
5433 * Deregister the access handler so PGM doesn't get upset.
5434 */
5435 Assert(pThis->GCPhysVRAM);
5436#ifdef VBOX_WITH_VMSVGA
5437 Assert(!pThis->svga.fEnabled || !pThis->svga.fVRAMTracking);
5438 if ( !pThis->svga.fEnabled
5439 || ( pThis->svga.fEnabled
5440 && pThis->svga.fVRAMTracking
5441 )
5442 )
5443 {
5444#endif
5445 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5446 AssertRC(rc);
5447#ifdef VBOX_WITH_VMSVGA
5448 }
5449 else
5450 rc = VINF_SUCCESS;
5451#endif
5452 pThis->GCPhysVRAM = 0;
5453 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5454 }
5455 PDMCritSectLeave(&pThis->CritSect);
5456 return rc;
5457}
5458
5459
5460/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5461
5462/**
5463 * Saves a important bits of the VGA device config.
5464 *
5465 * @param pThis The VGA instance data.
5466 * @param pSSM The saved state handle.
5467 */
5468static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
5469{
5470 SSMR3PutU32(pSSM, pThis->vram_size);
5471 SSMR3PutU32(pSSM, pThis->cMonitors);
5472}
5473
5474
5475/**
5476 * @copydoc FNSSMDEVLIVEEXEC
5477 */
5478static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5479{
5480 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5481 Assert(uPass == 0); NOREF(uPass);
5482 vgaR3SaveConfig(pThis, pSSM);
5483 return VINF_SSM_DONT_CALL_AGAIN;
5484}
5485
5486
5487/**
5488 * @copydoc FNSSMDEVSAVEPREP
5489 */
5490static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5491{
5492#ifdef VBOX_WITH_VIDEOHWACCEL
5493 return vboxVBVASaveStatePrep(pDevIns, pSSM);
5494#else
5495 return VINF_SUCCESS;
5496#endif
5497}
5498
5499static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5500{
5501#ifdef VBOX_WITH_VIDEOHWACCEL
5502 return vboxVBVASaveStateDone(pDevIns, pSSM);
5503#else
5504 return VINF_SUCCESS;
5505#endif
5506}
5507
5508/**
5509 * @copydoc FNSSMDEVSAVEEXEC
5510 */
5511static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5512{
5513 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5514
5515#ifdef VBOX_WITH_VDMA
5516 vboxVDMASaveStateExecPrep(pThis->pVdma, pSSM);
5517#endif
5518
5519 vgaR3SaveConfig(pThis, pSSM);
5520 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5521
5522#ifdef VBOX_WITH_HGSMI
5523 SSMR3PutBool(pSSM, true);
5524 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5525#else
5526 int rc = SSMR3PutBool(pSSM, false);
5527#endif
5528
5529 AssertRCReturn(rc, rc);
5530
5531#ifdef VBOX_WITH_VDMA
5532 rc = SSMR3PutU32(pSSM, 1);
5533 AssertRCReturn(rc, rc);
5534 rc = vboxVDMASaveStateExecPerform(pThis->pVdma, pSSM);
5535#else
5536 rc = SSMR3PutU32(pSSM, 0);
5537#endif
5538 AssertRCReturn(rc, rc);
5539
5540#ifdef VBOX_WITH_VDMA
5541 vboxVDMASaveStateExecDone(pThis->pVdma, pSSM);
5542#endif
5543
5544#ifdef VBOX_WITH_VMSVGA
5545 if (pThis->fVMSVGAEnabled)
5546 {
5547 rc = vmsvgaSaveExec(pDevIns, pSSM);
5548 AssertRCReturn(rc, rc);
5549 }
5550#endif
5551
5552 return rc;
5553}
5554
5555
5556/**
5557 * @copydoc FNSSMDEVSAVEEXEC
5558 */
5559static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5560{
5561 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5562 int rc;
5563
5564 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5565 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5566
5567 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5568 {
5569 /* Check the config */
5570 uint32_t cbVRam;
5571 rc = SSMR3GetU32(pSSM, &cbVRam);
5572 AssertRCReturn(rc, rc);
5573 if (pThis->vram_size != cbVRam)
5574 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5575
5576 uint32_t cMonitors;
5577 rc = SSMR3GetU32(pSSM, &cMonitors);
5578 AssertRCReturn(rc, rc);
5579 if (pThis->cMonitors != cMonitors)
5580 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5581 }
5582
5583 if (uPass == SSM_PASS_FINAL)
5584 {
5585 rc = vga_load(pSSM, pThis, uVersion);
5586 if (RT_FAILURE(rc))
5587 return rc;
5588 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5589 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5590 {
5591 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5592 AssertRCReturn(rc, rc);
5593 }
5594 if (fWithHgsmi)
5595 {
5596#ifdef VBOX_WITH_HGSMI
5597 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5598 AssertRCReturn(rc, rc);
5599#else
5600 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5601#endif
5602 }
5603
5604 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5605 {
5606 uint32_t u32;
5607 rc = SSMR3GetU32(pSSM, &u32);
5608 if (u32)
5609 {
5610#ifdef VBOX_WITH_VDMA
5611 if (u32 == 1)
5612 {
5613 rc = vboxVDMASaveLoadExecPerform(pThis->pVdma, pSSM, uVersion);
5614 AssertRCReturn(rc, rc);
5615 }
5616 else
5617#endif
5618 {
5619 LogRel(("invalid CmdVbva version info\n"));
5620 return VERR_VERSION_MISMATCH;
5621 }
5622 }
5623 }
5624
5625#ifdef VBOX_WITH_VMSVGA
5626 if (pThis->fVMSVGAEnabled)
5627 {
5628 rc = vmsvgaLoadExec(pDevIns, pSSM, uVersion, uPass);
5629 AssertRCReturn(rc, rc);
5630 }
5631#endif
5632 }
5633 return VINF_SUCCESS;
5634}
5635
5636
5637/**
5638 * @copydoc FNSSMDEVLOADDONE
5639 */
5640static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5641{
5642 int rc = VINF_SUCCESS;
5643
5644#ifdef VBOX_WITH_HGSMI
5645 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5646 VBVAPause(pThis, (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0);
5647 rc = vboxVBVALoadStateDone(pDevIns, pSSM);
5648 AssertRCReturn(rc, rc);
5649# ifdef VBOX_WITH_VDMA
5650 rc = vboxVDMASaveLoadDone(pThis->pVdma);
5651 AssertRCReturn(rc, rc);
5652# endif
5653#endif
5654#ifdef VBOX_WITH_VMSVGA
5655 if (pThis->fVMSVGAEnabled)
5656 {
5657 rc = vmsvgaLoadDone(pDevIns);
5658 AssertRCReturn(rc, rc);
5659 }
5660#endif
5661 return rc;
5662}
5663
5664
5665/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5666
5667/**
5668 * @interface_method_impl{PDMDEVREG,pfnReset}
5669 */
5670static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5671{
5672 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5673 char *pchStart;
5674 char *pchEnd;
5675 LogFlow(("vgaReset\n"));
5676
5677 if (pThis->pVdma)
5678 vboxVDMAReset(pThis->pVdma);
5679
5680#ifdef VBOX_WITH_VMSVGA
5681 if (pThis->fVMSVGAEnabled)
5682 vmsvgaReset(pDevIns);
5683#endif
5684
5685#ifdef VBOX_WITH_HGSMI
5686 VBVAReset(pThis);
5687#endif /* VBOX_WITH_HGSMI */
5688
5689
5690 /* Clear the VRAM ourselves. */
5691 if (pThis->vram_ptrR3 && pThis->vram_size)
5692 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5693
5694 /*
5695 * Zero most of it.
5696 *
5697 * Unlike vga_reset we're leaving out a few members which we believe
5698 * must remain unchanged....
5699 */
5700 /* 1st part. */
5701 pchStart = (char *)&pThis->latch;
5702 pchEnd = (char *)&pThis->invalidated_y_table;
5703 memset(pchStart, 0, pchEnd - pchStart);
5704
5705 /* 2nd part. */
5706 pchStart = (char *)&pThis->last_palette;
5707 pchEnd = (char *)&pThis->u32Marker;
5708 memset(pchStart, 0, pchEnd - pchStart);
5709
5710
5711 /*
5712 * Restore and re-init some bits.
5713 */
5714 pThis->get_bpp = vga_get_bpp;
5715 pThis->get_offsets = vga_get_offsets;
5716 pThis->get_resolution = vga_get_resolution;
5717 pThis->graphic_mode = -1; /* Force full update. */
5718#ifdef CONFIG_BOCHS_VBE
5719 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5720 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5721 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
5722 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5723#endif /* CONFIG_BOCHS_VBE */
5724
5725 /*
5726 * Reset the LBF mapping.
5727 */
5728 pThis->fLFBUpdated = false;
5729 if ( ( pThis->fGCEnabled
5730 || pThis->fR0Enabled)
5731 && pThis->GCPhysVRAM
5732 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
5733 {
5734 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5735 AssertRC(rc);
5736 }
5737 if (pThis->fRemappedVGA)
5738 {
5739 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5740 pThis->fRemappedVGA = false;
5741 }
5742
5743 /*
5744 * Reset the logo data.
5745 */
5746 pThis->LogoCommand = LOGO_CMD_NOP;
5747 pThis->offLogoData = 0;
5748
5749 /* notify port handler */
5750 if (pThis->pDrv)
5751 {
5752 PDMCritSectLeave(&pThis->CritSect); /* hack around lock order issue. */
5753 pThis->pDrv->pfnReset(pThis->pDrv);
5754 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
5755 }
5756
5757 /* Reset latched access mask. */
5758 pThis->uMaskLatchAccess = 0x3ff;
5759 pThis->cLatchAccesses = 0;
5760 pThis->u64LastLatchedAccess = 0;
5761 pThis->iMask = 0;
5762
5763 /* Reset retrace emulation. */
5764 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5765}
5766
5767
5768/**
5769 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5770 */
5771static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5772{
5773 if (offDelta)
5774 {
5775 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5776 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5777
5778 pThis->vram_ptrRC += offDelta;
5779 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5780 }
5781}
5782
5783
5784/**
5785 * @interface_method_impl{PDMDEVREG,pfnAttach}
5786 *
5787 * This is like plugging in the monitor after turning on the PC.
5788 */
5789static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5790{
5791 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5792
5793 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5794 ("VGA device does not support hotplugging\n"),
5795 VERR_INVALID_PARAMETER);
5796
5797 switch (iLUN)
5798 {
5799 /* LUN #0: Display port. */
5800 case 0:
5801 {
5802 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5803 if (RT_SUCCESS(rc))
5804 {
5805 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5806 if (pThis->pDrv)
5807 {
5808 /* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
5809 if ( pThis->pDrv->pfnRefresh
5810 && pThis->pDrv->pfnResize
5811 && pThis->pDrv->pfnUpdateRect)
5812 rc = VINF_SUCCESS;
5813 else
5814 {
5815 Assert(pThis->pDrv->pfnRefresh);
5816 Assert(pThis->pDrv->pfnResize);
5817 Assert(pThis->pDrv->pfnUpdateRect);
5818 pThis->pDrv = NULL;
5819 pThis->pDrvBase = NULL;
5820 rc = VERR_INTERNAL_ERROR;
5821 }
5822#ifdef VBOX_WITH_VIDEOHWACCEL
5823 if(rc == VINF_SUCCESS)
5824 {
5825 rc = vbvaVHWAConstruct(pThis);
5826 if (rc != VERR_NOT_IMPLEMENTED)
5827 AssertRC(rc);
5828 }
5829#endif
5830 }
5831 else
5832 {
5833 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5834 pThis->pDrvBase = NULL;
5835 rc = VERR_PDM_MISSING_INTERFACE;
5836 }
5837 }
5838 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5839 {
5840 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5841 rc = VINF_SUCCESS;
5842 }
5843 else
5844 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5845 return rc;
5846 }
5847
5848 default:
5849 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5850 return VERR_PDM_NO_SUCH_LUN;
5851 }
5852}
5853
5854
5855/**
5856 * @interface_method_impl{PDMDEVREG,pfnDetach}
5857 *
5858 * This is like unplugging the monitor while the PC is still running.
5859 */
5860static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5861{
5862 /*
5863 * Reset the interfaces and update the controller state.
5864 */
5865 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5866
5867 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5868 ("VGA device does not support hotplugging\n"));
5869
5870 switch (iLUN)
5871 {
5872 /* LUN #0: Display port. */
5873 case 0:
5874 pThis->pDrv = NULL;
5875 pThis->pDrvBase = NULL;
5876 break;
5877
5878 default:
5879 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5880 break;
5881 }
5882}
5883
5884
5885/**
5886 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5887 */
5888static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5889{
5890 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5891
5892#ifdef VBE_NEW_DYN_LIST
5893 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5894 LogFlow(("vgaR3Destruct:\n"));
5895
5896# ifdef VBOX_WITH_VDMA
5897 if (pThis->pVdma)
5898 vboxVDMADestruct(pThis->pVdma);
5899# endif
5900
5901#ifdef VBOX_WITH_VMSVGA
5902 if (pThis->fVMSVGAEnabled)
5903 vmsvgaDestruct(pDevIns);
5904#endif
5905
5906 /*
5907 * Free MM heap pointers.
5908 */
5909 if (pThis->pu8VBEExtraData)
5910 {
5911 MMR3HeapFree(pThis->pu8VBEExtraData);
5912 pThis->pu8VBEExtraData = NULL;
5913 }
5914#endif /* VBE_NEW_DYN_LIST */
5915 if (pThis->pu8VgaBios)
5916 {
5917 MMR3HeapFree(pThis->pu8VgaBios);
5918 pThis->pu8VgaBios = NULL;
5919 }
5920
5921 if (pThis->pszVgaBiosFile)
5922 {
5923 MMR3HeapFree(pThis->pszVgaBiosFile);
5924 pThis->pszVgaBiosFile = NULL;
5925 }
5926
5927 if (pThis->pszLogoFile)
5928 {
5929 MMR3HeapFree(pThis->pszLogoFile);
5930 pThis->pszLogoFile = NULL;
5931 }
5932
5933 PDMR3CritSectDelete(&pThis->CritSect);
5934 return VINF_SUCCESS;
5935}
5936
5937
5938/**
5939 * Adjust VBE mode information
5940 *
5941 * Depending on the configured VRAM size, certain parts of VBE mode
5942 * information must be updated.
5943 *
5944 * @param pThis The device instance data.
5945 * @param pMode The mode information structure.
5946 */
5947static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
5948{
5949 int maxPage;
5950 int bpl;
5951
5952
5953 /* For 4bpp modes, the planes are "stacked" on top of each other. */
5954 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
5955 /* The "number of image pages" is really the max page index... */
5956 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
5957 Assert(maxPage >= 0);
5958 if (maxPage > 255)
5959 maxPage = 255; /* 8-bit value. */
5960 pMode->info.NumberOfImagePages = maxPage;
5961 pMode->info.LinNumberOfPages = maxPage;
5962}
5963
5964
5965/**
5966 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5967 */
5968static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5969{
5970
5971 static bool s_fExpandDone = false;
5972 int rc;
5973 unsigned i;
5974#ifdef VBE_NEW_DYN_LIST
5975 uint32_t cCustomModes;
5976 uint32_t cyReduction;
5977 uint32_t cbPitch;
5978 PVBEHEADER pVBEDataHdr;
5979 ModeInfoListItem *pCurMode;
5980 unsigned cb;
5981#endif
5982 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5983 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5984 PVM pVM = PDMDevHlpGetVM(pDevIns);
5985
5986 Assert(iInstance == 0);
5987 Assert(pVM);
5988
5989 /*
5990 * Init static data.
5991 */
5992 if (!s_fExpandDone)
5993 {
5994 s_fExpandDone = true;
5995 vga_init_expand();
5996 }
5997
5998 /*
5999 * Validate configuration.
6000 */
6001 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
6002 "MonitorCount\0"
6003 "GCEnabled\0"
6004 "R0Enabled\0"
6005 "FadeIn\0"
6006 "FadeOut\0"
6007 "LogoTime\0"
6008 "LogoFile\0"
6009 "ShowBootMenu\0"
6010 "BiosRom\0"
6011 "RealRetrace\0"
6012 "CustomVideoModes\0"
6013 "HeightReduction\0"
6014 "CustomVideoMode1\0"
6015 "CustomVideoMode2\0"
6016 "CustomVideoMode3\0"
6017 "CustomVideoMode4\0"
6018 "CustomVideoMode5\0"
6019 "CustomVideoMode6\0"
6020 "CustomVideoMode7\0"
6021 "CustomVideoMode8\0"
6022 "CustomVideoMode9\0"
6023 "CustomVideoMode10\0"
6024 "CustomVideoMode11\0"
6025 "CustomVideoMode12\0"
6026 "CustomVideoMode13\0"
6027 "CustomVideoMode14\0"
6028 "CustomVideoMode15\0"
6029 "CustomVideoMode16\0"
6030 "MaxBiosXRes\0"
6031 "MaxBiosYRes\0"
6032#ifdef VBOX_WITH_VMSVGA
6033 "VMSVGAEnabled\0"
6034#endif
6035#ifdef VBOX_WITH_VMSVGA3D
6036 "VMSVGA3dEnabled\0"
6037 "HostWindowId\0"
6038#endif
6039 ))
6040 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
6041 N_("Invalid configuration for vga device"));
6042
6043 /*
6044 * Init state data.
6045 */
6046 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6047 AssertLogRelRCReturn(rc, rc);
6048 if (pThis->vram_size > VGA_VRAM_MAX)
6049 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6050 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6051 if (pThis->vram_size < VGA_VRAM_MIN)
6052 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6053 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6054 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6055 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6056 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6057
6058 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6059 AssertLogRelRCReturn(rc, rc);
6060
6061 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
6062 AssertLogRelRCReturn(rc, rc);
6063
6064 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
6065 AssertLogRelRCReturn(rc, rc);
6066 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
6067
6068#ifdef VBOX_WITH_VMSVGA
6069 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6070 AssertLogRelRCReturn(rc, rc);
6071 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6072#endif
6073#ifdef VBOX_WITH_VMSVGA3D
6074 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6075 AssertLogRelRCReturn(rc, rc);
6076 rc = CFGMR3QueryU64Def(pCfg, "HostWindowId", &pThis->svga.u64HostWindowId, 0);
6077 AssertLogRelRCReturn(rc, rc);
6078 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6079 Log(("VMSVGA: HostWindowId = 0x%x\n", pThis->svga.u64HostWindowId));
6080#endif
6081
6082 pThis->pDevInsR3 = pDevIns;
6083 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
6084 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6085
6086 vgaR3Reset(pDevIns);
6087
6088 /* The PCI devices configuration. */
6089#ifdef VBOX_WITH_VMSVGA
6090 if (pThis->fVMSVGAEnabled)
6091 {
6092 /* Extend our VGA device with VMWare SVGA functionality. */
6093 PCIDevSetVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6094 PCIDevSetDeviceId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6095 PCIDevSetSubSystemVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6096 PCIDevSetSubSystemId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6097 }
6098 else
6099 {
6100#endif /* VBOX_WITH_VMSVGA */
6101 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
6102 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
6103#ifdef VBOX_WITH_VMSVGA
6104 }
6105#endif
6106 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
6107 PCIDevSetClassBase( &pThis->Dev, 0x03);
6108 PCIDevSetHeaderType(&pThis->Dev, 0x00);
6109#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6110 PCIDevSetInterruptPin(&pThis->Dev, 1);
6111#endif
6112
6113 /* the interfaces. */
6114 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
6115
6116 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
6117 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
6118 pThis->IPort.pfnQueryVideoMode = vgaPortQueryVideoMode;
6119 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
6120 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
6121 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
6122 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
6123 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
6124 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
6125 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
6126#ifdef VBOX_WITH_VMSVGA
6127 pThis->IPort.pfnSetViewPort = vmsvgaPortSetViewPort;
6128#endif
6129 pThis->IPort.pfnSendModeHint = vbvaPortSendModeHint;
6130 pThis->IPort.pfnReportHostCursorCapabilities
6131 = vbvaPortReportHostCursorCapabilities;
6132 pThis->IPort.pfnReportHostCursorPosition
6133 = vbvaPortReportHostCursorPosition;
6134
6135#if defined(VBOX_WITH_HGSMI)
6136# if defined(VBOX_WITH_VIDEOHWACCEL)
6137 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaVHWACommandCompleteAsync;
6138# endif
6139#if defined(VBOX_WITH_CRHGSMI)
6140 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
6141 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
6142
6143 pThis->IVBVACallbacks.pfnCrCtlSubmit = vboxCmdVBVACmdHostCtl;
6144 pThis->IVBVACallbacks.pfnCrCtlSubmitSync = vboxCmdVBVACmdHostCtlSync;
6145# endif
6146#endif
6147
6148 pThis->ILeds.pfnQueryStatusLed = vgaPortQueryStatusLed;
6149
6150 RT_ZERO(pThis->Led3D);
6151 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6152
6153 /*
6154 * We use our own critical section to avoid unncessary pointer indirections
6155 * in interface methods (as well as for historical reasons).
6156 */
6157 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6158 AssertRCReturn(rc, rc);
6159 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6160 AssertRCReturn(rc, rc);
6161
6162 /*
6163 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
6164 */
6165#ifdef VBOX_WITH_VMSVGA
6166 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
6167
6168 if (pThis->fVMSVGAEnabled)
6169 {
6170 /*
6171 * Allocate and initialize the FIFO MMIO2 memory.
6172 */
6173 rc = PDMDevHlpMMIO2Register(pDevIns, 2 /*iRegion*/, VMSVGA_FIFO_SIZE, 0 /*fFlags*/, (void **)&pThis->svga.pFIFOR3, "VMSVGA-FIFO");
6174 if (RT_FAILURE(rc))
6175 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6176 N_("Failed to allocate %u bytes of memory for the VMSVGA device"), VMSVGA_FIFO_SIZE);
6177 pThis->svga.pFIFOR0 = (RTR0PTR)pThis->svga.pFIFOR3;
6178 pThis->svga.cbFIFO = VMSVGA_FIFO_SIZE;
6179 }
6180#else
6181 int iPCIRegionVRAM = 0;
6182#endif
6183 rc = PDMDevHlpMMIO2Register(pDevIns, iPCIRegionVRAM, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
6184 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
6185 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo @bugref{1865} Map parts into R0 or just use PGM access (Mac only). */
6186
6187 if (pThis->fGCEnabled)
6188 {
6189 RTRCPTR pRCMapping = 0;
6190 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
6191 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6192 pThis->vram_ptrRC = pRCMapping;
6193# ifdef VBOX_WITH_VMSVGA
6194 /* Don't need a mapping in RC */
6195# endif
6196 }
6197
6198#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
6199 if (pThis->fR0Enabled)
6200 {
6201 RTR0PTR pR0Mapping = 0;
6202 rc = PDMDevHlpMMIO2MapKernel(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
6203 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6204 pThis->vram_ptrR0 = pR0Mapping;
6205# ifdef VBOX_WITH_VMSVGA
6206 if (pThis->fVMSVGAEnabled)
6207 {
6208 RTR0PTR pR0Mapping = 0;
6209 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 2 /* iRegion */, 0 /* off */, VMSVGA_FIFO_SIZE, "VMSVGA-FIFO", &pR0Mapping);
6210 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VMSVGA_FIFO_SIZE, rc), rc);
6211 pThis->svga.pFIFOR0 = pR0Mapping;
6212 }
6213# endif
6214 }
6215#endif
6216
6217 /*
6218 * Register access handler types.
6219 */
6220 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
6221 vgaR3LFBAccessHandler,
6222 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler",
6223 g_DeviceVga.szRCMod, "vgaRCLFBAccessHandler",
6224 "VGA LFB", &pThis->hLfbAccessHandlerType);
6225 AssertRCReturn(rc, rc);
6226
6227
6228 /*
6229 * Register I/O ports.
6230 */
6231 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
6232 if (RT_FAILURE(rc))
6233 return rc;
6234 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
6235 if (RT_FAILURE(rc))
6236 return rc;
6237 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
6238 if (RT_FAILURE(rc))
6239 return rc;
6240 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
6241 if (RT_FAILURE(rc))
6242 return rc;
6243 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
6244 if (RT_FAILURE(rc))
6245 return rc;
6246#ifdef VBOX_WITH_HGSMI
6247 /* Use reserved VGA IO ports for HGSMI. */
6248 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
6249 if (RT_FAILURE(rc))
6250 return rc;
6251 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
6252 if (RT_FAILURE(rc))
6253 return rc;
6254#endif /* VBOX_WITH_HGSMI */
6255
6256#ifdef CONFIG_BOCHS_VBE
6257 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
6258 if (RT_FAILURE(rc))
6259 return rc;
6260 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
6261 if (RT_FAILURE(rc))
6262 return rc;
6263#endif /* CONFIG_BOCHS_VBE */
6264
6265 /* guest context extension */
6266 if (pThis->fGCEnabled)
6267 {
6268 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6269 if (RT_FAILURE(rc))
6270 return rc;
6271 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6272 if (RT_FAILURE(rc))
6273 return rc;
6274 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6275 if (RT_FAILURE(rc))
6276 return rc;
6277 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6278 if (RT_FAILURE(rc))
6279 return rc;
6280 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6281 if (RT_FAILURE(rc))
6282 return rc;
6283#ifdef CONFIG_BOCHS_VBE
6284 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6285 if (RT_FAILURE(rc))
6286 return rc;
6287 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6288 if (RT_FAILURE(rc))
6289 return rc;
6290#endif /* CONFIG_BOCHS_VBE */
6291 }
6292
6293 /* R0 context extension */
6294 if (pThis->fR0Enabled)
6295 {
6296 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6297 if (RT_FAILURE(rc))
6298 return rc;
6299 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6300 if (RT_FAILURE(rc))
6301 return rc;
6302 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6303 if (RT_FAILURE(rc))
6304 return rc;
6305 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6306 if (RT_FAILURE(rc))
6307 return rc;
6308 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6309 if (RT_FAILURE(rc))
6310 return rc;
6311#ifdef CONFIG_BOCHS_VBE
6312 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6313 if (RT_FAILURE(rc))
6314 return rc;
6315 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6316 if (RT_FAILURE(rc))
6317 return rc;
6318#endif /* CONFIG_BOCHS_VBE */
6319 }
6320
6321 /* vga mmio */
6322 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
6323 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
6324 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
6325 if (RT_FAILURE(rc))
6326 return rc;
6327 if (pThis->fGCEnabled)
6328 {
6329 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6330 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6331 if (RT_FAILURE(rc))
6332 return rc;
6333 }
6334 if (pThis->fR0Enabled)
6335 {
6336 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6337 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6338 if (RT_FAILURE(rc))
6339 return rc;
6340 }
6341
6342 /* vga bios */
6343 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6344 if (RT_FAILURE(rc))
6345 return rc;
6346 if (pThis->fR0Enabled)
6347 {
6348 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6349 if (RT_FAILURE(rc))
6350 return rc;
6351 }
6352
6353 /*
6354 * Get the VGA BIOS ROM file name.
6355 */
6356 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6357 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6358 {
6359 pThis->pszVgaBiosFile = NULL;
6360 rc = VINF_SUCCESS;
6361 }
6362 else if (RT_FAILURE(rc))
6363 return PDMDEV_SET_ERROR(pDevIns, rc,
6364 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6365 else if (!*pThis->pszVgaBiosFile)
6366 {
6367 MMR3HeapFree(pThis->pszVgaBiosFile);
6368 pThis->pszVgaBiosFile = NULL;
6369 }
6370
6371 /*
6372 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6373 */
6374 RTFILE FileVgaBios = NIL_RTFILE;
6375 if (pThis->pszVgaBiosFile)
6376 {
6377 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6378 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6379 if (RT_SUCCESS(rc))
6380 {
6381 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6382 if (RT_SUCCESS(rc))
6383 {
6384 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6385 || pThis->cbVgaBios > _64K
6386 || pThis->cbVgaBios < 16 * _1K)
6387 rc = VERR_TOO_MUCH_DATA;
6388 }
6389 }
6390 if (RT_FAILURE(rc))
6391 {
6392 /*
6393 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6394 */
6395 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6396 RTFileClose(FileVgaBios);
6397 FileVgaBios = NIL_RTFILE;
6398 MMR3HeapFree(pThis->pszVgaBiosFile);
6399 pThis->pszVgaBiosFile = NULL;
6400 }
6401 }
6402
6403 /*
6404 * Attempt to get the VGA BIOS ROM data from file.
6405 */
6406 if (pThis->pszVgaBiosFile)
6407 {
6408 /*
6409 * Allocate buffer for the VGA BIOS ROM data.
6410 */
6411 pThis->pu8VgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6412 if (pThis->pu8VgaBios)
6413 {
6414 rc = RTFileRead(FileVgaBios, pThis->pu8VgaBios, pThis->cbVgaBios, NULL);
6415 if (RT_FAILURE(rc))
6416 {
6417 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6418 MMR3HeapFree(pThis->pu8VgaBios);
6419 pThis->pu8VgaBios = NULL;
6420 }
6421 rc = VINF_SUCCESS;
6422 }
6423 else
6424 rc = VERR_NO_MEMORY;
6425 }
6426 else
6427 pThis->pu8VgaBios = NULL;
6428
6429 /* cleanup */
6430 if (FileVgaBios != NIL_RTFILE)
6431 RTFileClose(FileVgaBios);
6432
6433 /* If we were unable to get the data from file for whatever reason, fall
6434 back to the built-in ROM image. */
6435 const uint8_t *pu8VgaBiosBinary;
6436 uint64_t cbVgaBiosBinary;
6437 uint32_t fFlags = 0;
6438 if (pThis->pu8VgaBios == NULL)
6439 {
6440 pu8VgaBiosBinary = g_abVgaBiosBinary;
6441 cbVgaBiosBinary = g_cbVgaBiosBinary;
6442 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6443 }
6444 else
6445 {
6446 pu8VgaBiosBinary = pThis->pu8VgaBios;
6447 cbVgaBiosBinary = pThis->cbVgaBios;
6448 }
6449
6450 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6451 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6452 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6453 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pu8VgaBiosBinary, cbVgaBiosBinary,
6454 fFlags, "VGA BIOS");
6455 if (RT_FAILURE(rc))
6456 return rc;
6457
6458 /*
6459 * Saved state.
6460 */
6461 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6462 NULL, vgaR3LiveExec, NULL,
6463 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6464 NULL, vgaR3LoadExec, vgaR3LoadDone);
6465 if (RT_FAILURE(rc))
6466 return rc;
6467
6468 /*
6469 * PCI device registration.
6470 */
6471 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6472 if (RT_FAILURE(rc))
6473 return rc;
6474 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
6475 if (pThis->Dev.devfn != 16 && iInstance == 0)
6476 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
6477
6478#ifdef VBOX_WITH_VMSVGA
6479 if (pThis->fVMSVGAEnabled)
6480 {
6481 /* Register the io command ports. */
6482 rc = PDMDevHlpPCIIORegionRegister (pDevIns, 0 /* iRegion */, 0x10, PCI_ADDRESS_SPACE_IO, vmsvgaR3IORegionMap);
6483 if (RT_FAILURE (rc))
6484 return rc;
6485 /* VMware's MetalKit doesn't like PCI_ADDRESS_SPACE_MEM_PREFETCH */
6486 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vgaR3IORegionMap);
6487 if (RT_FAILURE(rc))
6488 return rc;
6489 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2 /* iRegion */, VMSVGA_FIFO_SIZE, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vmsvgaR3IORegionMap);
6490 if (RT_FAILURE(rc))
6491 return rc;
6492 }
6493 else
6494#endif /* VBOX_WITH_VMSVGA */
6495 rc = PDMDevHlpPCIIORegionRegister(pDevIns, iPCIRegionVRAM, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6496 if (RT_FAILURE(rc))
6497 return rc;
6498
6499 /*
6500 * Create the refresh timer.
6501 */
6502 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6503 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6504 "VGA Refresh Timer", &pThis->RefreshTimer);
6505 if (RT_FAILURE(rc))
6506 return rc;
6507
6508 /*
6509 * Attach to the display.
6510 */
6511 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6512 if (RT_FAILURE(rc))
6513 return rc;
6514
6515 /*
6516 * Initialize the retrace flag.
6517 */
6518 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6519 AssertLogRelRCReturn(rc, rc);
6520
6521#ifdef VBE_NEW_DYN_LIST
6522
6523 uint16_t maxBiosXRes;
6524 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6525 AssertLogRelRCReturn(rc, rc);
6526 uint16_t maxBiosYRes;
6527 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6528 AssertLogRelRCReturn(rc, rc);
6529
6530 /*
6531 * Compute buffer size for the VBE BIOS Extra Data.
6532 */
6533 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6534
6535 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6536 if (RT_SUCCESS(rc) && cyReduction)
6537 cb *= 2; /* Default mode list will be twice long */
6538 else
6539 cyReduction = 0;
6540
6541 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6542 if (RT_SUCCESS(rc) && cCustomModes)
6543 cb += sizeof(ModeInfoListItem) * cCustomModes;
6544 else
6545 cCustomModes = 0;
6546
6547 /*
6548 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6549 */
6550 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6551 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6552 pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6553 if (!pThis->pu8VBEExtraData)
6554 return VERR_NO_MEMORY;
6555
6556 pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
6557 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6558 pVBEDataHdr->cbData = cb;
6559
6560# ifndef VRAM_SIZE_FIX
6561 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6562 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6563# else /* VRAM_SIZE_FIX defined */
6564 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6565 for (i = 0; i < MODE_INFO_SIZE; i++)
6566 {
6567 uint32_t pixelWidth, reqSize;
6568 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6569 pixelWidth = 2;
6570 else
6571 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6572 reqSize = mode_info_list[i].info.XResolution
6573 * mode_info_list[i].info.YResolution
6574 * pixelWidth;
6575 if (reqSize >= pThis->vram_size)
6576 continue;
6577 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6578 || mode_info_list[i].info.YResolution > maxBiosYRes)
6579 continue;
6580 *pCurMode = mode_info_list[i];
6581 vgaAdjustModeInfo(pThis, pCurMode);
6582 pCurMode++;
6583 }
6584# endif /* VRAM_SIZE_FIX defined */
6585
6586 /*
6587 * Copy default modes with subtracted YResolution.
6588 */
6589 if (cyReduction)
6590 {
6591 ModeInfoListItem *pDefMode = mode_info_list;
6592 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6593# ifndef VRAM_SIZE_FIX
6594 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6595 {
6596 *pCurMode = *pDefMode;
6597 pCurMode->mode += 0x30;
6598 pCurMode->info.YResolution -= cyReduction;
6599 }
6600# else /* VRAM_SIZE_FIX defined */
6601 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6602 {
6603 uint32_t pixelWidth, reqSize;
6604 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6605 pixelWidth = 2;
6606 else
6607 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6608 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6609 if (reqSize >= pThis->vram_size)
6610 continue;
6611 if ( pDefMode->info.XResolution > maxBiosXRes
6612 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6613 continue;
6614 *pCurMode = *pDefMode;
6615 pCurMode->mode += 0x30;
6616 pCurMode->info.YResolution -= cyReduction;
6617 pCurMode++;
6618 }
6619# endif /* VRAM_SIZE_FIX defined */
6620 }
6621
6622
6623 /*
6624 * Add custom modes.
6625 */
6626 if (cCustomModes)
6627 {
6628 uint16_t u16CurMode = 0x160;
6629 for (i = 1; i <= cCustomModes; i++)
6630 {
6631 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6632 char *pszExtraData = NULL;
6633
6634 /* query and decode the custom mode string. */
6635 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6636 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6637 if (RT_SUCCESS(rc))
6638 {
6639 ModeInfoListItem *pDefMode = mode_info_list;
6640 unsigned int cx, cy, cBits, cParams, j;
6641 uint16_t u16DefMode;
6642
6643 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6644 if ( cParams != 3
6645 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6646 {
6647 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6648 return VERR_VGA_INVALID_CUSTOM_MODE;
6649 }
6650 cbPitch = calc_line_pitch(cBits, cx);
6651# ifdef VRAM_SIZE_FIX
6652 if (cy * cbPitch >= pThis->vram_size)
6653 {
6654 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",
6655 cx, cy, cBits, pThis->vram_size / _1M));
6656 return VERR_VGA_INVALID_CUSTOM_MODE;
6657 }
6658# endif /* VRAM_SIZE_FIX defined */
6659 MMR3HeapFree(pszExtraData);
6660
6661 /* Use defaults from max@bpp mode. */
6662 switch (cBits)
6663 {
6664 case 8:
6665 u16DefMode = VBE_VESA_MODE_1024X768X8;
6666 break;
6667
6668 case 16:
6669 u16DefMode = VBE_VESA_MODE_1024X768X565;
6670 break;
6671
6672 case 24:
6673 u16DefMode = VBE_VESA_MODE_1024X768X888;
6674 break;
6675
6676 case 32:
6677 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6678 break;
6679
6680 default: /* gcc, shut up! */
6681 AssertMsgFailed(("gone postal!\n"));
6682 continue;
6683 }
6684
6685 /* mode_info_list is not terminated */
6686 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6687 pDefMode++;
6688 Assert(j < MODE_INFO_SIZE);
6689
6690 *pCurMode = *pDefMode;
6691 pCurMode->mode = u16CurMode++;
6692
6693 /* adjust defaults */
6694 pCurMode->info.XResolution = cx;
6695 pCurMode->info.YResolution = cy;
6696 pCurMode->info.BytesPerScanLine = cbPitch;
6697 pCurMode->info.LinBytesPerScanLine = cbPitch;
6698 vgaAdjustModeInfo(pThis, pCurMode);
6699
6700 /* commit it */
6701 pCurMode++;
6702 }
6703 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6704 {
6705 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6706 return rc;
6707 }
6708 } /* foreach custom mode key */
6709 }
6710
6711 /*
6712 * Add the "End of list" mode.
6713 */
6714 memset(pCurMode, 0, sizeof(*pCurMode));
6715 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6716
6717 /*
6718 * Register I/O Port for the VBE BIOS Extra Data.
6719 */
6720 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6721 if (RT_FAILURE(rc))
6722 return rc;
6723#endif /* VBE_NEW_DYN_LIST */
6724
6725 /*
6726 * Register I/O Port for the BIOS Logo.
6727 */
6728 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6729 if (RT_FAILURE(rc))
6730 return rc;
6731
6732 /*
6733 * Register debugger info callbacks.
6734 */
6735 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6736 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6737 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6738 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6739 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6740 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6741 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaInfoPlanar);
6742 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6743 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6744
6745 /*
6746 * Construct the logo header.
6747 */
6748 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6749
6750 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6751 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6752 LogoHdr.fu8FadeIn = 1;
6753 else if (RT_FAILURE(rc))
6754 return PDMDEV_SET_ERROR(pDevIns, rc,
6755 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6756
6757 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6758 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6759 LogoHdr.fu8FadeOut = 1;
6760 else if (RT_FAILURE(rc))
6761 return PDMDEV_SET_ERROR(pDevIns, rc,
6762 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6763
6764 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6765 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6766 LogoHdr.u16LogoMillies = 0;
6767 else if (RT_FAILURE(rc))
6768 return PDMDEV_SET_ERROR(pDevIns, rc,
6769 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6770
6771 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6772 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6773 LogoHdr.fu8ShowBootMenu = 0;
6774 else if (RT_FAILURE(rc))
6775 return PDMDEV_SET_ERROR(pDevIns, rc,
6776 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6777
6778#if defined(DEBUG) && !defined(DEBUG_sunlover)
6779 /* Disable the logo abd menu if all default settings. */
6780 if ( LogoHdr.fu8FadeIn
6781 && LogoHdr.fu8FadeOut
6782 && LogoHdr.u16LogoMillies == 0
6783 && LogoHdr.fu8ShowBootMenu == 2)
6784 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.fu8ShowBootMenu = 0;
6785#endif
6786
6787 /* Delay the logo a little bit */
6788 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6789 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6790
6791 /*
6792 * Get the Logo file name.
6793 */
6794 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6795 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6796 pThis->pszLogoFile = NULL;
6797 else if (RT_FAILURE(rc))
6798 return PDMDEV_SET_ERROR(pDevIns, rc,
6799 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6800 else if (!*pThis->pszLogoFile)
6801 {
6802 MMR3HeapFree(pThis->pszLogoFile);
6803 pThis->pszLogoFile = NULL;
6804 }
6805
6806 /*
6807 * Determine the logo size, open any specified logo file in the process.
6808 */
6809 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6810 RTFILE FileLogo = NIL_RTFILE;
6811 if (pThis->pszLogoFile)
6812 {
6813 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6814 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6815 if (RT_SUCCESS(rc))
6816 {
6817 uint64_t cbFile;
6818 rc = RTFileGetSize(FileLogo, &cbFile);
6819 if (RT_SUCCESS(rc))
6820 {
6821 if (cbFile > 0 && cbFile < 32*_1M)
6822 LogoHdr.cbLogo = (uint32_t)cbFile;
6823 else
6824 rc = VERR_TOO_MUCH_DATA;
6825 }
6826 }
6827 if (RT_FAILURE(rc))
6828 {
6829 /*
6830 * Ignore failure and fall back to the default logo.
6831 */
6832 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6833 if (FileLogo != NIL_RTFILE)
6834 RTFileClose(FileLogo);
6835 FileLogo = NIL_RTFILE;
6836 MMR3HeapFree(pThis->pszLogoFile);
6837 pThis->pszLogoFile = NULL;
6838 }
6839 }
6840
6841 /*
6842 * Disable graphic splash screen if it doesn't fit into VRAM.
6843 */
6844 if (pThis->vram_size < LOGO_MAX_SIZE)
6845 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6846
6847 /*
6848 * Allocate buffer for the logo data.
6849 * RT_MAX() is applied to let us fall back to default logo on read failure.
6850 */
6851 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6852 pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6853 if (pThis->pu8Logo)
6854 {
6855 /*
6856 * Write the logo header.
6857 */
6858 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
6859 *pLogoHdr = LogoHdr;
6860
6861 /*
6862 * Write the logo bitmap.
6863 */
6864 if (pThis->pszLogoFile)
6865 {
6866 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6867 if (RT_SUCCESS(rc))
6868 rc = vbeParseBitmap(pThis);
6869 if (RT_FAILURE(rc))
6870 {
6871 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
6872 rc, pThis->pszLogoFile));
6873 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6874 }
6875 }
6876 if ( !pThis->pszLogoFile
6877 || RT_FAILURE(rc))
6878 {
6879 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6880 rc = vbeParseBitmap(pThis);
6881 if (RT_FAILURE(rc))
6882 AssertReleaseMsgFailed(("Parsing of internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6883 }
6884
6885 rc = VINF_SUCCESS;
6886 }
6887 else
6888 rc = VERR_NO_MEMORY;
6889
6890 /*
6891 * Cleanup.
6892 */
6893 if (FileLogo != NIL_RTFILE)
6894 RTFileClose(FileLogo);
6895
6896#ifdef VBOX_WITH_HGSMI
6897 VBVAInit (pThis);
6898#endif /* VBOX_WITH_HGSMI */
6899
6900#ifdef VBOX_WITH_VDMA
6901 if (rc == VINF_SUCCESS)
6902 {
6903 rc = vboxVDMAConstruct(pThis, 1024);
6904 AssertRC(rc);
6905 }
6906#endif
6907
6908#ifdef VBOX_WITH_VMSVGA
6909 if ( rc == VINF_SUCCESS
6910 && pThis->fVMSVGAEnabled)
6911 {
6912 rc = vmsvgaInit(pDevIns);
6913 }
6914#endif
6915
6916 /*
6917 * Statistics.
6918 */
6919 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6920 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6921 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6922 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6923 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6924 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
6925
6926 /* Init latched access mask. */
6927 pThis->uMaskLatchAccess = 0x3ff;
6928
6929 if (RT_SUCCESS(rc))
6930 {
6931 PPDMIBASE pBase;
6932 /*
6933 * Attach status driver (optional).
6934 */
6935 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
6936 AssertRC(rc);
6937 if (RT_SUCCESS(rc))
6938 {
6939 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
6940 pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
6941 }
6942 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6943 {
6944 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6945 rc = VINF_SUCCESS;
6946 }
6947 else
6948 {
6949 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
6950 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
6951 }
6952 }
6953 return rc;
6954}
6955
6956
6957/**
6958 * The device registration structure.
6959 */
6960const PDMDEVREG g_DeviceVga =
6961{
6962 /* u32Version */
6963 PDM_DEVREG_VERSION,
6964 /* szName */
6965 "vga",
6966 /* szRCMod */
6967 "VBoxDDGC.gc",
6968 /* szR0Mod */
6969 "VBoxDDR0.r0",
6970 /* pszDescription */
6971 "VGA Adaptor with VESA extensions.",
6972 /* fFlags */
6973 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6974 /* fClass */
6975 PDM_DEVREG_CLASS_GRAPHICS,
6976 /* cMaxInstances */
6977 1,
6978 /* cbInstance */
6979 sizeof(VGASTATE),
6980 /* pfnConstruct */
6981 vgaR3Construct,
6982 /* pfnDestruct */
6983 vgaR3Destruct,
6984 /* pfnRelocate */
6985 vgaR3Relocate,
6986 /* pfnMemSetup */
6987 NULL,
6988 /* pfnPowerOn */
6989#ifdef VBOX_WITH_VMSVGA
6990 vmsvgaR3PowerOn,
6991#else
6992 NULL,
6993#endif
6994 /* pfnReset */
6995 vgaR3Reset,
6996 /* pfnSuspend */
6997 NULL,
6998 /* pfnResume */
6999 NULL,
7000 /* pfnAttach */
7001 vgaAttach,
7002 /* pfnDetach */
7003 vgaDetach,
7004 /* pfnQueryInterface */
7005 NULL,
7006 /* pfnInitComplete */
7007 NULL,
7008 /* pfnPowerOff */
7009 NULL,
7010 /* pfnSoftReset */
7011 NULL,
7012 /* u32VersionEnd */
7013 PDM_DEVREG_VERSION
7014};
7015
7016#endif /* !IN_RING3 */
7017#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7018
7019/*
7020 * Local Variables:
7021 * nuke-trailing-whitespace-p:nil
7022 * End:
7023 */
Note: See TracBrowser for help on using the repository browser.

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