VirtualBox

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

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

Devices/Graphics/DevVGA: fix a bug in r14534 which set scan line pitch in bytes instead of pixels

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

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