VirtualBox

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

Last change on this file since 62032 was 62032, checked in by vboxsync, 8 years ago

Devices/VGA: fixed alignment of CritSectIRQ (important for 32-bit debug builds)

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