VirtualBox

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

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

VGA: solaris fix (for some reason, moddi3.c is not part of VMMGC.gc)

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

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