VirtualBox

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

Last change on this file since 32807 was 32807, checked in by vboxsync, 14 years ago

VGA: Make sure the selected bank cannot get past VRAM size.

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