VirtualBox

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

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

DevVGA: Better variable naming, corrected assertion.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use