VirtualBox

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

Last change on this file since 72550 was 71698, checked in by vboxsync, 7 years ago

DevVGA: Code cleanup in progress. ?bugref:9094

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

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