VirtualBox

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

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

Devices/Graphics: build fix

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

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