VirtualBox

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

Last change on this file since 7218 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
File size: 160.4 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 (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will 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 (s->pDrv->pu8Data == s->vram_ptrHC) /* Do not clear the VRAM itself. */
2130 return;
2131 if (!full_update)
2132 return;
2133 if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
2134 return;
2135 if (s->pDrv->cBits == 8)
2136 val = s->rgb_to_pixel(0, 0, 0);
2137 else
2138 val = 0;
2139 w = s->last_scr_width * ((s->pDrv->cBits + 7) >> 3);
2140 d = s->pDrv->pu8Data;
2141 for(i = 0; i < (int)s->last_scr_height; i++) {
2142 memset(d, val, w);
2143 d += cbScanline;
2144 }
2145 s->pDrv->pfnUpdateRect(s->pDrv, 0, 0, s->last_scr_width, s->last_scr_height);
2146#endif /* VBOX */
2147}
2148
2149#define GMODE_TEXT 0
2150#define GMODE_GRAPH 1
2151#define GMODE_BLANK 2
2152
2153#ifndef VBOX
2154void vga_update_display(void)
2155{
2156 VGAState *s = vga_state;
2157#else /* VBOX */
2158static int vga_update_display(PVGASTATE s)
2159{
2160 int rc = VINF_SUCCESS;
2161#endif /* VBOX */
2162 int full_update, graphic_mode;
2163
2164#ifndef VBOX
2165 if (s->ds->depth == 0) {
2166#else /* VBOX */
2167 if (s->pDrv->cBits == 0) {
2168#endif /* VBOX */
2169 /* nothing to do */
2170 } else {
2171#ifndef VBOX
2172 switch(s->ds->depth) {
2173#else /* VBOX */
2174 switch(s->pDrv->cBits) {
2175#endif /* VBOX */
2176 case 8:
2177 s->rgb_to_pixel = rgb_to_pixel8_dup;
2178 break;
2179 case 15:
2180 s->rgb_to_pixel = rgb_to_pixel15_dup;
2181 break;
2182 default:
2183 case 16:
2184 s->rgb_to_pixel = rgb_to_pixel16_dup;
2185 break;
2186 case 32:
2187 s->rgb_to_pixel = rgb_to_pixel32_dup;
2188 break;
2189 }
2190
2191 full_update = 0;
2192 if (!(s->ar_index & 0x20)) {
2193 graphic_mode = GMODE_BLANK;
2194 } else {
2195 graphic_mode = s->gr[6] & 1;
2196 }
2197 if (graphic_mode != s->graphic_mode) {
2198 s->graphic_mode = graphic_mode;
2199 full_update = 1;
2200 }
2201 switch(graphic_mode) {
2202 case GMODE_TEXT:
2203#ifdef VBOX
2204 rc =
2205#endif /* VBOX */
2206 vga_draw_text(s, full_update);
2207 break;
2208 case GMODE_GRAPH:
2209#ifdef VBOX
2210 rc =
2211#endif /* VBOX */
2212 vga_draw_graphic(s, full_update);
2213 break;
2214 case GMODE_BLANK:
2215 default:
2216 vga_draw_blank(s, full_update);
2217 break;
2218 }
2219 }
2220#ifdef VBOX
2221 return rc;
2222#endif /* VBOX */
2223}
2224
2225/* force a full display refresh */
2226#ifndef VBOX
2227void vga_invalidate_display(void)
2228{
2229 VGAState *s = vga_state;
2230
2231 s->last_width = -1;
2232 s->last_height = -1;
2233}
2234#endif /* !VBOX */
2235
2236#ifndef VBOX /* see vgaR3Reset() */
2237static void vga_reset(VGAState *s)
2238{
2239 memset(s, 0, sizeof(VGAState));
2240 s->graphic_mode = -1; /* force full update */
2241}
2242#endif /* !VBOX */
2243
2244#ifndef VBOX
2245static CPUReadMemoryFunc *vga_mem_read[3] = {
2246 vga_mem_readb,
2247 vga_mem_readw,
2248 vga_mem_readl,
2249};
2250
2251static CPUWriteMemoryFunc *vga_mem_write[3] = {
2252 vga_mem_writeb,
2253 vga_mem_writew,
2254 vga_mem_writel,
2255};
2256#endif /* !VBOX */
2257
2258static void vga_save(QEMUFile *f, void *opaque)
2259{
2260 VGAState *s = (VGAState*)opaque;
2261 int i;
2262
2263 qemu_put_be32s(f, &s->latch);
2264 qemu_put_8s(f, &s->sr_index);
2265 qemu_put_buffer(f, s->sr, 8);
2266 qemu_put_8s(f, &s->gr_index);
2267 qemu_put_buffer(f, s->gr, 16);
2268 qemu_put_8s(f, &s->ar_index);
2269 qemu_put_buffer(f, s->ar, 21);
2270 qemu_put_be32s(f, &s->ar_flip_flop);
2271 qemu_put_8s(f, &s->cr_index);
2272 qemu_put_buffer(f, s->cr, 256);
2273 qemu_put_8s(f, &s->msr);
2274 qemu_put_8s(f, &s->fcr);
2275 qemu_put_8s(f, &s->st00);
2276 qemu_put_8s(f, &s->st01);
2277
2278 qemu_put_8s(f, &s->dac_state);
2279 qemu_put_8s(f, &s->dac_sub_index);
2280 qemu_put_8s(f, &s->dac_read_index);
2281 qemu_put_8s(f, &s->dac_write_index);
2282 qemu_put_buffer(f, s->dac_cache, 3);
2283 qemu_put_buffer(f, s->palette, 768);
2284
2285 qemu_put_be32s(f, &s->bank_offset);
2286#ifdef CONFIG_BOCHS_VBE
2287 qemu_put_byte(f, 1);
2288 qemu_put_be16s(f, &s->vbe_index);
2289 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2290 qemu_put_be16s(f, &s->vbe_regs[i]);
2291 qemu_put_be32s(f, &s->vbe_start_addr);
2292 qemu_put_be32s(f, &s->vbe_line_offset);
2293 qemu_put_be32s(f, &s->vbe_bank_mask);
2294#else
2295 qemu_put_byte(f, 0);
2296#endif
2297}
2298
2299static int vga_load(QEMUFile *f, void *opaque, int version_id)
2300{
2301 VGAState *s = (VGAState*)opaque;
2302 int is_vbe, i;
2303
2304 if (version_id != 1)
2305#ifndef VBOX
2306 return -EINVAL;
2307#else /* VBOX */
2308 {
2309 Log(("vga_load: version_id=%d - UNKNOWN\n", version_id));
2310 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
2311 }
2312#endif /* VBOX */
2313
2314 qemu_get_be32s(f, &s->latch);
2315 qemu_get_8s(f, &s->sr_index);
2316 qemu_get_buffer(f, s->sr, 8);
2317 qemu_get_8s(f, &s->gr_index);
2318 qemu_get_buffer(f, s->gr, 16);
2319 qemu_get_8s(f, &s->ar_index);
2320 qemu_get_buffer(f, s->ar, 21);
2321 qemu_get_be32s(f, (uint32_t *)&s->ar_flip_flop);
2322 qemu_get_8s(f, &s->cr_index);
2323 qemu_get_buffer(f, s->cr, 256);
2324 qemu_get_8s(f, &s->msr);
2325 qemu_get_8s(f, &s->fcr);
2326 qemu_get_8s(f, &s->st00);
2327 qemu_get_8s(f, &s->st01);
2328
2329 qemu_get_8s(f, &s->dac_state);
2330 qemu_get_8s(f, &s->dac_sub_index);
2331 qemu_get_8s(f, &s->dac_read_index);
2332 qemu_get_8s(f, &s->dac_write_index);
2333 qemu_get_buffer(f, s->dac_cache, 3);
2334 qemu_get_buffer(f, s->palette, 768);
2335
2336 qemu_get_be32s(f, (uint32_t *)&s->bank_offset);
2337 is_vbe = qemu_get_byte(f);
2338#ifdef CONFIG_BOCHS_VBE
2339 if (!is_vbe)
2340#ifndef VBOX
2341 return -EINVAL;
2342#else /* VBOX */
2343 {
2344 Log(("vga_load: !is_vbe !!\n"));
2345 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2346 }
2347#endif /* VBOX */
2348 qemu_get_be16s(f, &s->vbe_index);
2349 for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
2350 qemu_get_be16s(f, &s->vbe_regs[i]);
2351 qemu_get_be32s(f, &s->vbe_start_addr);
2352 qemu_get_be32s(f, &s->vbe_line_offset);
2353 qemu_get_be32s(f, &s->vbe_bank_mask);
2354#else
2355 if (is_vbe)
2356#ifndef VBOX
2357 return -EINVAL;
2358#else /* VBOX */
2359 {
2360 Log(("vga_load: is_vbe !!\n"));
2361 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2362 }
2363#endif /* VBOX */
2364#endif
2365
2366 /* force refresh */
2367 s->graphic_mode = -1;
2368 return 0;
2369}
2370
2371#ifndef VBOX /* see vgaR3IORegionMap */
2372static void vga_map(PCIDevice *pci_dev, int region_num,
2373 uint32_t addr, uint32_t size, int type)
2374{
2375 VGAState *s = vga_state;
2376
2377 cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
2378}
2379#endif
2380
2381#ifndef VBOX /* see vgaR3Construct */
2382void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base,
2383 unsigned long vga_ram_offset, int vga_ram_size)
2384#else
2385static void vga_init_expand(void)
2386#endif
2387{
2388 int i, j, v, b;
2389
2390 for(i = 0;i < 256; i++) {
2391 v = 0;
2392 for(j = 0; j < 8; j++) {
2393 v |= ((i >> j) & 1) << (j * 4);
2394 }
2395 expand4[i] = v;
2396
2397 v = 0;
2398 for(j = 0; j < 4; j++) {
2399 v |= ((i >> (2 * j)) & 3) << (j * 4);
2400 }
2401 expand2[i] = v;
2402 }
2403 for(i = 0; i < 16; i++) {
2404 v = 0;
2405 for(j = 0; j < 4; j++) {
2406 b = ((i >> j) & 1);
2407 v |= b << (2 * j);
2408 v |= b << (2 * j + 1);
2409 }
2410 expand4to8[i] = v;
2411 }
2412#ifdef VBOX
2413}
2414#else /* !VBOX */
2415 vga_reset(s);
2416
2417 s->vram_ptr = vga_ram_base;
2418 s->vram_offset = vga_ram_offset;
2419 s->vram_size = vga_ram_size;
2420 s->ds = ds;
2421 s->get_bpp = vga_get_bpp;
2422 s->get_offsets = vga_get_offsets;
2423 s->get_resolution = vga_get_resolution;
2424 /* XXX: currently needed for display */
2425 vga_state = s;
2426}
2427
2428
2429int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
2430 unsigned long vga_ram_offset, int vga_ram_size)
2431{
2432 VGAState *s;
2433
2434 s = qemu_mallocz(sizeof(VGAState));
2435 if (!s)
2436 return -1;
2437
2438 vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
2439
2440 register_savevm("vga", 0, 1, vga_save, vga_load, s);
2441
2442 register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
2443
2444 register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
2445 register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
2446 register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
2447 register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
2448
2449 register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
2450
2451 register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
2452 register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
2453 register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
2454 register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
2455 s->bank_offset = 0;
2456
2457#ifdef CONFIG_BOCHS_VBE
2458 s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
2459 s->vbe_bank_mask = ((s->vram_size >> 16) - 1);
2460#if defined (TARGET_I386)
2461 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2462 register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
2463
2464 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2465 register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
2466
2467 /* old Bochs IO ports */
2468 register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
2469 register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
2470
2471 register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
2472 register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s);
2473#else
2474 register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
2475 register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
2476
2477 register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
2478 register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
2479#endif
2480#endif /* CONFIG_BOCHS_VBE */
2481
2482 vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
2483 cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
2484 vga_io_memory);
2485
2486 if (bus) {
2487 PCIDevice *d;
2488 uint8_t *pci_conf;
2489
2490 d = pci_register_device(bus, "VGA",
2491 sizeof(PCIDevice),
2492 -1, NULL, NULL);
2493 pci_conf = d->config;
2494 pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
2495 pci_conf[0x01] = 0x12;
2496 pci_conf[0x02] = 0x11;
2497 pci_conf[0x03] = 0x11;
2498 pci_conf[0x0a] = 0x00; // VGA controller
2499 pci_conf[0x0b] = 0x03;
2500 pci_conf[0x0e] = 0x00; // header_type
2501
2502 /* XXX: vga_ram_size must be a power of two */
2503 pci_register_io_region(d, 0, vga_ram_size,
2504 PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
2505 } else {
2506#ifdef CONFIG_BOCHS_VBE
2507 /* XXX: use optimized standard vga accesses */
2508 cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS,
2509 vga_ram_size, vga_ram_offset);
2510#endif
2511 }
2512 return 0;
2513}
2514#endif /* !VBOX */
2515
2516
2517#ifndef VBOX
2518/********************************************************/
2519/* vga screen dump */
2520
2521static int vga_save_w, vga_save_h;
2522
2523static void vga_save_dpy_update(DisplayState *s,
2524 int x, int y, int w, int h)
2525{
2526}
2527
2528static void vga_save_dpy_resize(DisplayState *s, int w, int h)
2529{
2530 s->linesize = w * 4;
2531#ifndef VBOX
2532 s->data = qemu_malloc(h * s->linesize);
2533#else /* VBOX */
2534 if (!s->data)
2535 {
2536 PPDMDEVINS pDevIns = VGASTATE2DEVINS((PVGASTATE)s->pvVgaState);
2537 s->data = PDMDevHlpMMHeapAlloc(pDevIns, h * s->linesize);
2538 }
2539 else // (32-bpp buffer is allocated by the caller)
2540 s->linesize = ((w * 32 + 31) / 32) * 4;
2541#endif /* VBOX */
2542 vga_save_w = w;
2543 vga_save_h = h;
2544}
2545
2546static void vga_save_dpy_refresh(DisplayState *s)
2547{
2548}
2549
2550static int ppm_save(const char *filename, uint8_t *data,
2551 int w, int h, int linesize)
2552{
2553 FILE *f;
2554 uint8_t *d, *d1;
2555 unsigned int v;
2556 int y, x;
2557
2558 f = fopen(filename, "wb");
2559 if (!f)
2560 return -1;
2561 fprintf(f, "P6\n%d %d\n%d\n",
2562 w, h, 255);
2563 d1 = data;
2564 for(y = 0; y < h; y++) {
2565 d = d1;
2566 for(x = 0; x < w; x++) {
2567 v = *(uint32_t *)d;
2568 fputc((v >> 16) & 0xff, f);
2569 fputc((v >> 8) & 0xff, f);
2570 fputc((v) & 0xff, f);
2571 d += 4;
2572 }
2573 d1 += linesize;
2574 }
2575 fclose(f);
2576 return 0;
2577}
2578
2579/* save the vga display in a PPM image even if no display is
2580 available */
2581void vga_screen_dump(const char *filename)
2582{
2583 VGAState *s = vga_state;
2584 DisplayState *saved_ds, ds1, *ds = &ds1;
2585
2586 /* XXX: this is a little hackish */
2587 vga_invalidate_display();
2588 saved_ds = s->ds;
2589
2590 memset(ds, 0, sizeof(DisplayState));
2591 ds->dpy_update = vga_save_dpy_update;
2592 ds->dpy_resize = vga_save_dpy_resize;
2593 ds->dpy_refresh = vga_save_dpy_refresh;
2594 ds->depth = 32;
2595
2596 s->ds = ds;
2597 s->graphic_mode = -1;
2598 vga_update_display();
2599
2600 if (ds->data) {
2601 ppm_save(filename, ds->data, vga_save_w, vga_save_h,
2602 s->ds->linesize);
2603 qemu_free(ds->data);
2604 }
2605 s->ds = saved_ds;
2606}
2607#endif /* !VBOX */
2608
2609
2610#if 0 //def VBOX
2611/* copy the vga display contents to the given buffer. the size of the buffer
2612 must be sufficient to store the screen copy (see below). the width and height
2613 parameters determine the required dimensions of the copy. If they differ
2614 from the actual screen dimensions, then the returned copy is shrinked or
2615 stretched accordingly. The copy is always a 32-bit image, so the size of
2616 the buffer supplied must be at least (((width * 32 + 31) / 32) * 4) * height,
2617 i.e. dword-aligned. returns zero if the operation was successfull and -1
2618 otherwise. */
2619
2620static int vga_copy_screen_to(PVGASTATE s, uint8_t *buf, int width, int height)
2621{
2622 DisplayState *saved_ds, ds1, *ds = &ds1;
2623 if (!buf || width <= 0 || height <= 0)
2624 return -1;
2625
2626 /* XXX: this is a little hackish */
2627 vga_invalidate_display(s);
2628 saved_ds = s->ds;
2629
2630 memset(ds, 0, sizeof(DisplayState));
2631 ds->dpy_update = vga_save_dpy_update;
2632 ds->dpy_resize = vga_save_dpy_resize;
2633 ds->dpy_refresh = vga_save_dpy_refresh;
2634 ds->depth = 32;
2635 ds->data = buf;
2636 ds->pvVgaState = s;
2637
2638 s->ds = ds;
2639 s->graphic_mode = -1;
2640 vga_update_display(s);
2641
2642//@@TODO (dmik): implement stretching/shrinking!
2643
2644 s->ds = saved_ds;
2645 return 0;
2646}
2647
2648/* copy the given buffer to the vga display. width and height define the
2649 dimensions of the image in the buffer. x and y define the point on the
2650 vga display to copy the image to. the buffer is assumed to contain a 32-bit
2651 image, so the size of one scanline must be ((width * 32 + 31) / 32) * 4),
2652 i.e. dword-aligned. returns zero if the operation was successfull and -1
2653 otherwise. */
2654static int vga_copy_screen_from(PVGASTATE s, uint8_t *buf, int x, int y, int width, int height)
2655{
2656 int bpl = ((width * 32 + 31) / 32) * 4;
2657 int linesize = s->ds->linesize;
2658 uint8_t *dst;
2659 uint8_t *src;
2660 int bpp;
2661 vga_draw_line_func *vga_draw_line;
2662
2663 if (!buf || x < 0 || y < 0 || width <= 0 || height <= 0
2664 || x + width > s->ds->width || y + height > s->ds->height)
2665 return -1;
2666
2667 vga_draw_line = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(s->ds->depth)];
2668 switch (s->ds->depth) {
2669 case 8: bpp = 1; break;
2670 case 15:
2671 case 16: bpp = 2; break;
2672 case 32: bpp = 4; break;
2673 default: return -1;
2674 }
2675
2676 dst = s->ds->data + y * linesize + x * bpp;
2677 src = buf;
2678 for (y = 0; y < height; y ++)
2679 {
2680 vga_draw_line(s, dst, src, width);
2681 dst += linesize;
2682 src += bpl;
2683 }
2684
2685 return 0;
2686}
2687#endif
2688
2689#endif /* !VBOX || !IN_GC || !IN_RING0 */
2690
2691
2692
2693#ifdef VBOX /* innotek code start */
2694
2695
2696/* -=-=-=-=-=- all contexts -=-=-=-=-=- */
2697
2698/**
2699 * Port I/O Handler for VGA OUT operations.
2700 *
2701 * @returns VBox status code.
2702 *
2703 * @param pDevIns The device instance.
2704 * @param pvUser User argument - ignored.
2705 * @param Port Port number used for the IN operation.
2706 * @param u32 The value to output.
2707 * @param cb The value size in bytes.
2708 */
2709PDMBOTHCBDECL(int) vgaIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2710{
2711 NOREF(pvUser);
2712 if (cb == 1)
2713 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2714 else if (cb == 2)
2715 {
2716 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32 & 0xff);
2717 vga_ioport_write(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1, u32 >> 8);
2718 }
2719 return VINF_SUCCESS;
2720}
2721
2722
2723/**
2724 * Port I/O Handler for VGA IN operations.
2725 *
2726 * @returns VBox status code.
2727 *
2728 * @param pDevIns The device instance.
2729 * @param pvUser User argument - ignored.
2730 * @param Port Port number used for the IN operation.
2731 * @param pu32 Where to store the result.
2732 * @param cb Number of bytes read.
2733 */
2734PDMBOTHCBDECL(int) vgaIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2735{
2736 NOREF(pvUser);
2737 if (cb == 1)
2738 {
2739 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2740 return VINF_SUCCESS;
2741 }
2742 else if (cb == 2)
2743 {
2744 *pu32 = vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port)
2745 | (vga_ioport_read(PDMINS2DATA(pDevIns, PVGASTATE), Port + 1) << 8);
2746 return VINF_SUCCESS;
2747 }
2748 return VERR_IOM_IOPORT_UNUSED;
2749}
2750
2751
2752/**
2753 * Port I/O Handler for VBE OUT operations.
2754 *
2755 * @returns VBox status code.
2756 *
2757 * @param pDevIns The device instance.
2758 * @param pvUser User argument - ignored.
2759 * @param Port Port number used for the IN operation.
2760 * @param u32 The value to output.
2761 * @param cb The value size in bytes.
2762 */
2763PDMBOTHCBDECL(int) vgaIOPortWriteVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2764{
2765 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2766
2767 NOREF(pvUser);
2768
2769#ifndef IN_RING3
2770 /*
2771 * This has to be done on the host in order to execute the connector callbacks.
2772 */
2773 if (s->vbe_index == VBE_DISPI_INDEX_ENABLE
2774 || s->vbe_index == VBE_DISPI_INDEX_VBOX_VIDEO)
2775 {
2776 Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE - Switching to host...\n"));
2777 return VINF_IOM_HC_IOPORT_WRITE;
2778 }
2779#endif
2780#ifdef VBE_BYTEWISE_IO
2781 if (cb == 1)
2782 {
2783 if (!s->fWriteVBEData)
2784 {
2785 if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2786 && (u32 & VBE_DISPI_ENABLED))
2787 {
2788 s->fWriteVBEData = false;
2789 return vbe_ioport_write_data(s, Port, u32 & 0xFF);
2790 }
2791 else
2792 {
2793 s->cbWriteVBEData = u32 & 0xFF;
2794 s->fWriteVBEData = true;
2795 return VINF_SUCCESS;
2796 }
2797 }
2798 else
2799 {
2800 u32 = (s->cbWriteVBEData << 8) | (u32 & 0xFF);
2801 s->fWriteVBEData = false;
2802 cb = 2;
2803 }
2804 }
2805#endif
2806 if (cb == 2 || cb == 4)
2807 {
2808//#ifdef IN_GC
2809// /*
2810// * The VBE_DISPI_INDEX_ENABLE memsets the entire frame buffer.
2811// * Since we're not mapping the entire framebuffer any longer that
2812// * has to be done on the host.
2813// */
2814// if ( (s->vbe_index == VBE_DISPI_INDEX_ENABLE)
2815// && (u32 & VBE_DISPI_ENABLED))
2816// {
2817// Log(("vgaIOPortWriteVBEData: VBE_DISPI_INDEX_ENABLE & VBE_DISPI_ENABLED - Switching to host...\n"));
2818// return VINF_IOM_HC_IOPORT_WRITE;
2819// }
2820//#endif
2821 return vbe_ioport_write_data(s, Port, u32);
2822 }
2823 else
2824 AssertMsgFailed(("vgaIOPortWriteVBEData: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2825 return VINF_SUCCESS;
2826}
2827
2828
2829/**
2830 * Port I/O Handler for VBE OUT operations.
2831 *
2832 * @returns VBox status code.
2833 *
2834 * @param pDevIns The device instance.
2835 * @param pvUser User argument - ignored.
2836 * @param Port Port number used for the IN operation.
2837 * @param u32 The value to output.
2838 * @param cb The value size in bytes.
2839 */
2840PDMBOTHCBDECL(int) vgaIOPortWriteVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
2841{
2842 NOREF(pvUser);
2843#ifdef VBE_BYTEWISE_IO
2844 if (cb == 1)
2845 {
2846 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2847 if (!s->fWriteVBEIndex)
2848 {
2849 s->cbWriteVBEIndex = u32 & 0x00FF;
2850 s->fWriteVBEIndex = true;
2851 return VINF_SUCCESS;
2852 }
2853 else
2854 {
2855 s->fWriteVBEIndex = false;
2856 vbe_ioport_write_index(s, Port, (s->cbWriteVBEIndex << 8) | (u32 & 0x00FF));
2857 return VINF_SUCCESS;
2858 }
2859 }
2860 else
2861#endif
2862 if (cb == 2)
2863 vbe_ioport_write_index(PDMINS2DATA(pDevIns, PVGASTATE), Port, u32);
2864 else
2865 AssertMsgFailed(("vgaIOPortWriteVBEIndex: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2866 return VINF_SUCCESS;
2867}
2868
2869
2870/**
2871 * Port I/O Handler for VBE IN operations.
2872 *
2873 * @returns VBox status code.
2874 *
2875 * @param pDevIns The device instance.
2876 * @param pvUser User argument - ignored.
2877 * @param Port Port number used for the IN operation.
2878 * @param pu32 Where to store the result.
2879 * @param cb Number of bytes to read.
2880 */
2881PDMBOTHCBDECL(int) vgaIOPortReadVBEData(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2882{
2883 NOREF(pvUser);
2884#ifdef VBE_BYTEWISE_IO
2885 if (cb == 1)
2886 {
2887 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2888
2889 if (!s->fReadVBEData)
2890 {
2891 *pu32 = (vbe_ioport_read_data(s, Port) >> 8) & 0xFF;
2892 s->fReadVBEData = true;
2893 return VINF_SUCCESS;
2894 }
2895 else
2896 {
2897 *pu32 = vbe_ioport_read_data(s, Port) & 0xFF;
2898 s->fReadVBEData = false;
2899 return VINF_SUCCESS;
2900 }
2901 }
2902 else
2903#endif
2904 if (cb == 2)
2905 {
2906 *pu32 = vbe_ioport_read_data(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2907 return VINF_SUCCESS;
2908 }
2909 else if (cb == 4)
2910 {
2911 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2912 /* Quick hack for getting the vram size. */
2913 *pu32 = s->vram_size;
2914 return VINF_SUCCESS;
2915 }
2916 AssertMsgFailed(("vgaIOPortReadVBEData: Port=%#x cb=%d\n", Port, cb));
2917 return VERR_IOM_IOPORT_UNUSED;
2918}
2919
2920
2921/**
2922 * Port I/O Handler for VBE IN operations.
2923 *
2924 * @returns VBox status code.
2925 *
2926 * @param pDevIns The device instance.
2927 * @param pvUser User argument - ignored.
2928 * @param Port Port number used for the IN operation.
2929 * @param pu32 Where to store the result.
2930 * @param cb Number of bytes to read.
2931 */
2932PDMBOTHCBDECL(int) vgaIOPortReadVBEIndex(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
2933{
2934 NOREF(pvUser);
2935#ifdef VBE_BYTEWISE_IO
2936 if (cb == 1)
2937 {
2938 VGAState *s = PDMINS2DATA(pDevIns, PVGASTATE);
2939
2940 if (!s->fReadVBEIndex)
2941 {
2942 *pu32 = (vbe_ioport_read_index(s, Port) >> 8) & 0xFF;
2943 s->fReadVBEIndex = true;
2944 return VINF_SUCCESS;
2945 }
2946 else
2947 {
2948 *pu32 = vbe_ioport_read_index(s, Port) & 0xFF;
2949 s->fReadVBEIndex = false;
2950 return VINF_SUCCESS;
2951 }
2952 }
2953 else
2954#endif
2955 if (cb == 2)
2956 {
2957 *pu32 = vbe_ioport_read_index(PDMINS2DATA(pDevIns, PVGASTATE), Port);
2958 return VINF_SUCCESS;
2959 }
2960 AssertMsgFailed(("vgaIOPortReadVBEIndex: Port=%#x cb=%d\n", Port, cb));
2961 return VERR_IOM_IOPORT_UNUSED;
2962}
2963
2964
2965
2966
2967
2968/* -=-=-=-=-=- Guest Context -=-=-=-=-=- */
2969
2970/*
2971 * Internal. For use inside VGAGCMemoryFillWrite only.
2972 * Macro for apply logical operation and bit mask.
2973 */
2974#define APPLY_LOGICAL_AND_MASK(s, val, bit_mask) \
2975 /* apply logical operation */ \
2976 switch(s->gr[3] >> 3) \
2977 { \
2978 case 0: \
2979 default: \
2980 /* nothing to do */ \
2981 break; \
2982 case 1: \
2983 /* and */ \
2984 val &= s->latch; \
2985 break; \
2986 case 2: \
2987 /* or */ \
2988 val |= s->latch; \
2989 break; \
2990 case 3: \
2991 /* xor */ \
2992 val ^= s->latch; \
2993 break; \
2994 } \
2995 /* apply bit mask */ \
2996 val = (val & bit_mask) | (s->latch & ~bit_mask)
2997
2998/**
2999 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM and from the inside of VGADeviceGC.cpp.
3000 * This is the advanced version of vga_mem_writeb function.
3001 *
3002 * @returns VBox status code.
3003 * @param pDevIns Pointer device instance.
3004 * @param pvUser User argument - ignored.
3005 * @param GCPhysAddr Physical address of memory to write.
3006 * @param u32Item Data to write, up to 4 bytes.
3007 * @param cbItem Size of data Item, only 1/2/4 bytes is allowed for now.
3008 * @param cItems Number of data items to write.
3009 */
3010PDMBOTHCBDECL(int) vgaMMIOFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, uint32_t u32Item, unsigned cbItem, unsigned cItems)
3011{
3012 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3013 uint32_t b;
3014 uint32_t write_mask, bit_mask, set_mask;
3015 uint32_t aVal[4]; /** @todo r=bird: Why is this an 32-bit array? */
3016 unsigned i;
3017 NOREF(pvUser);
3018 for (i = 0; i < cbItem; i++)
3019 {
3020 aVal[i] = u32Item & 0xff;
3021 u32Item >>= 8;
3022 }
3023
3024 /* convert to VGA memory offset */
3025 /// @todo add check for the end of region
3026 GCPhysAddr &= 0x1ffff;
3027 switch((pData->gr[6] >> 2) & 3) {
3028 case 0:
3029 break;
3030 case 1:
3031 if (GCPhysAddr >= 0x10000)
3032 return VINF_SUCCESS;
3033 GCPhysAddr += pData->bank_offset;
3034 break;
3035 case 2:
3036 GCPhysAddr -= 0x10000;
3037 if (GCPhysAddr >= 0x8000)
3038 return VINF_SUCCESS;
3039 break;
3040 default:
3041 case 3:
3042 GCPhysAddr -= 0x18000;
3043 if (GCPhysAddr >= 0x8000)
3044 return VINF_SUCCESS;
3045 break;
3046 }
3047
3048 if (pData->sr[4] & 0x08) {
3049 /* chain 4 mode : simplest access */
3050#ifdef IN_GC
3051 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
3052 return VINF_IOM_HC_MMIO_WRITE;
3053#else
3054 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
3055 {
3056 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3057 return VINF_SUCCESS;
3058 }
3059#endif
3060
3061 while (cItems-- > 0)
3062 for (i = 0; i < cbItem; i++)
3063 {
3064 if (pData->sr[2] & (1 << (GCPhysAddr & 3)))
3065 {
3066 CTXSUFF(pData->vram_ptr)[GCPhysAddr] = aVal[i];
3067 vga_set_dirty(pData, GCPhysAddr);
3068 }
3069 GCPhysAddr++;
3070 }
3071 } else if (pData->gr[5] & 0x10) {
3072 /* odd/even mode (aka text mode mapping) */
3073#ifdef IN_GC
3074 if (GCPhysAddr * 2 + cItems * cbItem >= VGA_MAPPING_SIZE)
3075 return VINF_IOM_HC_MMIO_WRITE;
3076#else
3077 if (GCPhysAddr * 2 + cItems * cbItem >= pData->vram_size)
3078 {
3079 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3080 return VINF_SUCCESS;
3081 }
3082#endif
3083 while (cItems-- > 0)
3084 for (i = 0; i < cbItem; i++)
3085 {
3086 unsigned plane = (pData->gr[4] & 2) | (GCPhysAddr & 1);
3087 if (pData->sr[2] & (1 << plane)) {
3088 RTGCPHYS PhysAddr2 = ((GCPhysAddr & ~1) << 2) | plane;
3089 CTXSUFF(pData->vram_ptr)[PhysAddr2] = aVal[i];
3090 vga_set_dirty(pData, PhysAddr2);
3091 }
3092 GCPhysAddr++;
3093 }
3094 } else {
3095#ifdef IN_GC
3096 if (GCPhysAddr + cItems * cbItem >= VGA_MAPPING_SIZE)
3097 return VINF_IOM_HC_MMIO_WRITE;
3098#else
3099 if (GCPhysAddr + cItems * cbItem >= pData->vram_size)
3100 {
3101 AssertMsgFailed(("GCPhysAddr=%VGp cItems=%#x cbItem=%d\n", GCPhysAddr, cItems, cbItem));
3102 return VINF_SUCCESS;
3103 }
3104#endif
3105
3106 /* standard VGA latched access */
3107 switch(pData->gr[5] & 3) {
3108 default:
3109 case 0:
3110 /* rotate */
3111 b = pData->gr[3] & 7;
3112 bit_mask = pData->gr[8];
3113 bit_mask |= bit_mask << 8;
3114 bit_mask |= bit_mask << 16;
3115 set_mask = mask16[pData->gr[1]];
3116
3117 for (i = 0; i < cbItem; i++)
3118 {
3119 aVal[i] = ((aVal[i] >> b) | (aVal[i] << (8 - b))) & 0xff;
3120 aVal[i] |= aVal[i] << 8;
3121 aVal[i] |= aVal[i] << 16;
3122
3123 /* apply set/reset mask */
3124 aVal[i] = (aVal[i] & ~set_mask) | (mask16[pData->gr[0]] & set_mask);
3125
3126 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3127 }
3128 break;
3129 case 1:
3130 for (i = 0; i < cbItem; i++)
3131 aVal[i] = pData->latch;
3132 break;
3133 case 2:
3134 bit_mask = pData->gr[8];
3135 bit_mask |= bit_mask << 8;
3136 bit_mask |= bit_mask << 16;
3137 for (i = 0; i < cbItem; i++)
3138 {
3139 aVal[i] = mask16[aVal[i] & 0x0f];
3140
3141 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3142 }
3143 break;
3144 case 3:
3145 /* rotate */
3146 b = pData->gr[3] & 7;
3147
3148 for (i = 0; i < cbItem; i++)
3149 {
3150 aVal[i] = (aVal[i] >> b) | (aVal[i] << (8 - b));
3151 bit_mask = pData->gr[8] & aVal[i];
3152 bit_mask |= bit_mask << 8;
3153 bit_mask |= bit_mask << 16;
3154 aVal[i] = mask16[pData->gr[0]];
3155
3156 APPLY_LOGICAL_AND_MASK(pData, aVal[i], bit_mask);
3157 }
3158 break;
3159 }
3160
3161 /* mask data according to sr[2] */
3162 write_mask = mask16[pData->sr[2]];
3163
3164 /* actually write data */
3165 if (cbItem == 1)
3166 {
3167 /* The most frequently case is 1 byte I/O. */
3168 while (cItems-- > 0)
3169 {
3170 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3171 vga_set_dirty(pData, GCPhysAddr << 2);
3172 GCPhysAddr++;
3173 }
3174 }
3175 else if (cbItem == 2)
3176 {
3177 /* The second case is 2 bytes I/O. */
3178 while (cItems-- > 0)
3179 {
3180 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[0] & write_mask);
3181 vga_set_dirty(pData, GCPhysAddr << 2);
3182 GCPhysAddr++;
3183
3184 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[1] & write_mask);
3185 vga_set_dirty(pData, GCPhysAddr << 2);
3186 GCPhysAddr++;
3187 }
3188 }
3189 else
3190 {
3191 /* And the rest is 4 bytes. */
3192 Assert(cbItem == 4);
3193 while (cItems-- > 0)
3194 for (i = 0; i < cbItem; i++)
3195 {
3196 ((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] = (((uint32_t *)pData->CTXSUFF(vram_ptr))[GCPhysAddr] & ~write_mask) | (aVal[i] & write_mask);
3197 vga_set_dirty(pData, GCPhysAddr << 2);
3198 GCPhysAddr++;
3199 }
3200 }
3201 }
3202 return VINF_SUCCESS;
3203}
3204#undef APPLY_LOGICAL_AND_MASK
3205
3206
3207/**
3208 * Legacy VGA memory (0xa0000 - 0xbffff) read hook, to be called from IOM.
3209 *
3210 * @returns VBox status code.
3211 * @param pDevIns Pointer device instance.
3212 * @param pvUser User argument - ignored.
3213 * @param GCPhysAddr Physical address of memory to read.
3214 * @param pv Where to store readed data.
3215 * @param cb Bytes to read.
3216 */
3217PDMBOTHCBDECL(int) vgaMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3218{
3219 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3220 STAM_PROFILE_START(&pData->StatGCMemoryRead, a);
3221 NOREF(pvUser);
3222 switch (cb)
3223 {
3224 case 1:
3225 *(uint8_t *)pv = vga_mem_readb(pData, GCPhysAddr); break;
3226 case 2:
3227 *(uint16_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3228 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8);
3229 break;
3230 case 4:
3231 *(uint32_t *)pv = vga_mem_readb(pData, GCPhysAddr)
3232 | (vga_mem_readb(pData, GCPhysAddr + 1) << 8)
3233 | (vga_mem_readb(pData, GCPhysAddr + 2) << 16)
3234 | (vga_mem_readb(pData, GCPhysAddr + 3) << 24);
3235 break;
3236
3237 default:
3238 {
3239 uint8_t *pu8Data = (uint8_t *)pv;
3240 while (cb-- > 0)
3241 *pu8Data++ = vga_mem_readb(pData, GCPhysAddr++);
3242 }
3243 }
3244 STAM_PROFILE_STOP(&pData->StatGCMemoryRead, a);
3245 return VINF_SUCCESS;
3246}
3247
3248/**
3249 * Legacy VGA memory (0xa0000 - 0xbffff) write hook, to be called from IOM.
3250 *
3251 * @returns VBox status code.
3252 * @param pDevIns Pointer device instance.
3253 * @param pvUser User argument - ignored.
3254 * @param GCPhysAddr Physical address of memory to write.
3255 * @param pv Pointer to data.
3256 * @param cb Bytes to write.
3257 */
3258PDMBOTHCBDECL(int) vgaMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
3259{
3260 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3261 uint8_t *pu8 = (uint8_t *)pv;
3262 int rc = VINF_SUCCESS;
3263 STAM_PROFILE_START(&pData->StatGCMemoryWrite, a);
3264
3265 switch (cb)
3266 {
3267 case 1:
3268 rc = vga_mem_writeb(pData, GCPhysAddr, *pu8);
3269 break;
3270#if 1
3271 case 2:
3272 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3273 if (RT_LIKELY(rc == VINF_SUCCESS))
3274 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3275 break;
3276 case 4:
3277 rc = vga_mem_writeb(pData, GCPhysAddr + 0, pu8[0]);
3278 if (RT_LIKELY(rc == VINF_SUCCESS))
3279 rc = vga_mem_writeb(pData, GCPhysAddr + 1, pu8[1]);
3280 if (RT_LIKELY(rc == VINF_SUCCESS))
3281 rc = vga_mem_writeb(pData, GCPhysAddr + 2, pu8[2]);
3282 if (RT_LIKELY(rc == VINF_SUCCESS))
3283 rc = vga_mem_writeb(pData, GCPhysAddr + 3, pu8[3]);
3284 break;
3285#else
3286 case 2:
3287 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint16_t *)pv, 2, 1);
3288 break;
3289 case 4:
3290 rc = vgaMMIOFill(pDevIns, GCPhysAddr, *(uint32_t *)pv, 4, 1);
3291 break;
3292#endif
3293 default:
3294 while (cb-- > 0 && rc == VINF_SUCCESS)
3295 rc = vga_mem_writeb(pData, GCPhysAddr++, *pu8++);
3296 break;
3297
3298 }
3299 STAM_PROFILE_STOP(&pData->StatGCMemoryWrite, a);
3300 return rc;
3301}
3302
3303
3304/**
3305 * Handle LFB access.
3306 * @returns VBox status code.
3307 * @param pVM VM handle.
3308 * @param pData VGA device instance data.
3309 * @param GCPhys The access physical address.
3310 * @param GCPtr The access virtual address (only GC).
3311 */
3312static int vgaLFBAccess(PVM pVM, PVGASTATE pData, RTGCPHYS GCPhys, RTGCPTR GCPtr)
3313{
3314 int rc;
3315
3316 /*
3317 * Set page dirty bit.
3318 */
3319 vga_set_dirty(pData, GCPhys - pData->GCPhysVRAM);
3320 pData->fLFBUpdated = true;
3321
3322 /*
3323 * Turn of the write handler for this particular page and make it R/W.
3324 * Then return telling the caller to restart the guest instruction.
3325 * ASSUME: the guest always maps video memory RW.
3326 */
3327 rc = PGMHandlerPhysicalPageTempOff(pVM, pData->GCPhysVRAM, GCPhys);
3328 if (VBOX_SUCCESS(rc))
3329 {
3330#ifndef IN_RING3
3331 rc = PGMShwModifyPage(pVM, GCPtr, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
3332 if (VBOX_SUCCESS(rc))
3333 return VINF_SUCCESS;
3334 else
3335 AssertMsgFailed(("PGMShwModifyPage -> rc=%d\n", rc));
3336#else /* IN_RING3 : We don't have any virtual page address of the access here. */
3337 Assert(GCPtr == 0);
3338 return VINF_SUCCESS;
3339#endif
3340 }
3341 else
3342 AssertMsgFailed(("PGMHandlerPhysicalPageTempOff -> rc=%d\n", rc));
3343
3344 return rc;
3345}
3346
3347
3348#ifdef IN_GC
3349/**
3350 * #PF Handler for VBE LFB access.
3351 *
3352 * @returns VBox status code (appropriate for GC return).
3353 * @param pVM VM Handle.
3354 * @param uErrorCode CPU Error code.
3355 * @param pRegFrame Trap register frame.
3356 * @param pvFault The fault address (cr2).
3357 * @param GCPhysFault The GC physical address corresponding to pvFault.
3358 * @param pvUser User argument, ignored.
3359 */
3360PDMBOTHCBDECL(int) vgaGCLFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3361{
3362 PVGASTATE pData = (PVGASTATE)pvUser;
3363 Assert(pData);
3364 Assert(GCPhysFault >= pData->GCPhysVRAM);
3365 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3366
3367 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3368}
3369
3370#elif IN_RING0
3371
3372/**
3373 * #PF Handler for VBE LFB access.
3374 *
3375 * @returns VBox status code (appropriate for GC return).
3376 * @param pVM VM Handle.
3377 * @param uErrorCode CPU Error code.
3378 * @param pRegFrame Trap register frame.
3379 * @param pvFault The fault address (cr2).
3380 * @param GCPhysFault The GC physical address corresponding to pvFault.
3381 * @param pvUser User argument, ignored.
3382 */
3383PDMBOTHCBDECL(int) vgaR0LFBAccessHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPHYS GCPhysFault, void *pvUser)
3384{
3385 PVGASTATE pData = (PVGASTATE)pvUser;
3386 Assert(pData);
3387 Assert(GCPhysFault >= pData->GCPhysVRAM);
3388 AssertMsg(uErrorCode & X86_TRAP_PF_RW, ("uErrorCode=%#x\n", uErrorCode));
3389
3390 return vgaLFBAccess(pVM, pData, GCPhysFault, pvFault);
3391}
3392
3393#else /* IN_RING3 */
3394
3395/**
3396 * HC access handler for the LFB.
3397 *
3398 * @returns VINF_SUCCESS if the handler have carried out the operation.
3399 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
3400 * @param pVM VM Handle.
3401 * @param GCPhys The physical address the guest is writing to.
3402 * @param pvPhys The HC mapping of that address.
3403 * @param pvBuf What the guest is reading/writing.
3404 * @param cbBuf How much it's reading/writing.
3405 * @param enmAccessType The access type.
3406 * @param pvUser User argument.
3407 */
3408static DECLCALLBACK(int) vgaR3LFBAccessHandler(PVM pVM, RTGCPHYS GCPhys, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
3409{
3410 PVGASTATE pData = (PVGASTATE)pvUser;
3411 int rc;
3412 Assert(pData);
3413 Assert(GCPhys >= pData->GCPhysVRAM);
3414 rc = vgaLFBAccess(pVM, pData, GCPhys, 0);
3415 if (VBOX_SUCCESS(rc))
3416 return VINF_PGM_HANDLER_DO_DEFAULT;
3417 AssertMsg(rc <= VINF_SUCCESS, ("rc=%Vrc\n", rc));
3418 return rc;
3419}
3420#endif /* IN_RING3 */
3421
3422
3423/* -=-=-=-=-=- Ring 3 -=-=-=-=-=- */
3424
3425#ifdef IN_RING3
3426
3427# ifdef VBE_NEW_DYN_LIST
3428/**
3429 * Port I/O Handler for VBE Extra OUT operations.
3430 *
3431 * @returns VBox status code.
3432 *
3433 * @param pDevIns The device instance.
3434 * @param pvUser User argument - ignored.
3435 * @param Port Port number used for the IN operation.
3436 * @param u32 The value to output.
3437 * @param cb The value size in bytes.
3438 */
3439PDMBOTHCBDECL(int) vbeIOPortWriteVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3440{
3441 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3442 NOREF(pvUser);
3443 NOREF(Port);
3444
3445 if (cb == 2)
3446 {
3447 Log(("vbeIOPortWriteVBEExtra: addr=%#RX32\n", u32));
3448 pData->u16VBEExtraAddress = u32;
3449 return VINF_SUCCESS;
3450 }
3451
3452 Log(("vbeIOPortWriteVBEExtra: Ignoring invalid cb=%d writes to the VBE Extra port!!!\n", cb));
3453 return VINF_SUCCESS;
3454}
3455
3456
3457/**
3458 * Port I/O Handler for VBE Extra IN operations.
3459 *
3460 * @returns VBox status code.
3461 *
3462 * @param pDevIns The device instance.
3463 * @param pvUser User argument - ignored.
3464 * @param Port Port number used for the IN operation.
3465 * @param pu32 Where to store the result.
3466 * @param cb Number of bytes read.
3467 */
3468PDMBOTHCBDECL(int) vbeIOPortReadVBEExtra(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3469{
3470 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
3471 NOREF(pvUser);
3472 NOREF(Port);
3473
3474 if (pData->u16VBEExtraAddress == 0xffff)
3475 {
3476 Log(("vbeIOPortReadVBEExtra: Requested number of 64k video banks\n"));
3477 *pu32 = pData->vram_size / _64K;
3478 return VINF_SUCCESS;
3479 }
3480
3481 if ( pData->u16VBEExtraAddress >= pData->cbVBEExtraData
3482 || pData->u16VBEExtraAddress + cb > pData->cbVBEExtraData)
3483 {
3484 *pu32 = 0;
3485 Log(("vbeIOPortReadVBEExtra: Requested address is out of VBE data!!! Address=%#x(%d) cbVBEExtraData=%#x(%d)\n",
3486 pData->u16VBEExtraAddress, pData->u16VBEExtraAddress, pData->cbVBEExtraData, pData->cbVBEExtraData));
3487 return VINF_SUCCESS;
3488 }
3489
3490 if (cb == 1)
3491 {
3492 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress] & 0xFF;
3493
3494 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3495 return VINF_SUCCESS;
3496 }
3497
3498 if (cb == 2)
3499 {
3500 *pu32 = pData->pu8VBEExtraData[pData->u16VBEExtraAddress]
3501 | pData->pu8VBEExtraData[pData->u16VBEExtraAddress + 1] << 8;
3502
3503 Log(("vbeIOPortReadVBEExtra: cb=%#x %.*Vhxs\n", cb, cb, pu32));
3504 return VINF_SUCCESS;
3505 }
3506 Log(("vbeIOPortReadVBEExtra: Invalid cb=%d read from the VBE Extra port!!!\n", cb));
3507 return VERR_IOM_IOPORT_UNUSED;
3508}
3509# endif /* VBE_NEW_DYN_LIST */
3510
3511
3512
3513
3514/* -=-=-=-=-=- Ring 3: VGA BIOS I/Os -=-=-=-=-=- */
3515
3516/**
3517 * Port I/O Handler for VGA BIOS IN operations.
3518 *
3519 * @returns VBox status code.
3520 *
3521 * @param pDevIns The device instance.
3522 * @param pvUser User argument - ignored.
3523 * @param Port Port number used for the IN operation.
3524 * @param pu32 Where to store the result.
3525 * @param cb Number of bytes read.
3526 */
3527static DECLCALLBACK(int) vgaIOPortReadBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
3528{
3529 NOREF(pDevIns);
3530 NOREF(pvUser);
3531 NOREF(Port);
3532 NOREF(pu32);
3533 NOREF(cb);
3534 return VERR_IOM_IOPORT_UNUSED;
3535}
3536
3537/**
3538 * Port I/O Handler for VGA BIOS OUT operations.
3539 *
3540 * @returns VBox status code.
3541 *
3542 * @param pDevIns The device instance.
3543 * @param pvUser User argument - ignored.
3544 * @param Port Port number used for the IN operation.
3545 * @param u32 The value to output.
3546 * @param cb The value size in bytes.
3547 */
3548static DECLCALLBACK(int) vgaIOPortWriteBIOS(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
3549{
3550 static int lastWasNotNewline = 0; /* We are only called in a single-threaded way */
3551 /*
3552 * VGA BIOS char printing.
3553 */
3554 if ( cb == 1
3555 && Port == VBE_PRINTF_PORT)
3556 {
3557#if 0
3558 switch (u32)
3559 {
3560 case '\r': Log(("vgabios: <return>\n")); break;
3561 case '\n': Log(("vgabios: <newline>\n")); break;
3562 case '\t': Log(("vgabios: <tab>\n")); break;
3563 default:
3564 Log(("vgabios: %c\n", u32));
3565 }
3566#else
3567 if (lastWasNotNewline == 0)
3568 Log(("vgabios: "));
3569 if (u32 != '\r') /* return - is only sent in conjunction with '\n' */
3570 Log(("%c", u32));
3571 if (u32 == '\n')
3572 lastWasNotNewline = 0;
3573 else
3574 lastWasNotNewline = 1;
3575#endif
3576 return VINF_SUCCESS;
3577 }
3578
3579 /* not in use. */
3580 return VINF_SUCCESS;
3581}
3582
3583
3584/* -=-=-=-=-=- Ring 3: IBase -=-=-=-=-=- */
3585
3586/**
3587 * Queries an interface to the driver.
3588 *
3589 * @returns Pointer to interface.
3590 * @returns NULL if the interface was not supported by the driver.
3591 * @param pInterface Pointer to this interface structure.
3592 * @param enmInterface The requested interface identification.
3593 * @thread Any thread.
3594 */
3595static DECLCALLBACK(void *) vgaPortQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
3596{
3597 PVGASTATE pData = (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Base));
3598 switch (enmInterface)
3599 {
3600 case PDMINTERFACE_BASE:
3601 return &pData->Base;
3602 case PDMINTERFACE_DISPLAY_PORT:
3603 return &pData->Port;
3604 default:
3605 return NULL;
3606 }
3607}
3608
3609
3610/* -=-=-=-=-=- Ring 3: Dummy IDisplayConnector -=-=-=-=-=- */
3611
3612/**
3613 * Resize the display.
3614 * This is called when the resolution changes. This usually happens on
3615 * request from the guest os, but may also happen as the result of a reset.
3616 *
3617 * @param pInterface Pointer to this interface.
3618 * @param cx New display width.
3619 * @param cy New display height
3620 * @thread The emulation thread.
3621 */
3622static DECLCALLBACK(int) vgaDummyResize(PPDMIDISPLAYCONNECTOR pInterface, uint32_t bpp, void *pvVRAM, uint32_t cbLine, uint32_t cx, uint32_t cy)
3623{
3624 return VINF_SUCCESS;
3625}
3626
3627
3628/**
3629 * Update a rectangle of the display.
3630 * PDMIDISPLAYPORT::pfnUpdateDisplay is the caller.
3631 *
3632 * @param pInterface Pointer to this interface.
3633 * @param x The upper left corner x coordinate of the rectangle.
3634 * @param y The upper left corner y coordinate of the rectangle.
3635 * @param cx The width of the rectangle.
3636 * @param cy The height of the rectangle.
3637 * @thread The emulation thread.
3638 */
3639static DECLCALLBACK(void) vgaDummyUpdateRect(PPDMIDISPLAYCONNECTOR pInterface, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3640{
3641}
3642
3643
3644/**
3645 * Refresh the display.
3646 *
3647 * The interval between these calls is set by
3648 * PDMIDISPLAYPORT::pfnSetRefreshRate(). The driver should call
3649 * PDMIDISPLAYPORT::pfnUpdateDisplay() if it wishes to refresh the
3650 * display. PDMIDISPLAYPORT::pfnUpdateDisplay calls pfnUpdateRect with
3651 * the changed rectangles.
3652 *
3653 * @param pInterface Pointer to this interface.
3654 * @thread The emulation thread.
3655 */
3656static DECLCALLBACK(void) vgaDummyRefresh(PPDMIDISPLAYCONNECTOR pInterface)
3657{
3658}
3659
3660
3661/* -=-=-=-=-=- Ring 3: IDisplayPort -=-=-=-=-=- */
3662
3663/** Converts a display port interface pointer to a vga state pointer. */
3664#define IDISPLAYPORT_2_VGASTATE(pInterface) ( (PVGASTATE)((uintptr_t)pInterface - RT_OFFSETOF(VGASTATE, Port)) )
3665
3666
3667/**
3668 * Update the display with any changed regions.
3669 *
3670 * @param pInterface Pointer to this interface.
3671 * @see PDMIKEYBOARDPORT::pfnUpdateDisplay() for details.
3672 */
3673static DECLCALLBACK(int) vgaPortUpdateDisplay(PPDMIDISPLAYPORT pInterface)
3674{
3675 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3676 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3677
3678#ifdef DEBUG_sunlover
3679 LogFlow(("vgaPortUpdateDisplay\n"));
3680#endif /* DEBUG_sunlover */
3681
3682 /* This should be called only in non VBVA mode. */
3683
3684 int rc = vga_update_display(pData);
3685 if (rc != VINF_SUCCESS)
3686 return rc;
3687
3688 if (pData->fHaveDirtyBits)
3689 {
3690 PPDMDEVINS pDevIns = pData->pDevInsHC;
3691 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
3692 pData->fHaveDirtyBits = false;
3693 }
3694
3695 return VINF_SUCCESS;
3696}
3697
3698
3699/**
3700 * Update the entire display.
3701 *
3702 * @param pInterface Pointer to this interface.
3703 * @see PDMIKEYBOARDPORT::pfnUpdateDisplayAll() for details.
3704 */
3705static DECLCALLBACK(int) vgaPortUpdateDisplayAll(PPDMIDISPLAYPORT pInterface)
3706{
3707 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3708 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3709
3710 /* This is called both in VBVA mode and normal modes. */
3711
3712#ifdef DEBUG_sunlover
3713 LogFlow(("vgaPortUpdateDisplayAll\n"));
3714#endif /* DEBUG_sunlover */
3715
3716 pData->graphic_mode = -1; /* force full update */
3717
3718 int rc = vga_update_display(pData);
3719
3720 /* The dirty bits array has been just cleared, reset handlers as well. */
3721 PPDMDEVINS pDevIns = pData->pDevInsHC;
3722 PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
3723
3724 return rc;
3725}
3726
3727
3728/**
3729 * Sets the refresh rate and restart the timer.
3730 *
3731 * @returns VBox status code.
3732 * @param pInterface Pointer to this interface.
3733 * @param cMilliesInterval Number of millies between two refreshes.
3734 * @see PDMIKEYBOARDPORT::pfnSetRefreshRate() for details.
3735 */
3736static DECLCALLBACK(int) vgaPortSetRefreshRate(PPDMIDISPLAYPORT pInterface, uint32_t cMilliesInterval)
3737{
3738 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3739
3740 pData->cMilliesRefreshInterval = cMilliesInterval;
3741 if (cMilliesInterval)
3742 return TMTimerSetMillies(pData->RefreshTimer, cMilliesInterval);
3743 return TMTimerStop(pData->RefreshTimer);
3744}
3745
3746
3747/** @copydoc PDMIDISPLAYPORT::pfnQueryColorDepth */
3748static DECLCALLBACK(int) vgaPortQueryColorDepth(PPDMIDISPLAYPORT pInterface, uint32_t *pcBits)
3749{
3750 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3751
3752 if (!pcBits)
3753 return VERR_INVALID_PARAMETER;
3754 *pcBits = vga_get_bpp(pData);
3755 return VINF_SUCCESS;
3756}
3757
3758/**
3759 * Create a 32-bbp snapshot of the display.
3760 *
3761 * @param pInterface Pointer to this interface.
3762 * @param pvData Pointer the buffer to copy the bits to.
3763 * @param cbData Size of the buffer.
3764 * @param pcx Where to store the width of the bitmap. (optional)
3765 * @param pcy Where to store the height of the bitmap. (optional)
3766 * @param pcbData Where to store the actual size of the bitmap. (optional)
3767 * @see PDMIKEYBOARDPORT::pfnSnapshot() for details.
3768 */
3769static DECLCALLBACK(int) vgaPortSnapshot(PPDMIDISPLAYPORT pInterface, void *pvData, size_t cbData, uint32_t *pcx, uint32_t *pcy, size_t *pcbData)
3770{
3771 /* @todo r=sunlover: replace the method with a direct VRAM rendering like in vgaPortUpdateDisplayRect. */
3772 PPDMIDISPLAYCONNECTOR pConnector;
3773 PDMIDISPLAYCONNECTOR Connector;
3774 int32_t graphic_mode;
3775 uint32_t fRenderVRAM;
3776 size_t cbRequired;
3777 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3778 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3779 LogFlow(("vgaPortSnapshot: pvData=%p cbData=%d pcx=%p pcy=%p pcbData=%p\n", pvData, cbData, pcx, pcy, pcbData));
3780
3781 /*
3782 * Validate input.
3783 */
3784 if (!pvData)
3785 return VERR_INVALID_PARAMETER;
3786
3787 /*
3788 * Do a regular refresh first to resolve any pending resize issues.
3789 *
3790 * 20060317 It used to be pfnUpdateDisplay, but by VBVA design
3791 * only pfnUpdateDisplayAll is allowed to be called in VBVA mode.
3792 * Also since the goal here is to have updated display for screenshot,
3793 * the UpdateDisplayAll is even more logical to call. (sunlover)
3794 */
3795 pInterface->pfnUpdateDisplayAll(pInterface);
3796
3797 /*
3798 * Validate the buffer size.
3799 */
3800 cbRequired = RT_ALIGN_Z(pData->last_scr_width, 4) * pData->last_scr_height * 4;
3801 if (cbRequired > cbData)
3802 {
3803 Log(("vgaPortSnapshot: %d bytes are required, a buffer of %d bytes is profiled.\n", cbRequired, cbData));
3804 return VERR_BUFFER_OVERFLOW;
3805 }
3806
3807 /*
3808 * Temporarily replace the display connector interface with a fake one.
3809 */
3810 Connector.pu8Data = (uint8_t*)pvData;
3811 Connector.cBits = 32;
3812 Connector.cx = pData->pDrv->cx;
3813 Connector.cy = pData->pDrv->cy;
3814 Connector.cbScanline = RT_ALIGN_32(Connector.cx, 4) * 4;
3815 Connector.pfnRefresh = vgaDummyRefresh;
3816 Connector.pfnResize = vgaDummyResize;
3817 Connector.pfnUpdateRect = vgaDummyUpdateRect;
3818
3819 /* save & replace state data. */
3820 pConnector = pData->pDrv;
3821 pData->pDrv = &Connector;
3822 graphic_mode = pData->graphic_mode;
3823 pData->graphic_mode = -1; /* force a full refresh. */
3824 fRenderVRAM = pData->fRenderVRAM;
3825 pData->fRenderVRAM = 1; /* force the guest VRAM rendering to the given buffer. */
3826
3827 /* make the snapshot. */
3828 int rc = vga_update_display(pData);
3829
3830 /* restore */
3831 pData->pDrv = pConnector;
3832 pData->graphic_mode = graphic_mode;
3833 pData->fRenderVRAM = fRenderVRAM;
3834
3835 if (rc != VINF_SUCCESS)
3836 return rc;
3837
3838 /*
3839 * Return the result.
3840 */
3841 if (pcx)
3842 *pcx = Connector.cx;
3843 if (pcy)
3844 *pcy = Connector.cy;
3845 if (pcbData)
3846 *pcbData = cbRequired;
3847 LogFlow(("vgaPortSnapshot: returns VINF_SUCCESS (cx=%d cy=%d cbData=%d)\n", Connector.cx, Connector.cy, cbRequired));
3848 return VINF_SUCCESS;
3849}
3850
3851
3852/**
3853 * Copy bitmap to the display.
3854 *
3855 * @param pInterface Pointer to this interface.
3856 * @param pvData Pointer to the bitmap bits.
3857 * @param x The upper left corner x coordinate of the destination rectangle.
3858 * @param y The upper left corner y coordinate of the destination rectangle.
3859 * @param cx The width of the source and destination rectangles.
3860 * @param cy The height of the source and destination rectangles.
3861 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
3862 */
3863static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3864{
3865 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3866 int rc = VINF_SUCCESS;
3867 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3868 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
3869
3870 /*
3871 * Validate input.
3872 */
3873 if ( pvData
3874 && x < pData->pDrv->cx
3875 && cx <= pData->pDrv->cx
3876 && cx + x <= pData->pDrv->cx
3877 && y < pData->pDrv->cy
3878 && cy <= pData->pDrv->cy
3879 && cy + y <= pData->pDrv->cy)
3880 {
3881 /*
3882 * Determin bytes per pixel in the destination buffer.
3883 */
3884 size_t cbPixelDst = 0;
3885 switch (pData->pDrv->cBits)
3886 {
3887 case 8:
3888 cbPixelDst = 1;
3889 break;
3890 case 15:
3891 case 16:
3892 cbPixelDst = 2;
3893 break;
3894 case 24:
3895 cbPixelDst = 3;
3896 break;
3897 case 32:
3898 cbPixelDst = 4;
3899 break;
3900 default:
3901 rc = VERR_INVALID_PARAMETER;
3902 break;
3903 }
3904 if (VBOX_SUCCESS(rc))
3905 {
3906 /*
3907 * The blitting loop.
3908 */
3909 size_t cbLineSrc = RT_ALIGN_Z(cx, 4) * 4;
3910 uint8_t *pu8Src = (uint8_t *)pvData;
3911 size_t cbLineDst = pData->pDrv->cbScanline;
3912 uint8_t *pu8Dst = pData->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3913 uint32_t cyLeft = cy;
3914 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pData->pDrv->cBits)];
3915 Assert(pfnVgaDrawLine);
3916 while (cyLeft-- > 0)
3917 {
3918 pfnVgaDrawLine(pData, pu8Dst, pu8Src, cx);
3919 pu8Dst += cbLineDst;
3920 pu8Src += cbLineSrc;
3921 }
3922
3923 /*
3924 * Invalidate the area.
3925 */
3926 pData->pDrv->pfnUpdateRect(pData->pDrv, x, y, cx, cy);
3927 }
3928 }
3929 else
3930 rc = VERR_INVALID_PARAMETER;
3931
3932 LogFlow(("vgaPortDisplayBlt: returns %Vrc\n", rc));
3933 return rc;
3934}
3935
3936static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
3937{
3938 uint32_t v;
3939 vga_draw_line_func *vga_draw_line;
3940
3941 uint32_t cbPixelDst;
3942 uint32_t cbLineDst;
3943 uint8_t *pu8Dst;
3944
3945 uint32_t cbPixelSrc;
3946 uint32_t cbLineSrc;
3947 uint8_t *pu8Src;
3948
3949 uint32_t u32OffsetSrc, u32Dummy;
3950
3951 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3952
3953#ifdef DEBUG_sunlover
3954 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
3955#endif /* DEBUG_sunlover */
3956
3957 Assert(pInterface);
3958 Assert(s->pDrv);
3959 Assert(s->pDrv->pu8Data);
3960
3961 /* Check if there is something to do at all. */
3962 if (!s->fRenderVRAM)
3963 {
3964 /* The framebuffer uses the guest VRAM directly. */
3965#ifdef DEBUG_sunlover
3966 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
3967#endif /* DEBUG_sunlover */
3968 return;
3969 }
3970
3971 /* Correct negative x and y coordinates. */
3972 if (x < 0)
3973 {
3974 x += w; /* Compute xRight which is also the new width. */
3975 w = (x < 0) ? 0 : x;
3976 x = 0;
3977 }
3978
3979 if (y < 0)
3980 {
3981 y += h; /* Compute yBottom, which is also the new height. */
3982 h = (y < 0) ? 0 : y;
3983 y = 0;
3984 }
3985
3986 /* Also check if coords are greater than the display resolution. */
3987 if (x + w > s->pDrv->cx)
3988 {
3989#ifndef VBOX
3990 w = s->pDrv->cx > x? s->pDrv->cx - x: 0;
3991#else
3992 // x < 0 is not possible here
3993 w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
3994#endif
3995 }
3996
3997 if (y + h > s->pDrv->cy)
3998 {
3999#ifndef VBOX
4000 h = s->pDrv->cy > y? s->pDrv->cy - y: 0;
4001#else
4002 // y < 0 is not possible here
4003 h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
4004#endif
4005 }
4006
4007#ifdef DEBUG_sunlover
4008 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
4009#endif /* DEBUG_sunlover */
4010
4011 /* Check if there is something to do at all. */
4012 if (w == 0 || h == 0)
4013 {
4014 /* Empty rectangle. */
4015#ifdef DEBUG_sunlover
4016 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
4017#endif /* DEBUG_sunlover */
4018 return;
4019 }
4020
4021 /** @todo This method should be made universal and not only for VBVA.
4022 * VGA_DRAW_LINE* must be selected and src/dst address calculation
4023 * changed.
4024 */
4025
4026 /* Choose the rendering function. */
4027 switch(s->get_bpp(s))
4028 {
4029 default:
4030 case 0:
4031 /* A LFB mode is already disabled, but the callback is still called
4032 * by Display because VBVA buffer is being flushed.
4033 * Nothing to do, just return.
4034 */
4035 return;
4036 case 8:
4037 v = VGA_DRAW_LINE8;
4038 break;
4039 case 15:
4040 v = VGA_DRAW_LINE15;
4041 break;
4042 case 16:
4043 v = VGA_DRAW_LINE16;
4044 break;
4045 case 24:
4046 v = VGA_DRAW_LINE24;
4047 break;
4048 case 32:
4049 v = VGA_DRAW_LINE32;
4050 break;
4051 }
4052
4053 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
4054
4055 /* Compute source and destination addresses and pitches. */
4056 cbPixelDst = (s->pDrv->cBits + 7) / 8;
4057 cbLineDst = s->pDrv->cbScanline;
4058 pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4059
4060 cbPixelSrc = (s->get_bpp(s) + 7) / 8;
4061 s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
4062
4063 /* Assume that rendering is performed only on visible part of VRAM.
4064 * This is true because coordinates were verified.
4065 */
4066 pu8Src = s->vram_ptrHC;
4067 pu8Src += u32OffsetSrc + y * cbLineSrc + x * cbPixelSrc;
4068
4069 /* Render VRAM to framebuffer. */
4070
4071#ifdef DEBUG_sunlover
4072 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
4073#endif /* DEBUG_sunlover */
4074
4075 while (h-- > 0)
4076 {
4077 vga_draw_line (s, pu8Dst, pu8Src, w);
4078 pu8Dst += cbLineDst;
4079 pu8Src += cbLineSrc;
4080 }
4081
4082#ifdef DEBUG_sunlover
4083 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
4084#endif /* DEBUG_sunlover */
4085}
4086
4087static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
4088{
4089 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
4090
4091 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
4092
4093 s->fRenderVRAM = fRender;
4094}
4095
4096
4097static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer)
4098{
4099 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4100 if (pData->pDrv)
4101 pData->pDrv->pfnRefresh(pData->pDrv);
4102 if (pData->cMilliesRefreshInterval)
4103 TMTimerSetMillies(pTimer, pData->cMilliesRefreshInterval);
4104}
4105
4106
4107/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
4108
4109/**
4110 * Callback function for mapping an PCI I/O region.
4111 *
4112 * @return VBox status code.
4113 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
4114 * @param iRegion The region number.
4115 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
4116 * I/O port, else it's a physical address.
4117 * This address is *NOT* relative to pci_mem_base like earlier!
4118 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
4119 */
4120static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
4121{
4122 int rc;
4123 PVGASTATE pData = PDMINS2DATA(pPciDev->pDevIns, PVGASTATE);
4124 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%VGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
4125
4126 /*
4127 * VRam mapping.
4128 */
4129 if (iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH)
4130 {
4131 /*
4132 * Register and lock the VRAM.
4133 *
4134 * Windows usually re-initializes the PCI devices, so we have to check whether the memory was
4135 * already registered before trying to do that all over again.
4136 */
4137 PVM pVM = PDMDevHlpGetVM(pPciDev->pDevIns);
4138 if (pData->GCPhysVRAM)
4139 {
4140 AssertMsg(pData->GCPhysVRAM == GCPhysAddress,
4141 ("The Guest OS relocated our LFB! old GCPhysVRAM=%VGp new GCPhysAddress=%VGp\n",
4142 pData->GCPhysVRAM, GCPhysAddress));
4143 rc = VINF_SUCCESS;
4144 }
4145 else
4146 {
4147 /*
4148 * Register and lock the VRAM.
4149 */
4150 rc = MMR3PhysRegister(pVM, pData->vram_ptrHC, GCPhysAddress, pData->vram_size, MM_RAM_FLAGS_MMIO2, "VRam");
4151 if (VBOX_SUCCESS(rc))
4152 {
4153 if (!pData->GCPhysVRAM)
4154 rc = PGMR3HandlerPhysicalRegister(pVM, PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
4155 GCPhysAddress, GCPhysAddress + (pData->vram_size - 1),
4156 vgaR3LFBAccessHandler, pData,
4157 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pData->pDevInsHC->pvInstanceDataR0,
4158 g_DeviceVga.szGCMod, "vgaGCLFBAccessHandler", pData->pDevInsHC->pvInstanceDataGC,
4159 "VGA LFB");
4160 if (VBOX_SUCCESS(rc))
4161 {
4162 /*
4163 * Map the first 256KB of the VRAM into GC for GC VGA support.
4164 */
4165 RTGCPTR GCPtr;
4166 rc = MMR3HyperMapGCPhys(pVM, GCPhysAddress, VGA_MAPPING_SIZE, "VGA VRam", &GCPtr);
4167 if (VBOX_SUCCESS(rc))
4168 {
4169 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
4170
4171 pData->vram_ptrGC = GCPtr;
4172 pData->GCPhysVRAM = GCPhysAddress;
4173 return VINF_SUCCESS;
4174 }
4175 AssertMsgFailed(("MMR3HyperMapGCPhys failed, rc=%Vrc\n", rc));
4176 }
4177 else
4178 AssertMsgFailed(("Failed to register write handler for VRAM! rc=%Vrc\n", rc));
4179 }
4180 else
4181 AssertReleaseMsgFailed(("Failed to register VRAM! rc=%Vra\n", rc));
4182 }
4183 return rc;
4184 }
4185 else
4186 AssertReleaseMsgFailed(("Huh!?! iRegion=%d enmType=%d\n", iRegion, enmType));
4187 return VERR_INTERNAL_ERROR;
4188}
4189
4190
4191/* -=-=-=-=-=- Ring3: Misc Wrappers -=-=-=-=-=- */
4192
4193/**
4194 * Saves a state of the VGA device.
4195 *
4196 * @returns VBox status code.
4197 * @param pDevIns The device instance.
4198 * @param pSSMHandle The handle to save the state to.
4199 */
4200static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
4201{
4202 vga_save(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE));
4203 return VINF_SUCCESS;
4204}
4205
4206
4207/**
4208 * Loads a saved VGA device state.
4209 *
4210 * @returns VBox status code.
4211 * @param pDevIns The device instance.
4212 * @param pSSMHandle The handle to the saved state.
4213 * @param u32Version The data unit version number.
4214 */
4215static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
4216{
4217 if (vga_load(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE), u32Version))
4218 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4219 return VINF_SUCCESS;
4220}
4221
4222
4223/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
4224
4225/**
4226 * Reset notification.
4227 *
4228 * @returns VBox status.
4229 * @param pDevIns The device instance data.
4230 */
4231static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
4232{
4233 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4234 char *pchStart;
4235 char *pchEnd;
4236 LogFlow(("vgaReset\n"));
4237
4238 /* Clear the VRAM ourselves. */
4239 if (pData->vram_ptrHC && pData->vram_size)
4240 {
4241#ifdef LOG_ENABLED /** @todo separate function. */
4242 /* First dump the textmode contents to the log; handy for capturing Windows blue screens. */
4243 uint8_t graphic_mode;
4244 VGAState *s = pData;
4245
4246 if (!(s->ar_index & 0x20)) {
4247 graphic_mode = GMODE_BLANK;
4248 } else {
4249 graphic_mode = s->gr[6] & 1;
4250 }
4251 switch(graphic_mode)
4252 case GMODE_TEXT:
4253 {
4254 int cw, height, width, cheight, cx_min, cx_max, cy, cx;
4255 int x_incr;
4256 uint8_t *s1, *src, ch, cattr;
4257 int line_offset;
4258 uint16_t ch_attr;
4259
4260 line_offset = s->line_offset;
4261 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 4);
4262
4263 /* total width & height */
4264 cheight = (s->cr[9] & 0x1f) + 1;
4265 cw = 8;
4266 if (!(s->sr[1] & 0x01))
4267 cw = 9;
4268 if (s->sr[1] & 0x08)
4269 cw = 16; /* NOTE: no 18 pixel wide */
4270 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
4271 width = (s->cr[0x01] + 1);
4272 if (s->cr[0x06] == 100) {
4273 /* ugly hack for CGA 160x100x16 - explain me the logic */
4274 height = 100;
4275 } else {
4276 height = s->cr[0x12] |
4277 ((s->cr[0x07] & 0x02) << 7) |
4278 ((s->cr[0x07] & 0x40) << 3);
4279 height = (height + 1) / cheight;
4280 }
4281 if ((height * width) > CH_ATTR_SIZE) {
4282 /* better than nothing: exit if transient size is too big */
4283 break;
4284 }
4285 RTLogPrintf("VGA textmode BEGIN (%dx%d):\n\n", height, width);
4286 for(cy = 0; cy < height; cy++) {
4287 src = s1;
4288 cx_min = width;
4289 cx_max = -1;
4290 for(cx = 0; cx < width; cx++) {
4291 ch_attr = *(uint16_t *)src;
4292 if (cx < cx_min)
4293 cx_min = cx;
4294 if (cx > cx_max)
4295 cx_max = cx;
4296# ifdef WORDS_BIGENDIAN
4297 ch = ch_attr >> 8;
4298 cattr = ch_attr & 0xff;
4299# else
4300 ch = ch_attr & 0xff;
4301 cattr = ch_attr >> 8;
4302# endif
4303 RTLogPrintf("%c", ch);
4304
4305 src += 4;
4306 }
4307 if (cx_max != -1)
4308 RTLogPrintf("\n");
4309
4310 s1 += line_offset;
4311 }
4312 RTLogPrintf("VGA textmode END:\n\n");
4313 }
4314
4315#endif
4316 memset(pData->vram_ptrHC, 0, pData->vram_size);
4317 }
4318
4319 /*
4320 * Zero most of it.
4321 *
4322 * Unlike vga_reset we're leaving out a few members which believe must
4323 * remain unchanged....
4324 */
4325 /* 1st part. */
4326 pchStart = (char *)&pData->latch;
4327 pchEnd = (char *)&pData->invalidated_y_table;
4328 memset(pchStart, 0, pchEnd - pchStart);
4329
4330 /* 2nd part. */
4331 pchStart = (char *)&pData->last_palette;
4332 pchEnd = (char *)&pData->u32Marker;
4333 memset(pchStart, 0, pchEnd - pchStart);
4334
4335
4336 /*
4337 * Restore and re-init some bits.
4338 */
4339 pData->get_bpp = vga_get_bpp;
4340 pData->get_offsets = vga_get_offsets;
4341 pData->get_resolution = vga_get_resolution;
4342 pData->graphic_mode = -1; /* Force full update. */
4343#ifdef CONFIG_BOCHS_VBE
4344 pData->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
4345 pData->vbe_regs[VBE_DISPI_INDEX_VBOX_VIDEO] = 0;
4346 pData->vbe_bank_mask = ((pData->vram_size >> 16) - 1);
4347#endif /* CONFIG_BOCHS_VBE */
4348
4349 /*
4350 * Reset the LBF mapping.
4351 */
4352 pData->fLFBUpdated = false;
4353 if ( ( pData->fGCEnabled
4354 || pData->fR0Enabled)
4355 && pData->GCPhysVRAM)
4356 {
4357 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
4358 AssertRC(rc);
4359 }
4360
4361 /* notify port handler */
4362 if (pData->pDrv)
4363 pData->pDrv->pfnReset(pData->pDrv);
4364}
4365
4366
4367/**
4368 * Device relocation callback.
4369 *
4370 * @param pDevIns Pointer to the device instance.
4371 * @param offDelta The relocation delta relative to the old location.
4372 *
4373 * @see FNPDMDEVRELOCATE for details.
4374 */
4375static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4376{
4377 if (offDelta)
4378 {
4379 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4380 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
4381
4382 pData->GCPtrLFBHandler += offDelta;
4383 pData->vram_ptrGC += offDelta;
4384 }
4385}
4386
4387
4388/**
4389 * Attach command.
4390 *
4391 * This is called to let the device attach to a driver for a specified LUN
4392 * during runtime. This is not called during VM construction, the device
4393 * constructor have to attach to all the available drivers.
4394 *
4395 * This is like plugging in the monitor after turning on the PC.
4396 *
4397 * @returns VBox status code.
4398 * @param pDevIns The device instance.
4399 * @param iLUN The logical unit which is being detached.
4400 */
4401static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN)
4402{
4403 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4404 switch (iLUN)
4405 {
4406 /* LUN #0: Display port. */
4407 case 0:
4408 {
4409 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pData->Base, &pData->pDrvBase, "Display Port");
4410 if (VBOX_SUCCESS(rc))
4411 {
4412 pData->pDrv = (PDMIDISPLAYCONNECTOR*)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_DISPLAY_CONNECTOR);
4413 if (pData->pDrv)
4414 {
4415 /* pData->pDrv->pu8Data can be NULL when there is no framebuffer. */
4416 if ( pData->pDrv->pfnRefresh
4417 && pData->pDrv->pfnResize
4418 && pData->pDrv->pfnUpdateRect)
4419 rc = VINF_SUCCESS;
4420 else
4421 {
4422 Assert(pData->pDrv->pfnRefresh);
4423 Assert(pData->pDrv->pfnResize);
4424 Assert(pData->pDrv->pfnUpdateRect);
4425 pData->pDrv = NULL;
4426 pData->pDrvBase = NULL;
4427 rc = VERR_INTERNAL_ERROR;
4428 }
4429 }
4430 else
4431 {
4432 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Vrc\n", rc));
4433 pData->pDrvBase = NULL;
4434 rc = VERR_PDM_MISSING_INTERFACE;
4435 }
4436 }
4437 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4438 {
4439 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
4440 rc = VINF_SUCCESS;
4441 }
4442 else
4443 AssertMsgFailed(("Failed to attach LUN #0! rc=%Vrc\n", rc));
4444 return rc;
4445 }
4446
4447 default:
4448 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4449 return VERR_PDM_NO_SUCH_LUN;
4450 }
4451}
4452
4453
4454/**
4455 * Detach notification.
4456 *
4457 * This is called when a driver is detaching itself from a LUN of the device.
4458 * The device should adjust it's state to reflect this.
4459 *
4460 * This is like unplugging the monitor while the PC is still running.
4461 *
4462 * @param pDevIns The device instance.
4463 * @param iLUN The logical unit which is being detached.
4464 */
4465static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN)
4466{
4467 /*
4468 * Reset the interfaces and update the controller state.
4469 */
4470 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4471 switch (iLUN)
4472 {
4473 /* LUN #0: Display port. */
4474 case 0:
4475 pData->pDrv = NULL;
4476 pData->pDrvBase = NULL;
4477 break;
4478
4479 default:
4480 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4481 break;
4482 }
4483}
4484
4485
4486
4487/**
4488 * Construct a VGA device instance for a VM.
4489 *
4490 * @returns VBox status.
4491 * @param pDevIns The device instance data.
4492 * If the registration structure is needed, pDevIns->pDevReg points to it.
4493 * @param iInstance Instance number. Use this to figure out which registers and such to use.
4494 * The device number is also found in pDevIns->iInstance, but since it's
4495 * likely to be freqently used PDM passes it as parameter.
4496 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
4497 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
4498 * iInstance it's expected to be used a bit in this function.
4499 */
4500static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
4501{
4502 static bool fExpandDone = false;
4503 bool f;
4504 int rc;
4505 unsigned i;
4506 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4507 PVM pVM = PDMDevHlpGetVM(pDevIns);
4508#ifdef VBE_NEW_DYN_LIST
4509 uint32_t cCustomModes;
4510 uint32_t cyReduction;
4511 PVBEHEADER pVBEDataHdr;
4512 ModeInfoListItem *pCurMode;
4513 unsigned cb;
4514#endif
4515 Assert(iInstance == 0);
4516 Assert(pVM);
4517
4518 /*
4519 * Init static data.
4520 */
4521 if (!fExpandDone)
4522 {
4523 fExpandDone = true;
4524 vga_init_expand();
4525 }
4526
4527 /*
4528 * Validate configuration.
4529 */
4530 if (!CFGMR3AreValuesValid(pCfgHandle, "VRamSize\0"
4531 "GCEnabled\0"
4532 "R0Enabled\0"
4533 "CustomVideoModes\0"
4534 "HeightReduction\0"
4535 "CustomVideoMode1\0"
4536 "CustomVideoMode2\0"
4537 "CustomVideoMode3\0"
4538 "CustomVideoMode4\0"
4539 "CustomVideoMode5\0"
4540 "CustomVideoMode6\0"
4541 "CustomVideoMode7\0"
4542 "CustomVideoMode8\0"
4543 "CustomVideoMode9\0"
4544 "CustomVideoMode10\0"
4545 "CustomVideoMode11\0"
4546 "CustomVideoMode12\0"
4547 "CustomVideoMode13\0"
4548 "CustomVideoMode14\0"
4549 "CustomVideoMode15\0"
4550 "CustomVideoMode16\0"))
4551 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4552 N_("Invalid configuration for vga device"));
4553
4554 /*
4555 * Init state data.
4556 */
4557 rc = CFGMR3QueryU32(pCfgHandle, "VRamSize", &pData->vram_size);
4558 if (VBOX_FAILURE(rc) || !pData->vram_size)
4559 pData->vram_size = VGA_VRAM_DEFAULT;
4560 else if (pData->vram_size > VGA_VRAM_MAX)
4561 {
4562 AssertMsgFailed(("vram_size=%d max=%d\n", pData->vram_size, VGA_VRAM_MAX));
4563 pData->vram_size = VGA_VRAM_MAX;
4564 }
4565 else if (pData->vram_size < VGA_VRAM_MIN)
4566 {
4567 AssertMsgFailed(("vram_size=%d min=%d\n", pData->vram_size, VGA_VRAM_MIN));
4568 pData->vram_size = RT_ALIGN_32(pData->vram_size, _1M);
4569 }
4570 Log(("VGA: VRamSize=%#x\n", pData->vram_size));
4571
4572 pData->fGCEnabled = true;
4573 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &f);
4574 if (VBOX_SUCCESS(rc) && !f)
4575 pData->fGCEnabled = false;
4576 Log(("VGA: fGCEnabled=%d\n", pData->fGCEnabled));
4577
4578 pData->fR0Enabled = true;
4579 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &f);
4580 if (VBOX_SUCCESS(rc) && !f)
4581 pData->fR0Enabled = false;
4582 Log(("VGA: fR0Enabled=%d\n", pData->fR0Enabled));
4583
4584 pData->pDevInsHC = pDevIns;
4585
4586 vgaR3Reset(pDevIns);
4587
4588 /* The PCI devices configuration. */
4589 pData->Dev.config[0x00] = 0xee; /* PCI vendor, just a free bogus value */
4590 pData->Dev.config[0x01] = 0x80;
4591
4592 pData->Dev.config[0x02] = 0xef; /* Device ID */
4593 pData->Dev.config[0x03] = 0xbe;
4594
4595 pData->Dev.config[0x0a] = 0x00; /* VGA controller */
4596 pData->Dev.config[0x0b] = 0x03;
4597 pData->Dev.config[0x0e] = 0x00; /* header_type */
4598
4599 /* The LBF access handler - error handling is better here than in the map function. */
4600 rc = PDMR3GetSymbolGCLazy(pVM, pDevIns->pDevReg->szGCMod, "vgaGCLFBAccessHandler", &pData->GCPtrLFBHandler);
4601 if (VBOX_FAILURE(rc))
4602 {
4603 AssertReleaseMsgFailed(("PDMR3GetSymbolGC(, %s, \"vgaGCLFBAccessHandler\",) -> %Vrc\n", pDevIns->pDevReg->szGCMod, rc));
4604 return rc;
4605 }
4606
4607 /* the interfaces. */
4608 pData->Base.pfnQueryInterface = vgaPortQueryInterface;
4609
4610 pData->Port.pfnUpdateDisplay = vgaPortUpdateDisplay;
4611 pData->Port.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
4612 pData->Port.pfnQueryColorDepth = vgaPortQueryColorDepth;
4613 pData->Port.pfnSetRefreshRate = vgaPortSetRefreshRate;
4614 pData->Port.pfnSnapshot = vgaPortSnapshot;
4615 pData->Port.pfnDisplayBlt = vgaPortDisplayBlt;
4616 pData->Port.pfnUpdateDisplayRect = vgaPortUpdateDisplayRect;
4617 pData->Port.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
4618
4619
4620 /*
4621 * Register I/O ports, ROM and save state.
4622 */
4623 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
4624 if (VBOX_FAILURE(rc))
4625 return rc;
4626 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
4627 if (VBOX_FAILURE(rc))
4628 return rc;
4629 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
4630 if (VBOX_FAILURE(rc))
4631 return rc;
4632 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
4633 if (VBOX_FAILURE(rc))
4634 return rc;
4635 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
4636 if (VBOX_FAILURE(rc))
4637 return rc;
4638
4639#ifdef CONFIG_BOCHS_VBE
4640 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
4641 if (VBOX_FAILURE(rc))
4642 return rc;
4643 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
4644 if (VBOX_FAILURE(rc))
4645 return rc;
4646#if 0
4647 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4648 and tries to map other devices there */
4649 /* Old Bochs. */
4650 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff80, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, "VGA/VBE - Index Old");
4651 if (VBOX_FAILURE(rc))
4652 return rc;
4653 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff81, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, "VGA/VBE - Data Old");
4654 if (VBOX_FAILURE(rc))
4655 return rc;
4656#endif
4657#endif /* CONFIG_BOCHS_VBE */
4658
4659 /* guest context extension */
4660 if (pData->fGCEnabled)
4661 {
4662 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4663 if (VBOX_FAILURE(rc))
4664 return rc;
4665 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4666 if (VBOX_FAILURE(rc))
4667 return rc;
4668 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4669 if (VBOX_FAILURE(rc))
4670 return rc;
4671 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4672 if (VBOX_FAILURE(rc))
4673 return rc;
4674 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4675 if (VBOX_FAILURE(rc))
4676 return rc;
4677#ifdef CONFIG_BOCHS_VBE
4678 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4679 if (VBOX_FAILURE(rc))
4680 return rc;
4681 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4682 if (VBOX_FAILURE(rc))
4683 return rc;
4684
4685#if 0
4686 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4687 and tries to map other devices there */
4688 /* Old Bochs. */
4689 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4690 if (VBOX_FAILURE(rc))
4691 return rc;
4692 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4693 if (VBOX_FAILURE(rc))
4694 return rc;
4695#endif
4696
4697#endif /* CONFIG_BOCHS_VBE */
4698 }
4699
4700 /* R0 context extension */
4701 if (pData->fR0Enabled)
4702 {
4703 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4704 if (VBOX_FAILURE(rc))
4705 return rc;
4706 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4707 if (VBOX_FAILURE(rc))
4708 return rc;
4709 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4710 if (VBOX_FAILURE(rc))
4711 return rc;
4712 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4713 if (VBOX_FAILURE(rc))
4714 return rc;
4715 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4716 if (VBOX_FAILURE(rc))
4717 return rc;
4718#ifdef CONFIG_BOCHS_VBE
4719 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4720 if (VBOX_FAILURE(rc))
4721 return rc;
4722 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4723 if (VBOX_FAILURE(rc))
4724 return rc;
4725
4726#if 0
4727 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4728 and tries to map other devices there */
4729 /* Old Bochs. */
4730 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4731 if (VBOX_FAILURE(rc))
4732 return rc;
4733 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4734 if (VBOX_FAILURE(rc))
4735 return rc;
4736#endif
4737
4738#endif /* CONFIG_BOCHS_VBE */
4739 }
4740
4741 /* vga mmio */
4742 rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
4743 if (VBOX_FAILURE(rc))
4744 return rc;
4745 if (pData->fGCEnabled)
4746 {
4747 rc = PDMDevHlpMMIORegisterGC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4748 if (VBOX_FAILURE(rc))
4749 return rc;
4750 }
4751 if (pData->fR0Enabled)
4752 {
4753 rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4754 if (VBOX_FAILURE(rc))
4755 return rc;
4756 }
4757
4758 /* vga bios */
4759 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
4760 if (VBOX_FAILURE(rc))
4761 return rc;
4762 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4763 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4764 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, g_cbVgaBiosBinary, &g_abVgaBiosBinary[0],
4765 false /* fShadow */, "VGA BIOS");
4766 if (VBOX_FAILURE(rc))
4767 return rc;
4768
4769 /* save */
4770 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
4771 NULL, vgaR3SaveExec, NULL,
4772 NULL, vgaR3LoadExec, NULL);
4773 if (VBOX_FAILURE(rc))
4774 return rc;
4775
4776 /* PCI */
4777 rc = PDMDevHlpPCIRegister(pDevIns, &pData->Dev);
4778 if (VBOX_FAILURE(rc))
4779 return rc;
4780 /*AssertMsg(pData->Dev.devfn == 16 || iInstance != 0, ("pData->Dev.devfn=%d\n", pData->Dev.devfn));*/
4781 if (pData->Dev.devfn != 16 && iInstance == 0)
4782 Log(("!!WARNING!!: pData->dev.devfn=%d (ignore if testcase or no started by Main)\n", pData->Dev.devfn));
4783 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, pData->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
4784 if (VBOX_FAILURE(rc))
4785 return rc;
4786
4787 /*
4788 * Create the refresh timer.
4789 */
4790 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh, "VGA Refresh Timer", &pData->RefreshTimer);
4791 if (VBOX_FAILURE(rc))
4792 return rc;
4793
4794 /*
4795 * Attach to the display.
4796 */
4797 rc = vgaAttach(pDevIns, 0 /* display LUN # */);
4798 if (VBOX_FAILURE(rc))
4799 return rc;
4800
4801 /*
4802 * Allocate the VRAM.
4803 */
4804 rc = SUPPageAlloc(pData->vram_size >> PAGE_SHIFT, (void **)&pData->vram_ptrHC);
4805 if (VBOX_FAILURE(rc))
4806 {
4807 AssertMsgFailed(("SUPPageAlloc(%#x,) -> %d\n", pData->vram_size, rc));
4808 return rc;
4809 }
4810
4811#ifdef VBE_NEW_DYN_LIST
4812 /*
4813 * Compute buffer size for the VBE BIOS Extra Data.
4814 */
4815 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
4816
4817 rc = CFGMR3QueryU32(pCfgHandle, "HeightReduction", &cyReduction);
4818 if (VBOX_SUCCESS(rc) && cyReduction)
4819 cb *= 2; /* Default mode list will be twice long */
4820 else
4821 cyReduction = 0;
4822
4823 rc = CFGMR3QueryU32(pCfgHandle, "CustomVideoModes", &cCustomModes);
4824 if (VBOX_SUCCESS(rc) && cCustomModes)
4825 cb += sizeof(ModeInfoListItem) * cCustomModes;
4826 else
4827 cCustomModes = 0;
4828
4829 /*
4830 * Allocate and initialize buffer for the VBE BIOS Extra Data.
4831 */
4832 pData->cbVBEExtraData = sizeof(VBEHEADER) + cb;
4833 pData->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pData->cbVBEExtraData);
4834 if (!pData->pu8VBEExtraData)
4835 return VERR_NO_MEMORY;
4836
4837 pVBEDataHdr = (PVBEHEADER)pData->pu8VBEExtraData;
4838 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
4839 pVBEDataHdr->cbData = cb;
4840
4841#ifndef VRAM_SIZE_FIX
4842 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
4843 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
4844#else /* VRAM_SIZE_FIX defined */
4845 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
4846 for (i = 0; i < MODE_INFO_SIZE; i++)
4847 {
4848 uint32_t pixelWidth, reqSize;
4849 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4850 pixelWidth = 2;
4851 else
4852 pixelWidth = (mode_info_list[i].info.BitsPerPixel +7) / 8;
4853 reqSize = mode_info_list[i].info.XResolution
4854 * mode_info_list[i].info.YResolution
4855 * pixelWidth;
4856 if (reqSize >= pData->vram_size)
4857 continue;
4858 *pCurMode = mode_info_list[i];
4859 pCurMode++;
4860 }
4861#endif /* VRAM_SIZE_FIX defined */
4862
4863 /*
4864 * Copy default modes with subtractred YResolution.
4865 */
4866 if (cyReduction)
4867 {
4868 ModeInfoListItem *pDefMode = mode_info_list;
4869 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
4870#ifndef VRAM_SIZE_FIX
4871 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
4872 {
4873 *pCurMode = *pDefMode;
4874 pCurMode->mode += 0x30;
4875 pCurMode->info.YResolution -= cyReduction;
4876 }
4877#else /* VRAM_SIZE_FIX defined */
4878 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
4879 {
4880 uint32_t pixelWidth, reqSize;
4881 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4882 pixelWidth = 2;
4883 else
4884 pixelWidth = (pDefMode->info.BitsPerPixel + 7) / 8;
4885 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
4886 if (reqSize >= pData->vram_size)
4887 continue;
4888 *pCurMode = *pDefMode;
4889 pCurMode->mode += 0x30;
4890 pCurMode->info.YResolution -= cyReduction;
4891 pCurMode++;
4892 }
4893#endif /* VRAM_SIZE_FIX defined */
4894 }
4895
4896
4897 /*
4898 * Add custom modes.
4899 */
4900 if (cCustomModes)
4901 {
4902 uint16_t u16CurMode = 0x160;
4903 for (i = 1; i <= cCustomModes; i++)
4904 {
4905 char szExtraDataKey[sizeof("CustomVideoModeXX")];
4906 char *pszExtraData = NULL;
4907
4908 /* query and decode the custom mode string. */
4909 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
4910 rc = CFGMR3QueryStringAlloc(pCfgHandle, szExtraDataKey, &pszExtraData);
4911 if (VBOX_SUCCESS(rc))
4912 {
4913 ModeInfoListItem *pDefMode = mode_info_list;
4914 unsigned int cx, cy, cBits, cParams;
4915 uint16_t u16DefMode;
4916
4917 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
4918 if ( cParams != 3
4919 || (cBits != 16 && cBits != 24 && cBits != 32))
4920 {
4921 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
4922 return VERR_VGA_INVALID_CUSTOM_MODE;
4923 }
4924#ifdef VRAM_SIZE_FIX
4925 if (cx * cy * cBits / 8 >= pData->vram_size)
4926 {
4927 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",
4928 cx, cy, cBits, pData->vram_size / _1M));
4929 return VERR_VGA_INVALID_CUSTOM_MODE;
4930 }
4931#endif /* VRAM_SIZE_FIX defined */
4932 MMR3HeapFree(pszExtraData);
4933
4934 /* Use defaults from max@bpp mode. */
4935 switch (cBits)
4936 {
4937 case 16:
4938 u16DefMode = VBE_VESA_MODE_1024X768X565;
4939 break;
4940
4941 case 24:
4942 u16DefMode = VBE_VESA_MODE_1024X768X888;
4943 break;
4944
4945 case 32:
4946 u16DefMode = VBE_OWN_MODE_1024X768X8888;
4947 break;
4948
4949 default: /* gcc, shut up! */
4950 AssertMsgFailed(("gone postal!\n"));
4951 continue;
4952 }
4953
4954 while ( pDefMode->mode != u16DefMode
4955 && pDefMode->mode != VBE_VESA_MODE_END_OF_LIST)
4956 pDefMode++;
4957 Assert(pDefMode->mode != VBE_VESA_MODE_END_OF_LIST);
4958
4959 *pCurMode = *pDefMode;
4960 pCurMode->mode = u16CurMode++;
4961
4962 /* adjust defaults */
4963 pCurMode->info.XResolution = cx;
4964 pCurMode->info.YResolution = cy;
4965
4966 switch (cBits)
4967 {
4968 case 16:
4969 pCurMode->info.BytesPerScanLine = cx * 2;
4970 pCurMode->info.LinBytesPerScanLine = cx * 2;
4971 break;
4972
4973 case 24:
4974 pCurMode->info.BytesPerScanLine = cx * 3;
4975 pCurMode->info.LinBytesPerScanLine = cx * 3;
4976 break;
4977
4978 case 32:
4979 pCurMode->info.BytesPerScanLine = cx * 4;
4980 pCurMode->info.LinBytesPerScanLine = cx * 4;
4981 break;
4982 }
4983
4984 /* commit it */
4985 pCurMode++;
4986 }
4987 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
4988 {
4989 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Vrc\n", szExtraDataKey, rc));
4990 return rc;
4991 }
4992 } /* foreach custom mode key */
4993 }
4994
4995 /*
4996 * Add the "End of list" mode.
4997 */
4998 memset(pCurMode, 0, sizeof(*pCurMode));
4999 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
5000
5001 /*
5002 * Register I/O Port for the VBE BIOS Extra Data.
5003 */
5004 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
5005 if (VBOX_FAILURE(rc))
5006 return rc;
5007#endif
5008
5009 /*
5010 * Statistics.
5011 */
5012 STAM_REG(pVM, &pData->StatGCMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
5013 STAM_REG(pVM, &pData->StatGCMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
5014 STAM_REG(pVM, &pData->StatGCIOPortRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortRead() body.");
5015 STAM_REG(pVM, &pData->StatGCIOPortWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortWrite() body.");
5016
5017 return VINF_SUCCESS;
5018}
5019
5020
5021/**
5022 * Destruct a device instance.
5023 *
5024 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5025 * resources can be freed correctly.
5026 *
5027 * @param pDevIns The device instance data.
5028 */
5029static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5030{
5031#ifdef VBE_NEW_DYN_LIST
5032 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
5033 LogFlow(("vgaR3Destruct:\n"));
5034
5035 /*
5036 * Free MM heap pointers.
5037 */
5038 if (pData->pu8VBEExtraData)
5039 {
5040 MMR3HeapFree(pData->pu8VBEExtraData);
5041 pData->pu8VBEExtraData = NULL;
5042 }
5043#endif
5044
5045#if 0 /** @todo r=bird: We can't free the buffer here because it's still locked.
5046 * (That's the reason why we didn't do it earlier.) */
5047 /*
5048 * Free the VRAM.
5049 */
5050 int rc = SUPPageFree(pData->vram_ptrHC, pData->vram_size >> PAGE_SHIFT);
5051 if (VBOX_FAILURE(rc))
5052 {
5053 AssertMsgFailed(("SUPPageFree(%p, %#x) -> %d\n", pData->vram_ptrHC, pData->vram_size, rc));
5054 return rc;
5055 }
5056 pData->vram_ptrHC = NULL;
5057#endif
5058
5059 return VINF_SUCCESS;
5060}
5061
5062
5063/**
5064 * The device registration structure.
5065 */
5066const PDMDEVREG g_DeviceVga =
5067{
5068 /* u32Version */
5069 PDM_DEVREG_VERSION,
5070 /* szDeviceName */
5071 "vga",
5072 /* szGCMod */
5073 "VBoxDDGC.gc",
5074 /* szR0Mod */
5075 "VBoxDDR0.r0",
5076 /* pszDescription */
5077 "VGA Adaptor with VESA extensions.",
5078 /* fFlags */
5079 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
5080 /* fClass */
5081 PDM_DEVREG_CLASS_GRAPHICS,
5082 /* cMaxInstances */
5083 1,
5084 /* cbInstance */
5085 sizeof(VGASTATE),
5086 /* pfnConstruct */
5087 vgaR3Construct,
5088 /* pfnDestruct */
5089 vgaR3Destruct,
5090 /* pfnRelocate */
5091 vgaR3Relocate,
5092 /* pfnIOCtl */
5093 NULL,
5094 /* pfnPowerOn */
5095 NULL,
5096 /* pfnReset */
5097 vgaR3Reset,
5098 /* pfnSuspend */
5099 NULL,
5100 /* pfnResume */
5101 NULL,
5102 /* pfnAttach */
5103 vgaAttach,
5104 /* pfnDetach */
5105 vgaDetach,
5106 /* pfnQueryInterface */
5107 NULL,
5108 /* pfnInitComplete */
5109 NULL
5110};
5111
5112#endif /* !IN_RING3 */
5113#endif /* VBOX */
5114#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5115
5116/*
5117 * Local Variables:
5118 * nuke-trailing-whitespace-p:nil
5119 * End:
5120 */
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