VirtualBox

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

Last change on this file since 16684 was 15977, checked in by vboxsync, 16 years ago

Devices/VGA: round custom video modes up to an X resolution which is a multiple of eight

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

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