VirtualBox

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

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

DevVGA: replaced assert with if statement for a transient VGA state.

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

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