VirtualBox

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

Last change on this file since 45305 was 45305, checked in by vboxsync, 12 years ago

IOM: Adding pVCpu to a lot of calls and moving the lookup caches from VM to VMCPU.

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