VirtualBox

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

Last change on this file since 54153 was 53965, checked in by vboxsync, 10 years ago

Devices/Graphics, Main: optionally send cursor integration toggle and guest cursor position information through the graphics device.

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