VirtualBox

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

Last change on this file since 17596 was 17335, checked in by vboxsync, 16 years ago

Use PDMDevHlpCanEmulateIoBlock

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

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