VirtualBox

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

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

Devices/Graphics: clean up VBE register setting and do not overwrite virtual widths

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

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