VirtualBox

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

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

DevVGA.cpp: try fix lock order violation in vgaReset.

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