VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevPcBios.cpp@ 2685

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

provide MPS table if IOAPIC is present, use the last 16 bytes of the EBDA for MPS floating pointer structure

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.4 KB
Line 
1/** @file
2 *
3 * VBox basic PC devices:
4 * PC BIOS device
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung 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* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEV_PC_BIOS
27#include <VBox/pdm.h>
28#include <VBox/mm.h>
29
30#include <VBox/log.h>
31#include <iprt/assert.h>
32#include <iprt/alloc.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <VBox/err.h>
36
37#include "vl_vbox.h"
38#include "Builtins.h"
39#include "Builtins2.h"
40#include "DevPcBios.h"
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46
47/**
48 * The boot device.
49 */
50typedef enum DEVPCBIOSBOOT
51{
52 DEVPCBIOSBOOT_NONE,
53 DEVPCBIOSBOOT_FLOPPY,
54 DEVPCBIOSBOOT_HD,
55 DEVPCBIOSBOOT_DVD,
56 DEVPCBIOSBOOT_LAN
57} DEVPCBIOSBOOT;
58
59/**
60 * PC Bios instance data structure.
61 */
62typedef struct DEVPCBIOS
63{
64 /** Pointer back to the device instance. */
65 PPDMDEVINS pDevIns;
66
67 /** Boot devices (ordered). */
68 DEVPCBIOSBOOT aenmBootDevice[4];
69 /** Ram Size (in bytes). */
70 uint64_t cbRam;
71 /** Bochs shutdown index. */
72 uint32_t iShutdown;
73 /** Floppy device. */
74 char *pszFDDevice;
75 /** Harddisk device. */
76 char *pszHDDevice;
77 /** Bios message buffer. */
78 char szMsg[256];
79 /** Bios message buffer index. */
80 uint32_t iMsg;
81 /** Current logo data memory bank. */
82 uint8_t u8LogoBank;
83 /** The size of the BIOS logo data. */
84 uint32_t cbLogo;
85 /** The BIOS logo data. */
86 uint8_t *pu8Logo;
87 /** The name of the logo file. */
88 char *pszLogoFile;
89 /** The LAN boot ROM data. */
90 uint8_t *pu8LanBoot;
91 /** The name of the LAN boot ROM file. */
92 char *pszLanBootFile;
93 /** The DMI tables. */
94 uint8_t au8DMIPage[0x1000];
95 /** The boot countdown (in seconds). */
96 uint8_t uBootDelay;
97 /** I/O-APIC enabled? */
98 uint8_t u8IOAPIC;
99} DEVPCBIOS, *PDEVPCBIOS;
100
101
102/** @todo The logo stuff shared with the BIOS goes into a header of course. */
103
104/**
105 * PC Bios logo data structure.
106 */
107#pragma pack(2) /* pack(2) is important! (seems that bios compiled with pack(2)...) */
108typedef struct LOGOHDR
109{
110 /** Signature (0x66BB). */
111 uint16_t u16Signature;
112 /** Fade in - boolean. */
113 uint8_t u8FadeIn;
114 /** Fade out - boolean. */
115 uint8_t u8FadeOut;
116 /** Logo time (msec). */
117 uint16_t u16LogoMillies;
118 /** Show setup - boolean. */
119 uint8_t u8ShowBootMenu;
120 /** Logo file size. */
121 uint32_t cbLogo;
122} LOGOHDR, *PLOGOHDR;
123#pragma pack()
124
125/** The value of the LOGOHDR::u16Signature field. */
126#define LOGOHDR_MAGIC 0x66BB
127
128/** Size of a logo bank. */
129#define LOGO_BANK_SIZE 0xffff
130/** Logo offset mask. */
131#define LOGO_BANK_OFFSET 0xffff
132/** The last bank for custom logo data. */
133#define LOGO_BANK_LAST 254
134/** The bank which will give you the default logo. */
135#define LOGO_BANK_DEFAULT_LOGO 255
136
137#pragma pack(1)
138
139/** DMI header */
140typedef struct DMIHDR
141{
142 uint8_t u8Type;
143 uint8_t u8Length;
144 uint16_t u16Handle;
145} *PDMIHDR;
146AssertCompileSize(DMIHDR, 4);
147
148/** DMI BIOS information */
149typedef struct DMIBIOSINF
150{
151 DMIHDR header;
152 uint8_t u8Vendor;
153 uint8_t u8Version;
154 uint16_t u16Start;
155 uint8_t u8Release;
156 uint8_t u8ROMSize;
157 uint64_t u64Characteristics;
158 uint8_t u8CharacteristicsByte1;
159 uint8_t u8CharacteristicsByte2;
160} *PDMIBIOSINF;
161AssertCompileSize(DMIBIOSINF, 0x14);
162
163/** DMI system information */
164typedef struct DMISYSTEMINF
165{
166 DMIHDR header;
167 uint8_t u8Manufacturer;
168 uint8_t u8ProductName;
169 uint8_t u8Version;
170 uint8_t u8SerialNumber;
171 uint8_t au8Uuid[16];
172 uint8_t u8WakeupType;
173 uint8_t u8SKUNumber;
174 uint8_t u8Family;
175} *PDMISYSTEMINF;
176AssertCompileSize(DMISYSTEMINF, 0x1b);
177
178/** MPS floating pointer structure */
179typedef struct MPSFLOATPTR
180{
181 uint8_t au8Signature[4];
182 uint32_t u32MPSAddr;
183 uint8_t u8Length;
184 uint8_t u8SpecRev;
185 uint8_t u8Checksum;
186 uint8_t au8Feature[5];
187} *PMPSFLOATPTR;
188AssertCompileSize(MPSFLOATPTR, 16);
189
190/** MPS config table header */
191typedef struct MPSCFGTBLHEADER
192{
193 uint8_t au8Signature[4];
194 uint16_t u16Length;
195 uint8_t u8SpecRev;
196 uint8_t u8Checksum;
197 uint8_t au8OemId[8];
198 uint8_t au8ProductId[12];
199 uint32_t u32OemTablePtr;
200 uint16_t u16OemTableSize;
201 uint16_t u16EntryCount;
202 uint32_t u32AddrLocalApic;
203 uint16_t u16ExtTableLength;
204 uint8_t u8ExtTableChecksxum;
205 uint8_t u8Reserved;
206} *PMPSCFGTBLHEADER;
207AssertCompileSize(MPSCFGTBLHEADER, 0x2c);
208
209/** MPS processor entry */
210typedef struct MPSPROCENTRY
211{
212 uint8_t u8EntryType;
213 uint8_t u8LocalApicId;
214 uint8_t u8LocalApicVersion;
215 uint8_t u8CPUFlags;
216 uint32_t u32CPUSignature;
217 uint32_t u32CPUFeatureFlags;
218 uint32_t u32Reserved[2];
219} *PMPSPROCENTRY;
220AssertCompileSize(MPSPROCENTRY, 20);
221
222/** MPS bus entry */
223typedef struct MPSBUSENTRY
224{
225 uint8_t u8EntryType;
226 uint8_t u8BusId;
227 uint8_t au8BusTypeStr[6];
228} *PMPSBUSENTRY;
229AssertCompileSize(MPSBUSENTRY, 8);
230
231/** MPS I/O-APIC entry */
232typedef struct MPSIOAPICENTRY
233{
234 uint8_t u8EntryType;
235 uint8_t u8Id;
236 uint8_t u8Version;
237 uint8_t u8Flags;
238 uint32_t u32Addr;
239} *PMPSIOAPICENTRY;
240AssertCompileSize(MPSIOAPICENTRY, 8);
241
242/** MPS I/O-Interrupt entry */
243typedef struct MPSIOINTERRUPTENTRY
244{
245 uint8_t u8EntryType;
246 uint8_t u8Type;
247 uint16_t u16Flags;
248 uint8_t u8SrcBusId;
249 uint8_t u8SrcBusIrq;
250 uint8_t u8DstIOAPICId;
251 uint8_t u8DstIOAPICInt;
252} *PMPSIOIRQENTRY;
253AssertCompileSize(MPSIOINTERRUPTENTRY, 8);
254
255#pragma pack()
256
257
258/*******************************************************************************
259* Internal Functions *
260*******************************************************************************/
261__BEGIN_DECLS
262
263static DECLCALLBACK(int) logoMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
264static DECLCALLBACK(int) logoMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
265
266__END_DECLS
267
268
269/**
270 * Write to CMOS memory.
271 * This is used by the init complete code.
272 */
273static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
274{
275 Assert(off < 128);
276 Assert(u32Val < 256);
277
278#if 1
279 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
280 AssertRC(rc);
281#else
282 PVM pVM = PDMDevHlpGetVM(pDevIns);
283 IOMIOPortWrite(pVM, 0x70, off, 1);
284 IOMIOPortWrite(pVM, 0x71, u32Val, 1);
285 IOMIOPortWrite(pVM, 0x70, 0, 1);
286#endif
287}
288
289/* -=-=-=-=-=-=- based on code from pc.c -=-=-=-=-=-=- */
290
291/**
292 * Initializes the CMOS data for one harddisk.
293 */
294static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PPDMIBLOCKBIOS pBlockBios)
295{
296 if ( pBlockBios->pfnGetType(pBlockBios) == PDMBLOCKTYPE_HARD_DISK
297 && pBlockBios->pfnIsVisible(pBlockBios))
298 {
299 uint32_t cCylinders;
300 uint32_t cHeads;
301 uint32_t cSectors;
302 int rc = pBlockBios->pfnGetGeometry(pBlockBios, &cCylinders, &cHeads, &cSectors);
303 if (VBOX_SUCCESS(rc))
304 {
305 Log2(("pcbiosCmosInitHardDisk: offInfo=%#x: CHS=%d/%d/%d\n", offInfo, cCylinders, cHeads, cSectors));
306 pcbiosCmosWrite(pDevIns, offType, 47); /* 19h - First Extended Hard Disk Drive Type */
307 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(cCylinders, 16383) & 0xff); /* 1Bh - (AMI) First Hard Disk (type 47) user defined: # of Cylinders, LSB */
308 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(cCylinders, 16383) >> 8); /* 1Ch - (AMI) First Hard Disk user defined: # of Cylinders, High Byte */
309 pcbiosCmosWrite(pDevIns, offInfo + 2, cHeads); /* 1Dh - (AMI) First Hard Disk user defined: Number of Heads */
310 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff); /* 1Eh - (AMI) First Hard Disk user defined: Write Precompensation Cylinder, Low Byte */
311 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff); /* 1Fh - (AMI) First Hard Disk user defined: Write Precompensation Cylinder, High Byte */
312 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xc0 | ((cHeads > 8) << 3)); /* 20h - (AMI) First Hard Disk user defined: Control Byte */
313 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff); /* 21h - (AMI) First Hard Disk user defined: Landing Zone, Low Byte */
314 pcbiosCmosWrite(pDevIns, offInfo + 7, 0xff); /* 22h - (AMI) First Hard Disk user defined: Landing Zone, High Byte */
315 pcbiosCmosWrite(pDevIns, offInfo + 8, cSectors); /* 23h - (AMI) First Hard Disk user defined: # of Sectors per track */
316 return;
317 }
318 }
319 pcbiosCmosWrite(pDevIns, offType, 0);
320}
321
322
323/**
324 * Get BIOS boot code from enmBootDevice in order
325 *
326 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
327 */
328static uint8_t getBiosBootCode(PDEVPCBIOS pData, unsigned iOrder)
329{
330 switch (pData->aenmBootDevice[iOrder])
331 {
332 case DEVPCBIOSBOOT_NONE:
333 return 0;
334 case DEVPCBIOSBOOT_FLOPPY:
335 return 1;
336 case DEVPCBIOSBOOT_HD:
337 return 2;
338 case DEVPCBIOSBOOT_DVD:
339 return 3;
340 case DEVPCBIOSBOOT_LAN:
341 return 4;
342 default:
343 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pData->aenmBootDevice[iOrder]));
344 return 0;
345 }
346}
347
348
349/**
350 * Init complete notification.
351 * This routine will write information needed by the bios to the CMOS.
352 *
353 * @returns VBOX status code.
354 * @param pDevIns The device instance.
355 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
356 * a description of standard and non-standard CMOS registers.
357 */
358static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
359{
360 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
361 uint32_t u32;
362 unsigned i;
363 PVM pVM = PDMDevHlpGetVM(pDevIns);
364 PPDMIBLOCKBIOS apHDs[4] = {0};
365 PPDMIBLOCKBIOS apFDs[2] = {0};
366 AssertRelease(pVM);
367 LogFlow(("pcbiosInitComplete:\n"));
368
369 /*
370 * Memory sizes.
371 */
372 /* base memory. */
373 u32 = pData->cbRam > 640 ? 640 : (uint32_t)pData->cbRam / _1K;
374 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
375 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
376
377 /* Extended memory, up to 65MB */
378 u32 = pData->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pData->cbRam - _1M) / _1K;
379 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
380 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
381 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
382 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
383
384 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB */
385 if (pData->cbRam > 16 * _1M)
386 {
387 u32 = (uint32_t)( (pData->cbRam - 16 * _1M) / _64K );
388 u32 = RT_MIN(u32, 0xffff);
389 }
390 else
391 u32 = 0;
392 pcbiosCmosWrite(pDevIns, 0x34, u32 & 0xff);
393 pcbiosCmosWrite(pDevIns, 0x35, u32 >> 8);
394
395 /*
396 * Bochs BIOS specifics - boot device.
397 * We do both new and old (ami-style) settings.
398 * See rombios.c line ~7215 (int19_function).
399 */
400
401 uint8_t reg3d = getBiosBootCode(pData, 0) | (getBiosBootCode(pData, 1) << 4);
402 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pData, 2) << 4;
403 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
404 uint8_t reg3c = getBiosBootCode(pData, 3) | (pData->uBootDelay << 4);
405 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
406 pcbiosCmosWrite(pDevIns, 0x38, reg38);
407 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
408
409 /*
410 * Floppy drive type.
411 */
412 for (i = 0; i < ELEMENTS(apFDs); i++)
413 {
414 PPDMIBASE pBase;
415 int rc = PDMR3QueryLun(pVM, pData->pszFDDevice, 0, i, &pBase);
416 if (VBOX_SUCCESS(rc))
417 apFDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
418 }
419 u32 = 0;
420 if (apFDs[0])
421 switch (apFDs[0]->pfnGetType(apFDs[0]))
422 {
423 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << 4; break;
424 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << 4; break;
425 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << 4; break;
426 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << 4; break;
427 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << 4; break;
428 default: AssertFailed(); break;
429 }
430 if (apFDs[1])
431 switch (apFDs[1]->pfnGetType(apFDs[1]))
432 {
433 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1; break;
434 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2; break;
435 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3; break;
436 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4; break;
437 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5; break;
438 default: AssertFailed(); break;
439 }
440 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
441
442 /*
443 * Equipment byte.
444 */
445 u32 = !!apFDs[0] + !!apFDs[1];
446 switch (u32)
447 {
448 case 1: u32 = 0x01; break; /* floppy installed, 1 drive. */
449 case 2: u32 = 0x41; break; /* floppy installed, 2 drives. */
450 default:u32 = 0; break; /* floppy not installed. */
451 }
452 u32 |= BIT(1); /* math coprocessor installed */
453 u32 |= BIT(2); /* keyboard enabled (or mouse?) */
454 u32 |= BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
455 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
456
457 /*
458 * Harddisks.
459 */
460 for (i = 0; i < ELEMENTS(apHDs); i++)
461 {
462 PPDMIBASE pBase;
463 int rc = PDMR3QueryLun(pVM, pData->pszHDDevice, 0, i, &pBase);
464 if (VBOX_SUCCESS(rc))
465 apHDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
466 }
467
468 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0); /* 0Fh means extended and points to 1Ah, 1Bh */
469 pcbiosCmosWrite(pDevIns, 0x12, u32); /* 12h - Hard Disk Data (type) */
470 if (apHDs[0])
471 pcbiosCmosInitHardDisk(pDevIns, 0x19, 0x1b, apHDs[0]); /* 19h - First Extended Hard Disk Drive Type */
472 if (apHDs[1])
473 pcbiosCmosInitHardDisk(pDevIns, 0x1a, 0x24, apHDs[1]); /* 1Ah - Second Extended Hard Disk Drive Type */
474
475 /*
476 * Translation type - Bochs BIOS specific.
477 */
478 u32 = 0;
479 for (i = 0; i < 4; i++)
480 {
481 if (apHDs[i])
482 {
483 PDMBIOSTRANSLATION enmTranslation;
484 int rc = apHDs[i]->pfnGetTranslation(apHDs[i], &enmTranslation);
485 if (VBOX_FAILURE(rc) || enmTranslation == PDMBIOSTRANSLATION_AUTO)
486 {
487 uint32_t cCylinders, cHeads, cSectors;
488 rc = apHDs[i]->pfnGetGeometry(apHDs[i], &cCylinders, &cHeads, &cSectors);
489 if (VBOX_FAILURE(rc))
490 {
491 AssertMsg(rc == VERR_PDM_MEDIA_NOT_MOUNTED, ("This shouldn't happen! rc=%Vrc\n", rc));
492 enmTranslation = PDMBIOSTRANSLATION_NONE;
493 }
494 else if (cCylinders <= 1024 && cHeads <= 16 && cSectors <= 63)
495 {
496 /* Disk <= 512 MByte not needing LBA translation. */
497 enmTranslation = PDMBIOSTRANSLATION_NONE;
498 }
499 else if (cSectors != 63 || (cHeads != 16 && cHeads != 32 && cHeads != 64 && cHeads != 128 && cHeads != 255))
500 {
501 /* Disk with strange geometry. Using LBA here can
502 * break booting of the guest OS. Especially operating
503 * systems from Microsoft are sensitive to BIOS CHS not
504 * matching what the partition table says. */
505 enmTranslation = PDMBIOSTRANSLATION_NONE;
506 }
507 else
508 enmTranslation = PDMBIOSTRANSLATION_LBA;
509 }
510 switch (enmTranslation)
511 {
512 case PDMBIOSTRANSLATION_AUTO: /* makes gcc happy */
513 case PDMBIOSTRANSLATION_NONE:
514 /* u32 |= 0 << (i * 2) */
515 break;
516 default:
517 AssertMsgFailed(("bad enmTranslation=%d\n", enmTranslation));
518 case PDMBIOSTRANSLATION_LBA:
519 u32 |= 1 << (i * 2);
520 break;
521 }
522 }
523 }
524 Log2(("pcbiosInitComplete: translation byte: %#02x\n", u32));
525 pcbiosCmosWrite(pDevIns, 0x39, u32);
526
527 LogFlow(("pcbiosInitComplete: returns VINF_SUCCESS\n"));
528 return VINF_SUCCESS;
529}
530
531
532/**
533 * Port I/O Handler for IN operations.
534 *
535 * @returns VBox status code.
536 *
537 * @param pDevIns The device instance.
538 * @param pvUser User argument - ignored.
539 * @param Port Port number used for the IN operation.
540 * @param pu32 Where to store the result.
541 * @param cb Number of bytes read.
542 */
543static DECLCALLBACK(int) pcbiosIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
544{
545 NOREF(pDevIns);
546 NOREF(pvUser);
547 NOREF(Port);
548 NOREF(pu32);
549 NOREF(cb);
550 return VERR_IOM_IOPORT_UNUSED;
551}
552
553
554/**
555 * Port I/O Handler for OUT operations.
556 *
557 * @returns VBox status code.
558 *
559 * @param pDevIns The device instance.
560 * @param pvUser User argument - ignored.
561 * @param Port Port number used for the IN operation.
562 * @param u32 The value to output.
563 * @param cb The value size in bytes.
564 */
565static DECLCALLBACK(int) pcbiosIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
566{
567 /*
568 * Bochs BIOS Panic
569 */
570 if ( cb == 2
571 && ( Port == 0x400
572 || Port == 0x401))
573 {
574 Log(("pcbios: PC BIOS panic at rombios.c(%d)\n", u32));
575 AssertReleaseMsgFailed(("PC BIOS panic at rombios.c(%d)\n", u32));
576 return VERR_INTERNAL_ERROR;
577 }
578
579 /*
580 * Bochs BIOS char printing.
581 */
582 if ( cb == 1
583 && ( Port == 0x402
584 || Port == 0x403))
585 {
586 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
587 /* The raw version. */
588 switch (u32)
589 {
590 case '\r': Log2(("pcbios: <return>\n")); break;
591 case '\n': Log2(("pcbios: <newline>\n")); break;
592 case '\t': Log2(("pcbios: <tab>\n")); break;
593 default: Log2(("pcbios: %c (%02x)\n", u32, u32)); break;
594 }
595
596 /* The readable, buffered version. */
597 if (u32 == '\n' || u32 == '\r')
598 {
599 pData->szMsg[pData->iMsg] = '\0';
600 if (pData->iMsg)
601 Log(("pcbios: %s\n", pData->szMsg));
602 pData->iMsg = 0;
603 }
604 else
605 {
606 if (pData->iMsg >= sizeof(pData->szMsg))
607 {
608 pData->szMsg[pData->iMsg] = '\0';
609 Log(("pcbios: %s\n", pData->szMsg));
610 pData->iMsg = 0;
611 }
612 pData->szMsg[pData->iMsg] = (char )u32;
613 pData->szMsg[++pData->iMsg] = '\0';
614 }
615 return VINF_SUCCESS;
616 }
617
618 /*
619 * Bochs BIOS shutdown request.
620 */
621 if (cb == 1 && Port == 0x8900)
622 {
623 static const unsigned char szShutdown[] = "Shutdown";
624 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
625 if (u32 == szShutdown[pData->iShutdown])
626 {
627 pData->iShutdown++;
628 if (pData->iShutdown == 8)
629 {
630 pData->iShutdown = 0;
631 LogRel(("8900h shutdown request.\n"));
632 return PDMDevHlpVMPowerOff(pDevIns);
633 }
634 }
635 else
636 pData->iShutdown = 0;
637 return VINF_SUCCESS;
638 }
639
640 /* not in use. */
641 return VINF_SUCCESS;
642}
643
644
645/**
646 * Legacy LOGO memory (0xd0000 - 0xdffff) read hook, to be called from IOM.
647 *
648 * @returns VBox status code.
649 * @param pDevIns Pointer device instance.
650 * @param pvUser User argument - ignored.
651 * @param GCPhysAddr Physical address of memory to read.
652 * @param pv Where to store readed data.
653 * @param cb Bytes to read.
654 */
655static DECLCALLBACK(int) logoMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
656{
657 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
658 Log(("logoMMIORead call GCPhysAddr:%x pv:%x cb:%d (%d)\n", GCPhysAddr, pv, cb, pData->u8LogoBank));
659
660 uint32_t offLogo = GCPhysAddr & LOGO_BANK_OFFSET;
661 if (pData->u8LogoBank != LOGO_BANK_DEFAULT_LOGO)
662 {
663 /*
664 * Banked logo.
665 */
666 offLogo += pData->u8LogoBank * LOGO_BANK_SIZE;
667 if ( offLogo > pData->cbLogo
668 || offLogo + cb > pData->cbLogo)
669 {
670 Log(("logoMMIORead: Requested address is out of Logo data!!! offLogo=%#x(%d) cbLogo=%#x(%d)\n",
671 offLogo, offLogo, pData->cbLogo, pData->cbLogo));
672 return VINF_SUCCESS;
673 }
674
675 memcpy(pv, &pData->pu8Logo[offLogo], cb);
676 Log(("logoMMIORead: offLogo=%#x(%d) cb=%#x %.*Vhxs\n", offLogo, offLogo, cb, cb, &pData->pu8Logo[offLogo]));
677 }
678 else
679 {
680 /*
681 * Default bios logo.
682 */
683 if (offLogo > g_cbPcDefBiosLogo || offLogo + cb > g_cbPcDefBiosLogo)
684 {
685 Log(("logoMMIORead: Requested address is out of Logo data!!! offLogo=%#x(%d) max:%#x(%d)\n",
686 offLogo, offLogo, g_cbPcDefBiosLogo, g_cbPcDefBiosLogo));
687 return VINF_SUCCESS;
688 }
689
690 memcpy(pv, &g_abPcDefBiosLogo[offLogo], cb);
691 Log(("logoMMIORead: offLogo=%#x(%d) cb=%#x %.*Vhxs\n", offLogo, offLogo, cb, cb, &g_abPcDefBiosLogo[offLogo]));
692 }
693
694 return VINF_SUCCESS;
695}
696
697
698/**
699 * Legacy LOGO memory (0xd0000 - 0xdffff) write hook, to be called from IOM.
700 *
701 * @returns VBox status code.
702 * @param pDevIns Pointer device instance.
703 * @param pvUser User argument - ignored.
704 * @param GCPhysAddr Physical address of memory to write.
705 * @param pv Pointer to data.
706 * @param cb Bytes to write.
707 */
708static DECLCALLBACK(int) logoMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
709{
710 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
711 Log(("logoMMIOWrite: GCPhysAddr=%x cb=%d pv[0]=%#04x (byte)\n", GCPhysAddr, pv, cb, *(uint8_t *)pv));
712
713 /*
714 * Byte write to off 0: Switch the logo bank.
715 */
716 if ((GCPhysAddr & LOGO_BANK_OFFSET) == 0 && cb == 1)
717 {
718 uint8_t u8Bank = *(uint8_t *)pv;
719 uint32_t off = u8Bank * LOGO_BANK_SIZE;
720
721 if ( u8Bank != LOGO_BANK_DEFAULT_LOGO
722 && off > pData->cbLogo)
723 {
724 Log(("logoMMIOWrite: The requested bank is outside the logo image! (cbLogo=%d off=%d)\n", pData->cbLogo, off));
725 return VINF_SUCCESS;
726 }
727
728 pData->u8LogoBank = u8Bank;
729 }
730
731 return VINF_SUCCESS;
732}
733
734/**
735 * Construct the DMI table.
736 *
737 * @param table pointer to DMI table.
738 */
739#define STRCPY(p, s) do { memcpy (p, s, sizeof(s)); p += sizeof(s); } while (0)
740static void pcbiosPlantDMITable(uint8_t *pTable)
741{
742 char *pszStr = (char*)pTable;
743 int iStrNr;
744
745 PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
746 pszStr = (char*)(pBIOSInf+1);
747 iStrNr = 1;
748 pBIOSInf->header.u8Type = 0; /* BIOS Information */
749 pBIOSInf->header.u8Length = sizeof(*pBIOSInf);
750 pBIOSInf->header.u16Handle = 0x0000;
751 pBIOSInf->u8Vendor = iStrNr++;
752 STRCPY(pszStr, "InnoTek Systemberatung GmbH");
753 pBIOSInf->u8Version = iStrNr++;
754 STRCPY(pszStr, "VirtualBox");
755 pBIOSInf->u16Start = 0xE000;
756 pBIOSInf->u8Release = iStrNr++;
757 STRCPY(pszStr, "12/01/2006");
758 pBIOSInf->u8ROMSize = 1; /* 128K */
759 pBIOSInf->u64Characteristics = BIT(4) /* ISA is supported */
760 | BIT(7) /* PCI is supported */
761 | BIT(15) /* Boot from CD is supported */
762 | BIT(16) /* Selectable Boot is supported */
763 | BIT(27) /* Int 9h, 8042 Keyboard services supported */
764 | BIT(30) /* Int 10h, CGA/Mono Video Services supported */
765 /* any more?? */
766 ;
767 pBIOSInf->u8CharacteristicsByte1 = BIT(0) /* ACPI is supported */
768 /* any more?? */
769 ;
770 pBIOSInf->u8CharacteristicsByte2 = 0
771 /* any more?? */
772 ;
773 *pszStr++ = '\0';
774
775
776 PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
777 pszStr = (char*)(pSystemInf+1);
778 iStrNr = 1;
779 pSystemInf->header.u8Type = 1; /* System Information */
780 pSystemInf->header.u8Length = sizeof(*pSystemInf);
781 pSystemInf->header.u16Handle = 0x0001;
782 pSystemInf->u8Manufacturer = iStrNr++;
783 STRCPY(pszStr, "InnoTek Systemberatung GmbH");
784 pSystemInf->u8ProductName = iStrNr++;
785 STRCPY(pszStr, "VirtualBox");
786 pSystemInf->u8Version = iStrNr++;
787 STRCPY(pszStr, "1.2");
788 pSystemInf->u8SerialNumber = iStrNr++;
789 STRCPY(pszStr, "0");
790 pSystemInf->u8WakeupType = 6; /* Power Switch */
791 pSystemInf->u8SKUNumber = 0;
792 pSystemInf->u8Family = iStrNr++;
793 STRCPY(pszStr, "Virtual Machine");
794 *pszStr++ = '\0';
795
796 AssertMsg(pszStr - (char*)pTable == VBOX_DMI_TABLE_SIZE,
797 ("VBOX_DMI_TABLE_SIZE=%d, actual DMI table size is %d",
798 VBOX_DMI_TABLE_SIZE, pszStr - (char*)pTable));
799}
800AssertCompile(VBOX_DMI_TABLE_ENTR == 2);
801
802
803/**
804 * Calculate a simple checksum for the MPS table.
805 *
806 * @param data data
807 * @param len size of data
808 */
809static uint8_t pcbiosChecksum(const uint8_t * const au8Data, uint32_t u32Length)
810{
811 uint8_t u8Sum = 0;
812 for (size_t i = 0; i < u32Length; ++i)
813 u8Sum += au8Data[i];
814 return -u8Sum;
815}
816
817/**
818 * Construct the MPS table. Only applicable if IOAPIC is active.
819 *
820 * @param pDevIns The device instance data.
821 * @param addr physical address in guest memory.
822 */
823static void pcbiosPlantMPStable(PPDMDEVINS pDevIns, uint8_t *pTable)
824{
825 /* configuration table */
826 PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
827 memcpy(pCfgTab->au8Signature, "PCMP", 4);
828 pCfgTab->u8SpecRev = 4; /* 1.4 */
829 memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
830 memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
831 pCfgTab->u32OemTablePtr = 0;
832 pCfgTab->u16OemTableSize = 0;
833 pCfgTab->u16EntryCount = 1 /* Processor */
834 + 1 /* ISA Bus */
835 + 1 /* I/O-APIC */
836 + 16 /* Interrupts */;
837 pCfgTab->u32AddrLocalApic = 0xfee00000;
838 pCfgTab->u16ExtTableLength = 0;
839 pCfgTab->u8ExtTableChecksxum = 0;
840 pCfgTab->u8Reserved = 0;
841
842 uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
843 uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
844 uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
845 PDMDevHlpQueryCPUId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
846 if (u32Eax >= 1)
847 {
848 PDMDevHlpQueryCPUId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
849 u32CPUSignature = u32Eax & 0xfff;
850 /* Local APIC will be enabled later so override it here. Since we provide
851 * an MP table we have an IOAPIC and therefore a Local APIC. */
852 u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
853 }
854
855 /* one processor so far */
856 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
857 pProcEntry->u8EntryType = 0; /* processor entry */
858 pProcEntry->u8LocalApicId = 0;
859 pProcEntry->u8LocalApicVersion = 0x11;
860 pProcEntry->u8CPUFlags = 2 /* bootstrap processor */ | 1 /* enabled */;
861 pProcEntry->u32CPUSignature = u32CPUSignature;
862 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
863 pProcEntry->u32Reserved[0] =
864 pProcEntry->u32Reserved[1] = 0;
865
866 /* ISA bus */
867 PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)(pProcEntry+1);
868 pBusEntry->u8EntryType = 1; /* bus entry */
869 pBusEntry->u8BusId = 0; /* this ID is referenced by the interrupt entries */
870 memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
871
872 /* PCI bus? */
873
874 /* I/O-APIC.
875 * MP spec: "The configuration table contains one or more entries for I/O APICs.
876 * ... At least one I/O APIC must be enabled." */
877 PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1);
878 pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
879 pIOAPICEntry->u8Id = 1; /* this ID is referenced by the interrupt entries */
880 pIOAPICEntry->u8Version = 0x11;
881 pIOAPICEntry->u8Flags = 1 /* enable */;
882 pIOAPICEntry->u32Addr = 0xfec00000;
883
884 PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
885 for (int i=0; i<16; i++, pIrqEntry++)
886 {
887 pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
888 pIrqEntry->u8Type = 0; /* INT, vectored interrupt */
889 pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
890 trigger mode = conforms to bus */
891 pIrqEntry->u8SrcBusId = 0; /* ISA bus */
892 pIrqEntry->u8SrcBusIrq = i;
893 pIrqEntry->u8DstIOAPICId = 1;
894 pIrqEntry->u8DstIOAPICInt = i;
895 }
896
897 pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
898 pCfgTab->u8Checksum = pcbiosChecksum(pTable, pCfgTab->u16Length);
899
900 AssertMsg(pCfgTab->u16Length < 0x1000 - 0x100,
901 ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
902 pCfgTab->u16Length, 0x1000-0x100));
903
904 MPSFLOATPTR floatPtr;
905 floatPtr.au8Signature[0] = '_';
906 floatPtr.au8Signature[1] = 'M';
907 floatPtr.au8Signature[2] = 'P';
908 floatPtr.au8Signature[3] = '_';
909 floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE;
910 floatPtr.u8Length = 1; /* structure size in paragraphs */
911 floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
912 floatPtr.u8Checksum = 0;
913 floatPtr.au8Feature[0] = 0;
914 floatPtr.au8Feature[1] = 0;
915 floatPtr.au8Feature[2] = 0;
916 floatPtr.au8Feature[3] = 0;
917 floatPtr.au8Feature[4] = 0;
918 floatPtr.u8Checksum = pcbiosChecksum((uint8_t*)&floatPtr, 16);
919 PDMDevHlpPhysWrite (pDevIns, 0x9fff0, &floatPtr, 16);
920}
921
922
923/**
924 * Reset notification.
925 *
926 * @returns VBox status.
927 * @param pDevIns The device instance data.
928 */
929static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
930{
931 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
932 LogFlow(("pcbiosReset:\n"));
933
934 pData->u8LogoBank = 0;
935 /** @todo Should we perhaps do pcbiosInitComplete() on reset? */
936
937#if 1
938 /*
939 * Paranoia: Check that the BIOS ROM hasn't changed.
940 */
941 PVM pVM = PDMDevHlpGetVM(pDevIns);
942 /* the low ROM mapping. */
943 unsigned cb = RT_MIN(g_cbPcBiosBinary, 128 * _1K);
944 const uint8_t *pb1 = (uint8_t *)MMPhysGCPhys2HCVirt(pVM, 0x00100000 - cb, cb);
945 AssertRelease(pb1);
946 const uint8_t *pb2 = &g_abPcBiosBinary[g_cbPcBiosBinary - cb];
947 if (memcmp(pb1, pb2, cb))
948 {
949 AssertMsg2("low ROM mismatch! cb=%#x\n", cb);
950 for (unsigned off = 0; off < cb; off++)
951 if (pb1[off] != pb2[off])
952 AssertMsg2("%05x: %02x expected %02x\n", off, pb1[off], pb2[off]);
953 AssertReleaseFailed();
954 }
955
956 /* the high ROM mapping. */
957 pb1 = (uint8_t *)MMPhysGCPhys2HCVirt(pVM, (uint32_t)-g_cbPcBiosBinary, g_cbPcBiosBinary);
958 AssertRelease(pb1);
959 pb2 = &g_abPcBiosBinary[0];
960 if (memcmp(pb1, pb2, g_cbPcBiosBinary))
961 {
962 AssertMsg2("high ROM mismatch! g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary);
963 for (unsigned off = 0; off < g_cbPcBiosBinary; off++)
964 if (pb1[off] != pb2[off])
965 AssertMsg2("%05x: %02x expected %02x\n", off, pb1[off], pb2[off]);
966 AssertReleaseFailed();
967 }
968#endif
969
970 if (pData->u8IOAPIC)
971 pcbiosPlantMPStable(pDevIns, pData->au8DMIPage + 0x100);
972}
973
974
975/**
976 * Destruct a device instance.
977 *
978 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
979 * resources can be freed correctly.
980 *
981 * @param pDevIns The device instance data.
982 */
983static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
984{
985 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
986 LogFlow(("pcbiosDestruct:\n"));
987
988 /*
989 * Free MM heap pointers.
990 */
991 if (pData->pu8LanBoot)
992 {
993 MMR3HeapFree(pData->pu8LanBoot);
994 pData->pu8LanBoot = NULL;
995 }
996
997 if (pData->pszLanBootFile)
998 {
999 MMR3HeapFree(pData->pszLanBootFile);
1000 pData->pszLanBootFile = NULL;
1001 }
1002
1003 if (pData->pu8Logo)
1004 {
1005 MMR3HeapFree(pData->pu8Logo);
1006 pData->pu8Logo = NULL;
1007 }
1008
1009 if (pData->pszLogoFile)
1010 {
1011 MMR3HeapFree(pData->pszLogoFile);
1012 pData->pszLogoFile = NULL;
1013 }
1014
1015 return VINF_SUCCESS;
1016}
1017
1018
1019/**
1020 * Convert config value to DEVPCBIOSBOOT.
1021 *
1022 * @returns VBox status code.
1023 * @param pCfgHandle Configuration handle.
1024 * @param pszParam The name of the value to read.
1025 * @param penmBoot Where to store the boot method.
1026 */
1027static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfgHandle, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1028{
1029 char *psz;
1030 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszParam, &psz);
1031 if (VBOX_FAILURE(rc))
1032 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1033 N_("Configuration error: Querying \"%s\" as a string failed"),
1034 pszParam);
1035 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
1036 *penmBoot = DEVPCBIOSBOOT_DVD;
1037 else if (!strcmp(psz, "IDE"))
1038 *penmBoot = DEVPCBIOSBOOT_HD;
1039 else if (!strcmp(psz, "FLOPPY"))
1040 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1041 else if (!strcmp(psz, "LAN"))
1042 *penmBoot = DEVPCBIOSBOOT_LAN;
1043 else if (!strcmp(psz, "NONE"))
1044 *penmBoot = DEVPCBIOSBOOT_NONE;
1045 else
1046 {
1047 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1048 N_("Configuration error: The \"%s\" value \"%s\" is unknown.\n"),
1049 pszParam, psz);
1050 rc = VERR_INTERNAL_ERROR;
1051 }
1052 MMR3HeapFree(psz);
1053 return rc;
1054}
1055
1056/**
1057 * Construct a device instance for a VM.
1058 *
1059 * @returns VBox status.
1060 * @param pDevIns The device instance data.
1061 * If the registration structure is needed, pDevIns->pDevReg points to it.
1062 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1063 * The device number is also found in pDevIns->iInstance, but since it's
1064 * likely to be freqently used PDM passes it as parameter.
1065 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1066 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1067 * iInstance it's expected to be used a bit in this function.
1068 */
1069static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1070{
1071 unsigned i;
1072 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
1073 int rc;
1074 int cb;
1075
1076 Assert(iInstance == 0);
1077
1078 /*
1079 * Validate configuration.
1080 */
1081 if (!CFGMR3AreValuesValid(pCfgHandle,
1082 "BootDevice0\0"
1083 "BootDevice1\0"
1084 "BootDevice2\0"
1085 "BootDevice3\0"
1086 "RamSize\0"
1087 "HardDiskDevice\0"
1088 "FloppyDevice\0"
1089 "FadeIn\0"
1090 "FadeOut\0"
1091 "LogoTime\0"
1092 "LogoFile\0"
1093 "ShowBootMenu\0"
1094 "DelayBoot\0"
1095 "LanBootRom\0"
1096 "IOAPIC\0"))
1097 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1098 N_("Invalid configuraton for device pcbios device"));
1099
1100 /*
1101 * Init the data.
1102 */
1103 rc = CFGMR3QueryU64(pCfgHandle, "RamSize", &pData->cbRam);
1104 if (VBOX_FAILURE(rc))
1105 return PDMDEV_SET_ERROR(pDevIns, rc,
1106 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1107
1108 rc = CFGMR3QueryU8 (pCfgHandle, "IOAPIC", &pData->u8IOAPIC);
1109 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1110 pData->u8IOAPIC = 1;
1111 else if (VBOX_FAILURE (rc))
1112 return PDMDEV_SET_ERROR(pDevIns, rc,
1113 N_("Configuration error: Failed to read \"IOAPIC\"."));
1114
1115 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1116 Assert(ELEMENTS(s_apszBootDevices) == ELEMENTS(pData->aenmBootDevice));
1117 for (i = 0; i < ELEMENTS(pData->aenmBootDevice); i++)
1118 {
1119 rc = pcbiosBootFromCfg(pDevIns, pCfgHandle, s_apszBootDevices[i], &pData->aenmBootDevice[i]);
1120 if (VBOX_FAILURE(rc))
1121 return rc;
1122 }
1123
1124 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HardDiskDevice", &pData->pszHDDevice);
1125 if (VBOX_FAILURE(rc))
1126 return PDMDEV_SET_ERROR(pDevIns, rc,
1127 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1128
1129 rc = CFGMR3QueryStringAlloc(pCfgHandle, "FloppyDevice", &pData->pszFDDevice);
1130 if (VBOX_FAILURE(rc))
1131 return PDMDEV_SET_ERROR(pDevIns, rc,
1132 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1133
1134 /*
1135 * Register I/O Ports and PC BIOS.
1136 */
1137 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1138 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1139 if (VBOX_FAILURE(rc))
1140 return rc;
1141 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1142 NULL, NULL, "Bochs PC BIOS - Shutdown");
1143 if (VBOX_FAILURE(rc))
1144 return rc;
1145
1146 pcbiosPlantDMITable(pData->au8DMIPage);
1147 if (pData->u8IOAPIC)
1148 pcbiosPlantMPStable(pDevIns, pData->au8DMIPage + 0x100);
1149
1150 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, 0x1000, pData->au8DMIPage, "DMI tables");
1151 if (VBOX_FAILURE(rc))
1152 return rc;
1153
1154 /*
1155 * Map the BIOS into memory.
1156 * There are two mappings:
1157 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1158 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1159 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1160 */
1161 AssertReleaseMsg(g_cbPcBiosBinary >= _64K, ("g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary));
1162 AssertReleaseMsg(RT_ALIGN_Z(g_cbPcBiosBinary, _64K) == g_cbPcBiosBinary,
1163 ("g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary));
1164 cb = RT_MIN(g_cbPcBiosBinary, 128 * _1K);
1165 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb,
1166 &g_abPcBiosBinary[g_cbPcBiosBinary - cb], "PC BIOS - 0xfffff");
1167 if (VBOX_FAILURE(rc))
1168 return rc;
1169 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-g_cbPcBiosBinary, g_cbPcBiosBinary,
1170 &g_abPcBiosBinary[0], "PC BIOS - 0xffffffff");
1171 if (VBOX_FAILURE(rc))
1172 return rc;
1173
1174 /*
1175 * Register the MMIO region for the BIOS Logo: 0x000d0000 to 0x000dffff (64k)
1176 */
1177 rc = PDMDevHlpMMIORegister(pDevIns, 0x000d0000, 0x00010000, 0,
1178 logoMMIOWrite, logoMMIORead, NULL, "PC BIOS - Logo Buffer");
1179 if (VBOX_FAILURE(rc))
1180 return rc;
1181
1182 /*
1183 * Construct the logo header.
1184 */
1185 LOGOHDR LogoHdr = { LOGOHDR_MAGIC, 0, 0, 0, 0, 0 };
1186
1187 rc = CFGMR3QueryU8(pCfgHandle, "FadeIn", &LogoHdr.u8FadeIn);
1188 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1189 LogoHdr.u8FadeIn = 1;
1190 else if (VBOX_FAILURE(rc))
1191 return PDMDEV_SET_ERROR(pDevIns, rc,
1192 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
1193
1194 rc = CFGMR3QueryU8(pCfgHandle, "FadeOut", &LogoHdr.u8FadeOut);
1195 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1196 LogoHdr.u8FadeOut = 1;
1197 else if (VBOX_FAILURE(rc))
1198 return PDMDEV_SET_ERROR(pDevIns, rc,
1199 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
1200
1201 rc = CFGMR3QueryU16(pCfgHandle, "LogoTime", &LogoHdr.u16LogoMillies);
1202 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1203 LogoHdr.u16LogoMillies = 1;
1204 else if (VBOX_FAILURE(rc))
1205 return PDMDEV_SET_ERROR(pDevIns, rc,
1206 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
1207
1208 rc = CFGMR3QueryU8(pCfgHandle, "ShowBootMenu", &LogoHdr.u8ShowBootMenu);
1209 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1210 LogoHdr.u8ShowBootMenu = 0;
1211 else if (VBOX_FAILURE(rc))
1212 return PDMDEV_SET_ERROR(pDevIns, rc,
1213 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
1214
1215 /*
1216 * Get the Logo file name.
1217 */
1218 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LogoFile", &pData->pszLogoFile);
1219 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1220 pData->pszLogoFile = NULL;
1221 else if (VBOX_FAILURE(rc))
1222 return PDMDEV_SET_ERROR(pDevIns, rc,
1223 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
1224 else if (!*pData->pszLogoFile)
1225 {
1226 MMR3HeapFree(pData->pszLogoFile);
1227 pData->pszLogoFile = NULL;
1228 }
1229
1230 /*
1231 * Determine the logo size, open any specified logo file in the process.
1232 */
1233 LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1234 RTFILE FileLogo = NIL_RTFILE;
1235 if (pData->pszLogoFile)
1236 {
1237 rc = RTFileOpen(&FileLogo, pData->pszLogoFile,
1238 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1239 if (VBOX_SUCCESS(rc))
1240 {
1241 uint64_t cbFile;
1242 rc = RTFileGetSize(FileLogo, &cbFile);
1243 if (VBOX_SUCCESS(rc))
1244 {
1245 if ( cbFile > 0
1246 && cbFile < ((LOGO_BANK_LAST + 1) * LOGO_BANK_SIZE) - sizeof(LogoHdr))
1247 LogoHdr.cbLogo = (uint32_t)cbFile;
1248 else
1249 rc = VERR_TOO_MUCH_DATA;
1250 }
1251 }
1252 if (VBOX_FAILURE(rc))
1253 {
1254 /*
1255 * Ignore failure and fall back to the default logo.
1256 */
1257 LogRel(("pcbiosConstruct: Failed to open logo file '%s', rc=%Vrc!\n", pData->pszLogoFile, rc));
1258 RTFileClose(FileLogo);
1259 FileLogo = NIL_RTFILE;
1260 MMR3HeapFree(pData->pszLogoFile);
1261 pData->pszLogoFile = NULL;
1262 }
1263 }
1264
1265 /*
1266 * Allocate buffer for the logo data.
1267 * RT_MAX() is applied to let us fall back to default logo on read failure.
1268 */
1269 pData->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
1270 pData->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pData->cbLogo, g_cbPcDefBiosLogo + sizeof(LogoHdr)));
1271 if (pData->pu8Logo)
1272 {
1273 /*
1274 * Write the logo header.
1275 */
1276 PLOGOHDR pLogoHdr = (PLOGOHDR)pData->pu8Logo;
1277 *pLogoHdr = LogoHdr;
1278
1279 /*
1280 * Write the logo bitmap.
1281 */
1282 if (pData->pszLogoFile)
1283 {
1284 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
1285 if (VBOX_FAILURE(rc))
1286 {
1287 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", LogoHdr.cbLogo, rc));
1288 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1289 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1290 }
1291 }
1292 else
1293 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1294
1295 /*
1296 * Call reset to set values and stuff.
1297 */
1298 pcbiosReset(pDevIns);
1299 rc = VINF_SUCCESS;
1300 }
1301 else
1302 rc = VERR_NO_MEMORY;
1303
1304 /* cleanup */
1305 if (FileLogo != NIL_RTFILE)
1306 RTFileClose(FileLogo);
1307
1308 /*
1309 * Get the LAN boot ROM file name.
1310 */
1311 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LanBootRom", &pData->pszLanBootFile);
1312 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1313 {
1314 pData->pszLanBootFile = NULL;
1315 rc = VINF_SUCCESS;
1316 }
1317 else if (VBOX_FAILURE(rc))
1318 return PDMDEV_SET_ERROR(pDevIns, rc,
1319 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1320 else if (!*pData->pszLanBootFile)
1321 {
1322 MMR3HeapFree(pData->pszLanBootFile);
1323 pData->pszLanBootFile = NULL;
1324 }
1325
1326 const uint8_t *pu8LanBoot = NULL;
1327 uint64_t cbFileLanBoot;
1328#ifdef VBOX_DO_NOT_LINK_LANBOOT
1329 /*
1330 * Determine the LAN boot ROM size, open specified ROM file in the process.
1331 */
1332 RTFILE FileLanBoot = NIL_RTFILE;
1333 if (pData->pszLanBootFile)
1334 {
1335 rc = RTFileOpen(&FileLanBoot, pData->pszLanBootFile,
1336 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1337 if (VBOX_SUCCESS(rc))
1338 {
1339 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1340 if (VBOX_SUCCESS(rc))
1341 {
1342 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1343 || cbFileLanBoot > _32K)
1344 rc = VERR_TOO_MUCH_DATA;
1345 }
1346 }
1347 if (VBOX_FAILURE(rc))
1348 {
1349 /*
1350 * Ignore failure and fall back to no LAN boot ROM.
1351 */
1352 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Vrc!\n", pData->pszLanBootFile, rc));
1353 RTFileClose(FileLanBoot);
1354 FileLanBoot = NIL_RTFILE;
1355 MMR3HeapFree(pData->pszLanBootFile);
1356 pData->pszLanBootFile = NULL;
1357 }
1358 }
1359
1360 /*
1361 * Get the LAN boot ROM data.
1362 */
1363 if (pData->pszLanBootFile)
1364 {
1365 /*
1366 * Allocate buffer for the LAN boot ROM data.
1367 */
1368 pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1369 pData->pu8LanBoot = pu8LanBoot;
1370 if (pu8LanBoot)
1371 {
1372 rc = RTFileRead(FileLanBoot, pData->pu8LanBoot, cbFileLanBoot, NULL);
1373 if (VBOX_FAILURE(rc))
1374 {
1375 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", cbFileLanBoot, rc));
1376 MMR3HeapFree(pu8LanBoot);
1377 pu8LanBoot = NULL;
1378 pData->pu8LanBoot = NULL;
1379 }
1380 rc = VINF_SUCCESS;
1381 }
1382 else
1383 rc = VERR_NO_MEMORY;
1384 }
1385 else
1386 pData->pu8LanBoot = NULL;
1387
1388 /* cleanup */
1389 if (FileLanBoot != NIL_RTFILE)
1390 RTFileClose(FileLanBoot);
1391
1392#else /* !VBOX_DO_NOT_LINK_LANBOOT */
1393 pData->pu8LanBoot = NULL;
1394 pu8LanBoot = g_abNetBiosBinary;
1395 cbFileLanBoot = g_cbNetBiosBinary;
1396#endif /* !VBOX_DO_NOT_LINK_LANBOOT */
1397
1398 /*
1399 * Map the Network Boot ROM into memory.
1400 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1401 * the (up to) 32 kb ROM image.
1402 */
1403 if (pu8LanBoot)
1404 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbFileLanBoot, pu8LanBoot, "Net Boot ROM");
1405
1406 rc = CFGMR3QueryU8(pCfgHandle, "DelayBoot", &pData->uBootDelay);
1407 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1408 {
1409 pData->uBootDelay = 0;
1410 rc = VINF_SUCCESS;
1411 }
1412 else
1413 {
1414 if (VBOX_FAILURE(rc))
1415 return PDMDEV_SET_ERROR(pDevIns, rc,
1416 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1417 if (pData->uBootDelay > 15)
1418 pData->uBootDelay = 15;
1419 }
1420
1421 return rc;
1422}
1423
1424
1425/**
1426 * The device registration structure.
1427 */
1428const PDMDEVREG g_DevicePcBios =
1429{
1430 /* u32Version */
1431 PDM_DEVREG_VERSION,
1432 /* szDeviceName */
1433 "pcbios",
1434 /* szGCMod */
1435 "",
1436 /* szR0Mod */
1437 "",
1438 /* pszDescription */
1439 "Bochs PC BIOS",
1440 /* fFlags */
1441 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1442 /* fClass */
1443 PDM_DEVREG_CLASS_ARCH_BIOS,
1444 /* cMaxInstances */
1445 1,
1446 /* cbInstance */
1447 sizeof(DEVPCBIOS),
1448 /* pfnConstruct */
1449 pcbiosConstruct,
1450 /* pfnDestruct */
1451 pcbiosDestruct,
1452 /* pfnRelocate */
1453 NULL,
1454 /* pfnIOCtl */
1455 NULL,
1456 /* pfnPowerOn */
1457 NULL,
1458 /* pfnReset */
1459 pcbiosReset,
1460 /* pfnSuspend */
1461 NULL,
1462 /* pfnResume */
1463 NULL,
1464 /* pfnAttach */
1465 NULL,
1466 /* pfnDetach */
1467 NULL,
1468 /* pfnQueryInterface. */
1469 NULL,
1470 /* pfnInitComplete. */
1471 pcbiosInitComplete
1472};
1473
Note: See TracBrowser for help on using the repository browser.

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