VirtualBox

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

Last change on this file since 25127 was 25062, checked in by vboxsync, 15 years ago

HGSMI,DevVGA: Use the offset based heap for the hostHeap (resides in VRAM). New staved state version for DevVGA for indicating this change.

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