VirtualBox

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

最後變更 在這個檔案從33178是 33178,由 vboxsync 提交於 14 年 前

VGA: Improve compatibility with bad old VESA software (Sanctuary Woods).

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

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