VirtualBox

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

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

fix r34221

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