VirtualBox

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

Last change on this file since 67795 was 67795, checked in by vboxsync, 7 years ago

VGA: Now you see me, now you don't.

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