VirtualBox

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

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

VGA: Setting X resolution also needs to set virtual width.

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