VirtualBox

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

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

Pass the VM's GUID to DevPcBIOS so that the GUID can be stored in DMI/SMBIOS
tables and used by applications.

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