VirtualBox

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

Last change on this file since 73214 was 73203, checked in by vboxsync, 6 years ago

VMM, Devices: bugref:9193 Remove unused code after using EMRZSetPendingIoPort[Read|Write].

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