VirtualBox

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

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

wddm/3d: enable hgsmi for chromium

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

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