VirtualBox

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

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

DevVGA: take the size of the framebuffer into account.

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

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