VirtualBox

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

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

PGM: Added a pVCpu parameter to all physical handler callouts and also a PGMACCESSORIGIN parameter to the ring-3 one. Fixed virtual handler callout mix up from previous commit.

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