VirtualBox

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

Last change on this file since 71146 was 71146, checked in by vboxsync, 7 years ago

DevVGA, Additions: added VBE_DISPI_ID_CFG for querying the virtual graphics card features.

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