VirtualBox

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

Last change on this file since 50667 was 50552, checked in by vboxsync, 11 years ago

DisplayImpl,DevVGA: take correct screenshot if VBVA is paused

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

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