VirtualBox

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

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

Devices: warnings

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

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