VirtualBox

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

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

Direct draw heap and miniport heap memory reservation for Windows guest additions.

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

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