VirtualBox

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

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

DevVGA: do not use invalid VBE values when resizing (fixes crash while booting Ubuntu, r68662 regression.

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