VirtualBox

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

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

made the file description correspond to the device/driver description.

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