VirtualBox

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

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

VGA: Implemented address wraparound for 16-color VGA modes.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette