VirtualBox

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

Last change on this file since 67474 was 67474, checked in by vboxsync, 8 years ago

Devices/DevVGA: Unify the drawing logic (full update is handled by the other case, too). Introduce proper VGA blanking.
Main/Display: Handle VGA blanking

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

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