VirtualBox

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

Last change on this file since 44876 was 44876, checked in by vboxsync, 12 years ago

DevVGA: Drop the default crit sect in favour of our own (now called CritSect instead of lock). s -> pThis (didn't mean to combine these changes, but I started doing things in the wrong order and suddenly had no choice).

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

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