VirtualBox

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

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

DevVGA: Add a few missing checks which skip the VRAM alias mapping to the LFB when the PCI BAR isn't set up (i.e. the LFB physical address is 0, conflicting with system RAM). Performance is very bad in this case, but it's not an expected use case to have incorrectly initialized BARs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 244.8 KB
Line 
1/* $Id: DevVGA.cpp 67587 2017-06-23 16:18:49Z 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) << 2) | 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) << 2) | 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 << 2));
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 y1 = 0;
2490 y2 = pThis->cr[0x09] & 0x1F; /* starting row scan count */
2491 for(y = 0; y < height; y++) {
2492 addr = addr1;
2493 /* CGA/MDA compatibility. Note that these addresses are all
2494 * shifted left by two compared to VGA specs.
2495 */
2496 if (!(pThis->cr[0x17] & 1)) {
2497 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2498 }
2499 if (!(pThis->cr[0x17] & 2)) {
2500 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2501 }
2502 page0 = addr & ~PAGE_OFFSET_MASK;
2503 page1 = (addr + bwidth - 1) & ~PAGE_OFFSET_MASK;
2504 /** @todo r=klaus this assumes that a line is fully covered by 3 pages,
2505 * irrespective of alignment. Not guaranteed for high res modes, i.e.
2506 * anything wider than 2050 pixels @32bpp. Need to check all pages
2507 * between the first and last one. */
2508 bool update = full_update | vga_is_dirty(pThis, page0) | vga_is_dirty(pThis, page1);
2509 if (page1 - page0 > PAGE_SIZE) {
2510 /* if wide line, can use another page */
2511 update |= vga_is_dirty(pThis, page0 + PAGE_SIZE);
2512 }
2513 /* explicit invalidation for the hardware cursor */
2514 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2515 if (update) {
2516 if (y_start < 0)
2517 y_start = y;
2518 if (page0 < page_min)
2519 page_min = page0;
2520 if (page1 > page_max)
2521 page_max = page1;
2522 if (pThis->fRenderVRAM)
2523 vga_draw_line(pThis, d, pThis->CTX_SUFF(vram_ptr) + addr, width);
2524 if (pThis->cursor_draw_line)
2525 pThis->cursor_draw_line(pThis, d, y);
2526 } else {
2527 if (y_start >= 0) {
2528 /* flush to display */
2529 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2530 y_start = -1;
2531 }
2532 }
2533 if (!multi_run) {
2534 y1++;
2535 multi_run = double_scan;
2536
2537 if (y2 == 0) {
2538 y2 = pThis->cr[0x09] & 0x1F;
2539 addr1 += line_offset;
2540 } else {
2541 --y2;
2542 }
2543 } else {
2544 multi_run--;
2545 }
2546 /* line compare acts on the displayed lines */
2547 if ((uint32_t)y == pThis->line_compare)
2548 addr1 = 0;
2549 d += linesize;
2550 }
2551 if (y_start >= 0) {
2552 /* flush to display */
2553 pDrv->pfnUpdateRect(pDrv, 0, y_start, disp_width, y - y_start);
2554 }
2555 /* reset modified pages */
2556 if (page_max != -1 && reset_dirty) {
2557 vga_reset_dirty(pThis, page_min, page_max + PAGE_SIZE);
2558 }
2559 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2560 return VINF_SUCCESS;
2561}
2562
2563/*
2564 * blanked modes
2565 */
2566static int vga_draw_blank(PVGASTATE pThis, bool full_update, bool fFailOnResize, bool reset_dirty,
2567 PDMIDISPLAYCONNECTOR *pDrv)
2568{
2569 int i, w, val;
2570 uint8_t *d;
2571 uint32_t cbScanline = pDrv->cbScanline;
2572 uint32_t page_min, page_max;
2573
2574 if (pThis->last_width != 0)
2575 {
2576 if (fFailOnResize)
2577 {
2578 /* The caller does not want to call the pfnResize. */
2579 return VERR_TRY_AGAIN;
2580 }
2581 pThis->last_width = 0;
2582 pThis->last_height = 0;
2583 /* For blanking signal width=0, height=0, bpp=0 and cbLine=0 here.
2584 * There is no screen content, which distinguishes it from text mode. */
2585 pDrv->pfnResize(pDrv, 0, NULL, 0, 0, 0);
2586 }
2587 /* reset modified pages, i.e. everything */
2588 if (reset_dirty && pThis->last_scr_height > 0)
2589 {
2590 page_min = (pThis->start_addr * 4) & ~PAGE_OFFSET_MASK;
2591 /* round up page_max by one page, as otherwise this can be -PAGE_SIZE,
2592 * which causes assertion trouble in vga_reset_dirty. */
2593 page_max = ( pThis->start_addr * 4 + pThis->line_offset * pThis->last_scr_height
2594 - 1 + PAGE_SIZE) & ~PAGE_OFFSET_MASK;
2595 vga_reset_dirty(pThis, page_min, page_max + PAGE_SIZE);
2596 }
2597 if (pDrv->pbData == pThis->vram_ptrR3) /* Do not clear the VRAM itself. */
2598 return VINF_SUCCESS;
2599 if (!full_update)
2600 return VINF_SUCCESS;
2601 if (pThis->last_scr_width <= 0 || pThis->last_scr_height <= 0)
2602 return VINF_SUCCESS;
2603 if (pDrv->cBits == 8)
2604 val = pThis->rgb_to_pixel(0, 0, 0);
2605 else
2606 val = 0;
2607 w = pThis->last_scr_width * ((pDrv->cBits + 7) >> 3);
2608 d = pDrv->pbData;
2609 if (pThis->fRenderVRAM)
2610 {
2611 for(i = 0; i < (int)pThis->last_scr_height; i++) {
2612 memset(d, val, w);
2613 d += cbScanline;
2614 }
2615 }
2616 pDrv->pfnUpdateRect(pDrv, 0, 0, pThis->last_scr_width, pThis->last_scr_height);
2617 return VINF_SUCCESS;
2618}
2619
2620
2621#define GMODE_TEXT 0
2622#define GMODE_GRAPH 1
2623#define GMODE_BLANK 2
2624#ifdef VBOX_WITH_VMSVGA
2625#define GMODE_SVGA 3
2626#endif
2627
2628static int vga_update_display(PVGASTATE pThis, bool fUpdateAll, bool fFailOnResize, bool reset_dirty,
2629 PDMIDISPLAYCONNECTOR *pDrv, int32_t *pcur_graphic_mode)
2630{
2631 int rc = VINF_SUCCESS;
2632 int graphic_mode;
2633
2634 if (pDrv->cBits == 0) {
2635 /* nothing to do */
2636 } else {
2637 switch(pDrv->cBits) {
2638 case 8:
2639 pThis->rgb_to_pixel = rgb_to_pixel8_dup;
2640 break;
2641 case 15:
2642 pThis->rgb_to_pixel = rgb_to_pixel15_dup;
2643 break;
2644 default:
2645 case 16:
2646 pThis->rgb_to_pixel = rgb_to_pixel16_dup;
2647 break;
2648 case 32:
2649 pThis->rgb_to_pixel = rgb_to_pixel32_dup;
2650 break;
2651 }
2652
2653#ifdef VBOX_WITH_VMSVGA
2654 if (pThis->svga.fEnabled) {
2655 graphic_mode = GMODE_SVGA;
2656 }
2657 else
2658#endif
2659 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2660 graphic_mode = GMODE_BLANK;
2661 } else {
2662 graphic_mode = pThis->gr[6] & 1 ? GMODE_GRAPH : GMODE_TEXT;
2663 }
2664 bool full_update = fUpdateAll || graphic_mode != *pcur_graphic_mode;
2665 if (full_update) {
2666 *pcur_graphic_mode = graphic_mode;
2667 }
2668 switch(graphic_mode) {
2669 case GMODE_TEXT:
2670 rc = vga_draw_text(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2671 break;
2672 case GMODE_GRAPH:
2673 rc = vga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2674 break;
2675#ifdef VBOX_WITH_VMSVGA
2676 case GMODE_SVGA:
2677 rc = vmsvga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2678 break;
2679#endif
2680 case GMODE_BLANK:
2681 default:
2682 rc = vga_draw_blank(pThis, full_update, fFailOnResize, reset_dirty, pDrv);
2683 break;
2684 }
2685 }
2686 return rc;
2687}
2688
2689static void vga_save(PSSMHANDLE pSSM, PVGASTATE pThis)
2690{
2691 int i;
2692
2693 SSMR3PutU32(pSSM, pThis->latch);
2694 SSMR3PutU8(pSSM, pThis->sr_index);
2695 SSMR3PutMem(pSSM, pThis->sr, 8);
2696 SSMR3PutU8(pSSM, pThis->gr_index);
2697 SSMR3PutMem(pSSM, pThis->gr, 16);
2698 SSMR3PutU8(pSSM, pThis->ar_index);
2699 SSMR3PutMem(pSSM, pThis->ar, 21);
2700 SSMR3PutU32(pSSM, pThis->ar_flip_flop);
2701 SSMR3PutU8(pSSM, pThis->cr_index);
2702 SSMR3PutMem(pSSM, pThis->cr, 256);
2703 SSMR3PutU8(pSSM, pThis->msr);
2704 SSMR3PutU8(pSSM, pThis->fcr);
2705 SSMR3PutU8(pSSM, pThis->st00);
2706 SSMR3PutU8(pSSM, pThis->st01);
2707
2708 SSMR3PutU8(pSSM, pThis->dac_state);
2709 SSMR3PutU8(pSSM, pThis->dac_sub_index);
2710 SSMR3PutU8(pSSM, pThis->dac_read_index);
2711 SSMR3PutU8(pSSM, pThis->dac_write_index);
2712 SSMR3PutMem(pSSM, pThis->dac_cache, 3);
2713 SSMR3PutMem(pSSM, pThis->palette, 768);
2714
2715 SSMR3PutU32(pSSM, pThis->bank_offset);
2716#ifdef CONFIG_BOCHS_VBE
2717 SSMR3PutU8(pSSM, 1);
2718 SSMR3PutU16(pSSM, pThis->vbe_index);
2719 for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
2720 SSMR3PutU16(pSSM, pThis->vbe_regs[i]);
2721 SSMR3PutU32(pSSM, pThis->vbe_start_addr);
2722 SSMR3PutU32(pSSM, pThis->vbe_line_offset);
2723#else
2724 SSMR3PutU8(pSSM, 0);
2725#endif
2726}
2727
2728static int vga_load(PSSMHANDLE pSSM, PVGASTATE pThis, int version_id)
2729{
2730 int is_vbe, i;
2731 uint32_t u32Dummy;
2732 uint8_t u8;
2733
2734 SSMR3GetU32(pSSM, &pThis->latch);
2735 SSMR3GetU8(pSSM, &pThis->sr_index);
2736 SSMR3GetMem(pSSM, pThis->sr, 8);
2737 SSMR3GetU8(pSSM, &pThis->gr_index);
2738 SSMR3GetMem(pSSM, pThis->gr, 16);
2739 SSMR3GetU8(pSSM, &pThis->ar_index);
2740 SSMR3GetMem(pSSM, pThis->ar, 21);
2741 SSMR3GetU32(pSSM, (uint32_t *)&pThis->ar_flip_flop);
2742 SSMR3GetU8(pSSM, &pThis->cr_index);
2743 SSMR3GetMem(pSSM, pThis->cr, 256);
2744 SSMR3GetU8(pSSM, &pThis->msr);
2745 SSMR3GetU8(pSSM, &pThis->fcr);
2746 SSMR3GetU8(pSSM, &pThis->st00);
2747 SSMR3GetU8(pSSM, &pThis->st01);
2748
2749 SSMR3GetU8(pSSM, &pThis->dac_state);
2750 SSMR3GetU8(pSSM, &pThis->dac_sub_index);
2751 SSMR3GetU8(pSSM, &pThis->dac_read_index);
2752 SSMR3GetU8(pSSM, &pThis->dac_write_index);
2753 SSMR3GetMem(pSSM, pThis->dac_cache, 3);
2754 SSMR3GetMem(pSSM, pThis->palette, 768);
2755
2756 SSMR3GetU32(pSSM, (uint32_t *)&pThis->bank_offset);
2757 SSMR3GetU8(pSSM, &u8);
2758 is_vbe = !!u8;
2759#ifdef CONFIG_BOCHS_VBE
2760 if (!is_vbe)
2761 {
2762 Log(("vga_load: !is_vbe !!\n"));
2763 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2764 }
2765 SSMR3GetU16(pSSM, &pThis->vbe_index);
2766 for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
2767 SSMR3GetU16(pSSM, &pThis->vbe_regs[i]);
2768 if (version_id <= VGA_SAVEDSTATE_VERSION_INV_VHEIGHT)
2769 recalculate_data(pThis, false); /* <- re-calculate the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] since it might be invalid */
2770 SSMR3GetU32(pSSM, &pThis->vbe_start_addr);
2771 SSMR3GetU32(pSSM, &pThis->vbe_line_offset);
2772 if (version_id < 2)
2773 SSMR3GetU32(pSSM, &u32Dummy);
2774 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
2775#else
2776 if (is_vbe)
2777 {
2778 Log(("vga_load: is_vbe !!\n"));
2779 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2780 }
2781#endif
2782
2783 /* force refresh */
2784 pThis->graphic_mode = -1;
2785 return 0;
2786}
2787
2788/* see vgaR3Construct */
2789static void vga_init_expand(void)
2790{
2791 int i, j, v, b;
2792
2793 for(i = 0;i < 256; i++) {
2794 v = 0;
2795 for(j = 0; j < 8; j++) {
2796 v |= ((i >> j) & 1) << (j * 4);
2797 }
2798 expand4[i] = v;
2799
2800 v = 0;
2801 for(j = 0; j < 4; j++) {
2802 v |= ((i >> (2 * j)) & 3) << (j * 4);
2803 }
2804 expand2[i] = v;
2805 }
2806 for(i = 0; i < 16; i++) {
2807 v = 0;
2808 for(j = 0; j < 4; j++) {
2809 b = ((i >> j) & 1);
2810 v |= b << (2 * j);
2811 v |= b << (2 * j + 1);
2812 }
2813 expand4to8[i] = v;
2814 }
2815}
2816
2817#endif /* !IN_RING0 */
2818
2819
2820
2821/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2822
2823/**
2824 * @callback_method_impl{FNIOMIOPORTOUT,Generic VGA OUT dispatcher.}
2825 */
2826PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2827{
2828 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2829 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2830
2831 NOREF(pvUser);
2832 if (cb == 1)
2833 vga_ioport_write(pThis, Port, u32);
2834 else if (cb == 2)
2835 {
2836 vga_ioport_write(pThis, Port, u32 & 0xff);
2837 vga_ioport_write(pThis, Port + 1, u32 >> 8);
2838 }
2839 return VINF_SUCCESS;
2840}
2841
2842
2843/**
2844 * @callback_method_impl{FNIOMIOPORTOUT,Generic VGA IN dispatcher.}
2845 */
2846PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2847{
2848 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2849 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2850 NOREF(pvUser);
2851
2852 int rc = VINF_SUCCESS;
2853 if (cb == 1)
2854 *pu32 = vga_ioport_read(pThis, Port);
2855 else if (cb == 2)
2856 *pu32 = vga_ioport_read(pThis, Port)
2857 | (vga_ioport_read(pThis, Port + 1) << 8);
2858 else
2859 rc = VERR_IOM_IOPORT_UNUSED;
2860 return rc;
2861}
2862
2863
2864/**
2865 * @callback_method_impl{FNIOMIOPORTOUT,VBE Data Port OUT handler.}
2866 */
2867PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2868{
2869 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2870 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2871
2872 NOREF(pvUser);
2873
2874#ifndef IN_RING3
2875 /*
2876 * This has to be done on the host in order to execute the connector callbacks.
2877 */
2878 if ( pThis->vbe_index == VBE_DISPI_INDEX_ENABLE
2879 || pThis->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
2880 {
2881 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2882 return VINF_IOM_R3_IOPORT_WRITE;
2883 }
2884#endif
2885#ifdef VBE_BYTEWISE_IO
2886 if (cb == 1)
2887 {
2888 if (!pThis->fWriteVBEData)
2889 {
2890 if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
2891 && (u32 & VBE_DISPI_ENABLED))
2892 {
2893 pThis->fWriteVBEData = false;
2894 return vbe_ioport_write_data(pThis, Port, u32 & 0xFF);
2895 }
2896
2897 pThis->cbWriteVBEData = u32 & 0xFF;
2898 pThis->fWriteVBEData = true;
2899 return VINF_SUCCESS;
2900 }
2901
2902 u32 = (pThis->cbWriteVBEData << 8) | (u32 & 0xFF);
2903 pThis->fWriteVBEData = false;
2904 cb = 2;
2905 }
2906#endif
2907 if (cb == 2 || cb == 4)
2908 {
2909//#ifdef IN_RC
2910// /*
2911// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2912// * Since we're not mapping the entire framebuffer any longer that
2913// * has to be done on the host.
2914// */
2915// if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
2916// && (u32 & VBE_DISPI_ENABLED))
2917// {
2918// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2919// return VINF_IOM_R3_IOPORT_WRITE;
2920// }
2921//#endif
2922 return vbe_ioport_write_data(pThis, Port, u32);
2923 }
2924 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2925
2926 return VINF_SUCCESS;
2927}
2928
2929
2930/**
2931 * @callback_method_impl{FNIOMIOPORTOUT,VBE Index Port OUT handler.}
2932 */
2933PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2934{
2935 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
2936 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2937
2938#ifdef VBE_BYTEWISE_IO
2939 if (cb == 1)
2940 {
2941 if (!pThis->fWriteVBEIndex)
2942 {
2943 pThis->cbWriteVBEIndex = u32 & 0x00FF;
2944 pThis->fWriteVBEIndex = true;
2945 return VINF_SUCCESS;
2946 }
2947 pThis->fWriteVBEIndex = false;
2948 vbe_ioport_write_index(pThis, Port, (pThis->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2949 return VINF_SUCCESS;
2950 }
2951#endif
2952
2953 if (cb == 2)
2954 vbe_ioport_write_index(pThis, Port, u32);
2955 else
2956 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2957 return VINF_SUCCESS;
2958}
2959
2960
2961/**
2962 * @callback_method_impl{FNIOMIOPORTOUT,VBE Data Port IN handler.}
2963 */
2964PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2965{
2966 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
2967 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2968
2969
2970#ifdef VBE_BYTEWISE_IO
2971 if (cb == 1)
2972 {
2973 if (!pThis->fReadVBEData)
2974 {
2975 *pu32 = (vbe_ioport_read_data(pThis, Port) >> 8) & 0xFF;
2976 pThis->fReadVBEData = true;
2977 return VINF_SUCCESS;
2978 }
2979 *pu32 = vbe_ioport_read_data(pThis, Port) & 0xFF;
2980 pThis->fReadVBEData = false;
2981 return VINF_SUCCESS;
2982 }
2983#endif
2984 if (cb == 2)
2985 {
2986 *pu32 = vbe_ioport_read_data(pThis, Port);
2987 return VINF_SUCCESS;
2988 }
2989 if (cb == 4)
2990 {
2991 /* Quick hack for getting the vram size. */
2992 *pu32 = pThis->vram_size;
2993 return VINF_SUCCESS;
2994 }
2995 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2996 return VERR_IOM_IOPORT_UNUSED;
2997}
2998
2999
3000/**
3001 * @callback_method_impl{FNIOMIOPORTOUT,VBE Index Port IN handler.}
3002 */
3003PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3004{
3005 NOREF(pvUser);
3006 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3007 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3008
3009#ifdef VBE_BYTEWISE_IO
3010 if (cb == 1)
3011 {
3012 if (!pThis->fReadVBEIndex)
3013 {
3014 *pu32 = (vbe_ioport_read_index(pThis, Port) >> 8) & 0xFF;
3015 pThis->fReadVBEIndex = true;
3016 return VINF_SUCCESS;
3017 }
3018 *pu32 = vbe_ioport_read_index(pThis, Port) & 0xFF;
3019 pThis->fReadVBEIndex = false;
3020 return VINF_SUCCESS;
3021 }
3022#endif
3023 if (cb == 2)
3024 {
3025 *pu32 = vbe_ioport_read_index(pThis, Port);
3026 return VINF_SUCCESS;
3027 }
3028 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
3029 return VERR_IOM_IOPORT_UNUSED;
3030}
3031
3032#ifdef VBOX_WITH_HGSMI
3033# ifdef IN_RING3
3034/**
3035 * @callback_method_impl{FNIOMIOPORTOUT,HGSMI OUT handler.}
3036 */
3037static DECLCALLBACK(int) vgaR3IOPortHGSMIWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3038{
3039 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3040 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3041 LogFlowFunc(("Port 0x%x, u32 0x%x, cb %d\n", Port, u32, cb));
3042
3043
3044 NOREF(pvUser);
3045
3046 if (cb == 4)
3047 {
3048 switch (Port)
3049 {
3050 case VGA_PORT_HGSMI_HOST: /* Host */
3051 {
3052# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
3053 if (u32 == HGSMIOFFSET_VOID)
3054 {
3055 PDMCritSectEnter(&pThis->CritSectIRQ, VERR_SEM_BUSY);
3056
3057 if (pThis->fu32PendingGuestFlags == 0)
3058 {
3059 PDMDevHlpPCISetIrqNoWait(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
3060 HGSMIClearHostGuestFlags(pThis->pHGSMI,
3061 HGSMIHOSTFLAGS_IRQ
3062# ifdef VBOX_VDMA_WITH_WATCHDOG
3063 | HGSMIHOSTFLAGS_WATCHDOG
3064# endif
3065 | HGSMIHOSTFLAGS_VSYNC
3066 | HGSMIHOSTFLAGS_HOTPLUG
3067 | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES
3068 );
3069 }
3070 else
3071 {
3072 HGSMISetHostGuestFlags(pThis->pHGSMI, HGSMIHOSTFLAGS_IRQ | pThis->fu32PendingGuestFlags);
3073 pThis->fu32PendingGuestFlags = 0;
3074 /* Keep the IRQ unchanged. */
3075 }
3076
3077 PDMCritSectLeave(&pThis->CritSectIRQ);
3078 }
3079 else
3080# endif
3081 {
3082 HGSMIHostWrite(pThis->pHGSMI, u32);
3083 }
3084 break;
3085 }
3086
3087 case VGA_PORT_HGSMI_GUEST: /* Guest */
3088 HGSMIGuestWrite(pThis->pHGSMI, u32);
3089 break;
3090
3091 default:
3092# ifdef DEBUG_sunlover
3093 AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3094# endif
3095 break;
3096 }
3097 }
3098 else
3099 {
3100# ifdef DEBUG_sunlover
3101 AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
3102# endif
3103 }
3104
3105 return VINF_SUCCESS;
3106}
3107
3108
3109/**
3110 * @callback_method_impl{FNIOMIOPORTOUT,HGSMI IN handler.}
3111 */
3112static DECLCALLBACK(int) vgaR3IOPortHGSMIRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3113{
3114 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3115 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3116 LogFlowFunc(("Port 0x%x, cb %d\n", Port, cb));
3117
3118 NOREF(pvUser);
3119
3120 int rc = VINF_SUCCESS;
3121 if (cb == 4)
3122 {
3123 switch (Port)
3124 {
3125 case VGA_PORT_HGSMI_HOST: /* Host */
3126 *pu32 = HGSMIHostRead(pThis->pHGSMI);
3127 break;
3128 case VGA_PORT_HGSMI_GUEST: /* Guest */
3129 *pu32 = HGSMIGuestRead(pThis->pHGSMI);
3130 break;
3131 default:
3132# ifdef DEBUG_sunlover
3133 AssertMsgFailed(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
3134# endif
3135 rc = VERR_IOM_IOPORT_UNUSED;
3136 break;
3137 }
3138 }
3139 else
3140 {
3141# ifdef DEBUG_sunlover
3142 Log(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
3143# endif
3144 rc = VERR_IOM_IOPORT_UNUSED;
3145 }
3146
3147 return rc;
3148}
3149# endif /* IN_RING3 */
3150#endif /* VBOX_WITH_HGSMI */
3151
3152
3153
3154
3155/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
3156
3157/**
3158 * @internal. For use inside VGAGCMemoryFillWrite only.
3159 * Macro for apply logical operation and bit mask.
3160 */
3161#define APPLY_LOGICAL_AND_MASK(pThis, val, bit_mask) \
3162 /* apply logical operation */ \
3163 switch (pThis->gr[3] >> 3) \
3164 { \
3165 case 0: \
3166 default: \
3167 /* nothing to do */ \
3168 break; \
3169 case 1: \
3170 /* and */ \
3171 val &= pThis->latch; \
3172 break; \
3173 case 2: \
3174 /* or */ \
3175 val |= pThis->latch; \
3176 break; \
3177 case 3: \
3178 /* xor */ \
3179 val ^= pThis->latch; \
3180 break; \
3181 } \
3182 /* apply bit mask */ \
3183 val = (val & bit_mask) | (pThis->latch & ~bit_mask)
3184
3185/**
3186 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3187 * This is the advanced version of vga_mem_writeb function.
3188 *
3189 * @returns VBox status code.
3190 * @param pThis VGA device structure
3191 * @param pvUser User argument - ignored.
3192 * @param GCPhysAddr Physical address of memory to write.
3193 * @param u32Item Data to write, up to 4 bytes.
3194 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3195 * @param cItems Number of data items to write.
3196 */
3197static int vgaInternalMMIOFill(PVGASTATE pThis, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3198{
3199 uint32_t b;
3200 uint32_t write_mask, bit_mask, set_mask;
3201 uint32_t aVal[4];
3202 unsigned i;
3203 NOREF(pvUser);
3204
3205 for (i = 0; i < cbItem; i++)
3206 {
3207 aVal[i] = u32Item & 0xff;
3208 u32Item >>= 8;
3209 }
3210
3211 /* convert to VGA memory offset */
3212 /// @todo add check for the end of region
3213 GCPhysAddr &= 0x1ffff;
3214 switch((pThis->gr[6] >> 2) & 3) {
3215 case 0:
3216 break;
3217 case 1:
3218 if (GCPhysAddr >= 0x10000)
3219 return VINF_SUCCESS;
3220 GCPhysAddr += pThis->bank_offset;
3221 break;
3222 case 2:
3223 GCPhysAddr -= 0x10000;
3224 if (GCPhysAddr >= 0x8000)
3225 return VINF_SUCCESS;
3226 break;
3227 default:
3228 case 3:
3229 GCPhysAddr -= 0x18000;
3230 if (GCPhysAddr >= 0x8000)
3231 return VINF_SUCCESS;
3232 break;
3233 }
3234
3235 if (pThis->sr[4] & 0x08) {
3236 /* chain 4 mode : simplest access */
3237 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3238
3239 while (cItems-- > 0)
3240 for (i = 0; i < cbItem; i++)
3241 {
3242 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3243 {
3244 pThis->CTX_SUFF(vram_ptr)[GCPhysAddr] = aVal[i];
3245 vga_set_dirty(pThis, GCPhysAddr);
3246 }
3247 GCPhysAddr++;
3248 }
3249 } else if (pThis->gr[5] & 0x10) {
3250 /* odd/even mode (aka text mode mapping) */
3251 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3252 while (cItems-- > 0)
3253 for (i = 0; i < cbItem; i++)
3254 {
3255 unsigned plane = (pThis->gr[4] & 2) | (GCPhysAddr & 1);
3256 if (pThis->sr[2] & (1 << plane)) {
3257 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
3258 pThis->CTX_SUFF(vram_ptr)[PhysAddr2] = aVal[i];
3259 vga_set_dirty(pThis, PhysAddr2);
3260 }
3261 GCPhysAddr++;
3262 }
3263 } else {
3264 /* standard VGA latched access */
3265 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3266
3267 switch(pThis->gr[5] & 3) {
3268 default:
3269 case 0:
3270 /* rotate */
3271 b = pThis->gr[3] & 7;
3272 bit_mask = pThis->gr[8];
3273 bit_mask |= bit_mask << 8;
3274 bit_mask |= bit_mask << 16;
3275 set_mask = mask16[pThis->gr[1]];
3276
3277 for (i = 0; i < cbItem; i++)
3278 {
3279 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3280 aVal[i] |= aVal[i] << 8;
3281 aVal[i] |= aVal[i] << 16;
3282
3283 /* apply set/reset mask */
3284 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3285
3286 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3287 }
3288 break;
3289 case 1:
3290 for (i = 0; i < cbItem; i++)
3291 aVal[i] = pThis->latch;
3292 break;
3293 case 2:
3294 bit_mask = pThis->gr[8];
3295 bit_mask |= bit_mask << 8;
3296 bit_mask |= bit_mask << 16;
3297 for (i = 0; i < cbItem; i++)
3298 {
3299 aVal[i] = mask16[aVal[i] & 0x0f];
3300
3301 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3302 }
3303 break;
3304 case 3:
3305 /* rotate */
3306 b = pThis->gr[3] & 7;
3307
3308 for (i = 0; i < cbItem; i++)
3309 {
3310 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3311 bit_mask = pThis->gr[8] & aVal[i];
3312 bit_mask |= bit_mask << 8;
3313 bit_mask |= bit_mask << 16;
3314 aVal[i] = mask16[pThis->gr[0]];
3315
3316 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3317 }
3318 break;
3319 }
3320
3321 /* mask data according to sr[2] */
3322 write_mask = mask16[pThis->sr[2]];
3323
3324 /* actually write data */
3325 if (cbItem == 1)
3326 {
3327 /* The most frequently case is 1 byte I/O. */
3328 while (cItems-- > 0)
3329 {
3330 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3331 vga_set_dirty(pThis, GCPhysAddr << 2);
3332 GCPhysAddr++;
3333 }
3334 }
3335 else if (cbItem == 2)
3336 {
3337 /* The second case is 2 bytes I/O. */
3338 while (cItems-- > 0)
3339 {
3340 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3341 vga_set_dirty(pThis, GCPhysAddr << 2);
3342 GCPhysAddr++;
3343
3344 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3345 vga_set_dirty(pThis, GCPhysAddr << 2);
3346 GCPhysAddr++;
3347 }
3348 }
3349 else
3350 {
3351 /* And the rest is 4 bytes. */
3352 Assert(cbItem == 4);
3353 while (cItems-- > 0)
3354 for (i = 0; i < cbItem; i++)
3355 {
3356 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3357 vga_set_dirty(pThis, GCPhysAddr << 2);
3358 GCPhysAddr++;
3359 }
3360 }
3361 }
3362 return VINF_SUCCESS;
3363}
3364
3365
3366/**
3367 * @callback_method_impl{FNIOMMMIOFILL,
3368 * Legacy VGA memory (0xa0000 - 0xbffff) write hook\, to be called from IOM and
3369 * from the inside of VGADeviceGC.cpp. This is the advanced version of
3370 * vga_mem_writeb function.}
3371 */
3372PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3373{
3374 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3375 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3376
3377 return vgaInternalMMIOFill(pThis, pvUser, GCPhysAddr, u32Item, cbItem, cItems);
3378}
3379#undef APPLY_LOGICAL_AND_MASK
3380
3381
3382/**
3383 * @callback_method_impl{FNIOMMMIOREAD, Legacy VGA memory (0xa0000 - 0xbffff)
3384 * read hook\, to be called from IOM.}
3385 */
3386PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3387{
3388 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3389 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3390 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3391 NOREF(pvUser);
3392
3393 int rc = VINF_SUCCESS;
3394 switch (cb)
3395 {
3396 case 1:
3397 *(uint8_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc);
3398 break;
3399 case 2:
3400 *(uint16_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3401 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8);
3402 break;
3403 case 4:
3404 *(uint32_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3405 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3406 | (vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3407 | (vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24);
3408 break;
3409
3410 case 8:
3411 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pThis, GCPhysAddr, &rc)
3412 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3413 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3414 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24)
3415 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 4, &rc) << 32)
3416 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 5, &rc) << 40)
3417 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 6, &rc) << 48)
3418 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 7, &rc) << 56);
3419 break;
3420
3421 default:
3422 {
3423 uint8_t *pbData = (uint8_t *)pv;
3424 while (cb-- > 0)
3425 {
3426 *pbData++ = vga_mem_readb(pThis, GCPhysAddr++, &rc);
3427 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3428 break;
3429 }
3430 }
3431 }
3432
3433 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3434 return rc;
3435}
3436
3437/**
3438 * @callback_method_impl{FNIOMMMIOWRITE, Legacy VGA memory (0xa0000 - 0xbffff)
3439 * write hook\, to be called from IOM.}
3440 */
3441PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
3442{
3443 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3444 uint8_t const *pbSrc = (uint8_t const *)pv;
3445 NOREF(pvUser);
3446 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3447 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3448
3449 int rc;
3450 switch (cb)
3451 {
3452 case 1:
3453 rc = vga_mem_writeb(pThis, GCPhysAddr, *pbSrc);
3454 break;
3455#if 1
3456 case 2:
3457 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pbSrc[0]);
3458 if (RT_LIKELY(rc == VINF_SUCCESS))
3459 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pbSrc[1]);
3460 break;
3461 case 4:
3462 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pbSrc[0]);
3463 if (RT_LIKELY(rc == VINF_SUCCESS))
3464 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pbSrc[1]);
3465 if (RT_LIKELY(rc == VINF_SUCCESS))
3466 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pbSrc[2]);
3467 if (RT_LIKELY(rc == VINF_SUCCESS))
3468 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pbSrc[3]);
3469 break;
3470 case 8:
3471 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pbSrc[0]);
3472 if (RT_LIKELY(rc == VINF_SUCCESS))
3473 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pbSrc[1]);
3474 if (RT_LIKELY(rc == VINF_SUCCESS))
3475 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pbSrc[2]);
3476 if (RT_LIKELY(rc == VINF_SUCCESS))
3477 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pbSrc[3]);
3478 if (RT_LIKELY(rc == VINF_SUCCESS))
3479 rc = vga_mem_writeb(pThis, GCPhysAddr + 4, pbSrc[4]);
3480 if (RT_LIKELY(rc == VINF_SUCCESS))
3481 rc = vga_mem_writeb(pThis, GCPhysAddr + 5, pbSrc[5]);
3482 if (RT_LIKELY(rc == VINF_SUCCESS))
3483 rc = vga_mem_writeb(pThis, GCPhysAddr + 6, pbSrc[6]);
3484 if (RT_LIKELY(rc == VINF_SUCCESS))
3485 rc = vga_mem_writeb(pThis, GCPhysAddr + 7, pbSrc[7]);
3486 break;
3487#else
3488 case 2:
3489 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3490 break;
3491 case 4:
3492 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3493 break;
3494 case 8:
3495 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint64_t *)pv, 8, 1);
3496 break;
3497#endif
3498 default:
3499 rc = VINF_SUCCESS;
3500 while (cb-- > 0 && rc == VINF_SUCCESS)
3501 rc = vga_mem_writeb(pThis, GCPhysAddr++, *pbSrc++);
3502 break;
3503
3504 }
3505 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3506 return rc;
3507}
3508
3509
3510/**
3511 * Handle LFB access.
3512 * @returns VBox status code.
3513 * @param pVM VM handle.
3514 * @param pThis VGA device instance data.
3515 * @param GCPhys The access physical address.
3516 * @param GCPtr The access virtual address (only GC).
3517 */
3518static int vgaLFBAccess(PVM pVM, PVGASTATE pThis, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3519{
3520 int rc = PDMCritSectEnter(&pThis->CritSect, VINF_EM_RAW_EMULATE_INSTR);
3521 if (rc != VINF_SUCCESS)
3522 return rc;
3523
3524 /*
3525 * Set page dirty bit.
3526 */
3527 vga_set_dirty(pThis, GCPhys - pThis->GCPhysVRAM);
3528 pThis->fLFBUpdated = true;
3529
3530 /*
3531 * Turn of the write handler for this particular page and make it R/W.
3532 * Then return telling the caller to restart the guest instruction.
3533 * ASSUME: the guest always maps video memory RW.
3534 */
3535 rc = PGMHandlerPhysicalPageTempOff(pVM, pThis->GCPhysVRAM, GCPhys);
3536 if (RT_SUCCESS(rc))
3537 {
3538#ifndef IN_RING3
3539 rc = PGMShwMakePageWritable(PDMDevHlpGetVMCPU(pThis->CTX_SUFF(pDevIns)), GCPtr,
3540 PGM_MK_PG_IS_MMIO2 | PGM_MK_PG_IS_WRITE_FAULT);
3541 PDMCritSectLeave(&pThis->CritSect);
3542 AssertMsgReturn( rc == VINF_SUCCESS
3543 /* In the SMP case the page table might be removed while we wait for the PGM lock in the trap handler. */
3544 || rc == VERR_PAGE_TABLE_NOT_PRESENT
3545 || rc == VERR_PAGE_NOT_PRESENT,
3546 ("PGMShwModifyPage -> GCPtr=%RGv rc=%d\n", GCPtr, rc),
3547 rc);
3548#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3549 PDMCritSectLeave(&pThis->CritSect);
3550 Assert(GCPtr == 0);
3551 RT_NOREF1(GCPtr);
3552#endif
3553 return VINF_SUCCESS;
3554 }
3555
3556 PDMCritSectLeave(&pThis->CritSect);
3557 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3558 return rc;
3559}
3560
3561
3562#ifndef IN_RING3
3563/**
3564 * @callback_method_impl{FNPGMRCPHYSHANDLER, \#PF Handler for VBE LFB access.}
3565 */
3566PDMBOTHCBDECL(VBOXSTRICTRC) vgaLbfAccessPfHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame,
3567 RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3568{
3569 PVGASTATE pThis = (PVGASTATE)pvUser;
3570 AssertPtr(pThis);
3571 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3572 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3573 RT_NOREF3(pVCpu, pRegFrame, uErrorCode);
3574
3575 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3576}
3577#endif /* !IN_RING3 */
3578
3579
3580/**
3581 * @callback_method_impl{FNPGMPHYSHANDLER,
3582 * VBE LFB write access handler for the dirty tracking.}
3583 */
3584PGM_ALL_CB_DECL(VBOXSTRICTRC) vgaLFBAccessHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf,
3585 PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
3586{
3587 PVGASTATE pThis = (PVGASTATE)pvUser;
3588 int rc;
3589 Assert(pThis);
3590 Assert(GCPhys >= pThis->GCPhysVRAM);
3591 NOREF(pVCpu); NOREF(pvPhys); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmAccessType); NOREF(enmOrigin);
3592
3593 rc = vgaLFBAccess(pVM, pThis, GCPhys, 0);
3594 if (RT_SUCCESS(rc))
3595 return VINF_PGM_HANDLER_DO_DEFAULT;
3596 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
3597 return rc;
3598}
3599
3600
3601/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3602
3603/**
3604 * @callback_method_impl{FNIOMIOPORTIN,
3605 * Port I/O Handler for VGA BIOS IN operations.}
3606 */
3607PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3608{
3609 NOREF(pDevIns);
3610 NOREF(pvUser);
3611 NOREF(Port);
3612 NOREF(pu32);
3613 NOREF(cb);
3614 return VERR_IOM_IOPORT_UNUSED;
3615}
3616
3617/**
3618 * @callback_method_impl{FNIOMIOPORTOUT,
3619 * Port I/O Handler for VGA BIOS IN operations.}
3620 */
3621PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3622{
3623 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3624 RT_NOREF2(pDevIns, pvUser);
3625 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3626
3627 /*
3628 * VGA BIOS char printing.
3629 */
3630 if ( cb == 1
3631 && Port == VBE_PRINTF_PORT)
3632 {
3633#if 0
3634 switch (u32)
3635 {
3636 case '\r': Log(("vgabios: <return>\n")); break;
3637 case '\n': Log(("vgabios: <newline>\n")); break;
3638 case '\t': Log(("vgabios: <tab>\n")); break;
3639 default:
3640 Log(("vgabios: %c\n", u32));
3641 }
3642#else
3643 if (lastWasNotNewline == 0)
3644 Log(("vgabios: "));
3645 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3646 Log(("%c", u32));
3647 if (u32 == '\n')
3648 lastWasNotNewline = 0;
3649 else
3650 lastWasNotNewline = 1;
3651#endif
3652 return VINF_SUCCESS;
3653 }
3654
3655 /* not in use. */
3656 return VERR_IOM_IOPORT_UNUSED;
3657}
3658
3659
3660/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3661
3662#ifdef IN_RING3
3663
3664# ifdef VBE_NEW_DYN_LIST
3665/**
3666 * @callback_method_impl{FNIOMIOPORTOUT,
3667 * Port I/O Handler for VBE Extra OUT operations.}
3668 */
3669PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3670{
3671 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3672 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3673 NOREF(pvUser); NOREF(Port);
3674
3675 if (cb == 2)
3676 {
3677 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3678 pThis->u16VBEExtraAddress = u32;
3679 }
3680 else
3681 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3682
3683 return VINF_SUCCESS;
3684}
3685
3686
3687/**
3688 * @callback_method_impl{FNIOMIOPORTIN,
3689 * Port I/O Handler for VBE Extra IN operations.}
3690 */
3691PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3692{
3693 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3694 NOREF(pvUser); NOREF(Port);
3695 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3696
3697 int rc = VINF_SUCCESS;
3698 if (pThis->u16VBEExtraAddress == 0xffff)
3699 {
3700 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3701 *pu32 = pThis->vram_size / _64K;
3702 }
3703 else if ( pThis->u16VBEExtraAddress >= pThis->cbVBEExtraData
3704 || pThis->u16VBEExtraAddress + cb > pThis->cbVBEExtraData)
3705 {
3706 *pu32 = 0;
3707 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3708 pThis->u16VBEExtraAddress, pThis->u16VBEExtraAddress, pThis->cbVBEExtraData, pThis->cbVBEExtraData));
3709 }
3710 else if (cb == 1)
3711 {
3712 *pu32 = pThis->pbVBEExtraData[pThis->u16VBEExtraAddress] & 0xFF;
3713
3714 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3715 }
3716 else if (cb == 2)
3717 {
3718 *pu32 = pThis->pbVBEExtraData[pThis->u16VBEExtraAddress]
3719 | (uint32_t)pThis->pbVBEExtraData[pThis->u16VBEExtraAddress + 1] << 8;
3720
3721 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3722 }
3723 else
3724 {
3725 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3726 rc = VERR_IOM_IOPORT_UNUSED;
3727 }
3728
3729 return rc;
3730}
3731# endif /* VBE_NEW_DYN_LIST */
3732
3733
3734/**
3735 * Parse the logo bitmap data at init time.
3736 *
3737 * @returns VBox status code.
3738 *
3739 * @param pThis The VGA instance data.
3740 */
3741static int vbeParseBitmap(PVGASTATE pThis)
3742{
3743 uint16_t i;
3744 PBMPINFO bmpInfo;
3745 POS2HDR pOs2Hdr;
3746 POS22HDR pOs22Hdr;
3747 PWINHDR pWinHdr;
3748
3749 /*
3750 * Get bitmap header data
3751 */
3752 bmpInfo = (PBMPINFO)(pThis->pbLogo + sizeof(LOGOHDR));
3753 pWinHdr = (PWINHDR)(pThis->pbLogo + sizeof(LOGOHDR) + sizeof(BMPINFO));
3754
3755 if (bmpInfo->Type == BMP_ID)
3756 {
3757 switch (pWinHdr->Size)
3758 {
3759 case BMP_HEADER_OS21:
3760 pOs2Hdr = (POS2HDR)pWinHdr;
3761 pThis->cxLogo = pOs2Hdr->Width;
3762 pThis->cyLogo = pOs2Hdr->Height;
3763 pThis->cLogoPlanes = pOs2Hdr->Planes;
3764 pThis->cLogoBits = pOs2Hdr->BitCount;
3765 pThis->LogoCompression = BMP_COMPRESS_NONE;
3766 pThis->cLogoUsedColors = 0;
3767 break;
3768
3769 case BMP_HEADER_OS22:
3770 pOs22Hdr = (POS22HDR)pWinHdr;
3771 pThis->cxLogo = pOs22Hdr->Width;
3772 pThis->cyLogo = pOs22Hdr->Height;
3773 pThis->cLogoPlanes = pOs22Hdr->Planes;
3774 pThis->cLogoBits = pOs22Hdr->BitCount;
3775 pThis->LogoCompression = pOs22Hdr->Compression;
3776 pThis->cLogoUsedColors = pOs22Hdr->ClrUsed;
3777 break;
3778
3779 case BMP_HEADER_WIN3:
3780 pThis->cxLogo = pWinHdr->Width;
3781 pThis->cyLogo = pWinHdr->Height;
3782 pThis->cLogoPlanes = pWinHdr->Planes;
3783 pThis->cLogoBits = pWinHdr->BitCount;
3784 pThis->LogoCompression = pWinHdr->Compression;
3785 pThis->cLogoUsedColors = pWinHdr->ClrUsed;
3786 break;
3787
3788 default:
3789 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pWinHdr->Size),
3790 VERR_INVALID_PARAMETER);
3791 break;
3792 }
3793
3794 AssertLogRelMsgReturn(pThis->cxLogo <= LOGO_MAX_WIDTH && pThis->cyLogo <= LOGO_MAX_HEIGHT,
3795 ("Bitmap %ux%u is too big.\n", pThis->cxLogo, pThis->cyLogo),
3796 VERR_INVALID_PARAMETER);
3797
3798 AssertLogRelMsgReturn(pThis->cLogoPlanes == 1,
3799 ("Bitmap planes %u != 1.\n", pThis->cLogoPlanes),
3800 VERR_INVALID_PARAMETER);
3801
3802 AssertLogRelMsgReturn(pThis->cLogoBits == 4 || pThis->cLogoBits == 8 || pThis->cLogoBits == 24,
3803 ("Unsupported %u depth.\n", pThis->cLogoBits),
3804 VERR_INVALID_PARAMETER);
3805
3806 AssertLogRelMsgReturn(pThis->cLogoUsedColors <= 256,
3807 ("Unsupported %u colors.\n", pThis->cLogoUsedColors),
3808 VERR_INVALID_PARAMETER);
3809
3810 AssertLogRelMsgReturn(pThis->LogoCompression == BMP_COMPRESS_NONE,
3811 ("Unsupported %u compression.\n", pThis->LogoCompression),
3812 VERR_INVALID_PARAMETER);
3813
3814 /*
3815 * Read bitmap palette
3816 */
3817 if (!pThis->cLogoUsedColors)
3818 pThis->cLogoPalEntries = 1 << (pThis->cLogoPlanes * pThis->cLogoBits);
3819 else
3820 pThis->cLogoPalEntries = pThis->cLogoUsedColors;
3821
3822 if (pThis->cLogoPalEntries)
3823 {
3824 const uint8_t *pbPal = pThis->pbLogo + sizeof(LOGOHDR) + sizeof(BMPINFO) + pWinHdr->Size; /* ASSUMES Size location (safe) */
3825
3826 for (i = 0; i < pThis->cLogoPalEntries; i++)
3827 {
3828 uint16_t j;
3829 uint32_t u32Pal = 0;
3830
3831 for (j = 0; j < 3; j++)
3832 {
3833 uint8_t b = *pbPal++;
3834 u32Pal <<= 8;
3835 u32Pal |= b;
3836 }
3837
3838 pbPal++; /* skip unused byte */
3839 pThis->au32LogoPalette[i] = u32Pal;
3840 }
3841 }
3842
3843 /*
3844 * Bitmap data offset
3845 */
3846 pThis->pbLogoBitmap = pThis->pbLogo + sizeof(LOGOHDR) + bmpInfo->Offset;
3847 }
3848 else
3849 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
3850
3851 return VINF_SUCCESS;
3852}
3853
3854
3855/**
3856 * Show logo bitmap data.
3857 *
3858 * @returns VBox status code.
3859 *
3860 * @param cBits Logo depth.
3861 * @param xLogo Logo X position.
3862 * @param yLogo Logo Y position.
3863 * @param cxLogo Logo width.
3864 * @param cyLogo Logo height.
3865 * @param fInverse True if the bitmask is black on white (only for 1bpp)
3866 * @param iStep Fade in/fade out step.
3867 * @param pu32Palette Palette data.
3868 * @param pbSrc Source buffer.
3869 * @param pbDst Destination buffer.
3870 */
3871static void vbeShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo,
3872 bool fInverse, uint8_t iStep,
3873 const uint32_t *pu32Palette, const uint8_t *pbSrc, uint8_t *pbDst)
3874{
3875 uint16_t i;
3876 size_t cbPadBytes = 0;
3877 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
3878 uint16_t cyLeft = cyLogo;
3879
3880 pbDst += xLogo * 4 + yLogo * cbLineDst;
3881
3882 switch (cBits)
3883 {
3884 case 1:
3885 pbDst += cyLogo * cbLineDst;
3886 cbPadBytes = 0;
3887 break;
3888
3889 case 4:
3890 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
3891 cbPadBytes = 0;
3892 else if ((cxLogo % 8) <= 2)
3893 cbPadBytes = 3;
3894 else if ((cxLogo % 8) <= 4)
3895 cbPadBytes = 2;
3896 else
3897 cbPadBytes = 1;
3898 break;
3899
3900 case 8:
3901 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
3902 break;
3903
3904 case 24:
3905 cbPadBytes = cxLogo % 4;
3906 break;
3907 }
3908
3909 uint8_t j = 0, c = 0;
3910
3911 while (cyLeft-- > 0)
3912 {
3913 uint8_t *pbTmpDst = pbDst;
3914
3915 if (cBits != 1)
3916 j = 0;
3917
3918 for (i = 0; i < cxLogo; i++)
3919 {
3920 switch (cBits)
3921 {
3922 case 1:
3923 {
3924 if (!j)
3925 c = *pbSrc++;
3926
3927 if (c & 1)
3928 {
3929 if (fInverse)
3930 {
3931 *pbTmpDst++ = 0;
3932 *pbTmpDst++ = 0;
3933 *pbTmpDst++ = 0;
3934 pbTmpDst++;
3935 }
3936 else
3937 {
3938 uint8_t pix = 0xFF * iStep / LOGO_SHOW_STEPS;
3939 *pbTmpDst++ = pix;
3940 *pbTmpDst++ = pix;
3941 *pbTmpDst++ = pix;
3942 pbTmpDst++;
3943 }
3944 }
3945 else
3946 pbTmpDst += 4;
3947 c >>= 1;
3948 j = (j + 1) % 8;
3949 break;
3950 }
3951
3952 case 4:
3953 {
3954 if (!j)
3955 c = *pbSrc++;
3956
3957 uint8_t pix = (c >> 4) & 0xF;
3958 c <<= 4;
3959
3960 uint32_t u32Pal = pu32Palette[pix];
3961
3962 pix = (u32Pal >> 16) & 0xFF;
3963 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
3964 pix = (u32Pal >> 8) & 0xFF;
3965 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
3966 pix = u32Pal & 0xFF;
3967 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
3968 pbTmpDst++;
3969
3970 j = (j + 1) % 2;
3971 break;
3972 }
3973
3974 case 8:
3975 {
3976 uint32_t u32Pal = pu32Palette[*pbSrc++];
3977
3978 uint8_t pix = (u32Pal >> 16) & 0xFF;
3979 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
3980 pix = (u32Pal >> 8) & 0xFF;
3981 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
3982 pix = u32Pal & 0xFF;
3983 *pbTmpDst++ = pix * iStep / LOGO_SHOW_STEPS;
3984 pbTmpDst++;
3985 break;
3986 }
3987
3988 case 24:
3989 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
3990 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
3991 *pbTmpDst++ = *pbSrc++ * iStep / LOGO_SHOW_STEPS;
3992 pbTmpDst++;
3993 break;
3994 }
3995 }
3996
3997 pbDst -= cbLineDst;
3998 pbSrc += cbPadBytes;
3999 }
4000}
4001
4002
4003/**
4004 * @callback_method_impl{FNIOMIOPORTOUT,
4005 * Port I/O Handler for BIOS Logo OUT operations.}
4006 */
4007PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
4008{
4009 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4010 NOREF(pvUser);
4011 NOREF(Port);
4012
4013 Log(("vbeIOPortWriteCMDLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
4014
4015 if (cb == 2)
4016 {
4017 /* Get the logo command */
4018 switch (u32 & 0xFF00)
4019 {
4020 case LOGO_CMD_SET_OFFSET:
4021 pThis->offLogoData = u32 & 0xFF;
4022 break;
4023
4024 case LOGO_CMD_SHOW_BMP:
4025 {
4026 uint8_t iStep = u32 & 0xFF;
4027 const uint8_t *pbSrc = pThis->pbLogoBitmap;
4028 uint8_t *pbDst;
4029 PCLOGOHDR pLogoHdr = (PCLOGOHDR)pThis->pbLogo;
4030 uint32_t offDirty = 0;
4031 uint16_t xLogo = (LOGO_MAX_WIDTH - pThis->cxLogo) / 2;
4032 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThis->cyLogo) / 2;
4033
4034 /* Check VRAM size */
4035 if (pThis->vram_size < LOGO_MAX_SIZE)
4036 break;
4037
4038 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4039 pbDst = pThis->vram_ptrR3 + LOGO_MAX_SIZE;
4040 else
4041 pbDst = pThis->vram_ptrR3;
4042
4043 /* Clear screen - except on power on... */
4044 if (!pThis->fLogoClearScreen)
4045 {
4046 /* Clear vram */
4047 uint32_t *pu32Dst = (uint32_t *)pbDst;
4048 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4049 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4050 *pu32Dst++ = 0;
4051 pThis->fLogoClearScreen = true;
4052 }
4053
4054 /* Show the bitmap. */
4055 vbeShowBitmap(pThis->cLogoBits, xLogo, yLogo,
4056 pThis->cxLogo, pThis->cyLogo,
4057 false, iStep, &pThis->au32LogoPalette[0],
4058 pbSrc, pbDst);
4059
4060 /* Show the 'Press F12...' text. */
4061 if (pLogoHdr->fu8ShowBootMenu == 2)
4062 vbeShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
4063 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
4064 pThis->fBootMenuInverse, iStep, &pThis->au32LogoPalette[0],
4065 &g_abLogoF12BootText[0], pbDst);
4066
4067 /* Blit the offscreen buffer. */
4068 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
4069 {
4070 uint32_t *pu32TmpDst = (uint32_t *)pThis->vram_ptrR3;
4071 uint32_t *pu32TmpSrc = (uint32_t *)(pThis->vram_ptrR3 + LOGO_MAX_SIZE);
4072 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
4073 {
4074 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
4075 *pu32TmpDst++ = *pu32TmpSrc++;
4076 }
4077 }
4078
4079 /* Set the dirty flags. */
4080 while (offDirty <= LOGO_MAX_SIZE)
4081 {
4082 vga_set_dirty(pThis, offDirty);
4083 offDirty += PAGE_SIZE;
4084 }
4085 break;
4086 }
4087
4088 default:
4089 Log(("vbeIOPortWriteCMDLogo: invalid command %d\n", u32));
4090 pThis->LogoCommand = LOGO_CMD_NOP;
4091 break;
4092 }
4093
4094 return VINF_SUCCESS;
4095 }
4096
4097 Log(("vbeIOPortWriteCMDLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
4098 return VINF_SUCCESS;
4099}
4100
4101
4102/**
4103 * @callback_method_impl{FNIOMIOPORTIN,
4104 * Port I/O Handler for BIOS Logo IN operations.}
4105 */
4106PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
4107{
4108 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4109 NOREF(pvUser);
4110 NOREF(Port);
4111
4112
4113 if (pThis->offLogoData + cb > pThis->cbLogo)
4114 {
4115 Log(("vbeIOPortReadCMDLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4116 pThis->offLogoData, pThis->offLogoData, pThis->cbLogo, pThis->cbLogo));
4117 return VINF_SUCCESS;
4118 }
4119
4120 PCRTUINT64U p = (PCRTUINT64U)&pThis->pbLogo[pThis->offLogoData];
4121 switch (cb)
4122 {
4123 case 1: *pu32 = p->au8[0]; break;
4124 case 2: *pu32 = p->au16[0]; break;
4125 case 4: *pu32 = p->au32[0]; break;
4126 //case 8: *pu32 = p->au64[0]; break;
4127 default: AssertFailed(); break;
4128 }
4129 Log(("vbeIOPortReadCMDLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThis->offLogoData, pThis->offLogoData, cb, cb, pu32));
4130
4131 pThis->LogoCommand = LOGO_CMD_NOP;
4132 pThis->offLogoData += cb;
4133
4134 return VINF_SUCCESS;
4135}
4136
4137
4138/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4139
4140/**
4141 * @callback_method_impl{FNDBGFHANDLERDEV,
4142 * Dumps several interesting bits of the VGA state that are difficult to
4143 * decode from the registers.}
4144 */
4145static DECLCALLBACK(void) vgaInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4146{
4147 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4148 int is_graph, double_scan;
4149 int w, h, char_height, char_dots;
4150 int val, vfreq_hz, hfreq_hz;
4151 vga_retrace_s *r = &pThis->retrace_state;
4152 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4153 NOREF(pszArgs);
4154
4155 is_graph = pThis->gr[6] & 1;
4156 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4157 double_scan = pThis->cr[9] >> 7;
4158 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4159 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4160 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4161 val = pThis->cr[0] + 5;
4162 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4163 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4164 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4165 val = pThis->cr[1] + 1;
4166 w = val * char_dots;
4167 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4168 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4169 h = val;
4170 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4171 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4172 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4173 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4174 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4175 if (!is_graph)
4176 {
4177 val = (pThis->cr[9] & 0x1f) + 1;
4178 char_height = val;
4179 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4180 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4181
4182 uint32_t cbLine;
4183 uint32_t offStart;
4184 uint32_t uLineCompareIgn;
4185 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4186 if (!cbLine)
4187 cbLine = 80 * 8;
4188 offStart *= 8;
4189 pHlp->pfnPrintf(pHlp, "cbLine: %#x\n", cbLine);
4190 pHlp->pfnPrintf(pHlp, "offStart: %#x (line %#x)\n", offStart, offStart / cbLine);
4191 }
4192 if (pThis->fRealRetrace)
4193 {
4194 val = r->hb_start;
4195 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4196 val = r->hb_end;
4197 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4198 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4199 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4200 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4201 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4202 if (r->frame_ns && r->h_total_ns) /* Careful in case state is temporarily invalid. */
4203 {
4204 vfreq_hz = 1000000000 / r->frame_ns;
4205 hfreq_hz = 1000000000 / r->h_total_ns;
4206 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4207 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4208 }
4209 }
4210 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4211
4212#ifdef VBOX_WITH_VMSVGA
4213 if (pThis->svga.fEnabled)
4214 pHlp->pfnPrintf(pHlp, pThis->svga.f3DEnabled ? "VMSVGA 3D enabled: %ux%ux%u\n" : "VMSVGA enabled: %ux%ux%u",
4215 pThis->svga.uWidth, pThis->svga.uHeight, pThis->svga.uBpp);
4216#endif
4217}
4218
4219
4220/**
4221 * Prints a separator line.
4222 *
4223 * @param pHlp Callback functions for doing output.
4224 * @param cCols The number of columns.
4225 * @param pszTitle The title text, NULL if none.
4226 */
4227static void vgaInfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4228{
4229 if (pszTitle)
4230 {
4231 size_t cchTitle = strlen(pszTitle);
4232 if (cchTitle + 6 >= cCols)
4233 {
4234 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4235 cCols = 0;
4236 }
4237 else
4238 {
4239 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4240 cCols -= cchLeft + cchTitle + 2;
4241 while (cchLeft-- > 0)
4242 pHlp->pfnPrintf(pHlp, "-");
4243 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4244 }
4245 }
4246
4247 while (cCols-- > 0)
4248 pHlp->pfnPrintf(pHlp, "-");
4249 pHlp->pfnPrintf(pHlp, "\n");
4250}
4251
4252
4253/**
4254 * Worker for vgaInfoText.
4255 *
4256 * @param pThis The vga state.
4257 * @param pHlp Callback functions for doing output.
4258 * @param offStart Where to start dumping (relative to the VRAM).
4259 * @param cbLine The source line length (aka line_offset).
4260 * @param cCols The number of columns on the screen.
4261 * @param cRows The number of rows to dump.
4262 * @param iScrBegin The row at which the current screen output starts.
4263 * @param iScrEnd The row at which the current screen output end
4264 * (exclusive).
4265 */
4266static void vgaInfoTextWorker(PVGASTATE pThis, PCDBGFINFOHLP pHlp,
4267 uint32_t offStart, uint32_t cbLine,
4268 uint32_t cCols, uint32_t cRows,
4269 uint32_t iScrBegin, uint32_t iScrEnd)
4270{
4271 /* Title, */
4272 char szTitle[32];
4273 if (iScrBegin || iScrEnd < cRows)
4274 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4275 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4276 else
4277 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4278
4279 /* Do the dumping. */
4280 uint8_t const *pbSrcOuter = pThis->CTX_SUFF(vram_ptr) + offStart;
4281 uint32_t iRow;
4282 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4283 {
4284 if ((uintptr_t)(pbSrcOuter + cbLine - pThis->CTX_SUFF(vram_ptr)) > pThis->vram_size) {
4285 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4286 break;
4287 }
4288
4289 if (iRow == 0)
4290 vgaInfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4291 else if (iRow == iScrBegin)
4292 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4293 else if (iRow == iScrEnd)
4294 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4295
4296 uint8_t const *pbSrc = pbSrcOuter;
4297 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4298 {
4299 if (RT_C_IS_PRINT(*pbSrc))
4300 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4301 else
4302 pHlp->pfnPrintf(pHlp, ".");
4303 pbSrc += 8; /* chars are spaced 8 bytes apart */
4304 }
4305 pHlp->pfnPrintf(pHlp, "\n");
4306 }
4307
4308 /* Final separator. */
4309 vgaInfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4310}
4311
4312
4313/**
4314 * @callback_method_impl{FNDBGFHANDLERDEV,
4315 * Dumps VGA memory formatted as ASCII text\, no attributes. Only looks at
4316 * the first page.}
4317 */
4318static DECLCALLBACK(void) vgaInfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4319{
4320 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4321
4322 /*
4323 * Parse args.
4324 */
4325 bool fAll = true;
4326 if (pszArgs && *pszArgs)
4327 {
4328 if (!strcmp(pszArgs, "all"))
4329 fAll = true;
4330 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4331 fAll = false;
4332 else
4333 {
4334 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4335 return;
4336 }
4337 }
4338
4339 /*
4340 * Check that we're in text mode and that the VRAM is accessible.
4341 */
4342 if (!(pThis->gr[6] & 1))
4343 {
4344 uint8_t *pbSrc = pThis->vram_ptrR3;
4345 if (pbSrc)
4346 {
4347 /*
4348 * Figure out the display size and where the text is.
4349 *
4350 * Note! We're cutting quite a few corners here and this code could
4351 * do with some brushing up. Dumping from the start of the
4352 * frame buffer is done intentionally so that we're more
4353 * likely to obtain the full scrollback of a linux panic.
4354 * 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";
4355 */
4356 uint32_t cbLine;
4357 uint32_t offStart;
4358 uint32_t uLineCompareIgn;
4359 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4360 if (!cbLine)
4361 cbLine = 80 * 8;
4362 offStart *= 8;
4363
4364 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4365 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4366 uint32_t uDblScan = pThis->cr[9] >> 7;
4367 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4368 if (cScrRows < 25)
4369 cScrRows = 25;
4370 uint32_t iScrBegin = offStart / cbLine;
4371 uint32_t cRows = iScrBegin + cScrRows;
4372 uint32_t cCols = cbLine / 8;
4373
4374 if (fAll) {
4375 vgaInfoTextWorker(pThis, pHlp, offStart - iScrBegin * cbLine, cbLine,
4376 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4377 } else {
4378 vgaInfoTextWorker(pThis, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4379 }
4380 }
4381 else
4382 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4383 }
4384 else
4385 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4386}
4387
4388
4389/**
4390 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4391 */
4392static DECLCALLBACK(void) vgaInfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4393{
4394 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4395 unsigned i;
4396 NOREF(pszArgs);
4397
4398 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4399 Assert(sizeof(pThis->sr) >= 8);
4400 for (i = 0; i < 8; ++i)
4401 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4402 pHlp->pfnPrintf(pHlp, "\n");
4403}
4404
4405
4406/**
4407 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4408 */
4409static DECLCALLBACK(void) vgaInfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4410{
4411 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4412 unsigned i;
4413 NOREF(pszArgs);
4414
4415 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4416 Assert(sizeof(pThis->cr) >= 24);
4417 for (i = 0; i < 10; ++i)
4418 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4419 pHlp->pfnPrintf(pHlp, "\n");
4420 for (i = 10; i < 20; ++i)
4421 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4422 pHlp->pfnPrintf(pHlp, "\n");
4423 for (i = 20; i < 25; ++i)
4424 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4425 pHlp->pfnPrintf(pHlp, "\n");
4426}
4427
4428
4429/**
4430 * @callback_method_impl{FNDBGFHANDLERDEV,
4431 * Dumps VGA Graphics Controller registers.}
4432 */
4433static DECLCALLBACK(void) vgaInfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4434{
4435 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4436 unsigned i;
4437 NOREF(pszArgs);
4438
4439 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4440 Assert(sizeof(pThis->gr) >= 9);
4441 for (i = 0; i < 9; ++i)
4442 {
4443 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4444 }
4445 pHlp->pfnPrintf(pHlp, "\n");
4446}
4447
4448
4449/**
4450 * @callback_method_impl{FNDBGFHANDLERDEV,
4451 * Dumps VGA Attribute Controller registers.}
4452 */
4453static DECLCALLBACK(void) vgaInfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4454{
4455 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4456 unsigned i;
4457 NOREF(pszArgs);
4458
4459 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4460 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4461 Assert(sizeof(pThis->ar) >= 0x14);
4462 pHlp->pfnPrintf(pHlp, " Palette:");
4463 for (i = 0; i < 0x10; ++i)
4464 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4465 pHlp->pfnPrintf(pHlp, "\n");
4466 for (i = 0x10; i <= 0x14; ++i)
4467 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4468 pHlp->pfnPrintf(pHlp, "\n");
4469}
4470
4471
4472/**
4473 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4474 */
4475static DECLCALLBACK(void) vgaInfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4476{
4477 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4478 unsigned i;
4479 NOREF(pszArgs);
4480
4481 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4482 for (i = 0; i < 0x100; ++i)
4483 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4484 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4485}
4486
4487
4488/**
4489 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4490 */
4491static DECLCALLBACK(void) vgaInfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4492{
4493 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4494 NOREF(pszArgs);
4495
4496 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4497
4498 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4499 {
4500 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4501 return;
4502 }
4503
4504 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4505 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4506 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4507 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4508 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4509 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4510 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4511 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4512 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4513 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4514 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4515}
4516
4517
4518/**
4519 * @callback_method_impl{FNDBGFHANDLERDEV,
4520 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4521 * in human-readable form.}
4522 */
4523static DECLCALLBACK(void) vgaInfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4524{
4525 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4526 int val1, val2;
4527 NOREF(pszArgs);
4528
4529 val1 = (pThis->gr[5] >> 3) & 1;
4530 val2 = pThis->gr[5] & 3;
4531 pHlp->pfnPrintf(pHlp, "read mode : %d write mode: %d\n", val1, val2);
4532 val1 = pThis->gr[0];
4533 val2 = pThis->gr[1];
4534 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4535 val1 = pThis->gr[2];
4536 val2 = pThis->gr[4] & 3;
4537 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %d\n", val1, val2);
4538 val1 = pThis->gr[3] & 7;
4539 val2 = (pThis->gr[3] >> 3) & 3;
4540 pHlp->pfnPrintf(pHlp, "rotate : %d function : %d\n", val1, val2);
4541 val1 = pThis->gr[7];
4542 val2 = pThis->gr[8];
4543 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4544 val1 = pThis->sr[2];
4545 val2 = pThis->sr[4] & 8;
4546 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4547}
4548
4549
4550/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4551
4552/**
4553 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4554 */
4555static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4556{
4557 PVGASTATE pThis = RT_FROM_MEMBER(pInterface, VGASTATE, IBase);
4558 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4559 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPort);
4560#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI))
4561 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThis->IVBVACallbacks);
4562#endif
4563 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
4564 return NULL;
4565}
4566
4567/* -=-=-=-=-=- Ring 3: ILeds -=-=-=-=-=- */
4568#define ILEDPORTS_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, ILeds)) )
4569
4570/**
4571 * Gets the pointer to the status LED of a unit.
4572 *
4573 * @returns VBox status code.
4574 * @param pInterface Pointer to the interface structure containing the called function pointer.
4575 * @param iLUN The unit which status LED we desire.
4576 * @param ppLed Where to store the LED pointer.
4577 */
4578static DECLCALLBACK(int) vgaPortQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
4579{
4580 PVGASTATE pThis = ILEDPORTS_2_VGASTATE(pInterface);
4581 switch (iLUN)
4582 {
4583 /* LUN #0: Display port. */
4584 case 0:
4585 {
4586 *ppLed = &pThis->Led3D;
4587 Assert((*ppLed)->u32Magic == PDMLED_MAGIC);
4588 return VINF_SUCCESS;
4589 }
4590
4591 default:
4592 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4593 return VERR_PDM_NO_SUCH_LUN;
4594 }
4595}
4596
4597/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4598
4599/**
4600 * Resize the display.
4601 * This is called when the resolution changes. This usually happens on
4602 * request from the guest os, but may also happen as the result of a reset.
4603 *
4604 * @param pInterface Pointer to this interface.
4605 * @param bpp Bits per pixel.
4606 * @param pvVRAM VRAM.
4607 * @param cbLine Number of bytes per line.
4608 * @param cx New display width.
4609 * @param cy New display height
4610 * @thread The emulation thread.
4611 */
4612static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM,
4613 uint32_t cbLine, uint32_t cx, uint32_t cy)
4614{
4615 NOREF(pInterface); NOREF(bpp); NOREF(pvVRAM); NOREF(cbLine); NOREF(cx); NOREF(cy);
4616 return VINF_SUCCESS;
4617}
4618
4619
4620/**
4621 * Update a rectangle of the display.
4622 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
4623 *
4624 * @param pInterface Pointer to this interface.
4625 * @param x The upper left corner x coordinate of the rectangle.
4626 * @param y The upper left corner y coordinate of the rectangle.
4627 * @param cx The width of the rectangle.
4628 * @param cy The height of the rectangle.
4629 * @thread The emulation thread.
4630 */
4631static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4632{
4633 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(cx); NOREF(cy);
4634}
4635
4636
4637/**
4638 * Refresh the display.
4639 *
4640 * The interval between these calls is set by
4641 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
4642 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
4643 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
4644 * the changed rectangles.
4645 *
4646 * @param pInterface Pointer to this interface.
4647 * @thread The emulation thread.
4648 */
4649static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4650{
4651 NOREF(pInterface);
4652}
4653
4654
4655/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4656
4657/** Converts a display port interface pointer to a vga state pointer. */
4658#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, IPort)) )
4659
4660
4661/**
4662 * Update the display with any changed regions.
4663 *
4664 * @param pInterface Pointer to this interface.
4665 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
4666 */
4667static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4668{
4669 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4670 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4671 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4672
4673 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4674 AssertRC(rc);
4675
4676#ifdef VBOX_WITH_VMSVGA
4677 if ( pThis->svga.fEnabled
4678 && !pThis->svga.fTraces)
4679 {
4680 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4681 PDMCritSectLeave(&pThis->CritSect);
4682 return VINF_SUCCESS;
4683 }
4684#endif
4685
4686#ifndef VBOX_WITH_HGSMI
4687 /* This should be called only in non VBVA mode. */
4688#else
4689 if (VBVAUpdateDisplay (pThis) == VINF_SUCCESS)
4690 {
4691 PDMCritSectLeave(&pThis->CritSect);
4692 return VINF_SUCCESS;
4693 }
4694#endif /* VBOX_WITH_HGSMI */
4695
4696 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4697 if (pThis->fHasDirtyBits && pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4698 {
4699 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4700 pThis->fHasDirtyBits = false;
4701 }
4702 if (pThis->fRemappedVGA)
4703 {
4704 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4705 pThis->fRemappedVGA = false;
4706 }
4707
4708 rc = vga_update_display(pThis, false, false, true,
4709 pThis->pDrv, &pThis->graphic_mode);
4710 PDMCritSectLeave(&pThis->CritSect);
4711 return rc;
4712}
4713
4714
4715/**
4716 * Internal vgaPortUpdateDisplayAll worker called under pThis->CritSect.
4717 */
4718static int updateDisplayAll(PVGASTATE pThis, bool fFailOnResize)
4719{
4720 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4721
4722#ifdef VBOX_WITH_VMSVGA
4723 if ( !pThis->svga.fEnabled
4724 || pThis->svga.fTraces)
4725 {
4726#endif
4727 /* The dirty bits array has been just cleared, reset handlers as well. */
4728 if (pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4729 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4730#ifdef VBOX_WITH_VMSVGA
4731 }
4732#endif
4733 if (pThis->fRemappedVGA)
4734 {
4735 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4736 pThis->fRemappedVGA = false;
4737 }
4738
4739 pThis->graphic_mode = -1; /* force full update */
4740
4741 return vga_update_display(pThis, true, fFailOnResize, true,
4742 pThis->pDrv, &pThis->graphic_mode);
4743}
4744
4745
4746DECLCALLBACK(int) vgaUpdateDisplayAll(PVGASTATE pThis, bool fFailOnResize)
4747{
4748#ifdef DEBUG_sunlover
4749 LogFlow(("vgaPortUpdateDisplayAll\n"));
4750#endif /* DEBUG_sunlover */
4751
4752 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4753 AssertRC(rc);
4754
4755 rc = updateDisplayAll(pThis, fFailOnResize);
4756
4757 PDMCritSectLeave(&pThis->CritSect);
4758 return rc;
4759}
4760
4761/**
4762 * Update the entire display.
4763 *
4764 * @param pInterface Pointer to this interface.
4765 * @param fFailOnResize Fail on resize.
4766 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
4767 */
4768static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface, bool fFailOnResize)
4769{
4770 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4771 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4772
4773 /* This is called both in VBVA mode and normal modes. */
4774
4775 return vgaUpdateDisplayAll(pThis, fFailOnResize);
4776}
4777
4778
4779/**
4780 * Sets the refresh rate and restart the timer.
4781 *
4782 * @returns VBox status code.
4783 * @param pInterface Pointer to this interface.
4784 * @param cMilliesInterval Number of millis between two refreshes.
4785 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
4786 */
4787static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4788{
4789 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4790
4791 pThis->cMilliesRefreshInterval = cMilliesInterval;
4792 if (cMilliesInterval)
4793 return TMTimerSetMillies(pThis->RefreshTimer, cMilliesInterval);
4794 return TMTimerStop(pThis->RefreshTimer);
4795}
4796
4797
4798/** @interface_method_impl{PDMIDISPLAYPORT,pfnQueryVideoMode} */
4799static DECLCALLBACK(int) vgaPortQueryVideoMode(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits, uint32_t *pcx, uint32_t *pcy)
4800{
4801 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4802
4803 if (!pcBits)
4804 return VERR_INVALID_PARAMETER;
4805 *pcBits = vga_get_bpp(pThis);
4806 if (pcx)
4807 *pcx = pThis->last_scr_width;
4808 if (pcy)
4809 *pcy = pThis->last_scr_height;
4810 return VINF_SUCCESS;
4811}
4812
4813
4814/**
4815 * Create a 32-bbp screenshot of the display. Size of the bitmap scanline in bytes is 4*width.
4816 *
4817 * @param pInterface Pointer to this interface.
4818 * @param ppbData Where to store the pointer to the allocated
4819 * buffer.
4820 * @param pcbData Where to store the actual size of the bitmap.
4821 * @param pcx Where to store the width of the bitmap.
4822 * @param pcy Where to store the height of the bitmap.
4823 * @see PDMIDISPLAYPORT::pfnTakeScreenshot() for details.
4824 */
4825static DECLCALLBACK(int) vgaPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppbData, size_t *pcbData, uint32_t *pcx, uint32_t *pcy)
4826{
4827 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4828 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4829
4830 LogFlow(("vgaPortTakeScreenshot: ppbData=%p pcbData=%p pcx=%p pcy=%p\n", ppbData, pcbData, pcx, pcy));
4831
4832 /*
4833 * Validate input.
4834 */
4835 if (!RT_VALID_PTR(ppbData) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
4836 return VERR_INVALID_PARAMETER;
4837
4838 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4839 AssertRCReturn(rc, rc);
4840
4841 /*
4842 * Get screenshot. This function will fail if a resize is required.
4843 * So there is not need to do a 'updateDisplayAll' before taking screenshot.
4844 */
4845
4846 /*
4847 * Allocate the buffer for 32 bits per pixel bitmap
4848 *
4849 * Note! The size can't be zero or greater than the size of the VRAM.
4850 * Inconsistent VGA device state can cause the incorrect size values.
4851 */
4852 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
4853 if (cbRequired && cbRequired <= pThis->vram_size)
4854 {
4855 uint8_t *pbData = (uint8_t *)RTMemAlloc(cbRequired);
4856 if (pbData != NULL)
4857 {
4858 /*
4859 * Only 3 methods, assigned below, will be called during the screenshot update.
4860 * All other are already set to NULL.
4861 */
4862 /* The display connector interface is temporarily replaced with the fake one. */
4863 PDMIDISPLAYCONNECTOR Connector;
4864 RT_ZERO(Connector);
4865 Connector.pbData = pbData;
4866 Connector.cBits = 32;
4867 Connector.cx = pThis->last_scr_width;
4868 Connector.cy = pThis->last_scr_height;
4869 Connector.cbScanline = Connector.cx * 4;
4870 Connector.pfnRefresh = vgaDummyRefresh;
4871 Connector.pfnResize = vgaDummyResize;
4872 Connector.pfnUpdateRect = vgaDummyUpdateRect;
4873
4874 int32_t cur_graphic_mode = -1;
4875
4876 bool fSavedRenderVRAM = pThis->fRenderVRAM;
4877 pThis->fRenderVRAM = true;
4878
4879 /*
4880 * Take the screenshot.
4881 *
4882 * The second parameter is 'false' because the current display state is being rendered to an
4883 * external buffer using a fake connector. That is if display is blanked, we expect a black
4884 * screen in the external buffer.
4885 * If there is a pending resize, the function will fail.
4886 */
4887 rc = vga_update_display(pThis, false, true, false, &Connector, &cur_graphic_mode);
4888
4889 pThis->fRenderVRAM = fSavedRenderVRAM;
4890
4891 if (rc == VINF_SUCCESS)
4892 {
4893 /*
4894 * Return the result.
4895 */
4896 *ppbData = pbData;
4897 *pcbData = cbRequired;
4898 *pcx = Connector.cx;
4899 *pcy = Connector.cy;
4900 }
4901 else
4902 {
4903 /* If we do not return a success, then the data buffer must be freed. */
4904 RTMemFree(pbData);
4905 if (RT_SUCCESS_NP(rc))
4906 {
4907 AssertMsgFailed(("%Rrc\n", rc));
4908 rc = VERR_INTERNAL_ERROR_5;
4909 }
4910 }
4911 }
4912 else
4913 rc = VERR_NO_MEMORY;
4914 }
4915 else
4916 rc = VERR_NOT_SUPPORTED;
4917
4918 PDMCritSectLeave(&pThis->CritSect);
4919
4920 LogFlow(("vgaPortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
4921 return rc;
4922}
4923
4924/**
4925 * Free a screenshot buffer allocated in vgaPortTakeScreenshot.
4926 *
4927 * @param pInterface Pointer to this interface.
4928 * @param pbData Pointer returned by vgaPortTakeScreenshot.
4929 * @see PDMIDISPLAYPORT::pfnFreeScreenshot() for details.
4930 */
4931static DECLCALLBACK(void) vgaPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pbData)
4932{
4933 NOREF(pInterface);
4934
4935 LogFlow(("vgaPortFreeScreenshot: pbData=%p\n", pbData));
4936
4937 RTMemFree(pbData);
4938}
4939
4940/**
4941 * Copy bitmap to the display.
4942 *
4943 * @param pInterface Pointer to this interface.
4944 * @param pvData Pointer to the bitmap bits.
4945 * @param x The upper left corner x coordinate of the destination rectangle.
4946 * @param y The upper left corner y coordinate of the destination rectangle.
4947 * @param cx The width of the source and destination rectangles.
4948 * @param cy The height of the source and destination rectangles.
4949 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
4950 */
4951static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4952{
4953 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4954 int rc = VINF_SUCCESS;
4955 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4956 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
4957
4958 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4959 AssertRC(rc);
4960
4961 /*
4962 * Validate input.
4963 */
4964 if ( pvData
4965 && x < pThis->pDrv->cx
4966 && cx <= pThis->pDrv->cx
4967 && cx + x <= pThis->pDrv->cx
4968 && y < pThis->pDrv->cy
4969 && cy <= pThis->pDrv->cy
4970 && cy + y <= pThis->pDrv->cy)
4971 {
4972 /*
4973 * Determine bytes per pixel in the destination buffer.
4974 */
4975 size_t cbPixelDst = 0;
4976 switch (pThis->pDrv->cBits)
4977 {
4978 case 8:
4979 cbPixelDst = 1;
4980 break;
4981 case 15:
4982 case 16:
4983 cbPixelDst = 2;
4984 break;
4985 case 24:
4986 cbPixelDst = 3;
4987 break;
4988 case 32:
4989 cbPixelDst = 4;
4990 break;
4991 default:
4992 rc = VERR_INVALID_PARAMETER;
4993 break;
4994 }
4995 if (RT_SUCCESS(rc))
4996 {
4997 /*
4998 * The blitting loop.
4999 */
5000 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
5001 uint8_t *pbSrc = (uint8_t *)pvData;
5002 size_t cbLineDst = pThis->pDrv->cbScanline;
5003 uint8_t *pbDst = pThis->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5004 uint32_t cyLeft = cy;
5005 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
5006 Assert(pfnVgaDrawLine);
5007 while (cyLeft-- > 0)
5008 {
5009 pfnVgaDrawLine(pThis, pbDst, pbSrc, cx);
5010 pbDst += cbLineDst;
5011 pbSrc += cbLineSrc;
5012 }
5013
5014 /*
5015 * Invalidate the area.
5016 */
5017 pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
5018 }
5019 }
5020 else
5021 rc = VERR_INVALID_PARAMETER;
5022
5023 PDMCritSectLeave(&pThis->CritSect);
5024
5025 LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
5026 return rc;
5027}
5028
5029static DECLCALLBACK(void) vgaPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
5030{
5031 uint32_t v;
5032 vga_draw_line_func *vga_draw_line;
5033
5034 uint32_t cbPixelDst;
5035 uint32_t cbLineDst;
5036 uint8_t *pbDst;
5037
5038 uint32_t cbPixelSrc;
5039 uint32_t cbLineSrc;
5040 uint8_t *pbSrc;
5041
5042 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5043
5044#ifdef DEBUG_sunlover
5045 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
5046#endif /* DEBUG_sunlover */
5047
5048 Assert(pInterface);
5049
5050 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5051 AssertRC(rc);
5052
5053 /* Check if there is something to do at all. */
5054 if (!pThis->fRenderVRAM)
5055 {
5056 /* The framebuffer uses the guest VRAM directly. */
5057#ifdef DEBUG_sunlover
5058 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
5059#endif /* DEBUG_sunlover */
5060 PDMCritSectLeave(&pThis->CritSect);
5061 return;
5062 }
5063
5064 Assert(pThis->pDrv);
5065 Assert(pThis->pDrv->pbData);
5066
5067 /* Correct negative x and y coordinates. */
5068 if (x < 0)
5069 {
5070 x += w; /* Compute xRight which is also the new width. */
5071 w = (x < 0) ? 0 : x;
5072 x = 0;
5073 }
5074
5075 if (y < 0)
5076 {
5077 y += h; /* Compute yBottom, which is also the new height. */
5078 h = (y < 0) ? 0 : y;
5079 y = 0;
5080 }
5081
5082 /* Also check if coords are greater than the display resolution. */
5083 if (x + w > pThis->pDrv->cx)
5084 {
5085 // x < 0 is not possible here
5086 w = pThis->pDrv->cx > (uint32_t)x? pThis->pDrv->cx - x: 0;
5087 }
5088
5089 if (y + h > pThis->pDrv->cy)
5090 {
5091 // y < 0 is not possible here
5092 h = pThis->pDrv->cy > (uint32_t)y? pThis->pDrv->cy - y: 0;
5093 }
5094
5095#ifdef DEBUG_sunlover
5096 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
5097#endif /* DEBUG_sunlover */
5098
5099 /* Check if there is something to do at all. */
5100 if (w == 0 || h == 0)
5101 {
5102 /* Empty rectangle. */
5103#ifdef DEBUG_sunlover
5104 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
5105#endif /* DEBUG_sunlover */
5106 PDMCritSectLeave(&pThis->CritSect);
5107 return;
5108 }
5109
5110 /** @todo This method should be made universal and not only for VBVA.
5111 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5112 * changed.
5113 */
5114
5115 /* Choose the rendering function. */
5116 switch(pThis->get_bpp(pThis))
5117 {
5118 default:
5119 case 0:
5120 /* A LFB mode is already disabled, but the callback is still called
5121 * by Display because VBVA buffer is being flushed.
5122 * Nothing to do, just return.
5123 */
5124 PDMCritSectLeave(&pThis->CritSect);
5125 return;
5126 case 8:
5127 v = VGA_DRAW_LINE8;
5128 break;
5129 case 15:
5130 v = VGA_DRAW_LINE15;
5131 break;
5132 case 16:
5133 v = VGA_DRAW_LINE16;
5134 break;
5135 case 24:
5136 v = VGA_DRAW_LINE24;
5137 break;
5138 case 32:
5139 v = VGA_DRAW_LINE32;
5140 break;
5141 }
5142
5143 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pThis->pDrv->cBits)];
5144
5145 /* Compute source and destination addresses and pitches. */
5146 cbPixelDst = (pThis->pDrv->cBits + 7) / 8;
5147 cbLineDst = pThis->pDrv->cbScanline;
5148 pbDst = pThis->pDrv->pbData + y * cbLineDst + x * cbPixelDst;
5149
5150 cbPixelSrc = (pThis->get_bpp(pThis) + 7) / 8;
5151 uint32_t offSrc, u32Dummy;
5152 pThis->get_offsets(pThis, &cbLineSrc, &offSrc, &u32Dummy);
5153
5154 /* Assume that rendering is performed only on visible part of VRAM.
5155 * This is true because coordinates were verified.
5156 */
5157 pbSrc = pThis->vram_ptrR3;
5158 pbSrc += offSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5159
5160 /* Render VRAM to framebuffer. */
5161
5162#ifdef DEBUG_sunlover
5163 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDst, cbLineDst, cbPixelDst, pbSrc, cbLineSrc, cbPixelSrc));
5164#endif /* DEBUG_sunlover */
5165
5166 while (h-- > 0)
5167 {
5168 vga_draw_line (pThis, pbDst, pbSrc, w);
5169 pbDst += cbLineDst;
5170 pbSrc += cbLineSrc;
5171 }
5172
5173 PDMCritSectLeave(&pThis->CritSect);
5174#ifdef DEBUG_sunlover
5175 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
5176#endif /* DEBUG_sunlover */
5177}
5178
5179
5180static DECLCALLBACK(int)
5181vgaPortCopyRect(PPDMIDISPLAYPORT pInterface,
5182 uint32_t cx,
5183 uint32_t cy,
5184 const uint8_t *pbSrc, int32_t xSrc, int32_t ySrc, uint32_t cxSrc, uint32_t cySrc,
5185 uint32_t cbSrcLine, uint32_t cSrcBitsPerPixel,
5186 uint8_t *pbDst, int32_t xDst, int32_t yDst, uint32_t cxDst, uint32_t cyDst,
5187 uint32_t cbDstLine, uint32_t cDstBitsPerPixel)
5188{
5189 uint32_t v;
5190 vga_draw_line_func *vga_draw_line;
5191
5192#ifdef DEBUG_sunlover
5193 LogFlow(("vgaPortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, cx, cy, xDst, yDst));
5194#endif /* DEBUG_sunlover */
5195
5196 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5197
5198 Assert(pInterface);
5199 Assert(pThis->pDrv);
5200
5201 int32_t xSrcCorrected = xSrc;
5202 int32_t ySrcCorrected = ySrc;
5203 uint32_t cxCorrected = cx;
5204 uint32_t cyCorrected = cy;
5205
5206 /* Correct source coordinates to be within the source bitmap. */
5207 if (xSrcCorrected < 0)
5208 {
5209 xSrcCorrected += cxCorrected; /* Compute xRight which is also the new width. */
5210 cxCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5211 xSrcCorrected = 0;
5212 }
5213
5214 if (ySrcCorrected < 0)
5215 {
5216 ySrcCorrected += cyCorrected; /* Compute yBottom, which is also the new height. */
5217 cyCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5218 ySrcCorrected = 0;
5219 }
5220
5221 /* Also check if coords are greater than the display resolution. */
5222 if (xSrcCorrected + cxCorrected > cxSrc)
5223 {
5224 /* xSrcCorrected < 0 is not possible here */
5225 cxCorrected = cxSrc > (uint32_t)xSrcCorrected ? cxSrc - xSrcCorrected : 0;
5226 }
5227
5228 if (ySrcCorrected + cyCorrected > cySrc)
5229 {
5230 /* y < 0 is not possible here */
5231 cyCorrected = cySrc > (uint32_t)ySrcCorrected ? cySrc - ySrcCorrected : 0;
5232 }
5233
5234#ifdef DEBUG_sunlover
5235 LogFlow(("vgaPortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, cxCorrected, cyCorrected));
5236#endif /* DEBUG_sunlover */
5237
5238 /* Check if there is something to do at all. */
5239 if (cxCorrected == 0 || cyCorrected == 0)
5240 {
5241 /* Empty rectangle. */
5242#ifdef DEBUG_sunlover
5243 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", cxCorrected, cyCorrected));
5244#endif /* DEBUG_sunlover */
5245 return VINF_SUCCESS;
5246 }
5247
5248 /* Check that the corrected source rectangle is within the destination.
5249 * Note: source rectangle is adjusted, but the target must be large enough.
5250 */
5251 if ( xDst < 0
5252 || yDst < 0
5253 || xDst + cxCorrected > cxDst
5254 || yDst + cyCorrected > cyDst)
5255 {
5256 return VERR_INVALID_PARAMETER;
5257 }
5258
5259 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5260 AssertRC(rc);
5261
5262 /* This method only works if the VGA device is in a VBE mode or not paused VBVA mode.
5263 * VGA modes are reported to the caller by returning VERR_INVALID_STATE.
5264 *
5265 * If VBE_DISPI_ENABLED is set, then it is a VBE or VBE compatible VBVA mode. Both of them can be handled.
5266 *
5267 * If VBE_DISPI_ENABLED is clear, then it is either a VGA mode or a VBVA mode set by guest additions
5268 * which have VBVACAPS_USE_VBVA_ONLY capability.
5269 * When VBE_DISPI_ENABLED is being cleared and VBVACAPS_USE_VBVA_ONLY is not set (i.e. guest wants a VGA mode),
5270 * then VBVAOnVBEChanged makes sure that VBVA is paused.
5271 * That is a not paused VBVA means that the video mode can be handled even if VBE_DISPI_ENABLED is clear.
5272 */
5273 if ( (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0
5274 && VBVAIsPaused(pThis))
5275 {
5276 PDMCritSectLeave(&pThis->CritSect);
5277 return VERR_INVALID_STATE;
5278 }
5279
5280 /* Choose the rendering function. */
5281 switch (cSrcBitsPerPixel)
5282 {
5283 default:
5284 case 0:
5285 /* Nothing to do, just return. */
5286 PDMCritSectLeave(&pThis->CritSect);
5287 return VINF_SUCCESS;
5288 case 8:
5289 v = VGA_DRAW_LINE8;
5290 break;
5291 case 15:
5292 v = VGA_DRAW_LINE15;
5293 break;
5294 case 16:
5295 v = VGA_DRAW_LINE16;
5296 break;
5297 case 24:
5298 v = VGA_DRAW_LINE24;
5299 break;
5300 case 32:
5301 v = VGA_DRAW_LINE32;
5302 break;
5303 }
5304
5305 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(cDstBitsPerPixel)];
5306
5307 /* Compute source and destination addresses and pitches. */
5308 uint32_t cbPixelDst = (cDstBitsPerPixel + 7) / 8;
5309 uint32_t cbLineDst = cbDstLine;
5310 uint8_t *pbDstCur = pbDst + yDst * cbLineDst + xDst * cbPixelDst;
5311
5312 uint32_t cbPixelSrc = (cSrcBitsPerPixel + 7) / 8;
5313 uint32_t cbLineSrc = cbSrcLine;
5314 const uint8_t *pbSrcCur = pbSrc + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5315
5316#ifdef DEBUG_sunlover
5317 LogFlow(("vgaPortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pbDstCur, cbLineDst, cbPixelDst, pbSrcCur, cbLineSrc, cbPixelSrc));
5318#endif /* DEBUG_sunlover */
5319
5320 while (cyCorrected-- > 0)
5321 {
5322 vga_draw_line(pThis, pbDstCur, pbSrcCur, cxCorrected);
5323 pbDstCur += cbLineDst;
5324 pbSrcCur += cbLineSrc;
5325 }
5326
5327 PDMCritSectLeave(&pThis->CritSect);
5328#ifdef DEBUG_sunlover
5329 LogFlow(("vgaPortCopyRect: completed.\n"));
5330#endif /* DEBUG_sunlover */
5331
5332 return VINF_SUCCESS;
5333}
5334
5335static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5336{
5337 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5338
5339 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
5340
5341 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5342 AssertRC(rc);
5343
5344 pThis->fRenderVRAM = fRender;
5345
5346 PDMCritSectLeave(&pThis->CritSect);
5347}
5348
5349
5350static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5351{
5352 PVGASTATE pThis = (PVGASTATE)pvUser;
5353 NOREF(pDevIns);
5354
5355 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5356 {
5357 VBVARaiseIrq(pThis, HGSMIHOSTFLAGS_VSYNC);
5358 }
5359
5360 if (pThis->pDrv)
5361 pThis->pDrv->pfnRefresh(pThis->pDrv);
5362
5363 if (pThis->cMilliesRefreshInterval)
5364 TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
5365
5366#ifdef VBOX_WITH_VIDEOHWACCEL
5367 vbvaTimerCb(pThis);
5368#endif
5369
5370#ifdef VBOX_WITH_CRHGSMI
5371 vboxCmdVBVACmdTimer(pThis);
5372#endif
5373}
5374
5375#ifdef VBOX_WITH_VMSVGA
5376int vgaR3RegisterVRAMHandler(PVGASTATE pVGAState, uint64_t cbFrameBuffer)
5377{
5378 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5379 Assert(pVGAState->GCPhysVRAM);
5380
5381 int rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5382 pVGAState->GCPhysVRAM, pVGAState->GCPhysVRAM + (cbFrameBuffer - 1),
5383 pVGAState->hLfbAccessHandlerType, pVGAState, pDevIns->pvInstanceDataR0,
5384 pDevIns->pvInstanceDataRC, "VGA LFB");
5385
5386 AssertRC(rc);
5387 return rc;
5388}
5389
5390int vgaR3UnregisterVRAMHandler(PVGASTATE pVGAState)
5391{
5392 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5393
5394 Assert(pVGAState->GCPhysVRAM);
5395 int rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pVGAState->GCPhysVRAM);
5396 AssertRC(rc);
5397 return rc;
5398}
5399#endif
5400
5401/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5402
5403/**
5404 * @callback_method_impl{FNPCIIOREGIONMAP, Mapping/unmapping the VRAM MMI2 region}
5405 */
5406static DECLCALLBACK(int) vgaR3IORegionMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5407 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
5408{
5409 RT_NOREF1(cb);
5410 int rc;
5411 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5412 Log(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%RGp enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5413#ifdef VBOX_WITH_VMSVGA
5414 AssertReturn( iRegion == (pThis->fVMSVGAEnabled ? 1U : 0U)
5415 && enmType == (pThis->fVMSVGAEnabled ? PCI_ADDRESS_SPACE_MEM : PCI_ADDRESS_SPACE_MEM_PREFETCH),
5416 VERR_INTERNAL_ERROR);
5417#else
5418 AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5419#endif
5420
5421 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5422 AssertRC(rc);
5423
5424 if (GCPhysAddress != NIL_RTGCPHYS)
5425 {
5426 /*
5427 * Mapping the VRAM.
5428 */
5429 rc = PDMDevHlpMMIOExMap(pDevIns, pPciDev, iRegion, GCPhysAddress);
5430 AssertRC(rc);
5431 if (RT_SUCCESS(rc))
5432 {
5433 rc = PGMHandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns), GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5434 pThis->hLfbAccessHandlerType, pThis, pDevIns->pvInstanceDataR0,
5435 pDevIns->pvInstanceDataRC, "VGA LFB");
5436 AssertRC(rc);
5437 if (RT_SUCCESS(rc))
5438 {
5439 pThis->GCPhysVRAM = GCPhysAddress;
5440 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5441 }
5442 }
5443 }
5444 else
5445 {
5446 /*
5447 * Unmapping of the VRAM in progress.
5448 * Deregister the access handler so PGM doesn't get upset.
5449 */
5450 Assert(pThis->GCPhysVRAM);
5451#ifdef VBOX_WITH_VMSVGA
5452 Assert(!pThis->svga.fEnabled || !pThis->svga.fVRAMTracking);
5453 if ( !pThis->svga.fEnabled
5454 || ( pThis->svga.fEnabled
5455 && pThis->svga.fVRAMTracking
5456 )
5457 )
5458 {
5459#endif
5460 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5461 AssertRC(rc);
5462#ifdef VBOX_WITH_VMSVGA
5463 }
5464 else
5465 rc = VINF_SUCCESS;
5466#endif
5467 pThis->GCPhysVRAM = 0;
5468 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5469 }
5470 PDMCritSectLeave(&pThis->CritSect);
5471 return rc;
5472}
5473
5474
5475#ifdef VBOX_WITH_VMSVGA /* Currently not needed in the non-VMSVGA mode, but keeping it flexible for later. */
5476/**
5477 * @interface_method_impl{PDMPCIDEV,pfnRegionLoadChangeHookR3}
5478 */
5479static DECLCALLBACK(int) vgaR3PciRegionLoadChangeHook(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
5480 uint64_t cbRegion, PCIADDRESSSPACE enmType,
5481 PFNPCIIOREGIONOLDSETTER pfnOldSetter)
5482{
5483 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5484
5485# ifdef VBOX_WITH_VMSVGA
5486 /*
5487 * The VMSVGA changed the default FIFO size from 128KB to 2MB after 5.1.
5488 */
5489 if (pThis->fVMSVGAEnabled)
5490 {
5491 if (iRegion == 2 /*FIFO*/)
5492 {
5493 /* Make sure it's still 32-bit memory. Ignore fluxtuations in the prefetch flag */
5494 AssertLogRelMsgReturn(!(enmType & (PCI_ADDRESS_SPACE_IO | PCI_ADDRESS_SPACE_BAR64)), ("enmType=%#x\n", enmType),
5495 VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE);
5496
5497 /* If the size didn't change we're fine, so just return already. */
5498 if (cbRegion == pThis->svga.cbFIFO)
5499 return VINF_SUCCESS;
5500
5501 /* If the size is larger than the current configuration, refuse to load. */
5502 AssertLogRelMsgReturn(cbRegion <= pThis->svga.cbFIFOConfig,
5503 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x\n",
5504 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO),
5505 VERR_SSM_LOAD_CONFIG_MISMATCH);
5506
5507 /* Adjust the size down. */
5508 int rc = PDMDevHlpMMIOExReduce(pDevIns, pPciDev, iRegion, cbRegion);
5509 AssertLogRelMsgRCReturn(rc,
5510 ("cbRegion=%#RGp cbFIFOConfig=%#x cbFIFO=%#x: %Rrc\n",
5511 cbRegion, pThis->svga.cbFIFOConfig, pThis->svga.cbFIFO, rc),
5512 rc);
5513 pThis->svga.cbFIFO = cbRegion;
5514 return rc;
5515
5516 }
5517 /* Emulate callbacks for 5.1 and older saved states by recursion. */
5518 else if (iRegion == UINT32_MAX)
5519 {
5520 int rc = vgaR3PciRegionLoadChangeHook(pDevIns, pPciDev, 2, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM, NULL);
5521 if (RT_SUCCESS(rc))
5522 rc = pfnOldSetter(pPciDev, 2, VMSVGA_FIFO_SIZE_OLD, PCI_ADDRESS_SPACE_MEM);
5523 return rc;
5524 }
5525 }
5526# endif /* VBOX_WITH_VMSVGA */
5527
5528 return VERR_VGA_UNEXPECTED_PCI_REGION_LOAD_CHANGE;
5529}
5530#endif /* VBOX_WITH_VMSVGA */
5531
5532
5533/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5534
5535/**
5536 * Saves a important bits of the VGA device config.
5537 *
5538 * @param pThis The VGA instance data.
5539 * @param pSSM The saved state handle.
5540 */
5541static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
5542{
5543 SSMR3PutU32(pSSM, pThis->vram_size);
5544 SSMR3PutU32(pSSM, pThis->cMonitors);
5545}
5546
5547
5548/**
5549 * @callback_method_impl{FNSSMDEVLIVEEXEC}
5550 */
5551static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5552{
5553 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5554 Assert(uPass == 0); NOREF(uPass);
5555 vgaR3SaveConfig(pThis, pSSM);
5556 return VINF_SSM_DONT_CALL_AGAIN;
5557}
5558
5559
5560/**
5561 * @callback_method_impl{FNSSMDEVSAVEPREP}
5562 */
5563static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5564{
5565#ifdef VBOX_WITH_VIDEOHWACCEL
5566 RT_NOREF(pSSM);
5567 return vboxVBVASaveStatePrep(pDevIns);
5568#else
5569 RT_NOREF(pDevIns, pSSM);
5570 return VINF_SUCCESS;
5571#endif
5572}
5573
5574/**
5575 * @callback_method_impl{FNSSMDEVSAVEDONE}
5576 */
5577static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5578{
5579#ifdef VBOX_WITH_VIDEOHWACCEL
5580 RT_NOREF(pSSM);
5581 return vboxVBVASaveStateDone(pDevIns);
5582#else
5583 RT_NOREF(pDevIns, pSSM);
5584 return VINF_SUCCESS;
5585#endif
5586}
5587
5588/**
5589 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5590 */
5591static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5592{
5593 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5594
5595#ifdef VBOX_WITH_VDMA
5596 vboxVDMASaveStateExecPrep(pThis->pVdma);
5597#endif
5598
5599 vgaR3SaveConfig(pThis, pSSM);
5600 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5601
5602 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5603#ifdef VBOX_WITH_HGSMI
5604 SSMR3PutBool(pSSM, true);
5605 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5606#else
5607 int rc = SSMR3PutBool(pSSM, false);
5608#endif
5609
5610 AssertRCReturn(rc, rc);
5611
5612 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5613#ifdef VBOX_WITH_VDMA
5614 rc = SSMR3PutU32(pSSM, 1);
5615 AssertRCReturn(rc, rc);
5616 rc = vboxVDMASaveStateExecPerform(pThis->pVdma, pSSM);
5617#else
5618 rc = SSMR3PutU32(pSSM, 0);
5619#endif
5620 AssertRCReturn(rc, rc);
5621
5622#ifdef VBOX_WITH_VDMA
5623 vboxVDMASaveStateExecDone(pThis->pVdma);
5624#endif
5625
5626 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5627#ifdef VBOX_WITH_VMSVGA
5628 if (pThis->fVMSVGAEnabled)
5629 {
5630 rc = vmsvgaSaveExec(pDevIns, pSSM);
5631 AssertRCReturn(rc, rc);
5632 }
5633#endif
5634 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5635
5636 return rc;
5637}
5638
5639
5640/**
5641 * @copydoc FNSSMDEVLOADEXEC
5642 */
5643static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5644{
5645 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5646 int rc;
5647
5648 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5649 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5650
5651 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5652 {
5653 /* Check the config */
5654 uint32_t cbVRam;
5655 rc = SSMR3GetU32(pSSM, &cbVRam);
5656 AssertRCReturn(rc, rc);
5657 if (pThis->vram_size != cbVRam)
5658 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5659
5660 uint32_t cMonitors;
5661 rc = SSMR3GetU32(pSSM, &cMonitors);
5662 AssertRCReturn(rc, rc);
5663 if (pThis->cMonitors != cMonitors)
5664 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5665 }
5666
5667 if (uPass == SSM_PASS_FINAL)
5668 {
5669 rc = vga_load(pSSM, pThis, uVersion);
5670 if (RT_FAILURE(rc))
5671 return rc;
5672
5673 /*
5674 * Restore the HGSMI state, if present.
5675 */
5676 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5677 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5678 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5679 {
5680 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5681 AssertRCReturn(rc, rc);
5682 }
5683 if (fWithHgsmi)
5684 {
5685#ifdef VBOX_WITH_HGSMI
5686 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5687 AssertRCReturn(rc, rc);
5688#else
5689 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5690#endif
5691 }
5692
5693 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5694 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5695 {
5696 uint32_t u32;
5697 rc = SSMR3GetU32(pSSM, &u32);
5698 if (u32)
5699 {
5700#ifdef VBOX_WITH_VDMA
5701 if (u32 == 1)
5702 {
5703 rc = vboxVDMASaveLoadExecPerform(pThis->pVdma, pSSM, uVersion);
5704 AssertRCReturn(rc, rc);
5705 }
5706 else
5707#endif
5708 {
5709 LogRel(("invalid CmdVbva version info\n"));
5710 return VERR_VERSION_MISMATCH;
5711 }
5712 }
5713 }
5714
5715 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
5716#ifdef VBOX_WITH_VMSVGA
5717 if (pThis->fVMSVGAEnabled)
5718 {
5719 rc = vmsvgaLoadExec(pDevIns, pSSM, uVersion, uPass);
5720 AssertRCReturn(rc, rc);
5721 }
5722#endif
5723 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
5724 }
5725 return VINF_SUCCESS;
5726}
5727
5728
5729/**
5730 * @copydoc FNSSMDEVLOADDONE
5731 */
5732static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5733{
5734 RT_NOREF(pSSM);
5735 int rc = VINF_SUCCESS;
5736
5737#ifdef VBOX_WITH_HGSMI
5738 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5739 rc = vboxVBVALoadStateDone(pDevIns);
5740 AssertRCReturn(rc, rc);
5741# ifdef VBOX_WITH_VDMA
5742 rc = vboxVDMASaveLoadDone(pThis->pVdma);
5743 AssertRCReturn(rc, rc);
5744# endif
5745 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
5746 VBVAOnVBEChanged(pThis);
5747#endif
5748#ifdef VBOX_WITH_VMSVGA
5749 if (pThis->fVMSVGAEnabled)
5750 {
5751 rc = vmsvgaLoadDone(pDevIns);
5752 AssertRCReturn(rc, rc);
5753 }
5754#endif
5755 return rc;
5756}
5757
5758
5759/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5760
5761/**
5762 * @interface_method_impl{PDMDEVREG,pfnReset}
5763 */
5764static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5765{
5766 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5767 char *pchStart;
5768 char *pchEnd;
5769 LogFlow(("vgaReset\n"));
5770
5771 if (pThis->pVdma)
5772 vboxVDMAReset(pThis->pVdma);
5773
5774#ifdef VBOX_WITH_VMSVGA
5775 if (pThis->fVMSVGAEnabled)
5776 vmsvgaReset(pDevIns);
5777#endif
5778
5779#ifdef VBOX_WITH_HGSMI
5780 VBVAReset(pThis);
5781#endif /* VBOX_WITH_HGSMI */
5782
5783
5784 /* Clear the VRAM ourselves. */
5785 if (pThis->vram_ptrR3 && pThis->vram_size)
5786 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5787
5788 /*
5789 * Zero most of it.
5790 *
5791 * Unlike vga_reset we're leaving out a few members which we believe
5792 * must remain unchanged....
5793 */
5794 /* 1st part. */
5795 pchStart = (char *)&pThis->latch;
5796 pchEnd = (char *)&pThis->invalidated_y_table;
5797 memset(pchStart, 0, pchEnd - pchStart);
5798
5799 /* 2nd part. */
5800 pchStart = (char *)&pThis->last_palette;
5801 pchEnd = (char *)&pThis->u32Marker;
5802 memset(pchStart, 0, pchEnd - pchStart);
5803
5804
5805 /*
5806 * Restore and re-init some bits.
5807 */
5808 pThis->get_bpp = vga_get_bpp;
5809 pThis->get_offsets = vga_get_offsets;
5810 pThis->get_resolution = vga_get_resolution;
5811 pThis->graphic_mode = -1; /* Force full update. */
5812#ifdef CONFIG_BOCHS_VBE
5813 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5814 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5815 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
5816 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5817#endif /* CONFIG_BOCHS_VBE */
5818
5819 /*
5820 * Reset the LFB mapping.
5821 */
5822 pThis->fLFBUpdated = false;
5823 if ( ( pThis->fGCEnabled
5824 || pThis->fR0Enabled)
5825 && pThis->GCPhysVRAM
5826 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
5827 {
5828 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5829 AssertRC(rc);
5830 }
5831 if (pThis->fRemappedVGA)
5832 {
5833 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5834 pThis->fRemappedVGA = false;
5835 }
5836
5837 /*
5838 * Reset the logo data.
5839 */
5840 pThis->LogoCommand = LOGO_CMD_NOP;
5841 pThis->offLogoData = 0;
5842
5843 /* notify port handler */
5844 if (pThis->pDrv)
5845 {
5846 PDMCritSectLeave(&pThis->CritSect); /* hack around lock order issue. */
5847 pThis->pDrv->pfnReset(pThis->pDrv);
5848 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
5849 }
5850
5851 /* Reset latched access mask. */
5852 pThis->uMaskLatchAccess = 0x3ff;
5853 pThis->cLatchAccesses = 0;
5854 pThis->u64LastLatchedAccess = 0;
5855 pThis->iMask = 0;
5856
5857 /* Reset retrace emulation. */
5858 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5859}
5860
5861
5862/**
5863 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5864 */
5865static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5866{
5867 if (offDelta)
5868 {
5869 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5870 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5871
5872 pThis->vram_ptrRC += offDelta;
5873 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5874 }
5875}
5876
5877
5878/**
5879 * @interface_method_impl{PDMDEVREG,pfnAttach}
5880 *
5881 * This is like plugging in the monitor after turning on the PC.
5882 */
5883static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5884{
5885 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5886
5887 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5888 ("VGA device does not support hotplugging\n"),
5889 VERR_INVALID_PARAMETER);
5890
5891 switch (iLUN)
5892 {
5893 /* LUN #0: Display port. */
5894 case 0:
5895 {
5896 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5897 if (RT_SUCCESS(rc))
5898 {
5899 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5900 if (pThis->pDrv)
5901 {
5902 /* pThis->pDrv->pbData can be NULL when there is no framebuffer. */
5903 if ( pThis->pDrv->pfnRefresh
5904 && pThis->pDrv->pfnResize
5905 && pThis->pDrv->pfnUpdateRect)
5906 rc = VINF_SUCCESS;
5907 else
5908 {
5909 Assert(pThis->pDrv->pfnRefresh);
5910 Assert(pThis->pDrv->pfnResize);
5911 Assert(pThis->pDrv->pfnUpdateRect);
5912 pThis->pDrv = NULL;
5913 pThis->pDrvBase = NULL;
5914 rc = VERR_INTERNAL_ERROR;
5915 }
5916#ifdef VBOX_WITH_VIDEOHWACCEL
5917 if(rc == VINF_SUCCESS)
5918 {
5919 rc = vbvaVHWAConstruct(pThis);
5920 if (rc != VERR_NOT_IMPLEMENTED)
5921 AssertRC(rc);
5922 }
5923#endif
5924 }
5925 else
5926 {
5927 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5928 pThis->pDrvBase = NULL;
5929 rc = VERR_PDM_MISSING_INTERFACE;
5930 }
5931 }
5932 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5933 {
5934 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5935 rc = VINF_SUCCESS;
5936 }
5937 else
5938 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5939 return rc;
5940 }
5941
5942 default:
5943 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5944 return VERR_PDM_NO_SUCH_LUN;
5945 }
5946}
5947
5948
5949/**
5950 * @interface_method_impl{PDMDEVREG,pfnDetach}
5951 *
5952 * This is like unplugging the monitor while the PC is still running.
5953 */
5954static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5955{
5956 RT_NOREF1(fFlags);
5957 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5958 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
5959
5960 /*
5961 * Reset the interfaces and update the controller state.
5962 */
5963 switch (iLUN)
5964 {
5965 /* LUN #0: Display port. */
5966 case 0:
5967 pThis->pDrv = NULL;
5968 pThis->pDrvBase = NULL;
5969 break;
5970
5971 default:
5972 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5973 break;
5974 }
5975}
5976
5977
5978/**
5979 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5980 */
5981static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5982{
5983 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5984
5985#ifdef VBE_NEW_DYN_LIST
5986 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5987 LogFlow(("vgaR3Destruct:\n"));
5988
5989# ifdef VBOX_WITH_VDMA
5990 if (pThis->pVdma)
5991 vboxVDMADestruct(pThis->pVdma);
5992# endif
5993
5994#ifdef VBOX_WITH_VMSVGA
5995 if (pThis->fVMSVGAEnabled)
5996 vmsvgaDestruct(pDevIns);
5997#endif
5998
5999#ifdef VBOX_WITH_HGSMI
6000 VBVADestroy(pThis);
6001#endif
6002
6003 /*
6004 * Free MM heap pointers.
6005 */
6006 if (pThis->pbVBEExtraData)
6007 {
6008 PDMDevHlpMMHeapFree(pDevIns, pThis->pbVBEExtraData);
6009 pThis->pbVBEExtraData = NULL;
6010 }
6011#endif /* VBE_NEW_DYN_LIST */
6012 if (pThis->pbVgaBios)
6013 {
6014 PDMDevHlpMMHeapFree(pDevIns, pThis->pbVgaBios);
6015 pThis->pbVgaBios = NULL;
6016 }
6017
6018 if (pThis->pszVgaBiosFile)
6019 {
6020 MMR3HeapFree(pThis->pszVgaBiosFile);
6021 pThis->pszVgaBiosFile = NULL;
6022 }
6023
6024 if (pThis->pszLogoFile)
6025 {
6026 MMR3HeapFree(pThis->pszLogoFile);
6027 pThis->pszLogoFile = NULL;
6028 }
6029
6030 if (pThis->pbLogo)
6031 {
6032 PDMDevHlpMMHeapFree(pDevIns, pThis->pbLogo);
6033 pThis->pbLogo = NULL;
6034 }
6035
6036 PDMR3CritSectDelete(&pThis->CritSectIRQ);
6037 PDMR3CritSectDelete(&pThis->CritSect);
6038 return VINF_SUCCESS;
6039}
6040
6041
6042/**
6043 * Adjust VBE mode information
6044 *
6045 * Depending on the configured VRAM size, certain parts of VBE mode
6046 * information must be updated.
6047 *
6048 * @param pThis The device instance data.
6049 * @param pMode The mode information structure.
6050 */
6051static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
6052{
6053 int maxPage;
6054 int bpl;
6055
6056
6057 /* For 4bpp modes, the planes are "stacked" on top of each other. */
6058 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
6059 /* The "number of image pages" is really the max page index... */
6060 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6061 Assert(maxPage >= 0);
6062 if (maxPage > 255)
6063 maxPage = 255; /* 8-bit value. */
6064 pMode->info.NumberOfImagePages = maxPage;
6065 pMode->info.LinNumberOfPages = maxPage;
6066}
6067
6068
6069/**
6070 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6071 */
6072static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6073{
6074
6075 static bool s_fExpandDone = false;
6076 int rc;
6077 unsigned i;
6078#ifdef VBE_NEW_DYN_LIST
6079 uint32_t cCustomModes;
6080 uint32_t cyReduction;
6081 uint32_t cbPitch;
6082 PVBEHEADER pVBEDataHdr;
6083 ModeInfoListItem *pCurMode;
6084 unsigned cb;
6085#endif
6086 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6087 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
6088 PVM pVM = PDMDevHlpGetVM(pDevIns);
6089
6090 Assert(iInstance == 0);
6091 Assert(pVM);
6092
6093 /*
6094 * Init static data.
6095 */
6096 if (!s_fExpandDone)
6097 {
6098 s_fExpandDone = true;
6099 vga_init_expand();
6100 }
6101
6102 /*
6103 * Validate configuration.
6104 */
6105 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
6106 "MonitorCount\0"
6107 "GCEnabled\0"
6108 "R0Enabled\0"
6109 "FadeIn\0"
6110 "FadeOut\0"
6111 "LogoTime\0"
6112 "LogoFile\0"
6113 "ShowBootMenu\0"
6114 "BiosRom\0"
6115 "RealRetrace\0"
6116 "CustomVideoModes\0"
6117 "HeightReduction\0"
6118 "CustomVideoMode1\0"
6119 "CustomVideoMode2\0"
6120 "CustomVideoMode3\0"
6121 "CustomVideoMode4\0"
6122 "CustomVideoMode5\0"
6123 "CustomVideoMode6\0"
6124 "CustomVideoMode7\0"
6125 "CustomVideoMode8\0"
6126 "CustomVideoMode9\0"
6127 "CustomVideoMode10\0"
6128 "CustomVideoMode11\0"
6129 "CustomVideoMode12\0"
6130 "CustomVideoMode13\0"
6131 "CustomVideoMode14\0"
6132 "CustomVideoMode15\0"
6133 "CustomVideoMode16\0"
6134 "MaxBiosXRes\0"
6135 "MaxBiosYRes\0"
6136#ifdef VBOX_WITH_VMSVGA
6137 "VMSVGAEnabled\0"
6138 "VMSVGAFifoSize\0"
6139#endif
6140#ifdef VBOX_WITH_VMSVGA3D
6141 "VMSVGA3dEnabled\0"
6142 "HostWindowId\0"
6143#endif
6144 "SuppressNewYearSplash\0"
6145 ))
6146 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
6147 N_("Invalid configuration for vga device"));
6148
6149 /*
6150 * Init state data.
6151 */
6152 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6153 AssertLogRelRCReturn(rc, rc);
6154 if (pThis->vram_size > VGA_VRAM_MAX)
6155 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6156 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6157 if (pThis->vram_size < VGA_VRAM_MIN)
6158 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6159 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6160 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6161 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6162 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6163
6164 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6165 AssertLogRelRCReturn(rc, rc);
6166
6167 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
6168 AssertLogRelRCReturn(rc, rc);
6169
6170 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
6171 AssertLogRelRCReturn(rc, rc);
6172 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
6173
6174#ifdef VBOX_WITH_VMSVGA
6175 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6176 AssertLogRelRCReturn(rc, rc);
6177 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6178
6179 rc = CFGMR3QueryU32Def(pCfg, "VMSVGAFifoSize", &pThis->svga.cbFIFO, VMSVGA_FIFO_SIZE);
6180 AssertLogRelRCReturn(rc, rc);
6181 AssertLogRelMsgReturn(pThis->svga.cbFIFO >= _128K, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6182 AssertLogRelMsgReturn(pThis->svga.cbFIFO <= _16M, ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_OUT_OF_RANGE);
6183 AssertLogRelMsgReturn(RT_IS_POWER_OF_TWO(pThis->svga.cbFIFO), ("cbFIFO=%#x\n", pThis->svga.cbFIFO), VERR_NOT_POWER_OF_TWO);
6184 pThis->svga.cbFIFOConfig = pThis->svga.cbFIFO;
6185 Log(("VMSVGA: VMSVGAFifoSize = %#x (%'u)\n", pThis->svga.cbFIFO, pThis->svga.cbFIFO));
6186#endif
6187#ifdef VBOX_WITH_VMSVGA3D
6188 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6189 AssertLogRelRCReturn(rc, rc);
6190 rc = CFGMR3QueryU64Def(pCfg, "HostWindowId", &pThis->svga.u64HostWindowId, 0);
6191 AssertLogRelRCReturn(rc, rc);
6192 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6193 Log(("VMSVGA: HostWindowId = 0x%x\n", pThis->svga.u64HostWindowId));
6194#endif
6195
6196 pThis->pDevInsR3 = pDevIns;
6197 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
6198 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6199
6200 vgaR3Reset(pDevIns);
6201
6202 /* The PCI devices configuration. */
6203#ifdef VBOX_WITH_VMSVGA
6204 if (pThis->fVMSVGAEnabled)
6205 {
6206 /* Extend our VGA device with VMWare SVGA functionality. */
6207 PCIDevSetVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6208 PCIDevSetDeviceId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6209 PCIDevSetSubSystemVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6210 PCIDevSetSubSystemId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6211 }
6212 else
6213 {
6214#endif /* VBOX_WITH_VMSVGA */
6215 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
6216 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
6217#ifdef VBOX_WITH_VMSVGA
6218 }
6219#endif
6220 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
6221 PCIDevSetClassBase( &pThis->Dev, 0x03);
6222 PCIDevSetHeaderType(&pThis->Dev, 0x00);
6223#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6224 PCIDevSetInterruptPin(&pThis->Dev, 1);
6225#endif
6226
6227 /* the interfaces. */
6228 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
6229
6230 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
6231 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
6232 pThis->IPort.pfnQueryVideoMode = vgaPortQueryVideoMode;
6233 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
6234 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
6235 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
6236 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
6237 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
6238 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
6239 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
6240#ifdef VBOX_WITH_VMSVGA
6241 pThis->IPort.pfnSetViewport = vmsvgaPortSetViewport;
6242#else
6243 pThis->IPort.pfnSetViewport = NULL;
6244#endif
6245 pThis->IPort.pfnSendModeHint = vbvaPortSendModeHint;
6246 pThis->IPort.pfnReportHostCursorCapabilities
6247 = vbvaPortReportHostCursorCapabilities;
6248 pThis->IPort.pfnReportHostCursorPosition
6249 = vbvaPortReportHostCursorPosition;
6250
6251#if defined(VBOX_WITH_HGSMI)
6252# if defined(VBOX_WITH_VIDEOHWACCEL)
6253 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaVHWACommandCompleteAsync;
6254# endif
6255#if defined(VBOX_WITH_CRHGSMI)
6256 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
6257 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
6258
6259 pThis->IVBVACallbacks.pfnCrCtlSubmit = vboxCmdVBVACmdHostCtl;
6260 pThis->IVBVACallbacks.pfnCrCtlSubmitSync = vboxCmdVBVACmdHostCtlSync;
6261# endif
6262#endif
6263
6264 pThis->ILeds.pfnQueryStatusLed = vgaPortQueryStatusLed;
6265
6266 RT_ZERO(pThis->Led3D);
6267 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6268
6269 /*
6270 * We use our own critical section to avoid unncessary pointer indirections
6271 * in interface methods (as well as for historical reasons).
6272 */
6273 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6274 AssertRCReturn(rc, rc);
6275 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6276 AssertRCReturn(rc, rc);
6277
6278 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6279 AssertRCReturn(rc, rc);
6280
6281 /*
6282 * PCI device registration.
6283 */
6284 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6285 if (RT_FAILURE(rc))
6286 return rc;
6287 /*AssertMsg(pThis->Dev.uDevFn == 16 || iInstance != 0, ("pThis->Dev.uDevFn=%d\n", pThis->Dev.uDevFn));*/
6288 if (pThis->Dev.uDevFn != 16 && iInstance == 0)
6289 Log(("!!WARNING!!: pThis->dev.uDevFn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.uDevFn));
6290
6291#ifdef VBOX_WITH_VMSVGA
6292 if (pThis->fVMSVGAEnabled)
6293 {
6294 /* Register the io command ports. */
6295 rc = PDMDevHlpPCIIORegionRegister (pDevIns, 0 /* iRegion */, 0x10, PCI_ADDRESS_SPACE_IO, vmsvgaR3IORegionMap);
6296 if (RT_FAILURE (rc))
6297 return rc;
6298 /* VMware's MetalKit doesn't like PCI_ADDRESS_SPACE_MEM_PREFETCH */
6299 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1 /* iRegion */, pThis->vram_size,
6300 PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vgaR3IORegionMap);
6301 if (RT_FAILURE(rc))
6302 return rc;
6303 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2 /* iRegion */, pThis->svga.cbFIFO,
6304 PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vmsvgaR3IORegionMap);
6305 if (RT_FAILURE(rc))
6306 return rc;
6307 pThis->Dev.pfnRegionLoadChangeHookR3 = vgaR3PciRegionLoadChangeHook;
6308 }
6309 else
6310#endif /* VBOX_WITH_VMSVGA */
6311 {
6312#ifdef VBOX_WITH_VMSVGA
6313 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
6314#else
6315 int iPCIRegionVRAM = 0;
6316#endif
6317 rc = PDMDevHlpPCIIORegionRegister(pDevIns, iPCIRegionVRAM, pThis->vram_size,
6318 PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6319 if (RT_FAILURE(rc))
6320 return rc;
6321 }
6322
6323 /*
6324 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
6325 */
6326#ifdef VBOX_WITH_VMSVGA
6327 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
6328
6329 if (pThis->fVMSVGAEnabled)
6330 {
6331 /*
6332 * Allocate and initialize the FIFO MMIO2 memory.
6333 */
6334 rc = PDMDevHlpMMIO2Register(pDevIns, &pThis->Dev, 2 /*iRegion*/, pThis->svga.cbFIFO,
6335 0 /*fFlags*/, (void **)&pThis->svga.pFIFOR3, "VMSVGA-FIFO");
6336 if (RT_FAILURE(rc))
6337 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6338 N_("Failed to allocate %u bytes of memory for the VMSVGA device"), pThis->svga.cbFIFO);
6339 pThis->svga.pFIFOR0 = (RTR0PTR)pThis->svga.pFIFOR3;
6340 }
6341#else
6342 int iPCIRegionVRAM = 0;
6343#endif
6344 rc = PDMDevHlpMMIO2Register(pDevIns, &pThis->Dev, iPCIRegionVRAM, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
6345 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
6346 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo @bugref{1865} Map parts into R0 or just use PGM access (Mac only). */
6347
6348 if (pThis->fGCEnabled)
6349 {
6350 RTRCPTR pRCMapping = 0;
6351 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, &pThis->Dev, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE,
6352 "VGA VRam", &pRCMapping);
6353 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6354 pThis->vram_ptrRC = pRCMapping;
6355#ifdef VBOX_WITH_VMSVGA
6356 /* Don't need a mapping in RC */
6357#endif
6358 }
6359
6360#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
6361 if (pThis->fR0Enabled)
6362 {
6363 RTR0PTR pR0Mapping = 0;
6364 rc = PDMDevHlpMMIO2MapKernel(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
6365 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6366 pThis->vram_ptrR0 = pR0Mapping;
6367# ifdef VBOX_WITH_VMSVGA
6368 if (pThis->fVMSVGAEnabled)
6369 {
6370 RTR0PTR pR0Mapping = 0;
6371 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 2 /* iRegion */, 0 /* off */, pThis->svga.cbFIFO, "VMSVGA-FIFO", &pR0Mapping);
6372 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pThis->svga.cbFIFO, rc), rc);
6373 pThis->svga.pFIFOR0 = pR0Mapping;
6374 }
6375# endif
6376 }
6377#endif
6378
6379 /*
6380 * Register access handler types.
6381 */
6382 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
6383 vgaLFBAccessHandler,
6384 g_DeviceVga.szR0Mod, "vgaLFBAccessHandler", "vgaLbfAccessPfHandler",
6385 g_DeviceVga.szRCMod, "vgaLFBAccessHandler", "vgaLbfAccessPfHandler",
6386 "VGA LFB", &pThis->hLfbAccessHandlerType);
6387 AssertRCReturn(rc, rc);
6388
6389
6390 /*
6391 * Register I/O ports.
6392 */
6393 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
6394 if (RT_FAILURE(rc))
6395 return rc;
6396 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
6397 if (RT_FAILURE(rc))
6398 return rc;
6399 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
6400 if (RT_FAILURE(rc))
6401 return rc;
6402 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
6403 if (RT_FAILURE(rc))
6404 return rc;
6405 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
6406 if (RT_FAILURE(rc))
6407 return rc;
6408#ifdef VBOX_WITH_HGSMI
6409 /* Use reserved VGA IO ports for HGSMI. */
6410 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
6411 if (RT_FAILURE(rc))
6412 return rc;
6413 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
6414 if (RT_FAILURE(rc))
6415 return rc;
6416#endif /* VBOX_WITH_HGSMI */
6417
6418#ifdef CONFIG_BOCHS_VBE
6419 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
6420 if (RT_FAILURE(rc))
6421 return rc;
6422 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
6423 if (RT_FAILURE(rc))
6424 return rc;
6425#endif /* CONFIG_BOCHS_VBE */
6426
6427 /* guest context extension */
6428 if (pThis->fGCEnabled)
6429 {
6430 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6431 if (RT_FAILURE(rc))
6432 return rc;
6433 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6434 if (RT_FAILURE(rc))
6435 return rc;
6436 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6437 if (RT_FAILURE(rc))
6438 return rc;
6439 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6440 if (RT_FAILURE(rc))
6441 return rc;
6442 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6443 if (RT_FAILURE(rc))
6444 return rc;
6445#ifdef CONFIG_BOCHS_VBE
6446 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6447 if (RT_FAILURE(rc))
6448 return rc;
6449 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6450 if (RT_FAILURE(rc))
6451 return rc;
6452#endif /* CONFIG_BOCHS_VBE */
6453 }
6454
6455 /* R0 context extension */
6456 if (pThis->fR0Enabled)
6457 {
6458 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6459 if (RT_FAILURE(rc))
6460 return rc;
6461 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6462 if (RT_FAILURE(rc))
6463 return rc;
6464 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6465 if (RT_FAILURE(rc))
6466 return rc;
6467 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6468 if (RT_FAILURE(rc))
6469 return rc;
6470 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6471 if (RT_FAILURE(rc))
6472 return rc;
6473#ifdef CONFIG_BOCHS_VBE
6474 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6475 if (RT_FAILURE(rc))
6476 return rc;
6477 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6478 if (RT_FAILURE(rc))
6479 return rc;
6480#endif /* CONFIG_BOCHS_VBE */
6481 }
6482
6483 /* vga mmio */
6484 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
6485 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
6486 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
6487 if (RT_FAILURE(rc))
6488 return rc;
6489 if (pThis->fGCEnabled)
6490 {
6491 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6492 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6493 if (RT_FAILURE(rc))
6494 return rc;
6495 }
6496 if (pThis->fR0Enabled)
6497 {
6498 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6499 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6500 if (RT_FAILURE(rc))
6501 return rc;
6502 }
6503
6504 /* vga bios */
6505 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6506 if (RT_FAILURE(rc))
6507 return rc;
6508 if (pThis->fR0Enabled)
6509 {
6510 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6511 if (RT_FAILURE(rc))
6512 return rc;
6513 }
6514
6515 /*
6516 * Get the VGA BIOS ROM file name.
6517 */
6518 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6519 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6520 {
6521 pThis->pszVgaBiosFile = NULL;
6522 rc = VINF_SUCCESS;
6523 }
6524 else if (RT_FAILURE(rc))
6525 return PDMDEV_SET_ERROR(pDevIns, rc,
6526 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6527 else if (!*pThis->pszVgaBiosFile)
6528 {
6529 MMR3HeapFree(pThis->pszVgaBiosFile);
6530 pThis->pszVgaBiosFile = NULL;
6531 }
6532
6533 /*
6534 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6535 */
6536 RTFILE FileVgaBios = NIL_RTFILE;
6537 if (pThis->pszVgaBiosFile)
6538 {
6539 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6540 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6541 if (RT_SUCCESS(rc))
6542 {
6543 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6544 if (RT_SUCCESS(rc))
6545 {
6546 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6547 || pThis->cbVgaBios > _64K
6548 || pThis->cbVgaBios < 16 * _1K)
6549 rc = VERR_TOO_MUCH_DATA;
6550 }
6551 }
6552 if (RT_FAILURE(rc))
6553 {
6554 /*
6555 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6556 */
6557 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6558 RTFileClose(FileVgaBios);
6559 FileVgaBios = NIL_RTFILE;
6560 MMR3HeapFree(pThis->pszVgaBiosFile);
6561 pThis->pszVgaBiosFile = NULL;
6562 }
6563 }
6564
6565 /*
6566 * Attempt to get the VGA BIOS ROM data from file.
6567 */
6568 if (pThis->pszVgaBiosFile)
6569 {
6570 /*
6571 * Allocate buffer for the VGA BIOS ROM data.
6572 */
6573 pThis->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6574 if (pThis->pbVgaBios)
6575 {
6576 rc = RTFileRead(FileVgaBios, pThis->pbVgaBios, pThis->cbVgaBios, NULL);
6577 if (RT_FAILURE(rc))
6578 {
6579 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6580 PDMDevHlpMMHeapFree(pDevIns, pThis->pbVgaBios);
6581 pThis->pbVgaBios = NULL;
6582 }
6583 rc = VINF_SUCCESS;
6584 }
6585 else
6586 rc = VERR_NO_MEMORY;
6587 }
6588 else
6589 pThis->pbVgaBios = NULL;
6590
6591 /* cleanup */
6592 if (FileVgaBios != NIL_RTFILE)
6593 RTFileClose(FileVgaBios);
6594
6595 /* If we were unable to get the data from file for whatever reason, fall
6596 back to the built-in ROM image. */
6597 const uint8_t *pbVgaBiosBinary;
6598 uint64_t cbVgaBiosBinary;
6599 uint32_t fFlags = 0;
6600 if (pThis->pbVgaBios == NULL)
6601 {
6602 CPUMMICROARCH enmMicroarch = pVM ? pVM->cpum.ro.GuestFeatures.enmMicroarch : kCpumMicroarch_Intel_P6;
6603 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6604 || enmMicroarch == kCpumMicroarch_Intel_80186
6605 || enmMicroarch == kCpumMicroarch_NEC_V20
6606 || enmMicroarch == kCpumMicroarch_NEC_V30)
6607 {
6608 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6609 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6610 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6611 }
6612 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6613 {
6614 pbVgaBiosBinary = g_abVgaBiosBinary286;
6615 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6616 LogRel(("VGA: Using the 286 BIOS image!\n"));
6617 }
6618 else
6619 {
6620 pbVgaBiosBinary = g_abVgaBiosBinary386;
6621 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6622 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6623 }
6624 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6625 }
6626 else
6627 {
6628 pbVgaBiosBinary = pThis->pbVgaBios;
6629 cbVgaBiosBinary = pThis->cbVgaBios;
6630 }
6631
6632 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6633 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6634 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6635 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6636 fFlags, "VGA BIOS");
6637 if (RT_FAILURE(rc))
6638 return rc;
6639
6640 /*
6641 * Saved state.
6642 */
6643 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6644 NULL, vgaR3LiveExec, NULL,
6645 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6646 NULL, vgaR3LoadExec, vgaR3LoadDone);
6647 if (RT_FAILURE(rc))
6648 return rc;
6649
6650 /*
6651 * Create the refresh timer.
6652 */
6653 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6654 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6655 "VGA Refresh Timer", &pThis->RefreshTimer);
6656 if (RT_FAILURE(rc))
6657 return rc;
6658
6659 /*
6660 * Attach to the display.
6661 */
6662 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6663 if (RT_FAILURE(rc))
6664 return rc;
6665
6666 /*
6667 * Initialize the retrace flag.
6668 */
6669 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6670 AssertLogRelRCReturn(rc, rc);
6671
6672#ifdef VBE_NEW_DYN_LIST
6673
6674 uint16_t maxBiosXRes;
6675 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6676 AssertLogRelRCReturn(rc, rc);
6677 uint16_t maxBiosYRes;
6678 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6679 AssertLogRelRCReturn(rc, rc);
6680
6681 /*
6682 * Compute buffer size for the VBE BIOS Extra Data.
6683 */
6684 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6685
6686 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6687 if (RT_SUCCESS(rc) && cyReduction)
6688 cb *= 2; /* Default mode list will be twice long */
6689 else
6690 cyReduction = 0;
6691
6692 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6693 if (RT_SUCCESS(rc) && cCustomModes)
6694 cb += sizeof(ModeInfoListItem) * cCustomModes;
6695 else
6696 cCustomModes = 0;
6697
6698 /*
6699 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6700 */
6701 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6702 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6703 pThis->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6704 if (!pThis->pbVBEExtraData)
6705 return VERR_NO_MEMORY;
6706
6707 pVBEDataHdr = (PVBEHEADER)pThis->pbVBEExtraData;
6708 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6709 pVBEDataHdr->cbData = cb;
6710
6711# ifndef VRAM_SIZE_FIX
6712 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6713 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6714# else /* VRAM_SIZE_FIX defined */
6715 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6716 for (i = 0; i < MODE_INFO_SIZE; i++)
6717 {
6718 uint32_t pixelWidth, reqSize;
6719 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6720 pixelWidth = 2;
6721 else
6722 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6723 reqSize = mode_info_list[i].info.XResolution
6724 * mode_info_list[i].info.YResolution
6725 * pixelWidth;
6726 if (reqSize >= pThis->vram_size)
6727 continue;
6728 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6729 || mode_info_list[i].info.YResolution > maxBiosYRes)
6730 continue;
6731 *pCurMode = mode_info_list[i];
6732 vgaAdjustModeInfo(pThis, pCurMode);
6733 pCurMode++;
6734 }
6735# endif /* VRAM_SIZE_FIX defined */
6736
6737 /*
6738 * Copy default modes with subtracted YResolution.
6739 */
6740 if (cyReduction)
6741 {
6742 ModeInfoListItem *pDefMode = mode_info_list;
6743 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6744# ifndef VRAM_SIZE_FIX
6745 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6746 {
6747 *pCurMode = *pDefMode;
6748 pCurMode->mode += 0x30;
6749 pCurMode->info.YResolution -= cyReduction;
6750 }
6751# else /* VRAM_SIZE_FIX defined */
6752 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6753 {
6754 uint32_t pixelWidth, reqSize;
6755 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6756 pixelWidth = 2;
6757 else
6758 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6759 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6760 if (reqSize >= pThis->vram_size)
6761 continue;
6762 if ( pDefMode->info.XResolution > maxBiosXRes
6763 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6764 continue;
6765 *pCurMode = *pDefMode;
6766 pCurMode->mode += 0x30;
6767 pCurMode->info.YResolution -= cyReduction;
6768 pCurMode++;
6769 }
6770# endif /* VRAM_SIZE_FIX defined */
6771 }
6772
6773
6774 /*
6775 * Add custom modes.
6776 */
6777 if (cCustomModes)
6778 {
6779 uint16_t u16CurMode = 0x160;
6780 for (i = 1; i <= cCustomModes; i++)
6781 {
6782 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6783 char *pszExtraData = NULL;
6784
6785 /* query and decode the custom mode string. */
6786 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6787 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6788 if (RT_SUCCESS(rc))
6789 {
6790 ModeInfoListItem *pDefMode = mode_info_list;
6791 unsigned int cx, cy, cBits, cParams, j;
6792 uint16_t u16DefMode;
6793
6794 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6795 if ( cParams != 3
6796 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6797 {
6798 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6799 return VERR_VGA_INVALID_CUSTOM_MODE;
6800 }
6801 cbPitch = calc_line_pitch(cBits, cx);
6802# ifdef VRAM_SIZE_FIX
6803 if (cy * cbPitch >= pThis->vram_size)
6804 {
6805 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",
6806 cx, cy, cBits, pThis->vram_size / _1M));
6807 return VERR_VGA_INVALID_CUSTOM_MODE;
6808 }
6809# endif /* VRAM_SIZE_FIX defined */
6810 MMR3HeapFree(pszExtraData);
6811
6812 /* Use defaults from max@bpp mode. */
6813 switch (cBits)
6814 {
6815 case 8:
6816 u16DefMode = VBE_VESA_MODE_1024X768X8;
6817 break;
6818
6819 case 16:
6820 u16DefMode = VBE_VESA_MODE_1024X768X565;
6821 break;
6822
6823 case 24:
6824 u16DefMode = VBE_VESA_MODE_1024X768X888;
6825 break;
6826
6827 case 32:
6828 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6829 break;
6830
6831 default: /* gcc, shut up! */
6832 AssertMsgFailed(("gone postal!\n"));
6833 continue;
6834 }
6835
6836 /* mode_info_list is not terminated */
6837 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6838 pDefMode++;
6839 Assert(j < MODE_INFO_SIZE);
6840
6841 *pCurMode = *pDefMode;
6842 pCurMode->mode = u16CurMode++;
6843
6844 /* adjust defaults */
6845 pCurMode->info.XResolution = cx;
6846 pCurMode->info.YResolution = cy;
6847 pCurMode->info.BytesPerScanLine = cbPitch;
6848 pCurMode->info.LinBytesPerScanLine = cbPitch;
6849 vgaAdjustModeInfo(pThis, pCurMode);
6850
6851 /* commit it */
6852 pCurMode++;
6853 }
6854 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6855 {
6856 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6857 return rc;
6858 }
6859 } /* foreach custom mode key */
6860 }
6861
6862 /*
6863 * Add the "End of list" mode.
6864 */
6865 memset(pCurMode, 0, sizeof(*pCurMode));
6866 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6867
6868 /*
6869 * Register I/O Port for the VBE BIOS Extra Data.
6870 */
6871 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6872 if (RT_FAILURE(rc))
6873 return rc;
6874#endif /* VBE_NEW_DYN_LIST */
6875
6876 /*
6877 * Register I/O Port for the BIOS Logo.
6878 */
6879 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6880 if (RT_FAILURE(rc))
6881 return rc;
6882
6883 /*
6884 * Register debugger info callbacks.
6885 */
6886 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6887 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6888 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6889 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6890 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6891 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6892 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaInfoPlanar);
6893 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6894 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6895
6896 /*
6897 * Construct the logo header.
6898 */
6899 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6900
6901 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6902 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6903 LogoHdr.fu8FadeIn = 1;
6904 else if (RT_FAILURE(rc))
6905 return PDMDEV_SET_ERROR(pDevIns, rc,
6906 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6907
6908 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6909 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6910 LogoHdr.fu8FadeOut = 1;
6911 else if (RT_FAILURE(rc))
6912 return PDMDEV_SET_ERROR(pDevIns, rc,
6913 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6914
6915 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6916 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6917 LogoHdr.u16LogoMillies = 0;
6918 else if (RT_FAILURE(rc))
6919 return PDMDEV_SET_ERROR(pDevIns, rc,
6920 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6921
6922 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6923 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6924 LogoHdr.fu8ShowBootMenu = 0;
6925 else if (RT_FAILURE(rc))
6926 return PDMDEV_SET_ERROR(pDevIns, rc,
6927 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6928
6929#if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
6930 /* Disable the logo abd menu if all default settings. */
6931 if ( LogoHdr.fu8FadeIn
6932 && LogoHdr.fu8FadeOut
6933 && LogoHdr.u16LogoMillies == 0
6934 && LogoHdr.fu8ShowBootMenu == 2)
6935 {
6936 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
6937 LogoHdr.u16LogoMillies = 500;
6938 }
6939#endif
6940
6941 /* Delay the logo a little bit */
6942 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6943 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6944
6945 /*
6946 * Get the Logo file name.
6947 */
6948 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6949 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6950 pThis->pszLogoFile = NULL;
6951 else if (RT_FAILURE(rc))
6952 return PDMDEV_SET_ERROR(pDevIns, rc,
6953 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6954 else if (!*pThis->pszLogoFile)
6955 {
6956 MMR3HeapFree(pThis->pszLogoFile);
6957 pThis->pszLogoFile = NULL;
6958 }
6959
6960 /*
6961 * Determine the logo size, open any specified logo file in the process.
6962 */
6963 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6964 RTFILE FileLogo = NIL_RTFILE;
6965 if (pThis->pszLogoFile)
6966 {
6967 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6968 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6969 if (RT_SUCCESS(rc))
6970 {
6971 uint64_t cbFile;
6972 rc = RTFileGetSize(FileLogo, &cbFile);
6973 if (RT_SUCCESS(rc))
6974 {
6975 if (cbFile > 0 && cbFile < 32*_1M)
6976 LogoHdr.cbLogo = (uint32_t)cbFile;
6977 else
6978 rc = VERR_TOO_MUCH_DATA;
6979 }
6980 }
6981 if (RT_FAILURE(rc))
6982 {
6983 /*
6984 * Ignore failure and fall back to the default logo.
6985 */
6986 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6987 if (FileLogo != NIL_RTFILE)
6988 RTFileClose(FileLogo);
6989 FileLogo = NIL_RTFILE;
6990 MMR3HeapFree(pThis->pszLogoFile);
6991 pThis->pszLogoFile = NULL;
6992 }
6993 }
6994
6995 /*
6996 * Disable graphic splash screen if it doesn't fit into VRAM.
6997 */
6998 if (pThis->vram_size < LOGO_MAX_SIZE)
6999 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
7000
7001 /*
7002 * Allocate buffer for the logo data.
7003 * Let us fall back to default logo on read failure.
7004 */
7005 pThis->cbLogo = LogoHdr.cbLogo;
7006 if (g_cbVgaDefBiosLogo)
7007 pThis->cbLogo = g_cbVgaDefBiosLogo;
7008#ifndef VBOX_OSE
7009 if (g_cbVgaDefBiosLogoNY)
7010 pThis->cbLogo = g_cbVgaDefBiosLogoNY;
7011#endif
7012 pThis->cbLogo += sizeof(LogoHdr);
7013
7014 pThis->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbLogo);
7015 if (pThis->pbLogo)
7016 {
7017 /*
7018 * Write the logo header.
7019 */
7020 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pbLogo;
7021 *pLogoHdr = LogoHdr;
7022
7023 /*
7024 * Write the logo bitmap.
7025 */
7026 if (pThis->pszLogoFile)
7027 {
7028 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
7029 if (RT_SUCCESS(rc))
7030 rc = vbeParseBitmap(pThis);
7031 if (RT_FAILURE(rc))
7032 {
7033 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
7034 rc, pThis->pszLogoFile));
7035 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
7036 }
7037 }
7038 if ( !pThis->pszLogoFile
7039 || RT_FAILURE(rc))
7040 {
7041#ifndef VBOX_OSE
7042 RTTIMESPEC Now;
7043 RTTimeLocalNow(&Now);
7044 RTTIME T;
7045 RTTimeLocalExplode(&T, &Now);
7046 bool fSuppressNewYearSplash = false;
7047 rc = CFGMR3QueryBoolDef(pCfg, "SuppressNewYearSplash", &fSuppressNewYearSplash, true);
7048 if ( !fSuppressNewYearSplash
7049 && (T.u16YearDay > 353 || T.u16YearDay < 10))
7050 {
7051 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogoNY;
7052 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogoNY, LogoHdr.cbLogo);
7053 pThis->fBootMenuInverse = true;
7054 }
7055 else
7056#endif
7057 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
7058 rc = vbeParseBitmap(pThis);
7059 if (RT_FAILURE(rc))
7060 AssertReleaseMsgFailed(("Parsing of internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
7061 }
7062
7063 rc = VINF_SUCCESS;
7064 }
7065 else
7066 rc = VERR_NO_MEMORY;
7067
7068 /*
7069 * Cleanup.
7070 */
7071 if (FileLogo != NIL_RTFILE)
7072 RTFileClose(FileLogo);
7073
7074#ifdef VBOX_WITH_HGSMI
7075 VBVAInit (pThis);
7076#endif /* VBOX_WITH_HGSMI */
7077
7078#ifdef VBOX_WITH_VDMA
7079 if (rc == VINF_SUCCESS)
7080 {
7081 rc = vboxVDMAConstruct(pThis, 1024);
7082 AssertRC(rc);
7083 }
7084#endif
7085
7086#ifdef VBOX_WITH_VMSVGA
7087 if ( rc == VINF_SUCCESS
7088 && pThis->fVMSVGAEnabled)
7089 {
7090 rc = vmsvgaInit(pDevIns);
7091 }
7092#endif
7093
7094 /*
7095 * Statistics.
7096 */
7097 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7098 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
7099 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7100 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
7101 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
7102 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
7103
7104 /* Init latched access mask. */
7105 pThis->uMaskLatchAccess = 0x3ff;
7106
7107 if (RT_SUCCESS(rc))
7108 {
7109 PPDMIBASE pBase;
7110 /*
7111 * Attach status driver (optional).
7112 */
7113 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
7114 if (RT_SUCCESS(rc))
7115 {
7116 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7117 pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
7118 }
7119 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7120 {
7121 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7122 rc = VINF_SUCCESS;
7123 }
7124 else
7125 {
7126 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7127 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7128 }
7129 }
7130 return rc;
7131}
7132
7133static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
7134{
7135 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
7136#ifdef VBOX_WITH_VMSVGA
7137 vmsvgaR3PowerOn(pDevIns);
7138#endif
7139 VBVAOnResume(pThis);
7140}
7141
7142static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
7143{
7144 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
7145 VBVAOnResume(pThis);
7146}
7147
7148/**
7149 * The device registration structure.
7150 */
7151const PDMDEVREG g_DeviceVga =
7152{
7153 /* u32Version */
7154 PDM_DEVREG_VERSION,
7155 /* szName */
7156 "vga",
7157 /* szRCMod */
7158 "VBoxDDRC.rc",
7159 /* szR0Mod */
7160 "VBoxDDR0.r0",
7161 /* pszDescription */
7162 "VGA Adaptor with VESA extensions.",
7163 /* fFlags */
7164 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
7165 /* fClass */
7166 PDM_DEVREG_CLASS_GRAPHICS,
7167 /* cMaxInstances */
7168 1,
7169 /* cbInstance */
7170 sizeof(VGASTATE),
7171 /* pfnConstruct */
7172 vgaR3Construct,
7173 /* pfnDestruct */
7174 vgaR3Destruct,
7175 /* pfnRelocate */
7176 vgaR3Relocate,
7177 /* pfnMemSetup */
7178 NULL,
7179 /* pfnPowerOn */
7180 vgaR3PowerOn,
7181 /* pfnReset */
7182 vgaR3Reset,
7183 /* pfnSuspend */
7184 NULL,
7185 /* pfnResume */
7186 vgaR3Resume,
7187 /* pfnAttach */
7188 vgaAttach,
7189 /* pfnDetach */
7190 vgaDetach,
7191 /* pfnQueryInterface */
7192 NULL,
7193 /* pfnInitComplete */
7194 NULL,
7195 /* pfnPowerOff */
7196 NULL,
7197 /* pfnSoftReset */
7198 NULL,
7199 /* u32VersionEnd */
7200 PDM_DEVREG_VERSION
7201};
7202
7203#endif /* !IN_RING3 */
7204#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7205
7206/*
7207 * Local Variables:
7208 * nuke-trailing-whitespace-p:nil
7209 * End:
7210 */
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