VirtualBox

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

Last change on this file since 67430 was 67355, checked in by vboxsync, 8 years ago

VGA: For writes, alias the two atrribute controller registers to allow word writes. Documented in C&T 82C441 datasheet and elsewhere.

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