VirtualBox

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

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

bugref:8282: Additions/linux: submit DRM driver to the Linux kernel: move all graphics device-related header files to a separate sub-directory and add that to the include path where they are needed. The intention is too be able to remove the VBox/ include folder in the DRM driver package.

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

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