VirtualBox

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

Last change on this file since 33105 was 33091, checked in by vboxsync, 15 years ago

VGA: Dump more state information.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette