VirtualBox

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

Last change on this file since 3155 was 3155, checked in by vboxsync, 18 years ago

Allow the VM to start with NULL framebuffer.

  • 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/**
3746 * Create a 32-bbp snapshot of the display.
3747 *
3748 * @param pInterface Pointer to this interface.
3749 * @param pvData Pointer the buffer to copy the bits to.
3750 * @param cbData Size of the buffer.
3751 * @param pcx Where to store the width of the bitmap. (optional)
3752 * @param pcy Where to store the height of the bitmap. (optional)
3753 * @param pcbData Where to store the actual size of the bitmap. (optional)
3754 * @see PDMIKEYBOARDPORT::pfnSnapshot() for details.
3755 */
3756static DECLCALLBACK(int) vgaPortSnapshot(PPDMIDISPLAYPORT pInterface, void *pvData, size_t cbData, uint32_t *pcx, uint32_t *pcy, size_t *pcbData)
3757{
3758 /* @todo r=sunlover: replace the method with a direct VRAM rendering like in vgaPortUpdateDisplayRect. */
3759 PPDMIDISPLAYCONNECTOR pConnector;
3760 PDMIDISPLAYCONNECTOR Connector;
3761 int32_t graphic_mode;
3762 uint32_t fRenderVRAM;
3763 size_t cbRequired;
3764 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3765 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3766 LogFlow(("vgaPortSnapshot: pvData=%p cbData=%d pcx=%p pcy=%p pcbData=%p\n", pvData, cbData, pcx, pcy, pcbData));
3767
3768 /*
3769 * Validate input.
3770 */
3771 if (!pvData)
3772 return VERR_INVALID_PARAMETER;
3773
3774 /*
3775 * Do a regular refresh first to resolve any pending resize issues.
3776 *
3777 * 20060317 It used to be pfnUpdateDisplay, but by VBVA design
3778 * only pfnUpdateDisplayAll is allowed to be called in VBVA mode.
3779 * Also since the goal here is to have updated display for screenshot,
3780 * the UpdateDisplayAll is even more logical to call. (sunlover)
3781 */
3782 pInterface->pfnUpdateDisplayAll(pInterface);
3783
3784 /*
3785 * Validate the buffer size.
3786 */
3787 cbRequired = RT_ALIGN_Z(pData->last_scr_width, 4) * pData->last_scr_height * 4;
3788 if (cbRequired > cbData)
3789 {
3790 Log(("vgaPortSnapshot: %d bytes are required, a buffer of %d bytes is profiled.\n", cbRequired, cbData));
3791 return VERR_BUFFER_OVERFLOW;
3792 }
3793
3794 /*
3795 * Temporarily replace the display connector interface with a fake one.
3796 */
3797 Connector.pu8Data = (uint8_t*)pvData;
3798 Connector.cBits = 32;
3799 Connector.cx = pData->pDrv->cx;
3800 Connector.cy = pData->pDrv->cy;
3801 Connector.cbScanline = RT_ALIGN_32(Connector.cx, 4) * 4;
3802 Connector.pfnRefresh = vgaDummyRefresh;
3803 Connector.pfnResize = vgaDummyResize;
3804 Connector.pfnUpdateRect = vgaDummyUpdateRect;
3805
3806 /* save & replace state data. */
3807 pConnector = pData->pDrv;
3808 pData->pDrv = &Connector;
3809 graphic_mode = pData->graphic_mode;
3810 pData->graphic_mode = -1; /* force a full refresh. */
3811 fRenderVRAM = pData->fRenderVRAM;
3812 pData->fRenderVRAM = 1; /* force the guest VRAM rendering to the given buffer. */
3813
3814 /* make the snapshot. */
3815 int rc = vga_update_display(pData);
3816
3817 /* restore */
3818 pData->pDrv = pConnector;
3819 pData->graphic_mode = graphic_mode;
3820 pData->fRenderVRAM = fRenderVRAM;
3821
3822 if (rc != VINF_SUCCESS)
3823 return rc;
3824
3825 /*
3826 * Return the result.
3827 */
3828 if (pcx)
3829 *pcx = Connector.cx;
3830 if (pcy)
3831 *pcy = Connector.cy;
3832 if (pcbData)
3833 *pcbData = cbRequired;
3834 LogFlow(("vgaPortSnapshot: returns VINF_SUCCESS (cx=%d cy=%d cbData=%d)\n", Connector.cx, Connector.cy, cbRequired));
3835 return VINF_SUCCESS;
3836}
3837
3838
3839/**
3840 * Copy bitmap to the display.
3841 *
3842 * @param pInterface Pointer to this interface.
3843 * @param pvData Pointer to the bitmap bits.
3844 * @param x The upper left corner x coordinate of the destination rectangle.
3845 * @param y The upper left corner y coordinate of the destination rectangle.
3846 * @param cx The width of the source and destination rectangles.
3847 * @param cy The height of the source and destination rectangles.
3848 * @see PDMIDISPLAYPORT::pfnDisplayBlt() for details.
3849 */
3850static DECLCALLBACK(int) vgaPortDisplayBlt(PPDMIDISPLAYPORT pInterface, const void *pvData, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)
3851{
3852 PVGASTATE pData = IDISPLAYPORT_2_VGASTATE(pInterface);
3853 int rc = VINF_SUCCESS;
3854 PDMDEV_ASSERT_EMT(VGASTATE2DEVINS(pData));
3855 LogFlow(("vgaPortDisplayBlt: pvData=%p x=%d y=%d cx=%d cy=%d\n", pvData, x, y, cx, cy));
3856
3857 /*
3858 * Validate input.
3859 */
3860 if ( pvData
3861 && x < pData->pDrv->cx
3862 && cx <= pData->pDrv->cx
3863 && cx + x <= pData->pDrv->cx
3864 && y < pData->pDrv->cy
3865 && cy <= pData->pDrv->cy
3866 && cy + y <= pData->pDrv->cy)
3867 {
3868 /*
3869 * Determin bytes per pixel in the destination buffer.
3870 */
3871 size_t cbPixelDst = 0;
3872 switch (pData->pDrv->cBits)
3873 {
3874 case 8:
3875 cbPixelDst = 1;
3876 break;
3877 case 15:
3878 case 16:
3879 cbPixelDst = 2;
3880 break;
3881 case 24:
3882 cbPixelDst = 3;
3883 break;
3884 case 32:
3885 cbPixelDst = 4;
3886 break;
3887 default:
3888 rc = VERR_INVALID_PARAMETER;
3889 break;
3890 }
3891 if (VBOX_SUCCESS(rc))
3892 {
3893 /*
3894 * The blitting loop.
3895 */
3896 size_t cbLineSrc = RT_ALIGN_Z(cx, 4) * 4;
3897 uint8_t *pu8Src = (uint8_t *)pvData;
3898 size_t cbLineDst = pData->pDrv->cbScanline;
3899 uint8_t *pu8Dst = pData->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
3900 uint32_t cyLeft = cy;
3901 vga_draw_line_func *pfnVgaDrawLine = vga_draw_line_table[VGA_DRAW_LINE32 * 4 + get_depth_index(pData->pDrv->cBits)];
3902 Assert(pfnVgaDrawLine);
3903 while (cyLeft-- > 0)
3904 {
3905 pfnVgaDrawLine(pData, pu8Dst, pu8Src, cx);
3906 pu8Dst += cbLineDst;
3907 pu8Src += cbLineSrc;
3908 }
3909
3910 /*
3911 * Invalidate the area.
3912 */
3913 pData->pDrv->pfnUpdateRect(pData->pDrv, x, y, cx, cy);
3914 }
3915 }
3916 else
3917 rc = VERR_INVALID_PARAMETER;
3918
3919 LogFlow(("vgaPortDisplayBlt: returns %Vrc\n", rc));
3920 return rc;
3921}
3922
3923static DECLCALLBACK(void) vgaPortUpdateDisplayRect (PPDMIDISPLAYPORT pInterface, int32_t x, int32_t y, uint32_t w, uint32_t h)
3924{
3925 uint32_t v;
3926 vga_draw_line_func *vga_draw_line;
3927
3928 uint32_t cbPixelDst;
3929 uint32_t cbLineDst;
3930 uint8_t *pu8Dst;
3931
3932 uint32_t cbPixelSrc;
3933 uint32_t cbLineSrc;
3934 uint8_t *pu8Src;
3935
3936 uint32_t u32OffsetSrc, u32Dummy;
3937
3938 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
3939
3940#ifdef DEBUG_sunlover
3941 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d\n", x, y, w, h));
3942#endif /* DEBUG_sunlover */
3943
3944 Assert(pInterface);
3945 Assert(s->pDrv);
3946 Assert(s->pDrv->pu8Data);
3947
3948 /* Check if there is something to do at all. */
3949 if (!s->fRenderVRAM)
3950 {
3951 /* The framebuffer uses the guest VRAM directly. */
3952#ifdef DEBUG_sunlover
3953 LogFlow(("vgaPortUpdateDisplayRect: nothing to do fRender is false.\n"));
3954#endif /* DEBUG_sunlover */
3955 return;
3956 }
3957
3958 /* Correct negative x and y coordinates. */
3959 if (x < 0)
3960 {
3961 x += w; /* Compute xRight which is also the new width. */
3962 w = (x < 0) ? 0 : x;
3963 x = 0;
3964 }
3965
3966 if (y < 0)
3967 {
3968 y += h; /* Compute yBottom, which is also the new height. */
3969 h = (y < 0) ? 0 : y;
3970 y = 0;
3971 }
3972
3973 /* Also check if coords are greater than the display resolution. */
3974 if (x + w > s->pDrv->cx)
3975 {
3976#ifndef VBOX
3977 w = s->pDrv->cx > x? s->pDrv->cx - x: 0;
3978#else
3979 // x < 0 is not possible here
3980 w = s->pDrv->cx > (uint32_t)x? s->pDrv->cx - x: 0;
3981#endif
3982 }
3983
3984 if (y + h > s->pDrv->cy)
3985 {
3986#ifndef VBOX
3987 h = s->pDrv->cy > y? s->pDrv->cy - y: 0;
3988#else
3989 // y < 0 is not possible here
3990 h = s->pDrv->cy > (uint32_t)y? s->pDrv->cy - y: 0;
3991#endif
3992 }
3993
3994#ifdef DEBUG_sunlover
3995 LogFlow(("vgaPortUpdateDisplayRect: %d,%d %dx%d (corrected coords)\n", x, y, w, h));
3996#endif /* DEBUG_sunlover */
3997
3998 /* Check if there is something to do at all. */
3999 if (w == 0 || h == 0)
4000 {
4001 /* Empty rectangle. */
4002#ifdef DEBUG_sunlover
4003 LogFlow(("vgaPortUpdateDisplayRect: nothing to do: %dx%d\n", w, h));
4004#endif /* DEBUG_sunlover */
4005 return;
4006 }
4007
4008 /** @todo This method should be made universal and not only for VBVA.
4009 * VGA_DRAW_LINE* must be selected and src/dst address calculation
4010 * changed.
4011 */
4012
4013 /* Choose the rendering function. */
4014 switch(s->get_bpp(s))
4015 {
4016 default:
4017 case 0:
4018 AssertMsgFailed(("Unsupported BPP %d\n", s->get_bpp (s)));
4019 return;
4020 case 8:
4021 v = VGA_DRAW_LINE8;
4022 break;
4023 case 15:
4024 v = VGA_DRAW_LINE15;
4025 break;
4026 case 16:
4027 v = VGA_DRAW_LINE16;
4028 break;
4029 case 24:
4030 v = VGA_DRAW_LINE24;
4031 break;
4032 case 32:
4033 v = VGA_DRAW_LINE32;
4034 break;
4035 }
4036
4037 vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->pDrv->cBits)];
4038
4039 /* Compute source and destination addresses and pitches. */
4040 cbPixelDst = (s->pDrv->cBits + 7) / 8;
4041 cbLineDst = s->pDrv->cbScanline;
4042 pu8Dst = s->pDrv->pu8Data + y * cbLineDst + x * cbPixelDst;
4043
4044 cbPixelSrc = (s->get_bpp(s) + 7) / 8;
4045 s->get_offsets (s, &cbLineSrc, &u32OffsetSrc, &u32Dummy);
4046
4047 /* Assume that rendering is performed only on visible part of VRAM.
4048 * This is true because coordinates were verified.
4049 */
4050 pu8Src = s->vram_ptrHC;
4051 pu8Src += u32OffsetSrc + y * cbLineSrc + x * cbPixelSrc;
4052
4053 /* Render VRAM to framebuffer. */
4054
4055#ifdef DEBUG_sunlover
4056 LogFlow(("vgaPortUpdateDisplayRect: dst: %p, %d, %d. src: %p, %d, %d\n", pu8Dst, cbLineDst, cbPixelDst, pu8Src, cbLineSrc, cbPixelSrc));
4057#endif /* DEBUG_sunlover */
4058
4059 while (h-- > 0)
4060 {
4061 vga_draw_line (s, pu8Dst, pu8Src, w);
4062 pu8Dst += cbLineDst;
4063 pu8Src += cbLineSrc;
4064 }
4065
4066#ifdef DEBUG_sunlover
4067 LogFlow(("vgaPortUpdateDisplayRect: completed.\n"));
4068#endif /* DEBUG_sunlover */
4069}
4070
4071static DECLCALLBACK(void) vgaPortSetRenderVRAM(PPDMIDISPLAYPORT pInterface, bool fRender)
4072{
4073 PVGASTATE s = IDISPLAYPORT_2_VGASTATE(pInterface);
4074
4075 LogFlow(("vgaPortSetRenderVRAM: fRender = %d\n", fRender));
4076
4077 s->fRenderVRAM = fRender;
4078}
4079
4080
4081static DECLCALLBACK(void) vgaTimerRefresh(PPDMDEVINS pDevIns, PTMTIMER pTimer)
4082{
4083 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4084 if (pData->pDrv)
4085 pData->pDrv->pfnRefresh(pData->pDrv);
4086 if (pData->cMilliesRefreshInterval)
4087 TMTimerSetMillies(pTimer, pData->cMilliesRefreshInterval);
4088}
4089
4090
4091/* -=-=-=-=-=- Ring 3: PCI Device -=-=-=-=-=- */
4092
4093/**
4094 * Callback function for mapping an PCI I/O region.
4095 *
4096 * @return VBox status code.
4097 * @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
4098 * @param iRegion The region number.
4099 * @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
4100 * I/O port, else it's a physical address.
4101 * This address is *NOT* relative to pci_mem_base like earlier!
4102 * @param enmType One of the PCI_ADDRESS_SPACE_* values.
4103 */
4104static DECLCALLBACK(int) vgaR3IORegionMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
4105{
4106 int rc;
4107 PVGASTATE pData = PDMINS2DATA(pPciDev->pDevIns, PVGASTATE);
4108 LogFlow(("vgaR3IORegionMap: iRegion=%d GCPhysAddress=%VGp cb=%#x enmType=%d\n", iRegion, GCPhysAddress, cb, enmType));
4109
4110 /*
4111 * VRam mapping.
4112 */
4113 if (iRegion == 0 && enmType == PCI_ADDRESS_SPACE_MEM_PREFETCH)
4114 {
4115 /*
4116 * Register and lock the VRAM.
4117 *
4118 * Windows usually re-initializes the PCI devices, so we have to check whether the memory was
4119 * already registered before trying to do that all over again.
4120 */
4121 PVM pVM = PDMDevHlpGetVM(pPciDev->pDevIns);
4122 if (pData->GCPhysVRAM)
4123 {
4124 AssertMsg(pData->GCPhysVRAM == GCPhysAddress,
4125 ("The Guest OS relocated our LFB! old GCPhysVRAM=%VGp new GCPhysAddress=%VGp\n",
4126 pData->GCPhysVRAM, GCPhysAddress));
4127 rc = VINF_SUCCESS;
4128 }
4129 else
4130 {
4131 /*
4132 * Register and lock the VRAM.
4133 */
4134 rc = MMR3PhysRegister(pVM, pData->vram_ptrHC, GCPhysAddress, pData->vram_size, MM_RAM_FLAGS_MMIO2, "VRam");
4135 if (VBOX_SUCCESS(rc))
4136 {
4137 if (!pData->GCPhysVRAM)
4138 rc = PGMR3HandlerPhysicalRegister(pVM, PGMPHYSHANDLERTYPE_PHYSICAL_WRITE,
4139 GCPhysAddress, GCPhysAddress + (pData->vram_size - 1),
4140 vgaR3LFBAccessHandler, pData,
4141 g_DeviceVga.szR0Mod, "vgaR0LFBAccessHandler", pData->pDevInsHC->pvInstanceDataR0,
4142 g_DeviceVga.szGCMod, "vgaGCLFBAccessHandler", pData->pDevInsHC->pvInstanceDataGC,
4143 "VGA LFB");
4144 if (VBOX_SUCCESS(rc))
4145 {
4146 /*
4147 * Map the first 256KB of the VRAM into GC for GC VGA support.
4148 */
4149 RTGCPTR GCPtr;
4150 rc = MMR3HyperMapGCPhys(pVM, GCPhysAddress, VGA_MAPPING_SIZE, "VGA VRam", &GCPtr);
4151 if (VBOX_SUCCESS(rc))
4152 {
4153 MMR3HyperReserve(pVM, PAGE_SIZE, "fence", NULL);
4154
4155 pData->vram_ptrGC = GCPtr;
4156 pData->GCPhysVRAM = GCPhysAddress;
4157 return VINF_SUCCESS;
4158 }
4159 AssertMsgFailed(("MMR3HyperMapGCPhys failed, rc=%Vrc\n", rc));
4160 }
4161 else
4162 AssertMsgFailed(("Failed to register write handler for VRAM! rc=%Vrc\n", rc));
4163 }
4164 else
4165 AssertReleaseMsgFailed(("Failed to register VRAM! rc=%Vra\n", rc));
4166 }
4167 return rc;
4168 }
4169 else
4170 AssertReleaseMsgFailed(("Huh!?! iRegion=%d enmType=%d\n", iRegion, enmType));
4171 return VERR_INTERNAL_ERROR;
4172}
4173
4174
4175/* -=-=-=-=-=- Ring3: Misc Wrappers -=-=-=-=-=- */
4176
4177/**
4178 * Saves a state of the VGA device.
4179 *
4180 * @returns VBox status code.
4181 * @param pDevIns The device instance.
4182 * @param pSSMHandle The handle to save the state to.
4183 */
4184static DECLCALLBACK(int) vgaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
4185{
4186 vga_save(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE));
4187 return VINF_SUCCESS;
4188}
4189
4190
4191/**
4192 * Loads a saved VGA device state.
4193 *
4194 * @returns VBox status code.
4195 * @param pDevIns The device instance.
4196 * @param pSSMHandle The handle to the saved state.
4197 * @param u32Version The data unit version number.
4198 */
4199static DECLCALLBACK(int) vgaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
4200{
4201 if (vga_load(pSSMHandle, PDMINS2DATA(pDevIns, PVGASTATE), u32Version))
4202 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4203 return VINF_SUCCESS;
4204}
4205
4206
4207/* -=-=-=-=-=- Ring 3: Device callbacks -=-=-=-=-=- */
4208
4209/**
4210 * Reset notification.
4211 *
4212 * @returns VBox status.
4213 * @param pDevIns The device instance data.
4214 */
4215static DECLCALLBACK(void) vgaR3Reset(PPDMDEVINS pDevIns)
4216{
4217 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4218 char *pchStart;
4219 char *pchEnd;
4220 LogFlow(("vgaReset\n"));
4221
4222 /* Clear the VRAM ourselves. */
4223 if (pData->vram_ptrHC && pData->vram_size)
4224 {
4225#ifdef LOG_ENABLED /** @todo separate function. */
4226 /* First dump the textmode contents to the log; handy for capturing Windows blue screens. */
4227 uint8_t graphic_mode;
4228 VGAState *s = pData;
4229
4230 if (!(s->ar_index & 0x20)) {
4231 graphic_mode = GMODE_BLANK;
4232 } else {
4233 graphic_mode = s->gr[6] & 1;
4234 }
4235 switch(graphic_mode)
4236 case GMODE_TEXT:
4237 {
4238 int cw, height, width, cheight, cx_min, cx_max, cy, cx;
4239 int x_incr;
4240 uint8_t *s1, *src, ch, cattr;
4241 int line_offset;
4242 uint16_t ch_attr;
4243
4244 line_offset = s->line_offset;
4245 s1 = s->CTXSUFF(vram_ptr) + (s->start_addr * 4);
4246
4247 /* total width & height */
4248 cheight = (s->cr[9] & 0x1f) + 1;
4249 cw = 8;
4250 if (!(s->sr[1] & 0x01))
4251 cw = 9;
4252 if (s->sr[1] & 0x08)
4253 cw = 16; /* NOTE: no 18 pixel wide */
4254 x_incr = cw * ((s->pDrv->cBits + 7) >> 3);
4255 width = (s->cr[0x01] + 1);
4256 if (s->cr[0x06] == 100) {
4257 /* ugly hack for CGA 160x100x16 - explain me the logic */
4258 height = 100;
4259 } else {
4260 height = s->cr[0x12] |
4261 ((s->cr[0x07] & 0x02) << 7) |
4262 ((s->cr[0x07] & 0x40) << 3);
4263 height = (height + 1) / cheight;
4264 }
4265 if ((height * width) > CH_ATTR_SIZE) {
4266 /* better than nothing: exit if transient size is too big */
4267 break;
4268 }
4269 RTLogPrintf("VGA textmode BEGIN (%dx%d):\n\n", height, width);
4270 for(cy = 0; cy < height; cy++) {
4271 src = s1;
4272 cx_min = width;
4273 cx_max = -1;
4274 for(cx = 0; cx < width; cx++) {
4275 ch_attr = *(uint16_t *)src;
4276 if (cx < cx_min)
4277 cx_min = cx;
4278 if (cx > cx_max)
4279 cx_max = cx;
4280# ifdef WORDS_BIGENDIAN
4281 ch = ch_attr >> 8;
4282 cattr = ch_attr & 0xff;
4283# else
4284 ch = ch_attr & 0xff;
4285 cattr = ch_attr >> 8;
4286# endif
4287 RTLogPrintf("%c", ch);
4288
4289 src += 4;
4290 }
4291 if (cx_max != -1)
4292 RTLogPrintf("\n");
4293
4294 s1 += line_offset;
4295 }
4296 RTLogPrintf("VGA textmode END:\n\n");
4297 }
4298
4299#endif
4300 memset(pData->vram_ptrHC, 0, pData->vram_size);
4301 }
4302
4303 /*
4304 * Zero most of it.
4305 *
4306 * Unlike vga_reset we're leaving out a few members which believe must
4307 * remain unchanged....
4308 */
4309 /* 1st part. */
4310 pchStart = (char *)&pData->latch;
4311 pchEnd = (char *)&pData->invalidated_y_table;
4312 memset(pchStart, 0, pchEnd - pchStart);
4313
4314 /* 2nd part. */
4315 pchStart = (char *)&pData->last_palette;
4316 pchEnd = (char *)&pData->u32Marker;
4317 memset(pchStart, 0, pchEnd - pchStart);
4318
4319
4320 /*
4321 * Restore and re-init some bits.
4322 */
4323 pData->get_bpp = vga_get_bpp;
4324 pData->get_offsets = vga_get_offsets;
4325 pData->get_resolution = vga_get_resolution;
4326 pData->graphic_mode = -1; /* Force full update. */
4327#ifdef CONFIG_BOCHS_VBE
4328 pData->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
4329 pData->vbe_regs[VBE_DISPI_INDEX_CMONITORS] = pData->monitor_count;
4330 pData->vbe_bank_mask = ((pData->vram_size >> 16) - 1);
4331#endif /* CONFIG_BOCHS_VBE */
4332
4333 /*
4334 * Reset the LBF mapping.
4335 */
4336 pData->fLFBUpdated = false;
4337 if ( ( pData->fGCEnabled
4338 || pData->fR0Enabled)
4339 && pData->GCPhysVRAM)
4340 {
4341 int rc = PGMHandlerPhysicalReset(PDMDevHlpGetVM(pDevIns), pData->GCPhysVRAM);
4342 AssertRC(rc);
4343 }
4344
4345 /* notify port handler */
4346 if (pData->pDrv)
4347 pData->pDrv->pfnReset(pData->pDrv);
4348}
4349
4350
4351/**
4352 * Device relocation callback.
4353 *
4354 * @param pDevIns Pointer to the device instance.
4355 * @param offDelta The relocation delta relative to the old location.
4356 *
4357 * @see FNPDMDEVRELOCATE for details.
4358 */
4359static DECLCALLBACK(void) vgaR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
4360{
4361 if (offDelta)
4362 {
4363 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4364 LogFlow(("vgaRelocate: offDelta = %08X\n", offDelta));
4365
4366 pData->GCPtrLFBHandler += offDelta;
4367 pData->vram_ptrGC += offDelta;
4368 }
4369}
4370
4371
4372/**
4373 * Attach command.
4374 *
4375 * This is called to let the device attach to a driver for a specified LUN
4376 * during runtime. This is not called during VM construction, the device
4377 * constructor have to attach to all the available drivers.
4378 *
4379 * This is like plugging in the monitor after turning on the PC.
4380 *
4381 * @returns VBox status code.
4382 * @param pDevIns The device instance.
4383 * @param iLUN The logical unit which is being detached.
4384 */
4385static DECLCALLBACK(int) vgaAttach(PPDMDEVINS pDevIns, unsigned iLUN)
4386{
4387 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4388 switch (iLUN)
4389 {
4390 /* LUN #0: Display port. */
4391 case 0:
4392 {
4393 int rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pData->Base, &pData->pDrvBase, "Display Port");
4394 if (VBOX_SUCCESS(rc))
4395 {
4396 pData->pDrv = (PDMIDISPLAYCONNECTOR*)pData->pDrvBase->pfnQueryInterface(pData->pDrvBase, PDMINTERFACE_DISPLAY_CONNECTOR);
4397 if (pData->pDrv)
4398 {
4399 /* pData->pDrv->pu8Data can be NULL when there is no framebuffer. */
4400 if ( pData->pDrv->pfnRefresh
4401 && pData->pDrv->pfnResize
4402 && pData->pDrv->pfnUpdateRect)
4403 rc = VINF_SUCCESS;
4404 else
4405 {
4406 Assert(pData->pDrv->pfnRefresh);
4407 Assert(pData->pDrv->pfnResize);
4408 Assert(pData->pDrv->pfnUpdateRect);
4409 pData->pDrv = NULL;
4410 pData->pDrvBase = NULL;
4411 rc = VERR_INTERNAL_ERROR;
4412 }
4413 }
4414 else
4415 {
4416 AssertMsgFailed(("LUN #0 doesn't have a display connector interface! rc=%Vrc\n", rc));
4417 pData->pDrvBase = NULL;
4418 rc = VERR_PDM_MISSING_INTERFACE;
4419 }
4420 }
4421 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
4422 {
4423 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pDevReg->szDeviceName, pDevIns->iInstance));
4424 rc = VINF_SUCCESS;
4425 }
4426 else
4427 AssertMsgFailed(("Failed to attach LUN #0! rc=%Vrc\n", rc));
4428 return rc;
4429 }
4430
4431 default:
4432 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4433 return VERR_PDM_NO_SUCH_LUN;
4434 }
4435}
4436
4437
4438/**
4439 * Detach notification.
4440 *
4441 * This is called when a driver is detaching itself from a LUN of the device.
4442 * The device should adjust it's state to reflect this.
4443 *
4444 * This is like unplugging the monitor while the PC is still running.
4445 *
4446 * @param pDevIns The device instance.
4447 * @param iLUN The logical unit which is being detached.
4448 */
4449static DECLCALLBACK(void) vgaDetach(PPDMDEVINS pDevIns, unsigned iLUN)
4450{
4451 /*
4452 * Reset the interfaces and update the controller state.
4453 */
4454 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4455 switch (iLUN)
4456 {
4457 /* LUN #0: Display port. */
4458 case 0:
4459 pData->pDrv = NULL;
4460 pData->pDrvBase = NULL;
4461 break;
4462
4463 default:
4464 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
4465 break;
4466 }
4467}
4468
4469
4470
4471/**
4472 * Construct a VGA device instance for a VM.
4473 *
4474 * @returns VBox status.
4475 * @param pDevIns The device instance data.
4476 * If the registration structure is needed, pDevIns->pDevReg points to it.
4477 * @param iInstance Instance number. Use this to figure out which registers and such to use.
4478 * The device number is also found in pDevIns->iInstance, but since it's
4479 * likely to be freqently used PDM passes it as parameter.
4480 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
4481 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
4482 * iInstance it's expected to be used a bit in this function.
4483 */
4484static DECLCALLBACK(int) vgaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
4485{
4486 static bool fExpandDone = false;
4487 bool f;
4488 int rc;
4489 unsigned i;
4490 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
4491 PVM pVM = PDMDevHlpGetVM(pDevIns);
4492#ifdef VBE_NEW_DYN_LIST
4493 uint32_t cCustomModes;
4494 uint32_t cyReduction;
4495 PVBEHEADER pVBEDataHdr;
4496 ModeInfoListItem *pCurMode;
4497 unsigned cb;
4498#endif
4499 Assert(iInstance == 0);
4500 Assert(pVM);
4501
4502 /*
4503 * Init static data.
4504 */
4505 if (!fExpandDone)
4506 {
4507 fExpandDone = true;
4508 vga_init_expand();
4509 }
4510
4511 /*
4512 * Validate configuration.
4513 */
4514 if (!CFGMR3AreValuesValid(pCfgHandle, "VRamSize\0"
4515 "MonitorCount\0"
4516 "GCEnabled\0"
4517 "R0Enabled\0"
4518 "CustomVideoModes\0"
4519 "HeightReduction\0"
4520 "CustomVideoMode1\0"
4521 "CustomVideoMode2\0"
4522 "CustomVideoMode3\0"
4523 "CustomVideoMode4\0"
4524 "CustomVideoMode5\0"
4525 "CustomVideoMode6\0"
4526 "CustomVideoMode7\0"
4527 "CustomVideoMode8\0"
4528 "CustomVideoMode9\0"
4529 "CustomVideoMode10\0"
4530 "CustomVideoMode11\0"
4531 "CustomVideoMode12\0"
4532 "CustomVideoMode13\0"
4533 "CustomVideoMode14\0"
4534 "CustomVideoMode15\0"
4535 "CustomVideoMode16\0"))
4536 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
4537 N_("Invalid configuration for vga device"));
4538
4539 /*
4540 * Init state data.
4541 */
4542 rc = CFGMR3QueryU32(pCfgHandle, "VRamSize", &pData->vram_size);
4543 if (VBOX_FAILURE(rc) || !pData->vram_size)
4544 pData->vram_size = VGA_VRAM_DEFAULT;
4545 else if (pData->vram_size > VGA_VRAM_MAX)
4546 {
4547 AssertMsgFailed(("vram_size=%d max=%d\n", pData->vram_size, VGA_VRAM_MAX));
4548 pData->vram_size = VGA_VRAM_MAX;
4549 }
4550 else if (pData->vram_size < VGA_VRAM_MIN)
4551 {
4552 AssertMsgFailed(("vram_size=%d min=%d\n", pData->vram_size, VGA_VRAM_MIN));
4553 pData->vram_size = RT_ALIGN_32(pData->vram_size, _1M);
4554 }
4555 Log(("VGA: VRamSize=%#x\n", pData->vram_size));
4556
4557 rc = CFGMR3QueryU32(pCfgHandle, "MonitorCount", &pData->monitor_count);
4558 if (VBOX_FAILURE(rc) || !pData->monitor_count)
4559 pData->monitor_count = 1;
4560 else if (pData->monitor_count > VGA_MONITORS_MAX)
4561 {
4562 AssertMsgFailed(("monitor_count=%d max=%d\n", pData->monitor_count, VGA_MONITORS_MAX));
4563 pData->monitor_count = 1;
4564 }
4565 pData->vbe_regs[VBE_DISPI_INDEX_CMONITORS] = pData->monitor_count;
4566 Log(("VGA: MonitorCount=%d\n", pData->monitor_count));
4567
4568 pData->fGCEnabled = true;
4569 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &f);
4570 if (VBOX_SUCCESS(rc) && !f)
4571 pData->fGCEnabled = false;
4572 Log(("VGA: fGCEnabled=%d\n", pData->fGCEnabled));
4573
4574 pData->fR0Enabled = true;
4575 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &f);
4576 if (VBOX_SUCCESS(rc) && !f)
4577 pData->fR0Enabled = false;
4578 Log(("VGA: fR0Enabled=%d\n", pData->fR0Enabled));
4579
4580 pData->pDevInsHC = pDevIns;
4581
4582 vgaR3Reset(pDevIns);
4583
4584 /* The PCI devices configuration. */
4585 pData->Dev.config[0x00] = 0xee; /* PCI vendor, just a free bogus value */
4586 pData->Dev.config[0x01] = 0x80;
4587
4588 pData->Dev.config[0x02] = 0xef; /* Device ID */
4589 pData->Dev.config[0x03] = 0xbe;
4590
4591 pData->Dev.config[0x0a] = 0x00; /* VGA controller */
4592 pData->Dev.config[0x0b] = 0x03;
4593 pData->Dev.config[0x0e] = 0x00; /* header_type */
4594
4595 /* The LBF access handler - error handling is better here than in the map function. */
4596 rc = PDMR3GetSymbolGCLazy(pVM, pDevIns->pDevReg->szGCMod, "vgaGCLFBAccessHandler", &pData->GCPtrLFBHandler);
4597 if (VBOX_FAILURE(rc))
4598 {
4599 AssertReleaseMsgFailed(("PDMR3GetSymbolGC(, %s, \"vgaGCLFBAccessHandler\",) -> %Vrc\n", pDevIns->pDevReg->szGCMod, rc));
4600 return rc;
4601 }
4602
4603 /* the interfaces. */
4604 pData->Base.pfnQueryInterface = vgaPortQueryInterface;
4605
4606 pData->Port.pfnUpdateDisplay = vgaPortUpdateDisplay;
4607 pData->Port.pfnUpdateDisplayAll = vgaPortUpdateDisplayAll;
4608 pData->Port.pfnQueryColorDepth = vgaPortQueryColorDepth;
4609 pData->Port.pfnSetRefreshRate = vgaPortSetRefreshRate;
4610 pData->Port.pfnSnapshot = vgaPortSnapshot;
4611 pData->Port.pfnDisplayBlt = vgaPortDisplayBlt;
4612 pData->Port.pfnUpdateDisplayRect= vgaPortUpdateDisplayRect;
4613 pData->Port.pfnSetRenderVRAM = vgaPortSetRenderVRAM;
4614
4615
4616 /*
4617 * Register I/O ports, ROM and save state.
4618 */
4619 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3c0, 16, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3c0");
4620 if (VBOX_FAILURE(rc))
4621 return rc;
4622 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3b4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3b4");
4623 if (VBOX_FAILURE(rc))
4624 return rc;
4625 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3ba, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3ba");
4626 if (VBOX_FAILURE(rc))
4627 return rc;
4628 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3d4, 2, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3d4");
4629 if (VBOX_FAILURE(rc))
4630 return rc;
4631 rc = PDMDevHlpIOPortRegister(pDevIns, 0x3da, 1, NULL, vgaIOPortWrite, vgaIOPortRead, NULL, NULL, "VGA - 3da");
4632 if (VBOX_FAILURE(rc))
4633 return rc;
4634
4635#ifdef CONFIG_BOCHS_VBE
4636 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1ce, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, NULL, NULL, "VGA/VBE - Index");
4637 if (VBOX_FAILURE(rc))
4638 return rc;
4639 rc = PDMDevHlpIOPortRegister(pDevIns, 0x1cf, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, NULL, NULL, "VGA/VBE - Data");
4640 if (VBOX_FAILURE(rc))
4641 return rc;
4642#if 0
4643 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4644 and tries to map other devices there */
4645 /* Old Bochs. */
4646 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff80, 1, NULL, vgaIOPortWriteVBEIndex, vgaIOPortReadVBEIndex, "VGA/VBE - Index Old");
4647 if (VBOX_FAILURE(rc))
4648 return rc;
4649 rc = PDMDevHlpIOPortRegister(pDevIns, 0xff81, 1, NULL, vgaIOPortWriteVBEData, vgaIOPortReadVBEData, "VGA/VBE - Data Old");
4650 if (VBOX_FAILURE(rc))
4651 return rc;
4652#endif
4653#endif /* CONFIG_BOCHS_VBE */
4654
4655 /* guest context extension */
4656 if (pData->fGCEnabled)
4657 {
4658 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4659 if (VBOX_FAILURE(rc))
4660 return rc;
4661 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4662 if (VBOX_FAILURE(rc))
4663 return rc;
4664 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4665 if (VBOX_FAILURE(rc))
4666 return rc;
4667 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4668 if (VBOX_FAILURE(rc))
4669 return rc;
4670 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4671 if (VBOX_FAILURE(rc))
4672 return rc;
4673#ifdef CONFIG_BOCHS_VBE
4674 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4675 if (VBOX_FAILURE(rc))
4676 return rc;
4677 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4678 if (VBOX_FAILURE(rc))
4679 return rc;
4680
4681#if 0
4682 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4683 and tries to map other devices there */
4684 /* Old Bochs. */
4685 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4686 if (VBOX_FAILURE(rc))
4687 return rc;
4688 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4689 if (VBOX_FAILURE(rc))
4690 return rc;
4691#endif
4692
4693#endif /* CONFIG_BOCHS_VBE */
4694 }
4695
4696 /* R0 context extension */
4697 if (pData->fR0Enabled)
4698 {
4699 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3c0, 16, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3c0 (GC)");
4700 if (VBOX_FAILURE(rc))
4701 return rc;
4702 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3b4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3b4 (GC)");
4703 if (VBOX_FAILURE(rc))
4704 return rc;
4705 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3ba, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3ba (GC)");
4706 if (VBOX_FAILURE(rc))
4707 return rc;
4708 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3d4, 2, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3d4 (GC)");
4709 if (VBOX_FAILURE(rc))
4710 return rc;
4711 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x3da, 1, 0, "vgaIOPortWrite", "vgaIOPortRead", NULL, NULL, "VGA - 3da (GC)");
4712 if (VBOX_FAILURE(rc))
4713 return rc;
4714#ifdef CONFIG_BOCHS_VBE
4715 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1ce, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", NULL, NULL, "VGA/VBE - Index (GC)");
4716 if (VBOX_FAILURE(rc))
4717 return rc;
4718 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0x1cf, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", NULL, NULL, "VGA/VBE - Data (GC)");
4719 if (VBOX_FAILURE(rc))
4720 return rc;
4721
4722#if 0
4723 /* This now causes conflicts with Win2k & XP; it is not aware this range is taken
4724 and tries to map other devices there */
4725 /* Old Bochs. */
4726 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff80, 1, 0, "vgaIOPortWriteVBEIndex", "vgaIOPortReadVBEIndex", "VGA/VBE - Index Old (GC)");
4727 if (VBOX_FAILURE(rc))
4728 return rc;
4729 rc = PDMDevHlpIOPortRegisterR0(pDevIns, 0xff81, 1, 0, "vgaIOPortWriteVBEData", "vgaIOPortReadVBEData", "VGA/VBE - Index Old (GC)");
4730 if (VBOX_FAILURE(rc))
4731 return rc;
4732#endif
4733
4734#endif /* CONFIG_BOCHS_VBE */
4735 }
4736
4737 /* vga mmio */
4738 rc = PDMDevHlpMMIORegister(pDevIns, 0x000a0000, 0x00020000, 0, vgaMMIOWrite, vgaMMIORead, vgaMMIOFill, "VGA - VGA Video Buffer");
4739 if (VBOX_FAILURE(rc))
4740 return rc;
4741 if (pData->fGCEnabled)
4742 {
4743 rc = PDMDevHlpMMIORegisterGC(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4744 if (VBOX_FAILURE(rc))
4745 return rc;
4746 }
4747 if (pData->fR0Enabled)
4748 {
4749 rc = PDMDevHlpMMIORegisterR0(pDevIns, 0x000a0000, 0x00020000, 0, "vgaMMIOWrite", "vgaMMIORead", "vgaMMIOFill", "VGA - VGA Video Buffer");
4750 if (VBOX_FAILURE(rc))
4751 return rc;
4752 }
4753
4754 /* vga bios */
4755 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_PRINTF_PORT, 1, NULL, vgaIOPortWriteBIOS, vgaIOPortReadBIOS, NULL, NULL, "VGA BIOS debug/panic");
4756 if (VBOX_FAILURE(rc))
4757 return rc;
4758 AssertReleaseMsg(g_cbVgaBiosBinary <= _64K && g_cbVgaBiosBinary >= 32*_1K, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4759 AssertReleaseMsg(RT_ALIGN_Z(g_cbVgaBiosBinary, PAGE_SIZE) == g_cbVgaBiosBinary, ("g_cbVgaBiosBinary=%#x\n", g_cbVgaBiosBinary));
4760 rc = PDMDevHlpROMRegister(pDevIns, 0x000c0000, g_cbVgaBiosBinary, &g_abVgaBiosBinary[0], "VGA BIOS");
4761 if (VBOX_FAILURE(rc))
4762 return rc;
4763
4764 /* save */
4765 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, 1 /* version */, sizeof(*pData),
4766 NULL, vgaR3SaveExec, NULL,
4767 NULL, vgaR3LoadExec, NULL);
4768 if (VBOX_FAILURE(rc))
4769 return rc;
4770
4771 /* PCI */
4772 rc = PDMDevHlpPCIRegister(pDevIns, &pData->Dev);
4773 if (VBOX_FAILURE(rc))
4774 return rc;
4775 /*AssertMsg(pData->Dev.devfn == 16 || iInstance != 0, ("pData->Dev.devfn=%d\n", pData->Dev.devfn));*/
4776 if (pData->Dev.devfn != 16 && iInstance == 0)
4777 Log(("!!WARNING!!: pData->dev.devfn=%d (ignore if testcase or no started by Main)\n", pData->Dev.devfn));
4778 rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, pData->vram_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, vgaR3IORegionMap);
4779 if (VBOX_FAILURE(rc))
4780 return rc;
4781
4782 /*
4783 * Create the refresh timer.
4784 */
4785 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, vgaTimerRefresh, "VGA Refresh Timer", &pData->RefreshTimer);
4786 if (VBOX_FAILURE(rc))
4787 return rc;
4788
4789 /*
4790 * Attach to the display.
4791 */
4792 rc = vgaAttach(pDevIns, 0 /* display LUN # */);
4793 if (VBOX_FAILURE(rc))
4794 return rc;
4795
4796 /*
4797 * Allocate the VRAM.
4798 */
4799 rc = SUPPageAlloc(pData->vram_size >> PAGE_SHIFT, (void **)&pData->vram_ptrHC);
4800 if (VBOX_FAILURE(rc))
4801 {
4802 AssertMsgFailed(("SUPPageAlloc(%#x,) -> %d\n", pData->vram_size, rc));
4803 return rc;
4804 }
4805
4806#ifdef VBE_NEW_DYN_LIST
4807 /*
4808 * Compute buffer size for the VBE BIOS Extra Data.
4809 */
4810 cb = sizeof(mode_info_list) + sizeof(ModeInfoListItem);
4811
4812 rc = CFGMR3QueryU32(pCfgHandle, "HeightReduction", &cyReduction);
4813 if (VBOX_SUCCESS(rc) && cyReduction)
4814 cb *= 2; /* Default mode list will be twice long */
4815 else
4816 cyReduction = 0;
4817
4818 rc = CFGMR3QueryU32(pCfgHandle, "CustomVideoModes", &cCustomModes);
4819 if (VBOX_SUCCESS(rc) && cCustomModes)
4820 cb += sizeof(ModeInfoListItem) * cCustomModes;
4821 else
4822 cCustomModes = 0;
4823
4824 /*
4825 * Allocate and initialize buffer for the VBE BIOS Extra Data.
4826 */
4827 pData->cbVBEExtraData = sizeof(VBEHEADER) + cb;
4828 pData->pu8VBEExtraData = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, pData->cbVBEExtraData);
4829 if (!pData->pu8VBEExtraData)
4830 return VERR_NO_MEMORY;
4831
4832 pVBEDataHdr = (PVBEHEADER)pData->pu8VBEExtraData;
4833 pVBEDataHdr->u16Signature = VBEHEADER_MAGIC;
4834 pVBEDataHdr->cbData = cb;
4835
4836#ifndef VRAM_SIZE_FIX
4837 pCurMode = memcpy(pVBEDataHdr + 1, &mode_info_list, sizeof(mode_info_list));
4838 pCurMode = (ModeInfoListItem *)((uintptr_t)pCurMode + sizeof(mode_info_list));
4839#else /* VRAM_SIZE_FIX defined */
4840 pCurMode = (ModeInfoListItem *)(pVBEDataHdr + 1);
4841 for (i = 0; i < MODE_INFO_SIZE; i++)
4842 {
4843 uint32_t pixelWidth, reqSize;
4844 if (mode_info_list[i].info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4845 pixelWidth = 2;
4846 else
4847 pixelWidth = mode_info_list[i].info.BitsPerPixel / 8;
4848 reqSize = mode_info_list[i].info.XResolution
4849 * mode_info_list[i].info.YResolution
4850 * pixelWidth;
4851 if (reqSize >= pData->vram_size)
4852 continue;
4853 *pCurMode = mode_info_list[i];
4854 pCurMode++;
4855 }
4856#endif /* VRAM_SIZE_FIX defined */
4857
4858 /*
4859 * Copy default modes with subtractred YResolution.
4860 */
4861 if (cyReduction)
4862 {
4863 ModeInfoListItem *pDefMode = mode_info_list;
4864 Log(("vgaR3Construct: cyReduction=%u\n", cyReduction));
4865#ifndef VRAM_SIZE_FIX
4866 for (i = 0; i < MODE_INFO_SIZE; i++, pCurMode++, pDefMode++)
4867 {
4868 *pCurMode = *pDefMode;
4869 pCurMode->mode += 0x30;
4870 pCurMode->info.YResolution -= cyReduction;
4871 }
4872#else /* VRAM_SIZE_FIX defined */
4873 for (i = 0; i < MODE_INFO_SIZE; i++, pDefMode++)
4874 {
4875 uint32_t pixelWidth, reqSize;
4876 if (pDefMode->info.MemoryModel == VBE_MEMORYMODEL_TEXT_MODE)
4877 pixelWidth = 2;
4878 else
4879 pixelWidth = pDefMode->info.BitsPerPixel / 8;
4880 reqSize = pDefMode->info.XResolution * pDefMode->info.YResolution * pixelWidth;
4881 if (reqSize >= pData->vram_size)
4882 continue;
4883 *pCurMode = *pDefMode;
4884 pCurMode->mode += 0x30;
4885 pCurMode->info.YResolution -= cyReduction;
4886 pCurMode++;
4887 }
4888#endif /* VRAM_SIZE_FIX defined */
4889 }
4890
4891
4892 /*
4893 * Add custom modes.
4894 */
4895 if (cCustomModes)
4896 {
4897 uint16_t u16CurMode = 0x160;
4898 for (i = 1; i <= cCustomModes; i++)
4899 {
4900 char szExtraDataKey[sizeof("CustomVideoModeXX")];
4901 char *pszExtraData = NULL;
4902
4903 /* query and decode the custom mode string. */
4904 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", i);
4905 rc = CFGMR3QueryStringAlloc(pCfgHandle, szExtraDataKey, &pszExtraData);
4906 if (VBOX_SUCCESS(rc))
4907 {
4908 ModeInfoListItem *pDefMode = mode_info_list;
4909 unsigned int cx, cy, cBits, cParams;
4910 uint16_t u16DefMode;
4911
4912 cParams = sscanf(pszExtraData, "%ux%ux%u", &cx, &cy, &cBits);
4913 if ( cParams != 3
4914 || (cBits != 16 && cBits != 24 && cBits != 32))
4915 {
4916 AssertMsgFailed(("Configuration error: Invalid mode data '%s' for '%s'! cBits=%d\n", pszExtraData, szExtraDataKey, cBits));
4917 return VERR_VGA_INVALID_CUSTOM_MODE;
4918 }
4919#ifdef VRAM_SIZE_FIX
4920 if (cx * cy * cBits / 8 >= pData->vram_size)
4921 {
4922 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",
4923 cx, cy, cBits, pData->vram_size / _1M));
4924 return VERR_VGA_INVALID_CUSTOM_MODE;
4925 }
4926#endif /* VRAM_SIZE_FIX defined */
4927 MMR3HeapFree(pszExtraData);
4928
4929 /* Use defaults from max@bpp mode. */
4930 switch (cBits)
4931 {
4932 case 16:
4933 u16DefMode = VBE_VESA_MODE_1024X768X565;
4934 break;
4935
4936 case 24:
4937 u16DefMode = VBE_VESA_MODE_1024X768X888;
4938 break;
4939
4940 case 32:
4941 u16DefMode = VBE_OWN_MODE_1024X768X8888;
4942 break;
4943
4944 default: /* gcc, shut up! */
4945 AssertMsgFailed(("gone postal!\n"));
4946 continue;
4947 }
4948
4949 while ( pDefMode->mode != u16DefMode
4950 && pDefMode->mode != VBE_VESA_MODE_END_OF_LIST)
4951 pDefMode++;
4952 Assert(pDefMode->mode != VBE_VESA_MODE_END_OF_LIST);
4953
4954 *pCurMode = *pDefMode;
4955 pCurMode->mode = u16CurMode++;
4956
4957 /* adjust defaults */
4958 pCurMode->info.XResolution = cx;
4959 pCurMode->info.YResolution = cy;
4960
4961 switch (cBits)
4962 {
4963 case 16:
4964 pCurMode->info.BytesPerScanLine = cx * 2;
4965 pCurMode->info.LinBytesPerScanLine = cx * 2;
4966 break;
4967
4968 case 24:
4969 pCurMode->info.BytesPerScanLine = cx * 3;
4970 pCurMode->info.LinBytesPerScanLine = cx * 3;
4971 break;
4972
4973 case 32:
4974 pCurMode->info.BytesPerScanLine = cx * 4;
4975 pCurMode->info.LinBytesPerScanLine = cx * 4;
4976 break;
4977 }
4978
4979 /* commit it */
4980 pCurMode++;
4981 }
4982 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
4983 {
4984 AssertMsgFailed(("CFGMR3QueryStringAlloc(,'%s',) -> %Vrc\n", szExtraDataKey, rc));
4985 return rc;
4986 }
4987 } /* foreach custom mode key */
4988 }
4989
4990 /*
4991 * Add the "End of list" mode.
4992 */
4993 memset(pCurMode, 0, sizeof(*pCurMode));
4994 pCurMode->mode = VBE_VESA_MODE_END_OF_LIST;
4995
4996 /*
4997 * Register I/O Port for the VBE BIOS Extra Data.
4998 */
4999 rc = PDMDevHlpIOPortRegister(pDevIns, VBE_EXTRA_PORT, 1, NULL, vbeIOPortWriteVBEExtra, vbeIOPortReadVBEExtra, NULL, NULL, "VBE BIOS Extra Data");
5000 if (VBOX_FAILURE(rc))
5001 return rc;
5002#endif
5003
5004 /*
5005 * Statistics.
5006 */
5007 STAM_REG(pVM, &pData->StatGCMemoryRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryRead() body.");
5008 STAM_REG(pVM, &pData->StatGCMemoryWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/Memory/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCMemoryWrite() body.");
5009 STAM_REG(pVM, &pData->StatGCIOPortRead, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Read", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortRead() body.");
5010 STAM_REG(pVM, &pData->StatGCIOPortWrite, STAMTYPE_PROFILE, "/Devices/VGA/GC/IOPort/Write", STAMUNIT_TICKS_PER_CALL, "Profiling of the VGAGCIOPortWrite() body.");
5011
5012 return VINF_SUCCESS;
5013}
5014
5015
5016/**
5017 * Destruct a device instance.
5018 *
5019 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
5020 * resources can be freed correctly.
5021 *
5022 * @param pDevIns The device instance data.
5023 */
5024static DECLCALLBACK(int) vgaR3Destruct(PPDMDEVINS pDevIns)
5025{
5026#ifdef VBE_NEW_DYN_LIST
5027 PVGASTATE pData = PDMINS2DATA(pDevIns, PVGASTATE);
5028 LogFlow(("vgaR3Destruct:\n"));
5029
5030 /*
5031 * Free MM heap pointers.
5032 */
5033 if (pData->pu8VBEExtraData)
5034 {
5035 MMR3HeapFree(pData->pu8VBEExtraData);
5036 pData->pu8VBEExtraData = NULL;
5037 }
5038#endif
5039
5040#if 0 /** @todo r=bird: We can't free the buffer here because it's still locked.
5041 * (That's the reason why we didn't do it earlier.) */
5042 /*
5043 * Free the VRAM.
5044 */
5045 int rc = SUPPageFree(pData->vram_ptrHC, pData->vram_size >> PAGE_SHIFT);
5046 if (VBOX_FAILURE(rc))
5047 {
5048 AssertMsgFailed(("SUPPageFree(%p, %#x) -> %d\n", pData->vram_ptrHC, pData->vram_size, rc));
5049 return rc;
5050 }
5051 pData->vram_ptrHC = NULL;
5052#endif
5053
5054 return VINF_SUCCESS;
5055}
5056
5057
5058/**
5059 * The device registration structure.
5060 */
5061const PDMDEVREG g_DeviceVga =
5062{
5063 /* u32Version */
5064 PDM_DEVREG_VERSION,
5065 /* szDeviceName */
5066 "vga",
5067 /* szGCMod */
5068 "VBoxDDGC.gc",
5069 /* szR0Mod */
5070 "VBoxDDR0.r0",
5071 /* pszDescription */
5072 "VGA Adaptor with VESA extensions.",
5073 /* fFlags */
5074 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
5075 /* fClass */
5076 PDM_DEVREG_CLASS_GRAPHICS,
5077 /* cMaxInstances */
5078 1,
5079 /* cbInstance */
5080 sizeof(VGASTATE),
5081 /* pfnConstruct */
5082 vgaR3Construct,
5083 /* pfnDestruct */
5084 vgaR3Destruct,
5085 /* pfnRelocate */
5086 vgaR3Relocate,
5087 /* pfnIOCtl */
5088 NULL,
5089 /* pfnPowerOn */
5090 NULL,
5091 /* pfnReset */
5092 vgaR3Reset,
5093 /* pfnSuspend */
5094 NULL,
5095 /* pfnResume */
5096 NULL,
5097 /* pfnAttach */
5098 vgaAttach,
5099 /* pfnDetach */
5100 vgaDetach,
5101 /* pfnQueryInterface */
5102 NULL,
5103 /* pfnInitComplete */
5104 NULL
5105};
5106
5107#endif /* !IN_RING3 */
5108#endif /* VBOX */
5109#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
5110
5111/*
5112 * Local Variables:
5113 * nuke-trailing-whitespace-p:nil
5114 * End:
5115 */
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