VirtualBox

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

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

DevVGA: free the screenshot buffer on not success.

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

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