VirtualBox

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

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

Devices/Graphics: scm cleanups

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