VirtualBox

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

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

*: scm cleanup run.

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