VirtualBox

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

Last change on this file since 76191 was 76181, checked in by vboxsync, 6 years ago

DevVGA-SVGA: process the three GMRFB related commands when VBOX_WITH_VMSVGA3D is not defined.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette