VirtualBox

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

Last change on this file since 5627 was 5450, checked in by vboxsync, 17 years ago

Synced with updated VGA/VESA BIOS.

  • Property svn:eol-style set to native
File size: 160.0 KB
Line 
1#ifdef VBOX
2/** @file
3 *
4 * VBox VGA/VESA device
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 * --------------------------------------------------------------------
18 *
19 * This code is based on:
20 *
21 * QEMU VGA Emulator.
22 *
23 * Copyright (c) 2003 Fabrice Bellard
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a copy
26 * of this software and associated documentation files (the "Software"), to deal
27 * in the Software without restriction, including without limitation the rights
28 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29 * copies of the Software, and to permit persons to whom the Software is
30 * furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 * THE SOFTWARE.
42 */
43
44/*******************************************************************************
45* Defined Constants And Macros *
46*******************************************************************************/
47/** The default amount of VRAM. */
48#define VGA_VRAM_DEFAULT (_4M)
49/** The maximum amount of VRAM. */
50#define VGA_VRAM_MAX (128 * _1M)
51/** The minimum amount of VRAM. */
52#define VGA_VRAM_MIN (_1M)
53
54/** The size of the VGA GC mapping.
55 * This is supposed to be all the VGA memory accessible to the guest.
56 * The initial value was 256KB but NTAllInOne.iso appears to access more
57 * thus the limit was upped to 512KB.
58 *
59 * @todo Someone with some VGA knowhow should make a better guess at this value.
60 */
61#define VGA_MAPPING_SIZE _512K
62
63/** Converts a vga adaptor state pointer to a device instance pointer. */
64#define VGASTATE2DEVINS(pVgaState) ((pVgaState)->CTXSUFF(pDevIns))
65
66/** Use VBE bytewise I/O */
67#define VBE_BYTEWISE_IO
68
69/** Use VBE new dynamic mode list.
70 * If this is not defined, no checks are carried out to see if the modes all
71 * fit into the framebuffer! See the VRAM_SIZE_FIX define. */
72#define VBE_NEW_DYN_LIST
73
74/** Check that the video modes fit into virtual video memory.
75 * Only works when VBE_NEW_DYN_LIST is defined! */
76#define VRAM_SIZE_FIX
77
78/** Some fixes to ensure that logical scan-line lengths are not overwritten. */
79#define KEEP_SCAN_LINE_LENGTH
80
81
82/*******************************************************************************
83* Header Files *
84*******************************************************************************/
85#define LOG_GROUP LOG_GROUP_DEV_VGA
86#include <VBox/pdmdev.h>
87#include <VBox/pgm.h>
88#include <iprt/assert.h>
89#include <iprt/asm.h>
90#include <iprt/string.h>
91
92#include <VBox/VBoxGuest.h>
93#include <VBox/VBoxVideo.h>
94
95#if defined(VBE_NEW_DYN_LIST) && defined(IN_RING3) && !defined(VBOX_DEVICE_STRUCT_TESTCASE)
96# include "DevVGAModes.h"
97# include <stdio.h> /* sscan */
98#endif
99
100#include "vl_vbox.h"
101#include "DevVGA.h"
102#include "Builtins.h"
103#include "Builtins2.h"
104
105
106#ifndef VBOX_DEVICE_STRUCT_TESTCASE
107/*******************************************************************************
108* Internal Functions *
109*******************************************************************************/
110__BEGIN_DECLS
111
112PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
113PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
114PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
115PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
116PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
117PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
118PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems);
119PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
120PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
121#ifdef IN_GC
122PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
123#endif
124#ifdef IN_RING0
125PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser);
126#endif
127#ifdef IN_RING3
128# ifdef VBE_NEW_DYN_LIST
129PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
130PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
131# endif
132#endif /* IN_RING3 */
133
134
135__END_DECLS
136
137
138/**
139 * Set a VRAM page dirty.
140 *
141 * @param pData VGA instance data.
142 * @param offVRAM The VRAM offset of the page to set.
143 */
144DECLINLINE(void) vga_set_dirty(VGAState *pData, RTGCPHYS offVRAM)
145{
146 AssertMsg(offVRAM < pData->vram_size, ("offVRAM = %p, pData->vram_size = %p\n", offVRAM, pData->vram_size));
147 ASMBitSet(&pData->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
148 pData->fHaveDirtyBits = true;
149}
150
151/**
152 * Tests if a VRAM page is dirty.
153 *
154 * @returns true if dirty.
155 * @returns false if clean.
156 * @param pData VGA instance data.
157 * @param offVRAM The VRAM offset of the page to check.
158 */
159DECLINLINE(bool) vga_is_dirty(VGAState *pData, RTGCPHYS offVRAM)
160{
161 AssertMsg(offVRAM < pData->vram_size, ("offVRAM = %p, pData->vram_size = %p\n", offVRAM, pData->vram_size));
162 return ASMBitTest(&pData->au32DirtyBitmap[0], offVRAM >> PAGE_SHIFT);
163}
164
165/**
166 * Reset dirty flags in a give range.
167 *
168 * @param pData VGA instance data.
169 * @param offVRAMStart Offset into the VRAM buffer of the first page.
170 * @param offVRAMEnd Offset into the VRAM buffer of the last page - exclusive.
171 */
172DECLINLINE(void) vga_reset_dirty(VGAState *pData, RTGCPHYS offVRAMStart, RTGCPHYS offVRAMEnd)
173{
174 Assert(offVRAMStart < pData->vram_size);
175 Assert(offVRAMEnd <= pData->vram_size);
176 Assert(offVRAMStart < offVRAMEnd);
177 ASMBitClearRange(&pData->au32DirtyBitmap[0], offVRAMStart >> PAGE_SHIFT, offVRAMEnd >> PAGE_SHIFT);
178}
179
180#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
181#endif /* VBOX */
182#ifndef VBOX_DEVICE_STRUCT_TESTCASE
183
184#ifndef VBOX
185#include "vl.h"
186#include "vga_int.h"
187#endif /* !VBOX */
188
189#ifdef LOG_ENABLED
190//#define DEBUG_VGA
191//#define DEBUG_VGA_MEM
192//#define DEBUG_VGA_REG
193
194#define DEBUG_BOCHS_VBE
195
196#endif
197
198/* force some bits to zero */
199#ifdef VBOX
200static
201#endif /* VBOX */
202const uint8_t sr_mask[8] = {
203 (uint8_t)~0xfc,
204 (uint8_t)~0xc2,
205 (uint8_t)~0xf0,
206 (uint8_t)~0xc0,
207 (uint8_t)~0xf1,
208 (uint8_t)~0xff,
209 (uint8_t)~0xff,
210 (uint8_t)~0x00,
211};
212
213#ifdef VBOX
214static
215#endif /* VBOX */
216const uint8_t gr_mask[16] = {
217 (uint8_t)~0xf0, /* 0x00 */
218 (uint8_t)~0xf0, /* 0x01 */
219 (uint8_t)~0xf0, /* 0x02 */
220 (uint8_t)~0xe0, /* 0x03 */
221 (uint8_t)~0xfc, /* 0x04 */
222 (uint8_t)~0x84, /* 0x05 */
223 (uint8_t)~0xf0, /* 0x06 */
224 (uint8_t)~0xf0, /* 0x07 */
225 (uint8_t)~0x00, /* 0x08 */
226 (uint8_t)~0xff, /* 0x09 */
227 (uint8_t)~0xff, /* 0x0a */
228 (uint8_t)~0xff, /* 0x0b */
229 (uint8_t)~0xff, /* 0x0c */
230 (uint8_t)~0xff, /* 0x0d */
231 (uint8_t)~0xff, /* 0x0e */
232 (uint8_t)~0xff, /* 0x0f */
233};
234
235#define cbswap_32(__x) \
236((uint32_t)( \
237 (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
238 (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
239 (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
240 (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
241
242#ifdef WORDS_BIGENDIAN
243#define PAT(x) cbswap_32(x)
244#else
245#define PAT(x) (x)
246#endif
247
248#ifdef WORDS_BIGENDIAN
249#define BIG 1
250#else
251#define BIG 0
252#endif
253
254#ifdef WORDS_BIGENDIAN
255#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff)
256#else
257#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
258#endif
259
260static const uint32_t mask16[16] = {
261 PAT(0x00000000),
262 PAT(0x000000ff),
263 PAT(0x0000ff00),
264 PAT(0x0000ffff),
265 PAT(0x00ff0000),
266 PAT(0x00ff00ff),
267 PAT(0x00ffff00),
268 PAT(0x00ffffff),
269 PAT(0xff000000),
270 PAT(0xff0000ff),
271 PAT(0xff00ff00),
272 PAT(0xff00ffff),
273 PAT(0xffff0000),
274 PAT(0xffff00ff),
275 PAT(0xffffff00),
276 PAT(0xffffffff),
277};
278
279#undef PAT
280
281#ifdef WORDS_BIGENDIAN
282#define PAT(x) (x)
283#else
284#define PAT(x) cbswap_32(x)
285#endif
286
287static const uint32_t dmask16[16] = {
288 PAT(0x00000000),
289 PAT(0x000000ff),
290 PAT(0x0000ff00),
291 PAT(0x0000ffff),
292 PAT(0x00ff0000),
293 PAT(0x00ff00ff),
294 PAT(0x00ffff00),
295 PAT(0x00ffffff),
296 PAT(0xff000000),
297 PAT(0xff0000ff),
298 PAT(0xff00ff00),
299 PAT(0xff00ffff),
300 PAT(0xffff0000),
301 PAT(0xffff00ff),
302 PAT(0xffffff00),
303 PAT(0xffffffff),
304};
305
306static const uint32_t dmask4[4] = {
307 PAT(0x00000000),
308 PAT(0x0000ffff),
309 PAT(0xffff0000),
310 PAT(0xffffffff),
311};
312
313#if defined(VBOX) && defined(IN_RING3)
314static uint32_t expand4[256];
315static uint16_t expand2[256];
316static uint8_t expand4to8[16];
317#endif /* VBOX && IN_RING3 */
318
319#ifndef VBOX
320VGAState *vga_state;
321int vga_io_memory;
322#endif /* !VBOX */
323
324static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
325{
326 VGAState *s = (VGAState*)opaque;
327 int val, index;
328
329 /* check port range access depending on color/monochrome mode */
330 if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
331 (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
332 val = 0xff;
333 Log(("VGA: following read ignored\n"));
334 } else {
335 switch(addr) {
336 case 0x3c0:
337 if (s->ar_flip_flop == 0) {
338 val = s->ar_index;
339 } else {
340 val = 0;
341 }
342 break;
343 case 0x3c1:
344 index = s->ar_index & 0x1f;
345 if (index < 21)
346 val = s->ar[index];
347 else
348 val = 0;
349 break;
350 case 0x3c2:
351 val = s->st00;
352 break;
353 case 0x3c4:
354 val = s->sr_index;
355 break;
356 case 0x3c5:
357 val = s->sr[s->sr_index];
358#ifdef DEBUG_VGA_REG
359 Log(("vga: read SR%x = 0x%02x\n", s->sr_index, val));
360#endif
361 break;
362 case 0x3c7:
363 val = s->dac_state;
364 break;
365 case 0x3c8:
366 val = s->dac_write_index;
367 break;
368 case 0x3c9:
369 val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
370 if (++s->dac_sub_index == 3) {
371 s->dac_sub_index = 0;
372 s->dac_read_index++;
373 }
374 break;
375 case 0x3ca:
376 val = s->fcr;
377 break;
378 case 0x3cc:
379 val = s->msr;
380 break;
381 case 0x3ce:
382 val = s->gr_index;
383 break;
384 case 0x3cf:
385 val = s->gr[s->gr_index];
386#ifdef DEBUG_VGA_REG
387 Log(("vga: read GR%x = 0x%02x\n", s->gr_index, val));
388#endif
389 break;
390 case 0x3b4:
391 case 0x3d4:
392 val = s->cr_index;
393 break;
394 case 0x3b5:
395 case 0x3d5:
396 val = s->cr[s->cr_index];
397#ifdef DEBUG_VGA_REG
398 Log(("vga: read CR%x = 0x%02x\n", s->cr_index, val));
399#endif
400 break;
401 case 0x3ba:
402 case 0x3da:
403 /* just toggle to fool polling */
404 s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
405 val = s->st01;
406 s->ar_flip_flop = 0;
407 break;
408 default:
409 val = 0x00;
410 break;
411 }
412 }
413#if defined(DEBUG_VGA)
414 Log(("VGA: read addr=0x%04x data=0x%02x\n", addr, val));
415#endif
416 return val;
417}
418
419static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
420{
421 VGAState *s = (VGAState*)opaque;
422 int index;
423
424#ifdef DEBUG_VGA
425 Log(("VGA: write addr=0x%04x data=0x%02x\n", addr, val));
426#endif
427
428 /* check port range access depending on color/monochrome mode */
429 if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
430 (addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
431 Log(("VGA: previous write ignored\n"));
432 return;
433 }
434
435 switch(addr) {
436 case 0x3c0:
437 if (s->ar_flip_flop == 0) {
438 val &= 0x3f;
439 s->ar_index = val;
440 } else {
441 index = s->ar_index & 0x1f;
442 switch(index) {
443#ifndef VBOX
444 case 0x00 ... 0x0f:
445#else /* VBOX */
446 case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
447 case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
448#endif /* VBOX */
449 s->ar[index] = val & 0x3f;
450 break;
451 case 0x10:
452 s->ar[index] = val & ~0x10;
453 break;
454 case 0x11:
455 s->ar[index] = val;
456 break;
457 case 0x12:
458 s->ar[index] = val & ~0xc0;
459 break;
460 case 0x13:
461 s->ar[index] = val & ~0xf0;
462 break;
463 case 0x14:
464 s->ar[index] = val & ~0xf0;
465 break;
466 default:
467 break;
468 }
469 }
470 s->ar_flip_flop ^= 1;
471 break;
472 case 0x3c2:
473 s->msr = val & ~0x10;
474 break;
475 case 0x3c4:
476 s->sr_index = val & 7;
477 break;
478 case 0x3c5:
479#ifdef DEBUG_VGA_REG
480 Log(("vga: write SR%x = 0x%02x\n", s->sr_index, val));
481#endif
482 s->sr[s->sr_index] = val & sr_mask[s->sr_index];
483 break;
484 case 0x3c7:
485 s->dac_read_index = val;
486 s->dac_sub_index = 0;
487 s->dac_state = 3;
488 break;
489 case 0x3c8:
490 s->dac_write_index = val;
491 s->dac_sub_index = 0;
492 s->dac_state = 0;
493 break;
494 case 0x3c9:
495 s->dac_cache[s->dac_sub_index] = val;
496 if (++s->dac_sub_index == 3) {
497 memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
498 s->dac_sub_index = 0;
499 s->dac_write_index++;
500 }
501 break;
502 case 0x3ce:
503 s->gr_index = val & 0x0f;
504 break;
505 case 0x3cf:
506#ifdef DEBUG_VGA_REG
507 Log(("vga: write GR%x = 0x%02x\n", s->gr_index, val));
508#endif
509 s->gr[s->gr_index] = val & gr_mask[s->gr_index];
510 break;
511 case 0x3b4:
512 case 0x3d4:
513 s->cr_index = val;
514 break;
515 case 0x3b5:
516 case 0x3d5:
517#ifdef DEBUG_VGA_REG
518 Log(("vga: write CR%x = 0x%02x\n", s->cr_index, val));
519#endif
520 /* handle CR0-7 protection */
521 if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
522 /* can always write bit 4 of CR7 */
523 if (s->cr_index == 7)
524 s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
525 return;
526 }
527 switch(s->cr_index) {
528 case 0x01: /* horizontal display end */
529 case 0x07:
530 case 0x09:
531 case 0x0c:
532 case 0x0d:
533 case 0x12: /* veritcal display end */
534 s->cr[s->cr_index] = val;
535 break;
536
537 default:
538 s->cr[s->cr_index] = val;
539 break;
540 }
541 break;
542 case 0x3ba:
543 case 0x3da:
544 s->fcr = val & 0x10;
545 break;
546 }
547}
548
549#ifdef CONFIG_BOCHS_VBE
550static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
551{
552 VGAState *s = (VGAState*)opaque;
553 uint32_t val;
554 val = s->vbe_index;
555 return val;
556}
557
558static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
559{
560 VGAState *s = (VGAState*)opaque;
561 uint32_t val;
562
563 if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
564 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
565 switch(s->vbe_index) {
566 /* XXX: do not hardcode ? */
567 case VBE_DISPI_INDEX_XRES:
568 val = VBE_DISPI_MAX_XRES;
569 break;
570 case VBE_DISPI_INDEX_YRES:
571 val = VBE_DISPI_MAX_YRES;
572 break;
573 case VBE_DISPI_INDEX_BPP:
574 val = VBE_DISPI_MAX_BPP;
575 break;
576 default:
577 val = s->vbe_regs[s->vbe_index];
578 break;
579 }
580 } else if (s->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO) {
581 /* Reading from the port means that the old additions are requesting the number of monitors. */
582 val = 1;
583 } else {
584 val = s->vbe_regs[s->vbe_index];
585 }
586 } else {
587 val = 0;
588 }
589#ifdef DEBUG_BOCHS_VBE
590 Log(("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val));
591#endif
592 return val;
593}
594
595static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
596{
597 VGAState *s = (VGAState*)opaque;
598 s->vbe_index = val;
599}
600
601static int vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
602{
603 VGAState *s = (VGAState*)opaque;
604
605 if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
606#ifdef DEBUG_BOCHS_VBE
607 Log(("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val));
608#endif
609 switch(s->vbe_index) {
610 case VBE_DISPI_INDEX_ID:
611 if (val == VBE_DISPI_ID0 ||
612 val == VBE_DISPI_ID1 ||
613 val == VBE_DISPI_ID2 ||
614 val == VBE_DISPI_ID3 ||
615 val == VBE_DISPI_ID4) {
616 s->vbe_regs[s->vbe_index] = val;
617 }
618#ifdef VBOX
619 if (val == VBE_DISPI_ID_VBOX_VIDEO) {
620 s->vbe_regs[s->vbe_index] = val;
621 }
622#endif /* VBOX */
623 break;
624 case VBE_DISPI_INDEX_XRES:
625 if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
626 s->vbe_regs[s->vbe_index] = val;
627#ifdef KEEP_SCAN_LINE_LENGTH
628 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
629 s->vbe_line_offset = val >> 1;
630 else
631 s->vbe_line_offset = val * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
632 /* XXX: support weird bochs semantics ? */
633 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = s->vbe_line_offset;
634 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
635 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
636 s->vbe_start_addr = 0;
637#endif /* KEEP_SCAN_LINE_LENGTH defined */
638 }
639 break;
640 case VBE_DISPI_INDEX_YRES:
641 if (val <= VBE_DISPI_MAX_YRES) {
642 s->vbe_regs[s->vbe_index] = val;
643#ifdef KEEP_SCAN_LINE_LENGTH
644 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = val;
645 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
646 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
647 s->vbe_start_addr = 0;
648#endif /* KEEP_SCAN_LINE_LENGTH defined */
649 }
650 break;
651 case VBE_DISPI_INDEX_BPP:
652 if (val == 0)
653 val = 8;
654 if (val == 4 || val == 8 || val == 15 ||
655 val == 16 || val == 24 || val == 32) {
656 s->vbe_regs[s->vbe_index] = val;
657#ifdef KEEP_SCAN_LINE_LENGTH
658 if (val == 4)
659 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
660 else
661 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((val + 7) >> 3);
662 /* XXX: support weird bochs semantics ? */
663 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = s->vbe_line_offset;
664 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
665 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
666 s->vbe_start_addr = 0;
667#endif /* KEEP_SCAN_LINE_LENGTH defined */
668 }
669 break;
670 case VBE_DISPI_INDEX_BANK:
671 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
672 val &= (s->vbe_bank_mask >> 2);
673 } else {
674 val &= s->vbe_bank_mask;
675 }
676 val &= s->vbe_bank_mask;
677 s->vbe_regs[s->vbe_index] = val;
678 s->bank_offset = (val << 16);
679 break;
680 case VBE_DISPI_INDEX_ENABLE:
681#ifndef IN_RING3
682 return VINF_IOM_HC_IOPORT_WRITE;
683#else
684 if ((val & VBE_DISPI_ENABLED) &&
685 !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
686 int h, shift_control;
687#ifdef VBOX
688 /* Check the values before we screw up with a resolution which is too big or small. */
689 size_t cb = s->vbe_regs[VBE_DISPI_INDEX_XRES];
690 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
691 cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
692 else
693 cb = s->vbe_regs[VBE_DISPI_INDEX_XRES] * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
694 cb *= s->vbe_regs[VBE_DISPI_INDEX_YRES];
695#ifndef KEEP_SCAN_LINE_LENGTH
696 if ( !s->vbe_regs[VBE_DISPI_INDEX_XRES]
697 || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
698 || cb > s->vram_size)
699 {
700 AssertMsgFailed(("XRES=%d YRES=%d cb=%d vram_size=%d\n",
701 s->vbe_regs[VBE_DISPI_INDEX_XRES], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
702 return VINF_SUCCESS; /* Note: silent failure like before */
703 }
704#else /* KEEP_SCAN_LINE_LENGTH defined */
705 if ( !s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH]
706 || !s->vbe_regs[VBE_DISPI_INDEX_YRES]
707 || cb > s->vram_size)
708 {
709 AssertMsgFailed(("VIRT WIDTH=%d YRES=%d cb=%d vram_size=%d\n",
710 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH], s->vbe_regs[VBE_DISPI_INDEX_YRES], cb, s->vram_size));
711 return VINF_SUCCESS; /* Note: silent failure like before */
712 }
713#endif /* KEEP_SCAN_LINE_LENGTH defined */
714#endif /* VBOX */
715
716#ifndef KEEP_SCAN_LINE_LENGTH
717 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
718 s->vbe_regs[VBE_DISPI_INDEX_XRES];
719 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
720 s->vbe_regs[VBE_DISPI_INDEX_YRES];
721 s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
722 s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
723
724 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
725 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
726 else
727 s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] *
728 ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
729 s->vbe_start_addr = 0;
730#endif /* KEEP_SCAN_LINE_LENGTH not defined */
731
732 /* clear the screen (should be done in BIOS) */
733 if (!(val & VBE_DISPI_NOCLEARMEM)) {
734#ifndef VBOX
735 memset(s->vram_ptr, 0,
736 s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
737#else /* VBOX */
738 memset(CTXSUFF(s->vram_ptr), 0,
739 s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
740#endif /* VBOX */
741 }
742
743 /* we initialize the VGA graphic mode (should be done
744 in BIOS) */
745 s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
746 s->cr[0x17] |= 3; /* no CGA modes */
747 s->cr[0x13] = s->vbe_line_offset >> 3;
748 /* width */
749 s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
750 /* height (only meaningful if < 1024) */
751 h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
752 s->cr[0x12] = h;
753 s->cr[0x07] = (s->cr[0x07] & ~0x42) |
754 ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
755 /* line compare to 1023 */
756 s->cr[0x18] = 0xff;
757 s->cr[0x07] |= 0x10;
758 s->cr[0x09] |= 0x40;
759
760 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
761 shift_control = 0;
762 s->sr[0x01] &= ~8; /* no double line */
763 } else {
764 shift_control = 2;
765 s->sr[4] |= 0x08; /* set chain 4 mode */
766 s->sr[2] |= 0x0f; /* activate all planes */
767 }
768 s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
769 s->cr[0x09] &= ~0x9f; /* no double scan */
770#ifdef VBOX
771 /* sunlover 30.05.2007
772 * The ar_index remains with bit 0x20 cleared after a switch from fullscreen
773 * DOS mode on Windows XP guest. That leads to GMODE_BLANK in vga_update_display.
774 * But the VBE mode is graphics, so not a blank anymore.
775 */
776 s->ar_index |= 0x20;
777#endif /* VBOX */
778 } else {
779 /* XXX: the bios should do that */
780#ifdef VBOX
781 /* sunlover 21.12.2006
782 * Here is probably more to reset. When this was executed in GC
783 * then the *update* functions could not detect a mode change.
784 * Or may be these update function should take the s->vbe_regs[s->vbe_index]
785 * into account when detecting a mode change.
786 *
787 * The 'mode reset not detected' problem is now fixed by executing the
788 * VBE_DISPI_INDEX_ENABLE case always in RING3 in order to call the
789 * LFBChange callback.
790 */
791#endif /* VBOX */
792 s->bank_offset = 0;
793 }
794 s->vbe_regs[s->vbe_index] = val;
795 /*
796 * LFB video mode is either disabled or changed. This notification
797 * is used by the display to disable VBVA.
798 */
799 s->pDrv->pfnLFBModeChange(s->pDrv, (val & VBE_DISPI_ENABLED) != 0);
800 break;
801#endif /* IN_RING3 */
802 case VBE_DISPI_INDEX_VIRT_WIDTH:
803 {
804 int w, h, line_offset;
805
806 if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
807 return VINF_SUCCESS;
808 w = val;
809 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
810 line_offset = w >> 1;
811 else
812 line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
813 h = s->vram_size / line_offset;
814 /* XXX: support weird bochs semantics ? */
815 if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
816 return VINF_SUCCESS;
817 s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
818 s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
819 s->vbe_line_offset = line_offset;
820 }
821 break;
822 case VBE_DISPI_INDEX_X_OFFSET:
823 case VBE_DISPI_INDEX_Y_OFFSET:
824 {
825 int x;
826 s->vbe_regs[s->vbe_index] = val;
827 s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
828 x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
829 if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
830 s->vbe_start_addr += x >> 1;
831 else
832 s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
833 s->vbe_start_addr >>= 2;
834 }
835 break;
836 case VBE_DISPI_INDEX_VBOX_VIDEO:
837#ifdef VBOX
838#ifndef IN_RING3
839 return VINF_IOM_HC_IOPORT_WRITE;
840#else
841 /* Changes in the VGA device are minimal. The device is bypassed. The driver does all work. */
842 if (val == VBOX_VIDEO_DISABLE_ADAPTER_MEMORY)
843 {
844 s->pDrv->pfnProcessAdapterData(s->pDrv, NULL, 0);
845 }
846 else if (val == VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY)
847 {
848 s->pDrv->pfnProcessAdapterData(s->pDrv, s->CTXSUFF(vram_ptr), s->vram_size);
849 }
850 else if ((val & 0xFFFF0000) == VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE)
851 {
852 s->pDrv->pfnProcessDisplayData(s->pDrv, s->CTXSUFF(vram_ptr), val & 0xFFFF);
853 }
854#endif /* IN_RING3 */
855#endif /* VBOX */
856 break;
857 default:
858 break;
859 }
860 }
861 return VINF_SUCCESS;
862}
863#endif
864
865/* called for accesses between 0xa0000 and 0xc0000 */
866#ifdef VBOX
867static
868#endif /* VBOX */
869uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr)
870{
871 VGAState *s = (VGAState*)opaque;
872 int memory_map_mode, plane;
873 uint32_t ret;
874
875#ifdef DEBUG_VGA_MEM
876 Log(("vga: read [0x%x] -> ", addr));
877#endif
878 /* convert to VGA memory offset */
879 memory_map_mode = (s->gr[6] >> 2) & 3;
880 addr &= 0x1ffff;
881 switch(memory_map_mode) {
882 case 0:
883 break;
884 case 1:
885 if (addr >= 0x10000)
886 return 0xff;
887 addr += s->bank_offset;
888 break;
889 case 2:
890 addr -= 0x10000;
891 if (addr >= 0x8000)
892 return 0xff;
893 break;
894 default:
895 case 3:
896 addr -= 0x18000;
897 if (addr >= 0x8000)
898 return 0xff;
899 break;
900 }
901
902#ifdef IN_GC
903 if (addr >= VGA_MAPPING_SIZE)
904 return VINF_IOM_HC_MMIO_WRITE;
905#endif
906
907 if (s->sr[4] & 0x08) {
908 /* chain 4 mode : simplest access */
909#ifndef VBOX
910 ret = s->vram_ptr[addr];
911#else /* VBOX */
912 ret = s->CTXSUFF(vram_ptr)[addr];
913#endif /* VBOX */
914 } else if (!(s->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
915 /* odd/even mode (aka text mode mapping) */
916 plane = (s->gr[4] & 2) | (addr & 1);
917#ifndef VBOX
918 ret = s->vram_ptr[((addr & ~1) << 1) | plane];
919#else /* VBOX */
920 /* See the comment for a similar line in vga_mem_writeb. */
921 ret = s->CTXSUFF(vram_ptr)[((addr & ~1) << 2) | plane];
922#endif /* VBOX */
923 } else {
924 /* standard VGA latched access */
925#ifndef VBOX
926 s->latch = ((uint32_t *)s->vram_ptr)[addr];
927#else /* VBOX && IN_GC */
928 s->latch = ((uint32_t *)s->CTXSUFF(vram_ptr))[addr];
929#endif /* VBOX && IN_GC */
930
931 if (!(s->gr[5] & 0x08)) {
932 /* read mode 0 */
933 plane = s->gr[4];
934 ret = GET_PLANE(s->latch, plane);
935 } else {
936 /* read mode 1 */
937 ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
938 ret |= ret >> 16;
939 ret |= ret >> 8;
940 ret = (~ret) & 0xff;
941 }
942 }
943#ifdef DEBUG_VGA_MEM
944 Log((" 0x%02x\n", ret));
945#endif
946 return ret;
947}
948
949#ifndef VBOX
950static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr)
951{
952 uint32_t v;
953#ifdef TARGET_WORDS_BIGENDIAN
954 v = vga_mem_readb(opaque, addr) << 8;
955 v |= vga_mem_readb(opaque, addr + 1);
956#else
957 v = vga_mem_readb(opaque, addr);
958 v |= vga_mem_readb(opaque, addr + 1) << 8;
959#endif
960 return v;
961}
962
963static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr)
964{
965 uint32_t v;
966#ifdef TARGET_WORDS_BIGENDIAN
967 v = vga_mem_readb(opaque, addr) << 24;
968 v |= vga_mem_readb(opaque, addr + 1) << 16;
969 v |= vga_mem_readb(opaque, addr + 2) << 8;
970 v |= vga_mem_readb(opaque, addr + 3);
971#else
972 v = vga_mem_readb(opaque, addr);
973 v |= vga_mem_readb(opaque, addr + 1) << 8;
974 v |= vga_mem_readb(opaque, addr + 2) << 16;
975 v |= vga_mem_readb(opaque, addr + 3) << 24;
976#endif
977 return v;
978}
979#endif /* !VBOX */
980
981/* called for accesses between 0xa0000 and 0xc0000 */
982#ifdef VBOX
983static
984#endif /* VBOX */
985int vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
986{
987 VGAState *s = (VGAState*)opaque;
988 int memory_map_mode, plane, write_mode, b, func_select, mask;
989 uint32_t write_mask, bit_mask, set_mask;
990
991#ifdef DEBUG_VGA_MEM
992 Log(("vga: [0x%x] = 0x%02x\n", addr, val));
993#endif
994 /* convert to VGA memory offset */
995 memory_map_mode = (s->gr[6] >> 2) & 3;
996 addr &= 0x1ffff;
997 switch(memory_map_mode) {
998 case 0:
999 break;
1000 case 1:
1001 if (addr >= 0x10000)
1002 return VINF_SUCCESS;
1003 addr += s->bank_offset;
1004 break;
1005 case 2:
1006 addr -= 0x10000;
1007 if (addr >= 0x8000)
1008 return VINF_SUCCESS;
1009 break;
1010 default:
1011 case 3:
1012 addr -= 0x18000;
1013 if (addr >= 0x8000)
1014 return VINF_SUCCESS;
1015 break;
1016 }
1017
1018 if (s->sr[4] & 0x08) {
1019 /* chain 4 mode : simplest access */
1020 plane = addr & 3;
1021 mask = (1 << plane);
1022 if (s->sr[2] & mask) {
1023#ifndef VBOX
1024 s->vram_ptr[addr] = val;
1025#else /* VBOX */
1026#ifdef IN_GC
1027 if (addr >= VGA_MAPPING_SIZE)
1028 return VINF_IOM_HC_MMIO_WRITE;
1029#else
1030 if (addr >= s->vram_size)
1031 {
1032 AssertMsgFailed(("addr=%VGp - this needs to be done in HC! bank_offset=%08x memory_map_mode=%d\n",
1033 addr, s->bank_offset, memory_map_mode));
1034 return VINF_SUCCESS;
1035 }
1036#endif
1037 s->CTXSUFF(vram_ptr)[addr] = val;
1038#endif /* VBOX */
1039#ifdef DEBUG_VGA_MEM
1040 Log(("vga: chain4: [0x%x]\n", addr));
1041#endif
1042 s->plane_updated |= mask; /* only used to detect font change */
1043#ifndef VBOX
1044 cpu_physical_memory_set_dirty(s->vram_offset + addr);
1045#else /* VBOX */
1046 vga_set_dirty(s, addr);
1047#endif /* VBOX */
1048 }
1049 } else if (!(s->sr[4] & 0x04)) { /* Host access is controlled by SR4, not GR5! */
1050 /* odd/even mode (aka text mode mapping) */
1051 plane = (s->gr[4] & 2) | (addr & 1);
1052 mask = (1 << plane);
1053 if (s->sr[2] & mask) {
1054#ifndef VBOX
1055 addr = ((addr & ~1) << 1) | plane;
1056#else
1057 /* 'addr' is offset in a plane, bit 0 selects the plane.
1058 * Mask the bit 0, convert plane index to vram offset,
1059 * that is multiply by the number of planes,
1060 * and select the plane byte in the vram offset.
1061 */
1062 addr = ((addr & ~1) << 2) | plane;
1063#endif /* VBOX */
1064#ifndef VBOX
1065 s->vram_ptr[addr] = val;
1066#else /* VBOX */
1067#ifdef IN_GC
1068 if (addr >= VGA_MAPPING_SIZE)
1069 return VINF_IOM_HC_MMIO_WRITE;
1070#else
1071 if (addr >= s->vram_size)
1072 {
1073 AssertMsgFailed(("addr=%VGp - this needs to be done in HC! bank_offset=%08x memory_map_mode=%d\n",
1074 addr, s->bank_offset, memory_map_mode));
1075 return VINF_SUCCESS;
1076 }
1077#endif
1078 s->CTXSUFF(vram_ptr)[addr] = val;
1079#endif /* VBOX */
1080#ifdef DEBUG_VGA_MEM
1081 Log(("vga: odd/even: [0x%x]\n", addr));
1082#endif
1083 s->plane_updated |= mask; /* only used to detect font change */
1084#ifndef VBOX
1085 cpu_physical_memory_set_dirty(s->vram_offset + addr);
1086#else /* VBOX */
1087 vga_set_dirty(s, addr);
1088#endif /* VBOX */
1089 }
1090 } else {
1091#ifdef IN_GC
1092 if (addr * 4 >= VGA_MAPPING_SIZE)
1093 return VINF_IOM_HC_MMIO_WRITE;
1094#else
1095 if (addr * 4 >= s->vram_size)
1096 {
1097 AssertMsgFailed(("addr=%VGp - this needs to be done in HC! bank_offset=%08x memory_map_mode=%d\n",
1098 addr * 4, s->bank_offset, memory_map_mode));
1099 return VINF_SUCCESS;
1100 }
1101#endif
1102
1103 /* standard VGA latched access */
1104 write_mode = s->gr[5] & 3;
1105 switch(write_mode) {
1106 default:
1107 case 0:
1108 /* rotate */
1109 b = s->gr[3] & 7;
1110 val = ((val >> b) | (val << (8 - b))) & 0xff;
1111 val |= val << 8;
1112 val |= val << 16;
1113
1114 /* apply set/reset mask */
1115 set_mask = mask16[s->gr[1]];
1116 val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
1117 bit_mask = s->gr[8];
1118 break;
1119 case 1:
1120 val = s->latch;
1121 goto do_write;
1122 case 2:
1123 val = mask16[val & 0x0f];
1124 bit_mask = s->gr[8];
1125 break;
1126 case 3:
1127 /* rotate */
1128 b = s->gr[3] & 7;
1129 val = (val >> b) | (val << (8 - b));
1130
1131 bit_mask = s->gr[8] & val;
1132 val = mask16[s->gr[0]];
1133 break;
1134 }
1135
1136 /* apply logical operation */
1137 func_select = s->gr[3] >> 3;
1138 switch(func_select) {
1139 case 0:
1140 default:
1141 /* nothing to do */
1142 break;
1143 case 1:
1144 /* and */
1145 val &= s->latch;
1146 break;
1147 case 2:
1148 /* or */
1149 val |= s->latch;
1150 break;
1151 case 3:
1152 /* xor */
1153 val ^= s->latch;
1154 break;
1155 }
1156
1157 /* apply bit mask */
1158 bit_mask |= bit_mask << 8;
1159 bit_mask |= bit_mask << 16;
1160 val = (val & bit_mask) | (s->latch & ~bit_mask);
1161
1162 do_write:
1163 /* mask data according to sr[2] */
1164 mask = s->sr[2];
1165 s->plane_updated |= mask; /* only used to detect font change */
1166 write_mask = mask16[mask];
1167#ifndef VBOX
1168 ((uint32_t *)s->vram_ptr)[addr] =
1169 (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) |
1170 (val & write_mask);
1171#else /* VBOX */
1172 ((uint32_t *)s->CTXSUFF(vram_ptr))[addr] =
1173 (((uint32_t *)s->CTXSUFF(vram_ptr))[addr] & ~write_mask) |
1174 (val & write_mask);
1175#endif /* VBOX */
1176#ifdef DEBUG_VGA_MEM
1177 Log(("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n",
1178 addr * 4, write_mask, val));
1179#endif
1180#ifndef VBOX
1181 cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2));
1182#else /* VBOX */
1183 vga_set_dirty(s, (addr << 2));
1184#endif /* VBOX */
1185 }
1186
1187 return VINF_SUCCESS;
1188}
1189
1190#ifndef VBOX
1191static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
1192{
1193#ifdef TARGET_WORDS_BIGENDIAN
1194 vga_mem_writeb(opaque, addr, (val >> 8) & 0xff);
1195 vga_mem_writeb(opaque, addr + 1, val & 0xff);
1196#else
1197 vga_mem_writeb(opaque, addr, val & 0xff);
1198 vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
1199#endif
1200}
1201
1202static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
1203{
1204#ifdef TARGET_WORDS_BIGENDIAN
1205 vga_mem_writeb(opaque, addr, (val >> 24) & 0xff);
1206 vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
1207 vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
1208 vga_mem_writeb(opaque, addr + 3, val & 0xff);
1209#else
1210 vga_mem_writeb(opaque, addr, val & 0xff);
1211 vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
1212 vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
1213 vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
1214#endif
1215}
1216#endif /* !VBOX */
1217
1218#if !defined(VBOX) || defined(IN_RING3)
1219typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
1220 const uint8_t *font_ptr, int h,
1221 uint32_t fgcol, uint32_t bgcol);
1222typedef void vga_draw_glyph9_func(uint8_t *d, int linesize,
1223 const uint8_t *font_ptr, int h,
1224 uint32_t fgcol, uint32_t bgcol, int dup9);
1225typedef void vga_draw_line_func(VGAState *s1, uint8_t *d,
1226 const uint8_t *s, int width);
1227
1228static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
1229{
1230 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
1231}
1232
1233static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
1234{
1235 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
1236}
1237
1238static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
1239{
1240 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
1241}
1242
1243static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
1244{
1245 return (r << 16) | (g << 8) | b;
1246}
1247
1248#define DEPTH 8
1249#include "DevVGATmpl.h"
1250
1251#define DEPTH 15
1252#include "DevVGATmpl.h"
1253
1254#define DEPTH 16
1255#include "DevVGATmpl.h"
1256
1257#define DEPTH 32
1258#include "DevVGATmpl.h"
1259
1260static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
1261{
1262 unsigned int col;
1263 col = rgb_to_pixel8(r, g, b);
1264 col |= col << 8;
1265 col |= col << 16;
1266 return col;
1267}
1268
1269static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
1270{
1271 unsigned int col;
1272 col = rgb_to_pixel15(r, g, b);
1273 col |= col << 16;
1274 return col;
1275}
1276
1277static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
1278{
1279 unsigned int col;
1280 col = rgb_to_pixel16(r, g, b);
1281 col |= col << 16;
1282 return col;
1283}
1284
1285static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
1286{
1287 unsigned int col;
1288 col = rgb_to_pixel32(r, g, b);
1289 return col;
1290}
1291
1292/* return true if the palette was modified */
1293static int update_palette16(VGAState *s)
1294{
1295 int full_update, i;
1296 uint32_t v, col, *palette;
1297
1298 full_update = 0;
1299 palette = s->last_palette;
1300 for(i = 0; i < 16; i++) {
1301 v = s->ar[i];
1302 if (s->ar[0x10] & 0x80)
1303 v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
1304 else
1305 v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
1306 v = v * 3;
1307 col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
1308 c6_to_8(s->palette[v + 1]),
1309 c6_to_8(s->palette[v + 2]));
1310 if (col != palette[i]) {
1311 full_update = 1;
1312 palette[i] = col;
1313 }
1314 }
1315 return full_update;
1316}
1317
1318/* return true if the palette was modified */
1319static int update_palette256(VGAState *s)
1320{
1321 int full_update, i;
1322 uint32_t v, col, *palette;
1323 int wide_dac;
1324
1325 full_update = 0;
1326 palette = s->last_palette;
1327 v = 0;
1328 wide_dac = (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC))
1329 == (VBE_DISPI_ENABLED | VBE_DISPI_8BIT_DAC);
1330 for(i = 0; i < 256; i++) {
1331 if (wide_dac)
1332 col = s->rgb_to_pixel(s->palette[v],
1333 s->palette[v + 1],
1334 s->palette[v + 2]);
1335 else
1336 col = s->rgb_to_pixel(c6_to_8(s->palette[v]),
1337 c6_to_8(s->palette[v + 1]),
1338 c6_to_8(s->palette[v + 2]));
1339 if (col != palette[i]) {
1340 full_update = 1;
1341 palette[i] = col;
1342 }
1343 v += 3;
1344 }
1345 return full_update;
1346}
1347
1348static void vga_get_offsets(VGAState *s,
1349 uint32_t *pline_offset,
1350 uint32_t *pstart_addr,
1351 uint32_t *pline_compare)
1352{
1353 uint32_t start_addr, line_offset, line_compare;
1354#ifdef CONFIG_BOCHS_VBE
1355 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1356 line_offset = s->vbe_line_offset;
1357 start_addr = s->vbe_start_addr;
1358 line_compare = 65535;
1359 } else
1360#endif
1361 {
1362 /* compute line_offset in bytes */
1363 line_offset = s->cr[0x13];
1364 line_offset <<= 3;
1365#ifdef VBOX
1366 if (!(s->cr[0x14] & 0x40) && !(s->cr[0x17] & 0x40))
1367 {
1368 /* Word mode. Used for odd/even modes. */
1369 line_offset *= 2;
1370 }
1371#endif /* VBOX */
1372
1373 /* starting address */
1374 start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
1375
1376 /* line compare */
1377 line_compare = s->cr[0x18] |
1378 ((s->cr[0x07] & 0x10) << 4) |
1379 ((s->cr[0x09] & 0x40) << 3);
1380 }
1381 *pline_offset = line_offset;
1382 *pstart_addr = start_addr;
1383 *pline_compare = line_compare;
1384}
1385
1386/* update start_addr and line_offset. Return TRUE if modified */
1387static int update_basic_params(VGAState *s)
1388{
1389 int full_update;
1390 uint32_t start_addr, line_offset, line_compare;
1391
1392 full_update = 0;
1393
1394 s->get_offsets(s, &line_offset, &start_addr, &line_compare);
1395
1396 if (line_offset != s->line_offset ||
1397 start_addr != s->start_addr ||
1398 line_compare != s->line_compare) {
1399 s->line_offset = line_offset;
1400 s->start_addr = start_addr;
1401 s->line_compare = line_compare;
1402 full_update = 1;
1403 }
1404 return full_update;
1405}
1406
1407static inline int get_depth_index(int depth)
1408{
1409 switch(depth) {
1410 default:
1411 case 8:
1412 return 0;
1413 case 15:
1414 return 1;
1415 case 16:
1416 return 2;
1417 case 32:
1418 return 3;
1419 }
1420}
1421
1422static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
1423 vga_draw_glyph8_8,
1424 vga_draw_glyph8_16,
1425 vga_draw_glyph8_16,
1426 vga_draw_glyph8_32,
1427};
1428
1429static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
1430 vga_draw_glyph16_8,
1431 vga_draw_glyph16_16,
1432 vga_draw_glyph16_16,
1433 vga_draw_glyph16_32,
1434};
1435
1436static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
1437 vga_draw_glyph9_8,
1438 vga_draw_glyph9_16,
1439 vga_draw_glyph9_16,
1440 vga_draw_glyph9_32,
1441};
1442
1443static const uint8_t cursor_glyph[32 * 4] = {
1444 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1445 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1446 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1447 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1448 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1449 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1450 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1451 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1452 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1453 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1454 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1455 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1456 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1457 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1458 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1459 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1460};
1461
1462/*
1463 * Text mode update
1464 * Missing:
1465 * - double scan
1466 * - double width
1467 * - underline
1468 * - flashing
1469 */
1470#ifndef VBOX
1471static void vga_draw_text(VGAState *s, int full_update)
1472#else
1473static int vga_draw_text(VGAState *s, int full_update)
1474#endif /* !VBOX */
1475{
1476 int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr;
1477 int cx_min, cx_max, linesize, x_incr;
1478 uint32_t offset, fgcol, bgcol, v, cursor_offset;
1479 uint8_t *d1, *d, *src, *s1, *dest, *cursor_ptr;
1480 const uint8_t *font_ptr, *font_base[2];
1481 int dup9, line_offset, depth_index;
1482 uint32_t *palette;
1483 uint32_t *ch_attr_ptr;
1484 vga_draw_glyph8_func *vga_draw_glyph8;
1485 vga_draw_glyph9_func *vga_draw_glyph9;
1486
1487 full_update |= update_palette16(s);
1488 palette = s->last_palette;
1489
1490 /* compute font data address (in plane 2) */
1491 v = s->sr[3];
1492 offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
1493 if (offset != s->font_offsets[0]) {
1494 s->font_offsets[0] = offset;
1495 full_update = 1;
1496 }
1497#ifndef VBOX
1498 font_base[0] = s->vram_ptr + offset;
1499#else /* VBOX */
1500 font_base[0] = s->CTXSUFF(vram_ptr) + offset;
1501#endif /* VBOX */
1502
1503 offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
1504#ifndef VBOX
1505 font_base[1] = s->vram_ptr + offset;
1506#else /* VBOX */
1507 font_base[1] = s->CTXSUFF(vram_ptr) + offset;
1508#endif /* VBOX */
1509 if (offset != s->font_offsets[1]) {
1510 s->font_offsets[1] = offset;
1511 full_update = 1;
1512 }
1513 if (s->plane_updated & (1 << 2)) {
1514 /* if the plane 2 was modified since the last display, it
1515 indicates the font may have been modified */
1516 s->plane_updated = 0;
1517 full_update = 1;
1518 }
1519 full_update |= update_basic_params(s);
1520
1521 line_offset = s->line_offset;
1522#ifndef VBOX
1523 s1 = s->vram_ptr + (s->start_addr * 4);
1524#else /* VBOX */
1525 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 8);
1526#endif /* VBOX */
1527
1528 /* total width & height */
1529 cheight = (s->cr[9] & 0x1f) + 1;
1530 cw = 8;
1531 if (!(s->sr[1] & 0x01))
1532 cw = 9;
1533 if (s->sr[1] & 0x08)
1534 cw = 16; /* NOTE: no 18 pixel wide */
1535#ifndef VBOX
1536 x_incr = cw * ((s->ds->depth + 7) >> 3);
1537#else /* VBOX */
1538 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
1539#endif /* VBOX */
1540 width = (s->cr[0x01] + 1);
1541 if (s->cr[0x06] == 100) {
1542 /* ugly hack for CGA 160x100x16 - explain me the logic */
1543 height = 100;
1544 } else {
1545 height = s->cr[0x12] |
1546 ((s->cr[0x07] & 0x02) << 7) |
1547 ((s->cr[0x07] & 0x40) << 3);
1548 height = (height + 1) / cheight;
1549 }
1550 if ((height * width) > CH_ATTR_SIZE) {
1551 /* better than nothing: exit if transient size is too big */
1552#ifndef VBOX
1553 return;
1554#else
1555 return VINF_SUCCESS;
1556#endif /* VBOX */
1557 }
1558
1559 if (width != (int)s->last_width || height != (int)s->last_height ||
1560 cw != s->last_cw || cheight != s->last_ch) {
1561 s->last_scr_width = width * cw;
1562 s->last_scr_height = height * cheight;
1563#ifndef VBOX
1564 dpy_resize(s->ds, s->last_scr_width, s->last_scr_height);
1565 s->last_width = width;
1566 s->last_height = height;
1567 s->last_ch = cheight;
1568 s->last_cw = cw;
1569 full_update = 1;
1570#else /* VBOX */
1571 /* For text modes the direct use of guest VRAM is not implemented, so bpp and cbLine are 0 here. */
1572 int rc = s->pDrv->pfnResize(s->pDrv, 0, NULL, 0, s->last_scr_width, s->last_scr_height);
1573 s->last_width = width;
1574 s->last_height = height;
1575 s->last_ch = cheight;
1576 s->last_cw = cw;
1577 full_update = 1;
1578 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1579 return rc;
1580 AssertRC(rc);
1581#endif /* VBOX */
1582 }
1583 cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr;
1584 if (cursor_offset != s->cursor_offset ||
1585 s->cr[0xa] != s->cursor_start ||
1586 s->cr[0xb] != s->cursor_end) {
1587 /* if the cursor position changed, we update the old and new
1588 chars */
1589 if (s->cursor_offset < CH_ATTR_SIZE)
1590 s->last_ch_attr[s->cursor_offset] = ~0;
1591 if (cursor_offset < CH_ATTR_SIZE)
1592 s->last_ch_attr[cursor_offset] = ~0;
1593 s->cursor_offset = cursor_offset;
1594 s->cursor_start = s->cr[0xa];
1595 s->cursor_end = s->cr[0xb];
1596 }
1597#ifndef VBOX
1598 cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
1599
1600 depth_index = get_depth_index(s->ds->depth);
1601#else /* VBOX */
1602 cursor_ptr = s->CTXSUFF(vram_ptr) + (s->start_addr + cursor_offset) * 8;
1603 depth_index = get_depth_index(s->pDrv->cBits);
1604#endif /* VBOX */
1605 if (cw == 16)
1606 vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
1607 else
1608 vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
1609 vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
1610
1611#ifndef VBOX
1612 dest = s->ds->data;
1613 linesize = s->ds->linesize;
1614#else /* VBOX */
1615 dest = s->pDrv->pu8Data;
1616 linesize = s->pDrv->cbScanline;
1617#endif /* VBOX */
1618 ch_attr_ptr = s->last_ch_attr;
1619
1620 for(cy = 0; cy < height; cy++) {
1621 d1 = dest;
1622 src = s1;
1623 cx_min = width;
1624 cx_max = -1;
1625 for(cx = 0; cx < width; cx++) {
1626 ch_attr = *(uint16_t *)src;
1627 if (full_update || ch_attr != (int)*ch_attr_ptr) {
1628 if (cx < cx_min)
1629 cx_min = cx;
1630 if (cx > cx_max)
1631 cx_max = cx;
1632 *ch_attr_ptr = ch_attr;
1633#ifdef WORDS_BIGENDIAN
1634 ch = ch_attr >> 8;
1635 cattr = ch_attr & 0xff;
1636#else
1637 ch = ch_attr & 0xff;
1638 cattr = ch_attr >> 8;
1639#endif
1640 font_ptr = font_base[(cattr >> 3) & 1];
1641 font_ptr += 32 * 4 * ch;
1642 bgcol = palette[cattr >> 4];
1643 fgcol = palette[cattr & 0x0f];
1644 if (cw != 9) {
1645 vga_draw_glyph8(d1, linesize,
1646 font_ptr, cheight, fgcol, bgcol);
1647 } else {
1648 dup9 = 0;
1649 if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
1650 dup9 = 1;
1651 vga_draw_glyph9(d1, linesize,
1652 font_ptr, cheight, fgcol, bgcol, dup9);
1653 }
1654 if (src == cursor_ptr &&
1655 !(s->cr[0x0a] & 0x20)) {
1656 int line_start, line_last, h;
1657 /* draw the cursor */
1658 line_start = s->cr[0x0a] & 0x1f;
1659 line_last = s->cr[0x0b] & 0x1f;
1660 /* XXX: check that */
1661 if (line_last > cheight - 1)
1662 line_last = cheight - 1;
1663 if (line_last >= line_start && line_start < cheight) {
1664 h = line_last - line_start + 1;
1665 d = d1 + linesize * line_start;
1666 if (cw != 9) {
1667 vga_draw_glyph8(d, linesize,
1668 cursor_glyph, h, fgcol, bgcol);
1669 } else {
1670 vga_draw_glyph9(d, linesize,
1671 cursor_glyph, h, fgcol, bgcol, 1);
1672 }
1673 }
1674 }
1675 }
1676 d1 += x_incr;
1677#ifndef VBOX
1678 src += 4;
1679#else
1680 src += 8; /* Every second byte of a plane is used in text mode. */
1681#endif
1682
1683 ch_attr_ptr++;
1684 }
1685#ifndef VBOX
1686 if (cx_max != -1) {
1687 dpy_update(s->ds, cx_min * cw, cy * cheight,
1688 (cx_max - cx_min + 1) * cw, cheight);
1689 }
1690#else
1691 if (cx_max != -1)
1692 s->pDrv->pfnUpdateRect(s->pDrv, cx_min * cw, cy * cheight, (cx_max - cx_min + 1) * cw, cheight);
1693#endif
1694 dest += linesize * cheight;
1695 s1 += line_offset;
1696 }
1697#ifdef VBOX
1698 return VINF_SUCCESS;
1699#endif /* VBOX */
1700}
1701
1702enum {
1703 VGA_DRAW_LINE2,
1704 VGA_DRAW_LINE2D2,
1705 VGA_DRAW_LINE4,
1706 VGA_DRAW_LINE4D2,
1707 VGA_DRAW_LINE8D2,
1708 VGA_DRAW_LINE8,
1709 VGA_DRAW_LINE15,
1710 VGA_DRAW_LINE16,
1711 VGA_DRAW_LINE24,
1712 VGA_DRAW_LINE32,
1713 VGA_DRAW_LINE_NB
1714};
1715
1716static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
1717 vga_draw_line2_8,
1718 vga_draw_line2_16,
1719 vga_draw_line2_16,
1720 vga_draw_line2_32,
1721
1722 vga_draw_line2d2_8,
1723 vga_draw_line2d2_16,
1724 vga_draw_line2d2_16,
1725 vga_draw_line2d2_32,
1726
1727 vga_draw_line4_8,
1728 vga_draw_line4_16,
1729 vga_draw_line4_16,
1730 vga_draw_line4_32,
1731
1732 vga_draw_line4d2_8,
1733 vga_draw_line4d2_16,
1734 vga_draw_line4d2_16,
1735 vga_draw_line4d2_32,
1736
1737 vga_draw_line8d2_8,
1738 vga_draw_line8d2_16,
1739 vga_draw_line8d2_16,
1740 vga_draw_line8d2_32,
1741
1742 vga_draw_line8_8,
1743 vga_draw_line8_16,
1744 vga_draw_line8_16,
1745 vga_draw_line8_32,
1746
1747 vga_draw_line15_8,
1748 vga_draw_line15_15,
1749 vga_draw_line15_16,
1750 vga_draw_line15_32,
1751
1752 vga_draw_line16_8,
1753 vga_draw_line16_15,
1754 vga_draw_line16_16,
1755 vga_draw_line16_32,
1756
1757 vga_draw_line24_8,
1758 vga_draw_line24_15,
1759 vga_draw_line24_16,
1760 vga_draw_line24_32,
1761
1762 vga_draw_line32_8,
1763 vga_draw_line32_15,
1764 vga_draw_line32_16,
1765 vga_draw_line32_32,
1766};
1767
1768static int vga_get_bpp(VGAState *s)
1769{
1770 int ret;
1771#ifdef CONFIG_BOCHS_VBE
1772 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1773 ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
1774 } else
1775#endif
1776 {
1777 ret = 0;
1778 }
1779 return ret;
1780}
1781
1782static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight)
1783{
1784 int width, height;
1785#ifdef CONFIG_BOCHS_VBE
1786 if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
1787 width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
1788 height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
1789 } else
1790#endif
1791 {
1792 width = (s->cr[0x01] + 1) * 8;
1793 height = s->cr[0x12] |
1794 ((s->cr[0x07] & 0x02) << 7) |
1795 ((s->cr[0x07] & 0x40) << 3);
1796 height = (height + 1);
1797 }
1798 *pwidth = width;
1799 *pheight = height;
1800}
1801
1802#ifndef VBOX
1803void vga_invalidate_scanlines(VGAState *s, int y1, int y2)
1804{
1805 int y;
1806 if (y1 >= VGA_MAX_HEIGHT)
1807 return;
1808 if (y2 >= VGA_MAX_HEIGHT)
1809 y2 = VGA_MAX_HEIGHT;
1810 for(y = y1; y < y2; y++) {
1811 s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f);
1812 }
1813}
1814#endif /* !VBOX*/
1815
1816#ifdef VBOX
1817/**
1818 * Performs the display driver resizing when in graphics mode.
1819 *
1820 * This will recalc / update any status data depending on the driver
1821 * properties (bit depth mostly).
1822 *
1823 * @returns VINF_SUCCESS on success.
1824 * @returns VINF_VGA_RESIZE_IN_PROGRESS if the operation wasn't complete.
1825 * @param s Pointer to the vga status.
1826 * @param cx The width.
1827 * @param cy The height.
1828 */
1829static int vga_resize_graphic(VGAState *s, int cx, int cy, int v)
1830{
1831 const unsigned cBits = s->get_bpp(s);
1832 /** @todo r=sunlover: If the guest changes VBE_DISPI_INDEX_X_OFFSET, VBE_DISPI_INDEX_Y_OFFSET
1833 * registers, then the third parameter of the following call should be
1834 * probably 's->CTXSUFF(vram_ptr) + s->vbe_start_addr'.
1835 */
1836 int rc = s->pDrv->pfnResize(s->pDrv, cBits, s->CTXSUFF(vram_ptr), s->line_offset, cx, cy);
1837
1838 /* last stuff */
1839 s->last_bpp = cBits;
1840 s->last_scr_width = cx;
1841 s->last_scr_height = cy;
1842 s->last_width = cx;
1843 s->last_height = cy;
1844
1845 if (rc == VINF_VGA_RESIZE_IN_PROGRESS)
1846 return rc;
1847 AssertRC(rc);
1848
1849 /* update palette */
1850 switch (s->pDrv->cBits)
1851 {
1852 case 32: s->rgb_to_pixel = rgb_to_pixel32_dup; break;
1853 case 16:
1854 default: s->rgb_to_pixel = rgb_to_pixel16_dup; break;
1855 case 15: s->rgb_to_pixel = rgb_to_pixel15_dup; break;
1856 case 8: s->rgb_to_pixel = rgb_to_pixel8_dup; break;
1857 }
1858 if (s->shift_control == 0)
1859 update_palette16(s);
1860 else if (s->shift_control == 1)
1861 update_palette16(s);
1862 return VINF_SUCCESS;
1863}
1864#endif /* VBOX */
1865
1866/*
1867 * graphic modes
1868 */
1869#ifndef VBOX
1870static void vga_draw_graphic(VGAState *s, int full_update)
1871#else
1872static int vga_draw_graphic(VGAState *s, int full_update)
1873#endif /* !VBOX */
1874{
1875 int y1, y2, y, update, page_min, page_max, linesize, y_start, double_scan;
1876 int width, height, shift_control, line_offset, page0, page1, bwidth;
1877 int disp_width, multi_run;
1878 uint8_t *d;
1879 uint32_t v, addr1, addr;
1880 vga_draw_line_func *vga_draw_line;
1881 bool offsets_changed;
1882
1883 offsets_changed = update_basic_params(s);
1884
1885 full_update |= offsets_changed;
1886
1887 s->get_resolution(s, &width, &height);
1888 disp_width = width;
1889
1890 shift_control = (s->gr[0x05] >> 5) & 3;
1891 double_scan = (s->cr[0x09] >> 7);
1892 multi_run = double_scan;
1893 if (shift_control != s->shift_control ||
1894 double_scan != s->double_scan) {
1895 full_update = 1;
1896 s->shift_control = shift_control;
1897 s->double_scan = double_scan;
1898 }
1899
1900 if (shift_control == 0) {
1901 full_update |= update_palette16(s);
1902 if (s->sr[0x01] & 8) {
1903 v = VGA_DRAW_LINE4D2;
1904 disp_width <<= 1;
1905 } else {
1906 v = VGA_DRAW_LINE4;
1907 }
1908 } else if (shift_control == 1) {
1909 full_update |= update_palette16(s);
1910 if (s->sr[0x01] & 8) {
1911 v = VGA_DRAW_LINE2D2;
1912 disp_width <<= 1;
1913 } else {
1914 v = VGA_DRAW_LINE2;
1915 }
1916 } else {
1917 switch(s->get_bpp(s)) {
1918 default:
1919 case 0:
1920 full_update |= update_palette256(s);
1921 v = VGA_DRAW_LINE8D2;
1922 break;
1923 case 8:
1924 full_update |= update_palette256(s);
1925 v = VGA_DRAW_LINE8;
1926 break;
1927 case 15:
1928 v = VGA_DRAW_LINE15;
1929 break;
1930 case 16:
1931 v = VGA_DRAW_LINE16;
1932 break;
1933 case 24:
1934 v = VGA_DRAW_LINE24;
1935 break;
1936 case 32:
1937 v = VGA_DRAW_LINE32;
1938 break;
1939 }
1940 }
1941#ifndef VBOX
1942 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)];
1943
1944 if (disp_width != s->last_width ||
1945 height != s->last_height) {
1946 dpy_resize(s->ds, disp_width, height);
1947 s->last_scr_width = disp_width;
1948 s->last_scr_height = height;
1949 s->last_width = disp_width;
1950 s->last_height = height;
1951 full_update = 1;
1952 }
1953#else /* VBOX */
1954 if ( disp_width != (int)s->last_width
1955 || height != (int)s->last_height
1956 || s->get_bpp(s) != (int)s->last_bpp
1957 || offsets_changed)
1958 {
1959 int rc = vga_resize_graphic(s, disp_width, height, v);
1960 if (rc != VINF_SUCCESS) /* Return any rc, particularly VINF_VGA_RESIZE_IN_PROGRESS, to the caller. */
1961 return rc;
1962 full_update = 1;
1963 }
1964 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
1965
1966#endif /* VBOX */
1967 if (s->cursor_invalidate)
1968 s->cursor_invalidate(s);
1969
1970 line_offset = s->line_offset;
1971#if 0
1972 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",
1973 width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]));
1974#endif
1975 addr1 = (s->start_addr * 4);
1976#ifndef VBOX
1977 bwidth = width * 4;
1978#else /* VBOX */
1979 /* The width of VRAM scanline. */
1980 bwidth = s->line_offset;
1981 /* In some cases the variable is not yet set, probably due to incomplete
1982 * programming of the virtual hardware ports. Just return.
1983 */
1984 if (bwidth == 0) return VINF_SUCCESS;
1985#endif /* VBOX */
1986 y_start = -1;
1987 page_min = 0x7fffffff;
1988 page_max = -1;
1989#ifndef VBOX
1990 d = s->ds->data;
1991 linesize = s->ds->linesize;
1992#else /* VBOX */
1993 d = s->pDrv->pu8Data;
1994 linesize = s->pDrv->cbScanline;
1995#endif /* VBOX */
1996
1997 y1 = 0;
1998 y2 = s->cr[0x09] & 0x1F; /* starting row scan count */
1999 for(y = 0; y < height; y++) {
2000 addr = addr1;
2001 /* CGA/MDA compatibility. Note that these addresses are all
2002 * shifted left by two compared to VGA specs.
2003 */
2004 if (!(s->cr[0x17] & 1)) {
2005 addr = (addr & ~(1 << 15)) | ((y1 & 1) << 15);
2006 }
2007 if (!(s->cr[0x17] & 2)) {
2008 addr = (addr & ~(1 << 16)) | ((y1 & 2) << 15);
2009 }
2010#ifndef VBOX
2011 page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
2012 page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
2013 update = full_update |
2014 cpu_physical_memory_get_dirty(page0, VGA_DIRTY_FLAG) |
2015 cpu_physical_memory_get_dirty(page1, VGA_DIRTY_FLAG);
2016 if ((page1 - page0) > TARGET_PAGE_SIZE) {
2017 /* if wide line, can use another page */
2018 update |= cpu_physical_memory_get_dirty(page0 + TARGET_PAGE_SIZE,
2019 VGA_DIRTY_FLAG);
2020 }
2021#else /* VBOX */
2022 page0 = addr & TARGET_PAGE_MASK;
2023 page1 = (addr + bwidth - 1) & TARGET_PAGE_MASK;
2024 update = full_update | vga_is_dirty(s, page0) | vga_is_dirty(s, page1);
2025 if (page1 - page0 > TARGET_PAGE_SIZE) {
2026 /* if wide line, can use another page */
2027 update |= vga_is_dirty(s, page0 + TARGET_PAGE_SIZE);
2028 }
2029#endif /* VBOX */
2030 /* explicit invalidation for the hardware cursor */
2031 update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
2032 if (update) {
2033 if (y_start < 0)
2034 y_start = y;
2035 if (page0 < page_min)
2036 page_min = page0;
2037 if (page1 > page_max)
2038 page_max = page1;
2039#ifndef VBOX
2040 vga_draw_line(s, d, s->vram_ptr + addr, width);
2041#else /* VBOX */
2042 if (s->fRenderVRAM)
2043 vga_draw_line(s, d, s->CTXSUFF(vram_ptr) + addr, width);
2044#endif /* VBOX */
2045 if (s->cursor_draw_line)
2046 s->cursor_draw_line(s, d, y);
2047 } else {
2048 if (y_start >= 0) {
2049 /* flush to display */
2050#ifndef VBOX
2051 dpy_update(s->ds, 0, y_start,
2052 disp_width, y - y_start);
2053#else /* VBOX */
2054 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
2055#endif /* VBOX */
2056 y_start = -1;
2057 }
2058 }
2059 if (!multi_run) {
2060 y1++;
2061 multi_run = double_scan;
2062
2063 if (y2 == 0) {
2064 y2 = s->cr[0x09] & 0x1F;
2065 addr1 += line_offset;
2066 } else {
2067 --y2;
2068 }
2069 } else {
2070 multi_run--;
2071 }
2072 /* line compare acts on the displayed lines */
2073 if ((uint32_t)y == s->line_compare)
2074 addr1 = 0;
2075 d += linesize;
2076 }
2077 if (y_start >= 0) {
2078 /* flush to display */
2079#ifndef VBOX
2080 dpy_update(s->ds, 0, y_start,
2081 disp_width, y - y_start);
2082#else /* VBOX */
2083 s->pDrv->pfnUpdateRect(s->pDrv, 0, y_start, disp_width, y - y_start);
2084#endif /* VBOX */
2085 }
2086 /* reset modified pages */
2087 if (page_max != -1) {
2088#ifndef VBOX
2089 cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
2090 VGA_DIRTY_FLAG);
2091#else /* VBOX */
2092 vga_reset_dirty(s, page_min, page_max + TARGET_PAGE_SIZE);
2093#endif /* VBOX */
2094 }
2095 memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
2096#ifdef VBOX
2097 return VINF_SUCCESS;
2098#endif /* VBOX */
2099}
2100
2101static void vga_draw_blank(VGAState *s, int full_update)
2102{
2103#ifndef VBOX
2104 int i, w, val;
2105 uint8_t *d;
2106
2107 if (!full_update)
2108 return;
2109 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2110 return;
2111 if (s->ds->depth == 8)
2112 val = s->rgb_to_pixel(0, 0, 0);
2113 else
2114 val = 0;
2115 w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
2116 d = s->ds->data;
2117 for(i = 0; i < s->last_scr_height; i++) {
2118 memset(d, val, w);
2119 d += s->ds->linesize;
2120 }
2121 dpy_update(s->ds, 0, 0,
2122 s->last_scr_width, s->last_scr_height);
2123#else /* VBOX */
2124
2125 int i, w, val;
2126 uint8_t *d;
2127 uint32_t cbScanline = s->pDrv->cbScanline;
2128
2129 if (!full_update)
2130 return;
2131 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2132 return;
2133 if (s->pDrv->cBits == 8)
2134 val = s->rgb_to_pixel(0, 0, 0);
2135 else
2136 val = 0;
2137 w = s->last_scr_width * ((s->pDrv->cBits + 7) >> 3);
2138 d = s->pDrv->pu8Data;
2139 for(i = 0; i < (int)s->last_scr_height; i++) {
2140 memset(d, val, w);
2141 d += cbScanline;
2142 }
2143 s->pDrv->pfnUpdateRect(s->pDrv, 0, 0, s->last_scr_width, s->last_scr_height);
2144#endif /* VBOX */
2145}
2146
2147#define GMODE_TEXT 0
2148#define GMODE_GRAPH 1
2149#define GMODE_BLANK 2
2150
2151#ifndef VBOX
2152void vga_update_display(void)
2153{
2154 VGAState *s = vga_state;
2155#else /* VBOX */
2156static int vga_update_display(PVGASTATE s)
2157{
2158 int rc = VINF_SUCCESS;
2159#endif /* VBOX */
2160 int full_update, graphic_mode;
2161
2162#ifndef VBOX
2163 if (s->ds->depth == 0) {
2164#else /* VBOX */
2165 if (s->pDrv->cBits == 0) {
2166#endif /* VBOX */
2167 /* nothing to do */
2168 } else {
2169#ifndef VBOX
2170 switch(s->ds->depth) {
2171#else /* VBOX */
2172 switch(s->pDrv->cBits) {
2173#endif /* VBOX */
2174 case 8:
2175 s->rgb_to_pixel = rgb_to_pixel8_dup;
2176 break;
2177 case 15:
2178 s->rgb_to_pixel = rgb_to_pixel15_dup;
2179 break;
2180 default:
2181 case 16:
2182 s->rgb_to_pixel = rgb_to_pixel16_dup;
2183 break;
2184 case 32:
2185 s->rgb_to_pixel = rgb_to_pixel32_dup;
2186 break;
2187 }
2188
2189 full_update = 0;
2190 if (!(s->ar_index & 0x20)) {
2191 graphic_mode = GMODE_BLANK;
2192 } else {
2193 graphic_mode = s->gr[6] & 1;
2194 }
2195 if (graphic_mode != s->graphic_mode) {
2196 s->graphic_mode = graphic_mode;
2197 full_update = 1;
2198 }
2199 switch(graphic_mode) {
2200 case GMODE_TEXT:
2201#ifdef VBOX
2202 rc =
2203#endif /* VBOX */
2204 vga_draw_text(s, full_update);
2205 break;
2206 case GMODE_GRAPH:
2207#ifdef VBOX
2208 rc =
2209#endif /* VBOX */
2210 vga_draw_graphic(s, full_update);
2211 break;
2212 case GMODE_BLANK:
2213 default:
2214 vga_draw_blank(s, full_update);
2215 break;
2216 }
2217 }
2218#ifdef VBOX
2219 return rc;
2220#endif /* VBOX */
2221}
2222
2223/* force a full display refresh */
2224#ifndef VBOX
2225void vga_invalidate_display(void)
2226{
2227 VGAState *s = vga_state;
2228
2229 s->last_width = -1;
2230 s->last_height = -1;
2231}
2232#endif /* !VBOX */
2233
2234#ifndef VBOX /* see vgaR3Reset() */
2235static void vga_reset(VGAState *s)
2236{
2237 memset(s, 0, sizeof(VGAState));
2238 s->graphic_mode = -1; /* force full update */
2239}
2240#endif /* !VBOX */
2241
2242#ifndef VBOX
2243static CPUReadMemoryFunc *vga_mem_read[3] = {
2244 vga_mem_readb,
2245 vga_mem_readw,
2246 vga_mem_readl,
2247};
2248
2249static CPUWriteMemoryFunc *vga_mem_write[3] = {
2250 vga_mem_writeb,
2251 vga_mem_writew,
2252 vga_mem_writel,
2253};
2254#endif /* !VBOX */
2255
2256static void vga_save(QEMUFile *f, void *opaque)
2257{
2258 VGAState *s = (VGAState*)opaque;
2259 int i;
2260
2261 qemu_put_be32s(f, &s->latch);
2262 qemu_put_8s(f, &s->sr_index);
2263 qemu_put_buffer(f, s->sr, 8);
2264 qemu_put_8s(f, &s->gr_index);
2265 qemu_put_buffer(f, s->gr, 16);
2266 qemu_put_8s(f, &s->ar_index);
2267 qemu_put_buffer(f, s->ar, 21);
2268 qemu_put_be32s(f, &s->ar_flip_flop);
2269 qemu_put_8s(f, &s->cr_index);
2270 qemu_put_buffer(f, s->cr, 256);
2271 qemu_put_8s(f, &s->msr);
2272 qemu_put_8s(f, &s->fcr);
2273 qemu_put_8s(f, &s->st00);
2274 qemu_put_8s(f, &s->st01);
2275
2276 qemu_put_8s(f, &s->dac_state);
2277 qemu_put_8s(f, &s->dac_sub_index);
2278 qemu_put_8s(f, &s->dac_read_index);
2279 qemu_put_8s(f, &s->dac_write_index);
2280 qemu_put_buffer(f, s->dac_cache, 3);
2281 qemu_put_buffer(f, s->palette, 768);
2282
2283 qemu_put_be32s(f, &s->bank_offset);
2284#ifdef CONFIG_BOCHS_VBE
2285 qemu_put_byte(f, 1);
2286 qemu_put_be16s(f, &s->vbe_index);
2287 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2288 qemu_put_be16s(f, &s->vbe_regs[i]);
2289 qemu_put_be32s(f, &s->vbe_start_addr);
2290 qemu_put_be32s(f, &s->vbe_line_offset);
2291 qemu_put_be32s(f, &s->vbe_bank_mask);
2292#else
2293 qemu_put_byte(f, 0);
2294#endif
2295}
2296
2297static int vga_load(QEMUFile *f, void *opaque, int version_id)
2298{
2299 VGAState *s = (VGAState*)opaque;
2300 int is_vbe, i;
2301
2302 if (version_id != 1)
2303#ifndef VBOX
2304 return -EINVAL;
2305#else /* VBOX */
2306 {
2307 Log(("vga_load: version_id=%d - UNKNOWN\n", version_id));
2308 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2309 }
2310#endif /* VBOX */
2311
2312 qemu_get_be32s(f, &s->latch);
2313 qemu_get_8s(f, &s->sr_index);
2314 qemu_get_buffer(f, s->sr, 8);
2315 qemu_get_8s(f, &s->gr_index);
2316 qemu_get_buffer(f, s->gr, 16);
2317 qemu_get_8s(f, &s->ar_index);
2318 qemu_get_buffer(f, s->ar, 21);
2319 qemu_get_be32s(f, (uint32_t *)&s->ar_flip_flop);
2320 qemu_get_8s(f, &s->cr_index);
2321 qemu_get_buffer(f, s->cr, 256);
2322 qemu_get_8s(f, &s->msr);
2323 qemu_get_8s(f, &s->fcr);
2324 qemu_get_8s(f, &s->st00);
2325 qemu_get_8s(f, &s->st01);
2326
2327 qemu_get_8s(f, &s->dac_state);
2328 qemu_get_8s(f, &s->dac_sub_index);
2329 qemu_get_8s(f, &s->dac_read_index);
2330 qemu_get_8s(f, &s->dac_write_index);
2331 qemu_get_buffer(f, s->dac_cache, 3);
2332 qemu_get_buffer(f, s->palette, 768);
2333
2334 qemu_get_be32s(f, (uint32_t *)&s->bank_offset);
2335 is_vbe = qemu_get_byte(f);
2336#ifdef CONFIG_BOCHS_VBE
2337 if (!is_vbe)
2338#ifndef VBOX
2339 return -EINVAL;
2340#else /* VBOX */
2341 {
2342 Log(("vga_load: !is_vbe !!\n"));
2343 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2344 }
2345#endif /* VBOX */
2346 qemu_get_be16s(f, &s->vbe_index);
2347 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2348 qemu_get_be16s(f, &s->vbe_regs[i]);
2349 qemu_get_be32s(f, &s->vbe_start_addr);
2350 qemu_get_be32s(f, &s->vbe_line_offset);
2351 qemu_get_be32s(f, &s->vbe_bank_mask);
2352#else
2353 if (is_vbe)
2354#ifndef VBOX
2355 return -EINVAL;
2356#else /* VBOX */
2357 {
2358 Log(("vga_load: is_vbe !!\n"));
2359 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2360 }
2361#endif /* VBOX */
2362#endif
2363
2364 /* force refresh */
2365 s->graphic_mode = -1;
2366 return 0;
2367}
2368
2369#ifndef VBOX /* see vgaR3IORegionMap */
2370static void vga_map(PCIDevice *pci_dev, int region_num,
2371 uint32_t addr, uint32_t size, int type)
2372{
2373 VGAState *s = vga_state;
2374
2375 cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
2376}
2377#endif
2378
2379#ifndef VBOX /* see vgaR3Construct */
2380void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
2381 unsigned long vga_ram_offset, int vga_ram_size)
2382#else
2383static void vga_init_expand(void)
2384#endif
2385{
2386 int i, j, v, b;
2387
2388 for(i = 0;i < 256; i++) {
2389 v = 0;
2390 for(j = 0; j < 8; j++) {
2391 v |= ((i >> j) & 1) << (j * 4);
2392 }
2393 expand4[i] = v;
2394
2395 v = 0;
2396 for(j = 0; j < 4; j++) {
2397 v |= ((i >> (2 * j)) & 3) << (j * 4);
2398 }
2399 expand2[i] = v;
2400 }
2401 for(i = 0; i < 16; i++) {
2402 v = 0;
2403 for(j = 0; j < 4; j++) {
2404 b = ((i >> j) & 1);
2405 v |= b << (2 * j);
2406 v |= b << (2 * j + 1);
2407 }
2408 expand4to8[i] = v;
2409 }
2410#ifdef VBOX
2411}
2412#else /* !VBOX */
2413 vga_reset(s);
2414
2415 s->vram_ptr = vga_ram_base;
2416 s->vram_offset = vga_ram_offset;
2417 s->vram_size = vga_ram_size;
2418 s->ds = ds;
2419 s->get_bpp = vga_get_bpp;
2420 s->get_offsets = vga_get_offsets;
2421 s->get_resolution = vga_get_resolution;
2422 /* XXX: currently needed for display */
2423 vga_state = s;
2424}
2425
2426
2427int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
2428 unsigned long vga_ram_offset, int vga_ram_size)
2429{
2430 VGAState *s;
2431
2432 s = qemu_mallocz(sizeof(VGAState));
2433 if (!s)
2434 return -1;
2435
2436 vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
2437
2438 register_savevm("vga", 0, 1, vga_save, vga_load, s);
2439
2440 register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
2441
2442 register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
2443 register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
2444 register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
2445 register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
2446
2447 register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
2448
2449 register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
2450 register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
2451 register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
2452 register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
2453 s->bank_offset = 0;
2454
2455#ifdef CONFIG_BOCHS_VBE
2456 s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
2457 s->vbe_bank_mask = ((s->vram_size >> 16) - 1);
2458#if defined (TARGET_I386)
2459 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2460 register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
2461
2462 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2463 register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
2464
2465 /* old Bochs IO ports */
2466 register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
2467 register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
2468
2469 register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
2470 register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
2471#else
2472 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2473 register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
2474
2475 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2476 register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
2477#endif
2478#endif /* CONFIG_BOCHS_VBE */
2479
2480 vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
2481 cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
2482 vga_io_memory);
2483
2484 if (bus) {
2485 PCIDevice *d;
2486 uint8_t *pci_conf;
2487
2488 d = pci_register_device(bus, "VGA",
2489 sizeof(PCIDevice),
2490 -1, NULL, NULL);
2491 pci_conf = d->config;
2492 pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
2493 pci_conf[0x01] = 0x12;
2494 pci_conf[0x02] = 0x11;
2495 pci_conf[0x03] = 0x11;
2496 pci_conf[0x0a] = 0x00; // VGA controller
2497 pci_conf[0x0b] = 0x03;
2498 pci_conf[0x0e] = 0x00; // header_type
2499
2500 /* XXX: vga_ram_size must be a power of two */
2501 pci_register_io_region(d, 0, vga_ram_size,
2502 PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
2503 } else {
2504#ifdef CONFIG_BOCHS_VBE
2505 /* XXX: use optimized standard vga accesses */
2506 cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
2507 vga_ram_size, vga_ram_offset);
2508#endif
2509 }
2510 return 0;
2511}
2512#endif /* !VBOX */
2513
2514
2515#ifndef VBOX
2516/********************************************************/
2517/* vga screen dump */
2518
2519static int vga_save_w, vga_save_h;
2520
2521static void vga_save_dpy_update(DisplayState *s,
2522 int x, int y, int w, int h)
2523{
2524}
2525
2526static void vga_save_dpy_resize(DisplayState *s, int w, int h)
2527{
2528 s->linesize = w * 4;
2529#ifndef VBOX
2530 s->data = qemu_malloc(h * s->linesize);
2531#else /* VBOX */
2532 if (!s->data)
2533 {
2534 PPDMDEVINS pDevIns = VGASTATE2DEVINS((PVGASTATE)s->pvVgaState);
2535 s->data = PDMDevHlpMMHeapAlloc(pDevIns, h * s->linesize);
2536 }
2537 else // (32-bpp buffer is allocated by the caller)
2538 s->linesize = ((w * 32 + 31) / 32) * 4;
2539#endif /* VBOX */
2540 vga_save_w = w;
2541 vga_save_h = h;
2542}
2543
2544static void vga_save_dpy_refresh(DisplayState *s)
2545{
2546}
2547
2548static int ppm_save(const char *filename, uint8_t *data,
2549 int w, int h, int linesize)
2550{
2551 FILE *f;
2552 uint8_t *d, *d1;
2553 unsigned int v;
2554 int y, x;
2555
2556 f = fopen(filename, "wb");
2557 if (!f)
2558 return -1;
2559 fprintf(f, "P6\n%d %d\n%d\n",
2560 w, h, 255);
2561 d1 = data;
2562 for(y = 0; y < h; y++) {
2563 d = d1;
2564 for(x = 0; x < w; x++) {
2565 v = *(uint32_t *)d;
2566 fputc((v >> 16) & 0xff, f);
2567 fputc((v >> 8) & 0xff, f);
2568 fputc((v) & 0xff, f);
2569 d += 4;
2570 }
2571 d1 += linesize;
2572 }
2573 fclose(f);
2574 return 0;
2575}
2576
2577/* save the vga display in a PPM image even if no display is
2578 available */
2579void vga_screen_dump(const char *filename)
2580{
2581 VGAState *s = vga_state;
2582 DisplayState *saved_ds, ds1, *ds = &ds1;
2583
2584 /* XXX: this is a little hackish */
2585 vga_invalidate_display();
2586 saved_ds = s->ds;
2587
2588 memset(ds, 0, sizeof(DisplayState));
2589 ds->dpy_update = vga_save_dpy_update;
2590 ds->dpy_resize = vga_save_dpy_resize;
2591 ds->dpy_refresh = vga_save_dpy_refresh;
2592 ds->depth = 32;
2593
2594 s->ds = ds;
2595 s->graphic_mode = -1;
2596 vga_update_display();
2597
2598 if (ds->data) {
2599 ppm_save(filename, ds->data, vga_save_w, vga_save_h,
2600 s->ds->linesize);
2601 qemu_free(ds->data);
2602 }
2603 s->ds = saved_ds;
2604}
2605#endif /* !VBOX */
2606
2607
2608#if 0 //def VBOX
2609/* copy the vga display contents to the given buffer. the size of the buffer
2610 must be sufficient to store the screen copy (see below). the width and height
2611 parameters determine the required dimensions of the copy. If they differ
2612 from the actual screen dimensions, then the returned copy is shrinked or
2613 stretched accordingly. The copy is always a 32-bit image, so the size of
2614 the buffer supplied must be at least (((width * 32 + 31) / 32) * 4) * height,
2615 i.e. dword-aligned. returns zero if the operation was successfull and -1
2616 otherwise. */
2617
2618static int vga_copy_screen_to(PVGASTATE s, uint8_t *buf, int width, int height)
2619{
2620 DisplayState *saved_ds, ds1, *ds = &ds1;
2621 if (!buf || width <= 0 || height <= 0)
2622 return -1;
2623
2624 /* XXX: this is a little hackish */
2625 vga_invalidate_display(s);
2626 saved_ds = s->ds;
2627
2628 memset(ds, 0, sizeof(DisplayState));
2629 ds->dpy_update = vga_save_dpy_update;
2630 ds->dpy_resize = vga_save_dpy_resize;
2631 ds->dpy_refresh = vga_save_dpy_refresh;
2632 ds->depth = 32;
2633 ds->data = buf;
2634 ds->pvVgaState = s;
2635
2636 s->ds = ds;
2637 s->graphic_mode = -1;
2638 vga_update_display(s);
2639
2640//@@TODO (dmik): implement stretching/shrinking!
2641
2642 s->ds = saved_ds;
2643 return 0;
2644}
2645
2646/* copy the given buffer to the vga display. width and height define the
2647 dimensions of the image in the buffer. x and y define the point on the
2648 vga display to copy the image to. the buffer is assumed to contain a 32-bit
2649 image, so the size of one scanline must be ((width * 32 + 31) / 32) * 4),
2650 i.e. dword-aligned. returns zero if the operation was successfull and -1
2651 otherwise. */
2652static int vga_copy_screen_from(PVGASTATE s, uint8_t *buf, int x, int y, int width, int height)
2653{
2654 int bpl = ((width * 32 + 31) / 32) * 4;
2655 int linesize = s->ds->linesize;
2656 uint8_t *dst;
2657 uint8_t *src;
2658 int bpp;
2659 vga_draw_line_func *vga_draw_line;
2660
2661 if (!buf || x < 0 || y < 0 || width <= 0 || height <= 0
2662 || x + width > s->ds->width || y + height > s->ds->height)
2663 return -1;
2664
2665 vga_draw_line = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(s->ds->depth)];
2666 switch (s->ds->depth) {
2667 case 8: bpp = 1; break;
2668 case 15:
2669 case 16: bpp = 2; break;
2670 case 32: bpp = 4; break;
2671 default: return -1;
2672 }
2673
2674 dst = s->ds->data + y * linesize + x * bpp;
2675 src = buf;
2676 for (y = 0; y < height; y ++)
2677 {
2678 vga_draw_line(s, dst, src, width);
2679 dst += linesize;
2680 src += bpl;
2681 }
2682
2683 return 0;
2684}
2685#endif
2686
2687#endif /* !VBOX || !IN_GC || !IN_RING0 */
2688
2689
2690
2691#ifdef VBOX /* innotek code start */
2692
2693
2694/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2695
2696/**
2697 * Port I/O Handler for VGA OUT operations.
2698 *
2699 * @returns VBox status code.
2700 *
2701 * @param pDevIns The device instance.
2702 * @param pvUser User argument - ignored.
2703 * @param Port Port number used for the IN operation.
2704 * @param u32 The value to output.
2705 * @param cb The value size in bytes.
2706 */
2707PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2708{
2709 NOREF(pvUser);
2710 if (cb == 1)
2711 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2712 else if (cb == 2)
2713 {
2714 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32 & 0xff);
2715 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1, u32 >> 8);
2716 }
2717 return VINF_SUCCESS;
2718}
2719
2720
2721/**
2722 * Port I/O Handler for VGA IN operations.
2723 *
2724 * @returns VBox status code.
2725 *
2726 * @param pDevIns The device instance.
2727 * @param pvUser User argument - ignored.
2728 * @param Port Port number used for the IN operation.
2729 * @param pu32 Where to store the result.
2730 * @param cb Number of bytes read.
2731 */
2732PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2733{
2734 NOREF(pvUser);
2735 if (cb == 1)
2736 {
2737 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2738 return VINF_SUCCESS;
2739 }
2740 else if (cb == 2)
2741 {
2742 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port)
2743 | (vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1) << 8);
2744 return VINF_SUCCESS;
2745 }
2746 return VERR_IOM_IOPORT_UNUSED;
2747}
2748
2749
2750/**
2751 * Port I/O Handler for VBE OUT operations.
2752 *
2753 * @returns VBox status code.
2754 *
2755 * @param pDevIns The device instance.
2756 * @param pvUser User argument - ignored.
2757 * @param Port Port number used for the IN operation.
2758 * @param u32 The value to output.
2759 * @param cb The value size in bytes.
2760 */
2761PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2762{
2763 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2764
2765 NOREF(pvUser);
2766
2767#ifndef IN_RING3
2768 /*
2769 * This has to be done on the host in order to execute the connector callbacks.
2770 */
2771 if (s->vbe_index == VBE_DISPI_INDEX_ENABLE
2772 || s->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
2773 {
2774 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2775 return VINF_IOM_HC_IOPORT_WRITE;
2776 }
2777#endif
2778#ifdef VBE_BYTEWISE_IO
2779 if (cb == 1)
2780 {
2781 if (!s->fWriteVBEData)
2782 {
2783 if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2784 && (u32 & VBE_DISPI_ENABLED))
2785 {
2786 s->fWriteVBEData = false;
2787 return vbe_ioport_write_data(s, Port, u32 & 0xFF);
2788 }
2789 else
2790 {
2791 s->cbWriteVBEData = u32 & 0xFF;
2792 s->fWriteVBEData = true;
2793 return VINF_SUCCESS;
2794 }
2795 }
2796 else
2797 {
2798 u32 = (s->cbWriteVBEData << 8) | (u32 & 0xFF);
2799 s->fWriteVBEData = false;
2800 cb = 2;
2801 }
2802 }
2803#endif
2804 if (cb == 2 || cb == 4)
2805 {
2806//#ifdef IN_GC
2807// /*
2808// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2809// * Since we're not mapping the entire framebuffer any longer that
2810// * has to be done on the host.
2811// */
2812// if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2813// && (u32 & VBE_DISPI_ENABLED))
2814// {
2815// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2816// return VINF_IOM_HC_IOPORT_WRITE;
2817// }
2818//#endif
2819 return vbe_ioport_write_data(s, Port, u32);
2820 }
2821 else
2822 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2823 return VINF_SUCCESS;
2824}
2825
2826
2827/**
2828 * Port I/O Handler for VBE OUT operations.
2829 *
2830 * @returns VBox status code.
2831 *
2832 * @param pDevIns The device instance.
2833 * @param pvUser User argument - ignored.
2834 * @param Port Port number used for the IN operation.
2835 * @param u32 The value to output.
2836 * @param cb The value size in bytes.
2837 */
2838PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2839{
2840 NOREF(pvUser);
2841#ifdef VBE_BYTEWISE_IO
2842 if (cb == 1)
2843 {
2844 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2845 if (!s->fWriteVBEIndex)
2846 {
2847 s->cbWriteVBEIndex = u32 & 0x00FF;
2848 s->fWriteVBEIndex = true;
2849 return VINF_SUCCESS;
2850 }
2851 else
2852 {
2853 s->fWriteVBEIndex = false;
2854 vbe_ioport_write_index(s, Port, (s->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2855 return VINF_SUCCESS;
2856 }
2857 }
2858 else
2859#endif
2860 if (cb == 2)
2861 vbe_ioport_write_index(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2862 else
2863 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2864 return VINF_SUCCESS;
2865}
2866
2867
2868/**
2869 * Port I/O Handler for VBE IN operations.
2870 *
2871 * @returns VBox status code.
2872 *
2873 * @param pDevIns The device instance.
2874 * @param pvUser User argument - ignored.
2875 * @param Port Port number used for the IN operation.
2876 * @param pu32 Where to store the result.
2877 * @param cb Number of bytes to read.
2878 */
2879PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2880{
2881 NOREF(pvUser);
2882#ifdef VBE_BYTEWISE_IO
2883 if (cb == 1)
2884 {
2885 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2886
2887 if (!s->fReadVBEData)
2888 {
2889 *pu32 = (vbe_ioport_read_data(s, Port) >> 8) & 0xFF;
2890 s->fReadVBEData = true;
2891 return VINF_SUCCESS;
2892 }
2893 else
2894 {
2895 *pu32 = vbe_ioport_read_data(s, Port) & 0xFF;
2896 s->fReadVBEData = false;
2897 return VINF_SUCCESS;
2898 }
2899 }
2900 else
2901#endif
2902 if (cb == 2)
2903 {
2904 *pu32 = vbe_ioport_read_data(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2905 return VINF_SUCCESS;
2906 }
2907 else if (cb == 4)
2908 {
2909 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2910 /* Quick hack for getting the vram size. */
2911 *pu32 = s->vram_size;
2912 return VINF_SUCCESS;
2913 }
2914 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2915 return VERR_IOM_IOPORT_UNUSED;
2916}
2917
2918
2919/**
2920 * Port I/O Handler for VBE IN operations.
2921 *
2922 * @returns VBox status code.
2923 *
2924 * @param pDevIns The device instance.
2925 * @param pvUser User argument - ignored.
2926 * @param Port Port number used for the IN operation.
2927 * @param pu32 Where to store the result.
2928 * @param cb Number of bytes to read.
2929 */
2930PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2931{
2932 NOREF(pvUser);
2933#ifdef VBE_BYTEWISE_IO
2934 if (cb == 1)
2935 {
2936 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2937
2938 if (!s->fReadVBEIndex)
2939 {
2940 *pu32 = (vbe_ioport_read_index(s, Port) >> 8) & 0xFF;
2941 s->fReadVBEIndex = true;
2942 return VINF_SUCCESS;
2943 }
2944 else
2945 {
2946 *pu32 = vbe_ioport_read_index(s, Port) & 0xFF;
2947 s->fReadVBEIndex = false;
2948 return VINF_SUCCESS;
2949 }
2950 }
2951 else
2952#endif
2953 if (cb == 2)
2954 {
2955 *pu32 = vbe_ioport_read_index(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2956 return VINF_SUCCESS;
2957 }
2958 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
2959 return VERR_IOM_IOPORT_UNUSED;
2960}
2961
2962
2963
2964
2965
2966/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
2967
2968/*
2969 * Internal. For use inside VGAGCMemoryFillWrite only.
2970 * Macro for apply logical operation and bit mask.
2971 */
2972#define APPLY_LOGICAL_AND_MASK(s, val, bit_mask) \
2973 /* apply logical operation */ \
2974 switch(s->gr[3] >> 3) \
2975 { \
2976 case 0: \
2977 default: \
2978 /* nothing to do */ \
2979 break; \
2980 case 1: \
2981 /* and */ \
2982 val &= s->latch; \
2983 break; \
2984 case 2: \
2985 /* or */ \
2986 val |= s->latch; \
2987 break; \
2988 case 3: \
2989 /* xor */ \
2990 val ^= s->latch; \
2991 break; \
2992 } \
2993 /* apply bit mask */ \
2994 val = (val & bit_mask) | (s->latch & ~bit_mask)
2995
2996/**
2997 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
2998 * This is the advanced version of vga_mem_writeb function.
2999 *
3000 * @returns VBox status code.
3001 * @param pDevIns Pointer device instance.
3002 * @param pvUser User argument - ignored.
3003 * @param GCPhysAddr Physical address of memory to write.
3004 * @param u32Item Data to write, up to 4 bytes.
3005 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3006 * @param cItems Number of data items to write.
3007 */
3008PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3009{
3010 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3011 uint32_t b;
3012 uint32_t write_mask, bit_mask, set_mask;
3013 uint32_t aVal[4]; /** @todo r=bird: Why is this an 32-bit array? */
3014 unsigned i;
3015 NOREF(pvUser);
3016 for (i = 0; i < cbItem; i++)
3017 {
3018 aVal[i] = u32Item & 0xff;
3019 u32Item >>= 8;
3020 }
3021
3022 /* convert to VGA memory offset */
3023 /// @todo add check for the end of region
3024 GCPhysAddr &= 0x1ffff;
3025 switch((pData->gr[6] >> 2) & 3) {
3026 case 0:
3027 break;
3028 case 1:
3029 if (GCPhysAddr >= 0x10000)
3030 return VINF_SUCCESS;
3031 GCPhysAddr += pData->bank_offset;
3032 break;
3033 case 2:
3034 GCPhysAddr -= 0x10000;
3035 if (GCPhysAddr >= 0x8000)
3036 return VINF_SUCCESS;
3037 break;
3038 default:
3039 case 3:
3040 GCPhysAddr -= 0x18000;
3041 if (GCPhysAddr >= 0x8000)
3042 return VINF_SUCCESS;
3043 break;
3044 }
3045
3046 if (pData->sr[4] & 0x08) {
3047 /* chain 4 mode : simplest access */
3048#ifdef IN_GC
3049 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
3050 return VINF_IOM_HC_MMIO_WRITE;
3051#else
3052 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
3053 {
3054 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3055 return VINF_SUCCESS;
3056 }
3057#endif
3058
3059 while (cItems-- > 0)
3060 for (i = 0; i < cbItem; i++)
3061 {
3062 if (pData->sr[2] & (1 << (GCPhysAddr & 3)))
3063 {
3064 CTXSUFF(pData->vram_ptr)[GCPhysAddr] = aVal[i];
3065 vga_set_dirty(pData, GCPhysAddr);
3066 }
3067 GCPhysAddr++;
3068 }
3069 } else if (pData->gr[5] & 0x10) {
3070 /* odd/even mode (aka text mode mapping) */
3071#ifdef IN_GC
3072 if (GCPhysAddr * 2 + cItems * cbItem >= VGA_MAPPING_SIZE)
3073 return VINF_IOM_HC_MMIO_WRITE;
3074#else
3075 if (GCPhysAddr * 2 + cItems * cbItem >= pData->vram_size)
3076 {
3077 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3078 return VINF_SUCCESS;
3079 }
3080#endif
3081 while (cItems-- > 0)
3082 for (i = 0; i < cbItem; i++)
3083 {
3084 unsigned plane = (pData->gr[4] & 2) | (GCPhysAddr & 1);
3085 if (pData->sr[2] & (1 << plane)) {
3086 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
3087 CTXSUFF(pData->vram_ptr)[PhysAddr2] = aVal[i];
3088 vga_set_dirty(pData, PhysAddr2);
3089 }
3090 GCPhysAddr++;
3091 }
3092 } else {
3093#ifdef IN_GC
3094 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
3095 return VINF_IOM_HC_MMIO_WRITE;
3096#else
3097 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
3098 {
3099 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3100 return VINF_SUCCESS;
3101 }
3102#endif
3103
3104 /* standard VGA latched access */
3105 switch(pData->gr[5] & 3) {
3106 default:
3107 case 0:
3108 /* rotate */
3109 b = pData->gr[3] & 7;
3110 bit_mask = pData->gr[8];
3111 bit_mask |= bit_mask << 8;
3112 bit_mask |= bit_mask << 16;
3113 set_mask = mask16[pData->gr[1]];
3114
3115 for (i = 0; i < cbItem; i++)
3116 {
3117 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3118 aVal[i] |= aVal[i] << 8;
3119 aVal[i] |= aVal[i] << 16;
3120
3121 /* apply set/reset mask */
3122 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pData->gr[0]] & set_mask);
3123
3124 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3125 }
3126 break;
3127 case 1:
3128 for (i = 0; i < cbItem; i++)
3129 aVal[i] = pData->latch;
3130 break;
3131 case 2:
3132 bit_mask = pData->gr[8];
3133 bit_mask |= bit_mask << 8;
3134 bit_mask |= bit_mask << 16;
3135 for (i = 0; i < cbItem; i++)
3136 {
3137 aVal[i] = mask16[aVal[i] & 0x0f];
3138
3139 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3140 }
3141 break;
3142 case 3:
3143 /* rotate */
3144 b = pData->gr[3] & 7;
3145
3146 for (i = 0; i < cbItem; i++)
3147 {
3148 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3149 bit_mask = pData->gr[8] & aVal[i];
3150 bit_mask |= bit_mask << 8;
3151 bit_mask |= bit_mask << 16;
3152 aVal[i] = mask16[pData->gr[0]];
3153
3154 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3155 }
3156 break;
3157 }
3158
3159 /* mask data according to sr[2] */
3160 write_mask = mask16[pData->sr[2]];
3161
3162 /* actually write data */
3163 if (cbItem == 1)
3164 {
3165 /* The most frequently case is 1 byte I/O. */
3166 while (cItems-- > 0)
3167 {
3168 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3169 vga_set_dirty(pData, GCPhysAddr << 2);
3170 GCPhysAddr++;
3171 }
3172 }
3173 else if (cbItem == 2)
3174 {
3175 /* The second case is 2 bytes I/O. */
3176 while (cItems-- > 0)
3177 {
3178 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3179 vga_set_dirty(pData, GCPhysAddr << 2);
3180 GCPhysAddr++;
3181
3182 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3183 vga_set_dirty(pData, GCPhysAddr << 2);
3184 GCPhysAddr++;
3185 }
3186 }
3187 else
3188 {
3189 /* And the rest is 4 bytes. */
3190 Assert(cbItem == 4);
3191 while (cItems-- > 0)
3192 for (i = 0; i < cbItem; i++)
3193 {
3194 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3195 vga_set_dirty(pData, GCPhysAddr << 2);
3196 GCPhysAddr++;
3197 }
3198 }
3199 }
3200 return VINF_SUCCESS;
3201}
3202#undef APPLY_LOGICAL_AND_MASK
3203
3204
3205/**
3206 * Legacy VGA memory (0xa0000 - 0xbffff) read hook, to be called from IOM.
3207 *
3208 * @returns VBox status code.
3209 * @param pDevIns Pointer device instance.
3210 * @param pvUser User argument - ignored.
3211 * @param GCPhysAddr Physical address of memory to read.
3212 * @param pv Where to store readed data.
3213 * @param cb Bytes to read.
3214 */
3215PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3216{
3217 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3218 STAM_PROFILE_START(&pData->StatGCMemoryRead, a);
3219 NOREF(pvUser);
3220 switch (cb)
3221 {
3222 case 1:
3223 *(uint8_t *)pv = vga_mem_readb(pData, GCPhysAddr); break;
3224 case 2:
3225 *(uint16_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3226 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8);
3227 break;
3228 case 4:
3229 *(uint32_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3230 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8)
3231 | (vga_mem_readb(pData, GCPhysAddr + 2) << 16)
3232 | (vga_mem_readb(pData, GCPhysAddr + 3) << 24);
3233 break;
3234
3235 default:
3236 {
3237 uint8_t *pu8Data = (uint8_t *)pv;
3238 while (cb-- > 0)
3239 *pu8Data++ = vga_mem_readb(pData, GCPhysAddr++);
3240 }
3241 }
3242 STAM_PROFILE_STOP(&pData->StatGCMemoryRead, a);
3243 return VINF_SUCCESS;
3244}
3245
3246/**
3247 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM.
3248 *
3249 * @returns VBox status code.
3250 * @param pDevIns Pointer device instance.
3251 * @param pvUser User argument - ignored.
3252 * @param GCPhysAddr Physical address of memory to write.
3253 * @param pv Pointer to data.
3254 * @param cb Bytes to write.
3255 */
3256PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3257{
3258 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3259 uint8_t *pu8 = (uint8_t *)pv;
3260 int rc = VINF_SUCCESS;
3261 STAM_PROFILE_START(&pData->StatGCMemoryWrite, a);
3262
3263 switch (cb)
3264 {
3265 case 1:
3266 rc = vga_mem_writeb(pData, GCPhysAddr, *pu8);
3267 break;
3268#if 1
3269 case 2:
3270 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3271 if (RT_LIKELY(rc == VINF_SUCCESS))
3272 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3273 break;
3274 case 4:
3275 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3276 if (RT_LIKELY(rc == VINF_SUCCESS))
3277 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3278 if (RT_LIKELY(rc == VINF_SUCCESS))
3279 rc = vga_mem_writeb(pData, GCPhysAddr + 2, pu8[2]);
3280 if (RT_LIKELY(rc == VINF_SUCCESS))
3281 rc = vga_mem_writeb(pData, GCPhysAddr + 3, pu8[3]);
3282 break;
3283#else
3284 case 2:
3285 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3286 break;
3287 case 4:
3288 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3289 break;
3290#endif
3291 default:
3292 while (cb-- > 0 && rc == VINF_SUCCESS)
3293 rc = vga_mem_writeb(pData, GCPhysAddr++, *pu8++);
3294 break;
3295
3296 }
3297 STAM_PROFILE_STOP(&pData->StatGCMemoryWrite, a);
3298 return rc;
3299}
3300
3301
3302/**
3303 * Handle LFB access.
3304 * @returns VBox status code.
3305 * @param pVM VM handle.
3306 * @param pData VGA device instance data.
3307 * @param GCPhys The access physical address.
3308 * @param GCPtr The access virtual address (only GC).
3309 */
3310static int vgaLFBAccess(PVM pVM, PVGASTATE pData, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3311{
3312 int rc;
3313
3314 /*
3315 * Set page dirty bit.
3316 */
3317 vga_set_dirty(pData, GCPhys - pData->GCPhysVRAM);
3318 pData->fLFBUpdated = true;
3319
3320 /*
3321 * Turn of the write handler for this particular page and make it R/W.
3322 * Then return telling the caller to restart the guest instruction.
3323 * ASSUME: the guest always maps video memory RW.
3324 */
3325 rc = PGMHandlerPhysicalPageTempOff(pVM, pData->GCPhysVRAM, GCPhys);
3326 if (VBOX_SUCCESS(rc))
3327 {
3328#ifndef IN_RING3
3329 rc = PGMShwModifyPage(pVM, GCPtr, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
3330 if (VBOX_SUCCESS(rc))
3331 return VINF_SUCCESS;
3332 else
3333 AssertMsgFailed(("PGMShwModifyPage -> rc=%d\n", rc));
3334#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3335 Assert(GCPtr == 0);
3336 return VINF_SUCCESS;
3337#endif
3338 }
3339 else
3340 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3341
3342 return rc;
3343}
3344
3345
3346#ifdef IN_GC
3347/**
3348 * #PF Handler for VBE LFB access.
3349 *
3350 * @returns VBox status code (appropriate for GC return).
3351 * @param pVM VM Handle.
3352 * @param uErrorCode CPU Error code.
3353 * @param pRegFrame Trap register frame.
3354 * @param pvFault The fault address (cr2).
3355 * @param GCPhysFault The GC physical address corresponding to pvFault.
3356 * @param pvUser User argument, ignored.
3357 */
3358PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3359{
3360 PVGASTATE pData = (PVGASTATE)pvUser;
3361 Assert(pData);
3362 Assert(GCPhysFault >= pData->GCPhysVRAM);
3363 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3364
3365 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3366}
3367
3368#elif IN_RING0
3369
3370/**
3371 * #PF Handler for VBE LFB access.
3372 *
3373 * @returns VBox status code (appropriate for GC return).
3374 * @param pVM VM Handle.
3375 * @param uErrorCode CPU Error code.
3376 * @param pRegFrame Trap register frame.
3377 * @param pvFault The fault address (cr2).
3378 * @param GCPhysFault The GC physical address corresponding to pvFault.
3379 * @param pvUser User argument, ignored.
3380 */
3381PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3382{
3383 PVGASTATE pData = (PVGASTATE)pvUser;
3384 Assert(pData);
3385 Assert(GCPhysFault >= pData->GCPhysVRAM);
3386 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3387
3388 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3389}
3390
3391#else /* IN_RING3 */
3392
3393/**
3394 * HC access handler for the LFB.
3395 *
3396 * @returns VINF_SUCCESS if the handler have carried out the operation.
3397 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
3398 * @param pVM VM Handle.
3399 * @param GCPhys The physical address the guest is writing to.
3400 * @param pvPhys The HC mapping of that address.
3401 * @param pvBuf What the guest is reading/writing.
3402 * @param cbBuf How much it's reading/writing.
3403 * @param enmAccessType The access type.
3404 * @param pvUser User argument.
3405 */
3406static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3407{
3408 PVGASTATE pData = (PVGASTATE)pvUser;
3409 int rc;
3410 Assert(pData);
3411 Assert(GCPhys >= pData->GCPhysVRAM);
3412 rc = vgaLFBAccess(pVM, pData, GCPhys, 0);
3413 if (VBOX_SUCCESS(rc))
3414 return VINF_PGM_HANDLER_DO_DEFAULT;
3415 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Vrc\n", rc));
3416 return rc;
3417}
3418#endif /* IN_RING3 */
3419
3420
3421/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3422
3423#ifdef IN_RING3
3424
3425# ifdef VBE_NEW_DYN_LIST
3426/**
3427 * Port I/O Handler for VBE Extra OUT operations.
3428 *
3429 * @returns VBox status code.
3430 *
3431 * @param pDevIns The device instance.
3432 * @param pvUser User argument - ignored.
3433 * @param Port Port number used for the IN operation.
3434 * @param u32 The value to output.
3435 * @param cb The value size in bytes.
3436 */
3437PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3438{
3439 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3440 NOREF(pvUser);
3441 NOREF(Port);
3442
3443 if (cb == 2)
3444 {
3445 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3446 pData->u16VBEExtraAddress = u32;
3447 return VINF_SUCCESS;
3448 }
3449
3450 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3451 return VINF_SUCCESS;
3452}
3453
3454
3455/**
3456 * Port I/O Handler for VBE Extra IN operations.
3457 *
3458 * @returns VBox status code.
3459 *
3460 * @param pDevIns The device instance.
3461 * @param pvUser User argument - ignored.
3462 * @param Port Port number used for the IN operation.
3463 * @param pu32 Where to store the result.
3464 * @param cb Number of bytes read.
3465 */
3466PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3467{
3468 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3469 NOREF(pvUser);
3470 NOREF(Port);
3471
3472 if (pData->u16VBEExtraAddress == 0xffff)
3473 {
3474 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3475 *pu32 = pData->vram_size / _64K;
3476 return VINF_SUCCESS;
3477 }
3478
3479 if ( pData->u16VBEExtraAddress >= pData->cbVBEExtraData
3480 || pData->u16VBEExtraAddress + cb > pData->cbVBEExtraData)
3481 {
3482 *pu32 = 0;
3483 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3484 pData->u16VBEExtraAddress, pData->u16VBEExtraAddress, pData->cbVBEExtraData, pData->cbVBEExtraData));
3485 return VINF_SUCCESS;
3486 }
3487
3488 if (cb == 1)
3489 {
3490 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress] & 0xFF;
3491
3492 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3493 return VINF_SUCCESS;
3494 }
3495
3496 if (cb == 2)
3497 {
3498 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress]
3499 | pData->pu8VBEExtraData[pData->u16VBEExtraAddress + 1] << 8;
3500
3501 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3502 return VINF_SUCCESS;
3503 }
3504 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3505 return VERR_IOM_IOPORT_UNUSED;
3506}
3507# endif /* VBE_NEW_DYN_LIST */
3508
3509
3510
3511
3512/* -=-=-=-=-=- Ring 3: VGA BIOS I/Os -=-=-=-=-=- */
3513
3514/**
3515 * Port I/O Handler for VGA BIOS IN operations.
3516 *
3517 * @returns VBox status code.
3518 *
3519 * @param pDevIns The device instance.
3520 * @param pvUser User argument - ignored.
3521 * @param Port Port number used for the IN operation.
3522 * @param pu32 Where to store the result.
3523 * @param cb Number of bytes read.
3524 */
3525static DECLCALLBACK(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3526{
3527 NOREF(pDevIns);
3528 NOREF(pvUser);
3529 NOREF(Port);
3530 NOREF(pu32);
3531 NOREF(cb);
3532 return VERR_IOM_IOPORT_UNUSED;
3533}
3534
3535/**
3536 * Port I/O Handler for VGA BIOS OUT operations.
3537 *
3538 * @returns VBox status code.
3539 *
3540 * @param pDevIns The device instance.
3541 * @param pvUser User argument - ignored.
3542 * @param Port Port number used for the IN operation.
3543 * @param u32 The value to output.
3544 * @param cb The value size in bytes.
3545 */
3546static DECLCALLBACK(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3547{
3548 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3549 /*
3550 * VGA BIOS char printing.
3551 */
3552 if ( cb == 1
3553 && Port == VBE_PRINTF_PORT)
3554 {
3555#if 0
3556 switch (u32)
3557 {
3558 case '\r': Log(("vgabios: <return>\n")); break;
3559 case '\n': Log(("vgabios: <newline>\n")); break;
3560 case '\t': Log(("vgabios: <tab>\n")); break;
3561 default:
3562 Log(("vgabios: %c\n", u32));
3563 }
3564#else
3565 if (lastWasNotNewline == 0)
3566 Log(("vgabios: "));
3567 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3568 Log(("%c", u32));
3569 if (u32 == '\n')
3570 lastWasNotNewline = 0;
3571 else
3572 lastWasNotNewline = 1;
3573#endif
3574 return VINF_SUCCESS;
3575 }
3576
3577 /* not in use. */
3578 return VINF_SUCCESS;
3579}
3580
3581
3582/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
3583
3584/**
3585 * Queries an interface to the driver.
3586 *
3587 * @returns Pointer to interface.
3588 * @returns NULL if the interface was not supported by the driver.
3589 * @param pInterface Pointer to this interface structure.
3590 * @param enmInterface The requested interface identification.
3591 * @thread Any thread.
3592 */
3593static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
3594{
3595 PVGASTATE pData = (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Base));
3596 switch (enmInterface)
3597 {
3598 case PDMINTERFACE_BASE:
3599 return &pData->Base;
3600 case PDMINTERFACE_DISPLAY_PORT:
3601 return &pData->Port;
3602 default:
3603 return NULL;
3604 }
3605}
3606
3607
3608/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
3609
3610/**
3611 * Resize the display.
3612 * This is called when the resolution changes. This usually happens on
3613 * request from the guest os, but may also happen as the result of a reset.
3614 *
3615 * @param pInterface Pointer to this interface.
3616 * @param cx New display width.
3617 * @param cy New display height
3618 * @thread The emulation thread.
3619 */
3620static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3621{
3622 return VINF_SUCCESS;
3623}
3624
3625
3626/**
3627 * Update a rectangle of the display.
3628 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
3629 *
3630 * @param pInterface Pointer to this interface.
3631 * @param x The upper left corner x coordinate of the rectangle.
3632 * @param y The upper left corner y coordinate of the rectangle.
3633 * @param cx The width of the rectangle.
3634 * @param cy The height of the rectangle.
3635 * @thread The emulation thread.
3636 */
3637static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3638{
3639}
3640
3641
3642/**
3643 * Refresh the display.
3644 *
3645 * The interval between these calls is set by
3646 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
3647 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
3648 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
3649 * the changed rectangles.
3650 *
3651 * @param pInterface Pointer to this interface.
3652 * @thread The emulation thread.
3653 */
3654static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
3655{
3656}
3657
3658
3659/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
3660
3661/** Converts a display port interface pointer to a vga state pointer. */
3662#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Port)) )
3663
3664
3665/**
3666 * Update the display with any changed regions.
3667 *
3668 * @param pInterface Pointer to this interface.
3669 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
3670 */
3671static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
3672{
3673 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3674 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3675
3676#ifdef DEBUG_sunlover
3677 LogFlow(("vgaPortUpdateDisplay\n"));
3678#endif /* DEBUG_sunlover */
3679
3680 /* This should be called only in non VBVA mode. */
3681
3682 int rc = vga_update_display(pData);
3683 if (rc != VINF_SUCCESS)
3684 return rc;
3685
3686 if (pData->fHaveDirtyBits)
3687 {
3688 PPDMDEVINS pDevIns = pData->pDevInsHC;
3689 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
3690 pData->fHaveDirtyBits = false;
3691 }
3692
3693 return VINF_SUCCESS;
3694}
3695
3696
3697/**
3698 * Update the entire display.
3699 *
3700 * @param pInterface Pointer to this interface.
3701 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
3702 */
3703static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
3704{
3705 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3706 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3707
3708 /* This is called both in VBVA mode and normal modes. */
3709
3710#ifdef DEBUG_sunlover
3711 LogFlow(("vgaPortUpdateDisplayAll\n"));
3712#endif /* DEBUG_sunlover */
3713
3714 pData->graphic_mode = -1; /* force full update */
3715 return vga_update_display(pData);
3716}
3717
3718
3719/**
3720 * Sets the refresh rate and restart the timer.
3721 *
3722 * @returns VBox status code.
3723 * @param pInterface Pointer to this interface.
3724 * @param cMilliesInterval Number of millies between two refreshes.
3725 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
3726 */
3727static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
3728{
3729 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3730
3731 pData->cMilliesRefreshInterval = cMilliesInterval;
3732 if (cMilliesInterval)
3733 return TMTimerSetMillies(pData->RefreshTimer, cMilliesInterval);
3734 return TMTimerStop(pData->RefreshTimer);
3735}
3736
3737
3738/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
3739static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
3740{
3741 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3742
3743 if (!pcBits)
3744 return VERR_INVALID_PARAMETER;
3745 *pcBits = vga_get_bpp(pData);
3746 return VINF_SUCCESS;
3747}
3748
3749/**
3750 * Create a 32-bbp snapshot of the display.
3751 *
3752 * @param pInterface Pointer to this interface.
3753 * @param pvData Pointer the buffer to copy the bits to.
3754 * @param cbData Size of the buffer.
3755 * @param pcx Where to store the width of the bitmap. (optional)
3756 * @param pcy Where to store the height of the bitmap. (optional)
3757 * @param pcbData Where to store the actual size of the bitmap. (optional)
3758 * @see PDMIKEYBOARDPORT::pfnSnapshot() for details.
3759 */
3760static DECLCALLBACK(int) vgaPortSnapshot(PPDMIDISPLAYPORT pInterface, void *pvData, size_t cbData, uint32_t *pcx, uint32_t *pcy, size_t *pcbData)
3761{
3762 /* @todo r=sunlover: replace the method with a direct VRAM rendering like in vgaPortUpdateDisplayRect. */
3763 PPDMIDISPLAYCONNECTOR pConnector;
3764 PDMIDISPLAYCONNECTOR Connector;
3765 int32_t graphic_mode;
3766 uint32_t fRenderVRAM;
3767 size_t cbRequired;
3768 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3769 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3770 LogFlow(("vgaPortSnapshot: pvData=%p cbData=%d pcx=%p pcy=%p pcbData=%p\n", pvData, cbData, pcx, pcy, pcbData));
3771
3772 /*
3773 * Validate input.
3774 */
3775 if (!pvData)
3776 return VERR_INVALID_PARAMETER;
3777
3778 /*
3779 * Do a regular refresh first to resolve any pending resize issues.
3780 *
3781 * 20060317 It used to be pfnUpdateDisplay, but by VBVA design
3782 * only pfnUpdateDisplayAll is allowed to be called in VBVA mode.
3783 * Also since the goal here is to have updated display for screenshot,
3784 * the UpdateDisplayAll is even more logical to call. (sunlover)
3785 */
3786 pInterface->pfnUpdateDisplayAll(pInterface);
3787
3788 /*
3789 * Validate the buffer size.
3790 */
3791 cbRequired = RT_ALIGN_Z(pData->last_scr_width, 4) * pData->last_scr_height * 4;
3792 if (cbRequired > cbData)
3793 {
3794 Log(("vgaPortSnapshot: %d bytes are required, a buffer of %d bytes is profiled.\n", cbRequired, cbData));
3795 return VERR_BUFFER_OVERFLOW;
3796 }
3797
3798 /*
3799 * Temporarily replace the display connector interface with a fake one.
3800 */
3801 Connector.pu8Data = (uint8_t*)pvData;
3802 Connector.cBits = 32;
3803 Connector.cx = pData->pDrv->cx;
3804 Connector.cy = pData->pDrv->cy;
3805 Connector.cbScanline = RT_ALIGN_32(Connector.cx, 4) * 4;
3806 Connector.pfnRefresh = vgaDummyRefresh;
3807 Connector.pfnResize = vgaDummyResize;
3808 Connector.pfnUpdateRect = vgaDummyUpdateRect;
3809
3810 /* save & replace state data. */
3811 pConnector = pData->pDrv;
3812 pData->pDrv = &Connector;
3813 graphic_mode = pData->graphic_mode;
3814 pData->graphic_mode = -1; /* force a full refresh. */
3815 fRenderVRAM = pData->fRenderVRAM;
3816 pData->fRenderVRAM = 1; /* force the guest VRAM rendering to the given buffer. */
3817
3818 /* make the snapshot. */
3819 int rc = vga_update_display(pData);
3820
3821 /* restore */
3822 pData->pDrv = pConnector;
3823 pData->graphic_mode = graphic_mode;
3824 pData->fRenderVRAM = fRenderVRAM;
3825
3826 if (rc != VINF_SUCCESS)
3827 return rc;
3828
3829 /*
3830 * Return the result.
3831 */
3832 if (pcx)
3833 *pcx = Connector.cx;
3834 if (pcy)
3835 *pcy = Connector.cy;
3836 if (pcbData)
3837 *pcbData = cbRequired;
3838 LogFlow(("vgaPortSnapshot: returns VINF_SUCCESS (cx=%d cy=%d cbData=%d)\n", Connector.cx, Connector.cy, cbRequired));
3839 return VINF_SUCCESS;
3840}
3841
3842
3843/**
3844 * Copy bitmap to the display.
3845 *
3846 * @param pInterface Pointer to this interface.
3847 * @param pvData Pointer to the bitmap bits.
3848 * @param x The upper left corner x coordinate of the destination rectangle.
3849 * @param y The upper left corner y coordinate of the destination rectangle.
3850 * @param cx The width of the source and destination rectangles.
3851 * @param cy The height of the source and destination rectangles.
3852 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
3853 */
3854static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3855{
3856 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3857 int rc = VINF_SUCCESS;
3858 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3859 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
3860
3861 /*
3862 * Validate input.
3863 */
3864 if ( pvData
3865 && x < pData->pDrv->cx
3866 && cx <= pData->pDrv->cx
3867 && cx + x <= pData->pDrv->cx
3868 && y < pData->pDrv->cy
3869 && cy <= pData->pDrv->cy
3870 && cy + y <= pData->pDrv->cy)
3871 {
3872 /*
3873 * Determin bytes per pixel in the destination buffer.
3874 */
3875 size_t cbPixelDst = 0;
3876 switch (pData->pDrv->cBits)
3877 {
3878 case 8:
3879 cbPixelDst = 1;
3880 break;
3881 case 15:
3882 case 16:
3883 cbPixelDst = 2;
3884 break;
3885 case 24:
3886 cbPixelDst = 3;
3887 break;
3888 case 32:
3889 cbPixelDst = 4;
3890 break;
3891 default:
3892 rc = VERR_INVALID_PARAMETER;
3893 break;
3894 }
3895 if (VBOX_SUCCESS(rc))
3896 {
3897 /*
3898 * The blitting loop.
3899 */
3900 size_t cbLineSrc = RT_ALIGN_Z(cx, 4) * 4;
3901 uint8_t *pu8Src = (uint8_t *)pvData;
3902 size_t cbLineDst = pData->pDrv->cbScanline;
3903 uint8_t *pu8Dst = pData->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3904 uint32_t cyLeft = cy;
3905 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pData->pDrv->cBits)];
3906 Assert(pfnVgaDrawLine);
3907 while (cyLeft-- > 0)
3908 {
3909 pfnVgaDrawLine(pData, pu8Dst, pu8Src, cx);
3910 pu8Dst += cbLineDst;
3911 pu8Src += cbLineSrc;
3912 }
3913
3914 /*
3915 * Invalidate the area.
3916 */
3917 pData->pDrv->pfnUpdateRect(pData->pDrv, x, y, cx, cy);
3918 }
3919 }
3920 else
3921 rc = VERR_INVALID_PARAMETER;
3922
3923 LogFlow(("vgaPortDisplayBlt: returns %Vrc\n", rc));
3924 return rc;
3925}
3926
3927static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
3928{
3929 uint32_t v;
3930 vga_draw_line_func *vga_draw_line;
3931
3932 uint32_t cbPixelDst;
3933 uint32_t cbLineDst;
3934 uint8_t *pu8Dst;
3935
3936 uint32_t cbPixelSrc;
3937 uint32_t cbLineSrc;
3938 uint8_t *pu8Src;
3939
3940 uint32_t u32OffsetSrc, u32Dummy;
3941
3942 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3943
3944#ifdef DEBUG_sunlover
3945 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
3946#endif /* DEBUG_sunlover */
3947
3948 Assert(pInterface);
3949 Assert(s->pDrv);
3950 Assert(s->pDrv->pu8Data);
3951
3952 /* Check if there is something to do at all. */
3953 if (!s->fRenderVRAM)
3954 {
3955 /* The framebuffer uses the guest VRAM directly. */
3956#ifdef DEBUG_sunlover
3957 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
3958#endif /* DEBUG_sunlover */
3959 return;
3960 }
3961
3962 /* Correct negative x and y coordinates. */
3963 if (x < 0)
3964 {
3965 x += w; /* Compute xRight which is also the new width. */
3966 w = (x < 0) ? 0 : x;
3967 x = 0;
3968 }
3969
3970 if (y < 0)
3971 {
3972 y += h; /* Compute yBottom, which is also the new height. */
3973 h = (y < 0) ? 0 : y;
3974 y = 0;
3975 }
3976
3977 /* Also check if coords are greater than the display resolution. */
3978 if (x + w > s->pDrv->cx)
3979 {
3980#ifndef VBOX
3981 w = s->pDrv->cx > x? s->pDrv->cx - x: 0;
3982#else
3983 // x < 0 is not possible here
3984 w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
3985#endif
3986 }
3987
3988 if (y + h > s->pDrv->cy)
3989 {
3990#ifndef VBOX
3991 h = s->pDrv->cy > y? s->pDrv->cy - y: 0;
3992#else
3993 // y < 0 is not possible here
3994 h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
3995#endif
3996 }
3997
3998#ifdef DEBUG_sunlover
3999 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
4000#endif /* DEBUG_sunlover */
4001
4002 /* Check if there is something to do at all. */
4003 if (w == 0 || h == 0)
4004 {
4005 /* Empty rectangle. */
4006#ifdef DEBUG_sunlover
4007 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
4008#endif /* DEBUG_sunlover */
4009 return;
4010 }
4011
4012 /** @todo This method should be made universal and not only for VBVA.
4013 * VGA_DRAW_LINE* must be selected and src/dst address calculation
4014 * changed.
4015 */
4016
4017 /* Choose the rendering function. */
4018 switch(s->get_bpp(s))
4019 {
4020 default:
4021 case 0:
4022 /* A LFB mode is already disabled, but the callback is still called
4023 * by Display because VBVA buffer is being flushed.
4024 * Nothing to do, just return.
4025 */
4026 return;
4027 case 8:
4028 v = VGA_DRAW_LINE8;
4029 break;
4030 case 15:
4031 v = VGA_DRAW_LINE15;
4032 break;
4033 case 16:
4034 v = VGA_DRAW_LINE16;
4035 break;
4036 case 24:
4037 v = VGA_DRAW_LINE24;
4038 break;
4039 case 32:
4040 v = VGA_DRAW_LINE32;
4041 break;
4042 }
4043
4044 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
4045
4046 /* Compute source and destination addresses and pitches. */
4047 cbPixelDst = (s->pDrv->cBits + 7) / 8;
4048 cbLineDst = s->pDrv->cbScanline;
4049 pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4050
4051 cbPixelSrc = (s->get_bpp(s) + 7) / 8;
4052 s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
4053
4054 /* Assume that rendering is performed only on visible part of VRAM.
4055 * This is true because coordinates were verified.
4056 */
4057 pu8Src = s->vram_ptrHC;
4058 pu8Src += u32OffsetSrc + y * cbLineSrc + x * cbPixelSrc;
4059
4060 /* Render VRAM to framebuffer. */
4061
4062#ifdef DEBUG_sunlover
4063 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
4064#endif /* DEBUG_sunlover */
4065
4066 while (h-- > 0)
4067 {
4068 vga_draw_line (s, pu8Dst, pu8Src, w);
4069 pu8Dst += cbLineDst;
4070 pu8Src += cbLineSrc;
4071 }
4072
4073#ifdef DEBUG_sunlover
4074 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
4075#endif /* DEBUG_sunlover */
4076}
4077
4078static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
4079{
4080 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
4081
4082 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
4083
4084 s->fRenderVRAM = fRender;
4085}
4086
4087
4088static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer)
4089{
4090 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4091 if (pData->pDrv)
4092 pData->pDrv->pfnRefresh(pData->pDrv);
4093 if (pData->cMilliesRefreshInterval)
4094 TMTimerSetMillies(pTimer, pData->cMilliesRefreshInterval);
4095}
4096
4097
4098/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
4099
4100/**
4101 * Callback function for mapping an PCI I/O region.
4102 *
4103 * @return VBox status code.
4104 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
4105 * @param iRegion The region number.
4106 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
4107 * I/O port, else it's a physical address.
4108 * This address is *NOT* relative to pci_mem_base like earlier!
4109 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
4110 */
4111static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
4112{
4113 int rc;
4114 PVGASTATE pData = PDMINS2DATA(pPciDev->pDevIns, PVGASTATE);
4115 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%VGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
4116
4117 /*
4118 * VRam mapping.
4119 */
4120 if (iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH)
4121 {
4122 /*
4123 * Register and lock the VRAM.
4124 *
4125 * Windows usually re-initializes the PCI devices, so we have to check whether the memory was
4126 * already registered before trying to do that all over again.
4127 */
4128 PVM pVM = PDMDevHlpGetVM(pPciDev->pDevIns);
4129 if (pData->GCPhysVRAM)
4130 {
4131 AssertMsg(pData->GCPhysVRAM == GCPhysAddress,
4132 ("The Guest OS relocated our LFB! old GCPhysVRAM=%VGp new GCPhysAddress=%VGp\n",
4133 pData->GCPhysVRAM, GCPhysAddress));
4134 rc = VINF_SUCCESS;
4135 }
4136 else
4137 {
4138 /*
4139 * Register and lock the VRAM.
4140 */
4141 rc = MMR3PhysRegister(pVM, pData->vram_ptrHC, GCPhysAddress, pData->vram_size, MM_RAM_FLAGS_MMIO2, "VRam");
4142 if (VBOX_SUCCESS(rc))
4143 {
4144 if (!pData->GCPhysVRAM)
4145 rc = PGMR3HandlerPhysicalRegister(pVM, PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
4146 GCPhysAddress, GCPhysAddress + (pData->vram_size - 1),
4147 vgaR3LFBAccessHandler, pData,
4148 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pData->pDevInsHC->pvInstanceDataR0,
4149 g_DeviceVga.szGCMod, "vgaGCLFBAccessHandler", pData->pDevInsHC->pvInstanceDataGC,
4150 "VGA LFB");
4151 if (VBOX_SUCCESS(rc))
4152 {
4153 /*
4154 * Map the first 256KB of the VRAM into GC for GC VGA support.
4155 */
4156 RTGCPTR GCPtr;
4157 rc = MMR3HyperMapGCPhys(pVM, GCPhysAddress, VGA_MAPPING_SIZE, "VGA VRam", &GCPtr);
4158 if (VBOX_SUCCESS(rc))
4159 {
4160 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
4161
4162 pData->vram_ptrGC = GCPtr;
4163 pData->GCPhysVRAM = GCPhysAddress;
4164 return VINF_SUCCESS;
4165 }
4166 AssertMsgFailed(("MMR3HyperMapGCPhys failed, rc=%Vrc\n", rc));
4167 }
4168 else
4169 AssertMsgFailed(("Failed to register write handler for VRAM! rc=%Vrc\n", rc));
4170 }
4171 else
4172 AssertReleaseMsgFailed(("Failed to register VRAM! rc=%Vra\n", rc));
4173 }
4174 return rc;
4175 }
4176 else
4177 AssertReleaseMsgFailed(("Huh!?! iRegion=%d enmType=%d\n", iRegion, enmType));
4178 return VERR_INTERNAL_ERROR;
4179}
4180
4181
4182/* -=-=-=-=-=- Ring3: Misc Wrappers -=-=-=-=-=- */
4183
4184/**
4185 * Saves a state of the VGA device.
4186 *
4187 * @returns VBox status code.
4188 * @param pDevIns The device instance.
4189 * @param pSSMHandle The handle to save the state to.
4190 */
4191static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
4192{
4193 vga_save(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE));
4194 return VINF_SUCCESS;
4195}
4196
4197
4198/**
4199 * Loads a saved VGA device state.
4200 *
4201 * @returns VBox status code.
4202 * @param pDevIns The device instance.
4203 * @param pSSMHandle The handle to the saved state.
4204 * @param u32Version The data unit version number.
4205 */
4206static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
4207{
4208 if (vga_load(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE), u32Version))
4209 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4210 return VINF_SUCCESS;
4211}
4212
4213
4214/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
4215
4216/**
4217 * Reset notification.
4218 *
4219 * @returns VBox status.
4220 * @param pDevIns The device instance data.
4221 */
4222static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
4223{
4224 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4225 char *pchStart;
4226 char *pchEnd;
4227 LogFlow(("vgaReset\n"));
4228
4229 /* Clear the VRAM ourselves. */
4230 if (pData->vram_ptrHC && pData->vram_size)
4231 {
4232#ifdef LOG_ENABLED /** @todo separate function. */
4233 /* First dump the textmode contents to the log; handy for capturing Windows blue screens. */
4234 uint8_t graphic_mode;
4235 VGAState *s = pData;
4236
4237 if (!(s->ar_index & 0x20)) {
4238 graphic_mode = GMODE_BLANK;
4239 } else {
4240 graphic_mode = s->gr[6] & 1;
4241 }
4242 switch(graphic_mode)
4243 case GMODE_TEXT:
4244 {
4245 int cw, height, width, cheight, cx_min, cx_max, cy, cx;
4246 int x_incr;
4247 uint8_t *s1, *src, ch, cattr;
4248 int line_offset;
4249 uint16_t ch_attr;
4250
4251 line_offset = s->line_offset;
4252 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 4);
4253
4254 /* total width & height */
4255 cheight = (s->cr[9] & 0x1f) + 1;
4256 cw = 8;
4257 if (!(s->sr[1] & 0x01))
4258 cw = 9;
4259 if (s->sr[1] & 0x08)
4260 cw = 16; /* NOTE: no 18 pixel wide */
4261 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
4262 width = (s->cr[0x01] + 1);
4263 if (s->cr[0x06] == 100) {
4264 /* ugly hack for CGA 160x100x16 - explain me the logic */
4265 height = 100;
4266 } else {
4267 height = s->cr[0x12] |
4268 ((s->cr[0x07] & 0x02) << 7) |
4269 ((s->cr[0x07] & 0x40) << 3);
4270 height = (height + 1) / cheight;
4271 }
4272 if ((height * width) > CH_ATTR_SIZE) {
4273 /* better than nothing: exit if transient size is too big */
4274 break;
4275 }
4276 RTLogPrintf("VGA textmode BEGIN (%dx%d):\n\n", height, width);
4277 for(cy = 0; cy < height; cy++) {
4278 src = s1;
4279 cx_min = width;
4280 cx_max = -1;
4281 for(cx = 0; cx < width; cx++) {
4282 ch_attr = *(uint16_t *)src;
4283 if (cx < cx_min)
4284 cx_min = cx;
4285 if (cx > cx_max)
4286 cx_max = cx;
4287# ifdef WORDS_BIGENDIAN
4288 ch = ch_attr >> 8;
4289 cattr = ch_attr & 0xff;
4290# else
4291 ch = ch_attr & 0xff;
4292 cattr = ch_attr >> 8;
4293# endif
4294 RTLogPrintf("%c", ch);
4295
4296 src += 4;
4297 }
4298 if (cx_max != -1)
4299 RTLogPrintf("\n");
4300
4301 s1 += line_offset;
4302 }
4303 RTLogPrintf("VGA textmode END:\n\n");
4304 }
4305
4306#endif
4307 memset(pData->vram_ptrHC, 0, pData->vram_size);
4308 }
4309
4310 /*
4311 * Zero most of it.
4312 *
4313 * Unlike vga_reset we're leaving out a few members which believe must
4314 * remain unchanged....
4315 */
4316 /* 1st part. */
4317 pchStart = (char *)&pData->latch;
4318 pchEnd = (char *)&pData->invalidated_y_table;
4319 memset(pchStart, 0, pchEnd - pchStart);
4320
4321 /* 2nd part. */
4322 pchStart = (char *)&pData->last_palette;
4323 pchEnd = (char *)&pData->u32Marker;
4324 memset(pchStart, 0, pchEnd - pchStart);
4325
4326
4327 /*
4328 * Restore and re-init some bits.
4329 */
4330 pData->get_bpp = vga_get_bpp;
4331 pData->get_offsets = vga_get_offsets;
4332 pData->get_resolution = vga_get_resolution;
4333 pData->graphic_mode = -1; /* Force full update. */
4334#ifdef CONFIG_BOCHS_VBE
4335 pData->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
4336 pData->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
4337 pData->vbe_bank_mask = ((pData->vram_size >> 16) - 1);
4338#endif /* CONFIG_BOCHS_VBE */
4339
4340 /*
4341 * Reset the LBF mapping.
4342 */
4343 pData->fLFBUpdated = false;
4344 if ( ( pData->fGCEnabled
4345 || pData->fR0Enabled)
4346 && pData->GCPhysVRAM)
4347 {
4348 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
4349 AssertRC(rc);
4350 }
4351
4352 /* notify port handler */
4353 if (pData->pDrv)
4354 pData->pDrv->pfnReset(pData->pDrv);
4355}
4356
4357
4358/**
4359 * Device relocation callback.
4360 *
4361 * @param pDevIns Pointer to the device instance.
4362 * @param offDelta The relocation delta relative to the old location.
4363 *
4364 * @see FNPDMDEVRELOCATE for details.
4365 */
4366static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4367{
4368 if (offDelta)
4369 {
4370 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4371 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
4372
4373 pData->GCPtrLFBHandler += offDelta;
4374 pData->vram_ptrGC += offDelta;
4375 }
4376}
4377
4378
4379/**
4380 * Attach command.
4381 *
4382 * This is called to let the device attach to a driver for a specified LUN
4383 * during runtime. This is not called during VM construction, the device
4384 * constructor have to attach to all the available drivers.
4385 *
4386 * This is like plugging in the monitor after turning on the PC.
4387 *
4388 * @returns VBox status code.
4389 * @param pDevIns The device instance.
4390 * @param iLUN The logical unit which is being detached.
4391 */
4392static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN)
4393{
4394 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4395 switch (iLUN)
4396 {
4397 /* LUN #0: Display port. */
4398 case 0:
4399 {
4400 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pData->Base, &pData->pDrvBase, "Display Port");
4401 if (VBOX_SUCCESS(rc))
4402 {
4403 pData->pDrv = (PDMIDISPLAYCONNECTOR*)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_DISPLAY_CONNECTOR);
4404 if (pData->pDrv)
4405 {
4406 /* pData->pDrv->pu8Data can be NULL when there is no framebuffer. */
4407 if ( pData->pDrv->pfnRefresh
4408 && pData->pDrv->pfnResize
4409 && pData->pDrv->pfnUpdateRect)
4410 rc = VINF_SUCCESS;
4411 else
4412 {
4413 Assert(pData->pDrv->pfnRefresh);
4414 Assert(pData->pDrv->pfnResize);
4415 Assert(pData->pDrv->pfnUpdateRect);
4416 pData->pDrv = NULL;
4417 pData->pDrvBase = NULL;
4418 rc = VERR_INTERNAL_ERROR;
4419 }
4420 }
4421 else
4422 {
4423 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Vrc\n", rc));
4424 pData->pDrvBase = NULL;
4425 rc = VERR_PDM_MISSING_INTERFACE;
4426 }
4427 }
4428 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4429 {
4430 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
4431 rc = VINF_SUCCESS;
4432 }
4433 else
4434 AssertMsgFailed(("Failed to attach LUN #0! rc=%Vrc\n", rc));
4435 return rc;
4436 }
4437
4438 default:
4439 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4440 return VERR_PDM_NO_SUCH_LUN;
4441 }
4442}
4443
4444
4445/**
4446 * Detach notification.
4447 *
4448 * This is called when a driver is detaching itself from a LUN of the device.
4449 * The device should adjust it's state to reflect this.
4450 *
4451 * This is like unplugging the monitor while the PC is still running.
4452 *
4453 * @param pDevIns The device instance.
4454 * @param iLUN The logical unit which is being detached.
4455 */
4456static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN)
4457{
4458 /*
4459 * Reset the interfaces and update the controller state.
4460 */
4461 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4462 switch (iLUN)
4463 {
4464 /* LUN #0: Display port. */
4465 case 0:
4466 pData->pDrv = NULL;
4467 pData->pDrvBase = NULL;
4468 break;
4469
4470 default:
4471 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4472 break;
4473 }
4474}
4475
4476
4477
4478/**
4479 * Construct a VGA device instance for a VM.
4480 *
4481 * @returns VBox status.
4482 * @param pDevIns The device instance data.
4483 * If the registration structure is needed, pDevIns->pDevReg points to it.
4484 * @param iInstance Instance number. Use this to figure out which registers and such to use.
4485 * The device number is also found in pDevIns->iInstance, but since it's
4486 * likely to be freqently used PDM passes it as parameter.
4487 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
4488 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
4489 * iInstance it's expected to be used a bit in this function.
4490 */
4491static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
4492{
4493 static bool fExpandDone = false;
4494 bool f;
4495 int rc;
4496 unsigned i;
4497 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4498 PVM pVM = PDMDevHlpGetVM(pDevIns);
4499#ifdef VBE_NEW_DYN_LIST
4500 uint32_t cCustomModes;
4501 uint32_t cyReduction;
4502 PVBEHEADER pVBEDataHdr;
4503 ModeInfoListItem *pCurMode;
4504 unsigned cb;
4505#endif
4506 Assert(iInstance == 0);
4507 Assert(pVM);
4508
4509 /*
4510 * Init static data.
4511 */
4512 if (!fExpandDone)
4513 {
4514 fExpandDone = true;
4515 vga_init_expand();
4516 }
4517
4518 /*
4519 * Validate configuration.
4520 */
4521 if (!CFGMR3AreValuesValid(pCfgHandle, "VRamSize\0"
4522 "GCEnabled\0"
4523 "R0Enabled\0"
4524 "CustomVideoModes\0"
4525 "HeightReduction\0"
4526 "CustomVideoMode1\0"
4527 "CustomVideoMode2\0"
4528 "CustomVideoMode3\0"
4529 "CustomVideoMode4\0"
4530 "CustomVideoMode5\0"
4531 "CustomVideoMode6\0"
4532 "CustomVideoMode7\0"
4533 "CustomVideoMode8\0"
4534 "CustomVideoMode9\0"
4535 "CustomVideoMode10\0"
4536 "CustomVideoMode11\0"
4537 "CustomVideoMode12\0"
4538 "CustomVideoMode13\0"
4539 "CustomVideoMode14\0"
4540 "CustomVideoMode15\0"
4541 "CustomVideoMode16\0"))
4542 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4543 N_("Invalid configuration for vga device"));
4544
4545 /*
4546 * Init state data.
4547 */
4548 rc = CFGMR3QueryU32(pCfgHandle, "VRamSize", &pData->vram_size);
4549 if (VBOX_FAILURE(rc) || !pData->vram_size)
4550 pData->vram_size = VGA_VRAM_DEFAULT;
4551 else if (pData->vram_size > VGA_VRAM_MAX)
4552 {
4553 AssertMsgFailed(("vram_size=%d max=%d\n", pData->vram_size, VGA_VRAM_MAX));
4554 pData->vram_size = VGA_VRAM_MAX;
4555 }
4556 else if (pData->vram_size < VGA_VRAM_MIN)
4557 {
4558 AssertMsgFailed(("vram_size=%d min=%d\n", pData->vram_size, VGA_VRAM_MIN));
4559 pData->vram_size = RT_ALIGN_32(pData->vram_size, _1M);
4560 }
4561 Log(("VGA: VRamSize=%#x\n", pData->vram_size));
4562
4563 pData->fGCEnabled = true;
4564 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &f);
4565 if (VBOX_SUCCESS(rc) && !f)
4566 pData->fGCEnabled = false;
4567 Log(("VGA: fGCEnabled=%d\n", pData->fGCEnabled));
4568
4569 pData->fR0Enabled = true;
4570 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &f);
4571 if (VBOX_SUCCESS(rc) && !f)
4572 pData->fR0Enabled = false;
4573 Log(("VGA: fR0Enabled=%d\n", pData->fR0Enabled));
4574
4575 pData->pDevInsHC = pDevIns;
4576
4577 vgaR3Reset(pDevIns);
4578
4579 /* The PCI devices configuration. */
4580 pData->Dev.config[0x00] = 0xee; /* PCI vendor, just a free bogus value */
4581 pData->Dev.config[0x01] = 0x80;
4582
4583 pData->Dev.config[0x02] = 0xef; /* Device ID */
4584 pData->Dev.config[0x03] = 0xbe;
4585
4586 pData->Dev.config[0x0a] = 0x00; /* VGA controller */
4587 pData->Dev.config[0x0b] = 0x03;
4588 pData->Dev.config[0x0e] = 0x00; /* header_type */
4589
4590 /* The LBF access handler - error handling is better here than in the map function. */
4591 rc = PDMR3GetSymbolGCLazy(pVM, pDevIns->pDevReg->szGCMod, "vgaGCLFBAccessHandler", &pData->GCPtrLFBHandler);
4592 if (VBOX_FAILURE(rc))
4593 {
4594 AssertReleaseMsgFailed(("PDMR3GetSymbolGC(, %s, \"vgaGCLFBAccessHandler\",) -> %Vrc\n", pDevIns->pDevReg->szGCMod, rc));
4595 return rc;
4596 }
4597
4598 /* the interfaces. */
4599 pData->Base.pfnQueryInterface = vgaPortQueryInterface;
4600
4601 pData->Port.pfnUpdateDisplay = vgaPortUpdateDisplay;
4602 pData->Port.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
4603 pData->Port.pfnQueryColorDepth = vgaPortQueryColorDepth;
4604 pData->Port.pfnSetRefreshRate = vgaPortSetRefreshRate;
4605 pData->Port.pfnSnapshot = vgaPortSnapshot;
4606 pData->Port.pfnDisplayBlt = vgaPortDisplayBlt;
4607 pData->Port.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
4608 pData->Port.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
4609
4610
4611 /*
4612 * Register I/O ports, ROM and save state.
4613 */
4614 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
4615 if (VBOX_FAILURE(rc))
4616 return rc;
4617 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
4618 if (VBOX_FAILURE(rc))
4619 return rc;
4620 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
4621 if (VBOX_FAILURE(rc))
4622 return rc;
4623 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
4624 if (VBOX_FAILURE(rc))
4625 return rc;
4626 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
4627 if (VBOX_FAILURE(rc))
4628 return rc;
4629
4630#ifdef CONFIG_BOCHS_VBE
4631 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
4632 if (VBOX_FAILURE(rc))
4633 return rc;
4634 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
4635 if (VBOX_FAILURE(rc))
4636 return rc;
4637#if 0
4638 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4639 and tries to map other devices there */
4640 /* Old Bochs. */
4641 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff80, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, "VGA/VBE - Index Old");
4642 if (VBOX_FAILURE(rc))
4643 return rc;
4644 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff81, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, "VGA/VBE - Data Old");
4645 if (VBOX_FAILURE(rc))
4646 return rc;
4647#endif
4648#endif /* CONFIG_BOCHS_VBE */
4649
4650 /* guest context extension */
4651 if (pData->fGCEnabled)
4652 {
4653 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4654 if (VBOX_FAILURE(rc))
4655 return rc;
4656 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4657 if (VBOX_FAILURE(rc))
4658 return rc;
4659 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4660 if (VBOX_FAILURE(rc))
4661 return rc;
4662 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4663 if (VBOX_FAILURE(rc))
4664 return rc;
4665 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4666 if (VBOX_FAILURE(rc))
4667 return rc;
4668#ifdef CONFIG_BOCHS_VBE
4669 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4670 if (VBOX_FAILURE(rc))
4671 return rc;
4672 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4673 if (VBOX_FAILURE(rc))
4674 return rc;
4675
4676#if 0
4677 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4678 and tries to map other devices there */
4679 /* Old Bochs. */
4680 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4681 if (VBOX_FAILURE(rc))
4682 return rc;
4683 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4684 if (VBOX_FAILURE(rc))
4685 return rc;
4686#endif
4687
4688#endif /* CONFIG_BOCHS_VBE */
4689 }
4690
4691 /* R0 context extension */
4692 if (pData->fR0Enabled)
4693 {
4694 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4695 if (VBOX_FAILURE(rc))
4696 return rc;
4697 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4698 if (VBOX_FAILURE(rc))
4699 return rc;
4700 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4701 if (VBOX_FAILURE(rc))
4702 return rc;
4703 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4704 if (VBOX_FAILURE(rc))
4705 return rc;
4706 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4707 if (VBOX_FAILURE(rc))
4708 return rc;
4709#ifdef CONFIG_BOCHS_VBE
4710 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4711 if (VBOX_FAILURE(rc))
4712 return rc;
4713 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4714 if (VBOX_FAILURE(rc))
4715 return rc;
4716
4717#if 0
4718 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4719 and tries to map other devices there */
4720 /* Old Bochs. */
4721 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4722 if (VBOX_FAILURE(rc))
4723 return rc;
4724 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4725 if (VBOX_FAILURE(rc))
4726 return rc;
4727#endif
4728
4729#endif /* CONFIG_BOCHS_VBE */
4730 }
4731
4732 /* vga mmio */
4733 rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
4734 if (VBOX_FAILURE(rc))
4735 return rc;
4736 if (pData->fGCEnabled)
4737 {
4738 rc = PDMDevHlpMMIORegisterGC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4739 if (VBOX_FAILURE(rc))
4740 return rc;
4741 }
4742 if (pData->fR0Enabled)
4743 {
4744 rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4745 if (VBOX_FAILURE(rc))
4746 return rc;
4747 }
4748
4749 /* vga bios */
4750 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
4751 if (VBOX_FAILURE(rc))
4752 return rc;
4753 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4754 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4755 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, g_cbVgaBiosBinary, &g_abVgaBiosBinary[0],
4756 false /* fShadow */, "VGA BIOS");
4757 if (VBOX_FAILURE(rc))
4758 return rc;
4759
4760 /* save */
4761 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
4762 NULL, vgaR3SaveExec, NULL,
4763 NULL, vgaR3LoadExec, NULL);
4764 if (VBOX_FAILURE(rc))
4765 return rc;
4766
4767 /* PCI */
4768 rc = PDMDevHlpPCIRegister(pDevIns, &pData->Dev);
4769 if (VBOX_FAILURE(rc))
4770 return rc;
4771 /*AssertMsg(pData->Dev.devfn == 16 || iInstance != 0, ("pData->Dev.devfn=%d\n", pData->Dev.devfn));*/
4772 if (pData->Dev.devfn != 16 && iInstance == 0)
4773 Log(("!!WARNING!!: pData->dev.devfn=%d (ignore if testcase or no started by Main)\n", pData->Dev.devfn));
4774 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, pData->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
4775 if (VBOX_FAILURE(rc))
4776 return rc;
4777
4778 /*
4779 * Create the refresh timer.
4780 */
4781 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh, "VGA Refresh Timer", &pData->RefreshTimer);
4782 if (VBOX_FAILURE(rc))
4783 return rc;
4784
4785 /*
4786 * Attach to the display.
4787 */
4788 rc = vgaAttach(pDevIns, 0 /* display LUN # */);
4789 if (VBOX_FAILURE(rc))
4790 return rc;
4791
4792 /*
4793 * Allocate the VRAM.
4794 */
4795 rc = SUPPageAlloc(pData->vram_size >> PAGE_SHIFT, (void **)&pData->vram_ptrHC);
4796 if (VBOX_FAILURE(rc))
4797 {
4798 AssertMsgFailed(("SUPPageAlloc(%#x,) -> %d\n", pData->vram_size, rc));
4799 return rc;
4800 }
4801
4802#ifdef VBE_NEW_DYN_LIST
4803 /*
4804 * Compute buffer size for the VBE BIOS Extra Data.
4805 */
4806 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
4807
4808 rc = CFGMR3QueryU32(pCfgHandle, "HeightReduction", &cyReduction);
4809 if (VBOX_SUCCESS(rc) && cyReduction)
4810 cb *= 2; /* Default mode list will be twice long */
4811 else
4812 cyReduction = 0;
4813
4814 rc = CFGMR3QueryU32(pCfgHandle, "CustomVideoModes", &cCustomModes);
4815 if (VBOX_SUCCESS(rc) && cCustomModes)
4816 cb += sizeof(ModeInfoListItem) * cCustomModes;
4817 else
4818 cCustomModes = 0;
4819
4820 /*
4821 * Allocate and initialize buffer for the VBE BIOS Extra Data.
4822 */
4823 pData->cbVBEExtraData = sizeof(VBEHEADER) + cb;
4824 pData->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pData->cbVBEExtraData);
4825 if (!pData->pu8VBEExtraData)
4826 return VERR_NO_MEMORY;
4827
4828 pVBEDataHdr = (PVBEHEADER)pData->pu8VBEExtraData;
4829 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
4830 pVBEDataHdr->cbData = cb;
4831
4832#ifndef VRAM_SIZE_FIX
4833 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
4834 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
4835#else /* VRAM_SIZE_FIX defined */
4836 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
4837 for (i = 0; i < MODE_INFO_SIZE; i++)
4838 {
4839 uint32_t pixelWidth, reqSize;
4840 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4841 pixelWidth = 2;
4842 else
4843 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
4844 reqSize = mode_info_list[i].info.XResolution
4845 * mode_info_list[i].info.YResolution
4846 * pixelWidth;
4847 if (reqSize >= pData->vram_size)
4848 continue;
4849 *pCurMode = mode_info_list[i];
4850 pCurMode++;
4851 }
4852#endif /* VRAM_SIZE_FIX defined */
4853
4854 /*
4855 * Copy default modes with subtractred YResolution.
4856 */
4857 if (cyReduction)
4858 {
4859 ModeInfoListItem *pDefMode = mode_info_list;
4860 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
4861#ifndef VRAM_SIZE_FIX
4862 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
4863 {
4864 *pCurMode = *pDefMode;
4865 pCurMode->mode += 0x30;
4866 pCurMode->info.YResolution -= cyReduction;
4867 }
4868#else /* VRAM_SIZE_FIX defined */
4869 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
4870 {
4871 uint32_t pixelWidth, reqSize;
4872 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4873 pixelWidth = 2;
4874 else
4875 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
4876 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
4877 if (reqSize >= pData->vram_size)
4878 continue;
4879 *pCurMode = *pDefMode;
4880 pCurMode->mode += 0x30;
4881 pCurMode->info.YResolution -= cyReduction;
4882 pCurMode++;
4883 }
4884#endif /* VRAM_SIZE_FIX defined */
4885 }
4886
4887
4888 /*
4889 * Add custom modes.
4890 */
4891 if (cCustomModes)
4892 {
4893 uint16_t u16CurMode = 0x160;
4894 for (i = 1; i <= cCustomModes; i++)
4895 {
4896 char szExtraDataKey[sizeof("CustomVideoModeXX")];
4897 char *pszExtraData = NULL;
4898
4899 /* query and decode the custom mode string. */
4900 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
4901 rc = CFGMR3QueryStringAlloc(pCfgHandle, szExtraDataKey, &pszExtraData);
4902 if (VBOX_SUCCESS(rc))
4903 {
4904 ModeInfoListItem *pDefMode = mode_info_list;
4905 unsigned int cx, cy, cBits, cParams;
4906 uint16_t u16DefMode;
4907
4908 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
4909 if ( cParams != 3
4910 || (cBits != 16 && cBits != 24 && cBits != 32))
4911 {
4912 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
4913 return VERR_VGA_INVALID_CUSTOM_MODE;
4914 }
4915#ifdef VRAM_SIZE_FIX
4916 if (cx * cy * cBits / 8 >= pData->vram_size)
4917 {
4918 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",
4919 cx, cy, cBits, pData->vram_size / _1M));
4920 return VERR_VGA_INVALID_CUSTOM_MODE;
4921 }
4922#endif /* VRAM_SIZE_FIX defined */
4923 MMR3HeapFree(pszExtraData);
4924
4925 /* Use defaults from max@bpp mode. */
4926 switch (cBits)
4927 {
4928 case 16:
4929 u16DefMode = VBE_VESA_MODE_1024X768X565;
4930 break;
4931
4932 case 24:
4933 u16DefMode = VBE_VESA_MODE_1024X768X888;
4934 break;
4935
4936 case 32:
4937 u16DefMode = VBE_OWN_MODE_1024X768X8888;
4938 break;
4939
4940 default: /* gcc, shut up! */
4941 AssertMsgFailed(("gone postal!\n"));
4942 continue;
4943 }
4944
4945 while ( pDefMode->mode != u16DefMode
4946 && pDefMode->mode != VBE_VESA_MODE_END_OF_LIST)
4947 pDefMode++;
4948 Assert(pDefMode->mode != VBE_VESA_MODE_END_OF_LIST);
4949
4950 *pCurMode = *pDefMode;
4951 pCurMode->mode = u16CurMode++;
4952
4953 /* adjust defaults */
4954 pCurMode->info.XResolution = cx;
4955 pCurMode->info.YResolution = cy;
4956
4957 switch (cBits)
4958 {
4959 case 16:
4960 pCurMode->info.BytesPerScanLine = cx * 2;
4961 pCurMode->info.LinBytesPerScanLine = cx * 2;
4962 break;
4963
4964 case 24:
4965 pCurMode->info.BytesPerScanLine = cx * 3;
4966 pCurMode->info.LinBytesPerScanLine = cx * 3;
4967 break;
4968
4969 case 32:
4970 pCurMode->info.BytesPerScanLine = cx * 4;
4971 pCurMode->info.LinBytesPerScanLine = cx * 4;
4972 break;
4973 }
4974
4975 /* commit it */
4976 pCurMode++;
4977 }
4978 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
4979 {
4980 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Vrc\n", szExtraDataKey, rc));
4981 return rc;
4982 }
4983 } /* foreach custom mode key */
4984 }
4985
4986 /*
4987 * Add the "End of list" mode.
4988 */
4989 memset(pCurMode, 0, sizeof(*pCurMode));
4990 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
4991
4992 /*
4993 * Register I/O Port for the VBE BIOS Extra Data.
4994 */
4995 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
4996 if (VBOX_FAILURE(rc))
4997 return rc;
4998#endif
4999
5000 /*
5001 * Statistics.
5002 */
5003 STAM_REG(pVM, &pData->StatGCMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
5004 STAM_REG(pVM, &pData->StatGCMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
5005 STAM_REG(pVM, &pData->StatGCIOPortRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortRead() body.");
5006 STAM_REG(pVM, &pData->StatGCIOPortWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortWrite() body.");
5007
5008 return VINF_SUCCESS;
5009}
5010
5011
5012/**
5013 * Destruct a device instance.
5014 *
5015 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5016 * resources can be freed correctly.
5017 *
5018 * @param pDevIns The device instance data.
5019 */
5020static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5021{
5022#ifdef VBE_NEW_DYN_LIST
5023 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
5024 LogFlow(("vgaR3Destruct:\n"));
5025
5026 /*
5027 * Free MM heap pointers.
5028 */
5029 if (pData->pu8VBEExtraData)
5030 {
5031 MMR3HeapFree(pData->pu8VBEExtraData);
5032 pData->pu8VBEExtraData = NULL;
5033 }
5034#endif
5035
5036#if 0 /** @todo r=bird: We can't free the buffer here because it's still locked.
5037 * (That's the reason why we didn't do it earlier.) */
5038 /*
5039 * Free the VRAM.
5040 */
5041 int rc = SUPPageFree(pData->vram_ptrHC, pData->vram_size >> PAGE_SHIFT);
5042 if (VBOX_FAILURE(rc))
5043 {
5044 AssertMsgFailed(("SUPPageFree(%p, %#x) -> %d\n", pData->vram_ptrHC, pData->vram_size, rc));
5045 return rc;
5046 }
5047 pData->vram_ptrHC = NULL;
5048#endif
5049
5050 return VINF_SUCCESS;
5051}
5052
5053
5054/**
5055 * The device registration structure.
5056 */
5057const PDMDEVREG g_DeviceVga =
5058{
5059 /* u32Version */
5060 PDM_DEVREG_VERSION,
5061 /* szDeviceName */
5062 "vga",
5063 /* szGCMod */
5064 "VBoxDDGC.gc",
5065 /* szR0Mod */
5066 "VBoxDDR0.r0",
5067 /* pszDescription */
5068 "VGA Adaptor with VESA extensions.",
5069 /* fFlags */
5070 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
5071 /* fClass */
5072 PDM_DEVREG_CLASS_GRAPHICS,
5073 /* cMaxInstances */
5074 1,
5075 /* cbInstance */
5076 sizeof(VGASTATE),
5077 /* pfnConstruct */
5078 vgaR3Construct,
5079 /* pfnDestruct */
5080 vgaR3Destruct,
5081 /* pfnRelocate */
5082 vgaR3Relocate,
5083 /* pfnIOCtl */
5084 NULL,
5085 /* pfnPowerOn */
5086 NULL,
5087 /* pfnReset */
5088 vgaR3Reset,
5089 /* pfnSuspend */
5090 NULL,
5091 /* pfnResume */
5092 NULL,
5093 /* pfnAttach */
5094 vgaAttach,
5095 /* pfnDetach */
5096 vgaDetach,
5097 /* pfnQueryInterface */
5098 NULL,
5099 /* pfnInitComplete */
5100 NULL
5101};
5102
5103#endif /* !IN_RING3 */
5104#endif /* VBOX */
5105#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5106
5107/*
5108 * Local Variables:
5109 * nuke-trailing-whitespace-p:nil
5110 * End:
5111 */
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