VirtualBox

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

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

Typo.

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