VirtualBox

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

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

Devices: doxygen fixes

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

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