VirtualBox

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

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

Removed obsolete visible region methods.

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