VirtualBox

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

Last change on this file since 75149 was 74474, checked in by vboxsync, 6 years ago

Main,Devices: Graphics controller updates. bugref:8893

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

© 2023 Oracle
ContactPrivacy policyTerms of Use