VirtualBox

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

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

PCI,Devices: Changed range size in FNPCIIOREGIONMAP from uint32_t to RTGCPHYS.

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