VirtualBox

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

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

warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 240.5 KB
Line 
1/* $Id: DevVGA.cpp 62951 2016-08-04 07:21:13Z 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 * @callback_method_impl{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 * @callback_method_impl{FNSSMDEVSAVEPREP}
5511 */
5512static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5513{
5514#ifdef VBOX_WITH_VIDEOHWACCEL
5515 RT_NOREF(pSSM);
5516 return vboxVBVASaveStatePrep(pDevIns);
5517#else
5518 RT_NOREF(pDevIns, pSSM);
5519 return VINF_SUCCESS;
5520#endif
5521}
5522
5523/**
5524 * @callback_method_impl{FNSSMDEVSAVEDONE}
5525 */
5526static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5527{
5528#ifdef VBOX_WITH_VIDEOHWACCEL
5529 RT_NOREF(pSSM);
5530 return vboxVBVASaveStateDone(pDevIns);
5531#else
5532 RT_NOREF(pDevIns, pSSM);
5533 return VINF_SUCCESS;
5534#endif
5535}
5536
5537/**
5538 * @callback_method_impl{FNSSMDEVSAVEEXEC}
5539 */
5540static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5541{
5542 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5543
5544#ifdef VBOX_WITH_VDMA
5545 vboxVDMASaveStateExecPrep(pThis->pVdma);
5546#endif
5547
5548 vgaR3SaveConfig(pThis, pSSM);
5549 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5550
5551 VGA_SAVED_STATE_PUT_MARKER(pSSM, 1);
5552#ifdef VBOX_WITH_HGSMI
5553 SSMR3PutBool(pSSM, true);
5554 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5555#else
5556 int rc = SSMR3PutBool(pSSM, false);
5557#endif
5558
5559 AssertRCReturn(rc, rc);
5560
5561 VGA_SAVED_STATE_PUT_MARKER(pSSM, 3);
5562#ifdef VBOX_WITH_VDMA
5563 rc = SSMR3PutU32(pSSM, 1);
5564 AssertRCReturn(rc, rc);
5565 rc = vboxVDMASaveStateExecPerform(pThis->pVdma, pSSM);
5566#else
5567 rc = SSMR3PutU32(pSSM, 0);
5568#endif
5569 AssertRCReturn(rc, rc);
5570
5571#ifdef VBOX_WITH_VDMA
5572 vboxVDMASaveStateExecDone(pThis->pVdma);
5573#endif
5574
5575 VGA_SAVED_STATE_PUT_MARKER(pSSM, 5);
5576#ifdef VBOX_WITH_VMSVGA
5577 if (pThis->fVMSVGAEnabled)
5578 {
5579 rc = vmsvgaSaveExec(pDevIns, pSSM);
5580 AssertRCReturn(rc, rc);
5581 }
5582#endif
5583 VGA_SAVED_STATE_PUT_MARKER(pSSM, 6);
5584
5585 return rc;
5586}
5587
5588
5589/**
5590 * @copydoc FNSSMDEVSAVEEXEC
5591 */
5592static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5593{
5594 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5595 int rc;
5596
5597 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5598 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5599
5600 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5601 {
5602 /* Check the config */
5603 uint32_t cbVRam;
5604 rc = SSMR3GetU32(pSSM, &cbVRam);
5605 AssertRCReturn(rc, rc);
5606 if (pThis->vram_size != cbVRam)
5607 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5608
5609 uint32_t cMonitors;
5610 rc = SSMR3GetU32(pSSM, &cMonitors);
5611 AssertRCReturn(rc, rc);
5612 if (pThis->cMonitors != cMonitors)
5613 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5614 }
5615
5616 if (uPass == SSM_PASS_FINAL)
5617 {
5618 rc = vga_load(pSSM, pThis, uVersion);
5619 if (RT_FAILURE(rc))
5620 return rc;
5621
5622 /*
5623 * Restore the HGSMI state, if present.
5624 */
5625 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 1);
5626 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5627 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5628 {
5629 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5630 AssertRCReturn(rc, rc);
5631 }
5632 if (fWithHgsmi)
5633 {
5634#ifdef VBOX_WITH_HGSMI
5635 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5636 AssertRCReturn(rc, rc);
5637#else
5638 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5639#endif
5640 }
5641
5642 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 3);
5643 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5644 {
5645 uint32_t u32;
5646 rc = SSMR3GetU32(pSSM, &u32);
5647 if (u32)
5648 {
5649#ifdef VBOX_WITH_VDMA
5650 if (u32 == 1)
5651 {
5652 rc = vboxVDMASaveLoadExecPerform(pThis->pVdma, pSSM, uVersion);
5653 AssertRCReturn(rc, rc);
5654 }
5655 else
5656#endif
5657 {
5658 LogRel(("invalid CmdVbva version info\n"));
5659 return VERR_VERSION_MISMATCH;
5660 }
5661 }
5662 }
5663
5664 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 5);
5665#ifdef VBOX_WITH_VMSVGA
5666 if (pThis->fVMSVGAEnabled)
5667 {
5668 rc = vmsvgaLoadExec(pDevIns, pSSM, uVersion, uPass);
5669 AssertRCReturn(rc, rc);
5670 }
5671#endif
5672 VGA_SAVED_STATE_GET_MARKER_RETURN_ON_MISMATCH(pSSM, uVersion, 6);
5673 }
5674 return VINF_SUCCESS;
5675}
5676
5677
5678/**
5679 * @copydoc FNSSMDEVLOADDONE
5680 */
5681static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5682{
5683 RT_NOREF(pSSM);
5684 int rc = VINF_SUCCESS;
5685
5686#ifdef VBOX_WITH_HGSMI
5687 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5688 rc = vboxVBVALoadStateDone(pDevIns);
5689 AssertRCReturn(rc, rc);
5690# ifdef VBOX_WITH_VDMA
5691 rc = vboxVDMASaveLoadDone(pThis->pVdma);
5692 AssertRCReturn(rc, rc);
5693# endif
5694 /* Now update the current VBVA state which depends on VBE registers. vboxVBVALoadStateDone cleared the state. */
5695 VBVAOnVBEChanged(pThis);
5696#endif
5697#ifdef VBOX_WITH_VMSVGA
5698 if (pThis->fVMSVGAEnabled)
5699 {
5700 rc = vmsvgaLoadDone(pDevIns);
5701 AssertRCReturn(rc, rc);
5702 }
5703#endif
5704 return rc;
5705}
5706
5707
5708/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5709
5710/**
5711 * @interface_method_impl{PDMDEVREG,pfnReset}
5712 */
5713static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5714{
5715 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5716 char *pchStart;
5717 char *pchEnd;
5718 LogFlow(("vgaReset\n"));
5719
5720 if (pThis->pVdma)
5721 vboxVDMAReset(pThis->pVdma);
5722
5723#ifdef VBOX_WITH_VMSVGA
5724 if (pThis->fVMSVGAEnabled)
5725 vmsvgaReset(pDevIns);
5726#endif
5727
5728#ifdef VBOX_WITH_HGSMI
5729 VBVAReset(pThis);
5730#endif /* VBOX_WITH_HGSMI */
5731
5732
5733 /* Clear the VRAM ourselves. */
5734 if (pThis->vram_ptrR3 && pThis->vram_size)
5735 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5736
5737 /*
5738 * Zero most of it.
5739 *
5740 * Unlike vga_reset we're leaving out a few members which we believe
5741 * must remain unchanged....
5742 */
5743 /* 1st part. */
5744 pchStart = (char *)&pThis->latch;
5745 pchEnd = (char *)&pThis->invalidated_y_table;
5746 memset(pchStart, 0, pchEnd - pchStart);
5747
5748 /* 2nd part. */
5749 pchStart = (char *)&pThis->last_palette;
5750 pchEnd = (char *)&pThis->u32Marker;
5751 memset(pchStart, 0, pchEnd - pchStart);
5752
5753
5754 /*
5755 * Restore and re-init some bits.
5756 */
5757 pThis->get_bpp = vga_get_bpp;
5758 pThis->get_offsets = vga_get_offsets;
5759 pThis->get_resolution = vga_get_resolution;
5760 pThis->graphic_mode = -1; /* Force full update. */
5761#ifdef CONFIG_BOCHS_VBE
5762 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5763 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5764 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
5765 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5766#endif /* CONFIG_BOCHS_VBE */
5767
5768 /*
5769 * Reset the LBF mapping.
5770 */
5771 pThis->fLFBUpdated = false;
5772 if ( ( pThis->fGCEnabled
5773 || pThis->fR0Enabled)
5774 && pThis->GCPhysVRAM
5775 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
5776 {
5777 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5778 AssertRC(rc);
5779 }
5780 if (pThis->fRemappedVGA)
5781 {
5782 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5783 pThis->fRemappedVGA = false;
5784 }
5785
5786 /*
5787 * Reset the logo data.
5788 */
5789 pThis->LogoCommand = LOGO_CMD_NOP;
5790 pThis->offLogoData = 0;
5791
5792 /* notify port handler */
5793 if (pThis->pDrv)
5794 {
5795 PDMCritSectLeave(&pThis->CritSect); /* hack around lock order issue. */
5796 pThis->pDrv->pfnReset(pThis->pDrv);
5797 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
5798 }
5799
5800 /* Reset latched access mask. */
5801 pThis->uMaskLatchAccess = 0x3ff;
5802 pThis->cLatchAccesses = 0;
5803 pThis->u64LastLatchedAccess = 0;
5804 pThis->iMask = 0;
5805
5806 /* Reset retrace emulation. */
5807 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5808}
5809
5810
5811/**
5812 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5813 */
5814static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5815{
5816 if (offDelta)
5817 {
5818 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5819 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5820
5821 pThis->vram_ptrRC += offDelta;
5822 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5823 }
5824}
5825
5826
5827/**
5828 * @interface_method_impl{PDMDEVREG,pfnAttach}
5829 *
5830 * This is like plugging in the monitor after turning on the PC.
5831 */
5832static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5833{
5834 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5835
5836 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5837 ("VGA device does not support hotplugging\n"),
5838 VERR_INVALID_PARAMETER);
5839
5840 switch (iLUN)
5841 {
5842 /* LUN #0: Display port. */
5843 case 0:
5844 {
5845 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5846 if (RT_SUCCESS(rc))
5847 {
5848 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5849 if (pThis->pDrv)
5850 {
5851 /* pThis->pDrv->pbData can be NULL when there is no framebuffer. */
5852 if ( pThis->pDrv->pfnRefresh
5853 && pThis->pDrv->pfnResize
5854 && pThis->pDrv->pfnUpdateRect)
5855 rc = VINF_SUCCESS;
5856 else
5857 {
5858 Assert(pThis->pDrv->pfnRefresh);
5859 Assert(pThis->pDrv->pfnResize);
5860 Assert(pThis->pDrv->pfnUpdateRect);
5861 pThis->pDrv = NULL;
5862 pThis->pDrvBase = NULL;
5863 rc = VERR_INTERNAL_ERROR;
5864 }
5865#ifdef VBOX_WITH_VIDEOHWACCEL
5866 if(rc == VINF_SUCCESS)
5867 {
5868 rc = vbvaVHWAConstruct(pThis);
5869 if (rc != VERR_NOT_IMPLEMENTED)
5870 AssertRC(rc);
5871 }
5872#endif
5873 }
5874 else
5875 {
5876 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5877 pThis->pDrvBase = NULL;
5878 rc = VERR_PDM_MISSING_INTERFACE;
5879 }
5880 }
5881 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5882 {
5883 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5884 rc = VINF_SUCCESS;
5885 }
5886 else
5887 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5888 return rc;
5889 }
5890
5891 default:
5892 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5893 return VERR_PDM_NO_SUCH_LUN;
5894 }
5895}
5896
5897
5898/**
5899 * @interface_method_impl{PDMDEVREG,pfnDetach}
5900 *
5901 * This is like unplugging the monitor while the PC is still running.
5902 */
5903static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5904{
5905 RT_NOREF1(fFlags);
5906 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5907 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG, ("VGA device does not support hotplugging\n"));
5908
5909 /*
5910 * Reset the interfaces and update the controller state.
5911 */
5912 switch (iLUN)
5913 {
5914 /* LUN #0: Display port. */
5915 case 0:
5916 pThis->pDrv = NULL;
5917 pThis->pDrvBase = NULL;
5918 break;
5919
5920 default:
5921 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5922 break;
5923 }
5924}
5925
5926
5927/**
5928 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5929 */
5930static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5931{
5932 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5933
5934#ifdef VBE_NEW_DYN_LIST
5935 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5936 LogFlow(("vgaR3Destruct:\n"));
5937
5938# ifdef VBOX_WITH_VDMA
5939 if (pThis->pVdma)
5940 vboxVDMADestruct(pThis->pVdma);
5941# endif
5942
5943#ifdef VBOX_WITH_VMSVGA
5944 if (pThis->fVMSVGAEnabled)
5945 vmsvgaDestruct(pDevIns);
5946#endif
5947
5948 /*
5949 * Free MM heap pointers.
5950 */
5951 if (pThis->pbVBEExtraData)
5952 {
5953 MMR3HeapFree(pThis->pbVBEExtraData);
5954 pThis->pbVBEExtraData = NULL;
5955 }
5956#endif /* VBE_NEW_DYN_LIST */
5957 if (pThis->pbVgaBios)
5958 {
5959 MMR3HeapFree(pThis->pbVgaBios);
5960 pThis->pbVgaBios = NULL;
5961 }
5962
5963 if (pThis->pszVgaBiosFile)
5964 {
5965 MMR3HeapFree(pThis->pszVgaBiosFile);
5966 pThis->pszVgaBiosFile = NULL;
5967 }
5968
5969 if (pThis->pszLogoFile)
5970 {
5971 MMR3HeapFree(pThis->pszLogoFile);
5972 pThis->pszLogoFile = NULL;
5973 }
5974
5975 PDMR3CritSectDelete(&pThis->CritSectIRQ);
5976 PDMR3CritSectDelete(&pThis->CritSect);
5977 return VINF_SUCCESS;
5978}
5979
5980
5981/**
5982 * Adjust VBE mode information
5983 *
5984 * Depending on the configured VRAM size, certain parts of VBE mode
5985 * information must be updated.
5986 *
5987 * @param pThis The device instance data.
5988 * @param pMode The mode information structure.
5989 */
5990static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
5991{
5992 int maxPage;
5993 int bpl;
5994
5995
5996 /* For 4bpp modes, the planes are "stacked" on top of each other. */
5997 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
5998 /* The "number of image pages" is really the max page index... */
5999 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
6000 Assert(maxPage >= 0);
6001 if (maxPage > 255)
6002 maxPage = 255; /* 8-bit value. */
6003 pMode->info.NumberOfImagePages = maxPage;
6004 pMode->info.LinNumberOfPages = maxPage;
6005}
6006
6007
6008/**
6009 * @interface_method_impl{PDMDEVREG,pfnConstruct}
6010 */
6011static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
6012{
6013
6014 static bool s_fExpandDone = false;
6015 int rc;
6016 unsigned i;
6017#ifdef VBE_NEW_DYN_LIST
6018 uint32_t cCustomModes;
6019 uint32_t cyReduction;
6020 uint32_t cbPitch;
6021 PVBEHEADER pVBEDataHdr;
6022 ModeInfoListItem *pCurMode;
6023 unsigned cb;
6024#endif
6025 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
6026 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
6027 PVM pVM = PDMDevHlpGetVM(pDevIns);
6028
6029 Assert(iInstance == 0);
6030 Assert(pVM);
6031
6032 /*
6033 * Init static data.
6034 */
6035 if (!s_fExpandDone)
6036 {
6037 s_fExpandDone = true;
6038 vga_init_expand();
6039 }
6040
6041 /*
6042 * Validate configuration.
6043 */
6044 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
6045 "MonitorCount\0"
6046 "GCEnabled\0"
6047 "R0Enabled\0"
6048 "FadeIn\0"
6049 "FadeOut\0"
6050 "LogoTime\0"
6051 "LogoFile\0"
6052 "ShowBootMenu\0"
6053 "BiosRom\0"
6054 "RealRetrace\0"
6055 "CustomVideoModes\0"
6056 "HeightReduction\0"
6057 "CustomVideoMode1\0"
6058 "CustomVideoMode2\0"
6059 "CustomVideoMode3\0"
6060 "CustomVideoMode4\0"
6061 "CustomVideoMode5\0"
6062 "CustomVideoMode6\0"
6063 "CustomVideoMode7\0"
6064 "CustomVideoMode8\0"
6065 "CustomVideoMode9\0"
6066 "CustomVideoMode10\0"
6067 "CustomVideoMode11\0"
6068 "CustomVideoMode12\0"
6069 "CustomVideoMode13\0"
6070 "CustomVideoMode14\0"
6071 "CustomVideoMode15\0"
6072 "CustomVideoMode16\0"
6073 "MaxBiosXRes\0"
6074 "MaxBiosYRes\0"
6075#ifdef VBOX_WITH_VMSVGA
6076 "VMSVGAEnabled\0"
6077#endif
6078#ifdef VBOX_WITH_VMSVGA3D
6079 "VMSVGA3dEnabled\0"
6080 "HostWindowId\0"
6081#endif
6082 ))
6083 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
6084 N_("Invalid configuration for vga device"));
6085
6086 /*
6087 * Init state data.
6088 */
6089 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
6090 AssertLogRelRCReturn(rc, rc);
6091 if (pThis->vram_size > VGA_VRAM_MAX)
6092 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6093 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
6094 if (pThis->vram_size < VGA_VRAM_MIN)
6095 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6096 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
6097 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
6098 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
6099 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
6100
6101 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
6102 AssertLogRelRCReturn(rc, rc);
6103
6104 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
6105 AssertLogRelRCReturn(rc, rc);
6106
6107 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
6108 AssertLogRelRCReturn(rc, rc);
6109 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
6110
6111#ifdef VBOX_WITH_VMSVGA
6112 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
6113 AssertLogRelRCReturn(rc, rc);
6114 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
6115#endif
6116#ifdef VBOX_WITH_VMSVGA3D
6117 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
6118 AssertLogRelRCReturn(rc, rc);
6119 rc = CFGMR3QueryU64Def(pCfg, "HostWindowId", &pThis->svga.u64HostWindowId, 0);
6120 AssertLogRelRCReturn(rc, rc);
6121 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
6122 Log(("VMSVGA: HostWindowId = 0x%x\n", pThis->svga.u64HostWindowId));
6123#endif
6124
6125 pThis->pDevInsR3 = pDevIns;
6126 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
6127 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
6128
6129 vgaR3Reset(pDevIns);
6130
6131 /* The PCI devices configuration. */
6132#ifdef VBOX_WITH_VMSVGA
6133 if (pThis->fVMSVGAEnabled)
6134 {
6135 /* Extend our VGA device with VMWare SVGA functionality. */
6136 PCIDevSetVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6137 PCIDevSetDeviceId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6138 PCIDevSetSubSystemVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
6139 PCIDevSetSubSystemId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
6140 }
6141 else
6142 {
6143#endif /* VBOX_WITH_VMSVGA */
6144 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
6145 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
6146#ifdef VBOX_WITH_VMSVGA
6147 }
6148#endif
6149 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
6150 PCIDevSetClassBase( &pThis->Dev, 0x03);
6151 PCIDevSetHeaderType(&pThis->Dev, 0x00);
6152#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6153 PCIDevSetInterruptPin(&pThis->Dev, 1);
6154#endif
6155
6156 /* the interfaces. */
6157 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
6158
6159 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
6160 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
6161 pThis->IPort.pfnQueryVideoMode = vgaPortQueryVideoMode;
6162 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
6163 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
6164 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
6165 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
6166 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
6167 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
6168 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
6169#ifdef VBOX_WITH_VMSVGA
6170 pThis->IPort.pfnSetViewport = vmsvgaPortSetViewport;
6171#else
6172 pThis->IPort.pfnSetViewport = NULL;
6173#endif
6174 pThis->IPort.pfnSendModeHint = vbvaPortSendModeHint;
6175 pThis->IPort.pfnReportHostCursorCapabilities
6176 = vbvaPortReportHostCursorCapabilities;
6177 pThis->IPort.pfnReportHostCursorPosition
6178 = vbvaPortReportHostCursorPosition;
6179
6180#if defined(VBOX_WITH_HGSMI)
6181# if defined(VBOX_WITH_VIDEOHWACCEL)
6182 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaVHWACommandCompleteAsync;
6183# endif
6184#if defined(VBOX_WITH_CRHGSMI)
6185 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
6186 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
6187
6188 pThis->IVBVACallbacks.pfnCrCtlSubmit = vboxCmdVBVACmdHostCtl;
6189 pThis->IVBVACallbacks.pfnCrCtlSubmitSync = vboxCmdVBVACmdHostCtlSync;
6190# endif
6191#endif
6192
6193 pThis->ILeds.pfnQueryStatusLed = vgaPortQueryStatusLed;
6194
6195 RT_ZERO(pThis->Led3D);
6196 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6197
6198 /*
6199 * We use our own critical section to avoid unncessary pointer indirections
6200 * in interface methods (as well as for historical reasons).
6201 */
6202 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6203 AssertRCReturn(rc, rc);
6204 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6205 AssertRCReturn(rc, rc);
6206
6207 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSectIRQ, RT_SRC_POS, "VGA#%u_IRQ", iInstance);
6208 AssertRCReturn(rc, rc);
6209
6210 /*
6211 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
6212 */
6213#ifdef VBOX_WITH_VMSVGA
6214 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
6215
6216 if (pThis->fVMSVGAEnabled)
6217 {
6218 /*
6219 * Allocate and initialize the FIFO MMIO2 memory.
6220 */
6221 rc = PDMDevHlpMMIO2Register(pDevIns, 2 /*iRegion*/, VMSVGA_FIFO_SIZE, 0 /*fFlags*/, (void **)&pThis->svga.pFIFOR3, "VMSVGA-FIFO");
6222 if (RT_FAILURE(rc))
6223 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6224 N_("Failed to allocate %u bytes of memory for the VMSVGA device"), VMSVGA_FIFO_SIZE);
6225 pThis->svga.pFIFOR0 = (RTR0PTR)pThis->svga.pFIFOR3;
6226 pThis->svga.cbFIFO = VMSVGA_FIFO_SIZE;
6227 }
6228#else
6229 int iPCIRegionVRAM = 0;
6230#endif
6231 rc = PDMDevHlpMMIO2Register(pDevIns, iPCIRegionVRAM, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
6232 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
6233 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo @bugref{1865} Map parts into R0 or just use PGM access (Mac only). */
6234
6235 if (pThis->fGCEnabled)
6236 {
6237 RTRCPTR pRCMapping = 0;
6238 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
6239 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6240 pThis->vram_ptrRC = pRCMapping;
6241# ifdef VBOX_WITH_VMSVGA
6242 /* Don't need a mapping in RC */
6243# endif
6244 }
6245
6246#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
6247 if (pThis->fR0Enabled)
6248 {
6249 RTR0PTR pR0Mapping = 0;
6250 rc = PDMDevHlpMMIO2MapKernel(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
6251 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6252 pThis->vram_ptrR0 = pR0Mapping;
6253# ifdef VBOX_WITH_VMSVGA
6254 if (pThis->fVMSVGAEnabled)
6255 {
6256 RTR0PTR pR0Mapping = 0;
6257 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 2 /* iRegion */, 0 /* off */, VMSVGA_FIFO_SIZE, "VMSVGA-FIFO", &pR0Mapping);
6258 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VMSVGA_FIFO_SIZE, rc), rc);
6259 pThis->svga.pFIFOR0 = pR0Mapping;
6260 }
6261# endif
6262 }
6263#endif
6264
6265 /*
6266 * Register access handler types.
6267 */
6268 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_WRITE,
6269 vgaLFBAccessHandler,
6270 g_DeviceVga.szR0Mod, "vgaLFBAccessHandler", "vgaLbfAccessPfHandler",
6271 g_DeviceVga.szRCMod, "vgaLFBAccessHandler", "vgaLbfAccessPfHandler",
6272 "VGA LFB", &pThis->hLfbAccessHandlerType);
6273 AssertRCReturn(rc, rc);
6274
6275
6276 /*
6277 * Register I/O ports.
6278 */
6279 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
6280 if (RT_FAILURE(rc))
6281 return rc;
6282 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
6283 if (RT_FAILURE(rc))
6284 return rc;
6285 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
6286 if (RT_FAILURE(rc))
6287 return rc;
6288 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
6289 if (RT_FAILURE(rc))
6290 return rc;
6291 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
6292 if (RT_FAILURE(rc))
6293 return rc;
6294#ifdef VBOX_WITH_HGSMI
6295 /* Use reserved VGA IO ports for HGSMI. */
6296 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
6297 if (RT_FAILURE(rc))
6298 return rc;
6299 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
6300 if (RT_FAILURE(rc))
6301 return rc;
6302#endif /* VBOX_WITH_HGSMI */
6303
6304#ifdef CONFIG_BOCHS_VBE
6305 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
6306 if (RT_FAILURE(rc))
6307 return rc;
6308 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
6309 if (RT_FAILURE(rc))
6310 return rc;
6311#endif /* CONFIG_BOCHS_VBE */
6312
6313 /* guest context extension */
6314 if (pThis->fGCEnabled)
6315 {
6316 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6317 if (RT_FAILURE(rc))
6318 return rc;
6319 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6320 if (RT_FAILURE(rc))
6321 return rc;
6322 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6323 if (RT_FAILURE(rc))
6324 return rc;
6325 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6326 if (RT_FAILURE(rc))
6327 return rc;
6328 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6329 if (RT_FAILURE(rc))
6330 return rc;
6331#ifdef CONFIG_BOCHS_VBE
6332 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6333 if (RT_FAILURE(rc))
6334 return rc;
6335 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6336 if (RT_FAILURE(rc))
6337 return rc;
6338#endif /* CONFIG_BOCHS_VBE */
6339 }
6340
6341 /* R0 context extension */
6342 if (pThis->fR0Enabled)
6343 {
6344 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6345 if (RT_FAILURE(rc))
6346 return rc;
6347 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6348 if (RT_FAILURE(rc))
6349 return rc;
6350 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6351 if (RT_FAILURE(rc))
6352 return rc;
6353 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6354 if (RT_FAILURE(rc))
6355 return rc;
6356 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6357 if (RT_FAILURE(rc))
6358 return rc;
6359#ifdef CONFIG_BOCHS_VBE
6360 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6361 if (RT_FAILURE(rc))
6362 return rc;
6363 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6364 if (RT_FAILURE(rc))
6365 return rc;
6366#endif /* CONFIG_BOCHS_VBE */
6367 }
6368
6369 /* vga mmio */
6370 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
6371 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
6372 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
6373 if (RT_FAILURE(rc))
6374 return rc;
6375 if (pThis->fGCEnabled)
6376 {
6377 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6378 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6379 if (RT_FAILURE(rc))
6380 return rc;
6381 }
6382 if (pThis->fR0Enabled)
6383 {
6384 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6385 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6386 if (RT_FAILURE(rc))
6387 return rc;
6388 }
6389
6390 /* vga bios */
6391 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6392 if (RT_FAILURE(rc))
6393 return rc;
6394 if (pThis->fR0Enabled)
6395 {
6396 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6397 if (RT_FAILURE(rc))
6398 return rc;
6399 }
6400
6401 /*
6402 * Get the VGA BIOS ROM file name.
6403 */
6404 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6405 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6406 {
6407 pThis->pszVgaBiosFile = NULL;
6408 rc = VINF_SUCCESS;
6409 }
6410 else if (RT_FAILURE(rc))
6411 return PDMDEV_SET_ERROR(pDevIns, rc,
6412 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6413 else if (!*pThis->pszVgaBiosFile)
6414 {
6415 MMR3HeapFree(pThis->pszVgaBiosFile);
6416 pThis->pszVgaBiosFile = NULL;
6417 }
6418
6419 /*
6420 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6421 */
6422 RTFILE FileVgaBios = NIL_RTFILE;
6423 if (pThis->pszVgaBiosFile)
6424 {
6425 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6426 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6427 if (RT_SUCCESS(rc))
6428 {
6429 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6430 if (RT_SUCCESS(rc))
6431 {
6432 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6433 || pThis->cbVgaBios > _64K
6434 || pThis->cbVgaBios < 16 * _1K)
6435 rc = VERR_TOO_MUCH_DATA;
6436 }
6437 }
6438 if (RT_FAILURE(rc))
6439 {
6440 /*
6441 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6442 */
6443 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6444 RTFileClose(FileVgaBios);
6445 FileVgaBios = NIL_RTFILE;
6446 MMR3HeapFree(pThis->pszVgaBiosFile);
6447 pThis->pszVgaBiosFile = NULL;
6448 }
6449 }
6450
6451 /*
6452 * Attempt to get the VGA BIOS ROM data from file.
6453 */
6454 if (pThis->pszVgaBiosFile)
6455 {
6456 /*
6457 * Allocate buffer for the VGA BIOS ROM data.
6458 */
6459 pThis->pbVgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6460 if (pThis->pbVgaBios)
6461 {
6462 rc = RTFileRead(FileVgaBios, pThis->pbVgaBios, pThis->cbVgaBios, NULL);
6463 if (RT_FAILURE(rc))
6464 {
6465 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6466 MMR3HeapFree(pThis->pbVgaBios);
6467 pThis->pbVgaBios = NULL;
6468 }
6469 rc = VINF_SUCCESS;
6470 }
6471 else
6472 rc = VERR_NO_MEMORY;
6473 }
6474 else
6475 pThis->pbVgaBios = NULL;
6476
6477 /* cleanup */
6478 if (FileVgaBios != NIL_RTFILE)
6479 RTFileClose(FileVgaBios);
6480
6481 /* If we were unable to get the data from file for whatever reason, fall
6482 back to the built-in ROM image. */
6483 const uint8_t *pbVgaBiosBinary;
6484 uint64_t cbVgaBiosBinary;
6485 uint32_t fFlags = 0;
6486 if (pThis->pbVgaBios == NULL)
6487 {
6488 CPUMMICROARCH enmMicroarch = pVM ? pVM->cpum.ro.GuestFeatures.enmMicroarch : kCpumMicroarch_Intel_P6;
6489 if ( enmMicroarch == kCpumMicroarch_Intel_8086
6490 || enmMicroarch == kCpumMicroarch_Intel_80186
6491 || enmMicroarch == kCpumMicroarch_NEC_V20
6492 || enmMicroarch == kCpumMicroarch_NEC_V30)
6493 {
6494 pbVgaBiosBinary = g_abVgaBiosBinary8086;
6495 cbVgaBiosBinary = g_cbVgaBiosBinary8086;
6496 LogRel(("VGA: Using the 8086 BIOS image!\n"));
6497 }
6498 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
6499 {
6500 pbVgaBiosBinary = g_abVgaBiosBinary286;
6501 cbVgaBiosBinary = g_cbVgaBiosBinary286;
6502 LogRel(("VGA: Using the 286 BIOS image!\n"));
6503 }
6504 else
6505 {
6506 pbVgaBiosBinary = g_abVgaBiosBinary386;
6507 cbVgaBiosBinary = g_cbVgaBiosBinary386;
6508 LogRel(("VGA: Using the 386+ BIOS image.\n"));
6509 }
6510 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6511 }
6512 else
6513 {
6514 pbVgaBiosBinary = pThis->pbVgaBios;
6515 cbVgaBiosBinary = pThis->cbVgaBios;
6516 }
6517
6518 AssertReleaseMsg(cbVgaBiosBinary <= _64K && cbVgaBiosBinary >= 32*_1K, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6519 AssertReleaseMsg(RT_ALIGN_Z(cbVgaBiosBinary, PAGE_SIZE) == cbVgaBiosBinary, ("cbVgaBiosBinary=%#x\n", cbVgaBiosBinary));
6520 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6521 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pbVgaBiosBinary, cbVgaBiosBinary,
6522 fFlags, "VGA BIOS");
6523 if (RT_FAILURE(rc))
6524 return rc;
6525
6526 /*
6527 * Saved state.
6528 */
6529 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6530 NULL, vgaR3LiveExec, NULL,
6531 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6532 NULL, vgaR3LoadExec, vgaR3LoadDone);
6533 if (RT_FAILURE(rc))
6534 return rc;
6535
6536 /*
6537 * PCI device registration.
6538 */
6539 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6540 if (RT_FAILURE(rc))
6541 return rc;
6542 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
6543 if (pThis->Dev.devfn != 16 && iInstance == 0)
6544 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
6545
6546#ifdef VBOX_WITH_VMSVGA
6547 if (pThis->fVMSVGAEnabled)
6548 {
6549 /* Register the io command ports. */
6550 rc = PDMDevHlpPCIIORegionRegister (pDevIns, 0 /* iRegion */, 0x10, PCI_ADDRESS_SPACE_IO, vmsvgaR3IORegionMap);
6551 if (RT_FAILURE (rc))
6552 return rc;
6553 /* VMware's MetalKit doesn't like PCI_ADDRESS_SPACE_MEM_PREFETCH */
6554 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vgaR3IORegionMap);
6555 if (RT_FAILURE(rc))
6556 return rc;
6557 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2 /* iRegion */, VMSVGA_FIFO_SIZE, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vmsvgaR3IORegionMap);
6558 if (RT_FAILURE(rc))
6559 return rc;
6560 }
6561 else
6562#endif /* VBOX_WITH_VMSVGA */
6563 rc = PDMDevHlpPCIIORegionRegister(pDevIns, iPCIRegionVRAM, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6564 if (RT_FAILURE(rc))
6565 return rc;
6566
6567 /*
6568 * Create the refresh timer.
6569 */
6570 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6571 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6572 "VGA Refresh Timer", &pThis->RefreshTimer);
6573 if (RT_FAILURE(rc))
6574 return rc;
6575
6576 /*
6577 * Attach to the display.
6578 */
6579 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6580 if (RT_FAILURE(rc))
6581 return rc;
6582
6583 /*
6584 * Initialize the retrace flag.
6585 */
6586 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6587 AssertLogRelRCReturn(rc, rc);
6588
6589#ifdef VBE_NEW_DYN_LIST
6590
6591 uint16_t maxBiosXRes;
6592 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6593 AssertLogRelRCReturn(rc, rc);
6594 uint16_t maxBiosYRes;
6595 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6596 AssertLogRelRCReturn(rc, rc);
6597
6598 /*
6599 * Compute buffer size for the VBE BIOS Extra Data.
6600 */
6601 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6602
6603 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6604 if (RT_SUCCESS(rc) && cyReduction)
6605 cb *= 2; /* Default mode list will be twice long */
6606 else
6607 cyReduction = 0;
6608
6609 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6610 if (RT_SUCCESS(rc) && cCustomModes)
6611 cb += sizeof(ModeInfoListItem) * cCustomModes;
6612 else
6613 cCustomModes = 0;
6614
6615 /*
6616 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6617 */
6618 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6619 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6620 pThis->pbVBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6621 if (!pThis->pbVBEExtraData)
6622 return VERR_NO_MEMORY;
6623
6624 pVBEDataHdr = (PVBEHEADER)pThis->pbVBEExtraData;
6625 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6626 pVBEDataHdr->cbData = cb;
6627
6628# ifndef VRAM_SIZE_FIX
6629 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6630 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6631# else /* VRAM_SIZE_FIX defined */
6632 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6633 for (i = 0; i < MODE_INFO_SIZE; i++)
6634 {
6635 uint32_t pixelWidth, reqSize;
6636 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6637 pixelWidth = 2;
6638 else
6639 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6640 reqSize = mode_info_list[i].info.XResolution
6641 * mode_info_list[i].info.YResolution
6642 * pixelWidth;
6643 if (reqSize >= pThis->vram_size)
6644 continue;
6645 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6646 || mode_info_list[i].info.YResolution > maxBiosYRes)
6647 continue;
6648 *pCurMode = mode_info_list[i];
6649 vgaAdjustModeInfo(pThis, pCurMode);
6650 pCurMode++;
6651 }
6652# endif /* VRAM_SIZE_FIX defined */
6653
6654 /*
6655 * Copy default modes with subtracted YResolution.
6656 */
6657 if (cyReduction)
6658 {
6659 ModeInfoListItem *pDefMode = mode_info_list;
6660 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6661# ifndef VRAM_SIZE_FIX
6662 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6663 {
6664 *pCurMode = *pDefMode;
6665 pCurMode->mode += 0x30;
6666 pCurMode->info.YResolution -= cyReduction;
6667 }
6668# else /* VRAM_SIZE_FIX defined */
6669 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6670 {
6671 uint32_t pixelWidth, reqSize;
6672 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6673 pixelWidth = 2;
6674 else
6675 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6676 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6677 if (reqSize >= pThis->vram_size)
6678 continue;
6679 if ( pDefMode->info.XResolution > maxBiosXRes
6680 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6681 continue;
6682 *pCurMode = *pDefMode;
6683 pCurMode->mode += 0x30;
6684 pCurMode->info.YResolution -= cyReduction;
6685 pCurMode++;
6686 }
6687# endif /* VRAM_SIZE_FIX defined */
6688 }
6689
6690
6691 /*
6692 * Add custom modes.
6693 */
6694 if (cCustomModes)
6695 {
6696 uint16_t u16CurMode = 0x160;
6697 for (i = 1; i <= cCustomModes; i++)
6698 {
6699 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6700 char *pszExtraData = NULL;
6701
6702 /* query and decode the custom mode string. */
6703 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6704 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6705 if (RT_SUCCESS(rc))
6706 {
6707 ModeInfoListItem *pDefMode = mode_info_list;
6708 unsigned int cx, cy, cBits, cParams, j;
6709 uint16_t u16DefMode;
6710
6711 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6712 if ( cParams != 3
6713 || (cBits != 8 && cBits != 16 && cBits != 24 && cBits != 32))
6714 {
6715 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6716 return VERR_VGA_INVALID_CUSTOM_MODE;
6717 }
6718 cbPitch = calc_line_pitch(cBits, cx);
6719# ifdef VRAM_SIZE_FIX
6720 if (cy * cbPitch >= pThis->vram_size)
6721 {
6722 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",
6723 cx, cy, cBits, pThis->vram_size / _1M));
6724 return VERR_VGA_INVALID_CUSTOM_MODE;
6725 }
6726# endif /* VRAM_SIZE_FIX defined */
6727 MMR3HeapFree(pszExtraData);
6728
6729 /* Use defaults from max@bpp mode. */
6730 switch (cBits)
6731 {
6732 case 8:
6733 u16DefMode = VBE_VESA_MODE_1024X768X8;
6734 break;
6735
6736 case 16:
6737 u16DefMode = VBE_VESA_MODE_1024X768X565;
6738 break;
6739
6740 case 24:
6741 u16DefMode = VBE_VESA_MODE_1024X768X888;
6742 break;
6743
6744 case 32:
6745 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6746 break;
6747
6748 default: /* gcc, shut up! */
6749 AssertMsgFailed(("gone postal!\n"));
6750 continue;
6751 }
6752
6753 /* mode_info_list is not terminated */
6754 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6755 pDefMode++;
6756 Assert(j < MODE_INFO_SIZE);
6757
6758 *pCurMode = *pDefMode;
6759 pCurMode->mode = u16CurMode++;
6760
6761 /* adjust defaults */
6762 pCurMode->info.XResolution = cx;
6763 pCurMode->info.YResolution = cy;
6764 pCurMode->info.BytesPerScanLine = cbPitch;
6765 pCurMode->info.LinBytesPerScanLine = cbPitch;
6766 vgaAdjustModeInfo(pThis, pCurMode);
6767
6768 /* commit it */
6769 pCurMode++;
6770 }
6771 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6772 {
6773 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6774 return rc;
6775 }
6776 } /* foreach custom mode key */
6777 }
6778
6779 /*
6780 * Add the "End of list" mode.
6781 */
6782 memset(pCurMode, 0, sizeof(*pCurMode));
6783 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6784
6785 /*
6786 * Register I/O Port for the VBE BIOS Extra Data.
6787 */
6788 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6789 if (RT_FAILURE(rc))
6790 return rc;
6791#endif /* VBE_NEW_DYN_LIST */
6792
6793 /*
6794 * Register I/O Port for the BIOS Logo.
6795 */
6796 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6797 if (RT_FAILURE(rc))
6798 return rc;
6799
6800 /*
6801 * Register debugger info callbacks.
6802 */
6803 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6804 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6805 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6806 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6807 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6808 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6809 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaInfoPlanar);
6810 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6811 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6812
6813 /*
6814 * Construct the logo header.
6815 */
6816 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6817
6818 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6819 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6820 LogoHdr.fu8FadeIn = 1;
6821 else if (RT_FAILURE(rc))
6822 return PDMDEV_SET_ERROR(pDevIns, rc,
6823 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6824
6825 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6826 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6827 LogoHdr.fu8FadeOut = 1;
6828 else if (RT_FAILURE(rc))
6829 return PDMDEV_SET_ERROR(pDevIns, rc,
6830 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6831
6832 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6833 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6834 LogoHdr.u16LogoMillies = 0;
6835 else if (RT_FAILURE(rc))
6836 return PDMDEV_SET_ERROR(pDevIns, rc,
6837 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6838
6839 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6840 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6841 LogoHdr.fu8ShowBootMenu = 0;
6842 else if (RT_FAILURE(rc))
6843 return PDMDEV_SET_ERROR(pDevIns, rc,
6844 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6845
6846#if defined(DEBUG) && !defined(DEBUG_sunlover) && !defined(DEBUG_michael)
6847 /* Disable the logo abd menu if all default settings. */
6848 if ( LogoHdr.fu8FadeIn
6849 && LogoHdr.fu8FadeOut
6850 && LogoHdr.u16LogoMillies == 0
6851 && LogoHdr.fu8ShowBootMenu == 2)
6852 {
6853 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = 0;
6854 LogoHdr.u16LogoMillies = 500;
6855 }
6856#endif
6857
6858 /* Delay the logo a little bit */
6859 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6860 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6861
6862 /*
6863 * Get the Logo file name.
6864 */
6865 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6866 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6867 pThis->pszLogoFile = NULL;
6868 else if (RT_FAILURE(rc))
6869 return PDMDEV_SET_ERROR(pDevIns, rc,
6870 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6871 else if (!*pThis->pszLogoFile)
6872 {
6873 MMR3HeapFree(pThis->pszLogoFile);
6874 pThis->pszLogoFile = NULL;
6875 }
6876
6877 /*
6878 * Determine the logo size, open any specified logo file in the process.
6879 */
6880 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6881 RTFILE FileLogo = NIL_RTFILE;
6882 if (pThis->pszLogoFile)
6883 {
6884 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6885 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6886 if (RT_SUCCESS(rc))
6887 {
6888 uint64_t cbFile;
6889 rc = RTFileGetSize(FileLogo, &cbFile);
6890 if (RT_SUCCESS(rc))
6891 {
6892 if (cbFile > 0 && cbFile < 32*_1M)
6893 LogoHdr.cbLogo = (uint32_t)cbFile;
6894 else
6895 rc = VERR_TOO_MUCH_DATA;
6896 }
6897 }
6898 if (RT_FAILURE(rc))
6899 {
6900 /*
6901 * Ignore failure and fall back to the default logo.
6902 */
6903 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6904 if (FileLogo != NIL_RTFILE)
6905 RTFileClose(FileLogo);
6906 FileLogo = NIL_RTFILE;
6907 MMR3HeapFree(pThis->pszLogoFile);
6908 pThis->pszLogoFile = NULL;
6909 }
6910 }
6911
6912 /*
6913 * Disable graphic splash screen if it doesn't fit into VRAM.
6914 */
6915 if (pThis->vram_size < LOGO_MAX_SIZE)
6916 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6917
6918 /*
6919 * Allocate buffer for the logo data.
6920 * RT_MAX() is applied to let us fall back to default logo on read failure.
6921 */
6922 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6923 pThis->pbLogo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6924 if (pThis->pbLogo)
6925 {
6926 /*
6927 * Write the logo header.
6928 */
6929 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pbLogo;
6930 *pLogoHdr = LogoHdr;
6931
6932 /*
6933 * Write the logo bitmap.
6934 */
6935 if (pThis->pszLogoFile)
6936 {
6937 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6938 if (RT_SUCCESS(rc))
6939 rc = vbeParseBitmap(pThis);
6940 if (RT_FAILURE(rc))
6941 {
6942 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
6943 rc, pThis->pszLogoFile));
6944 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6945 }
6946 }
6947 if ( !pThis->pszLogoFile
6948 || RT_FAILURE(rc))
6949 {
6950 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6951 rc = vbeParseBitmap(pThis);
6952 if (RT_FAILURE(rc))
6953 AssertReleaseMsgFailed(("Parsing of internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6954 }
6955
6956 rc = VINF_SUCCESS;
6957 }
6958 else
6959 rc = VERR_NO_MEMORY;
6960
6961 /*
6962 * Cleanup.
6963 */
6964 if (FileLogo != NIL_RTFILE)
6965 RTFileClose(FileLogo);
6966
6967#ifdef VBOX_WITH_HGSMI
6968 VBVAInit (pThis);
6969#endif /* VBOX_WITH_HGSMI */
6970
6971#ifdef VBOX_WITH_VDMA
6972 if (rc == VINF_SUCCESS)
6973 {
6974 rc = vboxVDMAConstruct(pThis, 1024);
6975 AssertRC(rc);
6976 }
6977#endif
6978
6979#ifdef VBOX_WITH_VMSVGA
6980 if ( rc == VINF_SUCCESS
6981 && pThis->fVMSVGAEnabled)
6982 {
6983 rc = vmsvgaInit(pDevIns);
6984 }
6985#endif
6986
6987 /*
6988 * Statistics.
6989 */
6990 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6991 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6992 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6993 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6994 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6995 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
6996
6997 /* Init latched access mask. */
6998 pThis->uMaskLatchAccess = 0x3ff;
6999
7000 if (RT_SUCCESS(rc))
7001 {
7002 PPDMIBASE pBase;
7003 /*
7004 * Attach status driver (optional).
7005 */
7006 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
7007 if (RT_SUCCESS(rc))
7008 {
7009 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
7010 pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
7011 }
7012 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
7013 {
7014 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
7015 rc = VINF_SUCCESS;
7016 }
7017 else
7018 {
7019 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
7020 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
7021 }
7022 }
7023 return rc;
7024}
7025
7026static DECLCALLBACK(void) vgaR3PowerOn(PPDMDEVINS pDevIns)
7027{
7028 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
7029#ifdef VBOX_WITH_VMSVGA
7030 vmsvgaR3PowerOn(pDevIns);
7031#endif
7032 VBVAOnResume(pThis);
7033}
7034
7035static DECLCALLBACK(void) vgaR3Resume(PPDMDEVINS pDevIns)
7036{
7037 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
7038 VBVAOnResume(pThis);
7039}
7040
7041/**
7042 * The device registration structure.
7043 */
7044const PDMDEVREG g_DeviceVga =
7045{
7046 /* u32Version */
7047 PDM_DEVREG_VERSION,
7048 /* szName */
7049 "vga",
7050 /* szRCMod */
7051 "VBoxDDRC.rc",
7052 /* szR0Mod */
7053 "VBoxDDR0.r0",
7054 /* pszDescription */
7055 "VGA Adaptor with VESA extensions.",
7056 /* fFlags */
7057 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
7058 /* fClass */
7059 PDM_DEVREG_CLASS_GRAPHICS,
7060 /* cMaxInstances */
7061 1,
7062 /* cbInstance */
7063 sizeof(VGASTATE),
7064 /* pfnConstruct */
7065 vgaR3Construct,
7066 /* pfnDestruct */
7067 vgaR3Destruct,
7068 /* pfnRelocate */
7069 vgaR3Relocate,
7070 /* pfnMemSetup */
7071 NULL,
7072 /* pfnPowerOn */
7073 vgaR3PowerOn,
7074 /* pfnReset */
7075 vgaR3Reset,
7076 /* pfnSuspend */
7077 NULL,
7078 /* pfnResume */
7079 vgaR3Resume,
7080 /* pfnAttach */
7081 vgaAttach,
7082 /* pfnDetach */
7083 vgaDetach,
7084 /* pfnQueryInterface */
7085 NULL,
7086 /* pfnInitComplete */
7087 NULL,
7088 /* pfnPowerOff */
7089 NULL,
7090 /* pfnSoftReset */
7091 NULL,
7092 /* u32VersionEnd */
7093 PDM_DEVREG_VERSION
7094};
7095
7096#endif /* !IN_RING3 */
7097#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
7098
7099/*
7100 * Local Variables:
7101 * nuke-trailing-whitespace-p:nil
7102 * End:
7103 */
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