VirtualBox

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

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

warning

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

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