VirtualBox

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

Last change on this file since 26506 was 26173, checked in by vboxsync, 15 years ago

PDM: s/pCfgHandle/pCfg/g - part 2.

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