VirtualBox

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

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

Devices/Graphics: VMware SVGA II compatible graphics emulation (2D only), including the associated small API and VBoxManage changes, contributed by trivirt AG.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 227.3 KB
Line 
1/* $Id: DevVGA.cpp 49983 2013-12-19 12:23:17Z 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 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pThis->pDrv->cBits)];
2354
2355 if (pThis->cursor_invalidate)
2356 pThis->cursor_invalidate(pThis);
2357
2358 line_offset = pThis->line_offset;
2359#if 0
2360 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",
2361 width, height, v, line_offset, pThis->cr[9], pThis->cr[0x17], pThis->line_compare, pThis->sr[0x01]));
2362#endif
2363 addr1 = (pThis->start_addr * 4);
2364 bwidth = (width * bits + 7) / 8; /* The visible width of a scanline. */
2365 y_start = -1;
2366 page_min = 0x7fffffff;
2367 page_max = -1;
2368 d = pThis->pDrv->pu8Data;
2369 linesize = pThis->pDrv->cbScanline;
2370
2371 y1 = 0;
2372 y2 = pThis->cr[0x09] & 0x1F; /* starting row scan count */
2373 for(y = 0; y < height; y++) {
2374 addr = addr1;
2375 /* CGA/MDA compatibility. Note that these addresses are all
2376 * shifted left by two compared to VGA specs.
2377 */
2378 if (!(pThis->cr[0x17] & 1)) {
2379 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2380 }
2381 if (!(pThis->cr[0x17] & 2)) {
2382 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2383 }
2384 page0 = addr & ~PAGE_OFFSET_MASK;
2385 page1 = (addr + bwidth - 1) & ~PAGE_OFFSET_MASK;
2386 bool update = full_update | vga_is_dirty(pThis, page0) | vga_is_dirty(pThis, page1);
2387 if (page1 - page0 > PAGE_SIZE) {
2388 /* if wide line, can use another page */
2389 update |= vga_is_dirty(pThis, page0 + PAGE_SIZE);
2390 }
2391 /* explicit invalidation for the hardware cursor */
2392 update |= (pThis->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2393 if (update) {
2394 if (y_start < 0)
2395 y_start = y;
2396 if (page0 < page_min)
2397 page_min = page0;
2398 if (page1 > page_max)
2399 page_max = page1;
2400 if (pThis->fRenderVRAM)
2401 vga_draw_line(pThis, d, pThis->CTX_SUFF(vram_ptr) + addr, width);
2402 if (pThis->cursor_draw_line)
2403 pThis->cursor_draw_line(pThis, d, y);
2404 } else {
2405 if (y_start >= 0) {
2406 /* flush to display */
2407 pThis->pDrv->pfnUpdateRect(pThis->pDrv, 0, y_start, disp_width, y - y_start);
2408 y_start = -1;
2409 }
2410 }
2411 if (!multi_run) {
2412 y1++;
2413 multi_run = double_scan;
2414
2415 if (y2 == 0) {
2416 y2 = pThis->cr[0x09] & 0x1F;
2417 addr1 += line_offset;
2418 } else {
2419 --y2;
2420 }
2421 } else {
2422 multi_run--;
2423 }
2424 /* line compare acts on the displayed lines */
2425 if ((uint32_t)y == pThis->line_compare)
2426 addr1 = 0;
2427 d += linesize;
2428 }
2429 if (y_start >= 0) {
2430 /* flush to display */
2431 pThis->pDrv->pfnUpdateRect(pThis->pDrv, 0, y_start, disp_width, y - y_start);
2432 }
2433 /* reset modified pages */
2434 if (page_max != -1 && reset_dirty) {
2435 vga_reset_dirty(pThis, page_min, page_max + PAGE_SIZE);
2436 }
2437 memset(pThis->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2438 return VINF_SUCCESS;
2439}
2440
2441static void vga_draw_blank(PVGASTATE pThis, int full_update)
2442{
2443 int i, w, val;
2444 uint8_t *d;
2445 uint32_t cbScanline = pThis->pDrv->cbScanline;
2446
2447 if (pThis->pDrv->pu8Data == pThis->vram_ptrR3) /* Do not clear the VRAM itself. */
2448 return;
2449 if (!full_update)
2450 return;
2451 if (pThis->last_scr_width <= 0 || pThis->last_scr_height <= 0)
2452 return;
2453 if (pThis->pDrv->cBits == 8)
2454 val = pThis->rgb_to_pixel(0, 0, 0);
2455 else
2456 val = 0;
2457 w = pThis->last_scr_width * ((pThis->pDrv->cBits + 7) >> 3);
2458 d = pThis->pDrv->pu8Data;
2459 if (pThis->fRenderVRAM)
2460 {
2461 for(i = 0; i < (int)pThis->last_scr_height; i++) {
2462 memset(d, val, w);
2463 d += cbScanline;
2464 }
2465 }
2466 pThis->pDrv->pfnUpdateRect(pThis->pDrv, 0, 0, pThis->last_scr_width, pThis->last_scr_height);
2467}
2468
2469static DECLCALLBACK(void) voidUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
2470{
2471 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(cx); NOREF(cy);
2472}
2473
2474
2475#define GMODE_TEXT 0
2476#define GMODE_GRAPH 1
2477#define GMODE_BLANK 2
2478#ifdef VBOX_WITH_VMSVGA
2479#define GMODE_SVGA 3
2480#endif
2481
2482static int vga_update_display(PVGASTATE pThis, bool fUpdateAll, bool fFailOnResize, bool reset_dirty)
2483{
2484 int rc = VINF_SUCCESS;
2485 int graphic_mode;
2486
2487 if (pThis->pDrv->cBits == 0) {
2488 /* nothing to do */
2489 } else {
2490 switch(pThis->pDrv->cBits) {
2491 case 8:
2492 pThis->rgb_to_pixel = rgb_to_pixel8_dup;
2493 break;
2494 case 15:
2495 pThis->rgb_to_pixel = rgb_to_pixel15_dup;
2496 break;
2497 default:
2498 case 16:
2499 pThis->rgb_to_pixel = rgb_to_pixel16_dup;
2500 break;
2501 case 32:
2502 pThis->rgb_to_pixel = rgb_to_pixel32_dup;
2503 break;
2504 }
2505
2506 if (fUpdateAll) {
2507 /* A full update is requested. Special processing for a "blank" mode is required, because
2508 * the request must process all pending resolution changes.
2509 *
2510 * Appropriate vga_draw_graphic or vga_draw_text function, which checks the resolution change,
2511 * must be called even if the screen has been blanked, but then the function should do no actual
2512 * screen update. To do this, pfnUpdateRect is replaced with a nop.
2513 */
2514 typedef DECLCALLBACK(void) FNUPDATERECT(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy);
2515 typedef FNUPDATERECT *PFNUPDATERECT;
2516
2517 PFNUPDATERECT pfnUpdateRect = NULL;
2518
2519 /* Detect the "screen blank" conditions. */
2520 int fBlank = 0;
2521 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2522 fBlank = 1;
2523 }
2524
2525 if (fBlank) {
2526 /* Provide a void pfnUpdateRect callback. */
2527 if (pThis->pDrv) {
2528 pfnUpdateRect = pThis->pDrv->pfnUpdateRect;
2529 pThis->pDrv->pfnUpdateRect = voidUpdateRect;
2530 }
2531 }
2532
2533 /* Do a complete redraw, which will pick up a new screen resolution. */
2534#ifdef VBOX_WITH_VMSVGA
2535 if (pThis->svga.fEnabled) {
2536 pThis->graphic_mode = GMODE_SVGA;
2537 rc = vmsvga_draw_graphic(pThis, 1, false, reset_dirty);
2538 }
2539 else
2540#endif
2541 if (pThis->gr[6] & 1) {
2542 pThis->graphic_mode = GMODE_GRAPH;
2543 rc = vga_draw_graphic(pThis, 1, false, reset_dirty);
2544 } else {
2545 pThis->graphic_mode = GMODE_TEXT;
2546 rc = vga_draw_text(pThis, 1, false, reset_dirty);
2547 }
2548
2549 if (fBlank) {
2550 /* Set the current mode and restore the callback. */
2551 pThis->graphic_mode = GMODE_BLANK;
2552 if (pThis->pDrv) {
2553 pThis->pDrv->pfnUpdateRect = pfnUpdateRect;
2554 }
2555 }
2556 return rc;
2557 }
2558
2559#ifdef VBOX_WITH_VMSVGA
2560 if (pThis->svga.fEnabled) {
2561 graphic_mode = GMODE_SVGA;
2562 }
2563 else
2564#endif
2565 if (!(pThis->ar_index & 0x20) || (pThis->sr[0x01] & 0x20)) {
2566 graphic_mode = GMODE_BLANK;
2567 } else {
2568 graphic_mode = pThis->gr[6] & 1;
2569 }
2570 bool full_update = graphic_mode != pThis->graphic_mode;
2571 if (full_update) {
2572 pThis->graphic_mode = graphic_mode;
2573 }
2574 switch(graphic_mode) {
2575 case GMODE_TEXT:
2576 rc = vga_draw_text(pThis, full_update, fFailOnResize, reset_dirty);
2577 break;
2578 case GMODE_GRAPH:
2579 rc = vga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty);
2580 break;
2581#ifdef VBOX_WITH_VMSVGA
2582 case GMODE_SVGA:
2583 rc = vmsvga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty);
2584 break;
2585#endif
2586 case GMODE_BLANK:
2587 default:
2588 vga_draw_blank(pThis, full_update);
2589 break;
2590 }
2591 }
2592 return rc;
2593}
2594
2595static void vga_save(PSSMHANDLE pSSM, PVGASTATE pThis)
2596{
2597 int i;
2598
2599 SSMR3PutU32(pSSM, pThis->latch);
2600 SSMR3PutU8(pSSM, pThis->sr_index);
2601 SSMR3PutMem(pSSM, pThis->sr, 8);
2602 SSMR3PutU8(pSSM, pThis->gr_index);
2603 SSMR3PutMem(pSSM, pThis->gr, 16);
2604 SSMR3PutU8(pSSM, pThis->ar_index);
2605 SSMR3PutMem(pSSM, pThis->ar, 21);
2606 SSMR3PutU32(pSSM, pThis->ar_flip_flop);
2607 SSMR3PutU8(pSSM, pThis->cr_index);
2608 SSMR3PutMem(pSSM, pThis->cr, 256);
2609 SSMR3PutU8(pSSM, pThis->msr);
2610 SSMR3PutU8(pSSM, pThis->fcr);
2611 SSMR3PutU8(pSSM, pThis->st00);
2612 SSMR3PutU8(pSSM, pThis->st01);
2613
2614 SSMR3PutU8(pSSM, pThis->dac_state);
2615 SSMR3PutU8(pSSM, pThis->dac_sub_index);
2616 SSMR3PutU8(pSSM, pThis->dac_read_index);
2617 SSMR3PutU8(pSSM, pThis->dac_write_index);
2618 SSMR3PutMem(pSSM, pThis->dac_cache, 3);
2619 SSMR3PutMem(pSSM, pThis->palette, 768);
2620
2621 SSMR3PutU32(pSSM, pThis->bank_offset);
2622#ifdef CONFIG_BOCHS_VBE
2623 SSMR3PutU8(pSSM, 1);
2624 SSMR3PutU16(pSSM, pThis->vbe_index);
2625 for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
2626 SSMR3PutU16(pSSM, pThis->vbe_regs[i]);
2627 SSMR3PutU32(pSSM, pThis->vbe_start_addr);
2628 SSMR3PutU32(pSSM, pThis->vbe_line_offset);
2629#else
2630 SSMR3PutU8(pSSM, 0);
2631#endif
2632}
2633
2634static int vga_load(PSSMHANDLE pSSM, PVGASTATE pThis, int version_id)
2635{
2636 int is_vbe, i;
2637 uint32_t u32Dummy;
2638 uint8_t u8;
2639
2640 SSMR3GetU32(pSSM, &pThis->latch);
2641 SSMR3GetU8(pSSM, &pThis->sr_index);
2642 SSMR3GetMem(pSSM, pThis->sr, 8);
2643 SSMR3GetU8(pSSM, &pThis->gr_index);
2644 SSMR3GetMem(pSSM, pThis->gr, 16);
2645 SSMR3GetU8(pSSM, &pThis->ar_index);
2646 SSMR3GetMem(pSSM, pThis->ar, 21);
2647 SSMR3GetU32(pSSM, (uint32_t *)&pThis->ar_flip_flop);
2648 SSMR3GetU8(pSSM, &pThis->cr_index);
2649 SSMR3GetMem(pSSM, pThis->cr, 256);
2650 SSMR3GetU8(pSSM, &pThis->msr);
2651 SSMR3GetU8(pSSM, &pThis->fcr);
2652 SSMR3GetU8(pSSM, &pThis->st00);
2653 SSMR3GetU8(pSSM, &pThis->st01);
2654
2655 SSMR3GetU8(pSSM, &pThis->dac_state);
2656 SSMR3GetU8(pSSM, &pThis->dac_sub_index);
2657 SSMR3GetU8(pSSM, &pThis->dac_read_index);
2658 SSMR3GetU8(pSSM, &pThis->dac_write_index);
2659 SSMR3GetMem(pSSM, pThis->dac_cache, 3);
2660 SSMR3GetMem(pSSM, pThis->palette, 768);
2661
2662 SSMR3GetU32(pSSM, (uint32_t *)&pThis->bank_offset);
2663 SSMR3GetU8(pSSM, &u8);
2664 is_vbe = !!u8;
2665#ifdef CONFIG_BOCHS_VBE
2666 if (!is_vbe)
2667 {
2668 Log(("vga_load: !is_vbe !!\n"));
2669 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2670 }
2671 SSMR3GetU16(pSSM, &pThis->vbe_index);
2672 for(i = 0; i < VBE_DISPI_INDEX_NB_SAVED; i++)
2673 SSMR3GetU16(pSSM, &pThis->vbe_regs[i]);
2674 if (version_id <= VGA_SAVEDSTATE_VERSION_INV_VHEIGHT)
2675 recalculate_data(pThis, false); /* <- re-calculate the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] since it might be invalid */
2676 SSMR3GetU32(pSSM, &pThis->vbe_start_addr);
2677 SSMR3GetU32(pSSM, &pThis->vbe_line_offset);
2678 if (version_id < 2)
2679 SSMR3GetU32(pSSM, &u32Dummy);
2680 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
2681#else
2682 if (is_vbe)
2683 {
2684 Log(("vga_load: is_vbe !!\n"));
2685 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2686 }
2687#endif
2688
2689 /* force refresh */
2690 pThis->graphic_mode = -1;
2691 return 0;
2692}
2693
2694/* see vgaR3Construct */
2695static void vga_init_expand(void)
2696{
2697 int i, j, v, b;
2698
2699 for(i = 0;i < 256; i++) {
2700 v = 0;
2701 for(j = 0; j < 8; j++) {
2702 v |= ((i >> j) & 1) << (j * 4);
2703 }
2704 expand4[i] = v;
2705
2706 v = 0;
2707 for(j = 0; j < 4; j++) {
2708 v |= ((i >> (2 * j)) & 3) << (j * 4);
2709 }
2710 expand2[i] = v;
2711 }
2712 for(i = 0; i < 16; i++) {
2713 v = 0;
2714 for(j = 0; j < 4; j++) {
2715 b = ((i >> j) & 1);
2716 v |= b << (2 * j);
2717 v |= b << (2 * j + 1);
2718 }
2719 expand4to8[i] = v;
2720 }
2721}
2722
2723#endif /* !IN_RING0 */
2724
2725
2726
2727/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2728
2729/**
2730 * @callback_method_impl{FNIOMIOPORTOUT,Generic VGA OUT dispatcher.}
2731 */
2732PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2733{
2734 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2735 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2736
2737 NOREF(pvUser);
2738 if (cb == 1)
2739 vga_ioport_write(pThis, Port, u32);
2740 else if (cb == 2)
2741 {
2742 vga_ioport_write(pThis, Port, u32 & 0xff);
2743 vga_ioport_write(pThis, Port + 1, u32 >> 8);
2744 }
2745 return VINF_SUCCESS;
2746}
2747
2748
2749/**
2750 * @callback_method_impl{FNIOMIOPORTOUT,Generic VGA IN dispatcher.}
2751 */
2752PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2753{
2754 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2755 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2756 NOREF(pvUser);
2757
2758 int rc = VINF_SUCCESS;
2759 if (cb == 1)
2760 *pu32 = vga_ioport_read(pThis, Port);
2761 else if (cb == 2)
2762 *pu32 = vga_ioport_read(pThis, Port)
2763 | (vga_ioport_read(pThis, Port + 1) << 8);
2764 else
2765 rc = VERR_IOM_IOPORT_UNUSED;
2766 return rc;
2767}
2768
2769
2770/**
2771 * @callback_method_impl{FNIOMIOPORTOUT,VBE Data Port OUT handler.}
2772 */
2773PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2774{
2775 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2776 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2777
2778 NOREF(pvUser);
2779
2780#ifndef IN_RING3
2781 /*
2782 * This has to be done on the host in order to execute the connector callbacks.
2783 */
2784 if ( pThis->vbe_index == VBE_DISPI_INDEX_ENABLE
2785 || pThis->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
2786 {
2787 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2788 return VINF_IOM_R3_IOPORT_WRITE;
2789 }
2790#endif
2791#ifdef VBE_BYTEWISE_IO
2792 if (cb == 1)
2793 {
2794 if (!pThis->fWriteVBEData)
2795 {
2796 if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
2797 && (u32 & VBE_DISPI_ENABLED))
2798 {
2799 pThis->fWriteVBEData = false;
2800 return vbe_ioport_write_data(pThis, Port, u32 & 0xFF);
2801 }
2802
2803 pThis->cbWriteVBEData = u32 & 0xFF;
2804 pThis->fWriteVBEData = true;
2805 return VINF_SUCCESS;
2806 }
2807
2808 u32 = (pThis->cbWriteVBEData << 8) | (u32 & 0xFF);
2809 pThis->fWriteVBEData = false;
2810 cb = 2;
2811 }
2812#endif
2813 if (cb == 2 || cb == 4)
2814 {
2815//#ifdef IN_RC
2816// /*
2817// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2818// * Since we're not mapping the entire framebuffer any longer that
2819// * has to be done on the host.
2820// */
2821// if ( (pThis->vbe_index == VBE_DISPI_INDEX_ENABLE)
2822// && (u32 & VBE_DISPI_ENABLED))
2823// {
2824// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2825// return VINF_IOM_R3_IOPORT_WRITE;
2826// }
2827//#endif
2828 return vbe_ioport_write_data(pThis, Port, u32);
2829 }
2830 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2831
2832 return VINF_SUCCESS;
2833}
2834
2835
2836/**
2837 * @callback_method_impl{FNIOMIOPORTOUT,VBE Index Port OUT handler.}
2838 */
2839PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2840{
2841 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
2842 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2843
2844#ifdef VBE_BYTEWISE_IO
2845 if (cb == 1)
2846 {
2847 if (!pThis->fWriteVBEIndex)
2848 {
2849 pThis->cbWriteVBEIndex = u32 & 0x00FF;
2850 pThis->fWriteVBEIndex = true;
2851 return VINF_SUCCESS;
2852 }
2853 pThis->fWriteVBEIndex = false;
2854 vbe_ioport_write_index(pThis, Port, (pThis->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2855 return VINF_SUCCESS;
2856 }
2857#endif
2858
2859 if (cb == 2)
2860 vbe_ioport_write_index(pThis, Port, u32);
2861 else
2862 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2863 return VINF_SUCCESS;
2864}
2865
2866
2867/**
2868 * @callback_method_impl{FNIOMIOPORTOUT,VBE Data Port IN handler.}
2869 */
2870PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2871{
2872 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE); NOREF(pvUser);
2873 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2874
2875
2876#ifdef VBE_BYTEWISE_IO
2877 if (cb == 1)
2878 {
2879 if (!pThis->fReadVBEData)
2880 {
2881 *pu32 = (vbe_ioport_read_data(pThis, Port) >> 8) & 0xFF;
2882 pThis->fReadVBEData = true;
2883 return VINF_SUCCESS;
2884 }
2885 *pu32 = vbe_ioport_read_data(pThis, Port) & 0xFF;
2886 pThis->fReadVBEData = false;
2887 return VINF_SUCCESS;
2888 }
2889#endif
2890 if (cb == 2)
2891 {
2892 *pu32 = vbe_ioport_read_data(pThis, Port);
2893 return VINF_SUCCESS;
2894 }
2895 if (cb == 4)
2896 {
2897 /* Quick hack for getting the vram size. */
2898 *pu32 = pThis->vram_size;
2899 return VINF_SUCCESS;
2900 }
2901 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2902 return VERR_IOM_IOPORT_UNUSED;
2903}
2904
2905
2906/**
2907 * @callback_method_impl{FNIOMIOPORTOUT,VBE Index Port IN handler.}
2908 */
2909PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2910{
2911 NOREF(pvUser);
2912 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2913 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2914
2915#ifdef VBE_BYTEWISE_IO
2916 if (cb == 1)
2917 {
2918 if (!pThis->fReadVBEIndex)
2919 {
2920 *pu32 = (vbe_ioport_read_index(pThis, Port) >> 8) & 0xFF;
2921 pThis->fReadVBEIndex = true;
2922 return VINF_SUCCESS;
2923 }
2924 *pu32 = vbe_ioport_read_index(pThis, Port) & 0xFF;
2925 pThis->fReadVBEIndex = false;
2926 return VINF_SUCCESS;
2927 }
2928#endif
2929 if (cb == 2)
2930 {
2931 *pu32 = vbe_ioport_read_index(pThis, Port);
2932 return VINF_SUCCESS;
2933 }
2934 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
2935 return VERR_IOM_IOPORT_UNUSED;
2936}
2937
2938#ifdef VBOX_WITH_HGSMI
2939# ifdef IN_RING3
2940/**
2941 * @callback_method_impl{FNIOMIOPORTOUT,HGSMI OUT handler.}
2942 */
2943static DECLCALLBACK(int) vgaR3IOPortHGSMIWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2944{
2945 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
2946 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
2947 LogFlowFunc(("Port 0x%x, u32 0x%x, cb %d\n", Port, u32, cb));
2948
2949
2950 NOREF(pvUser);
2951
2952 if (cb == 4)
2953 {
2954 switch (Port)
2955 {
2956 case VGA_PORT_HGSMI_HOST: /* Host */
2957 {
2958# if defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM)
2959 if (u32 == HGSMIOFFSET_VOID)
2960 {
2961 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
2962 HGSMIClearHostGuestFlags(pThis->pHGSMI,
2963 HGSMIHOSTFLAGS_IRQ
2964# ifdef VBOX_VDMA_WITH_WATCHDOG
2965 | HGSMIHOSTFLAGS_WATCHDOG
2966# endif
2967 | HGSMIHOSTFLAGS_VSYNC
2968 );
2969 }
2970 else
2971# endif
2972 {
2973 HGSMIHostWrite(pThis->pHGSMI, u32);
2974 }
2975 break;
2976 }
2977
2978 case VGA_PORT_HGSMI_GUEST: /* Guest */
2979 HGSMIGuestWrite(pThis->pHGSMI, u32);
2980 break;
2981
2982 default:
2983# ifdef DEBUG_sunlover
2984 AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2985# endif
2986 break;
2987 }
2988 }
2989 else
2990 {
2991# ifdef DEBUG_sunlover
2992 AssertMsgFailed(("vgaR3IOPortHGSMIWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2993# endif
2994 }
2995
2996 return VINF_SUCCESS;
2997}
2998
2999
3000/**
3001 * @callback_method_impl{FNIOMIOPORTOUT,HGSMI IN handler.}
3002 */
3003static DECLCALLBACK(int) vgaR3IOPortHGSMIRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3004{
3005 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3006 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3007 LogFlowFunc(("Port 0x%x, cb %d\n", Port, cb));
3008
3009 NOREF(pvUser);
3010
3011 int rc = VINF_SUCCESS;
3012 if (cb == 4)
3013 {
3014 switch (Port)
3015 {
3016 case VGA_PORT_HGSMI_HOST: /* Host */
3017 *pu32 = HGSMIHostRead(pThis->pHGSMI);
3018 break;
3019 case VGA_PORT_HGSMI_GUEST: /* Guest */
3020 *pu32 = HGSMIGuestRead(pThis->pHGSMI);
3021 break;
3022 default:
3023# ifdef DEBUG_sunlover
3024 AssertMsgFailed(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
3025# endif
3026 rc = VERR_IOM_IOPORT_UNUSED;
3027 break;
3028 }
3029 }
3030 else
3031 {
3032# ifdef DEBUG_sunlover
3033 Log(("vgaR3IOPortHGSMIRead: Port=%#x cb=%d\n", Port, cb));
3034# endif
3035 rc = VERR_IOM_IOPORT_UNUSED;
3036 }
3037
3038 return rc;
3039}
3040# endif /* IN_RING3 */
3041#endif /* VBOX_WITH_HGSMI */
3042
3043
3044
3045
3046/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
3047
3048/**
3049 * @internal. For use inside VGAGCMemoryFillWrite only.
3050 * Macro for apply logical operation and bit mask.
3051 */
3052#define APPLY_LOGICAL_AND_MASK(pThis, val, bit_mask) \
3053 /* apply logical operation */ \
3054 switch (pThis->gr[3] >> 3) \
3055 { \
3056 case 0: \
3057 default: \
3058 /* nothing to do */ \
3059 break; \
3060 case 1: \
3061 /* and */ \
3062 val &= pThis->latch; \
3063 break; \
3064 case 2: \
3065 /* or */ \
3066 val |= pThis->latch; \
3067 break; \
3068 case 3: \
3069 /* xor */ \
3070 val ^= pThis->latch; \
3071 break; \
3072 } \
3073 /* apply bit mask */ \
3074 val = (val & bit_mask) | (pThis->latch & ~bit_mask)
3075
3076/**
3077 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3078 * This is the advanced version of vga_mem_writeb function.
3079 *
3080 * @returns VBox status code.
3081 * @param pThis VGA device structure
3082 * @param pvUser User argument - ignored.
3083 * @param GCPhysAddr Physical address of memory to write.
3084 * @param u32Item Data to write, up to 4 bytes.
3085 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3086 * @param cItems Number of data items to write.
3087 */
3088static int vgaInternalMMIOFill(PVGASTATE pThis, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3089{
3090 uint32_t b;
3091 uint32_t write_mask, bit_mask, set_mask;
3092 uint32_t aVal[4];
3093 unsigned i;
3094 NOREF(pvUser);
3095
3096 for (i = 0; i < cbItem; i++)
3097 {
3098 aVal[i] = u32Item & 0xff;
3099 u32Item >>= 8;
3100 }
3101
3102 /* convert to VGA memory offset */
3103 /// @todo add check for the end of region
3104 GCPhysAddr &= 0x1ffff;
3105 switch((pThis->gr[6] >> 2) & 3) {
3106 case 0:
3107 break;
3108 case 1:
3109 if (GCPhysAddr >= 0x10000)
3110 return VINF_SUCCESS;
3111 GCPhysAddr += pThis->bank_offset;
3112 break;
3113 case 2:
3114 GCPhysAddr -= 0x10000;
3115 if (GCPhysAddr >= 0x8000)
3116 return VINF_SUCCESS;
3117 break;
3118 default:
3119 case 3:
3120 GCPhysAddr -= 0x18000;
3121 if (GCPhysAddr >= 0x8000)
3122 return VINF_SUCCESS;
3123 break;
3124 }
3125
3126 if (pThis->sr[4] & 0x08) {
3127 /* chain 4 mode : simplest access */
3128 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, GCPhysAddr + cItems * cbItem - 1);
3129
3130 while (cItems-- > 0)
3131 for (i = 0; i < cbItem; i++)
3132 {
3133 if (pThis->sr[2] & (1 << (GCPhysAddr & 3)))
3134 {
3135 pThis->CTX_SUFF(vram_ptr)[GCPhysAddr] = aVal[i];
3136 vga_set_dirty(pThis, GCPhysAddr);
3137 }
3138 GCPhysAddr++;
3139 }
3140 } else if (pThis->gr[5] & 0x10) {
3141 /* odd/even mode (aka text mode mapping) */
3142 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3143 while (cItems-- > 0)
3144 for (i = 0; i < cbItem; i++)
3145 {
3146 unsigned plane = (pThis->gr[4] & 2) | (GCPhysAddr & 1);
3147 if (pThis->sr[2] & (1 << plane)) {
3148 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
3149 pThis->CTX_SUFF(vram_ptr)[PhysAddr2] = aVal[i];
3150 vga_set_dirty(pThis, PhysAddr2);
3151 }
3152 GCPhysAddr++;
3153 }
3154 } else {
3155 /* standard VGA latched access */
3156 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, (GCPhysAddr + cItems * cbItem) * 4 - 1);
3157
3158 switch(pThis->gr[5] & 3) {
3159 default:
3160 case 0:
3161 /* rotate */
3162 b = pThis->gr[3] & 7;
3163 bit_mask = pThis->gr[8];
3164 bit_mask |= bit_mask << 8;
3165 bit_mask |= bit_mask << 16;
3166 set_mask = mask16[pThis->gr[1]];
3167
3168 for (i = 0; i < cbItem; i++)
3169 {
3170 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3171 aVal[i] |= aVal[i] << 8;
3172 aVal[i] |= aVal[i] << 16;
3173
3174 /* apply set/reset mask */
3175 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
3176
3177 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3178 }
3179 break;
3180 case 1:
3181 for (i = 0; i < cbItem; i++)
3182 aVal[i] = pThis->latch;
3183 break;
3184 case 2:
3185 bit_mask = pThis->gr[8];
3186 bit_mask |= bit_mask << 8;
3187 bit_mask |= bit_mask << 16;
3188 for (i = 0; i < cbItem; i++)
3189 {
3190 aVal[i] = mask16[aVal[i] & 0x0f];
3191
3192 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3193 }
3194 break;
3195 case 3:
3196 /* rotate */
3197 b = pThis->gr[3] & 7;
3198
3199 for (i = 0; i < cbItem; i++)
3200 {
3201 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3202 bit_mask = pThis->gr[8] & aVal[i];
3203 bit_mask |= bit_mask << 8;
3204 bit_mask |= bit_mask << 16;
3205 aVal[i] = mask16[pThis->gr[0]];
3206
3207 APPLY_LOGICAL_AND_MASK(pThis, aVal[i], bit_mask);
3208 }
3209 break;
3210 }
3211
3212 /* mask data according to sr[2] */
3213 write_mask = mask16[pThis->sr[2]];
3214
3215 /* actually write data */
3216 if (cbItem == 1)
3217 {
3218 /* The most frequently case is 1 byte I/O. */
3219 while (cItems-- > 0)
3220 {
3221 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3222 vga_set_dirty(pThis, GCPhysAddr << 2);
3223 GCPhysAddr++;
3224 }
3225 }
3226 else if (cbItem == 2)
3227 {
3228 /* The second case is 2 bytes I/O. */
3229 while (cItems-- > 0)
3230 {
3231 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3232 vga_set_dirty(pThis, GCPhysAddr << 2);
3233 GCPhysAddr++;
3234
3235 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3236 vga_set_dirty(pThis, GCPhysAddr << 2);
3237 GCPhysAddr++;
3238 }
3239 }
3240 else
3241 {
3242 /* And the rest is 4 bytes. */
3243 Assert(cbItem == 4);
3244 while (cItems-- > 0)
3245 for (i = 0; i < cbItem; i++)
3246 {
3247 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3248 vga_set_dirty(pThis, GCPhysAddr << 2);
3249 GCPhysAddr++;
3250 }
3251 }
3252 }
3253 return VINF_SUCCESS;
3254}
3255
3256
3257/**
3258 * @callback_method_impl{FNIOMMMIOFILL,
3259 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and
3260 * from the inside of VGADeviceGC.cpp. This is the advanced version of
3261 * vga_mem_writeb function.}
3262 */
3263PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3264{
3265 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3266 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3267
3268 return vgaInternalMMIOFill(pThis, pvUser, GCPhysAddr, u32Item, cbItem, cItems);
3269}
3270#undef APPLY_LOGICAL_AND_MASK
3271
3272
3273/**
3274 * @callback_method_impl{FNIOMMMIOREAD, Legacy VGA memory (0xa0000 - 0xbffff)
3275 * read hook, to be called from IOM.}
3276 */
3277PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3278{
3279 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3280 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3281 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3282 NOREF(pvUser);
3283
3284 int rc = VINF_SUCCESS;
3285 switch (cb)
3286 {
3287 case 1:
3288 *(uint8_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc);
3289 break;
3290 case 2:
3291 *(uint16_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3292 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8);
3293 break;
3294 case 4:
3295 *(uint32_t *)pv = vga_mem_readb(pThis, GCPhysAddr, &rc)
3296 | (vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3297 | (vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3298 | (vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24);
3299 break;
3300
3301 case 8:
3302 *(uint64_t *)pv = (uint64_t)vga_mem_readb(pThis, GCPhysAddr, &rc)
3303 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 1, &rc) << 8)
3304 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 2, &rc) << 16)
3305 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 3, &rc) << 24)
3306 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 4, &rc) << 32)
3307 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 5, &rc) << 40)
3308 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 6, &rc) << 48)
3309 | ((uint64_t)vga_mem_readb(pThis, GCPhysAddr + 7, &rc) << 56);
3310 break;
3311
3312 default:
3313 {
3314 uint8_t *pu8Data = (uint8_t *)pv;
3315 while (cb-- > 0)
3316 {
3317 *pu8Data++ = vga_mem_readb(pThis, GCPhysAddr++, &rc);
3318 if (RT_UNLIKELY(rc != VINF_SUCCESS))
3319 break;
3320 }
3321 }
3322 }
3323
3324 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
3325 return rc;
3326}
3327
3328/**
3329 * @callback_method_impl{FNIOMMMIOWRITE, Legacy VGA memory (0xa0000 - 0xbffff)
3330 * write hook, to be called from IOM.}
3331 */
3332PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
3333{
3334 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3335 uint8_t *pu8 = (uint8_t *)pv;
3336 NOREF(pvUser);
3337 STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3338 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3339
3340 int rc;
3341 switch (cb)
3342 {
3343 case 1:
3344 rc = vga_mem_writeb(pThis, GCPhysAddr, *pu8);
3345 break;
3346#if 1
3347 case 2:
3348 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3349 if (RT_LIKELY(rc == VINF_SUCCESS))
3350 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3351 break;
3352 case 4:
3353 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3354 if (RT_LIKELY(rc == VINF_SUCCESS))
3355 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3356 if (RT_LIKELY(rc == VINF_SUCCESS))
3357 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
3358 if (RT_LIKELY(rc == VINF_SUCCESS))
3359 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
3360 break;
3361 case 8:
3362 rc = vga_mem_writeb(pThis, GCPhysAddr + 0, pu8[0]);
3363 if (RT_LIKELY(rc == VINF_SUCCESS))
3364 rc = vga_mem_writeb(pThis, GCPhysAddr + 1, pu8[1]);
3365 if (RT_LIKELY(rc == VINF_SUCCESS))
3366 rc = vga_mem_writeb(pThis, GCPhysAddr + 2, pu8[2]);
3367 if (RT_LIKELY(rc == VINF_SUCCESS))
3368 rc = vga_mem_writeb(pThis, GCPhysAddr + 3, pu8[3]);
3369 if (RT_LIKELY(rc == VINF_SUCCESS))
3370 rc = vga_mem_writeb(pThis, GCPhysAddr + 4, pu8[4]);
3371 if (RT_LIKELY(rc == VINF_SUCCESS))
3372 rc = vga_mem_writeb(pThis, GCPhysAddr + 5, pu8[5]);
3373 if (RT_LIKELY(rc == VINF_SUCCESS))
3374 rc = vga_mem_writeb(pThis, GCPhysAddr + 6, pu8[6]);
3375 if (RT_LIKELY(rc == VINF_SUCCESS))
3376 rc = vga_mem_writeb(pThis, GCPhysAddr + 7, pu8[7]);
3377 break;
3378#else
3379 case 2:
3380 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3381 break;
3382 case 4:
3383 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3384 break;
3385 case 8:
3386 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint64_t *)pv, 8, 1);
3387 break;
3388#endif
3389 default:
3390 rc = VINF_SUCCESS;
3391 while (cb-- > 0 && rc == VINF_SUCCESS)
3392 rc = vga_mem_writeb(pThis, GCPhysAddr++, *pu8++);
3393 break;
3394
3395 }
3396 STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
3397 return rc;
3398}
3399
3400
3401/**
3402 * Handle LFB access.
3403 * @returns VBox status code.
3404 * @param pVM VM handle.
3405 * @param pThis VGA device instance data.
3406 * @param GCPhys The access physical address.
3407 * @param GCPtr The access virtual address (only GC).
3408 */
3409static int vgaLFBAccess(PVM pVM, PVGASTATE pThis, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3410{
3411 int rc = PDMCritSectEnter(&pThis->CritSect, VINF_EM_RAW_EMULATE_INSTR);
3412 if (rc != VINF_SUCCESS)
3413 return rc;
3414
3415 /*
3416 * Set page dirty bit.
3417 */
3418 vga_set_dirty(pThis, GCPhys - pThis->GCPhysVRAM);
3419 pThis->fLFBUpdated = true;
3420
3421 /*
3422 * Turn of the write handler for this particular page and make it R/W.
3423 * Then return telling the caller to restart the guest instruction.
3424 * ASSUME: the guest always maps video memory RW.
3425 */
3426 rc = PGMHandlerPhysicalPageTempOff(pVM, pThis->GCPhysVRAM, GCPhys);
3427 if (RT_SUCCESS(rc))
3428 {
3429#ifndef IN_RING3
3430 rc = PGMShwMakePageWritable(PDMDevHlpGetVMCPU(pThis->CTX_SUFF(pDevIns)), GCPtr,
3431 PGM_MK_PG_IS_MMIO2 | PGM_MK_PG_IS_WRITE_FAULT);
3432 PDMCritSectLeave(&pThis->CritSect);
3433 AssertMsgReturn( rc == VINF_SUCCESS
3434 /* In the SMP case the page table might be removed while we wait for the PGM lock in the trap handler. */
3435 || rc == VERR_PAGE_TABLE_NOT_PRESENT
3436 || rc == VERR_PAGE_NOT_PRESENT,
3437 ("PGMShwModifyPage -> GCPtr=%RGv rc=%d\n", GCPtr, rc),
3438 rc);
3439#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3440 PDMCritSectLeave(&pThis->CritSect);
3441 Assert(GCPtr == 0);
3442#endif
3443 return VINF_SUCCESS;
3444 }
3445
3446 PDMCritSectLeave(&pThis->CritSect);
3447 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3448 return rc;
3449}
3450
3451
3452#ifdef IN_RC
3453/**
3454 * @callback_method_impl{FNPGMRCPHYSHANDLER, \#PF Handler for VBE LFB access.}
3455 */
3456PDMBOTHCBDECL(int) vgaRCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3457{
3458 PVGASTATE pThis = (PVGASTATE)pvUser;
3459 AssertPtr(pThis);
3460 Assert(GCPhysFault >= pThis->GCPhysVRAM);
3461 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3462 NOREF(pRegFrame);
3463
3464 return vgaLFBAccess(pVM, pThis, GCPhysFault, pvFault);
3465}
3466
3467#elif IN_RING0
3468
3469/**
3470 * @callback_method_impl{FNPGMR0PHYSHANDLER, \#PF Handler for VBE LFB access.}
3471 */
3472PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3473{
3474 PVGASTATE pThis = (PVGASTATE)pvUser;
3475 Assert(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#else /* IN_RING3 */
3484
3485/**
3486 * @callback_method_impl{FNPGMR3PHYSHANDLER, HC access handler for the LFB.}
3487 */
3488static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3489{
3490 PVGASTATE pThis = (PVGASTATE)pvUser;
3491 int rc;
3492 Assert(pThis);
3493 Assert(GCPhys >= pThis->GCPhysVRAM);
3494 NOREF(pvPhys); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmAccessType);
3495
3496 rc = vgaLFBAccess(pVM, pThis, GCPhys, 0);
3497 if (RT_SUCCESS(rc))
3498 return VINF_PGM_HANDLER_DO_DEFAULT;
3499 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Rrc\n", rc));
3500 return rc;
3501}
3502#endif /* IN_RING3 */
3503
3504/* -=-=-=-=-=- All rings: VGA BIOS I/Os -=-=-=-=-=- */
3505
3506/**
3507 * @callback_method_impl{FNIOMIOPORTIN,
3508 * Port I/O Handler for VGA BIOS IN operations.}
3509 */
3510PDMBOTHCBDECL(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3511{
3512 NOREF(pDevIns);
3513 NOREF(pvUser);
3514 NOREF(Port);
3515 NOREF(pu32);
3516 NOREF(cb);
3517 return VERR_IOM_IOPORT_UNUSED;
3518}
3519
3520/**
3521 * @callback_method_impl{FNIOMIOPORTOUT,
3522 * Port I/O Handler for VGA BIOS IN operations.}
3523 */
3524PDMBOTHCBDECL(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3525{
3526 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3527 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3528 NOREF(pvUser);
3529 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3530
3531 /*
3532 * VGA BIOS char printing.
3533 */
3534 if ( cb == 1
3535 && Port == VBE_PRINTF_PORT)
3536 {
3537#if 0
3538 switch (u32)
3539 {
3540 case '\r': Log(("vgabios: <return>\n")); break;
3541 case '\n': Log(("vgabios: <newline>\n")); break;
3542 case '\t': Log(("vgabios: <tab>\n")); break;
3543 default:
3544 Log(("vgabios: %c\n", u32));
3545 }
3546#else
3547 if (lastWasNotNewline == 0)
3548 Log(("vgabios: "));
3549 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3550 Log(("%c", u32));
3551 if (u32 == '\n')
3552 lastWasNotNewline = 0;
3553 else
3554 lastWasNotNewline = 1;
3555#endif
3556 return VINF_SUCCESS;
3557 }
3558
3559 /* not in use. */
3560 return VERR_IOM_IOPORT_UNUSED;
3561}
3562
3563
3564/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3565
3566#ifdef IN_RING3
3567
3568# ifdef VBE_NEW_DYN_LIST
3569/**
3570 * @callback_method_impl{FNIOMIOPORTOUT,
3571 * Port I/O Handler for VBE Extra OUT operations.}
3572 */
3573PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3574{
3575 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3576 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3577 NOREF(pvUser); NOREF(Port);
3578
3579 if (cb == 2)
3580 {
3581 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3582 pThis->u16VBEExtraAddress = u32;
3583 }
3584 else
3585 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3586
3587 return VINF_SUCCESS;
3588}
3589
3590
3591/**
3592 * @callback_method_impl{FNIOMIOPORTIN,
3593 * Port I/O Handler for VBE Extra IN operations.}
3594 */
3595PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3596{
3597 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3598 NOREF(pvUser); NOREF(Port);
3599 Assert(PDMCritSectIsOwner(pDevIns->CTX_SUFF(pCritSectRo)));
3600
3601 int rc = VINF_SUCCESS;
3602 if (pThis->u16VBEExtraAddress == 0xffff)
3603 {
3604 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3605 *pu32 = pThis->vram_size / _64K;
3606 }
3607 else if ( pThis->u16VBEExtraAddress >= pThis->cbVBEExtraData
3608 || pThis->u16VBEExtraAddress + cb > pThis->cbVBEExtraData)
3609 {
3610 *pu32 = 0;
3611 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3612 pThis->u16VBEExtraAddress, pThis->u16VBEExtraAddress, pThis->cbVBEExtraData, pThis->cbVBEExtraData));
3613 }
3614 else if (cb == 1)
3615 {
3616 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress] & 0xFF;
3617
3618 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3619 }
3620 else if (cb == 2)
3621 {
3622 *pu32 = pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress]
3623 | pThis->pu8VBEExtraData[pThis->u16VBEExtraAddress + 1] << 8;
3624
3625 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Rhxs\n", cb, cb, pu32));
3626 }
3627 else
3628 {
3629 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3630 rc = VERR_IOM_IOPORT_UNUSED;
3631 }
3632
3633 return rc;
3634}
3635# endif /* VBE_NEW_DYN_LIST */
3636
3637
3638/**
3639 * Parse the logo bitmap data at init time.
3640 *
3641 * @returns VBox status code.
3642 *
3643 * @param pThis The VGA instance data.
3644 */
3645static int vbeParseBitmap(PVGASTATE pThis)
3646{
3647 uint16_t i;
3648 PBMPINFO bmpInfo;
3649 POS2HDR pOs2Hdr;
3650 POS22HDR pOs22Hdr;
3651 PWINHDR pWinHdr;
3652
3653 /*
3654 * Get bitmap header data
3655 */
3656 bmpInfo = (PBMPINFO)(pThis->pu8Logo + sizeof(LOGOHDR));
3657 pWinHdr = (PWINHDR)(pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO));
3658
3659 if (bmpInfo->Type == BMP_ID)
3660 {
3661 switch (pWinHdr->Size)
3662 {
3663 case BMP_HEADER_OS21:
3664 pOs2Hdr = (POS2HDR)pWinHdr;
3665 pThis->cxLogo = pOs2Hdr->Width;
3666 pThis->cyLogo = pOs2Hdr->Height;
3667 pThis->cLogoPlanes = pOs2Hdr->Planes;
3668 pThis->cLogoBits = pOs2Hdr->BitCount;
3669 pThis->LogoCompression = BMP_COMPRESS_NONE;
3670 pThis->cLogoUsedColors = 0;
3671 break;
3672
3673 case BMP_HEADER_OS22:
3674 pOs22Hdr = (POS22HDR)pWinHdr;
3675 pThis->cxLogo = pOs22Hdr->Width;
3676 pThis->cyLogo = pOs22Hdr->Height;
3677 pThis->cLogoPlanes = pOs22Hdr->Planes;
3678 pThis->cLogoBits = pOs22Hdr->BitCount;
3679 pThis->LogoCompression = pOs22Hdr->Compression;
3680 pThis->cLogoUsedColors = pOs22Hdr->ClrUsed;
3681 break;
3682
3683 case BMP_HEADER_WIN3:
3684 pThis->cxLogo = pWinHdr->Width;
3685 pThis->cyLogo = pWinHdr->Height;
3686 pThis->cLogoPlanes = pWinHdr->Planes;
3687 pThis->cLogoBits = pWinHdr->BitCount;
3688 pThis->LogoCompression = pWinHdr->Compression;
3689 pThis->cLogoUsedColors = pWinHdr->ClrUsed;
3690 break;
3691
3692 default:
3693 AssertLogRelMsgFailedReturn(("Unsupported bitmap header size %u.\n", pWinHdr->Size),
3694 VERR_INVALID_PARAMETER);
3695 break;
3696 }
3697
3698 AssertLogRelMsgReturn(pThis->cxLogo <= LOGO_MAX_WIDTH && pThis->cyLogo <= LOGO_MAX_HEIGHT,
3699 ("Bitmap %ux%u is too big.\n", pThis->cxLogo, pThis->cyLogo),
3700 VERR_INVALID_PARAMETER);
3701
3702 AssertLogRelMsgReturn(pThis->cLogoPlanes == 1,
3703 ("Bitmap planes %u != 1.\n", pThis->cLogoPlanes),
3704 VERR_INVALID_PARAMETER);
3705
3706 AssertLogRelMsgReturn(pThis->cLogoBits == 4 || pThis->cLogoBits == 8 || pThis->cLogoBits == 24,
3707 ("Unsupported %u depth.\n", pThis->cLogoBits),
3708 VERR_INVALID_PARAMETER);
3709
3710 AssertLogRelMsgReturn(pThis->cLogoUsedColors <= 256,
3711 ("Unsupported %u colors.\n", pThis->cLogoUsedColors),
3712 VERR_INVALID_PARAMETER);
3713
3714 AssertLogRelMsgReturn(pThis->LogoCompression == BMP_COMPRESS_NONE,
3715 ("Unsupported %u compression.\n", pThis->LogoCompression),
3716 VERR_INVALID_PARAMETER);
3717
3718 /*
3719 * Read bitmap palette
3720 */
3721 if (!pThis->cLogoUsedColors)
3722 pThis->cLogoPalEntries = 1 << (pThis->cLogoPlanes * pThis->cLogoBits);
3723 else
3724 pThis->cLogoPalEntries = pThis->cLogoUsedColors;
3725
3726 if (pThis->cLogoPalEntries)
3727 {
3728 const uint8_t *pu8Pal = pThis->pu8Logo + sizeof(LOGOHDR) + sizeof(BMPINFO) + pWinHdr->Size; /* ASSUMES Size location (safe) */
3729
3730 for (i = 0; i < pThis->cLogoPalEntries; i++)
3731 {
3732 uint16_t j;
3733 uint32_t u32Pal = 0;
3734
3735 for (j = 0; j < 3; j++)
3736 {
3737 uint8_t b = *pu8Pal++;
3738 u32Pal <<= 8;
3739 u32Pal |= b;
3740 }
3741
3742 pu8Pal++; /* skip unused byte */
3743 pThis->au32LogoPalette[i] = u32Pal;
3744 }
3745 }
3746
3747 /*
3748 * Bitmap data offset
3749 */
3750 pThis->pu8LogoBitmap = pThis->pu8Logo + sizeof(LOGOHDR) + bmpInfo->Offset;
3751 }
3752 else
3753 AssertLogRelMsgFailedReturn(("Not a BMP file.\n"), VERR_INVALID_PARAMETER);
3754
3755 return VINF_SUCCESS;
3756}
3757
3758
3759/**
3760 * Show logo bitmap data.
3761 *
3762 * @returns VBox status code.
3763 *
3764 * @param cbDepth Logo depth.
3765 * @param xLogo Logo X position.
3766 * @param yLogo Logo Y position.
3767 * @param cxLogo Logo width.
3768 * @param cyLogo Logo height.
3769 * @param iStep Fade in/fade out step.
3770 * @param pu32Palette Palette data.
3771 * @param pu8Src Source buffer.
3772 * @param pu8Dst Destination buffer.
3773 */
3774static void vbeShowBitmap(uint16_t cBits, uint16_t xLogo, uint16_t yLogo, uint16_t cxLogo, uint16_t cyLogo, uint8_t iStep,
3775 const uint32_t *pu32Palette, const uint8_t *pu8Src, uint8_t *pu8Dst)
3776{
3777 uint16_t i;
3778 size_t cbPadBytes = 0;
3779 size_t cbLineDst = LOGO_MAX_WIDTH * 4;
3780 uint16_t cyLeft = cyLogo;
3781
3782 pu8Dst += xLogo * 4 + yLogo * cbLineDst;
3783
3784 switch (cBits)
3785 {
3786 case 1:
3787 pu8Dst += cyLogo * cbLineDst;
3788 cbPadBytes = 0;
3789 break;
3790
3791 case 4:
3792 if (((cxLogo % 8) == 0) || ((cxLogo % 8) > 6))
3793 cbPadBytes = 0;
3794 else if ((cxLogo % 8) <= 2)
3795 cbPadBytes = 3;
3796 else if ((cxLogo % 8) <= 4)
3797 cbPadBytes = 2;
3798 else
3799 cbPadBytes = 1;
3800 break;
3801
3802 case 8:
3803 cbPadBytes = ((cxLogo % 4) == 0) ? 0 : (4 - (cxLogo % 4));
3804 break;
3805
3806 case 24:
3807 cbPadBytes = cxLogo % 4;
3808 break;
3809 }
3810
3811 uint8_t j = 0, c = 0;
3812
3813 while (cyLeft-- > 0)
3814 {
3815 uint8_t *pu8TmpPtr = pu8Dst;
3816
3817 if (cBits != 1)
3818 j = 0;
3819
3820 for (i = 0; i < cxLogo; i++)
3821 {
3822 uint8_t pix;
3823
3824 switch (cBits)
3825 {
3826 case 1:
3827 {
3828 if (!j)
3829 c = *pu8Src++;
3830
3831 pix = (c & 1) ? 0xFF : 0;
3832 c >>= 1;
3833
3834 if (pix)
3835 {
3836 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3837 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3838 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3839 *pu8TmpPtr++;
3840 }
3841 else
3842 {
3843 pu8TmpPtr += 4;
3844 }
3845
3846 j = (j + 1) % 8;
3847 break;
3848 }
3849
3850 case 4:
3851 {
3852 if (!j)
3853 c = *pu8Src++;
3854
3855 pix = (c >> 4) & 0xF;
3856 c <<= 4;
3857
3858 uint32_t u32Pal = pu32Palette[pix];
3859
3860 pix = (u32Pal >> 16) & 0xFF;
3861 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3862 pix = (u32Pal >> 8) & 0xFF;
3863 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3864 pix = u32Pal & 0xFF;
3865 *pu8TmpPtr++ = pix * iStep / LOGO_SHOW_STEPS;
3866 *pu8TmpPtr++;
3867
3868 j = (j + 1) % 2;
3869 break;
3870 }
3871
3872 case 8:
3873 {
3874 uint32_t u32Pal = pu32Palette[*pu8Src++];
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 break;
3884 }
3885
3886 case 24:
3887 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3888 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3889 *pu8TmpPtr++ = *pu8Src++ * iStep / LOGO_SHOW_STEPS;
3890 *pu8TmpPtr++;
3891 break;
3892 }
3893 }
3894
3895 pu8Dst -= cbLineDst;
3896 pu8Src += cbPadBytes;
3897 }
3898}
3899
3900
3901/**
3902 * @callback_method_impl{FNIOMIOPORTOUT,
3903 * Port I/O Handler for BIOS Logo OUT operations.}
3904 */
3905PDMBOTHCBDECL(int) vbeIOPortWriteCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3906{
3907 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
3908 NOREF(pvUser);
3909 NOREF(Port);
3910
3911 Log(("vbeIOPortWriteCMDLogo: cb=%d u32=%#04x(%#04d) (byte)\n", cb, u32, u32));
3912
3913 if (cb == 2)
3914 {
3915 /* Get the logo command */
3916 switch (u32 & 0xFF00)
3917 {
3918 case LOGO_CMD_SET_OFFSET:
3919 pThis->offLogoData = u32 & 0xFF;
3920 break;
3921
3922 case LOGO_CMD_SHOW_BMP:
3923 {
3924 uint8_t iStep = u32 & 0xFF;
3925 const uint8_t *pu8Src = pThis->pu8LogoBitmap;
3926 uint8_t *pu8Dst;
3927 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
3928 uint32_t offDirty = 0;
3929 uint16_t xLogo = (LOGO_MAX_WIDTH - pThis->cxLogo) / 2;
3930 uint16_t yLogo = LOGO_MAX_HEIGHT - (LOGO_MAX_HEIGHT - pThis->cyLogo) / 2;
3931
3932 /* Check VRAM size */
3933 if (pThis->vram_size < LOGO_MAX_SIZE)
3934 break;
3935
3936 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
3937 pu8Dst = pThis->vram_ptrR3 + LOGO_MAX_SIZE;
3938 else
3939 pu8Dst = pThis->vram_ptrR3;
3940
3941 /* Clear screen - except on power on... */
3942 if (!pThis->fLogoClearScreen)
3943 {
3944 uint32_t *pu32TmpPtr = (uint32_t *)pu8Dst;
3945
3946 /* Clear vram */
3947 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
3948 {
3949 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
3950 *pu32TmpPtr++ = 0;
3951 }
3952 pThis->fLogoClearScreen = true;
3953 }
3954
3955 /* Show the bitmap. */
3956 vbeShowBitmap(pThis->cLogoBits, xLogo, yLogo,
3957 pThis->cxLogo, pThis->cyLogo,
3958 iStep, &pThis->au32LogoPalette[0],
3959 pu8Src, pu8Dst);
3960
3961 /* Show the 'Press F12...' text. */
3962 if (pLogoHdr->fu8ShowBootMenu == 2)
3963 vbeShowBitmap(1, LOGO_F12TEXT_X, LOGO_F12TEXT_Y,
3964 LOGO_F12TEXT_WIDTH, LOGO_F12TEXT_HEIGHT,
3965 iStep, &pThis->au32LogoPalette[0],
3966 &g_abLogoF12BootText[0], pu8Dst);
3967
3968 /* Blit the offscreen buffer. */
3969 if (pThis->vram_size >= LOGO_MAX_SIZE * 2)
3970 {
3971 uint32_t *pu32TmpDst = (uint32_t *)pThis->vram_ptrR3;
3972 uint32_t *pu32TmpSrc = (uint32_t *)(pThis->vram_ptrR3 + LOGO_MAX_SIZE);
3973 for (int i = 0; i < LOGO_MAX_WIDTH; i++)
3974 {
3975 for (int j = 0; j < LOGO_MAX_HEIGHT; j++)
3976 *pu32TmpDst++ = *pu32TmpSrc++;
3977 }
3978 }
3979
3980 /* Set the dirty flags. */
3981 while (offDirty <= LOGO_MAX_SIZE)
3982 {
3983 vga_set_dirty(pThis, offDirty);
3984 offDirty += PAGE_SIZE;
3985 }
3986 break;
3987 }
3988
3989 default:
3990 Log(("vbeIOPortWriteCMDLogo: invalid command %d\n", u32));
3991 pThis->LogoCommand = LOGO_CMD_NOP;
3992 break;
3993 }
3994
3995 return VINF_SUCCESS;
3996 }
3997
3998 Log(("vbeIOPortWriteCMDLogo: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3999 return VINF_SUCCESS;
4000}
4001
4002
4003/**
4004 * @callback_method_impl{FNIOMIOPORTIN,
4005 * Port I/O Handler for BIOS Logo IN operations.}
4006 */
4007PDMBOTHCBDECL(int) vbeIOPortReadCMDLogo(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
4008{
4009 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4010 NOREF(pvUser);
4011 NOREF(Port);
4012
4013 PRTUINT64U p;
4014
4015 if (pThis->offLogoData + cb > pThis->cbLogo)
4016 {
4017 Log(("vbeIOPortReadCMDLogo: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
4018 pThis->offLogoData, pThis->offLogoData, pThis->cbLogo, pThis->cbLogo));
4019 return VINF_SUCCESS;
4020 }
4021 p = (PRTUINT64U)&pThis->pu8Logo[pThis->offLogoData];
4022
4023 switch (cb)
4024 {
4025 case 1: *pu32 = p->au8[0]; break;
4026 case 2: *pu32 = p->au16[0]; break;
4027 case 4: *pu32 = p->au32[0]; break;
4028 //case 8: *pu32 = p->au64[0]; break;
4029 default: AssertFailed(); break;
4030 }
4031 Log(("vbeIOPortReadCMDLogo: LogoOffset=%#x(%d) cb=%#x %.*Rhxs\n", pThis->offLogoData, pThis->offLogoData, cb, cb, pu32));
4032
4033 pThis->LogoCommand = LOGO_CMD_NOP;
4034 pThis->offLogoData += cb;
4035
4036 return VINF_SUCCESS;
4037}
4038
4039
4040/* -=-=-=-=-=- Ring 3: Debug Info Handlers -=-=-=-=-=- */
4041
4042/**
4043 * @callback_method_impl{FNDBGFHANDLERDEV,
4044 * Dumps several interesting bits of the VGA state that are difficult to
4045 * decode from the registers.}
4046 */
4047static DECLCALLBACK(void) vgaInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4048{
4049 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4050 int is_graph, double_scan;
4051 int w, h, char_height, char_dots;
4052 int val, vfreq_hz, hfreq_hz;
4053 vga_retrace_s *r = &pThis->retrace_state;
4054 const char *clocks[] = { "25.175 MHz", "28.322 MHz", "External", "Reserved?!" };
4055 NOREF(pszArgs);
4056
4057 is_graph = pThis->gr[6] & 1;
4058 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9;
4059 double_scan = pThis->cr[9] >> 7;
4060 pHlp->pfnPrintf(pHlp, "pixel clock: %s\n", clocks[(pThis->msr >> 2) & 3]);
4061 pHlp->pfnPrintf(pHlp, "double scanning %s\n", double_scan ? "on" : "off");
4062 pHlp->pfnPrintf(pHlp, "double clocking %s\n", pThis->sr[1] & 0x08 ? "on" : "off");
4063 val = pThis->cr[0] + 5;
4064 pHlp->pfnPrintf(pHlp, "htotal: %d px (%d cclk)\n", val * char_dots, val);
4065 val = pThis->cr[6] + ((pThis->cr[7] & 1) << 8) + ((pThis->cr[7] & 0x20) << 4) + 2;
4066 pHlp->pfnPrintf(pHlp, "vtotal: %d px\n", val);
4067 val = pThis->cr[1] + 1;
4068 w = val * char_dots;
4069 pHlp->pfnPrintf(pHlp, "hdisp : %d px (%d cclk)\n", w, val);
4070 val = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4071 h = val;
4072 pHlp->pfnPrintf(pHlp, "vdisp : %d px\n", val);
4073 val = ((pThis->cr[9] & 0x40) << 3) + ((pThis->cr[7] & 0x10) << 4) + pThis->cr[0x18];
4074 pHlp->pfnPrintf(pHlp, "split : %d ln\n", val);
4075 val = (pThis->cr[0xc] << 8) + pThis->cr[0xd];
4076 pHlp->pfnPrintf(pHlp, "start : %#x\n", val);
4077 if (!is_graph)
4078 {
4079 val = (pThis->cr[9] & 0x1f) + 1;
4080 char_height = val;
4081 pHlp->pfnPrintf(pHlp, "char height %d\n", val);
4082 pHlp->pfnPrintf(pHlp, "text mode %dx%d\n", w / char_dots, h / (char_height << double_scan));
4083 }
4084 if (pThis->fRealRetrace)
4085 {
4086 val = r->hb_start;
4087 pHlp->pfnPrintf(pHlp, "hblank start: %d px (%d cclk)\n", val * char_dots, val);
4088 val = r->hb_end;
4089 pHlp->pfnPrintf(pHlp, "hblank end : %d px (%d cclk)\n", val * char_dots, val);
4090 pHlp->pfnPrintf(pHlp, "vblank start: %d px, end: %d px\n", r->vb_start, r->vb_end);
4091 pHlp->pfnPrintf(pHlp, "vsync start : %d px, end: %d px\n", r->vs_start, r->vs_end);
4092 pHlp->pfnPrintf(pHlp, "cclks per frame: %d\n", r->frame_cclks);
4093 pHlp->pfnPrintf(pHlp, "cclk time (ns) : %d\n", r->cclk_ns);
4094 vfreq_hz = 1000000000 / r->frame_ns;
4095 hfreq_hz = 1000000000 / r->h_total_ns;
4096 pHlp->pfnPrintf(pHlp, "vfreq: %d Hz, hfreq: %d.%03d kHz\n",
4097 vfreq_hz, hfreq_hz / 1000, hfreq_hz % 1000);
4098 }
4099 pHlp->pfnPrintf(pHlp, "display refresh interval: %u ms\n", pThis->cMilliesRefreshInterval);
4100}
4101
4102
4103/**
4104 * Prints a separator line.
4105 *
4106 * @param pHlp Callback functions for doing output.
4107 * @param cCols The number of columns.
4108 * @param pszTitle The title text, NULL if none.
4109 */
4110static void vgaInfoTextPrintSeparatorLine(PCDBGFINFOHLP pHlp, size_t cCols, const char *pszTitle)
4111{
4112 if (pszTitle)
4113 {
4114 size_t cchTitle = strlen(pszTitle);
4115 if (cchTitle + 6 >= cCols)
4116 {
4117 pHlp->pfnPrintf(pHlp, "-- %s --", pszTitle);
4118 cCols = 0;
4119 }
4120 else
4121 {
4122 size_t cchLeft = (cCols - cchTitle - 2) / 2;
4123 cCols -= cchLeft + cchTitle + 2;
4124 while (cchLeft-- > 0)
4125 pHlp->pfnPrintf(pHlp, "-");
4126 pHlp->pfnPrintf(pHlp, " %s ", pszTitle);
4127 }
4128 }
4129
4130 while (cCols-- > 0)
4131 pHlp->pfnPrintf(pHlp, "-");
4132 pHlp->pfnPrintf(pHlp, "\n");
4133}
4134
4135
4136/**
4137 * Worker for vgaInfoText.
4138 *
4139 * @param pThis The vga state.
4140 * @param pHlp Callback functions for doing output.
4141 * @param offStart Where to start dumping (relative to the VRAM).
4142 * @param cbLine The source line length (aka line_offset).
4143 * @param cCols The number of columns on the screen.
4144 * @param cRows The number of rows to dump.
4145 * @param iScrBegin The row at which the current screen output starts.
4146 * @param iScrEnd The row at which the current screen output end
4147 * (exclusive).
4148 */
4149static void vgaInfoTextWorker(PVGASTATE pThis, PCDBGFINFOHLP pHlp,
4150 uint32_t offStart, uint32_t cbLine,
4151 uint32_t cCols, uint32_t cRows,
4152 uint32_t iScrBegin, uint32_t iScrEnd)
4153{
4154 /* Title, */
4155 char szTitle[32];
4156 if (iScrBegin || iScrEnd < cRows)
4157 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u (+%u before, +%u after)",
4158 cCols, iScrEnd - iScrBegin, iScrBegin, cRows - iScrEnd);
4159 else
4160 RTStrPrintf(szTitle, sizeof(szTitle), "%ux%u", cCols, iScrEnd - iScrBegin);
4161
4162 /* Do the dumping. */
4163 uint8_t const *pbSrcOuter = pThis->CTX_SUFF(vram_ptr) + offStart;
4164 uint32_t iRow;
4165 for (iRow = 0; iRow < cRows; iRow++, pbSrcOuter += cbLine)
4166 {
4167 if ((uintptr_t)(pbSrcOuter + cbLine - pThis->CTX_SUFF(vram_ptr)) > pThis->vram_size) {
4168 pHlp->pfnPrintf(pHlp, "The last %u row/rows is/are outside the VRAM.\n", cRows - iRow);
4169 break;
4170 }
4171
4172 if (iRow == 0)
4173 vgaInfoTextPrintSeparatorLine(pHlp, cCols, szTitle);
4174 else if (iRow == iScrBegin)
4175 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen start");
4176 else if (iRow == iScrEnd)
4177 vgaInfoTextPrintSeparatorLine(pHlp, cCols, "screen end");
4178
4179 uint8_t const *pbSrc = pbSrcOuter;
4180 for (uint32_t iCol = 0; iCol < cCols; ++iCol)
4181 {
4182 if (RT_C_IS_PRINT(*pbSrc))
4183 pHlp->pfnPrintf(pHlp, "%c", *pbSrc);
4184 else
4185 pHlp->pfnPrintf(pHlp, ".");
4186 pbSrc += 8; /* chars are spaced 8 bytes apart */
4187 }
4188 pHlp->pfnPrintf(pHlp, "\n");
4189 }
4190
4191 /* Final separator. */
4192 vgaInfoTextPrintSeparatorLine(pHlp, cCols, NULL);
4193}
4194
4195
4196/**
4197 * @callback_method_impl{FNDBGFHANDLERDEV,
4198 * Dumps VGA memory formatted as ASCII text, no attributes. Only looks at the
4199 * first page.}
4200 */
4201static DECLCALLBACK(void) vgaInfoText(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4202{
4203 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4204
4205 /*
4206 * Parse args.
4207 */
4208 bool fAll = true;
4209 if (pszArgs && *pszArgs)
4210 {
4211 if (!strcmp(pszArgs, "all"))
4212 fAll = true;
4213 else if (!strcmp(pszArgs, "scr") || !strcmp(pszArgs, "screen"))
4214 fAll = false;
4215 else
4216 {
4217 pHlp->pfnPrintf(pHlp, "Invalid argument: '%s'\n", pszArgs);
4218 return;
4219 }
4220 }
4221
4222 /*
4223 * Check that we're in text mode and that the VRAM is accessible.
4224 */
4225 if (!(pThis->gr[6] & 1))
4226 {
4227 uint8_t *pbSrc = pThis->vram_ptrR3;
4228 if (pbSrc)
4229 {
4230 /*
4231 * Figure out the display size and where the text is.
4232 *
4233 * Note! We're cutting quite a few corners here and this code could
4234 * do with some brushing up. Dumping from the start of the
4235 * frame buffer is done intentionally so that we're more
4236 * likely to obtain the full scrollback of a linux panic.
4237 */
4238 uint32_t cbLine;
4239 uint32_t offStart;
4240 uint32_t uLineCompareIgn;
4241 vga_get_offsets(pThis, &cbLine, &offStart, &uLineCompareIgn);
4242 if (!cbLine)
4243 cbLine = 80 * 8;
4244 offStart *= 8;
4245
4246 uint32_t uVDisp = pThis->cr[0x12] + ((pThis->cr[7] & 2) << 7) + ((pThis->cr[7] & 0x40) << 4) + 1;
4247 uint32_t uCharHeight = (pThis->cr[9] & 0x1f) + 1;
4248 uint32_t uDblScan = pThis->cr[9] >> 7;
4249 uint32_t cScrRows = uVDisp / (uCharHeight << uDblScan);
4250 if (cScrRows < 25)
4251 cScrRows = 25;
4252 uint32_t iScrBegin = offStart / cbLine;
4253 uint32_t cRows = iScrBegin + cScrRows;
4254 uint32_t cCols = cbLine / 8;
4255
4256 if (fAll) {
4257 vgaInfoTextWorker(pThis, pHlp, offStart - iScrBegin * cbLine, cbLine,
4258 cCols, cRows, iScrBegin, iScrBegin + cScrRows);
4259 } else {
4260 vgaInfoTextWorker(pThis, pHlp, offStart, cbLine, cCols, cScrRows, 0, cScrRows);
4261 }
4262 }
4263 else
4264 pHlp->pfnPrintf(pHlp, "VGA memory not available!\n");
4265 }
4266 else
4267 pHlp->pfnPrintf(pHlp, "Not in text mode!\n");
4268}
4269
4270
4271/**
4272 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA Sequencer registers.}
4273 */
4274static DECLCALLBACK(void) vgaInfoSR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4275{
4276 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4277 unsigned i;
4278 NOREF(pszArgs);
4279
4280 pHlp->pfnPrintf(pHlp, "VGA Sequencer (3C5): SR index 3C4:%02X\n", pThis->sr_index);
4281 Assert(sizeof(pThis->sr) >= 8);
4282 for (i = 0; i < 5; ++i)
4283 pHlp->pfnPrintf(pHlp, " SR%02X:%02X", i, pThis->sr[i]);
4284 pHlp->pfnPrintf(pHlp, "\n");
4285}
4286
4287
4288/**
4289 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA CRTC registers.}
4290 */
4291static DECLCALLBACK(void) vgaInfoCR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4292{
4293 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4294 unsigned i;
4295 NOREF(pszArgs);
4296
4297 pHlp->pfnPrintf(pHlp, "VGA CRTC (3D5): CRTC index 3D4:%02X\n", pThis->cr_index);
4298 Assert(sizeof(pThis->cr) >= 24);
4299 for (i = 0; i < 10; ++i)
4300 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4301 pHlp->pfnPrintf(pHlp, "\n");
4302 for (i = 10; i < 20; ++i)
4303 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4304 pHlp->pfnPrintf(pHlp, "\n");
4305 for (i = 20; i < 25; ++i)
4306 pHlp->pfnPrintf(pHlp, " CR%02X:%02X", i, pThis->cr[i]);
4307 pHlp->pfnPrintf(pHlp, "\n");
4308}
4309
4310
4311/**
4312 * @callback_method_impl{FNDBGFHANDLERDEV,
4313 * Dumps VGA Graphics Controller registers.}
4314 */
4315static DECLCALLBACK(void) vgaInfoGR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4316{
4317 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4318 unsigned i;
4319 NOREF(pszArgs);
4320
4321 pHlp->pfnPrintf(pHlp, "VGA Graphics Controller (3CF): GR index 3CE:%02X\n", pThis->gr_index);
4322 Assert(sizeof(pThis->gr) >= 9);
4323 for (i = 0; i < 9; ++i)
4324 {
4325 pHlp->pfnPrintf(pHlp, " GR%02X:%02X", i, pThis->gr[i]);
4326 }
4327 pHlp->pfnPrintf(pHlp, "\n");
4328}
4329
4330
4331/**
4332 * @callback_method_impl{FNDBGFHANDLERDEV,
4333 * Dumps VGA Attribute Controller registers.}
4334 */
4335static DECLCALLBACK(void) vgaInfoAR(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4336{
4337 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4338 unsigned i;
4339 NOREF(pszArgs);
4340
4341 pHlp->pfnPrintf(pHlp, "VGA Attribute Controller (3C0): index reg %02X, flip-flop: %d (%s)\n",
4342 pThis->ar_index, pThis->ar_flip_flop, pThis->ar_flip_flop ? "data" : "index" );
4343 Assert(sizeof(pThis->ar) >= 0x14);
4344 pHlp->pfnPrintf(pHlp, " Palette:");
4345 for (i = 0; i < 0x10; ++i)
4346 pHlp->pfnPrintf(pHlp, " %02X", pThis->ar[i]);
4347 pHlp->pfnPrintf(pHlp, "\n");
4348 for (i = 0x10; i <= 0x14; ++i)
4349 pHlp->pfnPrintf(pHlp, " AR%02X:%02X", i, pThis->ar[i]);
4350 pHlp->pfnPrintf(pHlp, "\n");
4351}
4352
4353
4354/**
4355 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VGA DAC registers.}
4356 */
4357static DECLCALLBACK(void) vgaInfoDAC(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4358{
4359 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4360 unsigned i;
4361 NOREF(pszArgs);
4362
4363 pHlp->pfnPrintf(pHlp, "VGA DAC contents:\n");
4364 for (i = 0; i < 0x100; ++i)
4365 pHlp->pfnPrintf(pHlp, " %02X: %02X %02X %02X\n",
4366 i, pThis->palette[i*3+0], pThis->palette[i*3+1], pThis->palette[i*3+2]);
4367}
4368
4369
4370/**
4371 * @callback_method_impl{FNDBGFHANDLERDEV, Dumps VBE registers.}
4372 */
4373static DECLCALLBACK(void) vgaInfoVBE(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4374{
4375 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4376 NOREF(pszArgs);
4377
4378 pHlp->pfnPrintf(pHlp, "LFB at %RGp\n", pThis->GCPhysVRAM);
4379
4380 if (!(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
4381 {
4382 pHlp->pfnPrintf(pHlp, "VBE disabled\n");
4383 return;
4384 }
4385
4386 pHlp->pfnPrintf(pHlp, "VBE state (chip ID 0x%04x):\n", pThis->vbe_regs[VBE_DISPI_INDEX_ID]);
4387 pHlp->pfnPrintf(pHlp, " Display resolution: %d x %d @ %dbpp\n",
4388 pThis->vbe_regs[VBE_DISPI_INDEX_XRES], pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
4389 pThis->vbe_regs[VBE_DISPI_INDEX_BPP]);
4390 pHlp->pfnPrintf(pHlp, " Virtual resolution: %d x %d\n",
4391 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
4392 pHlp->pfnPrintf(pHlp, " Display start addr: %d, %d\n",
4393 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET], pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]);
4394 pHlp->pfnPrintf(pHlp, " Linear scanline pitch: 0x%04x\n", pThis->vbe_line_offset);
4395 pHlp->pfnPrintf(pHlp, " Linear display start : 0x%04x\n", pThis->vbe_start_addr);
4396 pHlp->pfnPrintf(pHlp, " Selected bank: 0x%04x\n", pThis->vbe_regs[VBE_DISPI_INDEX_BANK]);
4397}
4398
4399
4400/**
4401 * @callback_method_impl{FNDBGFHANDLERDEV,
4402 * Dumps register state relevant to 16-color planar graphics modes (GR/SR)
4403 * in human-readable form.}
4404 */
4405static DECLCALLBACK(void) vgaInfoPlanar(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
4406{
4407 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
4408 int val1, val2;
4409 NOREF(pszArgs);
4410
4411 val1 = (pThis->gr[5] >> 3) & 1;
4412 val2 = pThis->gr[5] & 3;
4413 pHlp->pfnPrintf(pHlp, "read mode : %d write mode: %d\n", val1, val2);
4414 val1 = pThis->gr[0];
4415 val2 = pThis->gr[1];
4416 pHlp->pfnPrintf(pHlp, "set/reset data: %02X S/R enable: %02X\n", val1, val2);
4417 val1 = pThis->gr[2];
4418 val2 = pThis->gr[4] & 3;
4419 pHlp->pfnPrintf(pHlp, "color compare : %02X read map : %d\n", val1, val2);
4420 val1 = pThis->gr[3] & 7;
4421 val2 = (pThis->gr[3] >> 3) & 3;
4422 pHlp->pfnPrintf(pHlp, "rotate : %d function : %d\n", val1, val2);
4423 val1 = pThis->gr[7];
4424 val2 = pThis->gr[8];
4425 pHlp->pfnPrintf(pHlp, "don't care : %02X bit mask : %02X\n", val1, val2);
4426 val1 = pThis->sr[2];
4427 val2 = pThis->sr[4] & 8;
4428 pHlp->pfnPrintf(pHlp, "seq plane mask: %02X chain-4 : %s\n", val1, val2 ? "on" : "off");
4429}
4430
4431
4432/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
4433
4434/**
4435 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
4436 */
4437static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, const char *pszIID)
4438{
4439 PVGASTATE pThis = RT_FROM_MEMBER(pInterface, VGASTATE, IBase);
4440 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
4441 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYPORT, &pThis->IPort);
4442#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_CRHGSMI))
4443 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIDISPLAYVBVACALLBACKS, &pThis->IVBVACallbacks);
4444#endif
4445 return NULL;
4446}
4447
4448
4449/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
4450
4451/**
4452 * Resize the display.
4453 * This is called when the resolution changes. This usually happens on
4454 * request from the guest os, but may also happen as the result of a reset.
4455 *
4456 * @param pInterface Pointer to this interface.
4457 * @param cx New display width.
4458 * @param cy New display height
4459 * @thread The emulation thread.
4460 */
4461static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM,
4462 uint32_t cbLine, uint32_t cx, uint32_t cy)
4463{
4464 NOREF(pInterface); NOREF(bpp); NOREF(pvVRAM); NOREF(cbLine); NOREF(cx); NOREF(cy);
4465 return VINF_SUCCESS;
4466}
4467
4468
4469/**
4470 * Update a rectangle of the display.
4471 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
4472 *
4473 * @param pInterface Pointer to this interface.
4474 * @param x The upper left corner x coordinate of the rectangle.
4475 * @param y The upper left corner y coordinate of the rectangle.
4476 * @param cx The width of the rectangle.
4477 * @param cy The height of the rectangle.
4478 * @thread The emulation thread.
4479 */
4480static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4481{
4482 NOREF(pInterface); NOREF(x); NOREF(y); NOREF(cx); NOREF(cy);
4483}
4484
4485
4486/**
4487 * Refresh the display.
4488 *
4489 * The interval between these calls is set by
4490 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
4491 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
4492 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
4493 * the changed rectangles.
4494 *
4495 * @param pInterface Pointer to this interface.
4496 * @thread The emulation thread.
4497 */
4498static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
4499{
4500 NOREF(pInterface);
4501}
4502
4503
4504/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
4505
4506/** Converts a display port interface pointer to a vga state pointer. */
4507#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, IPort)) )
4508
4509
4510/**
4511 * Update the display with any changed regions.
4512 *
4513 * @param pInterface Pointer to this interface.
4514 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
4515 */
4516static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
4517{
4518 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4519 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4520 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4521
4522 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4523 AssertRC(rc);
4524
4525#ifdef VBOX_WITH_VMSVGA
4526 if ( pThis->svga.fEnabled
4527 && !pThis->svga.fTraces)
4528 {
4529 /* Nothing to do as the guest will explicitely update us about frame buffer changes. */
4530 PDMCritSectLeave(&pThis->CritSect);
4531 return VINF_SUCCESS;
4532 }
4533#endif
4534
4535#ifndef VBOX_WITH_HGSMI
4536 /* This should be called only in non VBVA mode. */
4537#else
4538 if (VBVAUpdateDisplay (pThis) == VINF_SUCCESS)
4539 {
4540 PDMCritSectLeave(&pThis->CritSect);
4541 return VINF_SUCCESS;
4542 }
4543#endif /* VBOX_WITH_HGSMI */
4544
4545 STAM_COUNTER_INC(&pThis->StatUpdateDisp);
4546 if (pThis->fHasDirtyBits && pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4547 {
4548 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4549 pThis->fHasDirtyBits = false;
4550 }
4551 if (pThis->fRemappedVGA)
4552 {
4553 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4554 pThis->fRemappedVGA = false;
4555 }
4556
4557 rc = vga_update_display(pThis, false, false, true);
4558 PDMCritSectLeave(&pThis->CritSect);
4559 return rc;
4560}
4561
4562
4563/**
4564 * Internal vgaPortUpdateDisplayAll worker called under pThis->CritSect.
4565 */
4566static int updateDisplayAll(PVGASTATE pThis)
4567{
4568 PPDMDEVINS pDevIns = pThis->CTX_SUFF(pDevIns);
4569
4570#ifdef VBOX_WITH_VMSVGA
4571 if ( !pThis->svga.fEnabled
4572 || pThis->svga.fTraces)
4573 {
4574#endif
4575 /* The dirty bits array has been just cleared, reset handlers as well. */
4576 if (pThis->GCPhysVRAM && pThis->GCPhysVRAM != NIL_RTGCPHYS)
4577 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
4578#ifdef VBOX_WITH_VMSVGA
4579 }
4580#endif
4581 if (pThis->fRemappedVGA)
4582 {
4583 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
4584 pThis->fRemappedVGA = false;
4585 }
4586
4587 pThis->graphic_mode = -1; /* force full update */
4588
4589 return vga_update_display(pThis, true, false, true);
4590}
4591
4592
4593/**
4594 * Update the entire display.
4595 *
4596 * @param pInterface Pointer to this interface.
4597 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
4598 */
4599static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
4600{
4601 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4602 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4603
4604 /* This is called both in VBVA mode and normal modes. */
4605
4606#ifdef DEBUG_sunlover
4607 LogFlow(("vgaPortUpdateDisplayAll\n"));
4608#endif /* DEBUG_sunlover */
4609
4610 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4611 AssertRC(rc);
4612
4613 rc = updateDisplayAll(pThis);
4614
4615 PDMCritSectLeave(&pThis->CritSect);
4616 return rc;
4617}
4618
4619
4620/**
4621 * Sets the refresh rate and restart the timer.
4622 *
4623 * @returns VBox status code.
4624 * @param pInterface Pointer to this interface.
4625 * @param cMilliesInterval Number of millis between two refreshes.
4626 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
4627 */
4628static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
4629{
4630 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4631
4632 pThis->cMilliesRefreshInterval = cMilliesInterval;
4633 if (cMilliesInterval)
4634 return TMTimerSetMillies(pThis->RefreshTimer, cMilliesInterval);
4635 return TMTimerStop(pThis->RefreshTimer);
4636}
4637
4638
4639/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
4640static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
4641{
4642 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4643
4644 if (!pcBits)
4645 return VERR_INVALID_PARAMETER;
4646 *pcBits = vga_get_bpp(pThis);
4647 return VINF_SUCCESS;
4648}
4649
4650
4651/**
4652 * Create a 32-bbp screenshot of the display. Size of the bitmap scanline in bytes is 4*width.
4653 *
4654 * @param pInterface Pointer to this interface.
4655 * @param ppu8Data Where to store the pointer to the allocated buffer.
4656 * @param pcbData Where to store the actual size of the bitmap.
4657 * @param pcx Where to store the width of the bitmap.
4658 * @param pcy Where to store the height of the bitmap.
4659 * @see PDMIDISPLAYPORT::pfnTakeScreenshot() for details.
4660 */
4661static DECLCALLBACK(int) vgaPortTakeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t **ppu8Data, size_t *pcbData, uint32_t *pcx, uint32_t *pcy)
4662{
4663 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4664 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4665
4666 LogFlow(("vgaPortTakeScreenshot: ppu8Data=%p pcbData=%p pcx=%p pcy=%p\n", ppu8Data, pcbData, pcx, pcy));
4667
4668 /*
4669 * Validate input.
4670 */
4671 if (!RT_VALID_PTR(ppu8Data) || !RT_VALID_PTR(pcbData) || !RT_VALID_PTR(pcx) || !RT_VALID_PTR(pcy))
4672 return VERR_INVALID_PARAMETER;
4673
4674 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4675 AssertRCReturn(rc, rc);
4676
4677 /*
4678 * Get screenshot. This function will fail if a resize is required.
4679 * So there is not need to do a 'updateDisplayAll' before taking screenshot.
4680 */
4681
4682 /*
4683 * Allocate the buffer for 32 bits per pixel bitmap
4684 *
4685 * Note! The size can't be zero or greater than the size of the VRAM.
4686 * Inconsistent VGA device state can cause the incorrect size values.
4687 */
4688 size_t cbRequired = pThis->last_scr_width * 4 * pThis->last_scr_height;
4689 if (cbRequired && cbRequired <= pThis->vram_size)
4690 {
4691 uint8_t *pu8Data = (uint8_t *)RTMemAlloc(cbRequired);
4692 if (pu8Data != NULL)
4693 {
4694 /*
4695 * Only 3 methods, assigned below, will be called during the screenshot update.
4696 * All other are already set to NULL.
4697 */
4698 /* The display connector interface is temporarily replaced with the fake one. */
4699 PDMIDISPLAYCONNECTOR Connector;
4700 RT_ZERO(Connector);
4701 Connector.pu8Data = pu8Data;
4702 Connector.cBits = 32;
4703 Connector.cx = pThis->last_scr_width;
4704 Connector.cy = pThis->last_scr_height;
4705 Connector.cbScanline = Connector.cx * 4;
4706 Connector.pfnRefresh = vgaDummyRefresh;
4707 Connector.pfnResize = vgaDummyResize;
4708 Connector.pfnUpdateRect = vgaDummyUpdateRect;
4709
4710 /* Save & replace state data. */
4711 PPDMIDISPLAYCONNECTOR pConnectorSaved = pThis->pDrv;
4712 int32_t graphic_mode_saved = pThis->graphic_mode;
4713 bool fRenderVRAMSaved = pThis->fRenderVRAM;
4714
4715 pThis->pDrv = &Connector;
4716 pThis->graphic_mode = -1; /* force a full refresh. */
4717 pThis->fRenderVRAM = 1; /* force the guest VRAM rendering to the given buffer. */
4718
4719 /*
4720 * Make the screenshot.
4721 *
4722 * The second parameter is 'false' because the current display state is being rendered to an
4723 * external buffer using a fake connector. That is if display is blanked, we expect a black
4724 * screen in the external buffer.
4725 * If there is a pending resize, the function will fail.
4726 */
4727 rc = vga_update_display(pThis, false, true, false);
4728
4729 /* Restore. */
4730 pThis->pDrv = pConnectorSaved;
4731 pThis->graphic_mode = graphic_mode_saved;
4732 pThis->fRenderVRAM = fRenderVRAMSaved;
4733
4734 if (rc == VINF_SUCCESS)
4735 {
4736 /*
4737 * Return the result.
4738 */
4739 *ppu8Data = pu8Data;
4740 *pcbData = cbRequired;
4741 *pcx = Connector.cx;
4742 *pcy = Connector.cy;
4743 }
4744 else
4745 {
4746 /* If we do not return a success, then the data buffer must be freed. */
4747 RTMemFree(pu8Data);
4748 if (RT_SUCCESS_NP(rc))
4749 {
4750 AssertMsgFailed(("%Rrc\n", rc));
4751 rc = VERR_INTERNAL_ERROR_5;
4752 }
4753 }
4754 }
4755 else
4756 rc = VERR_NO_MEMORY;
4757 }
4758 else
4759 rc = VERR_NOT_SUPPORTED;
4760
4761 PDMCritSectLeave(&pThis->CritSect);
4762
4763 LogFlow(("vgaPortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
4764 return rc;
4765}
4766
4767/**
4768 * Free a screenshot buffer allocated in vgaPortTakeScreenshot.
4769 *
4770 * @param pInterface Pointer to this interface.
4771 * @param pu8Data Pointer returned by vgaPortTakeScreenshot.
4772 * @see PDMIDISPLAYPORT::pfnFreeScreenshot() for details.
4773 */
4774static DECLCALLBACK(void) vgaPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pu8Data)
4775{
4776 NOREF(pInterface);
4777
4778 LogFlow(("vgaPortFreeScreenshot: pu8Data=%p\n", pu8Data));
4779
4780 RTMemFree(pu8Data);
4781}
4782
4783/**
4784 * Copy bitmap to the display.
4785 *
4786 * @param pInterface Pointer to this interface.
4787 * @param pvData Pointer to the bitmap bits.
4788 * @param x The upper left corner x coordinate of the destination rectangle.
4789 * @param y The upper left corner y coordinate of the destination rectangle.
4790 * @param cx The width of the source and destination rectangles.
4791 * @param cy The height of the source and destination rectangles.
4792 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
4793 */
4794static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4795{
4796 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4797 int rc = VINF_SUCCESS;
4798 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4799 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
4800
4801 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4802 AssertRC(rc);
4803
4804 /*
4805 * Validate input.
4806 */
4807 if ( pvData
4808 && x < pThis->pDrv->cx
4809 && cx <= pThis->pDrv->cx
4810 && cx + x <= pThis->pDrv->cx
4811 && y < pThis->pDrv->cy
4812 && cy <= pThis->pDrv->cy
4813 && cy + y <= pThis->pDrv->cy)
4814 {
4815 /*
4816 * Determine bytes per pixel in the destination buffer.
4817 */
4818 size_t cbPixelDst = 0;
4819 switch (pThis->pDrv->cBits)
4820 {
4821 case 8:
4822 cbPixelDst = 1;
4823 break;
4824 case 15:
4825 case 16:
4826 cbPixelDst = 2;
4827 break;
4828 case 24:
4829 cbPixelDst = 3;
4830 break;
4831 case 32:
4832 cbPixelDst = 4;
4833 break;
4834 default:
4835 rc = VERR_INVALID_PARAMETER;
4836 break;
4837 }
4838 if (RT_SUCCESS(rc))
4839 {
4840 /*
4841 * The blitting loop.
4842 */
4843 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
4844 uint8_t *pu8Src = (uint8_t *)pvData;
4845 size_t cbLineDst = pThis->pDrv->cbScanline;
4846 uint8_t *pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4847 uint32_t cyLeft = cy;
4848 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
4849 Assert(pfnVgaDrawLine);
4850 while (cyLeft-- > 0)
4851 {
4852 pfnVgaDrawLine(pThis, pu8Dst, pu8Src, cx);
4853 pu8Dst += cbLineDst;
4854 pu8Src += cbLineSrc;
4855 }
4856
4857 /*
4858 * Invalidate the area.
4859 */
4860 pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
4861 }
4862 }
4863 else
4864 rc = VERR_INVALID_PARAMETER;
4865
4866 PDMCritSectLeave(&pThis->CritSect);
4867
4868 LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
4869 return rc;
4870}
4871
4872static DECLCALLBACK(void) vgaPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
4873{
4874 uint32_t v;
4875 vga_draw_line_func *vga_draw_line;
4876
4877 uint32_t cbPixelDst;
4878 uint32_t cbLineDst;
4879 uint8_t *pu8Dst;
4880
4881 uint32_t cbPixelSrc;
4882 uint32_t cbLineSrc;
4883 uint8_t *pu8Src;
4884
4885 uint32_t u32OffsetSrc, u32Dummy;
4886
4887 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4888
4889#ifdef DEBUG_sunlover
4890 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
4891#endif /* DEBUG_sunlover */
4892
4893 Assert(pInterface);
4894 Assert(pThis->pDrv);
4895 Assert(pThis->pDrv->pu8Data);
4896
4897 /* Check if there is something to do at all. */
4898 if (!pThis->fRenderVRAM)
4899 {
4900 /* The framebuffer uses the guest VRAM directly. */
4901#ifdef DEBUG_sunlover
4902 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
4903#endif /* DEBUG_sunlover */
4904 return;
4905 }
4906
4907 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4908 AssertRC(rc);
4909
4910 /* Correct negative x and y coordinates. */
4911 if (x < 0)
4912 {
4913 x += w; /* Compute xRight which is also the new width. */
4914 w = (x < 0) ? 0 : x;
4915 x = 0;
4916 }
4917
4918 if (y < 0)
4919 {
4920 y += h; /* Compute yBottom, which is also the new height. */
4921 h = (y < 0) ? 0 : y;
4922 y = 0;
4923 }
4924
4925 /* Also check if coords are greater than the display resolution. */
4926 if (x + w > pThis->pDrv->cx)
4927 {
4928 // x < 0 is not possible here
4929 w = pThis->pDrv->cx > (uint32_t)x? pThis->pDrv->cx - x: 0;
4930 }
4931
4932 if (y + h > pThis->pDrv->cy)
4933 {
4934 // y < 0 is not possible here
4935 h = pThis->pDrv->cy > (uint32_t)y? pThis->pDrv->cy - y: 0;
4936 }
4937
4938#ifdef DEBUG_sunlover
4939 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
4940#endif /* DEBUG_sunlover */
4941
4942 /* Check if there is something to do at all. */
4943 if (w == 0 || h == 0)
4944 {
4945 /* Empty rectangle. */
4946#ifdef DEBUG_sunlover
4947 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
4948#endif /* DEBUG_sunlover */
4949 PDMCritSectLeave(&pThis->CritSect);
4950 return;
4951 }
4952
4953 /** @todo This method should be made universal and not only for VBVA.
4954 * VGA_DRAW_LINE* must be selected and src/dst address calculation
4955 * changed.
4956 */
4957
4958 /* Choose the rendering function. */
4959 switch(pThis->get_bpp(pThis))
4960 {
4961 default:
4962 case 0:
4963 /* A LFB mode is already disabled, but the callback is still called
4964 * by Display because VBVA buffer is being flushed.
4965 * Nothing to do, just return.
4966 */
4967 PDMCritSectLeave(&pThis->CritSect);
4968 return;
4969 case 8:
4970 v = VGA_DRAW_LINE8;
4971 break;
4972 case 15:
4973 v = VGA_DRAW_LINE15;
4974 break;
4975 case 16:
4976 v = VGA_DRAW_LINE16;
4977 break;
4978 case 24:
4979 v = VGA_DRAW_LINE24;
4980 break;
4981 case 32:
4982 v = VGA_DRAW_LINE32;
4983 break;
4984 }
4985
4986 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pThis->pDrv->cBits)];
4987
4988 /* Compute source and destination addresses and pitches. */
4989 cbPixelDst = (pThis->pDrv->cBits + 7) / 8;
4990 cbLineDst = pThis->pDrv->cbScanline;
4991 pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4992
4993 cbPixelSrc = (pThis->get_bpp(pThis) + 7) / 8;
4994 pThis->get_offsets(pThis, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
4995
4996 /* Assume that rendering is performed only on visible part of VRAM.
4997 * This is true because coordinates were verified.
4998 */
4999 pu8Src = pThis->vram_ptrR3;
5000 pu8Src += u32OffsetSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5001
5002 /* Render VRAM to framebuffer. */
5003
5004#ifdef DEBUG_sunlover
5005 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
5006#endif /* DEBUG_sunlover */
5007
5008 while (h-- > 0)
5009 {
5010 vga_draw_line (pThis, pu8Dst, pu8Src, w);
5011 pu8Dst += cbLineDst;
5012 pu8Src += cbLineSrc;
5013 }
5014
5015 PDMCritSectLeave(&pThis->CritSect);
5016#ifdef DEBUG_sunlover
5017 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
5018#endif /* DEBUG_sunlover */
5019}
5020
5021static DECLCALLBACK(int) vgaPortCopyRect (PPDMIDISPLAYPORT pInterface,
5022 uint32_t w,
5023 uint32_t h,
5024 const uint8_t *pu8Src,
5025 int32_t xSrc,
5026 int32_t ySrc,
5027 uint32_t u32SrcWidth,
5028 uint32_t u32SrcHeight,
5029 uint32_t u32SrcLineSize,
5030 uint32_t u32SrcBitsPerPixel,
5031 uint8_t *pu8Dst,
5032 int32_t xDst,
5033 int32_t yDst,
5034 uint32_t u32DstWidth,
5035 uint32_t u32DstHeight,
5036 uint32_t u32DstLineSize,
5037 uint32_t u32DstBitsPerPixel)
5038{
5039 uint32_t v;
5040 vga_draw_line_func *vga_draw_line;
5041
5042 uint32_t cbPixelDst;
5043 uint32_t cbLineDst;
5044 uint8_t *pu8DstPtr;
5045
5046 uint32_t cbPixelSrc;
5047 uint32_t cbLineSrc;
5048 const uint8_t *pu8SrcPtr;
5049
5050#ifdef DEBUG_sunlover
5051 LogFlow(("vgaPortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, w, h, xDst, yDst));
5052#endif /* DEBUG_sunlover */
5053
5054 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5055
5056 Assert(pInterface);
5057 Assert(pThis->pDrv);
5058
5059 int32_t xSrcCorrected = xSrc;
5060 int32_t ySrcCorrected = ySrc;
5061 uint32_t wCorrected = w;
5062 uint32_t hCorrected = h;
5063
5064 /* Correct source coordinates to be within the source bitmap. */
5065 if (xSrcCorrected < 0)
5066 {
5067 xSrcCorrected += wCorrected; /* Compute xRight which is also the new width. */
5068 wCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5069 xSrcCorrected = 0;
5070 }
5071
5072 if (ySrcCorrected < 0)
5073 {
5074 ySrcCorrected += hCorrected; /* Compute yBottom, which is also the new height. */
5075 hCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5076 ySrcCorrected = 0;
5077 }
5078
5079 /* Also check if coords are greater than the display resolution. */
5080 if (xSrcCorrected + wCorrected > u32SrcWidth)
5081 {
5082 /* xSrcCorrected < 0 is not possible here */
5083 wCorrected = u32SrcWidth > (uint32_t)xSrcCorrected? u32SrcWidth - xSrcCorrected: 0;
5084 }
5085
5086 if (ySrcCorrected + hCorrected > u32SrcHeight)
5087 {
5088 /* y < 0 is not possible here */
5089 hCorrected = u32SrcHeight > (uint32_t)ySrcCorrected? u32SrcHeight - ySrcCorrected: 0;
5090 }
5091
5092#ifdef DEBUG_sunlover
5093 LogFlow(("vgaPortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, wCorrected, hCorrected));
5094#endif /* DEBUG_sunlover */
5095
5096 /* Check if there is something to do at all. */
5097 if (wCorrected == 0 || hCorrected == 0)
5098 {
5099 /* Empty rectangle. */
5100#ifdef DEBUG_sunlover
5101 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", wCorrected, hCorrected));
5102#endif /* DEBUG_sunlover */
5103 return VINF_SUCCESS;
5104 }
5105
5106 /* Check that the corrected source rectangle is within the destination.
5107 * Note: source rectangle is adjusted, but the target must be large enough.
5108 */
5109 if ( xDst < 0
5110 || yDst < 0
5111 || xDst + wCorrected > u32DstWidth
5112 || yDst + hCorrected > u32DstHeight)
5113 {
5114 return VERR_INVALID_PARAMETER;
5115 }
5116
5117 /* Choose the rendering function. */
5118 switch(u32SrcBitsPerPixel)
5119 {
5120 default:
5121 case 0:
5122 /* Nothing to do, just return. */
5123 return VINF_SUCCESS;
5124 case 8:
5125 v = VGA_DRAW_LINE8;
5126 break;
5127 case 15:
5128 v = VGA_DRAW_LINE15;
5129 break;
5130 case 16:
5131 v = VGA_DRAW_LINE16;
5132 break;
5133 case 24:
5134 v = VGA_DRAW_LINE24;
5135 break;
5136 case 32:
5137 v = VGA_DRAW_LINE32;
5138 break;
5139 }
5140
5141 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5142 AssertRC(rc);
5143
5144 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(u32DstBitsPerPixel)];
5145
5146 /* Compute source and destination addresses and pitches. */
5147 cbPixelDst = (u32DstBitsPerPixel + 7) / 8;
5148 cbLineDst = u32DstLineSize;
5149 pu8DstPtr = pu8Dst + yDst * cbLineDst + xDst * cbPixelDst;
5150
5151 cbPixelSrc = (u32SrcBitsPerPixel + 7) / 8;
5152 cbLineSrc = u32SrcLineSize;
5153 pu8SrcPtr = pu8Src + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5154
5155#ifdef DEBUG_sunlover
5156 LogFlow(("vgaPortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8DstPtr, cbLineDst, cbPixelDst, pu8SrcPtr, cbLineSrc, cbPixelSrc));
5157#endif /* DEBUG_sunlover */
5158
5159 while (hCorrected-- > 0)
5160 {
5161 vga_draw_line (pThis, pu8DstPtr, pu8SrcPtr, wCorrected);
5162 pu8DstPtr += cbLineDst;
5163 pu8SrcPtr += cbLineSrc;
5164 }
5165
5166 PDMCritSectLeave(&pThis->CritSect);
5167#ifdef DEBUG_sunlover
5168 LogFlow(("vgaPortCopyRect: completed.\n"));
5169#endif /* DEBUG_sunlover */
5170
5171 return VINF_SUCCESS;
5172}
5173
5174static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5175{
5176 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5177
5178 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
5179
5180 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5181 AssertRC(rc);
5182
5183 pThis->fRenderVRAM = fRender;
5184
5185 PDMCritSectLeave(&pThis->CritSect);
5186}
5187
5188
5189static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5190{
5191 PVGASTATE pThis = (PVGASTATE)pvUser;
5192 NOREF(pDevIns);
5193
5194 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5195 {
5196 VBVARaiseIrq(pThis, HGSMIHOSTFLAGS_VSYNC);
5197 }
5198
5199 if (pThis->pDrv)
5200 pThis->pDrv->pfnRefresh(pThis->pDrv);
5201
5202 if (pThis->cMilliesRefreshInterval)
5203 TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
5204
5205#ifdef VBOX_WITH_VIDEOHWACCEL
5206 vbvaTimerCb(pThis);
5207#endif
5208
5209 vboxCmdVBVACmdTimer(pThis);
5210}
5211
5212#ifdef VBOX_WITH_VMSVGA
5213int vgaR3RegisterVRAMHandler(PVGASTATE pVGAState, uint64_t cbFrameBuffer)
5214{
5215 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5216
5217 Assert(pVGAState->GCPhysVRAM);
5218
5219 int rc = PGMR3HandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5220 PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
5221 pVGAState->GCPhysVRAM, pVGAState->GCPhysVRAM + (cbFrameBuffer - 1),
5222 vgaR3LFBAccessHandler, pVGAState,
5223 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pDevIns->pvInstanceDataR0,
5224 g_DeviceVga.szRCMod, "vgaGCLFBAccessHandler", pDevIns->pvInstanceDataRC,
5225 "VGA LFB");
5226 AssertRC(rc);
5227 return rc;
5228}
5229
5230int vgaR3UnregisterVRAMHandler(PVGASTATE pVGAState)
5231{
5232 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5233
5234 Assert(pVGAState->GCPhysVRAM);
5235 int rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pVGAState->GCPhysVRAM);
5236 AssertRC(rc);
5237 return rc;
5238}
5239#endif
5240
5241/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5242
5243/**
5244 * Callback function for unmapping and/or mapping the VRAM MMIO2 region (called by the PCI bus).
5245 *
5246 * @return VBox status code.
5247 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
5248 * @param iRegion The region number.
5249 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
5250 * I/O port, else it's a physical address.
5251 * This address is *NOT* relative to pci_mem_base like earlier!
5252 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
5253 */
5254static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5255{
5256 int rc;
5257 PPDMDEVINS pDevIns = pPciDev->pDevIns;
5258 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5259 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5260#ifdef VBOX_WITH_VMSVGA
5261 AssertReturn((iRegion == ((pThis->fVMSVGAEnabled) ? 1 : 0)) && (enmType == ((pThis->fVMSVGAEnabled) ? PCI_ADDRESS_SPACE_MEM : PCI_ADDRESS_SPACE_MEM_PREFETCH)), VERR_INTERNAL_ERROR);
5262#else
5263 AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5264#endif
5265
5266 if (GCPhysAddress != NIL_RTGCPHYS)
5267 {
5268 /*
5269 * Mapping the VRAM.
5270 */
5271 rc = PDMDevHlpMMIO2Map(pDevIns, iRegion, GCPhysAddress);
5272 AssertRC(rc);
5273 if (RT_SUCCESS(rc))
5274 {
5275 rc = PGMR3HandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5276 PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
5277 GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5278 vgaR3LFBAccessHandler, pThis,
5279 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pDevIns->pvInstanceDataR0,
5280 g_DeviceVga.szRCMod, "vgaRCLFBAccessHandler", pDevIns->pvInstanceDataRC,
5281 "VGA LFB");
5282 AssertRC(rc);
5283 if (RT_SUCCESS(rc))
5284 {
5285 pThis->GCPhysVRAM = GCPhysAddress;
5286 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5287 }
5288 }
5289 }
5290 else
5291 {
5292 /*
5293 * Unmapping of the VRAM in progress.
5294 * Deregister the access handler so PGM doesn't get upset.
5295 */
5296 Assert(pThis->GCPhysVRAM);
5297#ifdef VBOX_WITH_VMSVGA
5298 Assert(!pThis->svga.fEnabled);
5299#endif
5300 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5301 AssertRC(rc);
5302 pThis->GCPhysVRAM = 0;
5303 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5304 }
5305 return rc;
5306}
5307
5308
5309/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5310
5311/**
5312 * Saves a important bits of the VGA device config.
5313 *
5314 * @param pThis The VGA instance data.
5315 * @param pSSM The saved state handle.
5316 */
5317static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
5318{
5319 SSMR3PutU32(pSSM, pThis->vram_size);
5320 SSMR3PutU32(pSSM, pThis->cMonitors);
5321}
5322
5323
5324/**
5325 * @copydoc FNSSMDEVLIVEEXEC
5326 */
5327static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5328{
5329 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5330 Assert(uPass == 0); NOREF(uPass);
5331 vgaR3SaveConfig(pThis, pSSM);
5332 return VINF_SSM_DONT_CALL_AGAIN;
5333}
5334
5335
5336/**
5337 * @copydoc FNSSMDEVSAVEPREP
5338 */
5339static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5340{
5341#ifdef VBOX_WITH_VIDEOHWACCEL
5342 return vboxVBVASaveStatePrep(pDevIns, pSSM);
5343#else
5344 return VINF_SUCCESS;
5345#endif
5346}
5347
5348static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5349{
5350#ifdef VBOX_WITH_VIDEOHWACCEL
5351 return vboxVBVASaveStateDone(pDevIns, pSSM);
5352#else
5353 return VINF_SUCCESS;
5354#endif
5355}
5356
5357/**
5358 * @copydoc FNSSMDEVSAVEEXEC
5359 */
5360static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5361{
5362 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5363#ifdef VBOX_WITH_VDMA
5364 vboxVDMASaveStateExecPrep(pThis->pVdma, pSSM);
5365#endif
5366 vgaR3SaveConfig(pThis, pSSM);
5367 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5368#ifdef VBOX_WITH_HGSMI
5369 SSMR3PutBool(pSSM, true);
5370 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5371# ifdef VBOX_WITH_VDMA
5372 vboxVDMASaveStateExecDone(pThis->pVdma, pSSM);
5373# endif
5374 return rc;
5375#else
5376 SSMR3PutBool(pSSM, false);
5377#endif
5378#ifdef VBOX_WITH_VMSVGA
5379 if ( rc == VINF_SUCCESS
5380 && pThis->fVMSVGAEnabled)
5381 rc = vmsvgaSaveExec(pDevIns, pSSM);
5382#endif
5383 return rc;
5384}
5385
5386
5387/**
5388 * @copydoc FNSSMDEVSAVEEXEC
5389 */
5390static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5391{
5392 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5393 int rc;
5394
5395 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5396 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5397
5398 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5399 {
5400 /* Check the config */
5401 uint32_t cbVRam;
5402 rc = SSMR3GetU32(pSSM, &cbVRam);
5403 AssertRCReturn(rc, rc);
5404 if (pThis->vram_size != cbVRam)
5405 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5406
5407 uint32_t cMonitors;
5408 rc = SSMR3GetU32(pSSM, &cMonitors);
5409 AssertRCReturn(rc, rc);
5410 if (pThis->cMonitors != cMonitors)
5411 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5412 }
5413
5414 if (uPass == SSM_PASS_FINAL)
5415 {
5416 rc = vga_load(pSSM, pThis, uVersion);
5417 if (RT_FAILURE(rc))
5418 return rc;
5419 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5420 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5421 {
5422 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5423 AssertRCReturn(rc, rc);
5424 }
5425 if (fWithHgsmi)
5426 {
5427#ifdef VBOX_WITH_HGSMI
5428 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5429 AssertRCReturn(rc, rc);
5430#else
5431 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5432#endif
5433 }
5434#ifdef VBOX_WITH_VMSVGA
5435 if ( uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_2D
5436 && pThis->fVMSVGAEnabled)
5437 {
5438 rc = vmsvgaLoadExec(pDevIns, pSSM, uVersion, uPass);
5439 AssertRCReturn(rc, rc);
5440 }
5441#endif
5442 }
5443 return VINF_SUCCESS;
5444}
5445
5446
5447/**
5448 * @copydoc FNSSMDEVLOADDONE
5449 */
5450static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5451{
5452#ifdef VBOX_WITH_HGSMI
5453 return vboxVBVALoadStateDone(pDevIns, pSSM);
5454#else
5455 return VINF_SUCCESS;
5456#endif
5457}
5458
5459
5460/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5461
5462/**
5463 * @interface_method_impl{PDMDEVREG,pfnReset}
5464 */
5465static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5466{
5467 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5468 char *pchStart;
5469 char *pchEnd;
5470 LogFlow(("vgaReset\n"));
5471
5472#ifdef VBOX_WITH_HGSMI
5473 VBVAReset(pThis);
5474#endif /* VBOX_WITH_HGSMI */
5475
5476
5477 /* Clear the VRAM ourselves. */
5478 if (pThis->vram_ptrR3 && pThis->vram_size)
5479 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5480
5481 /*
5482 * Zero most of it.
5483 *
5484 * Unlike vga_reset we're leaving out a few members which we believe
5485 * must remain unchanged....
5486 */
5487 /* 1st part. */
5488 pchStart = (char *)&pThis->latch;
5489 pchEnd = (char *)&pThis->invalidated_y_table;
5490 memset(pchStart, 0, pchEnd - pchStart);
5491
5492 /* 2nd part. */
5493 pchStart = (char *)&pThis->last_palette;
5494 pchEnd = (char *)&pThis->u32Marker;
5495 memset(pchStart, 0, pchEnd - pchStart);
5496
5497
5498 /*
5499 * Restore and re-init some bits.
5500 */
5501 pThis->get_bpp = vga_get_bpp;
5502 pThis->get_offsets = vga_get_offsets;
5503 pThis->get_resolution = vga_get_resolution;
5504 pThis->graphic_mode = -1; /* Force full update. */
5505#ifdef CONFIG_BOCHS_VBE
5506 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5507 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5508 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
5509 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5510#endif /* CONFIG_BOCHS_VBE */
5511
5512 /*
5513 * Reset the LBF mapping.
5514 */
5515 pThis->fLFBUpdated = false;
5516 if ( ( pThis->fGCEnabled
5517 || pThis->fR0Enabled)
5518 && pThis->GCPhysVRAM
5519 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
5520 {
5521 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5522 AssertRC(rc);
5523 }
5524 if (pThis->fRemappedVGA)
5525 {
5526 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5527 pThis->fRemappedVGA = false;
5528 }
5529
5530 /*
5531 * Reset the logo data.
5532 */
5533 pThis->LogoCommand = LOGO_CMD_NOP;
5534 pThis->offLogoData = 0;
5535
5536 /* notify port handler */
5537 if (pThis->pDrv)
5538 {
5539 PDMCritSectLeave(&pThis->CritSect); /* hack around lock order issue. */
5540 pThis->pDrv->pfnReset(pThis->pDrv);
5541 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
5542 }
5543
5544 /* Reset latched access mask. */
5545 pThis->uMaskLatchAccess = 0x3ff;
5546 pThis->cLatchAccesses = 0;
5547 pThis->u64LastLatchedAccess = 0;
5548 pThis->iMask = 0;
5549
5550 /* Reset retrace emulation. */
5551 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5552}
5553
5554
5555/**
5556 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5557 */
5558static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5559{
5560 if (offDelta)
5561 {
5562 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5563 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5564
5565 pThis->vram_ptrRC += offDelta;
5566 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5567 }
5568}
5569
5570
5571/**
5572 * @interface_method_impl{PDMDEVREG,pfnAttach}
5573 *
5574 * This is like plugging in the monitor after turning on the PC.
5575 */
5576static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5577{
5578 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5579
5580 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5581 ("VGA device does not support hotplugging\n"),
5582 VERR_INVALID_PARAMETER);
5583
5584 switch (iLUN)
5585 {
5586 /* LUN #0: Display port. */
5587 case 0:
5588 {
5589 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5590 if (RT_SUCCESS(rc))
5591 {
5592 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5593 if (pThis->pDrv)
5594 {
5595 /* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
5596 if ( pThis->pDrv->pfnRefresh
5597 && pThis->pDrv->pfnResize
5598 && pThis->pDrv->pfnUpdateRect)
5599 rc = VINF_SUCCESS;
5600 else
5601 {
5602 Assert(pThis->pDrv->pfnRefresh);
5603 Assert(pThis->pDrv->pfnResize);
5604 Assert(pThis->pDrv->pfnUpdateRect);
5605 pThis->pDrv = NULL;
5606 pThis->pDrvBase = NULL;
5607 rc = VERR_INTERNAL_ERROR;
5608 }
5609#ifdef VBOX_WITH_VIDEOHWACCEL
5610 if(rc == VINF_SUCCESS)
5611 {
5612 rc = vbvaVHWAConstruct(pThis);
5613 if (rc != VERR_NOT_IMPLEMENTED)
5614 AssertRC(rc);
5615 }
5616#endif
5617 }
5618 else
5619 {
5620 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5621 pThis->pDrvBase = NULL;
5622 rc = VERR_PDM_MISSING_INTERFACE;
5623 }
5624 }
5625 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5626 {
5627 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5628 rc = VINF_SUCCESS;
5629 }
5630 else
5631 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5632 return rc;
5633 }
5634
5635 default:
5636 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5637 return VERR_PDM_NO_SUCH_LUN;
5638 }
5639}
5640
5641
5642/**
5643 * @interface_method_impl{PDMDEVREG,pfnDetach}
5644 *
5645 * This is like unplugging the monitor while the PC is still running.
5646 */
5647static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5648{
5649 /*
5650 * Reset the interfaces and update the controller state.
5651 */
5652 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5653
5654 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5655 ("VGA device does not support hotplugging\n"));
5656
5657 switch (iLUN)
5658 {
5659 /* LUN #0: Display port. */
5660 case 0:
5661 pThis->pDrv = NULL;
5662 pThis->pDrvBase = NULL;
5663 break;
5664
5665 default:
5666 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5667 break;
5668 }
5669}
5670
5671
5672/**
5673 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5674 */
5675static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5676{
5677 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5678
5679#ifdef VBE_NEW_DYN_LIST
5680 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5681 LogFlow(("vgaR3Destruct:\n"));
5682
5683# ifdef VBOX_WITH_VDMA
5684 vboxVDMADestruct(pThis->pVdma);
5685# endif
5686
5687#ifdef VBOX_WITH_VMSVGA
5688 if (pThis->fVMSVGAEnabled)
5689 vmsvgaDestruct(pDevIns);
5690#endif
5691
5692 /*
5693 * Free MM heap pointers.
5694 */
5695 if (pThis->pu8VBEExtraData)
5696 {
5697 MMR3HeapFree(pThis->pu8VBEExtraData);
5698 pThis->pu8VBEExtraData = NULL;
5699 }
5700#endif /* VBE_NEW_DYN_LIST */
5701 if (pThis->pu8VgaBios)
5702 {
5703 MMR3HeapFree(pThis->pu8VgaBios);
5704 pThis->pu8VgaBios = NULL;
5705 }
5706
5707 if (pThis->pszVgaBiosFile)
5708 {
5709 MMR3HeapFree(pThis->pszVgaBiosFile);
5710 pThis->pszVgaBiosFile = NULL;
5711 }
5712
5713 if (pThis->pszLogoFile)
5714 {
5715 MMR3HeapFree(pThis->pszLogoFile);
5716 pThis->pszLogoFile = NULL;
5717 }
5718
5719 PDMR3CritSectDelete(&pThis->CritSect);
5720 return VINF_SUCCESS;
5721}
5722
5723
5724/**
5725 * Adjust VBE mode information
5726 *
5727 * Depending on the configured VRAM size, certain parts of VBE mode
5728 * information must be updated.
5729 *
5730 * @param pThis The device instance data.
5731 * @param pMode The mode information structure.
5732 */
5733static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
5734{
5735 int maxPage;
5736 int bpl;
5737
5738
5739 /* For 4bpp modes, the planes are "stacked" on top of each other. */
5740 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
5741 /* The "number of image pages" is really the max page index... */
5742 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
5743 Assert(maxPage >= 0);
5744 if (maxPage > 255)
5745 maxPage = 255; /* 8-bit value. */
5746 pMode->info.NumberOfImagePages = maxPage;
5747 pMode->info.LinNumberOfPages = maxPage;
5748}
5749
5750
5751/**
5752 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5753 */
5754static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5755{
5756
5757 static bool s_fExpandDone = false;
5758 int rc;
5759 unsigned i;
5760#ifdef VBE_NEW_DYN_LIST
5761 uint32_t cCustomModes;
5762 uint32_t cyReduction;
5763 uint32_t cbPitch;
5764 PVBEHEADER pVBEDataHdr;
5765 ModeInfoListItem *pCurMode;
5766 unsigned cb;
5767#endif
5768 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5769 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5770 PVM pVM = PDMDevHlpGetVM(pDevIns);
5771
5772 Assert(iInstance == 0);
5773 Assert(pVM);
5774
5775 /*
5776 * Init static data.
5777 */
5778 if (!s_fExpandDone)
5779 {
5780 s_fExpandDone = true;
5781 vga_init_expand();
5782 }
5783
5784 /*
5785 * Validate configuration.
5786 */
5787 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
5788 "MonitorCount\0"
5789 "GCEnabled\0"
5790 "R0Enabled\0"
5791 "FadeIn\0"
5792 "FadeOut\0"
5793 "LogoTime\0"
5794 "LogoFile\0"
5795 "ShowBootMenu\0"
5796 "BiosRom\0"
5797 "RealRetrace\0"
5798 "CustomVideoModes\0"
5799 "HeightReduction\0"
5800 "CustomVideoMode1\0"
5801 "CustomVideoMode2\0"
5802 "CustomVideoMode3\0"
5803 "CustomVideoMode4\0"
5804 "CustomVideoMode5\0"
5805 "CustomVideoMode6\0"
5806 "CustomVideoMode7\0"
5807 "CustomVideoMode8\0"
5808 "CustomVideoMode9\0"
5809 "CustomVideoMode10\0"
5810 "CustomVideoMode11\0"
5811 "CustomVideoMode12\0"
5812 "CustomVideoMode13\0"
5813 "CustomVideoMode14\0"
5814 "CustomVideoMode15\0"
5815 "CustomVideoMode16\0"
5816 "MaxBiosXRes\0"
5817 "MaxBiosYRes\0"
5818#ifdef VBOX_WITH_VMSVGA
5819 "VMSVGAEnabled\0"
5820#endif
5821#ifdef VBOX_WITH_VMSVGA3D
5822 "VMSVGA3dEnabled\0"
5823 "HostWindowId\0"
5824#endif
5825 ))
5826 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5827 N_("Invalid configuration for vga device"));
5828
5829 /*
5830 * Init state data.
5831 */
5832 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
5833 AssertLogRelRCReturn(rc, rc);
5834 if (pThis->vram_size > VGA_VRAM_MAX)
5835 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5836 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
5837 if (pThis->vram_size < VGA_VRAM_MIN)
5838 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5839 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
5840 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
5841 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5842 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
5843
5844 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
5845 AssertLogRelRCReturn(rc, rc);
5846
5847 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
5848 AssertLogRelRCReturn(rc, rc);
5849
5850 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
5851 AssertLogRelRCReturn(rc, rc);
5852 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
5853
5854#ifdef VBOX_WITH_VMSVGA
5855 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
5856 AssertLogRelRCReturn(rc, rc);
5857 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
5858#endif
5859#ifdef VBOX_WITH_VMSVGA3D
5860 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
5861 AssertLogRelRCReturn(rc, rc);
5862 rc = CFGMR3QueryU64Def(pCfg, "HostWindowId", &pThis->svga.u64HostWindowId, 0);
5863 AssertLogRelRCReturn(rc, rc);
5864 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
5865 Log(("VMSVGA: HostWindowId = 0x%x\n", pThis->svga.u64HostWindowId));
5866#endif
5867
5868 pThis->pDevInsR3 = pDevIns;
5869 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5870 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5871
5872 vgaR3Reset(pDevIns);
5873
5874 /* The PCI devices configuration. */
5875#ifdef VBOX_WITH_VMSVGA
5876 if (pThis->fVMSVGAEnabled)
5877 {
5878 /* Extend our VGA device with VMWare SVGA functionality. */
5879 PCIDevSetVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
5880 PCIDevSetDeviceId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
5881 PCIDevSetSubSystemVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
5882 PCIDevSetSubSystemId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
5883 }
5884 else
5885 {
5886#endif /* VBOX_WITH_VMSVGA */
5887 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
5888 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
5889#ifdef VBOX_WITH_VMSVGA
5890 }
5891#endif
5892 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
5893 PCIDevSetClassBase( &pThis->Dev, 0x03);
5894 PCIDevSetHeaderType(&pThis->Dev, 0x00);
5895#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
5896 PCIDevSetInterruptPin(&pThis->Dev, 1);
5897#endif
5898
5899 /* the interfaces. */
5900 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
5901
5902 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
5903 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
5904 pThis->IPort.pfnQueryColorDepth = vgaPortQueryColorDepth;
5905 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
5906 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
5907 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
5908 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
5909 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
5910 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
5911 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
5912
5913#if defined(VBOX_WITH_HGSMI)
5914# if defined(VBOX_WITH_VIDEOHWACCEL)
5915 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsynch = vbvaVHWACommandCompleteAsynch;
5916# endif
5917#if defined(VBOX_WITH_CRHGSMI)
5918 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
5919 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
5920# endif
5921#endif
5922
5923 /*
5924 * We use our own critical section to avoid unncessary pointer indirections
5925 * in interface methods (as we all as for historical reasons).
5926 */
5927 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
5928 AssertRCReturn(rc, rc);
5929 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
5930 AssertRCReturn(rc, rc);
5931
5932 /*
5933 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
5934 */
5935#ifdef VBOX_WITH_VMSVGA
5936 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
5937
5938 if (pThis->fVMSVGAEnabled)
5939 {
5940 /*
5941 * Allocate and initialize the FIFO MMIO2 memory.
5942 */
5943 rc = PDMDevHlpMMIO2Register(pDevIns, 2 /*iRegion*/, VMSVGA_FIFO_SIZE, 0 /*fFlags*/, (void **)&pThis->svga.pFIFOR3, "VMSVGA-FIFO");
5944 if (RT_FAILURE(rc))
5945 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
5946 N_("Failed to allocate %u bytes of memory for the VMSVGA device"), VMSVGA_FIFO_SIZE);
5947 pThis->svga.pFIFOR0 = (RTR0PTR)pThis->svga.pFIFOR3;
5948 pThis->svga.cbFIFO = VMSVGA_FIFO_SIZE;
5949 }
5950#else
5951 int iPCIRegionVRAM = 0;
5952#endif
5953 rc = PDMDevHlpMMIO2Register(pDevIns, iPCIRegionVRAM, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
5954 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
5955 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo @bugref{1865} Map parts into R0 or just use PGM access (Mac only). */
5956
5957 if (pThis->fGCEnabled)
5958 {
5959 RTRCPTR pRCMapping = 0;
5960 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
5961 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
5962 pThis->vram_ptrRC = pRCMapping;
5963# ifdef VBOX_WITH_VMSVGA
5964 /* Don't need a mapping in RC */
5965# endif
5966 }
5967
5968#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
5969 if (pThis->fR0Enabled)
5970 {
5971 RTR0PTR pR0Mapping = 0;
5972 rc = PDMDevHlpMMIO2MapKernel(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
5973 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
5974 pThis->vram_ptrR0 = pR0Mapping;
5975# ifdef VBOX_WITH_VMSVGA
5976 if (pThis->fVMSVGAEnabled)
5977 {
5978 RTR0PTR pR0Mapping = 0;
5979 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 2 /* iRegion */, 0 /* off */, VMSVGA_FIFO_SIZE, "VMSVGA-FIFO", &pR0Mapping);
5980 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VMSVGA_FIFO_SIZE, rc), rc);
5981 pThis->svga.pFIFOR0 = pR0Mapping;
5982 }
5983# endif
5984 }
5985#endif
5986
5987 /*
5988 * Register I/O ports.
5989 */
5990 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
5991 if (RT_FAILURE(rc))
5992 return rc;
5993 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
5994 if (RT_FAILURE(rc))
5995 return rc;
5996 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
5997 if (RT_FAILURE(rc))
5998 return rc;
5999 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
6000 if (RT_FAILURE(rc))
6001 return rc;
6002 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
6003 if (RT_FAILURE(rc))
6004 return rc;
6005#ifdef VBOX_WITH_HGSMI
6006 /* Use reserved VGA IO ports for HGSMI. */
6007 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
6008 if (RT_FAILURE(rc))
6009 return rc;
6010 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
6011 if (RT_FAILURE(rc))
6012 return rc;
6013#endif /* VBOX_WITH_HGSMI */
6014
6015#ifdef CONFIG_BOCHS_VBE
6016 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
6017 if (RT_FAILURE(rc))
6018 return rc;
6019 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
6020 if (RT_FAILURE(rc))
6021 return rc;
6022#endif /* CONFIG_BOCHS_VBE */
6023
6024 /* guest context extension */
6025 if (pThis->fGCEnabled)
6026 {
6027 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6028 if (RT_FAILURE(rc))
6029 return rc;
6030 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6031 if (RT_FAILURE(rc))
6032 return rc;
6033 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6034 if (RT_FAILURE(rc))
6035 return rc;
6036 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6037 if (RT_FAILURE(rc))
6038 return rc;
6039 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6040 if (RT_FAILURE(rc))
6041 return rc;
6042#ifdef CONFIG_BOCHS_VBE
6043 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6044 if (RT_FAILURE(rc))
6045 return rc;
6046 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6047 if (RT_FAILURE(rc))
6048 return rc;
6049#endif /* CONFIG_BOCHS_VBE */
6050 }
6051
6052 /* R0 context extension */
6053 if (pThis->fR0Enabled)
6054 {
6055 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6056 if (RT_FAILURE(rc))
6057 return rc;
6058 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6059 if (RT_FAILURE(rc))
6060 return rc;
6061 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6062 if (RT_FAILURE(rc))
6063 return rc;
6064 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6065 if (RT_FAILURE(rc))
6066 return rc;
6067 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6068 if (RT_FAILURE(rc))
6069 return rc;
6070#ifdef CONFIG_BOCHS_VBE
6071 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6072 if (RT_FAILURE(rc))
6073 return rc;
6074 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6075 if (RT_FAILURE(rc))
6076 return rc;
6077#endif /* CONFIG_BOCHS_VBE */
6078 }
6079
6080 /* vga mmio */
6081 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
6082 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
6083 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
6084 if (RT_FAILURE(rc))
6085 return rc;
6086 if (pThis->fGCEnabled)
6087 {
6088 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6089 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6090 if (RT_FAILURE(rc))
6091 return rc;
6092 }
6093 if (pThis->fR0Enabled)
6094 {
6095 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6096 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6097 if (RT_FAILURE(rc))
6098 return rc;
6099 }
6100
6101 /* vga bios */
6102 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6103 if (RT_FAILURE(rc))
6104 return rc;
6105 if (pThis->fR0Enabled)
6106 {
6107 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6108 if (RT_FAILURE(rc))
6109 return rc;
6110 }
6111
6112 /*
6113 * Get the VGA BIOS ROM file name.
6114 */
6115 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6116 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6117 {
6118 pThis->pszVgaBiosFile = NULL;
6119 rc = VINF_SUCCESS;
6120 }
6121 else if (RT_FAILURE(rc))
6122 return PDMDEV_SET_ERROR(pDevIns, rc,
6123 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6124 else if (!*pThis->pszVgaBiosFile)
6125 {
6126 MMR3HeapFree(pThis->pszVgaBiosFile);
6127 pThis->pszVgaBiosFile = NULL;
6128 }
6129
6130 /*
6131 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6132 */
6133 RTFILE FileVgaBios = NIL_RTFILE;
6134 if (pThis->pszVgaBiosFile)
6135 {
6136 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6137 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6138 if (RT_SUCCESS(rc))
6139 {
6140 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6141 if (RT_SUCCESS(rc))
6142 {
6143 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6144 || pThis->cbVgaBios > _64K
6145 || pThis->cbVgaBios < 16 * _1K)
6146 rc = VERR_TOO_MUCH_DATA;
6147 }
6148 }
6149 if (RT_FAILURE(rc))
6150 {
6151 /*
6152 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6153 */
6154 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6155 RTFileClose(FileVgaBios);
6156 FileVgaBios = NIL_RTFILE;
6157 MMR3HeapFree(pThis->pszVgaBiosFile);
6158 pThis->pszVgaBiosFile = NULL;
6159 }
6160 }
6161
6162 /*
6163 * Attempt to get the VGA BIOS ROM data from file.
6164 */
6165 if (pThis->pszVgaBiosFile)
6166 {
6167 /*
6168 * Allocate buffer for the VGA BIOS ROM data.
6169 */
6170 pThis->pu8VgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6171 if (pThis->pu8VgaBios)
6172 {
6173 rc = RTFileRead(FileVgaBios, pThis->pu8VgaBios, pThis->cbVgaBios, NULL);
6174 if (RT_FAILURE(rc))
6175 {
6176 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6177 MMR3HeapFree(pThis->pu8VgaBios);
6178 pThis->pu8VgaBios = NULL;
6179 }
6180 rc = VINF_SUCCESS;
6181 }
6182 else
6183 rc = VERR_NO_MEMORY;
6184 }
6185 else
6186 pThis->pu8VgaBios = NULL;
6187
6188 /* cleanup */
6189 if (FileVgaBios != NIL_RTFILE)
6190 RTFileClose(FileVgaBios);
6191
6192 /* If we were unable to get the data from file for whatever reason, fall
6193 back to the built-in ROM image. */
6194 const uint8_t *pu8VgaBiosBinary;
6195 uint64_t cbVgaBiosBinary;
6196 uint32_t fFlags = 0;
6197 if (pThis->pu8VgaBios == NULL)
6198 {
6199 pu8VgaBiosBinary = g_abVgaBiosBinary;
6200 cbVgaBiosBinary = g_cbVgaBiosBinary;
6201 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6202 }
6203 else
6204 {
6205 pu8VgaBiosBinary = pThis->pu8VgaBios;
6206 cbVgaBiosBinary = pThis->cbVgaBios;
6207 }
6208
6209 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6210 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6211 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6212 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pu8VgaBiosBinary, cbVgaBiosBinary,
6213 fFlags, "VGA BIOS");
6214 if (RT_FAILURE(rc))
6215 return rc;
6216
6217 /*
6218 * Saved state.
6219 */
6220 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6221 NULL, vgaR3LiveExec, NULL,
6222 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6223 NULL, vgaR3LoadExec, vgaR3LoadDone);
6224 if (RT_FAILURE(rc))
6225 return rc;
6226
6227 /*
6228 * PCI device registration.
6229 */
6230 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6231 if (RT_FAILURE(rc))
6232 return rc;
6233 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
6234 if (pThis->Dev.devfn != 16 && iInstance == 0)
6235 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
6236
6237#ifdef VBOX_WITH_VMSVGA
6238 if (pThis->fVMSVGAEnabled)
6239 {
6240 /* Register the io command ports. */
6241 rc = PDMDevHlpPCIIORegionRegister (pDevIns, 0 /* iRegion */, 0x10, PCI_ADDRESS_SPACE_IO, vmsvgaR3IORegionMap);
6242 if (RT_FAILURE (rc))
6243 return rc;
6244 /* VMware's MetalKit doesn't like PCI_ADDRESS_SPACE_MEM_PREFETCH */
6245 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vgaR3IORegionMap);
6246 if (RT_FAILURE(rc))
6247 return rc;
6248 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2 /* iRegion */, VMSVGA_FIFO_SIZE, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vmsvgaR3IORegionMap);
6249 if (RT_FAILURE(rc))
6250 return rc;
6251 }
6252 else
6253#endif /* VBOX_WITH_VMSVGA */
6254 rc = PDMDevHlpPCIIORegionRegister(pDevIns, iPCIRegionVRAM, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6255 if (RT_FAILURE(rc))
6256 return rc;
6257
6258 /*
6259 * Create the refresh timer.
6260 */
6261 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6262 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6263 "VGA Refresh Timer", &pThis->RefreshTimer);
6264 if (RT_FAILURE(rc))
6265 return rc;
6266
6267 /*
6268 * Attach to the display.
6269 */
6270 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6271 if (RT_FAILURE(rc))
6272 return rc;
6273
6274 /*
6275 * Initialize the retrace flag.
6276 */
6277 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6278 AssertLogRelRCReturn(rc, rc);
6279
6280#ifdef VBE_NEW_DYN_LIST
6281
6282 uint16_t maxBiosXRes;
6283 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6284 AssertLogRelRCReturn(rc, rc);
6285 uint16_t maxBiosYRes;
6286 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6287 AssertLogRelRCReturn(rc, rc);
6288
6289 /*
6290 * Compute buffer size for the VBE BIOS Extra Data.
6291 */
6292 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6293
6294 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6295 if (RT_SUCCESS(rc) && cyReduction)
6296 cb *= 2; /* Default mode list will be twice long */
6297 else
6298 cyReduction = 0;
6299
6300 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6301 if (RT_SUCCESS(rc) && cCustomModes)
6302 cb += sizeof(ModeInfoListItem) * cCustomModes;
6303 else
6304 cCustomModes = 0;
6305
6306 /*
6307 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6308 */
6309 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6310 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6311 pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6312 if (!pThis->pu8VBEExtraData)
6313 return VERR_NO_MEMORY;
6314
6315 pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
6316 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6317 pVBEDataHdr->cbData = cb;
6318
6319# ifndef VRAM_SIZE_FIX
6320 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6321 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6322# else /* VRAM_SIZE_FIX defined */
6323 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6324 for (i = 0; i < MODE_INFO_SIZE; i++)
6325 {
6326 uint32_t pixelWidth, reqSize;
6327 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6328 pixelWidth = 2;
6329 else
6330 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6331 reqSize = mode_info_list[i].info.XResolution
6332 * mode_info_list[i].info.YResolution
6333 * pixelWidth;
6334 if (reqSize >= pThis->vram_size)
6335 continue;
6336 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6337 || mode_info_list[i].info.YResolution > maxBiosYRes)
6338 continue;
6339 *pCurMode = mode_info_list[i];
6340 vgaAdjustModeInfo(pThis, pCurMode);
6341 pCurMode++;
6342 }
6343# endif /* VRAM_SIZE_FIX defined */
6344
6345 /*
6346 * Copy default modes with subtracted YResolution.
6347 */
6348 if (cyReduction)
6349 {
6350 ModeInfoListItem *pDefMode = mode_info_list;
6351 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6352# ifndef VRAM_SIZE_FIX
6353 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6354 {
6355 *pCurMode = *pDefMode;
6356 pCurMode->mode += 0x30;
6357 pCurMode->info.YResolution -= cyReduction;
6358 }
6359# else /* VRAM_SIZE_FIX defined */
6360 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6361 {
6362 uint32_t pixelWidth, reqSize;
6363 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6364 pixelWidth = 2;
6365 else
6366 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6367 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6368 if (reqSize >= pThis->vram_size)
6369 continue;
6370 if ( pDefMode->info.XResolution > maxBiosXRes
6371 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6372 continue;
6373 *pCurMode = *pDefMode;
6374 pCurMode->mode += 0x30;
6375 pCurMode->info.YResolution -= cyReduction;
6376 pCurMode++;
6377 }
6378# endif /* VRAM_SIZE_FIX defined */
6379 }
6380
6381
6382 /*
6383 * Add custom modes.
6384 */
6385 if (cCustomModes)
6386 {
6387 uint16_t u16CurMode = 0x160;
6388 for (i = 1; i <= cCustomModes; i++)
6389 {
6390 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6391 char *pszExtraData = NULL;
6392
6393 /* query and decode the custom mode string. */
6394 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6395 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6396 if (RT_SUCCESS(rc))
6397 {
6398 ModeInfoListItem *pDefMode = mode_info_list;
6399 unsigned int cx, cy, cBits, cParams, j;
6400 uint16_t u16DefMode;
6401
6402 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6403 if ( cParams != 3
6404 || (cBits != 16 && cBits != 24 && cBits != 32))
6405 {
6406 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6407 return VERR_VGA_INVALID_CUSTOM_MODE;
6408 }
6409 cbPitch = calc_line_pitch(cBits, cx);
6410# ifdef VRAM_SIZE_FIX
6411 if (cy * cbPitch >= pThis->vram_size)
6412 {
6413 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",
6414 cx, cy, cBits, pThis->vram_size / _1M));
6415 return VERR_VGA_INVALID_CUSTOM_MODE;
6416 }
6417# endif /* VRAM_SIZE_FIX defined */
6418 MMR3HeapFree(pszExtraData);
6419
6420 /* Use defaults from max@bpp mode. */
6421 switch (cBits)
6422 {
6423 case 16:
6424 u16DefMode = VBE_VESA_MODE_1024X768X565;
6425 break;
6426
6427 case 24:
6428 u16DefMode = VBE_VESA_MODE_1024X768X888;
6429 break;
6430
6431 case 32:
6432 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6433 break;
6434
6435 default: /* gcc, shut up! */
6436 AssertMsgFailed(("gone postal!\n"));
6437 continue;
6438 }
6439
6440 /* mode_info_list is not terminated */
6441 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6442 pDefMode++;
6443 Assert(j < MODE_INFO_SIZE);
6444
6445 *pCurMode = *pDefMode;
6446 pCurMode->mode = u16CurMode++;
6447
6448 /* adjust defaults */
6449 pCurMode->info.XResolution = cx;
6450 pCurMode->info.YResolution = cy;
6451 pCurMode->info.BytesPerScanLine = cbPitch;
6452 pCurMode->info.LinBytesPerScanLine = cbPitch;
6453 vgaAdjustModeInfo(pThis, pCurMode);
6454
6455 /* commit it */
6456 pCurMode++;
6457 }
6458 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6459 {
6460 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6461 return rc;
6462 }
6463 } /* foreach custom mode key */
6464 }
6465
6466 /*
6467 * Add the "End of list" mode.
6468 */
6469 memset(pCurMode, 0, sizeof(*pCurMode));
6470 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6471
6472 /*
6473 * Register I/O Port for the VBE BIOS Extra Data.
6474 */
6475 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6476 if (RT_FAILURE(rc))
6477 return rc;
6478#endif /* VBE_NEW_DYN_LIST */
6479
6480 /*
6481 * Register I/O Port for the BIOS Logo.
6482 */
6483 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6484 if (RT_FAILURE(rc))
6485 return rc;
6486
6487 /*
6488 * Register debugger info callbacks.
6489 */
6490 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6491 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6492 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6493 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6494 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6495 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6496 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaInfoPlanar);
6497 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6498 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6499
6500 /*
6501 * Construct the logo header.
6502 */
6503 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6504
6505 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6506 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6507 LogoHdr.fu8FadeIn = 1;
6508 else if (RT_FAILURE(rc))
6509 return PDMDEV_SET_ERROR(pDevIns, rc,
6510 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6511
6512 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6513 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6514 LogoHdr.fu8FadeOut = 1;
6515 else if (RT_FAILURE(rc))
6516 return PDMDEV_SET_ERROR(pDevIns, rc,
6517 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6518
6519 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6520 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6521 LogoHdr.u16LogoMillies = 0;
6522 else if (RT_FAILURE(rc))
6523 return PDMDEV_SET_ERROR(pDevIns, rc,
6524 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6525
6526 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6527 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6528 LogoHdr.fu8ShowBootMenu = 0;
6529 else if (RT_FAILURE(rc))
6530 return PDMDEV_SET_ERROR(pDevIns, rc,
6531 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6532
6533#if defined(DEBUG) && !defined(DEBUG_sunlover)
6534 /* Disable the logo abd menu if all default settings. */
6535 if ( LogoHdr.fu8FadeIn
6536 && LogoHdr.fu8FadeOut
6537 && LogoHdr.u16LogoMillies == 0
6538 && LogoHdr.fu8ShowBootMenu == 2)
6539 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.fu8ShowBootMenu = 0;
6540#endif
6541
6542 /* Delay the logo a little bit */
6543 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6544 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6545
6546 /*
6547 * Get the Logo file name.
6548 */
6549 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6550 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6551 pThis->pszLogoFile = NULL;
6552 else if (RT_FAILURE(rc))
6553 return PDMDEV_SET_ERROR(pDevIns, rc,
6554 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6555 else if (!*pThis->pszLogoFile)
6556 {
6557 MMR3HeapFree(pThis->pszLogoFile);
6558 pThis->pszLogoFile = NULL;
6559 }
6560
6561 /*
6562 * Determine the logo size, open any specified logo file in the process.
6563 */
6564 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6565 RTFILE FileLogo = NIL_RTFILE;
6566 if (pThis->pszLogoFile)
6567 {
6568 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6569 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6570 if (RT_SUCCESS(rc))
6571 {
6572 uint64_t cbFile;
6573 rc = RTFileGetSize(FileLogo, &cbFile);
6574 if (RT_SUCCESS(rc))
6575 {
6576 if (cbFile > 0 && cbFile < 32*_1M)
6577 LogoHdr.cbLogo = (uint32_t)cbFile;
6578 else
6579 rc = VERR_TOO_MUCH_DATA;
6580 }
6581 }
6582 if (RT_FAILURE(rc))
6583 {
6584 /*
6585 * Ignore failure and fall back to the default logo.
6586 */
6587 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6588 if (FileLogo != NIL_RTFILE)
6589 RTFileClose(FileLogo);
6590 FileLogo = NIL_RTFILE;
6591 MMR3HeapFree(pThis->pszLogoFile);
6592 pThis->pszLogoFile = NULL;
6593 }
6594 }
6595
6596 /*
6597 * Disable graphic splash screen if it doesn't fit into VRAM.
6598 */
6599 if (pThis->vram_size < LOGO_MAX_SIZE)
6600 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6601
6602 /*
6603 * Allocate buffer for the logo data.
6604 * RT_MAX() is applied to let us fall back to default logo on read failure.
6605 */
6606 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6607 pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6608 if (pThis->pu8Logo)
6609 {
6610 /*
6611 * Write the logo header.
6612 */
6613 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
6614 *pLogoHdr = LogoHdr;
6615
6616 /*
6617 * Write the logo bitmap.
6618 */
6619 if (pThis->pszLogoFile)
6620 {
6621 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6622 if (RT_SUCCESS(rc))
6623 rc = vbeParseBitmap(pThis);
6624 if (RT_FAILURE(rc))
6625 {
6626 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
6627 rc, pThis->pszLogoFile));
6628 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6629 }
6630 }
6631 if ( !pThis->pszLogoFile
6632 || RT_FAILURE(rc))
6633 {
6634 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6635 rc = vbeParseBitmap(pThis);
6636 if (RT_FAILURE(rc))
6637 AssertReleaseMsgFailed(("Parsing of internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6638 }
6639
6640 rc = VINF_SUCCESS;
6641 }
6642 else
6643 rc = VERR_NO_MEMORY;
6644
6645 /*
6646 * Cleanup.
6647 */
6648 if (FileLogo != NIL_RTFILE)
6649 RTFileClose(FileLogo);
6650
6651#ifdef VBOX_WITH_HGSMI
6652 VBVAInit (pThis);
6653#endif /* VBOX_WITH_HGSMI */
6654
6655#ifdef VBOX_WITH_VDMA
6656 if (rc == VINF_SUCCESS)
6657 {
6658 rc = vboxVDMAConstruct(pThis, 1024);
6659 AssertRC(rc);
6660 }
6661#endif
6662
6663#ifdef VBOX_WITH_VMSVGA
6664 if ( rc == VINF_SUCCESS
6665 && pThis->fVMSVGAEnabled)
6666 {
6667 rc = vmsvgaInit(pDevIns);
6668 }
6669#endif
6670
6671 /*
6672 * Statistics.
6673 */
6674 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6675 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6676 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6677 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6678 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6679 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
6680
6681 /* Init latched access mask. */
6682 pThis->uMaskLatchAccess = 0x3ff;
6683 return rc;
6684}
6685
6686
6687/**
6688 * The device registration structure.
6689 */
6690const PDMDEVREG g_DeviceVga =
6691{
6692 /* u32Version */
6693 PDM_DEVREG_VERSION,
6694 /* szName */
6695 "vga",
6696 /* szRCMod */
6697 "VBoxDDGC.gc",
6698 /* szR0Mod */
6699 "VBoxDDR0.r0",
6700 /* pszDescription */
6701 "VGA Adaptor with VESA extensions.",
6702 /* fFlags */
6703 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6704 /* fClass */
6705 PDM_DEVREG_CLASS_GRAPHICS,
6706 /* cMaxInstances */
6707 1,
6708 /* cbInstance */
6709 sizeof(VGASTATE),
6710 /* pfnConstruct */
6711 vgaR3Construct,
6712 /* pfnDestruct */
6713 vgaR3Destruct,
6714 /* pfnRelocate */
6715 vgaR3Relocate,
6716 /* pfnMemSetup */
6717 NULL,
6718 /* pfnPowerOn */
6719#ifdef VBOX_WITH_VMSVGA
6720 vmsvgaR3PowerOn,
6721#else
6722 NULL,
6723#endif
6724 /* pfnReset */
6725 vgaR3Reset,
6726 /* pfnSuspend */
6727 NULL,
6728 /* pfnResume */
6729 NULL,
6730 /* pfnAttach */
6731 vgaAttach,
6732 /* pfnDetach */
6733 vgaDetach,
6734 /* pfnQueryInterface */
6735 NULL,
6736 /* pfnInitComplete */
6737 NULL,
6738 /* pfnPowerOff */
6739 NULL,
6740 /* pfnSoftReset */
6741 NULL,
6742 /* u32VersionEnd */
6743 PDM_DEVREG_VERSION
6744};
6745
6746#endif /* !IN_RING3 */
6747#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
6748
6749/*
6750 * Local Variables:
6751 * nuke-trailing-whitespace-p:nil
6752 * End:
6753 */
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