VirtualBox

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

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

DevVGA, Additions: minor improvements for the VGA configuration interface.

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