VirtualBox

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

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

VMM,Devices: Changed ROM registration and fixed some shadowed ROM issues in the new phys code.

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

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