VirtualBox

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

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

PDM: Added search paths to the device and driver DLL CFGM nodes so that VBoxEhciR0.r0/RC.rc can be found.

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