VirtualBox

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

Last change on this file since 63218 was 63218, checked in by vboxsync, 9 years ago

Devices: warnings (gcc)

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