VirtualBox

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

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

Graphics: never change connector to avoid racing; Graphics/Main: fix deadlocks

  • 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 51461 2014-05-29 13:20:06Z vboxsync $ */
2/** @file
3 * DevVGA - VBox VGA/VESA device.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on:
19 *
20 * QEMU VGA Emulator.
21 *
22 * Copyright (c) 2003 Fabrice Bellard
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46
47/* WARNING!!! All defines that affect VGAState should be placed to DevVGA.h !!!
48 * NEVER place them here as this would lead to VGASTATE inconsistency
49 * across different .cpp files !!!
50 */
51/** The size of the VGA GC mapping.
52 * This is supposed to be all the VGA memory accessible to the guest.
53 * The initial value was 256KB but NTAllInOne.iso appears to access more
54 * thus the limit was upped to 512KB.
55 *
56 * @todo Someone with some VGA knowhow should make a better guess at this value.
57 */
58#define VGA_MAPPING_SIZE _512K
59
60#ifdef VBOX_WITH_HGSMI
61#define PCIDEV_2_VGASTATE(pPciDev) ((PVGASTATE)((uintptr_t)pPciDev - RT_OFFSETOF(VGASTATE, Dev)))
62#endif /* VBOX_WITH_HGSMI */
63/** Converts a vga adaptor state pointer to a device instance pointer. */
64#define VGASTATE2DEVINS(pVgaState) ((pVgaState)->CTX_SUFF(pDevIns))
65
66/** Check that the video modes fit into virtual video memory.
67 * Only works when VBE_NEW_DYN_LIST is defined! */
68#define VRAM_SIZE_FIX
69
70/** Check buffer if an VRAM offset is within the right range or not. */
71#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
72# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
73 do { \
74 if ((off) >= VGA_MAPPING_SIZE) \
75 { \
76 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS); \
77 Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
78 return VINF_IOM_R3_MMIO_WRITE; \
79 } \
80 } while (0)
81#else
82# define VERIFY_VRAM_WRITE_OFF_RETURN(pThis, off) \
83 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), VINF_SUCCESS)
84#endif
85
86/** Check buffer if an VRAM offset is within the right range or not. */
87#if defined(IN_RC) || defined(VBOX_WITH_2X_4GB_ADDR_SPACE_IN_R0)
88# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
89 do { \
90 if ((off) >= VGA_MAPPING_SIZE) \
91 { \
92 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff); \
93 Log2(("%Rfn[%d]: %RX32 -> R3\n", __PRETTY_FUNCTION__, __LINE__, (off))); \
94 (rcVar) = VINF_IOM_R3_MMIO_READ; \
95 return 0; \
96 } \
97 } while (0)
98#else
99# define VERIFY_VRAM_READ_OFF_RETURN(pThis, off, rcVar) \
100 do { \
101 AssertMsgReturn((off) < (pThis)->vram_size, ("%RX32 !< %RX32\n", (uint32_t)(off), (pThis)->vram_size), 0xff); \
102 NOREF(rcVar); \
103 } while (0)
104#endif
105
106
107/*******************************************************************************
108* Header Files *
109*******************************************************************************/
110#define LOG_GROUP LOG_GROUP_DEV_VGA
111#include <VBox/vmm/pdmdev.h>
112#include <VBox/vmm/pgm.h>
113#ifdef IN_RING3
114# include <iprt/cdefs.h>
115# include <iprt/alloc.h>
116# include <iprt/ctype.h>
117#endif /* IN_RING3 */
118#include <iprt/assert.h>
119#include <iprt/asm.h>
120#include <iprt/file.h>
121#include <iprt/time.h>
122#include <iprt/string.h>
123#include <iprt/uuid.h>
124
125#include <VBox/VMMDev.h>
126#include <VBox/VBoxVideo.h>
127#include <VBox/bioslogo.h>
128
129/* should go BEFORE any other DevVGA include to make all DevVGA.h config defines be visible */
130#include "DevVGA.h"
131
132#if defined(VBE_NEW_DYN_LIST) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
133# include "DevVGAModes.h"
134# include <stdio.h> /* sscan */
135#endif
136
137#include "VBoxDD.h"
138#include "VBoxDD2.h"
139
140#ifdef VBOX_WITH_VMSVGA
141#include "DevVGA-SVGA.h"
142#include "vmsvga/svga_reg.h"
143#endif
144
145/*******************************************************************************
146* Structures and Typedefs *
147*******************************************************************************/
148#pragma pack(1)
149
150/** BMP File Format Bitmap Header. */
151typedef struct
152{
153 uint16_t Type; /* File Type Identifier */
154 uint32_t FileSize; /* Size of File */
155 uint16_t Reserved1; /* Reserved (should be 0) */
156 uint16_t Reserved2; /* Reserved (should be 0) */
157 uint32_t Offset; /* Offset to bitmap data */
158} BMPINFO;
159
160/** Pointer to a bitmap header*/
161typedef BMPINFO *PBMPINFO;
162
163/** OS/2 1.x Information Header Format. */
164typedef struct
165{
166 uint32_t Size; /* Size of Remaining Header */
167 uint16_t Width; /* Width of Bitmap in Pixels */
168 uint16_t Height; /* Height of Bitmap in Pixels */
169 uint16_t Planes; /* Number of Planes */
170 uint16_t BitCount; /* Color Bits Per Pixel */
171} OS2HDR;
172
173/** Pointer to a OS/2 1.x header format */
174typedef OS2HDR *POS2HDR;
175
176/** OS/2 2.0 Information Header Format. */
177typedef struct
178{
179 uint32_t Size; /* Size of Remaining Header */
180 uint32_t Width; /* Width of Bitmap in Pixels */
181 uint32_t Height; /* Height of Bitmap in Pixels */
182 uint16_t Planes; /* Number of Planes */
183 uint16_t BitCount; /* Color Bits Per Pixel */
184 uint32_t Compression; /* Compression Scheme (0=none) */
185 uint32_t SizeImage; /* Size of bitmap in bytes */
186 uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
187 uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
188 uint32_t ClrUsed; /* Number of Colors in Color Table */
189 uint32_t ClrImportant; /* Number of Important Colors */
190 uint16_t Units; /* Resolution Measurement Used */
191 uint16_t Reserved; /* Reserved FIelds (always 0) */
192 uint16_t Recording; /* Orientation of Bitmap */
193 uint16_t Rendering; /* Halftone Algorithm Used on Image */
194 uint32_t Size1; /* Halftone Algorithm Data */
195 uint32_t Size2; /* Halftone Algorithm Data */
196 uint32_t ColorEncoding; /* Color Table Format (always 0) */
197 uint32_t Identifier; /* Misc. Field for Application Use */
198} OS22HDR;
199
200/** Pointer to a OS/2 2.0 header format */
201typedef OS22HDR *POS22HDR;
202
203/** Windows 3.x Information Header Format. */
204typedef struct
205{
206 uint32_t Size; /* Size of Remaining Header */
207 uint32_t Width; /* Width of Bitmap in Pixels */
208 uint32_t Height; /* Height of Bitmap in Pixels */
209 uint16_t Planes; /* Number of Planes */
210 uint16_t BitCount; /* Bits Per Pixel */
211 uint32_t Compression; /* Compression Scheme (0=none) */
212 uint32_t SizeImage; /* Size of bitmap in bytes */
213 uint32_t XPelsPerMeter; /* Horz. Resolution in Pixels/Meter */
214 uint32_t YPelsPerMeter; /* Vert. Resolution in Pixels/Meter */
215 uint32_t ClrUsed; /* Number of Colors in Color Table */
216 uint32_t ClrImportant; /* Number of Important Colors */
217} WINHDR;
218
219/** Pointer to a Windows 3.x header format */
220typedef WINHDR *PWINHDR;
221
222#pragma pack()
223
224#define BMP_ID 0x4D42
225
226/** @name BMP compressions.
227 * @{ */
228#define BMP_COMPRESS_NONE 0
229#define BMP_COMPRESS_RLE8 1
230#define BMP_COMPRESS_RLE4 2
231/** @} */
232
233/** @name BMP header sizes.
234 * @{ */
235#define BMP_HEADER_OS21 12
236#define BMP_HEADER_OS22 64
237#define BMP_HEADER_WIN3 40
238/** @} */
239
240/** The BIOS boot menu text position, X. */
241#define LOGO_F12TEXT_X 304
242/** The BIOS boot menu text position, Y. */
243#define LOGO_F12TEXT_Y 460
244
245/** Width of the "Press F12 to select boot device." bitmap.
246 Anything that exceeds the limit of F12BootText below is filled with
247 background. */
248#define LOGO_F12TEXT_WIDTH 286
249/** Height of the boot device selection bitmap, see LOGO_F12TEXT_WIDTH. */
250#define LOGO_F12TEXT_HEIGHT 12
251
252/** The BIOS logo delay time (msec). */
253#define LOGO_DELAY_TIME 2000
254
255#define LOGO_MAX_WIDTH 640
256#define LOGO_MAX_HEIGHT 480
257#define LOGO_MAX_SIZE LOGO_MAX_WIDTH * LOGO_MAX_HEIGHT * 4
258
259
260/*******************************************************************************
261* Global Variables *
262*******************************************************************************/
263/* "Press F12 to select boot device." bitmap. */
264static const uint8_t g_abLogoF12BootText[] =
265{
266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
268 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x0F, 0x7C,
269 0xF8, 0xF0, 0x01, 0xE0, 0x81, 0x9F, 0x3F, 0x00, 0x70, 0xF8, 0x00, 0xE0, 0xC3,
270 0x07, 0x0F, 0x1F, 0x3E, 0x70, 0x00, 0xF0, 0xE1, 0xC3, 0x07, 0x0E, 0x00, 0x6E,
271 0x7C, 0x60, 0xE0, 0xE1, 0xC3, 0x07, 0xC6, 0x80, 0x81, 0x31, 0x63, 0xC6, 0x00,
272 0x30, 0x80, 0x61, 0x0C, 0x00, 0x36, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
273 0x18, 0x36, 0x00, 0xCC, 0x8C, 0x19, 0xC3, 0x06, 0xC0, 0x8C, 0x31, 0x3C, 0x30,
274 0x8C, 0x19, 0x83, 0x31, 0x60, 0x60, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x60, 0x18,
275 0x00, 0x80, 0xC1, 0x18, 0x00, 0x30, 0x06, 0x60, 0x18, 0x30, 0x80, 0x01, 0x00,
276 0x33, 0x63, 0xC6, 0x30, 0x00, 0x30, 0x63, 0x80, 0x19, 0x0C, 0x03, 0x06, 0x00,
277 0x0C, 0x18, 0x18, 0xC0, 0x81, 0x03, 0x00, 0x03, 0x18, 0x0C, 0x00, 0x60, 0x30,
278 0x06, 0x00, 0x87, 0x01, 0x18, 0x06, 0x0C, 0x60, 0x00, 0xC0, 0xCC, 0x98, 0x31,
279 0x0C, 0x00, 0xCC, 0x18, 0x30, 0x0C, 0xC3, 0x80, 0x01, 0x00, 0x03, 0x66, 0xFE,
280 0x18, 0x30, 0x00, 0xC0, 0x02, 0x06, 0x06, 0x00, 0x18, 0x8C, 0x01, 0x60, 0xE0,
281 0x0F, 0x86, 0x3F, 0x03, 0x18, 0x00, 0x30, 0x33, 0x66, 0x0C, 0x03, 0x00, 0x33,
282 0xFE, 0x0C, 0xC3, 0x30, 0xE0, 0x0F, 0xC0, 0x87, 0x9B, 0x31, 0x63, 0xC6, 0x00,
283 0xF0, 0x80, 0x01, 0x03, 0x00, 0x06, 0x63, 0x00, 0x8C, 0x19, 0x83, 0x61, 0xCC,
284 0x18, 0x06, 0x00, 0x6C, 0x8C, 0x19, 0xC3, 0x00, 0x80, 0x8D, 0x31, 0xC3, 0x30,
285 0x8C, 0x19, 0x03, 0x30, 0xB3, 0xC3, 0x87, 0x0F, 0x1F, 0x00, 0x2C, 0x60, 0x80,
286 0x01, 0xE0, 0x87, 0x0F, 0x00, 0x3E, 0x7C, 0x60, 0xF0, 0xE1, 0xE3, 0x07, 0x00,
287 0x0F, 0x3E, 0x7C, 0xFC, 0x00, 0xC0, 0xC3, 0xC7, 0x30, 0x0E, 0x3E, 0x7C, 0x00,
288 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x1E, 0xC0, 0x00, 0x60, 0x00,
289 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x00, 0x00,
290 0x0C, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
291 0x00, 0x00, 0x00, 0xC0, 0x0C, 0x87, 0x31, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
292 0x00, 0x06, 0x00, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x30,
293 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
294 0xF8, 0x83, 0xC1, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00,
295 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x80, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x30,
296 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
297 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
298 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
299};
300
301
302#ifndef VBOX_DEVICE_STRUCT_TESTCASE
303
304
305/**
306 * Set a VRAM page dirty.
307 *
308 * @param pThis VGA instance data.
309 * @param offVRAM The VRAM offset of the page to set.
310 */
311DECLINLINE(void) vga_set_dirty(PVGASTATE pThis, RTGCPHYS offVRAM)
312{
313 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
314 ASMBitSet(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
315 pThis->fHasDirtyBits = true;
316}
317
318/**
319 * Tests if a VRAM page is dirty.
320 *
321 * @returns true if dirty.
322 * @returns false if clean.
323 * @param pThis VGA instance data.
324 * @param offVRAM The VRAM offset of the page to check.
325 */
326DECLINLINE(bool) vga_is_dirty(PVGASTATE pThis, RTGCPHYS offVRAM)
327{
328 AssertMsg(offVRAM < pThis->vram_size, ("offVRAM = %p, pThis->vram_size = %p\n", offVRAM, pThis->vram_size));
329 return ASMBitTest(&pThis->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
330}
331
332/**
333 * Reset dirty flags in a give range.
334 *
335 * @param pThis VGA instance data.
336 * @param offVRAMStart Offset into the VRAM buffer of the first page.
337 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
338 */
339DECLINLINE(void) vga_reset_dirty(PVGASTATE pThis, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
340{
341 Assert(offVRAMStart < pThis->vram_size);
342 Assert(offVRAMEnd <= pThis->vram_size);
343 Assert(offVRAMStart < offVRAMEnd);
344 ASMBitClearRange(&pThis->au32DirtyBitmap[0], offVRAMStart >> PAGE_SHIFT, offVRAMEnd >> PAGE_SHIFT);
345}
346
347/* force some bits to zero */
348static const uint8_t sr_mask[8] = {
349 (uint8_t)~0xfc,
350 (uint8_t)~0xc2,
351 (uint8_t)~0xf0,
352 (uint8_t)~0xc0,
353 (uint8_t)~0xf1,
354 (uint8_t)~0xff,
355 (uint8_t)~0xff,
356 (uint8_t)~0x01,
357};
358
359static const uint8_t gr_mask[16] = {
360 (uint8_t)~0xf0, /* 0x00 */
361 (uint8_t)~0xf0, /* 0x01 */
362 (uint8_t)~0xf0, /* 0x02 */
363 (uint8_t)~0xe0, /* 0x03 */
364 (uint8_t)~0xfc, /* 0x04 */
365 (uint8_t)~0x84, /* 0x05 */
366 (uint8_t)~0xf0, /* 0x06 */
367 (uint8_t)~0xf0, /* 0x07 */
368 (uint8_t)~0x00, /* 0x08 */
369 (uint8_t)~0xff, /* 0x09 */
370 (uint8_t)~0xff, /* 0x0a */
371 (uint8_t)~0xff, /* 0x0b */
372 (uint8_t)~0xff, /* 0x0c */
373 (uint8_t)~0xff, /* 0x0d */
374 (uint8_t)~0xff, /* 0x0e */
375 (uint8_t)~0xff, /* 0x0f */
376};
377
378#define cbswap_32(__x) \
379((uint32_t)( \
380 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
381 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
382 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
383 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
384
385#ifdef WORDS_BIGENDIAN
386#define PAT(x) cbswap_32(x)
387#else
388#define PAT(x) (x)
389#endif
390
391#ifdef WORDS_BIGENDIAN
392#define BIG 1
393#else
394#define BIG 0
395#endif
396
397#ifdef WORDS_BIGENDIAN
398#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
399#else
400#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
401#endif
402
403static const uint32_t mask16[16] = {
404 PAT(0x00000000),
405 PAT(0x000000ff),
406 PAT(0x0000ff00),
407 PAT(0x0000ffff),
408 PAT(0x00ff0000),
409 PAT(0x00ff00ff),
410 PAT(0x00ffff00),
411 PAT(0x00ffffff),
412 PAT(0xff000000),
413 PAT(0xff0000ff),
414 PAT(0xff00ff00),
415 PAT(0xff00ffff),
416 PAT(0xffff0000),
417 PAT(0xffff00ff),
418 PAT(0xffffff00),
419 PAT(0xffffffff),
420};
421
422#undef PAT
423
424#ifdef WORDS_BIGENDIAN
425#define PAT(x) (x)
426#else
427#define PAT(x) cbswap_32(x)
428#endif
429
430static const uint32_t dmask16[16] = {
431 PAT(0x00000000),
432 PAT(0x000000ff),
433 PAT(0x0000ff00),
434 PAT(0x0000ffff),
435 PAT(0x00ff0000),
436 PAT(0x00ff00ff),
437 PAT(0x00ffff00),
438 PAT(0x00ffffff),
439 PAT(0xff000000),
440 PAT(0xff0000ff),
441 PAT(0xff00ff00),
442 PAT(0xff00ffff),
443 PAT(0xffff0000),
444 PAT(0xffff00ff),
445 PAT(0xffffff00),
446 PAT(0xffffffff),
447};
448
449static const uint32_t dmask4[4] = {
450 PAT(0x00000000),
451 PAT(0x0000ffff),
452 PAT(0xffff0000),
453 PAT(0xffffffff),
454};
455
456#if defined(IN_RING3)
457static uint32_t expand4[256];
458static uint16_t expand2[256];
459static uint8_t expand4to8[16];
460#endif /* IN_RING3 */
461
462/* Update the values needed for calculating Vertical Retrace and
463 * Display Enable status bits more or less accurately. The Display Enable
464 * bit is set (indicating *disabled* display signal) when either the
465 * horizontal (hblank) or vertical (vblank) blanking is active. The
466 * Vertical Retrace bit is set when vertical retrace (vsync) is active.
467 * Unless the CRTC is horribly misprogrammed, vsync implies vblank.
468 */
469static void vga_update_retrace_state(PVGASTATE pThis)
470{
471 unsigned htotal_cclks, vtotal_lines, chars_per_sec;
472 unsigned hblank_start_cclk, hblank_end_cclk, hblank_width, hblank_skew_cclks;
473 unsigned vsync_start_line, vsync_end, vsync_width;
474 unsigned vblank_start_line, vblank_end, vblank_width;
475 unsigned char_dots, clock_doubled, clock_index;
476 const int clocks[] = {25175000, 28322000, 25175000, 25175000};
477 vga_retrace_s *r = &pThis->retrace_state;
478
479 /* For horizontal timings, we only care about the blanking start/end. */
480 htotal_cclks = pThis->cr[0x00] + 5;
481 hblank_start_cclk = pThis->cr[0x02];
482 hblank_end_cclk = (pThis->cr[0x03] & 0x1f) + ((pThis->cr[0x05] & 0x80) >> 2);
483 hblank_skew_cclks = (pThis->cr[0x03] >> 5) & 3;
484
485 /* For vertical timings, we need both the blanking start/end... */
486 vtotal_lines = pThis->cr[0x06] + ((pThis->cr[0x07] & 1) << 8) + ((pThis->cr[0x07] & 0x20) << 4) + 2;
487 vblank_start_line = pThis->cr[0x15] + ((pThis->cr[0x07] & 8) << 5) + ((pThis->cr[0x09] & 0x20) << 4);
488 vblank_end = pThis->cr[0x16];
489 /* ... and the vertical retrace (vsync) start/end. */
490 vsync_start_line = pThis->cr[0x10] + ((pThis->cr[0x07] & 4) << 6) + ((pThis->cr[0x07] & 0x80) << 2);
491 vsync_end = pThis->cr[0x11] & 0xf;
492
493 /* Calculate the blanking and sync widths. The way it's implemented in
494 * the VGA with limited-width compare counters is quite a piece of work.
495 */
496 hblank_width = (hblank_end_cclk - hblank_start_cclk) & 0x3f;/* 6 bits */
497 vblank_width = (vblank_end - vblank_start_line) & 0xff; /* 8 bits */
498 vsync_width = (vsync_end - vsync_start_line) & 0xf; /* 4 bits */
499
500 /* Calculate the dot and character clock rates. */
501 clock_doubled = (pThis->sr[0x01] >> 3) & 1; /* Clock doubling bit. */
502 clock_index = (pThis->msr >> 2) & 3;
503 char_dots = (pThis->sr[0x01] & 1) ? 8 : 9; /* 8 or 9 dots per cclk. */
504
505 chars_per_sec = clocks[clock_index] / char_dots;
506 Assert(chars_per_sec); /* Can't possibly be zero. */
507
508 htotal_cclks <<= clock_doubled;
509
510 /* Calculate the number of cclks per entire frame. */
511 r->frame_cclks = vtotal_lines * htotal_cclks;
512 Assert(r->frame_cclks); /* Can't possibly be zero. */
513
514 if (r->v_freq_hz) { /* Could be set to emulate a specific rate. */
515 r->cclk_ns = 1000000000 / (r->frame_cclks * r->v_freq_hz);
516 } else {
517 r->cclk_ns = 1000000000 / chars_per_sec;
518 }
519 Assert(r->cclk_ns);
520 r->frame_ns = r->frame_cclks * r->cclk_ns;
521
522 /* Calculate timings in cclks/lines. Stored but not directly used. */
523 r->hb_start = hblank_start_cclk + hblank_skew_cclks;
524 r->hb_end = hblank_start_cclk + hblank_width + hblank_skew_cclks;
525 r->h_total = htotal_cclks;
526 Assert(r->h_total); /* Can't possibly be zero. */
527
528 r->vb_start = vblank_start_line;
529 r->vb_end = vblank_start_line + vblank_width + 1;
530 r->vs_start = vsync_start_line;
531 r->vs_end = vsync_start_line + vsync_width + 1;
532
533 /* Calculate timings in nanoseconds. For easier comparisons, the frame
534 * is considered to start at the beginning of the vertical and horizontal
535 * blanking period.
536 */
537 r->h_total_ns = htotal_cclks * r->cclk_ns;
538 r->hb_end_ns = hblank_width * r->cclk_ns;
539 r->vb_end_ns = vblank_width * r->h_total_ns;
540 r->vs_start_ns = (r->vs_start - r->vb_start) * r->h_total_ns;
541 r->vs_end_ns = (r->vs_end - r->vb_start) * r->h_total_ns;
542 Assert(r->h_total_ns); /* See h_total. */
543}
544
545static uint8_t vga_retrace(PVGASTATE pThis)
546{
547 vga_retrace_s *r = &pThis->retrace_state;
548
549 if (r->frame_ns) {
550 uint8_t val = pThis->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE);
551 unsigned cur_frame_ns, cur_line_ns;
552 uint64_t time_ns;
553
554 time_ns = PDMDevHlpTMTimeVirtGetNano(VGASTATE2DEVINS(pThis));
555
556 /* Determine the time within the frame. */
557 cur_frame_ns = time_ns % r->frame_ns;
558
559 /* See if we're in the vertical blanking period... */
560 if (cur_frame_ns < r->vb_end_ns) {
561 val |= ST01_DISP_ENABLE;
562 /* ... and additionally in the vertical sync period. */
563 if (cur_frame_ns >= r->vs_start_ns && cur_frame_ns <= r->vs_end_ns)
564 val |= ST01_V_RETRACE;
565 } else {
566 /* Determine the time within the current scanline. */
567 cur_line_ns = cur_frame_ns % r->h_total_ns;
568 /* See if we're in the horizontal blanking period. */
569 if (cur_line_ns < r->hb_end_ns)
570 val |= ST01_DISP_ENABLE;
571 }
572 return val;
573 } else {
574 return pThis->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE);
575 }
576}
577
578int vga_ioport_invalid(PVGASTATE pThis, uint32_t addr)
579{
580 if (pThis->msr & MSR_COLOR_EMULATION) {
581 /* Color */
582 return (addr >= 0x3b0 && addr <= 0x3bf);
583 } else {
584 /* Monochrome */
585 return (addr >= 0x3d0 && addr <= 0x3df);
586 }
587}
588
589static uint32_t vga_ioport_read(PVGASTATE pThis, uint32_t addr)
590{
591 int val, index;
592
593 /* check port range access depending on color/monochrome mode */
594 if (vga_ioport_invalid(pThis, addr)) {
595 val = 0xff;
596 Log(("VGA: following read ignored\n"));
597 } else {
598 switch(addr) {
599 case 0x3c0:
600 if (pThis->ar_flip_flop == 0) {
601 val = pThis->ar_index;
602 } else {
603 val = 0;
604 }
605 break;
606 case 0x3c1:
607 index = pThis->ar_index & 0x1f;
608 if (index < 21)
609 val = pThis->ar[index];
610 else
611 val = 0;
612 break;
613 case 0x3c2:
614 val = pThis->st00;
615 break;
616 case 0x3c4:
617 val = pThis->sr_index;
618 break;
619 case 0x3c5:
620 val = pThis->sr[pThis->sr_index];
621 Log2(("vga: read SR%x = 0x%02x\n", pThis->sr_index, val));
622 break;
623 case 0x3c7:
624 val = pThis->dac_state;
625 break;
626 case 0x3c8:
627 val = pThis->dac_write_index;
628 break;
629 case 0x3c9:
630 val = pThis->palette[pThis->dac_read_index * 3 + pThis->dac_sub_index];
631 if (++pThis->dac_sub_index == 3) {
632 pThis->dac_sub_index = 0;
633 pThis->dac_read_index++;
634 }
635 break;
636 case 0x3ca:
637 val = pThis->fcr;
638 break;
639 case 0x3cc:
640 val = pThis->msr;
641 break;
642 case 0x3ce:
643 val = pThis->gr_index;
644 break;
645 case 0x3cf:
646 val = pThis->gr[pThis->gr_index];
647 Log2(("vga: read GR%x = 0x%02x\n", pThis->gr_index, val));
648 break;
649 case 0x3b4:
650 case 0x3d4:
651 val = pThis->cr_index;
652 break;
653 case 0x3b5:
654 case 0x3d5:
655 val = pThis->cr[pThis->cr_index];
656 Log2(("vga: read CR%x = 0x%02x\n", pThis->cr_index, val));
657 break;
658 case 0x3ba:
659 case 0x3da:
660 val = pThis->st01 = vga_retrace(pThis);
661 pThis->ar_flip_flop = 0;
662 break;
663 default:
664 val = 0x00;
665 break;
666 }
667 }
668 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
669 return val;
670}
671
672static void vga_ioport_write(PVGASTATE pThis, uint32_t addr, uint32_t val)
673{
674 int index;
675
676 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
677
678 /* check port range access depending on color/monochrome mode */
679 if (vga_ioport_invalid(pThis, addr)) {
680 Log(("VGA: previous write ignored\n"));
681 return;
682 }
683
684 switch(addr) {
685 case 0x3c0:
686 if (pThis->ar_flip_flop == 0) {
687 val &= 0x3f;
688 pThis->ar_index = val;
689 } else {
690 index = pThis->ar_index & 0x1f;
691 switch(index) {
692 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
693 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
694 pThis->ar[index] = val & 0x3f;
695 break;
696 case 0x10:
697 pThis->ar[index] = val & ~0x10;
698 break;
699 case 0x11:
700 pThis->ar[index] = val;
701 break;
702 case 0x12:
703 pThis->ar[index] = val & ~0xc0;
704 break;
705 case 0x13:
706 pThis->ar[index] = val & ~0xf0;
707 break;
708 case 0x14:
709 pThis->ar[index] = val & ~0xf0;
710 break;
711 default:
712 break;
713 }
714 }
715 pThis->ar_flip_flop ^= 1;
716 break;
717 case 0x3c2:
718 pThis->msr = val & ~0x10;
719 if (pThis->fRealRetrace)
720 vga_update_retrace_state(pThis);
721 pThis->st00 = (pThis->st00 & ~0x10) | (0x90 >> ((val >> 2) & 0x3));
722 break;
723 case 0x3c4:
724 pThis->sr_index = val & 7;
725 break;
726 case 0x3c5:
727 Log2(("vga: write SR%x = 0x%02x\n", pThis->sr_index, val));
728 pThis->sr[pThis->sr_index] = val & sr_mask[pThis->sr_index];
729 /* Allow SR07 to disable VBE. */
730 if (pThis->sr_index == 0x07 && !(val & 1))
731 {
732 pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] = VBE_DISPI_DISABLED;
733 pThis->bank_offset = 0;
734 }
735 if (pThis->fRealRetrace && pThis->sr_index == 0x01)
736 vga_update_retrace_state(pThis);
737#ifndef IN_RC
738 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
739 if ( pThis->sr_index == 4 /* mode */
740 || pThis->sr_index == 2 /* plane mask */)
741 {
742 if (pThis->fRemappedVGA)
743 {
744 IOMMMIOResetRegion(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), 0x000a0000);
745 pThis->fRemappedVGA = false;
746 }
747 }
748#endif
749 break;
750 case 0x3c7:
751 pThis->dac_read_index = val;
752 pThis->dac_sub_index = 0;
753 pThis->dac_state = 3;
754 break;
755 case 0x3c8:
756 pThis->dac_write_index = val;
757 pThis->dac_sub_index = 0;
758 pThis->dac_state = 0;
759 break;
760 case 0x3c9:
761 pThis->dac_cache[pThis->dac_sub_index] = val;
762 if (++pThis->dac_sub_index == 3) {
763 memcpy(&pThis->palette[pThis->dac_write_index * 3], pThis->dac_cache, 3);
764 pThis->dac_sub_index = 0;
765 pThis->dac_write_index++;
766 }
767 break;
768 case 0x3ce:
769 pThis->gr_index = val & 0x0f;
770 break;
771 case 0x3cf:
772 Log2(("vga: write GR%x = 0x%02x\n", pThis->gr_index, val));
773 pThis->gr[pThis->gr_index] = val & gr_mask[pThis->gr_index];
774
775#ifndef IN_RC
776 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
777 if (pThis->gr_index == 6 /* memory map mode */)
778 {
779 if (pThis->fRemappedVGA)
780 {
781 IOMMMIOResetRegion(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), 0x000a0000);
782 pThis->fRemappedVGA = false;
783 }
784 }
785#endif
786 break;
787
788 case 0x3b4:
789 case 0x3d4:
790 pThis->cr_index = val;
791 break;
792 case 0x3b5:
793 case 0x3d5:
794 Log2(("vga: write CR%x = 0x%02x\n", pThis->cr_index, val));
795 /* handle CR0-7 protection */
796 if ((pThis->cr[0x11] & 0x80) && pThis->cr_index <= 7) {
797 /* can always write bit 4 of CR7 */
798 if (pThis->cr_index == 7)
799 pThis->cr[7] = (pThis->cr[7] & ~0x10) | (val & 0x10);
800 return;
801 }
802 pThis->cr[pThis->cr_index] = val;
803
804 if (pThis->fRealRetrace) {
805 /* The following registers are only updated during a mode set. */
806 switch(pThis->cr_index) {
807 case 0x00:
808 case 0x02:
809 case 0x03:
810 case 0x05:
811 case 0x06:
812 case 0x07:
813 case 0x09:
814 case 0x10:
815 case 0x11:
816 case 0x15:
817 case 0x16:
818 vga_update_retrace_state(pThis);
819 break;
820 }
821 }
822 break;
823 case 0x3ba:
824 case 0x3da:
825 pThis->fcr = val & 0x10;
826 break;
827 }
828}
829
830#ifdef CONFIG_BOCHS_VBE
831static uint32_t vbe_ioport_read_index(PVGASTATE pThis, uint32_t addr)
832{
833 uint32_t val = pThis->vbe_index;
834 NOREF(addr);
835 return val;
836}
837
838static uint32_t vbe_ioport_read_data(PVGASTATE pThis, uint32_t addr)
839{
840 uint32_t val;
841 NOREF(addr);
842
843 if (pThis->vbe_index < VBE_DISPI_INDEX_NB) {
844 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
845 switch(pThis->vbe_index) {
846 /* XXX: do not hardcode ? */
847 case VBE_DISPI_INDEX_XRES:
848 val = VBE_DISPI_MAX_XRES;
849 break;
850 case VBE_DISPI_INDEX_YRES:
851 val = VBE_DISPI_MAX_YRES;
852 break;
853 case VBE_DISPI_INDEX_BPP:
854 val = VBE_DISPI_MAX_BPP;
855 break;
856 default:
857 Assert(pThis->vbe_index < VBE_DISPI_INDEX_NB);
858 val = pThis->vbe_regs[pThis->vbe_index];
859 break;
860 }
861 } else {
862 switch(pThis->vbe_index) {
863 case VBE_DISPI_INDEX_VBOX_VIDEO:
864 /* Reading from the port means that the old additions are requesting the number of monitors. */
865 val = 1;
866 break;
867 default:
868 Assert(pThis->vbe_index < VBE_DISPI_INDEX_NB);
869 val = pThis->vbe_regs[pThis->vbe_index];
870 break;
871 }
872 }
873 } else {
874 val = 0;
875 }
876 Log(("VBE: read index=0x%x val=0x%x\n", pThis->vbe_index, val));
877 return val;
878}
879
880#define VBE_PITCH_ALIGN 4 /* Align pitch to 32 bits - Qt requires that. */
881
882/* Calculate scanline pitch based on bit depth and width in pixels. */
883static uint32_t calc_line_pitch(uint16_t bpp, uint16_t width)
884{
885 uint32_t pitch, aligned_pitch;
886
887 if (bpp <= 4)
888 pitch = width >> 1;
889 else
890 pitch = width * ((bpp + 7) >> 3);
891
892 /* Align the pitch to some sensible value. */
893 aligned_pitch = (pitch + (VBE_PITCH_ALIGN - 1)) & ~(VBE_PITCH_ALIGN - 1);
894 if (aligned_pitch != pitch)
895 Log(("VBE: Line pitch %d aligned to %d bytes\n", pitch, aligned_pitch));
896
897 return aligned_pitch;
898}
899
900#ifdef SOME_UNUSED_FUNCTION
901/* Calculate line width in pixels based on bit depth and pitch. */
902static uint32_t calc_line_width(uint16_t bpp, uint32_t pitch)
903{
904 uint32_t width;
905
906 if (bpp <= 4)
907 width = pitch << 1;
908 else
909 width = pitch / ((bpp + 7) >> 3);
910
911 return width;
912}
913#endif
914
915static void recalculate_data(PVGASTATE pThis, bool fVirtHeightOnly)
916{
917 uint16_t cBPP = pThis->vbe_regs[VBE_DISPI_INDEX_BPP];
918 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
919 uint16_t cX = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
920 if (!cBPP || !cX)
921 return; /* Not enough data has been set yet. */
922 uint32_t cbLinePitch = calc_line_pitch(cBPP, cVirtWidth);
923 if (!cbLinePitch)
924 cbLinePitch = calc_line_pitch(cBPP, cX);
925 Assert(cbLinePitch != 0);
926 uint32_t cVirtHeight = pThis->vram_size / cbLinePitch;
927 if (!fVirtHeightOnly)
928 {
929 uint16_t offX = pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
930 uint16_t offY = pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
931 uint32_t offStart = cbLinePitch * offY;
932 if (cBPP == 4)
933 offStart += offX >> 1;
934 else
935 offStart += offX * ((cBPP + 7) >> 3);
936 offStart >>= 2;
937 pThis->vbe_line_offset = RT_MIN(cbLinePitch, pThis->vram_size);
938 pThis->vbe_start_addr = RT_MIN(offStart, pThis->vram_size);
939 }
940
941 /* The VBE_DISPI_INDEX_VIRT_HEIGHT is used to prevent setting resolution bigger than VRAM permits
942 * it is used instead of VBE_DISPI_INDEX_YRES *only* in case
943 * pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] < pThis->vbe_regs[VBE_DISPI_INDEX_YRES]
944 * We can not simply do pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = cVirtHeight since
945 * the cVirtHeight we calculated can exceed the 16bit value range
946 * instead we'll check if it's bigger than pThis->vbe_regs[VBE_DISPI_INDEX_YRES], and if yes,
947 * assign the pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] with a dummy UINT16_MAX value
948 * that is always bigger than pThis->vbe_regs[VBE_DISPI_INDEX_YRES]
949 * to just ensure the pThis->vbe_regs[VBE_DISPI_INDEX_YRES] is always used */
950 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = (cVirtHeight >= (uint32_t)pThis->vbe_regs[VBE_DISPI_INDEX_YRES])
951 ? UINT16_MAX : (uint16_t)cVirtHeight;
952}
953
954static void vbe_ioport_write_index(PVGASTATE pThis, uint32_t addr, uint32_t val)
955{
956 pThis->vbe_index = val;
957 NOREF(addr);
958}
959
960static int vbe_ioport_write_data(PVGASTATE pThis, uint32_t addr, uint32_t val)
961{
962 uint32_t max_bank;
963 NOREF(addr);
964
965 if (pThis->vbe_index <= VBE_DISPI_INDEX_NB) {
966 bool fRecalculate = false;
967 Log(("VBE: write index=0x%x val=0x%x\n", pThis->vbe_index, val));
968 switch(pThis->vbe_index) {
969 case VBE_DISPI_INDEX_ID:
970 if (val == VBE_DISPI_ID0 ||
971 val == VBE_DISPI_ID1 ||
972 val == VBE_DISPI_ID2 ||
973 val == VBE_DISPI_ID3 ||
974 val == VBE_DISPI_ID4) {
975 pThis->vbe_regs[pThis->vbe_index] = val;
976 }
977 if (val == VBE_DISPI_ID_VBOX_VIDEO) {
978 pThis->vbe_regs[pThis->vbe_index] = val;
979 } else if (val == VBE_DISPI_ID_ANYX) {
980 pThis->vbe_regs[pThis->vbe_index] = val;
981 }
982#ifdef VBOX_WITH_HGSMI
983 else if (val == VBE_DISPI_ID_HGSMI) {
984 pThis->vbe_regs[pThis->vbe_index] = val;
985 }
986#endif /* VBOX_WITH_HGSMI */
987 break;
988 case VBE_DISPI_INDEX_XRES:
989 if (val <= VBE_DISPI_MAX_XRES)
990 {
991 pThis->vbe_regs[pThis->vbe_index] = val;
992 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = val;
993 fRecalculate = true;
994 }
995 break;
996 case VBE_DISPI_INDEX_YRES:
997 if (val <= VBE_DISPI_MAX_YRES)
998 pThis->vbe_regs[pThis->vbe_index] = val;
999 break;
1000 case VBE_DISPI_INDEX_BPP:
1001 if (val == 0)
1002 val = 8;
1003 if (val == 4 || val == 8 || val == 15 ||
1004 val == 16 || val == 24 || val == 32) {
1005 pThis->vbe_regs[pThis->vbe_index] = val;
1006 fRecalculate = true;
1007 }
1008 break;
1009 case VBE_DISPI_INDEX_BANK:
1010 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] <= 4)
1011 max_bank = pThis->vbe_bank_max >> 2; /* Each bank really covers 256K */
1012 else
1013 max_bank = pThis->vbe_bank_max;
1014 /* Old software may pass garbage in the high byte of bank. If the maximum
1015 * bank fits into a single byte, toss the high byte the user supplied.
1016 */
1017 if (max_bank < 0x100)
1018 val &= 0xff;
1019 if (val > max_bank)
1020 val = max_bank;
1021 pThis->vbe_regs[pThis->vbe_index] = val;
1022 pThis->bank_offset = (val << 16);
1023
1024#ifndef IN_RC
1025 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1026 if (pThis->fRemappedVGA)
1027 {
1028 IOMMMIOResetRegion(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), 0x000a0000);
1029 pThis->fRemappedVGA = false;
1030 }
1031#endif
1032 break;
1033
1034 case VBE_DISPI_INDEX_ENABLE:
1035#ifndef IN_RING3
1036 return VINF_IOM_R3_IOPORT_WRITE;
1037#else
1038 {
1039 if ((val & VBE_DISPI_ENABLED) &&
1040 !(pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
1041 int h, shift_control;
1042 /* Check the values before we screw up with a resolution which is too big or small. */
1043 size_t cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1044 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
1045 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
1046 else
1047 cb = pThis->vbe_regs[VBE_DISPI_INDEX_XRES] * ((pThis->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
1048 cb *= pThis->vbe_regs[VBE_DISPI_INDEX_YRES];
1049 uint16_t cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH];
1050 if (!cVirtWidth)
1051 cVirtWidth = pThis->vbe_regs[VBE_DISPI_INDEX_XRES];
1052 if ( !cVirtWidth
1053 || !pThis->vbe_regs[VBE_DISPI_INDEX_YRES]
1054 || cb > pThis->vram_size)
1055 {
1056 AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
1057 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], pThis->vbe_regs[VBE_DISPI_INDEX_YRES], cb, pThis->vram_size));
1058 return VINF_SUCCESS; /* Note: silent failure like before */
1059 }
1060
1061 /* When VBE interface is enabled, it is reset. */
1062 pThis->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
1063 pThis->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
1064 fRecalculate = true;
1065
1066 /* clear the screen (should be done in BIOS) */
1067 if (!(val & VBE_DISPI_NOCLEARMEM)) {
1068 uint16_t cY = RT_MIN(pThis->vbe_regs[VBE_DISPI_INDEX_YRES],
1069 pThis->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT]);
1070 uint16_t cbLinePitch = pThis->vbe_line_offset;
1071 memset(pThis->CTX_SUFF(vram_ptr), 0,
1072 cY * cbLinePitch);
1073 }
1074
1075 /* we initialize the VGA graphic mode (should be done
1076 in BIOS) */
1077 pThis->gr[0x06] = (pThis->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
1078 pThis->cr[0x17] |= 3; /* no CGA modes */
1079 pThis->cr[0x13] = pThis->vbe_line_offset >> 3;
1080 /* width */
1081 pThis->cr[0x01] = (cVirtWidth >> 3) - 1;
1082 /* height (only meaningful if < 1024) */
1083 h = pThis->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
1084 pThis->cr[0x12] = h;
1085 pThis->cr[0x07] = (pThis->cr[0x07] & ~0x42) |
1086 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
1087 /* line compare to 1023 */
1088 pThis->cr[0x18] = 0xff;
1089 pThis->cr[0x07] |= 0x10;
1090 pThis->cr[0x09] |= 0x40;
1091
1092 if (pThis->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
1093 shift_control = 0;
1094 pThis->sr[0x01] &= ~8; /* no double line */
1095 } else {
1096 shift_control = 2;
1097 pThis->sr[4] |= 0x08; /* set chain 4 mode */
1098 pThis->sr[2] |= 0x0f; /* activate all planes */
1099 /* Indicate non-VGA mode in SR07. */
1100 pThis->sr[7] |= 1;
1101 }
1102 pThis->gr[0x05] = (pThis->gr[0x05] & ~0x60) | (shift_control << 5);
1103 pThis->cr[0x09] &= ~0x9f; /* no double scan */
1104 /* sunlover 30.05.2007
1105 * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
1106 * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vga_update_display.
1107 * But the VBE mode is graphics, so not a blank anymore.
1108 */
1109 pThis->ar_index |= 0x20;
1110 } else {
1111 /* XXX: the bios should do that */
1112 /* sunlover 21.12.2006
1113 * Here is probably more to reset. When this was executed in GC
1114 * then the *update* functions could not detect a mode change.
1115 * Or may be these update function should take the pThis->vbe_regs[pThis->vbe_index]
1116 * into account when detecting a mode change.
1117 *
1118 * The 'mode reset not detected' problem is now fixed by executing the
1119 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
1120 * LFBChange callback.
1121 */
1122 pThis->bank_offset = 0;
1123 }
1124 pThis->vbe_regs[pThis->vbe_index] = val;
1125 /*
1126 * LFB video mode is either disabled or changed. Notify the display
1127 * and reset VBVA.
1128 */
1129 pThis->pDrv->pfnLFBModeChange(pThis->pDrv, (val & VBE_DISPI_ENABLED) != 0);
1130#ifdef VBOX_WITH_HGSMI
1131 VBVAPause(pThis, (val & VBE_DISPI_ENABLED) == 0);
1132#endif /* VBOX_WITH_HGSMI */
1133
1134 /* The VGA region is (could be) affected by this change; reset all aliases we've created. */
1135 if (pThis->fRemappedVGA)
1136 {
1137 IOMMMIOResetRegion(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), 0x000a0000);
1138 pThis->fRemappedVGA = false;
1139 }
1140 break;
1141 }
1142#endif /* IN_RING3 */
1143 case VBE_DISPI_INDEX_VIRT_WIDTH:
1144 case VBE_DISPI_INDEX_X_OFFSET:
1145 case VBE_DISPI_INDEX_Y_OFFSET:
1146 {
1147 pThis->vbe_regs[pThis->vbe_index] = val;
1148 fRecalculate = true;
1149 }
1150 break;
1151 case VBE_DISPI_INDEX_VBOX_VIDEO:
1152#ifndef IN_RING3
1153 return VINF_IOM_R3_IOPORT_WRITE;
1154#else
1155 /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
1156 if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
1157 {
1158 pThis->pDrv->pfnProcessAdapterData(pThis->pDrv, NULL, 0);
1159 }
1160 else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
1161 {
1162 pThis->pDrv->pfnProcessAdapterData(pThis->pDrv, pThis->CTX_SUFF(vram_ptr), pThis->vram_size);
1163 }
1164 else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
1165 {
1166 pThis->pDrv->pfnProcessDisplayData(pThis->pDrv, pThis->CTX_SUFF(vram_ptr), val & 0xFFFF);
1167 }
1168#endif /* IN_RING3 */
1169 break;
1170 default:
1171 break;
1172 }
1173 if (fRecalculate)
1174 {
1175 recalculate_data(pThis, false);
1176 }
1177 }
1178 return VINF_SUCCESS;
1179}
1180#endif
1181
1182/* called for accesses between 0xa0000 and 0xc0000 */
1183static uint32_t vga_mem_readb(PVGASTATE pThis, RTGCPHYS addr, int *prc)
1184{
1185 int memory_map_mode, plane;
1186 uint32_t ret;
1187
1188 Log3(("vga: read [0x%x] -> ", addr));
1189 /* convert to VGA memory offset */
1190 memory_map_mode = (pThis->gr[6] >> 2) & 3;
1191#ifndef IN_RC
1192 RTGCPHYS GCPhys = addr; /* save original address */
1193#endif
1194
1195 addr &= 0x1ffff;
1196 switch(memory_map_mode) {
1197 case 0:
1198 break;
1199 case 1:
1200 if (addr >= 0x10000)
1201 return 0xff;
1202 addr += pThis->bank_offset;
1203 break;
1204 case 2:
1205 addr -= 0x10000;
1206 if (addr >= 0x8000)
1207 return 0xff;
1208 break;
1209 default:
1210 case 3:
1211 addr -= 0x18000;
1212 if (addr >= 0x8000)
1213 return 0xff;
1214 break;
1215 }
1216
1217 if (pThis->sr[4] & 0x08) {
1218 /* chain 4 mode : simplest access */
1219# ifndef IN_RC
1220 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1221 if ( (pThis->sr[2] & 3) == 3
1222 && !vga_is_dirty(pThis, addr))
1223 {
1224 /** @todo only allow read access (doesn't work now) */
1225 STAM_COUNTER_INC(&pThis->StatMapPage);
1226 IOMMMIOMapMMIO2Page(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), GCPhys,
1227 pThis->GCPhysVRAM + addr, X86_PTE_RW | X86_PTE_P);
1228 /* Set as dirty as write accesses won't be noticed now. */
1229 vga_set_dirty(pThis, addr);
1230 pThis->fRemappedVGA = true;
1231 }
1232# endif /* IN_RC */
1233 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr, *prc);
1234 ret = pThis->CTX_SUFF(vram_ptr)[addr];
1235 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1236 /* odd/even mode (aka text mode mapping) */
1237 plane = (pThis->gr[4] & 2) | (addr & 1);
1238 /* See the comment for a similar line in vga_mem_writeb. */
1239 RTGCPHYS off = ((addr & ~1) << 2) | plane;
1240 VERIFY_VRAM_READ_OFF_RETURN(pThis, off, *prc);
1241 ret = pThis->CTX_SUFF(vram_ptr)[off];
1242 } else {
1243 /* standard VGA latched access */
1244 VERIFY_VRAM_READ_OFF_RETURN(pThis, addr, *prc);
1245 pThis->latch = ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[addr];
1246
1247 if (!(pThis->gr[5] & 0x08)) {
1248 /* read mode 0 */
1249 plane = pThis->gr[4];
1250 ret = GET_PLANE(pThis->latch, plane);
1251 } else {
1252 /* read mode 1 */
1253 ret = (pThis->latch ^ mask16[pThis->gr[2]]) & mask16[pThis->gr[7]];
1254 ret |= ret >> 16;
1255 ret |= ret >> 8;
1256 ret = (~ret) & 0xff;
1257 }
1258 }
1259 Log3((" 0x%02x\n", ret));
1260 return ret;
1261}
1262
1263/* called for accesses between 0xa0000 and 0xc0000 */
1264static int vga_mem_writeb(PVGASTATE pThis, RTGCPHYS addr, uint32_t val)
1265{
1266 int memory_map_mode, plane, write_mode, b, func_select, mask;
1267 uint32_t write_mask, bit_mask, set_mask;
1268
1269 Log3(("vga: [0x%x] = 0x%02x\n", addr, val));
1270 /* convert to VGA memory offset */
1271 memory_map_mode = (pThis->gr[6] >> 2) & 3;
1272#ifndef IN_RC
1273 RTGCPHYS GCPhys = addr; /* save original address */
1274#endif
1275
1276 addr &= 0x1ffff;
1277 switch(memory_map_mode) {
1278 case 0:
1279 break;
1280 case 1:
1281 if (addr >= 0x10000)
1282 return VINF_SUCCESS;
1283 addr += pThis->bank_offset;
1284 break;
1285 case 2:
1286 addr -= 0x10000;
1287 if (addr >= 0x8000)
1288 return VINF_SUCCESS;
1289 break;
1290 default:
1291 case 3:
1292 addr -= 0x18000;
1293 if (addr >= 0x8000)
1294 return VINF_SUCCESS;
1295 break;
1296 }
1297
1298 if (pThis->sr[4] & 0x08) {
1299 /* chain 4 mode : simplest access */
1300 plane = addr & 3;
1301 mask = (1 << plane);
1302 if (pThis->sr[2] & mask) {
1303# ifndef IN_RC
1304 /* If all planes are accessible, then map the page to the frame buffer and make it writable. */
1305 if ( (pThis->sr[2] & 3) == 3
1306 && !vga_is_dirty(pThis, addr))
1307 {
1308 STAM_COUNTER_INC(&pThis->StatMapPage);
1309 IOMMMIOMapMMIO2Page(PDMDevHlpGetVM(pThis->CTX_SUFF(pDevIns)), GCPhys,
1310 pThis->GCPhysVRAM + addr, X86_PTE_RW | X86_PTE_P);
1311 pThis->fRemappedVGA = true;
1312 }
1313# endif /* IN_RC */
1314
1315 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1316 pThis->CTX_SUFF(vram_ptr)[addr] = val;
1317 Log3(("vga: chain4: [0x%x]\n", addr));
1318 pThis->plane_updated |= mask; /* only used to detect font change */
1319 vga_set_dirty(pThis, addr);
1320 }
1321 } else if (!(pThis->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1322 /* odd/even mode (aka text mode mapping) */
1323 plane = (pThis->gr[4] & 2) | (addr & 1);
1324 mask = (1 << plane);
1325 if (pThis->sr[2] & mask) {
1326 /* 'addr' is offset in a plane, bit 0 selects the plane.
1327 * Mask the bit 0, convert plane index to vram offset,
1328 * that is multiply by the number of planes,
1329 * and select the plane byte in the vram offset.
1330 */
1331 addr = ((addr & ~1) << 2) | plane;
1332 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr);
1333 pThis->CTX_SUFF(vram_ptr)[addr] = val;
1334 Log3(("vga: odd/even: [0x%x]\n", addr));
1335 pThis->plane_updated |= mask; /* only used to detect font change */
1336 vga_set_dirty(pThis, addr);
1337 }
1338 } else {
1339 /* standard VGA latched access */
1340 VERIFY_VRAM_WRITE_OFF_RETURN(pThis, addr * 4 + 3);
1341
1342#ifdef IN_RING0
1343 if (((++pThis->cLatchAccesses) & pThis->uMaskLatchAccess) == pThis->uMaskLatchAccess)
1344 {
1345 static uint32_t const s_aMask[5] = { 0x3ff, 0x1ff, 0x7f, 0x3f, 0x1f};
1346 static uint64_t const s_aDelta[5] = {10000000, 5000000, 2500000, 1250000, 625000};
1347 if (PDMDevHlpCanEmulateIoBlock(pThis->CTX_SUFF(pDevIns)))
1348 {
1349 uint64_t u64CurTime = RTTimeSystemNanoTS();
1350
1351 /* About 1000 (or more) accesses per 10 ms will trigger a reschedule
1352 * to the recompiler
1353 */
1354 if (u64CurTime - pThis->u64LastLatchedAccess < s_aDelta[pThis->iMask])
1355 {
1356 pThis->u64LastLatchedAccess = 0;
1357 pThis->iMask = RT_MIN(pThis->iMask + 1U, RT_ELEMENTS(s_aMask) - 1U);
1358 pThis->uMaskLatchAccess = s_aMask[pThis->iMask];
1359 pThis->cLatchAccesses = pThis->uMaskLatchAccess - 1;
1360 return VINF_EM_RAW_EMULATE_IO_BLOCK;
1361 }
1362 if (pThis->u64LastLatchedAccess)
1363 {
1364 Log2(("Reset mask (was %d) delta %RX64 (limit %x)\n", pThis->iMask, u64CurTime - pThis->u64LastLatchedAccess, s_aDelta[pThis->iMask]));
1365 if (pThis->iMask)
1366 pThis->iMask--;
1367 pThis->uMaskLatchAccess = s_aMask[pThis->iMask];
1368 }
1369 pThis->u64LastLatchedAccess = u64CurTime;
1370 }
1371 else
1372 {
1373 pThis->u64LastLatchedAccess = 0;
1374 pThis->iMask = 0;
1375 pThis->uMaskLatchAccess = s_aMask[pThis->iMask];
1376 pThis->cLatchAccesses = 0;
1377 }
1378 }
1379#endif
1380
1381 write_mode = pThis->gr[5] & 3;
1382 switch(write_mode) {
1383 default:
1384 case 0:
1385 /* rotate */
1386 b = pThis->gr[3] & 7;
1387 val = ((val >> b) | (val << (8 - b))) & 0xff;
1388 val |= val << 8;
1389 val |= val << 16;
1390
1391 /* apply set/reset mask */
1392 set_mask = mask16[pThis->gr[1]];
1393 val = (val & ~set_mask) | (mask16[pThis->gr[0]] & set_mask);
1394 bit_mask = pThis->gr[8];
1395 break;
1396 case 1:
1397 val = pThis->latch;
1398 goto do_write;
1399 case 2:
1400 val = mask16[val & 0x0f];
1401 bit_mask = pThis->gr[8];
1402 break;
1403 case 3:
1404 /* rotate */
1405 b = pThis->gr[3] & 7;
1406 val = (val >> b) | (val << (8 - b));
1407
1408 bit_mask = pThis->gr[8] & val;
1409 val = mask16[pThis->gr[0]];
1410 break;
1411 }
1412
1413 /* apply logical operation */
1414 func_select = pThis->gr[3] >> 3;
1415 switch(func_select) {
1416 case 0:
1417 default:
1418 /* nothing to do */
1419 break;
1420 case 1:
1421 /* and */
1422 val &= pThis->latch;
1423 break;
1424 case 2:
1425 /* or */
1426 val |= pThis->latch;
1427 break;
1428 case 3:
1429 /* xor */
1430 val ^= pThis->latch;
1431 break;
1432 }
1433
1434 /* apply bit mask */
1435 bit_mask |= bit_mask << 8;
1436 bit_mask |= bit_mask << 16;
1437 val = (val & bit_mask) | (pThis->latch & ~bit_mask);
1438
1439 do_write:
1440 /* mask data according to sr[2] */
1441 mask = pThis->sr[2];
1442 pThis->plane_updated |= mask; /* only used to detect font change */
1443 write_mask = mask16[mask];
1444 ((uint32_t *)pThis->CTX_SUFF(vram_ptr))[addr] =
1445 (((uint32_t *)pThis->CTX_SUFF(vram_ptr))[addr] & ~write_mask) |
1446 (val & write_mask);
1447 Log3(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
1448 addr * 4, write_mask, val));
1449 vga_set_dirty(pThis, (addr << 2));
1450 }
1451
1452 return VINF_SUCCESS;
1453}
1454
1455#if defined(IN_RING3)
1456typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1457 const uint8_t *font_ptr, int h,
1458 uint32_t fgcol, uint32_t bgcol,
1459 int dscan);
1460typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1461 const uint8_t *font_ptr, int h,
1462 uint32_t fgcol, uint32_t bgcol, int dup9);
1463typedef void vga_draw_line_func(PVGASTATE pThis, uint8_t *pbDst, const uint8_t *pbSrc, int width);
1464
1465static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1466{
1467 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1468}
1469
1470static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1471{
1472 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1473}
1474
1475static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1476{
1477 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1478}
1479
1480static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1481{
1482 return (r << 16) | (g << 8) | b;
1483}
1484
1485#define DEPTH 8
1486#include "DevVGATmpl.h"
1487
1488#define DEPTH 15
1489#include "DevVGATmpl.h"
1490
1491#define DEPTH 16
1492#include "DevVGATmpl.h"
1493
1494#define DEPTH 32
1495#include "DevVGATmpl.h"
1496
1497static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1498{
1499 unsigned int col;
1500 col = rgb_to_pixel8(r, g, b);
1501 col |= col << 8;
1502 col |= col << 16;
1503 return col;
1504}
1505
1506static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1507{
1508 unsigned int col;
1509 col = rgb_to_pixel15(r, g, b);
1510 col |= col << 16;
1511 return col;
1512}
1513
1514static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1515{
1516 unsigned int col;
1517 col = rgb_to_pixel16(r, g, b);
1518 col |= col << 16;
1519 return col;
1520}
1521
1522static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1523{
1524 unsigned int col;
1525 col = rgb_to_pixel32(r, g, b);
1526 return col;
1527}
1528
1529/* return true if the palette was modified */
1530static bool update_palette16(PVGASTATE pThis)
1531{
1532 bool full_update = false;
1533 int i;
1534 uint32_t v, col, *palette;
1535
1536 palette = pThis->last_palette;
1537 for(i = 0; i < 16; i++) {
1538 v = pThis->ar[i];
1539 if (pThis->ar[0x10] & 0x80)
1540 v = ((pThis->ar[0x14] & 0xf) << 4) | (v & 0xf);
1541 else
1542 v = ((pThis->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1543 v = v * 3;
1544 col = pThis->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1545 c6_to_8(pThis->palette[v + 1]),
1546 c6_to_8(pThis->palette[v + 2]));
1547 if (col != palette[i]) {
1548 full_update = true;
1549 palette[i] = col;
1550 }
1551 }
1552 return full_update;
1553}
1554
1555/* return true if the palette was modified */
1556static bool update_palette256(PVGASTATE pThis)
1557{
1558 bool full_update = false;
1559 int i;
1560 uint32_t v, col, *palette;
1561 int wide_dac;
1562
1563 palette = pThis->last_palette;
1564 v = 0;
1565 wide_dac = (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
1566 == (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
1567 for(i = 0; i < 256; i++) {
1568 if (wide_dac)
1569 col = pThis->rgb_to_pixel(pThis->palette[v],
1570 pThis->palette[v + 1],
1571 pThis->palette[v + 2]);
1572 else
1573 col = pThis->rgb_to_pixel(c6_to_8(pThis->palette[v]),
1574 c6_to_8(pThis->palette[v + 1]),
1575 c6_to_8(pThis->palette[v + 2]));
1576 if (col != palette[i]) {
1577 full_update = true;
1578 palette[i] = col;
1579 }
1580 v += 3;
1581 }
1582 return full_update;
1583}
1584
1585static void vga_get_offsets(PVGASTATE pThis,
1586 uint32_t *pline_offset,
1587 uint32_t *pstart_addr,
1588 uint32_t *pline_compare)
1589{
1590 uint32_t start_addr, line_offset, line_compare;
1591#ifdef CONFIG_BOCHS_VBE
1592 if (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1593 line_offset = pThis->vbe_line_offset;
1594 start_addr = pThis->vbe_start_addr;
1595 line_compare = 65535;
1596 } else
1597#endif
1598 {
1599 /* compute line_offset in bytes */
1600 line_offset = pThis->cr[0x13];
1601 line_offset <<= 3;
1602 if (!(pThis->cr[0x14] & 0x40) && !(pThis->cr[0x17] & 0x40))
1603 {
1604 /* Word mode. Used for odd/even modes. */
1605 line_offset *= 2;
1606 }
1607
1608 /* starting address */
1609 start_addr = pThis->cr[0x0d] | (pThis->cr[0x0c] << 8);
1610
1611 /* line compare */
1612 line_compare = pThis->cr[0x18] |
1613 ((pThis->cr[0x07] & 0x10) << 4) |
1614 ((pThis->cr[0x09] & 0x40) << 3);
1615 }
1616 *pline_offset = line_offset;
1617 *pstart_addr = start_addr;
1618 *pline_compare = line_compare;
1619}
1620
1621/* update start_addr and line_offset. Return TRUE if modified */
1622static bool update_basic_params(PVGASTATE pThis)
1623{
1624 bool full_update = false;
1625 uint32_t start_addr, line_offset, line_compare;
1626
1627 pThis->get_offsets(pThis, &line_offset, &start_addr, &line_compare);
1628
1629 if (line_offset != pThis->line_offset ||
1630 start_addr != pThis->start_addr ||
1631 line_compare != pThis->line_compare) {
1632 pThis->line_offset = line_offset;
1633 pThis->start_addr = start_addr;
1634 pThis->line_compare = line_compare;
1635 full_update = true;
1636 }
1637 return full_update;
1638}
1639
1640static inline int get_depth_index(int depth)
1641{
1642 switch(depth) {
1643 default:
1644 case 8:
1645 return 0;
1646 case 15:
1647 return 1;
1648 case 16:
1649 return 2;
1650 case 32:
1651 return 3;
1652 }
1653}
1654
1655static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
1656 vga_draw_glyph8_8,
1657 vga_draw_glyph8_16,
1658 vga_draw_glyph8_16,
1659 vga_draw_glyph8_32,
1660};
1661
1662static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
1663 vga_draw_glyph16_8,
1664 vga_draw_glyph16_16,
1665 vga_draw_glyph16_16,
1666 vga_draw_glyph16_32,
1667};
1668
1669static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
1670 vga_draw_glyph9_8,
1671 vga_draw_glyph9_16,
1672 vga_draw_glyph9_16,
1673 vga_draw_glyph9_32,
1674};
1675
1676static const uint8_t cursor_glyph[32 * 4] = {
1677 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1678 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1679 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1680 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1681 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1682 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1683 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1684 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1685 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1686 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1687 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1688 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1689 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1690 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1691 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1692 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1693};
1694
1695/*
1696 * Text mode update
1697 * Missing:
1698 * - underline
1699 * - flashing
1700 */
1701static int vga_draw_text(PVGASTATE pThis, bool full_update, bool fFailOnResize, bool reset_dirty,
1702 PDMIDISPLAYCONNECTOR *pDrv, bool fRenderVRAM)
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 (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 (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 (fRenderVRAM)
1871 vga_draw_glyph8(d, linesize,
1872 cursor_glyph, h, fgcol, bgcol, dscan);
1873 } else {
1874 if (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, bool fRenderVRAM)
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 (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, bool fRenderVRAM)
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 && !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 (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 (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, bool fRenderVRAM)
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 (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, bool fRenderVRAM)
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, fRenderVRAM);
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, fRenderVRAM);
2567 } else {
2568 *pcur_graphic_mode = GMODE_TEXT;
2569 rc = vga_draw_text(pThis, 1, false, reset_dirty, pDrv, fRenderVRAM);
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, fRenderVRAM);
2600 break;
2601 case GMODE_GRAPH:
2602 rc = vga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty, pDrv, fRenderVRAM);
2603 break;
2604#ifdef VBOX_WITH_VMSVGA
2605 case GMODE_SVGA:
2606 rc = vmsvga_draw_graphic(pThis, full_update, fFailOnResize, reset_dirty, pDrv, fRenderVRAM);
2607 break;
2608#endif
2609 case GMODE_BLANK:
2610 default:
2611 vga_draw_blank(pThis, full_update, pDrv, fRenderVRAM);
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, pThis->fRenderVRAM);
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, pThis->fRenderVRAM);
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 /*
4775 * Make the screenshot.
4776 *
4777 * The second parameter is 'false' because the current display state is being rendered to an
4778 * external buffer using a fake connector. That is if display is blanked, we expect a black
4779 * screen in the external buffer.
4780 * If there is a pending resize, the function will fail.
4781 */
4782 rc = vga_update_display(pThis, false, true, false,
4783 &Connector, &cur_graphic_mode, 1);
4784
4785 if (rc == VINF_SUCCESS)
4786 {
4787 /*
4788 * Return the result.
4789 */
4790 *ppu8Data = pu8Data;
4791 *pcbData = cbRequired;
4792 *pcx = Connector.cx;
4793 *pcy = Connector.cy;
4794 }
4795 else
4796 {
4797 /* If we do not return a success, then the data buffer must be freed. */
4798 RTMemFree(pu8Data);
4799 if (RT_SUCCESS_NP(rc))
4800 {
4801 AssertMsgFailed(("%Rrc\n", rc));
4802 rc = VERR_INTERNAL_ERROR_5;
4803 }
4804 }
4805 }
4806 else
4807 rc = VERR_NO_MEMORY;
4808 }
4809 else
4810 rc = VERR_NOT_SUPPORTED;
4811
4812 PDMCritSectLeave(&pThis->CritSect);
4813
4814 LogFlow(("vgaPortTakeScreenshot: returns %Rrc (cbData=%d cx=%d cy=%d)\n", rc, *pcbData, *pcx, *pcy));
4815 return rc;
4816}
4817
4818/**
4819 * Free a screenshot buffer allocated in vgaPortTakeScreenshot.
4820 *
4821 * @param pInterface Pointer to this interface.
4822 * @param pu8Data Pointer returned by vgaPortTakeScreenshot.
4823 * @see PDMIDISPLAYPORT::pfnFreeScreenshot() for details.
4824 */
4825static DECLCALLBACK(void) vgaPortFreeScreenshot(PPDMIDISPLAYPORT pInterface, uint8_t *pu8Data)
4826{
4827 NOREF(pInterface);
4828
4829 LogFlow(("vgaPortFreeScreenshot: pu8Data=%p\n", pu8Data));
4830
4831 RTMemFree(pu8Data);
4832}
4833
4834/**
4835 * Copy bitmap to the display.
4836 *
4837 * @param pInterface Pointer to this interface.
4838 * @param pvData Pointer to the bitmap bits.
4839 * @param x The upper left corner x coordinate of the destination rectangle.
4840 * @param y The upper left corner y coordinate of the destination rectangle.
4841 * @param cx The width of the source and destination rectangles.
4842 * @param cy The height of the source and destination rectangles.
4843 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
4844 */
4845static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
4846{
4847 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4848 int rc = VINF_SUCCESS;
4849 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pThis));
4850 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
4851
4852 rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4853 AssertRC(rc);
4854
4855 /*
4856 * Validate input.
4857 */
4858 if ( pvData
4859 && x < pThis->pDrv->cx
4860 && cx <= pThis->pDrv->cx
4861 && cx + x <= pThis->pDrv->cx
4862 && y < pThis->pDrv->cy
4863 && cy <= pThis->pDrv->cy
4864 && cy + y <= pThis->pDrv->cy)
4865 {
4866 /*
4867 * Determine bytes per pixel in the destination buffer.
4868 */
4869 size_t cbPixelDst = 0;
4870 switch (pThis->pDrv->cBits)
4871 {
4872 case 8:
4873 cbPixelDst = 1;
4874 break;
4875 case 15:
4876 case 16:
4877 cbPixelDst = 2;
4878 break;
4879 case 24:
4880 cbPixelDst = 3;
4881 break;
4882 case 32:
4883 cbPixelDst = 4;
4884 break;
4885 default:
4886 rc = VERR_INVALID_PARAMETER;
4887 break;
4888 }
4889 if (RT_SUCCESS(rc))
4890 {
4891 /*
4892 * The blitting loop.
4893 */
4894 size_t cbLineSrc = cx * 4; /* 32 bits per pixel. */
4895 uint8_t *pu8Src = (uint8_t *)pvData;
4896 size_t cbLineDst = pThis->pDrv->cbScanline;
4897 uint8_t *pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4898 uint32_t cyLeft = cy;
4899 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pThis->pDrv->cBits)];
4900 Assert(pfnVgaDrawLine);
4901 while (cyLeft-- > 0)
4902 {
4903 pfnVgaDrawLine(pThis, pu8Dst, pu8Src, cx);
4904 pu8Dst += cbLineDst;
4905 pu8Src += cbLineSrc;
4906 }
4907
4908 /*
4909 * Invalidate the area.
4910 */
4911 pThis->pDrv->pfnUpdateRect(pThis->pDrv, x, y, cx, cy);
4912 }
4913 }
4914 else
4915 rc = VERR_INVALID_PARAMETER;
4916
4917 PDMCritSectLeave(&pThis->CritSect);
4918
4919 LogFlow(("vgaPortDisplayBlt: returns %Rrc\n", rc));
4920 return rc;
4921}
4922
4923static DECLCALLBACK(void) vgaPortUpdateDisplayRect(PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
4924{
4925 uint32_t v;
4926 vga_draw_line_func *vga_draw_line;
4927
4928 uint32_t cbPixelDst;
4929 uint32_t cbLineDst;
4930 uint8_t *pu8Dst;
4931
4932 uint32_t cbPixelSrc;
4933 uint32_t cbLineSrc;
4934 uint8_t *pu8Src;
4935
4936 uint32_t u32OffsetSrc, u32Dummy;
4937
4938 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
4939
4940#ifdef DEBUG_sunlover
4941 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
4942#endif /* DEBUG_sunlover */
4943
4944 Assert(pInterface);
4945 Assert(pThis->pDrv);
4946 Assert(pThis->pDrv->pu8Data);
4947
4948 /* Check if there is something to do at all. */
4949 if (!pThis->fRenderVRAM)
4950 {
4951 /* The framebuffer uses the guest VRAM directly. */
4952#ifdef DEBUG_sunlover
4953 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
4954#endif /* DEBUG_sunlover */
4955 return;
4956 }
4957
4958 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
4959 AssertRC(rc);
4960
4961 /* Correct negative x and y coordinates. */
4962 if (x < 0)
4963 {
4964 x += w; /* Compute xRight which is also the new width. */
4965 w = (x < 0) ? 0 : x;
4966 x = 0;
4967 }
4968
4969 if (y < 0)
4970 {
4971 y += h; /* Compute yBottom, which is also the new height. */
4972 h = (y < 0) ? 0 : y;
4973 y = 0;
4974 }
4975
4976 /* Also check if coords are greater than the display resolution. */
4977 if (x + w > pThis->pDrv->cx)
4978 {
4979 // x < 0 is not possible here
4980 w = pThis->pDrv->cx > (uint32_t)x? pThis->pDrv->cx - x: 0;
4981 }
4982
4983 if (y + h > pThis->pDrv->cy)
4984 {
4985 // y < 0 is not possible here
4986 h = pThis->pDrv->cy > (uint32_t)y? pThis->pDrv->cy - y: 0;
4987 }
4988
4989#ifdef DEBUG_sunlover
4990 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
4991#endif /* DEBUG_sunlover */
4992
4993 /* Check if there is something to do at all. */
4994 if (w == 0 || h == 0)
4995 {
4996 /* Empty rectangle. */
4997#ifdef DEBUG_sunlover
4998 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
4999#endif /* DEBUG_sunlover */
5000 PDMCritSectLeave(&pThis->CritSect);
5001 return;
5002 }
5003
5004 /** @todo This method should be made universal and not only for VBVA.
5005 * VGA_DRAW_LINE* must be selected and src/dst address calculation
5006 * changed.
5007 */
5008
5009 /* Choose the rendering function. */
5010 switch(pThis->get_bpp(pThis))
5011 {
5012 default:
5013 case 0:
5014 /* A LFB mode is already disabled, but the callback is still called
5015 * by Display because VBVA buffer is being flushed.
5016 * Nothing to do, just return.
5017 */
5018 PDMCritSectLeave(&pThis->CritSect);
5019 return;
5020 case 8:
5021 v = VGA_DRAW_LINE8;
5022 break;
5023 case 15:
5024 v = VGA_DRAW_LINE15;
5025 break;
5026 case 16:
5027 v = VGA_DRAW_LINE16;
5028 break;
5029 case 24:
5030 v = VGA_DRAW_LINE24;
5031 break;
5032 case 32:
5033 v = VGA_DRAW_LINE32;
5034 break;
5035 }
5036
5037 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(pThis->pDrv->cBits)];
5038
5039 /* Compute source and destination addresses and pitches. */
5040 cbPixelDst = (pThis->pDrv->cBits + 7) / 8;
5041 cbLineDst = pThis->pDrv->cbScanline;
5042 pu8Dst = pThis->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
5043
5044 cbPixelSrc = (pThis->get_bpp(pThis) + 7) / 8;
5045 pThis->get_offsets(pThis, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
5046
5047 /* Assume that rendering is performed only on visible part of VRAM.
5048 * This is true because coordinates were verified.
5049 */
5050 pu8Src = pThis->vram_ptrR3;
5051 pu8Src += u32OffsetSrc * 4 + y * cbLineSrc + x * cbPixelSrc;
5052
5053 /* Render VRAM to framebuffer. */
5054
5055#ifdef DEBUG_sunlover
5056 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
5057#endif /* DEBUG_sunlover */
5058
5059 while (h-- > 0)
5060 {
5061 vga_draw_line (pThis, pu8Dst, pu8Src, w);
5062 pu8Dst += cbLineDst;
5063 pu8Src += cbLineSrc;
5064 }
5065
5066 PDMCritSectLeave(&pThis->CritSect);
5067#ifdef DEBUG_sunlover
5068 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
5069#endif /* DEBUG_sunlover */
5070}
5071
5072static DECLCALLBACK(int) vgaPortCopyRect (PPDMIDISPLAYPORT pInterface,
5073 uint32_t w,
5074 uint32_t h,
5075 const uint8_t *pu8Src,
5076 int32_t xSrc,
5077 int32_t ySrc,
5078 uint32_t u32SrcWidth,
5079 uint32_t u32SrcHeight,
5080 uint32_t u32SrcLineSize,
5081 uint32_t u32SrcBitsPerPixel,
5082 uint8_t *pu8Dst,
5083 int32_t xDst,
5084 int32_t yDst,
5085 uint32_t u32DstWidth,
5086 uint32_t u32DstHeight,
5087 uint32_t u32DstLineSize,
5088 uint32_t u32DstBitsPerPixel)
5089{
5090 uint32_t v;
5091 vga_draw_line_func *vga_draw_line;
5092
5093 uint32_t cbPixelDst;
5094 uint32_t cbLineDst;
5095 uint8_t *pu8DstPtr;
5096
5097 uint32_t cbPixelSrc;
5098 uint32_t cbLineSrc;
5099 const uint8_t *pu8SrcPtr;
5100
5101#ifdef DEBUG_sunlover
5102 LogFlow(("vgaPortCopyRect: %d,%d %dx%d -> %d,%d\n", xSrc, ySrc, w, h, xDst, yDst));
5103#endif /* DEBUG_sunlover */
5104
5105 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5106
5107 Assert(pInterface);
5108 Assert(pThis->pDrv);
5109
5110 int32_t xSrcCorrected = xSrc;
5111 int32_t ySrcCorrected = ySrc;
5112 uint32_t wCorrected = w;
5113 uint32_t hCorrected = h;
5114
5115 /* Correct source coordinates to be within the source bitmap. */
5116 if (xSrcCorrected < 0)
5117 {
5118 xSrcCorrected += wCorrected; /* Compute xRight which is also the new width. */
5119 wCorrected = (xSrcCorrected < 0) ? 0 : xSrcCorrected;
5120 xSrcCorrected = 0;
5121 }
5122
5123 if (ySrcCorrected < 0)
5124 {
5125 ySrcCorrected += hCorrected; /* Compute yBottom, which is also the new height. */
5126 hCorrected = (ySrcCorrected < 0) ? 0 : ySrcCorrected;
5127 ySrcCorrected = 0;
5128 }
5129
5130 /* Also check if coords are greater than the display resolution. */
5131 if (xSrcCorrected + wCorrected > u32SrcWidth)
5132 {
5133 /* xSrcCorrected < 0 is not possible here */
5134 wCorrected = u32SrcWidth > (uint32_t)xSrcCorrected? u32SrcWidth - xSrcCorrected: 0;
5135 }
5136
5137 if (ySrcCorrected + hCorrected > u32SrcHeight)
5138 {
5139 /* y < 0 is not possible here */
5140 hCorrected = u32SrcHeight > (uint32_t)ySrcCorrected? u32SrcHeight - ySrcCorrected: 0;
5141 }
5142
5143#ifdef DEBUG_sunlover
5144 LogFlow(("vgaPortCopyRect: %d,%d %dx%d (corrected coords)\n", xSrcCorrected, ySrcCorrected, wCorrected, hCorrected));
5145#endif /* DEBUG_sunlover */
5146
5147 /* Check if there is something to do at all. */
5148 if (wCorrected == 0 || hCorrected == 0)
5149 {
5150 /* Empty rectangle. */
5151#ifdef DEBUG_sunlover
5152 LogFlow(("vgaPortUpdateDisplayRectEx: nothing to do: %dx%d\n", wCorrected, hCorrected));
5153#endif /* DEBUG_sunlover */
5154 return VINF_SUCCESS;
5155 }
5156
5157 /* Check that the corrected source rectangle is within the destination.
5158 * Note: source rectangle is adjusted, but the target must be large enough.
5159 */
5160 if ( xDst < 0
5161 || yDst < 0
5162 || xDst + wCorrected > u32DstWidth
5163 || yDst + hCorrected > u32DstHeight)
5164 {
5165 return VERR_INVALID_PARAMETER;
5166 }
5167
5168 /* Choose the rendering function. */
5169 switch(u32SrcBitsPerPixel)
5170 {
5171 default:
5172 case 0:
5173 /* Nothing to do, just return. */
5174 return VINF_SUCCESS;
5175 case 8:
5176 v = VGA_DRAW_LINE8;
5177 break;
5178 case 15:
5179 v = VGA_DRAW_LINE15;
5180 break;
5181 case 16:
5182 v = VGA_DRAW_LINE16;
5183 break;
5184 case 24:
5185 v = VGA_DRAW_LINE24;
5186 break;
5187 case 32:
5188 v = VGA_DRAW_LINE32;
5189 break;
5190 }
5191
5192 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5193 AssertRC(rc);
5194
5195 /* This method only works if the VGA device is in a VBE mode. */
5196 if ((pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0)
5197 {
5198 PDMCritSectLeave(&pThis->CritSect);
5199 return VERR_INVALID_STATE;
5200 }
5201
5202 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(u32DstBitsPerPixel)];
5203
5204 /* Compute source and destination addresses and pitches. */
5205 cbPixelDst = (u32DstBitsPerPixel + 7) / 8;
5206 cbLineDst = u32DstLineSize;
5207 pu8DstPtr = pu8Dst + yDst * cbLineDst + xDst * cbPixelDst;
5208
5209 cbPixelSrc = (u32SrcBitsPerPixel + 7) / 8;
5210 cbLineSrc = u32SrcLineSize;
5211 pu8SrcPtr = pu8Src + ySrcCorrected * cbLineSrc + xSrcCorrected * cbPixelSrc;
5212
5213#ifdef DEBUG_sunlover
5214 LogFlow(("vgaPortCopyRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8DstPtr, cbLineDst, cbPixelDst, pu8SrcPtr, cbLineSrc, cbPixelSrc));
5215#endif /* DEBUG_sunlover */
5216
5217 while (hCorrected-- > 0)
5218 {
5219 vga_draw_line (pThis, pu8DstPtr, pu8SrcPtr, wCorrected);
5220 pu8DstPtr += cbLineDst;
5221 pu8SrcPtr += cbLineSrc;
5222 }
5223
5224 PDMCritSectLeave(&pThis->CritSect);
5225#ifdef DEBUG_sunlover
5226 LogFlow(("vgaPortCopyRect: completed.\n"));
5227#endif /* DEBUG_sunlover */
5228
5229 return VINF_SUCCESS;
5230}
5231
5232static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
5233{
5234 PVGASTATE pThis = IDISPLAYPORT_2_VGASTATE(pInterface);
5235
5236 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
5237
5238 int rc = PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
5239 AssertRC(rc);
5240
5241 pThis->fRenderVRAM = fRender;
5242
5243 PDMCritSectLeave(&pThis->CritSect);
5244}
5245
5246
5247static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
5248{
5249 PVGASTATE pThis = (PVGASTATE)pvUser;
5250 NOREF(pDevIns);
5251
5252 if (pThis->fScanLineCfg & VBVASCANLINECFG_ENABLE_VSYNC_IRQ)
5253 {
5254 VBVARaiseIrq(pThis, HGSMIHOSTFLAGS_VSYNC);
5255 }
5256
5257 if (pThis->pDrv)
5258 pThis->pDrv->pfnRefresh(pThis->pDrv);
5259
5260 if (pThis->cMilliesRefreshInterval)
5261 TMTimerSetMillies(pTimer, pThis->cMilliesRefreshInterval);
5262
5263#ifdef VBOX_WITH_VIDEOHWACCEL
5264 vbvaTimerCb(pThis);
5265#endif
5266
5267#ifdef VBOX_WITH_CRHGSMI
5268 vboxCmdVBVACmdTimer(pThis);
5269#endif
5270}
5271
5272#ifdef VBOX_WITH_VMSVGA
5273int vgaR3RegisterVRAMHandler(PVGASTATE pVGAState, uint64_t cbFrameBuffer)
5274{
5275 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5276
5277 Assert(pVGAState->GCPhysVRAM);
5278
5279 int rc = PGMR3HandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5280 PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
5281 pVGAState->GCPhysVRAM, pVGAState->GCPhysVRAM + (cbFrameBuffer - 1),
5282 vgaR3LFBAccessHandler, pVGAState,
5283 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pDevIns->pvInstanceDataR0,
5284 g_DeviceVga.szRCMod, "vgaGCLFBAccessHandler", pDevIns->pvInstanceDataRC,
5285 "VGA LFB");
5286 AssertRC(rc);
5287 return rc;
5288}
5289
5290int vgaR3UnregisterVRAMHandler(PVGASTATE pVGAState)
5291{
5292 PPDMDEVINS pDevIns = pVGAState->pDevInsR3;
5293
5294 Assert(pVGAState->GCPhysVRAM);
5295 int rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pVGAState->GCPhysVRAM);
5296 AssertRC(rc);
5297 return rc;
5298}
5299#endif
5300
5301/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
5302
5303/**
5304 * Callback function for unmapping and/or mapping the VRAM MMIO2 region (called by the PCI bus).
5305 *
5306 * @return VBox status code.
5307 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
5308 * @param iRegion The region number.
5309 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
5310 * I/O port, else it's a physical address.
5311 * This address is *NOT* relative to pci_mem_base like earlier!
5312 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
5313 */
5314static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
5315{
5316 int rc;
5317 PPDMDEVINS pDevIns = pPciDev->pDevIns;
5318 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5319 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%RGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
5320#ifdef VBOX_WITH_VMSVGA
5321 AssertReturn((iRegion == ((pThis->fVMSVGAEnabled) ? 1 : 0)) && (enmType == ((pThis->fVMSVGAEnabled) ? PCI_ADDRESS_SPACE_MEM : PCI_ADDRESS_SPACE_MEM_PREFETCH)), VERR_INTERNAL_ERROR);
5322#else
5323 AssertReturn(iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH, VERR_INTERNAL_ERROR);
5324#endif
5325
5326 if (GCPhysAddress != NIL_RTGCPHYS)
5327 {
5328 /*
5329 * Mapping the VRAM.
5330 */
5331 rc = PDMDevHlpMMIO2Map(pDevIns, iRegion, GCPhysAddress);
5332 AssertRC(rc);
5333 if (RT_SUCCESS(rc))
5334 {
5335 rc = PGMR3HandlerPhysicalRegister(PDMDevHlpGetVM(pDevIns),
5336 PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
5337 GCPhysAddress, GCPhysAddress + (pThis->vram_size - 1),
5338 vgaR3LFBAccessHandler, pThis,
5339 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pDevIns->pvInstanceDataR0,
5340 g_DeviceVga.szRCMod, "vgaRCLFBAccessHandler", pDevIns->pvInstanceDataRC,
5341 "VGA LFB");
5342 AssertRC(rc);
5343 if (RT_SUCCESS(rc))
5344 {
5345 pThis->GCPhysVRAM = GCPhysAddress;
5346 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = GCPhysAddress >> 16;
5347 }
5348 }
5349 }
5350 else
5351 {
5352 /*
5353 * Unmapping of the VRAM in progress.
5354 * Deregister the access handler so PGM doesn't get upset.
5355 */
5356 Assert(pThis->GCPhysVRAM);
5357#ifdef VBOX_WITH_VMSVGA
5358 Assert(!pThis->svga.fEnabled);
5359#endif
5360 rc = PGMHandlerPhysicalDeregister(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5361 AssertRC(rc);
5362 pThis->GCPhysVRAM = 0;
5363 /* NB: VBE_DISPI_INDEX_FB_BASE_HI is left unchanged here. */
5364 }
5365 return rc;
5366}
5367
5368
5369/* -=-=-=-=-=- Ring3: Misc Wrappers & Sidekicks -=-=-=-=-=- */
5370
5371/**
5372 * Saves a important bits of the VGA device config.
5373 *
5374 * @param pThis The VGA instance data.
5375 * @param pSSM The saved state handle.
5376 */
5377static void vgaR3SaveConfig(PVGASTATE pThis, PSSMHANDLE pSSM)
5378{
5379 SSMR3PutU32(pSSM, pThis->vram_size);
5380 SSMR3PutU32(pSSM, pThis->cMonitors);
5381}
5382
5383
5384/**
5385 * @copydoc FNSSMDEVLIVEEXEC
5386 */
5387static DECLCALLBACK(int) vgaR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
5388{
5389 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5390 Assert(uPass == 0); NOREF(uPass);
5391 vgaR3SaveConfig(pThis, pSSM);
5392 return VINF_SSM_DONT_CALL_AGAIN;
5393}
5394
5395
5396/**
5397 * @copydoc FNSSMDEVSAVEPREP
5398 */
5399static DECLCALLBACK(int) vgaR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5400{
5401#ifdef VBOX_WITH_VIDEOHWACCEL
5402 return vboxVBVASaveStatePrep(pDevIns, pSSM);
5403#else
5404 return VINF_SUCCESS;
5405#endif
5406}
5407
5408static DECLCALLBACK(int) vgaR3SaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5409{
5410#ifdef VBOX_WITH_VIDEOHWACCEL
5411 return vboxVBVASaveStateDone(pDevIns, pSSM);
5412#else
5413 return VINF_SUCCESS;
5414#endif
5415}
5416
5417/**
5418 * @copydoc FNSSMDEVSAVEEXEC
5419 */
5420static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5421{
5422 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5423
5424#ifdef VBOX_WITH_VDMA
5425 vboxVDMASaveStateExecPrep(pThis->pVdma, pSSM);
5426#endif
5427
5428 vgaR3SaveConfig(pThis, pSSM);
5429 vga_save(pSSM, PDMINS_2_DATA(pDevIns, PVGASTATE));
5430
5431#ifdef VBOX_WITH_HGSMI
5432 SSMR3PutBool(pSSM, true);
5433 int rc = vboxVBVASaveStateExec(pDevIns, pSSM);
5434#else
5435 int rc = SSMR3PutBool(pSSM, false);
5436#endif
5437
5438 AssertRCReturn(rc, rc);
5439
5440#ifdef VBOX_WITH_VDMA
5441 rc = SSMR3PutU32(pSSM, 1);
5442 AssertRCReturn(rc, rc);
5443 rc = vboxVDMASaveStateExecPerform(pThis->pVdma, pSSM);
5444#else
5445 rc = SSMR3PutU32(pSSM, 0);
5446#endif
5447 AssertRCReturn(rc, rc);
5448
5449#ifdef VBOX_WITH_VDMA
5450 vboxVDMASaveStateExecDone(pThis->pVdma, pSSM);
5451#endif
5452
5453#ifdef VBOX_WITH_VMSVGA
5454 if ( rc == VINF_SUCCESS
5455 && pThis->fVMSVGAEnabled)
5456 rc = vmsvgaSaveExec(pDevIns, pSSM);
5457#endif
5458
5459 return rc;
5460}
5461
5462
5463/**
5464 * @copydoc FNSSMDEVSAVEEXEC
5465 */
5466static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
5467{
5468 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5469 int rc;
5470
5471 if (uVersion < VGA_SAVEDSTATE_VERSION_ANCIENT || uVersion > VGA_SAVEDSTATE_VERSION)
5472 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
5473
5474 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5475 {
5476 /* Check the config */
5477 uint32_t cbVRam;
5478 rc = SSMR3GetU32(pSSM, &cbVRam);
5479 AssertRCReturn(rc, rc);
5480 if (pThis->vram_size != cbVRam)
5481 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("VRAM size changed: config=%#x state=%#x"), pThis->vram_size, cbVRam);
5482
5483 uint32_t cMonitors;
5484 rc = SSMR3GetU32(pSSM, &cMonitors);
5485 AssertRCReturn(rc, rc);
5486 if (pThis->cMonitors != cMonitors)
5487 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Monitor count changed: config=%u state=%u"), pThis->cMonitors, cMonitors);
5488 }
5489
5490 if (uPass == SSM_PASS_FINAL)
5491 {
5492 rc = vga_load(pSSM, pThis, uVersion);
5493 if (RT_FAILURE(rc))
5494 return rc;
5495 bool fWithHgsmi = uVersion == VGA_SAVEDSTATE_VERSION_HGSMI;
5496 if (uVersion > VGA_SAVEDSTATE_VERSION_HGSMI)
5497 {
5498 rc = SSMR3GetBool(pSSM, &fWithHgsmi);
5499 AssertRCReturn(rc, rc);
5500 }
5501 if (fWithHgsmi)
5502 {
5503#ifdef VBOX_WITH_HGSMI
5504 rc = vboxVBVALoadStateExec(pDevIns, pSSM, uVersion);
5505 AssertRCReturn(rc, rc);
5506#else
5507 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("HGSMI is not compiled in, but it is present in the saved state"));
5508#endif
5509 }
5510
5511 if (uVersion >= VGA_SAVEDSTATE_VERSION_3D)
5512 {
5513 uint32_t u32;
5514 rc = SSMR3GetU32(pSSM, &u32);
5515 if (u32)
5516 {
5517#ifdef VBOX_WITH_VDMA
5518 if (u32 == 1)
5519 {
5520 rc = vboxVDMASaveLoadExecPerform(pThis->pVdma, pSSM, uVersion);
5521 AssertRCReturn(rc, rc);
5522 }
5523 else
5524#endif
5525 {
5526 LogRel(("invalid CmdVbva version info\n"));
5527 return VERR_VERSION_MISMATCH;
5528 }
5529 }
5530 }
5531
5532#ifdef VBOX_WITH_VMSVGA
5533 if ( uVersion >= VGA_SAVEDSTATE_VERSION_VMSVGA_2D
5534 && pThis->fVMSVGAEnabled)
5535 {
5536 rc = vmsvgaLoadExec(pDevIns, pSSM, uVersion, uPass);
5537 AssertRCReturn(rc, rc);
5538 }
5539#endif
5540 }
5541 return VINF_SUCCESS;
5542}
5543
5544
5545/**
5546 * @copydoc FNSSMDEVLOADDONE
5547 */
5548static DECLCALLBACK(int) vgaR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
5549{
5550#ifdef VBOX_WITH_HGSMI
5551 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5552 VBVAPause(pThis, (pThis->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) == 0);
5553 int rc = vboxVBVALoadStateDone(pDevIns, pSSM);
5554 AssertRCReturn(rc, rc);
5555# ifdef VBOX_WITH_VDMA
5556 rc = vboxVDMASaveLoadDone(pThis->pVdma);
5557 AssertRCReturn(rc, rc);
5558# endif
5559 return VINF_SUCCESS;
5560#else
5561 return VINF_SUCCESS;
5562#endif
5563}
5564
5565
5566/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
5567
5568/**
5569 * @interface_method_impl{PDMDEVREG,pfnReset}
5570 */
5571static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
5572{
5573 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5574 char *pchStart;
5575 char *pchEnd;
5576 LogFlow(("vgaReset\n"));
5577
5578 if (pThis->pVdma)
5579 vboxVDMAReset(pThis->pVdma);
5580
5581#ifdef VBOX_WITH_HGSMI
5582 VBVAReset(pThis);
5583#endif /* VBOX_WITH_HGSMI */
5584
5585
5586 /* Clear the VRAM ourselves. */
5587 if (pThis->vram_ptrR3 && pThis->vram_size)
5588 memset(pThis->vram_ptrR3, 0, pThis->vram_size);
5589
5590 /*
5591 * Zero most of it.
5592 *
5593 * Unlike vga_reset we're leaving out a few members which we believe
5594 * must remain unchanged....
5595 */
5596 /* 1st part. */
5597 pchStart = (char *)&pThis->latch;
5598 pchEnd = (char *)&pThis->invalidated_y_table;
5599 memset(pchStart, 0, pchEnd - pchStart);
5600
5601 /* 2nd part. */
5602 pchStart = (char *)&pThis->last_palette;
5603 pchEnd = (char *)&pThis->u32Marker;
5604 memset(pchStart, 0, pchEnd - pchStart);
5605
5606
5607 /*
5608 * Restore and re-init some bits.
5609 */
5610 pThis->get_bpp = vga_get_bpp;
5611 pThis->get_offsets = vga_get_offsets;
5612 pThis->get_resolution = vga_get_resolution;
5613 pThis->graphic_mode = -1; /* Force full update. */
5614#ifdef CONFIG_BOCHS_VBE
5615 pThis->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
5616 pThis->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
5617 pThis->vbe_regs[VBE_DISPI_INDEX_FB_BASE_HI] = pThis->GCPhysVRAM >> 16;
5618 pThis->vbe_bank_max = (pThis->vram_size >> 16) - 1;
5619#endif /* CONFIG_BOCHS_VBE */
5620
5621 /*
5622 * Reset the LBF mapping.
5623 */
5624 pThis->fLFBUpdated = false;
5625 if ( ( pThis->fGCEnabled
5626 || pThis->fR0Enabled)
5627 && pThis->GCPhysVRAM
5628 && pThis->GCPhysVRAM != NIL_RTGCPHYS)
5629 {
5630 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pThis->GCPhysVRAM);
5631 AssertRC(rc);
5632 }
5633 if (pThis->fRemappedVGA)
5634 {
5635 IOMMMIOResetRegion(PDMDevHlpGetVM(pDevIns), 0x000a0000);
5636 pThis->fRemappedVGA = false;
5637 }
5638
5639 /*
5640 * Reset the logo data.
5641 */
5642 pThis->LogoCommand = LOGO_CMD_NOP;
5643 pThis->offLogoData = 0;
5644
5645 /* notify port handler */
5646 if (pThis->pDrv)
5647 {
5648 PDMCritSectLeave(&pThis->CritSect); /* hack around lock order issue. */
5649 pThis->pDrv->pfnReset(pThis->pDrv);
5650 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
5651 }
5652
5653 /* Reset latched access mask. */
5654 pThis->uMaskLatchAccess = 0x3ff;
5655 pThis->cLatchAccesses = 0;
5656 pThis->u64LastLatchedAccess = 0;
5657 pThis->iMask = 0;
5658
5659 /* Reset retrace emulation. */
5660 memset(&pThis->retrace_state, 0, sizeof(pThis->retrace_state));
5661}
5662
5663
5664/**
5665 * @interface_method_impl{PDMDEVREG,pfnRelocate}
5666 */
5667static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
5668{
5669 if (offDelta)
5670 {
5671 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5672 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
5673
5674 pThis->vram_ptrRC += offDelta;
5675 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5676 }
5677}
5678
5679
5680/**
5681 * @interface_method_impl{PDMDEVREG,pfnAttach}
5682 *
5683 * This is like plugging in the monitor after turning on the PC.
5684 */
5685static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5686{
5687 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5688
5689 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5690 ("VGA device does not support hotplugging\n"),
5691 VERR_INVALID_PARAMETER);
5692
5693 switch (iLUN)
5694 {
5695 /* LUN #0: Display port. */
5696 case 0:
5697 {
5698 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->IBase, &pThis->pDrvBase, "Display Port");
5699 if (RT_SUCCESS(rc))
5700 {
5701 pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIDISPLAYCONNECTOR);
5702 if (pThis->pDrv)
5703 {
5704 /* pThis->pDrv->pu8Data can be NULL when there is no framebuffer. */
5705 if ( pThis->pDrv->pfnRefresh
5706 && pThis->pDrv->pfnResize
5707 && pThis->pDrv->pfnUpdateRect)
5708 rc = VINF_SUCCESS;
5709 else
5710 {
5711 Assert(pThis->pDrv->pfnRefresh);
5712 Assert(pThis->pDrv->pfnResize);
5713 Assert(pThis->pDrv->pfnUpdateRect);
5714 pThis->pDrv = NULL;
5715 pThis->pDrvBase = NULL;
5716 rc = VERR_INTERNAL_ERROR;
5717 }
5718#ifdef VBOX_WITH_VIDEOHWACCEL
5719 if(rc == VINF_SUCCESS)
5720 {
5721 rc = vbvaVHWAConstruct(pThis);
5722 if (rc != VERR_NOT_IMPLEMENTED)
5723 AssertRC(rc);
5724 }
5725#endif
5726 }
5727 else
5728 {
5729 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Rrc\n", rc));
5730 pThis->pDrvBase = NULL;
5731 rc = VERR_PDM_MISSING_INTERFACE;
5732 }
5733 }
5734 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
5735 {
5736 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
5737 rc = VINF_SUCCESS;
5738 }
5739 else
5740 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
5741 return rc;
5742 }
5743
5744 default:
5745 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5746 return VERR_PDM_NO_SUCH_LUN;
5747 }
5748}
5749
5750
5751/**
5752 * @interface_method_impl{PDMDEVREG,pfnDetach}
5753 *
5754 * This is like unplugging the monitor while the PC is still running.
5755 */
5756static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
5757{
5758 /*
5759 * Reset the interfaces and update the controller state.
5760 */
5761 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5762
5763 AssertMsg(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
5764 ("VGA device does not support hotplugging\n"));
5765
5766 switch (iLUN)
5767 {
5768 /* LUN #0: Display port. */
5769 case 0:
5770 pThis->pDrv = NULL;
5771 pThis->pDrvBase = NULL;
5772 break;
5773
5774 default:
5775 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
5776 break;
5777 }
5778}
5779
5780
5781/**
5782 * @interface_method_impl{PDMDEVREG,pfnDestruct}
5783 */
5784static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5785{
5786 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
5787
5788#ifdef VBE_NEW_DYN_LIST
5789 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5790 LogFlow(("vgaR3Destruct:\n"));
5791
5792# ifdef VBOX_WITH_VDMA
5793 vboxVDMADestruct(pThis->pVdma);
5794# endif
5795
5796#ifdef VBOX_WITH_VMSVGA
5797 if (pThis->fVMSVGAEnabled)
5798 vmsvgaDestruct(pDevIns);
5799#endif
5800
5801 /*
5802 * Free MM heap pointers.
5803 */
5804 if (pThis->pu8VBEExtraData)
5805 {
5806 MMR3HeapFree(pThis->pu8VBEExtraData);
5807 pThis->pu8VBEExtraData = NULL;
5808 }
5809#endif /* VBE_NEW_DYN_LIST */
5810 if (pThis->pu8VgaBios)
5811 {
5812 MMR3HeapFree(pThis->pu8VgaBios);
5813 pThis->pu8VgaBios = NULL;
5814 }
5815
5816 if (pThis->pszVgaBiosFile)
5817 {
5818 MMR3HeapFree(pThis->pszVgaBiosFile);
5819 pThis->pszVgaBiosFile = NULL;
5820 }
5821
5822 if (pThis->pszLogoFile)
5823 {
5824 MMR3HeapFree(pThis->pszLogoFile);
5825 pThis->pszLogoFile = NULL;
5826 }
5827
5828 PDMR3CritSectDelete(&pThis->CritSect);
5829 return VINF_SUCCESS;
5830}
5831
5832
5833/**
5834 * Adjust VBE mode information
5835 *
5836 * Depending on the configured VRAM size, certain parts of VBE mode
5837 * information must be updated.
5838 *
5839 * @param pThis The device instance data.
5840 * @param pMode The mode information structure.
5841 */
5842static void vgaAdjustModeInfo(PVGASTATE pThis, ModeInfoListItem *pMode)
5843{
5844 int maxPage;
5845 int bpl;
5846
5847
5848 /* For 4bpp modes, the planes are "stacked" on top of each other. */
5849 bpl = pMode->info.BytesPerScanLine * pMode->info.NumberOfPlanes;
5850 /* The "number of image pages" is really the max page index... */
5851 maxPage = pThis->vram_size / (pMode->info.YResolution * bpl) - 1;
5852 Assert(maxPage >= 0);
5853 if (maxPage > 255)
5854 maxPage = 255; /* 8-bit value. */
5855 pMode->info.NumberOfImagePages = maxPage;
5856 pMode->info.LinNumberOfPages = maxPage;
5857}
5858
5859
5860/**
5861 * @interface_method_impl{PDMDEVREG,pfnConstruct}
5862 */
5863static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
5864{
5865
5866 static bool s_fExpandDone = false;
5867 int rc;
5868 unsigned i;
5869#ifdef VBE_NEW_DYN_LIST
5870 uint32_t cCustomModes;
5871 uint32_t cyReduction;
5872 uint32_t cbPitch;
5873 PVBEHEADER pVBEDataHdr;
5874 ModeInfoListItem *pCurMode;
5875 unsigned cb;
5876#endif
5877 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
5878 PVGASTATE pThis = PDMINS_2_DATA(pDevIns, PVGASTATE);
5879 PVM pVM = PDMDevHlpGetVM(pDevIns);
5880
5881 Assert(iInstance == 0);
5882 Assert(pVM);
5883
5884 /*
5885 * Init static data.
5886 */
5887 if (!s_fExpandDone)
5888 {
5889 s_fExpandDone = true;
5890 vga_init_expand();
5891 }
5892
5893 /*
5894 * Validate configuration.
5895 */
5896 if (!CFGMR3AreValuesValid(pCfg, "VRamSize\0"
5897 "MonitorCount\0"
5898 "GCEnabled\0"
5899 "R0Enabled\0"
5900 "FadeIn\0"
5901 "FadeOut\0"
5902 "LogoTime\0"
5903 "LogoFile\0"
5904 "ShowBootMenu\0"
5905 "BiosRom\0"
5906 "RealRetrace\0"
5907 "CustomVideoModes\0"
5908 "HeightReduction\0"
5909 "CustomVideoMode1\0"
5910 "CustomVideoMode2\0"
5911 "CustomVideoMode3\0"
5912 "CustomVideoMode4\0"
5913 "CustomVideoMode5\0"
5914 "CustomVideoMode6\0"
5915 "CustomVideoMode7\0"
5916 "CustomVideoMode8\0"
5917 "CustomVideoMode9\0"
5918 "CustomVideoMode10\0"
5919 "CustomVideoMode11\0"
5920 "CustomVideoMode12\0"
5921 "CustomVideoMode13\0"
5922 "CustomVideoMode14\0"
5923 "CustomVideoMode15\0"
5924 "CustomVideoMode16\0"
5925 "MaxBiosXRes\0"
5926 "MaxBiosYRes\0"
5927#ifdef VBOX_WITH_VMSVGA
5928 "VMSVGAEnabled\0"
5929#endif
5930#ifdef VBOX_WITH_VMSVGA3D
5931 "VMSVGA3dEnabled\0"
5932 "HostWindowId\0"
5933#endif
5934 ))
5935 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
5936 N_("Invalid configuration for vga device"));
5937
5938 /*
5939 * Init state data.
5940 */
5941 rc = CFGMR3QueryU32Def(pCfg, "VRamSize", &pThis->vram_size, VGA_VRAM_DEFAULT);
5942 AssertLogRelRCReturn(rc, rc);
5943 if (pThis->vram_size > VGA_VRAM_MAX)
5944 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5945 "VRamSize is too large, %#x, max %#x", pThis->vram_size, VGA_VRAM_MAX);
5946 if (pThis->vram_size < VGA_VRAM_MIN)
5947 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5948 "VRamSize is too small, %#x, max %#x", pThis->vram_size, VGA_VRAM_MIN);
5949 if (pThis->vram_size & (_256K - 1)) /* Make sure there are no partial banks even in planar modes. */
5950 return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
5951 "VRamSize is not a multiple of 256K (%#x)", pThis->vram_size);
5952
5953 rc = CFGMR3QueryU32Def(pCfg, "MonitorCount", &pThis->cMonitors, 1);
5954 AssertLogRelRCReturn(rc, rc);
5955
5956 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true);
5957 AssertLogRelRCReturn(rc, rc);
5958
5959 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
5960 AssertLogRelRCReturn(rc, rc);
5961 Log(("VGA: VRamSize=%#x fGCenabled=%RTbool fR0Enabled=%RTbool\n", pThis->vram_size, pThis->fGCEnabled, pThis->fR0Enabled));
5962
5963#ifdef VBOX_WITH_VMSVGA
5964 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGAEnabled", &pThis->fVMSVGAEnabled, false);
5965 AssertLogRelRCReturn(rc, rc);
5966 Log(("VMSVGA: VMSVGAEnabled = %d\n", pThis->fVMSVGAEnabled));
5967#endif
5968#ifdef VBOX_WITH_VMSVGA3D
5969 rc = CFGMR3QueryBoolDef(pCfg, "VMSVGA3dEnabled", &pThis->svga.f3DEnabled, false);
5970 AssertLogRelRCReturn(rc, rc);
5971 rc = CFGMR3QueryU64Def(pCfg, "HostWindowId", &pThis->svga.u64HostWindowId, 0);
5972 AssertLogRelRCReturn(rc, rc);
5973 Log(("VMSVGA: VMSVGA3dEnabled = %d\n", pThis->svga.f3DEnabled));
5974 Log(("VMSVGA: HostWindowId = 0x%x\n", pThis->svga.u64HostWindowId));
5975#endif
5976
5977 pThis->pDevInsR3 = pDevIns;
5978 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
5979 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
5980
5981 vgaR3Reset(pDevIns);
5982
5983 /* The PCI devices configuration. */
5984#ifdef VBOX_WITH_VMSVGA
5985 if (pThis->fVMSVGAEnabled)
5986 {
5987 /* Extend our VGA device with VMWare SVGA functionality. */
5988 PCIDevSetVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
5989 PCIDevSetDeviceId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
5990 PCIDevSetSubSystemVendorId(&pThis->Dev, PCI_VENDOR_ID_VMWARE);
5991 PCIDevSetSubSystemId(&pThis->Dev, PCI_DEVICE_ID_VMWARE_SVGA2);
5992 }
5993 else
5994 {
5995#endif /* VBOX_WITH_VMSVGA */
5996 PCIDevSetVendorId( &pThis->Dev, 0x80ee); /* PCI vendor, just a free bogus value */
5997 PCIDevSetDeviceId( &pThis->Dev, 0xbeef);
5998#ifdef VBOX_WITH_VMSVGA
5999 }
6000#endif
6001 PCIDevSetClassSub( &pThis->Dev, 0x00); /* VGA controller */
6002 PCIDevSetClassBase( &pThis->Dev, 0x03);
6003 PCIDevSetHeaderType(&pThis->Dev, 0x00);
6004#if defined(VBOX_WITH_HGSMI) && (defined(VBOX_WITH_VIDEOHWACCEL) || defined(VBOX_WITH_VDMA) || defined(VBOX_WITH_WDDM))
6005 PCIDevSetInterruptPin(&pThis->Dev, 1);
6006#endif
6007
6008 /* the interfaces. */
6009 pThis->IBase.pfnQueryInterface = vgaPortQueryInterface;
6010
6011 pThis->IPort.pfnUpdateDisplay = vgaPortUpdateDisplay;
6012 pThis->IPort.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
6013 pThis->IPort.pfnQueryColorDepth = vgaPortQueryColorDepth;
6014 pThis->IPort.pfnSetRefreshRate = vgaPortSetRefreshRate;
6015 pThis->IPort.pfnTakeScreenshot = vgaPortTakeScreenshot;
6016 pThis->IPort.pfnFreeScreenshot = vgaPortFreeScreenshot;
6017 pThis->IPort.pfnDisplayBlt = vgaPortDisplayBlt;
6018 pThis->IPort.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
6019 pThis->IPort.pfnCopyRect = vgaPortCopyRect;
6020 pThis->IPort.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
6021
6022#if defined(VBOX_WITH_HGSMI)
6023# if defined(VBOX_WITH_VIDEOHWACCEL)
6024 pThis->IVBVACallbacks.pfnVHWACommandCompleteAsync = vbvaVHWACommandCompleteAsync;
6025# endif
6026#if defined(VBOX_WITH_CRHGSMI)
6027 pThis->IVBVACallbacks.pfnCrHgsmiCommandCompleteAsync = vboxVDMACrHgsmiCommandCompleteAsync;
6028 pThis->IVBVACallbacks.pfnCrHgsmiControlCompleteAsync = vboxVDMACrHgsmiControlCompleteAsync;
6029
6030 pThis->IVBVACallbacks.pfnCrCtlSubmit = vboxCmdVBVACmdHostCtl;
6031 pThis->IVBVACallbacks.pfnCrCtlSubmitSync = vboxCmdVBVACmdHostCtlSync;
6032# endif
6033#endif
6034
6035 pThis->ILeds.pfnQueryStatusLed = vgaPortQueryStatusLed;
6036
6037 RT_ZERO(pThis->Led3D);
6038 pThis->Led3D.u32Magic = PDMLED_MAGIC;
6039
6040 /*
6041 * We use our own critical section to avoid unncessary pointer indirections
6042 * in interface methods (as we all as for historical reasons).
6043 */
6044 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "VGA#%u", iInstance);
6045 AssertRCReturn(rc, rc);
6046 rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
6047 AssertRCReturn(rc, rc);
6048
6049 /*
6050 * Allocate the VRAM and map the first 512KB of it into GC so we can speed up VGA support.
6051 */
6052#ifdef VBOX_WITH_VMSVGA
6053 int iPCIRegionVRAM = (pThis->fVMSVGAEnabled) ? 1 : 0;
6054
6055 if (pThis->fVMSVGAEnabled)
6056 {
6057 /*
6058 * Allocate and initialize the FIFO MMIO2 memory.
6059 */
6060 rc = PDMDevHlpMMIO2Register(pDevIns, 2 /*iRegion*/, VMSVGA_FIFO_SIZE, 0 /*fFlags*/, (void **)&pThis->svga.pFIFOR3, "VMSVGA-FIFO");
6061 if (RT_FAILURE(rc))
6062 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
6063 N_("Failed to allocate %u bytes of memory for the VMSVGA device"), VMSVGA_FIFO_SIZE);
6064 pThis->svga.pFIFOR0 = (RTR0PTR)pThis->svga.pFIFOR3;
6065 pThis->svga.cbFIFO = VMSVGA_FIFO_SIZE;
6066 }
6067#else
6068 int iPCIRegionVRAM = 0;
6069#endif
6070 rc = PDMDevHlpMMIO2Register(pDevIns, iPCIRegionVRAM, pThis->vram_size, 0, (void **)&pThis->vram_ptrR3, "VRam");
6071 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMIO2Register(%#x,) -> %Rrc\n", pThis->vram_size, rc), rc);
6072 pThis->vram_ptrR0 = (RTR0PTR)pThis->vram_ptrR3; /** @todo @bugref{1865} Map parts into R0 or just use PGM access (Mac only). */
6073
6074 if (pThis->fGCEnabled)
6075 {
6076 RTRCPTR pRCMapping = 0;
6077 rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pRCMapping);
6078 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6079 pThis->vram_ptrRC = pRCMapping;
6080# ifdef VBOX_WITH_VMSVGA
6081 /* Don't need a mapping in RC */
6082# endif
6083 }
6084
6085#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
6086 if (pThis->fR0Enabled)
6087 {
6088 RTR0PTR pR0Mapping = 0;
6089 rc = PDMDevHlpMMIO2MapKernel(pDevIns, iPCIRegionVRAM, 0 /* off */, VGA_MAPPING_SIZE, "VGA VRam", &pR0Mapping);
6090 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VGA_MAPPING_SIZE, rc), rc);
6091 pThis->vram_ptrR0 = pR0Mapping;
6092# ifdef VBOX_WITH_VMSVGA
6093 if (pThis->fVMSVGAEnabled)
6094 {
6095 RTR0PTR pR0Mapping = 0;
6096 rc = PDMDevHlpMMIO2MapKernel(pDevIns, 2 /* iRegion */, 0 /* off */, VMSVGA_FIFO_SIZE, "VMSVGA-FIFO", &pR0Mapping);
6097 AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", VMSVGA_FIFO_SIZE, rc), rc);
6098 pThis->svga.pFIFOR0 = pR0Mapping;
6099 }
6100# endif
6101 }
6102#endif
6103
6104 /*
6105 * Register I/O ports.
6106 */
6107 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
6108 if (RT_FAILURE(rc))
6109 return rc;
6110 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
6111 if (RT_FAILURE(rc))
6112 return rc;
6113 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
6114 if (RT_FAILURE(rc))
6115 return rc;
6116 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
6117 if (RT_FAILURE(rc))
6118 return rc;
6119 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
6120 if (RT_FAILURE(rc))
6121 return rc;
6122#ifdef VBOX_WITH_HGSMI
6123 /* Use reserved VGA IO ports for HGSMI. */
6124 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_HOST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3b0 (HGSMI host)");
6125 if (RT_FAILURE(rc))
6126 return rc;
6127 rc = PDMDevHlpIOPortRegister(pDevIns, VGA_PORT_HGSMI_GUEST, 4, NULL, vgaR3IOPortHGSMIWrite, vgaR3IOPortHGSMIRead, NULL, NULL, "VGA - 3d0 (HGSMI guest)");
6128 if (RT_FAILURE(rc))
6129 return rc;
6130#endif /* VBOX_WITH_HGSMI */
6131
6132#ifdef CONFIG_BOCHS_VBE
6133 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
6134 if (RT_FAILURE(rc))
6135 return rc;
6136 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
6137 if (RT_FAILURE(rc))
6138 return rc;
6139#endif /* CONFIG_BOCHS_VBE */
6140
6141 /* guest context extension */
6142 if (pThis->fGCEnabled)
6143 {
6144 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6145 if (RT_FAILURE(rc))
6146 return rc;
6147 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6148 if (RT_FAILURE(rc))
6149 return rc;
6150 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6151 if (RT_FAILURE(rc))
6152 return rc;
6153 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6154 if (RT_FAILURE(rc))
6155 return rc;
6156 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6157 if (RT_FAILURE(rc))
6158 return rc;
6159#ifdef CONFIG_BOCHS_VBE
6160 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6161 if (RT_FAILURE(rc))
6162 return rc;
6163 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6164 if (RT_FAILURE(rc))
6165 return rc;
6166#endif /* CONFIG_BOCHS_VBE */
6167 }
6168
6169 /* R0 context extension */
6170 if (pThis->fR0Enabled)
6171 {
6172 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
6173 if (RT_FAILURE(rc))
6174 return rc;
6175 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
6176 if (RT_FAILURE(rc))
6177 return rc;
6178 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
6179 if (RT_FAILURE(rc))
6180 return rc;
6181 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
6182 if (RT_FAILURE(rc))
6183 return rc;
6184 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
6185 if (RT_FAILURE(rc))
6186 return rc;
6187#ifdef CONFIG_BOCHS_VBE
6188 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
6189 if (RT_FAILURE(rc))
6190 return rc;
6191 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
6192 if (RT_FAILURE(rc))
6193 return rc;
6194#endif /* CONFIG_BOCHS_VBE */
6195 }
6196
6197 /* vga mmio */
6198 rc = PDMDevHlpMMIORegisterEx(pDevIns, 0x000a0000, 0x00020000, NULL /*pvUser*/,
6199 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
6200 vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
6201 if (RT_FAILURE(rc))
6202 return rc;
6203 if (pThis->fGCEnabled)
6204 {
6205 rc = PDMDevHlpMMIORegisterRCEx(pDevIns, 0x000a0000, 0x00020000, NIL_RTRCPTR /*pvUser*/,
6206 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6207 if (RT_FAILURE(rc))
6208 return rc;
6209 }
6210 if (pThis->fR0Enabled)
6211 {
6212 rc = PDMDevHlpMMIORegisterR0Ex(pDevIns, 0x000a0000, 0x00020000, NIL_RTR0PTR /*pvUser*/,
6213 "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill");
6214 if (RT_FAILURE(rc))
6215 return rc;
6216 }
6217
6218 /* vga bios */
6219 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
6220 if (RT_FAILURE(rc))
6221 return rc;
6222 if (pThis->fR0Enabled)
6223 {
6224 rc = PDMDevHlpIOPortRegisterR0(pDevIns, VBE_PRINTF_PORT, 1, 0, "vgaIOPortWriteBIOS", "vgaIOPortReadBIOS", NULL, NULL, "VGA BIOS debug/panic");
6225 if (RT_FAILURE(rc))
6226 return rc;
6227 }
6228
6229 /*
6230 * Get the VGA BIOS ROM file name.
6231 */
6232 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszVgaBiosFile);
6233 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6234 {
6235 pThis->pszVgaBiosFile = NULL;
6236 rc = VINF_SUCCESS;
6237 }
6238 else if (RT_FAILURE(rc))
6239 return PDMDEV_SET_ERROR(pDevIns, rc,
6240 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
6241 else if (!*pThis->pszVgaBiosFile)
6242 {
6243 MMR3HeapFree(pThis->pszVgaBiosFile);
6244 pThis->pszVgaBiosFile = NULL;
6245 }
6246
6247 /*
6248 * Determine the VGA BIOS ROM size, open specified ROM file in the process.
6249 */
6250 RTFILE FileVgaBios = NIL_RTFILE;
6251 if (pThis->pszVgaBiosFile)
6252 {
6253 rc = RTFileOpen(&FileVgaBios, pThis->pszVgaBiosFile,
6254 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6255 if (RT_SUCCESS(rc))
6256 {
6257 rc = RTFileGetSize(FileVgaBios, &pThis->cbVgaBios);
6258 if (RT_SUCCESS(rc))
6259 {
6260 if ( RT_ALIGN(pThis->cbVgaBios, _4K) != pThis->cbVgaBios
6261 || pThis->cbVgaBios > _64K
6262 || pThis->cbVgaBios < 16 * _1K)
6263 rc = VERR_TOO_MUCH_DATA;
6264 }
6265 }
6266 if (RT_FAILURE(rc))
6267 {
6268 /*
6269 * In case of failure simply fall back to the built-in VGA BIOS ROM.
6270 */
6271 Log(("vgaConstruct: Failed to open VGA BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszVgaBiosFile, rc));
6272 RTFileClose(FileVgaBios);
6273 FileVgaBios = NIL_RTFILE;
6274 MMR3HeapFree(pThis->pszVgaBiosFile);
6275 pThis->pszVgaBiosFile = NULL;
6276 }
6277 }
6278
6279 /*
6280 * Attempt to get the VGA BIOS ROM data from file.
6281 */
6282 if (pThis->pszVgaBiosFile)
6283 {
6284 /*
6285 * Allocate buffer for the VGA BIOS ROM data.
6286 */
6287 pThis->pu8VgaBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbVgaBios);
6288 if (pThis->pu8VgaBios)
6289 {
6290 rc = RTFileRead(FileVgaBios, pThis->pu8VgaBios, pThis->cbVgaBios, NULL);
6291 if (RT_FAILURE(rc))
6292 {
6293 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbVgaBios, rc));
6294 MMR3HeapFree(pThis->pu8VgaBios);
6295 pThis->pu8VgaBios = NULL;
6296 }
6297 rc = VINF_SUCCESS;
6298 }
6299 else
6300 rc = VERR_NO_MEMORY;
6301 }
6302 else
6303 pThis->pu8VgaBios = NULL;
6304
6305 /* cleanup */
6306 if (FileVgaBios != NIL_RTFILE)
6307 RTFileClose(FileVgaBios);
6308
6309 /* If we were unable to get the data from file for whatever reason, fall
6310 back to the built-in ROM image. */
6311 const uint8_t *pu8VgaBiosBinary;
6312 uint64_t cbVgaBiosBinary;
6313 uint32_t fFlags = 0;
6314 if (pThis->pu8VgaBios == NULL)
6315 {
6316 pu8VgaBiosBinary = g_abVgaBiosBinary;
6317 cbVgaBiosBinary = g_cbVgaBiosBinary;
6318 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
6319 }
6320 else
6321 {
6322 pu8VgaBiosBinary = pThis->pu8VgaBios;
6323 cbVgaBiosBinary = pThis->cbVgaBios;
6324 }
6325
6326 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6327 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
6328 /* Note! Because of old saved states we'll always register at least 36KB of ROM. */
6329 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, RT_MAX(cbVgaBiosBinary, 36*_1K), pu8VgaBiosBinary, cbVgaBiosBinary,
6330 fFlags, "VGA BIOS");
6331 if (RT_FAILURE(rc))
6332 return rc;
6333
6334 /*
6335 * Saved state.
6336 */
6337 rc = PDMDevHlpSSMRegisterEx(pDevIns, VGA_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
6338 NULL, vgaR3LiveExec, NULL,
6339 vgaR3SavePrep, vgaR3SaveExec, vgaR3SaveDone,
6340 NULL, vgaR3LoadExec, vgaR3LoadDone);
6341 if (RT_FAILURE(rc))
6342 return rc;
6343
6344 /*
6345 * PCI device registration.
6346 */
6347 rc = PDMDevHlpPCIRegister(pDevIns, &pThis->Dev);
6348 if (RT_FAILURE(rc))
6349 return rc;
6350 /*AssertMsg(pThis->Dev.devfn == 16 || iInstance != 0, ("pThis->Dev.devfn=%d\n", pThis->Dev.devfn));*/
6351 if (pThis->Dev.devfn != 16 && iInstance == 0)
6352 Log(("!!WARNING!!: pThis->dev.devfn=%d (ignore if testcase or not started by Main)\n", pThis->Dev.devfn));
6353
6354#ifdef VBOX_WITH_VMSVGA
6355 if (pThis->fVMSVGAEnabled)
6356 {
6357 /* Register the io command ports. */
6358 rc = PDMDevHlpPCIIORegionRegister (pDevIns, 0 /* iRegion */, 0x10, PCI_ADDRESS_SPACE_IO, vmsvgaR3IORegionMap);
6359 if (RT_FAILURE (rc))
6360 return rc;
6361 /* VMware's MetalKit doesn't like PCI_ADDRESS_SPACE_MEM_PREFETCH */
6362 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 1 /* iRegion */, pThis->vram_size, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vgaR3IORegionMap);
6363 if (RT_FAILURE(rc))
6364 return rc;
6365 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 2 /* iRegion */, VMSVGA_FIFO_SIZE, PCI_ADDRESS_SPACE_MEM /* PCI_ADDRESS_SPACE_MEM_PREFETCH */, vmsvgaR3IORegionMap);
6366 if (RT_FAILURE(rc))
6367 return rc;
6368 }
6369 else
6370#endif /* VBOX_WITH_VMSVGA */
6371 rc = PDMDevHlpPCIIORegionRegister(pDevIns, iPCIRegionVRAM, pThis->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
6372 if (RT_FAILURE(rc))
6373 return rc;
6374
6375 /*
6376 * Create the refresh timer.
6377 */
6378 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh,
6379 pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
6380 "VGA Refresh Timer", &pThis->RefreshTimer);
6381 if (RT_FAILURE(rc))
6382 return rc;
6383
6384 /*
6385 * Attach to the display.
6386 */
6387 rc = vgaAttach(pDevIns, 0 /* display LUN # */, PDM_TACH_FLAGS_NOT_HOT_PLUG);
6388 if (RT_FAILURE(rc))
6389 return rc;
6390
6391 /*
6392 * Initialize the retrace flag.
6393 */
6394 rc = CFGMR3QueryBoolDef(pCfg, "RealRetrace", &pThis->fRealRetrace, false);
6395 AssertLogRelRCReturn(rc, rc);
6396
6397#ifdef VBE_NEW_DYN_LIST
6398
6399 uint16_t maxBiosXRes;
6400 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosXRes", &maxBiosXRes, UINT16_MAX);
6401 AssertLogRelRCReturn(rc, rc);
6402 uint16_t maxBiosYRes;
6403 rc = CFGMR3QueryU16Def(pCfg, "MaxBiosYRes", &maxBiosYRes, UINT16_MAX);
6404 AssertLogRelRCReturn(rc, rc);
6405
6406 /*
6407 * Compute buffer size for the VBE BIOS Extra Data.
6408 */
6409 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
6410
6411 rc = CFGMR3QueryU32(pCfg, "HeightReduction", &cyReduction);
6412 if (RT_SUCCESS(rc) && cyReduction)
6413 cb *= 2; /* Default mode list will be twice long */
6414 else
6415 cyReduction = 0;
6416
6417 rc = CFGMR3QueryU32(pCfg, "CustomVideoModes", &cCustomModes);
6418 if (RT_SUCCESS(rc) && cCustomModes)
6419 cb += sizeof(ModeInfoListItem) * cCustomModes;
6420 else
6421 cCustomModes = 0;
6422
6423 /*
6424 * Allocate and initialize buffer for the VBE BIOS Extra Data.
6425 */
6426 AssertRelease(sizeof(VBEHEADER) + cb < 65536);
6427 pThis->cbVBEExtraData = (uint16_t)(sizeof(VBEHEADER) + cb);
6428 pThis->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pThis->cbVBEExtraData);
6429 if (!pThis->pu8VBEExtraData)
6430 return VERR_NO_MEMORY;
6431
6432 pVBEDataHdr = (PVBEHEADER)pThis->pu8VBEExtraData;
6433 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
6434 pVBEDataHdr->cbData = cb;
6435
6436# ifndef VRAM_SIZE_FIX
6437 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
6438 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
6439# else /* VRAM_SIZE_FIX defined */
6440 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
6441 for (i = 0; i < MODE_INFO_SIZE; i++)
6442 {
6443 uint32_t pixelWidth, reqSize;
6444 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6445 pixelWidth = 2;
6446 else
6447 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
6448 reqSize = mode_info_list[i].info.XResolution
6449 * mode_info_list[i].info.YResolution
6450 * pixelWidth;
6451 if (reqSize >= pThis->vram_size)
6452 continue;
6453 if ( mode_info_list[i].info.XResolution > maxBiosXRes
6454 || mode_info_list[i].info.YResolution > maxBiosYRes)
6455 continue;
6456 *pCurMode = mode_info_list[i];
6457 vgaAdjustModeInfo(pThis, pCurMode);
6458 pCurMode++;
6459 }
6460# endif /* VRAM_SIZE_FIX defined */
6461
6462 /*
6463 * Copy default modes with subtracted YResolution.
6464 */
6465 if (cyReduction)
6466 {
6467 ModeInfoListItem *pDefMode = mode_info_list;
6468 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
6469# ifndef VRAM_SIZE_FIX
6470 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
6471 {
6472 *pCurMode = *pDefMode;
6473 pCurMode->mode += 0x30;
6474 pCurMode->info.YResolution -= cyReduction;
6475 }
6476# else /* VRAM_SIZE_FIX defined */
6477 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
6478 {
6479 uint32_t pixelWidth, reqSize;
6480 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
6481 pixelWidth = 2;
6482 else
6483 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
6484 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
6485 if (reqSize >= pThis->vram_size)
6486 continue;
6487 if ( pDefMode->info.XResolution > maxBiosXRes
6488 || pDefMode->info.YResolution - cyReduction > maxBiosYRes)
6489 continue;
6490 *pCurMode = *pDefMode;
6491 pCurMode->mode += 0x30;
6492 pCurMode->info.YResolution -= cyReduction;
6493 pCurMode++;
6494 }
6495# endif /* VRAM_SIZE_FIX defined */
6496 }
6497
6498
6499 /*
6500 * Add custom modes.
6501 */
6502 if (cCustomModes)
6503 {
6504 uint16_t u16CurMode = 0x160;
6505 for (i = 1; i <= cCustomModes; i++)
6506 {
6507 char szExtraDataKey[sizeof("CustomVideoModeXX")];
6508 char *pszExtraData = NULL;
6509
6510 /* query and decode the custom mode string. */
6511 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
6512 rc = CFGMR3QueryStringAlloc(pCfg, szExtraDataKey, &pszExtraData);
6513 if (RT_SUCCESS(rc))
6514 {
6515 ModeInfoListItem *pDefMode = mode_info_list;
6516 unsigned int cx, cy, cBits, cParams, j;
6517 uint16_t u16DefMode;
6518
6519 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
6520 if ( cParams != 3
6521 || (cBits != 16 && cBits != 24 && cBits != 32))
6522 {
6523 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
6524 return VERR_VGA_INVALID_CUSTOM_MODE;
6525 }
6526 cbPitch = calc_line_pitch(cBits, cx);
6527# ifdef VRAM_SIZE_FIX
6528 if (cy * cbPitch >= pThis->vram_size)
6529 {
6530 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",
6531 cx, cy, cBits, pThis->vram_size / _1M));
6532 return VERR_VGA_INVALID_CUSTOM_MODE;
6533 }
6534# endif /* VRAM_SIZE_FIX defined */
6535 MMR3HeapFree(pszExtraData);
6536
6537 /* Use defaults from max@bpp mode. */
6538 switch (cBits)
6539 {
6540 case 16:
6541 u16DefMode = VBE_VESA_MODE_1024X768X565;
6542 break;
6543
6544 case 24:
6545 u16DefMode = VBE_VESA_MODE_1024X768X888;
6546 break;
6547
6548 case 32:
6549 u16DefMode = VBE_OWN_MODE_1024X768X8888;
6550 break;
6551
6552 default: /* gcc, shut up! */
6553 AssertMsgFailed(("gone postal!\n"));
6554 continue;
6555 }
6556
6557 /* mode_info_list is not terminated */
6558 for (j = 0; j < MODE_INFO_SIZE && pDefMode->mode != u16DefMode; j++)
6559 pDefMode++;
6560 Assert(j < MODE_INFO_SIZE);
6561
6562 *pCurMode = *pDefMode;
6563 pCurMode->mode = u16CurMode++;
6564
6565 /* adjust defaults */
6566 pCurMode->info.XResolution = cx;
6567 pCurMode->info.YResolution = cy;
6568 pCurMode->info.BytesPerScanLine = cbPitch;
6569 pCurMode->info.LinBytesPerScanLine = cbPitch;
6570 vgaAdjustModeInfo(pThis, pCurMode);
6571
6572 /* commit it */
6573 pCurMode++;
6574 }
6575 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
6576 {
6577 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Rrc\n", szExtraDataKey, rc));
6578 return rc;
6579 }
6580 } /* foreach custom mode key */
6581 }
6582
6583 /*
6584 * Add the "End of list" mode.
6585 */
6586 memset(pCurMode, 0, sizeof(*pCurMode));
6587 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
6588
6589 /*
6590 * Register I/O Port for the VBE BIOS Extra Data.
6591 */
6592 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
6593 if (RT_FAILURE(rc))
6594 return rc;
6595#endif /* VBE_NEW_DYN_LIST */
6596
6597 /*
6598 * Register I/O Port for the BIOS Logo.
6599 */
6600 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, vbeIOPortWriteCMDLogo, vbeIOPortReadCMDLogo, NULL, NULL, "BIOS Logo");
6601 if (RT_FAILURE(rc))
6602 return rc;
6603
6604 /*
6605 * Register debugger info callbacks.
6606 */
6607 PDMDevHlpDBGFInfoRegister(pDevIns, "vga", "Display basic VGA state.", vgaInfoState);
6608 PDMDevHlpDBGFInfoRegister(pDevIns, "vgatext", "Display VGA memory formatted as text.", vgaInfoText);
6609 PDMDevHlpDBGFInfoRegister(pDevIns, "vgacr", "Dump VGA CRTC registers.", vgaInfoCR);
6610 PDMDevHlpDBGFInfoRegister(pDevIns, "vgagr", "Dump VGA Graphics Controller registers.", vgaInfoGR);
6611 PDMDevHlpDBGFInfoRegister(pDevIns, "vgasr", "Dump VGA Sequencer registers.", vgaInfoSR);
6612 PDMDevHlpDBGFInfoRegister(pDevIns, "vgaar", "Dump VGA Attribute Controller registers.", vgaInfoAR);
6613 PDMDevHlpDBGFInfoRegister(pDevIns, "vgapl", "Dump planar graphics state.", vgaInfoPlanar);
6614 PDMDevHlpDBGFInfoRegister(pDevIns, "vgadac", "Dump VGA DAC registers.", vgaInfoDAC);
6615 PDMDevHlpDBGFInfoRegister(pDevIns, "vbe", "Dump VGA VBE registers.", vgaInfoVBE);
6616
6617 /*
6618 * Construct the logo header.
6619 */
6620 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0, 0 };
6621
6622 rc = CFGMR3QueryU8(pCfg, "FadeIn", &LogoHdr.fu8FadeIn);
6623 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6624 LogoHdr.fu8FadeIn = 1;
6625 else if (RT_FAILURE(rc))
6626 return PDMDEV_SET_ERROR(pDevIns, rc,
6627 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
6628
6629 rc = CFGMR3QueryU8(pCfg, "FadeOut", &LogoHdr.fu8FadeOut);
6630 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6631 LogoHdr.fu8FadeOut = 1;
6632 else if (RT_FAILURE(rc))
6633 return PDMDEV_SET_ERROR(pDevIns, rc,
6634 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
6635
6636 rc = CFGMR3QueryU16(pCfg, "LogoTime", &LogoHdr.u16LogoMillies);
6637 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6638 LogoHdr.u16LogoMillies = 0;
6639 else if (RT_FAILURE(rc))
6640 return PDMDEV_SET_ERROR(pDevIns, rc,
6641 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
6642
6643 rc = CFGMR3QueryU8(pCfg, "ShowBootMenu", &LogoHdr.fu8ShowBootMenu);
6644 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6645 LogoHdr.fu8ShowBootMenu = 0;
6646 else if (RT_FAILURE(rc))
6647 return PDMDEV_SET_ERROR(pDevIns, rc,
6648 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
6649
6650#if defined(DEBUG) && !defined(DEBUG_sunlover)
6651 /* Disable the logo abd menu if all default settings. */
6652 if ( LogoHdr.fu8FadeIn
6653 && LogoHdr.fu8FadeOut
6654 && LogoHdr.u16LogoMillies == 0
6655 && LogoHdr.fu8ShowBootMenu == 2)
6656 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.fu8ShowBootMenu = 0;
6657#endif
6658
6659 /* Delay the logo a little bit */
6660 if (LogoHdr.fu8FadeIn && LogoHdr.fu8FadeOut && !LogoHdr.u16LogoMillies)
6661 LogoHdr.u16LogoMillies = RT_MAX(LogoHdr.u16LogoMillies, LOGO_DELAY_TIME);
6662
6663 /*
6664 * Get the Logo file name.
6665 */
6666 rc = CFGMR3QueryStringAlloc(pCfg, "LogoFile", &pThis->pszLogoFile);
6667 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
6668 pThis->pszLogoFile = NULL;
6669 else if (RT_FAILURE(rc))
6670 return PDMDEV_SET_ERROR(pDevIns, rc,
6671 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
6672 else if (!*pThis->pszLogoFile)
6673 {
6674 MMR3HeapFree(pThis->pszLogoFile);
6675 pThis->pszLogoFile = NULL;
6676 }
6677
6678 /*
6679 * Determine the logo size, open any specified logo file in the process.
6680 */
6681 LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6682 RTFILE FileLogo = NIL_RTFILE;
6683 if (pThis->pszLogoFile)
6684 {
6685 rc = RTFileOpen(&FileLogo, pThis->pszLogoFile,
6686 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
6687 if (RT_SUCCESS(rc))
6688 {
6689 uint64_t cbFile;
6690 rc = RTFileGetSize(FileLogo, &cbFile);
6691 if (RT_SUCCESS(rc))
6692 {
6693 if (cbFile > 0 && cbFile < 32*_1M)
6694 LogoHdr.cbLogo = (uint32_t)cbFile;
6695 else
6696 rc = VERR_TOO_MUCH_DATA;
6697 }
6698 }
6699 if (RT_FAILURE(rc))
6700 {
6701 /*
6702 * Ignore failure and fall back to the default logo.
6703 */
6704 LogRel(("vgaR3Construct: Failed to open logo file '%s', rc=%Rrc!\n", pThis->pszLogoFile, rc));
6705 if (FileLogo != NIL_RTFILE)
6706 RTFileClose(FileLogo);
6707 FileLogo = NIL_RTFILE;
6708 MMR3HeapFree(pThis->pszLogoFile);
6709 pThis->pszLogoFile = NULL;
6710 }
6711 }
6712
6713 /*
6714 * Disable graphic splash screen if it doesn't fit into VRAM.
6715 */
6716 if (pThis->vram_size < LOGO_MAX_SIZE)
6717 LogoHdr.fu8FadeIn = LogoHdr.fu8FadeOut = LogoHdr.u16LogoMillies = 0;
6718
6719 /*
6720 * Allocate buffer for the logo data.
6721 * RT_MAX() is applied to let us fall back to default logo on read failure.
6722 */
6723 pThis->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
6724 pThis->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pThis->cbLogo, g_cbVgaDefBiosLogo + sizeof(LogoHdr)));
6725 if (pThis->pu8Logo)
6726 {
6727 /*
6728 * Write the logo header.
6729 */
6730 PLOGOHDR pLogoHdr = (PLOGOHDR)pThis->pu8Logo;
6731 *pLogoHdr = LogoHdr;
6732
6733 /*
6734 * Write the logo bitmap.
6735 */
6736 if (pThis->pszLogoFile)
6737 {
6738 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
6739 if (RT_SUCCESS(rc))
6740 rc = vbeParseBitmap(pThis);
6741 if (RT_FAILURE(rc))
6742 {
6743 LogRel(("Error %Rrc reading logo file '%s', using internal logo\n",
6744 rc, pThis->pszLogoFile));
6745 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbVgaDefBiosLogo;
6746 }
6747 }
6748 if ( !pThis->pszLogoFile
6749 || RT_FAILURE(rc))
6750 {
6751 memcpy(pLogoHdr + 1, g_abVgaDefBiosLogo, LogoHdr.cbLogo);
6752 rc = vbeParseBitmap(pThis);
6753 if (RT_FAILURE(rc))
6754 AssertReleaseMsgFailed(("Parsing of internal bitmap failed! vbeParseBitmap() -> %Rrc\n", rc));
6755 }
6756
6757 rc = VINF_SUCCESS;
6758 }
6759 else
6760 rc = VERR_NO_MEMORY;
6761
6762 /*
6763 * Cleanup.
6764 */
6765 if (FileLogo != NIL_RTFILE)
6766 RTFileClose(FileLogo);
6767
6768#ifdef VBOX_WITH_HGSMI
6769 VBVAInit (pThis);
6770#endif /* VBOX_WITH_HGSMI */
6771
6772#ifdef VBOX_WITH_VDMA
6773 if (rc == VINF_SUCCESS)
6774 {
6775 rc = vboxVDMAConstruct(pThis, 1024);
6776 AssertRC(rc);
6777 }
6778#endif
6779
6780#ifdef VBOX_WITH_VMSVGA
6781 if ( rc == VINF_SUCCESS
6782 && pThis->fVMSVGAEnabled)
6783 {
6784 rc = vmsvgaInit(pDevIns);
6785 }
6786#endif
6787
6788 /*
6789 * Statistics.
6790 */
6791 STAM_REG(pVM, &pThis->StatRZMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6792 STAM_REG(pVM, &pThis->StatR3MemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
6793 STAM_REG(pVM, &pThis->StatRZMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/RZ/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6794 STAM_REG(pVM, &pThis->StatR3MemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/R3/MMIO-Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
6795 STAM_REG(pVM, &pThis->StatMapPage, STAMTYPE_COUNTER, "/Devices/VGA/MapPageCalls", STAMUNIT_OCCURENCES, "Calls to IOMMMIOMapMMIO2Page.");
6796 STAM_REG(pVM, &pThis->StatUpdateDisp, STAMTYPE_COUNTER, "/Devices/VGA/UpdateDisplay", STAMUNIT_OCCURENCES, "Calls to vgaPortUpdateDisplay().");
6797
6798 /* Init latched access mask. */
6799 pThis->uMaskLatchAccess = 0x3ff;
6800
6801 if (RT_SUCCESS(rc))
6802 {
6803 PPDMIBASE pBase;
6804 /*
6805 * Attach status driver (optional).
6806 */
6807 rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThis->IBase, &pBase, "Status Port");
6808 AssertRC(rc);
6809 if (RT_SUCCESS(rc))
6810 {
6811 pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
6812 pThis->pMediaNotify = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIANOTIFY);
6813 }
6814 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
6815 {
6816 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
6817 rc = VINF_SUCCESS;
6818 }
6819 else
6820 {
6821 AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
6822 rc = PDMDEV_SET_ERROR(pDevIns, rc, N_("VGA cannot attach to status driver"));
6823 }
6824 }
6825 return rc;
6826}
6827
6828
6829/**
6830 * The device registration structure.
6831 */
6832const PDMDEVREG g_DeviceVga =
6833{
6834 /* u32Version */
6835 PDM_DEVREG_VERSION,
6836 /* szName */
6837 "vga",
6838 /* szRCMod */
6839 "VBoxDDGC.gc",
6840 /* szR0Mod */
6841 "VBoxDDR0.r0",
6842 /* pszDescription */
6843 "VGA Adaptor with VESA extensions.",
6844 /* fFlags */
6845 PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
6846 /* fClass */
6847 PDM_DEVREG_CLASS_GRAPHICS,
6848 /* cMaxInstances */
6849 1,
6850 /* cbInstance */
6851 sizeof(VGASTATE),
6852 /* pfnConstruct */
6853 vgaR3Construct,
6854 /* pfnDestruct */
6855 vgaR3Destruct,
6856 /* pfnRelocate */
6857 vgaR3Relocate,
6858 /* pfnMemSetup */
6859 NULL,
6860 /* pfnPowerOn */
6861#ifdef VBOX_WITH_VMSVGA
6862 vmsvgaR3PowerOn,
6863#else
6864 NULL,
6865#endif
6866 /* pfnReset */
6867 vgaR3Reset,
6868 /* pfnSuspend */
6869 NULL,
6870 /* pfnResume */
6871 NULL,
6872 /* pfnAttach */
6873 vgaAttach,
6874 /* pfnDetach */
6875 vgaDetach,
6876 /* pfnQueryInterface */
6877 NULL,
6878 /* pfnInitComplete */
6879 NULL,
6880 /* pfnPowerOff */
6881 NULL,
6882 /* pfnSoftReset */
6883 NULL,
6884 /* u32VersionEnd */
6885 PDM_DEVREG_VERSION
6886};
6887
6888#endif /* !IN_RING3 */
6889#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
6890
6891/*
6892 * Local Variables:
6893 * nuke-trailing-whitespace-p:nil
6894 * End:
6895 */
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