VirtualBox

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

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

dev/vga: reset on vbe enable change only

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