VirtualBox

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

Last change on this file since 33760 was 33676, checked in by vboxsync, 14 years ago

scm cleanup run.

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

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