VirtualBox

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

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

VGA: Advertise unrestricted horizontal resolutions.

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

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