VirtualBox

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

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

DevVGA: fix regression from r91599

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