VirtualBox

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

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

Correction

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