VirtualBox

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

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

new year splash screen for Oracle builds

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