VirtualBox

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

Last change on this file since 24340 was 24288, checked in by vboxsync, 15 years ago

VGA: more critical section protection required

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