VirtualBox

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

Last change on this file since 82084 was 82084, checked in by vboxsync, 5 years ago

DevVGA: Move VERIFY_VRAM_WRITE_OFF_RETURN and VERIFY_VRAM_READ_OFF_RETURN to the DevVGA.h header file so the macro we test for when defining them have a chance to be set. duh. bugref:9218

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