VirtualBox

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

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

Cleaned up

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

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