VirtualBox

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

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

DevVGA: use actual fRenderVRAM value (fixed a regression from r94031)

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