VirtualBox

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

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

gcc-4.3 found this bug

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 51.8 KB
Line 
1/* $Id: DevPcBios.cpp 5905 2007-12-02 19:39:02Z 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#include <VBox/param.h>
32
33#include "Builtins.h"
34#include "Builtins2.h"
35#include "DevPcBios.h"
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41
42/**
43 * The boot device.
44 */
45typedef enum DEVPCBIOSBOOT
46{
47 DEVPCBIOSBOOT_NONE,
48 DEVPCBIOSBOOT_FLOPPY,
49 DEVPCBIOSBOOT_HD,
50 DEVPCBIOSBOOT_DVD,
51 DEVPCBIOSBOOT_LAN
52} DEVPCBIOSBOOT;
53
54/**
55 * PC Bios instance data structure.
56 */
57typedef struct DEVPCBIOS
58{
59 /** Pointer back to the device instance. */
60 PPDMDEVINS pDevIns;
61
62 /** Boot devices (ordered). */
63 DEVPCBIOSBOOT aenmBootDevice[4];
64 /** Ram Size (in bytes). */
65 uint64_t cbRam;
66 /** Bochs shutdown index. */
67 uint32_t iShutdown;
68 /** Floppy device. */
69 char *pszFDDevice;
70 /** Harddisk device. */
71 char *pszHDDevice;
72 /** Bios message buffer. */
73 char szMsg[256];
74 /** Bios message buffer index. */
75 uint32_t iMsg;
76 /** Current logo data offset. */
77 uint32_t offLogoData;
78 /** Use built-in or loaded logo. */
79 bool fDefaultLogo;
80 /** The size of the BIOS logo data. */
81 uint32_t cbLogo;
82 /** The BIOS logo data. */
83 uint8_t *pu8Logo;
84 /** The name of the logo file. */
85 char *pszLogoFile;
86 /** The LAN boot ROM data. */
87 uint8_t *pu8LanBoot;
88 /** The name of the LAN boot ROM file. */
89 char *pszLanBootFile;
90 /** The DMI tables. */
91 uint8_t au8DMIPage[0x1000];
92 /** The boot countdown (in seconds). */
93 uint8_t uBootDelay;
94 /** I/O-APIC enabled? */
95 uint8_t u8IOAPIC;
96 /** PXE debug logging enabled? */
97 uint8_t u8PXEDebug;
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 (LOGO_HDR_MAGIC/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/** PC port for Logo I/O */
125#define LOGO_IO_PORT 0x506
126
127/** The value of the LOGOHDR::u16Signature field. */
128#define LOGO_HDR_MAGIC 0x66BB
129
130/** The value which will switch you the default logo. */
131#define LOGO_DEFAULT_LOGO 0xFFFF
132
133/** The maximal logo size in bytes. (640x480x8bpp + header/palette) */
134#define LOGO_MAX_SIZE 640 * 480 + 0x442
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) logoIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
263static DECLCALLBACK(int) logoIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, 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 * PXE debug option.
410 */
411 pcbiosCmosWrite(pDevIns, 0x3f, pData->u8PXEDebug);
412
413 /*
414 * Floppy drive type.
415 */
416 for (i = 0; i < ELEMENTS(apFDs); i++)
417 {
418 PPDMIBASE pBase;
419 int rc = PDMR3QueryLun(pVM, pData->pszFDDevice, 0, i, &pBase);
420 if (VBOX_SUCCESS(rc))
421 apFDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
422 }
423 u32 = 0;
424 if (apFDs[0])
425 switch (apFDs[0]->pfnGetType(apFDs[0]))
426 {
427 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << 4; break;
428 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << 4; break;
429 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << 4; break;
430 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << 4; break;
431 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << 4; break;
432 default: AssertFailed(); break;
433 }
434 if (apFDs[1])
435 switch (apFDs[1]->pfnGetType(apFDs[1]))
436 {
437 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1; break;
438 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2; break;
439 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3; break;
440 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4; break;
441 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5; break;
442 default: AssertFailed(); break;
443 }
444 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
445
446 /*
447 * Equipment byte.
448 */
449 u32 = !!apFDs[0] + !!apFDs[1];
450 switch (u32)
451 {
452 case 1: u32 = 0x01; break; /* floppy installed, 1 drive. */
453 case 2: u32 = 0x41; break; /* floppy installed, 2 drives. */
454 default:u32 = 0; break; /* floppy not installed. */
455 }
456 u32 |= RT_BIT(1); /* math coprocessor installed */
457 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
458 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
459 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
460
461 /*
462 * Harddisks.
463 */
464 for (i = 0; i < ELEMENTS(apHDs); i++)
465 {
466 PPDMIBASE pBase;
467 int rc = PDMR3QueryLun(pVM, pData->pszHDDevice, 0, i, &pBase);
468 if (VBOX_SUCCESS(rc))
469 apHDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
470 }
471
472 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0); /* 0Fh means extended and points to 1Ah, 1Bh */
473 pcbiosCmosWrite(pDevIns, 0x12, u32); /* 12h - Hard Disk Data (type) */
474 if (apHDs[0])
475 pcbiosCmosInitHardDisk(pDevIns, 0x19, 0x1b, apHDs[0]); /* 19h - First Extended Hard Disk Drive Type */
476 if (apHDs[1])
477 pcbiosCmosInitHardDisk(pDevIns, 0x1a, 0x24, apHDs[1]); /* 1Ah - Second Extended Hard Disk Drive Type */
478
479 /*
480 * Translation type - Bochs BIOS specific.
481 */
482 u32 = 0;
483 for (i = 0; i < 4; i++)
484 {
485 if (apHDs[i])
486 {
487 PDMBIOSTRANSLATION enmTranslation;
488 int rc = apHDs[i]->pfnGetTranslation(apHDs[i], &enmTranslation);
489 if (VBOX_FAILURE(rc) || enmTranslation == PDMBIOSTRANSLATION_AUTO)
490 {
491 uint32_t cCylinders, cHeads, cSectors;
492 rc = apHDs[i]->pfnGetGeometry(apHDs[i], &cCylinders, &cHeads, &cSectors);
493 if (VBOX_FAILURE(rc))
494 {
495 AssertMsg(rc == VERR_PDM_MEDIA_NOT_MOUNTED, ("This shouldn't happen! rc=%Vrc\n", rc));
496 enmTranslation = PDMBIOSTRANSLATION_NONE;
497 }
498 else if (cCylinders <= 1024 && cHeads <= 16 && cSectors <= 63)
499 {
500 /* Disk <= 512 MByte not needing LBA translation. */
501 enmTranslation = PDMBIOSTRANSLATION_NONE;
502 }
503 else if (cSectors != 63 || (cHeads != 16 && cHeads != 32 && cHeads != 64 && cHeads != 128 && cHeads != 255))
504 {
505 /* Disk with strange geometry. Using LBA here can
506 * break booting of the guest OS. Especially operating
507 * systems from Microsoft are sensitive to BIOS CHS not
508 * matching what the partition table says. */
509 enmTranslation = PDMBIOSTRANSLATION_NONE;
510 }
511 else
512 enmTranslation = PDMBIOSTRANSLATION_LBA;
513 }
514 switch (enmTranslation)
515 {
516 case PDMBIOSTRANSLATION_AUTO: /* makes gcc happy */
517 case PDMBIOSTRANSLATION_NONE:
518 /* u32 |= 0 << (i * 2) */
519 break;
520 default:
521 AssertMsgFailed(("bad enmTranslation=%d\n", enmTranslation));
522 case PDMBIOSTRANSLATION_LBA:
523 u32 |= 1 << (i * 2);
524 break;
525 }
526 }
527 }
528 Log2(("pcbiosInitComplete: translation byte: %#02x\n", u32));
529 pcbiosCmosWrite(pDevIns, 0x39, u32);
530
531 LogFlow(("pcbiosInitComplete: returns VINF_SUCCESS\n"));
532 return VINF_SUCCESS;
533}
534
535
536/**
537 * Port I/O Handler for IN operations.
538 *
539 * @returns VBox status code.
540 *
541 * @param pDevIns The device instance.
542 * @param pvUser User argument - ignored.
543 * @param Port Port number used for the IN operation.
544 * @param pu32 Where to store the result.
545 * @param cb Number of bytes read.
546 */
547static DECLCALLBACK(int) pcbiosIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
548{
549 NOREF(pDevIns);
550 NOREF(pvUser);
551 NOREF(Port);
552 NOREF(pu32);
553 NOREF(cb);
554 return VERR_IOM_IOPORT_UNUSED;
555}
556
557
558/**
559 * Port I/O Handler for OUT operations.
560 *
561 * @returns VBox status code.
562 *
563 * @param pDevIns The device instance.
564 * @param pvUser User argument - ignored.
565 * @param Port Port number used for the IN operation.
566 * @param u32 The value to output.
567 * @param cb The value size in bytes.
568 */
569static DECLCALLBACK(int) pcbiosIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
570{
571 /*
572 * Bochs BIOS Panic
573 */
574 if ( cb == 2
575 && ( Port == 0x400
576 || Port == 0x401))
577 {
578 Log(("pcbios: PC BIOS panic at rombios.c(%d)\n", u32));
579 AssertReleaseMsgFailed(("PC BIOS panic at rombios.c(%d)\n", u32));
580 return VERR_INTERNAL_ERROR;
581 }
582
583 /*
584 * Bochs BIOS char printing.
585 */
586 if ( cb == 1
587 && ( Port == 0x402
588 || Port == 0x403))
589 {
590 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
591 /* The raw version. */
592 switch (u32)
593 {
594 case '\r': Log2(("pcbios: <return>\n")); break;
595 case '\n': Log2(("pcbios: <newline>\n")); break;
596 case '\t': Log2(("pcbios: <tab>\n")); break;
597 default: Log2(("pcbios: %c (%02x)\n", u32, u32)); break;
598 }
599
600 /* The readable, buffered version. */
601 if (u32 == '\n' || u32 == '\r')
602 {
603 pData->szMsg[pData->iMsg] = '\0';
604 if (pData->iMsg)
605 Log(("pcbios: %s\n", pData->szMsg));
606 pData->iMsg = 0;
607 }
608 else
609 {
610 if (pData->iMsg >= sizeof(pData->szMsg)-1)
611 {
612 pData->szMsg[pData->iMsg] = '\0';
613 Log(("pcbios: %s\n", pData->szMsg));
614 pData->iMsg = 0;
615 }
616 pData->szMsg[pData->iMsg] = (char )u32;
617 pData->szMsg[++pData->iMsg] = '\0';
618 }
619 return VINF_SUCCESS;
620 }
621
622 /*
623 * Bochs BIOS shutdown request.
624 */
625 if (cb == 1 && Port == 0x8900)
626 {
627 static const unsigned char szShutdown[] = "Shutdown";
628 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
629 if (u32 == szShutdown[pData->iShutdown])
630 {
631 pData->iShutdown++;
632 if (pData->iShutdown == 8)
633 {
634 pData->iShutdown = 0;
635 LogRel(("8900h shutdown request.\n"));
636 return PDMDevHlpVMPowerOff(pDevIns);
637 }
638 }
639 else
640 pData->iShutdown = 0;
641 return VINF_SUCCESS;
642 }
643
644 /* not in use. */
645 return VINF_SUCCESS;
646}
647
648
649/**
650 * LOGO port I/O Handler for IN operations.
651 *
652 * @returns VBox status code.
653 *
654 * @param pDevIns The device instance.
655 * @param pvUser User argument - ignored.
656 * @param uPort Port number used for the IN operation.
657 * @param pu32 Where to store the result.
658 * @param cb Number of bytes read.
659 */
660static DECLCALLBACK(int) logoIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
661{
662 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
663 Log(("logoIOPortRead call Port:%x pu32:%x cb:%d (%d)\n", Port, pu32, cb, pData->offLogoData));
664
665 PRTUINT64U p;
666 if (pData->fDefaultLogo)
667 {
668 /*
669 * Default bios logo.
670 */
671 if (pData->offLogoData + cb > g_cbPcDefBiosLogo)
672 {
673 Log(("logoIOPortRead: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
674 pData->offLogoData, pData->offLogoData, g_cbPcDefBiosLogo, g_cbPcDefBiosLogo));
675 return VINF_SUCCESS;
676 }
677 p = (PRTUINT64U)&g_abPcDefBiosLogo[pData->offLogoData];
678 }
679 else
680 {
681 /*
682 * Custom logo.
683 */
684 if (pData->offLogoData + cb > pData->cbLogo)
685 {
686 Log(("logoIOPortRead: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
687 pData->offLogoData, pData->offLogoData, pData->cbLogo, pData->cbLogo));
688 return VINF_SUCCESS;
689 }
690 p = (PRTUINT64U)&pData->pu8Logo[pData->offLogoData];
691 }
692
693 switch (cb)
694 {
695 case 1: *pu32 = p->au8[0]; break;
696 case 2: *pu32 = p->au16[0]; break;
697 case 4: *pu32 = p->au32[0]; break;
698 //case 8: *pu32 = p->au64[0]; break;
699 default: AssertFailed(); break;
700 }
701 Log(("logoIOPortRead: LogoOffset=%#x(%d) cb=%#x %.*Vhxs\n", pData->offLogoData, pData->offLogoData, cb, cb, pu32));
702 pData->offLogoData += cb;
703
704 return VINF_SUCCESS;
705}
706
707
708/**
709 * LOGO port I/O Handler for OUT operations.
710 *
711 * @returns VBox status code.
712 *
713 * @param pDevIns The device instance.
714 * @param pvUser User argument - ignored.
715 * @param uPort Port number used for the IN operation.
716 * @param u32 The value to output.
717 * @param cb The value size in bytes.
718 */
719static DECLCALLBACK(int) logoIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
720{
721 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
722 Log(("logoIOPortWrite: Port=%x cb=%d u32=%#04x (byte)\n", Port, cb, u32));
723
724 /* Switch to default BIOS logo or change logo data offset. */
725 if ( cb == 2
726 && u32 == LOGO_DEFAULT_LOGO)
727 {
728 pData->fDefaultLogo = true;
729 pData->offLogoData = 0;
730 }
731 else
732 pData->offLogoData = u32;
733
734 return VINF_SUCCESS;
735}
736
737
738/**
739 * Construct the DMI table.
740 *
741 * @param table pointer to DMI table.
742 */
743#define STRCPY(p, s) do { memcpy (p, s, sizeof(s)); p += sizeof(s); } while (0)
744static void pcbiosPlantDMITable(uint8_t *pTable, PRTUUID puuid)
745{
746 char *pszStr = (char*)pTable;
747 int iStrNr;
748
749 PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
750 pszStr = (char*)(pBIOSInf+1);
751 iStrNr = 1;
752 pBIOSInf->header.u8Type = 0; /* BIOS Information */
753 pBIOSInf->header.u8Length = sizeof(*pBIOSInf);
754 pBIOSInf->header.u16Handle = 0x0000;
755 pBIOSInf->u8Vendor = iStrNr++;
756 STRCPY(pszStr, "innotek GmbH");
757 pBIOSInf->u8Version = iStrNr++;
758 STRCPY(pszStr, "VirtualBox");
759 pBIOSInf->u16Start = 0xE000;
760 pBIOSInf->u8Release = iStrNr++;
761 STRCPY(pszStr, "12/01/2006");
762 pBIOSInf->u8ROMSize = 1; /* 128K */
763 pBIOSInf->u64Characteristics = RT_BIT(4) /* ISA is supported */
764 | RT_BIT(7) /* PCI is supported */
765 | RT_BIT(15) /* Boot from CD is supported */
766 | RT_BIT(16) /* Selectable Boot is supported */
767 | RT_BIT(27) /* Int 9h, 8042 Keyboard services supported */
768 | RT_BIT(30) /* Int 10h, CGA/Mono Video Services supported */
769 /* any more?? */
770 ;
771 pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0) /* ACPI is supported */
772 /* any more?? */
773 ;
774 pBIOSInf->u8CharacteristicsByte2 = 0
775 /* any more?? */
776 ;
777 *pszStr++ = '\0';
778
779
780 PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
781 pszStr = (char*)(pSystemInf+1);
782 iStrNr = 1;
783 pSystemInf->header.u8Type = 1; /* System Information */
784 pSystemInf->header.u8Length = sizeof(*pSystemInf);
785 pSystemInf->header.u16Handle = 0x0001;
786 pSystemInf->u8Manufacturer = iStrNr++;
787 STRCPY(pszStr, "innotek GmbH");
788 pSystemInf->u8ProductName = iStrNr++;
789 STRCPY(pszStr, "VirtualBox");
790 pSystemInf->u8Version = iStrNr++;
791 STRCPY(pszStr, "1.2");
792 pSystemInf->u8SerialNumber = iStrNr++;
793 STRCPY(pszStr, "0");
794 memcpy(pSystemInf->au8Uuid, puuid, sizeof(RTUUID));
795 pSystemInf->u8WakeupType = 6; /* Power Switch */
796 pSystemInf->u8SKUNumber = 0;
797 pSystemInf->u8Family = iStrNr++;
798 STRCPY(pszStr, "Virtual Machine");
799 *pszStr++ = '\0';
800
801 AssertMsg(pszStr - (char*)pTable == VBOX_DMI_TABLE_SIZE,
802 ("VBOX_DMI_TABLE_SIZE=%d, actual DMI table size is %d",
803 VBOX_DMI_TABLE_SIZE, pszStr - (char*)pTable));
804}
805AssertCompile(VBOX_DMI_TABLE_ENTR == 2);
806
807
808/**
809 * Calculate a simple checksum for the MPS table.
810 *
811 * @param data data
812 * @param len size of data
813 */
814static uint8_t pcbiosChecksum(const uint8_t * const au8Data, uint32_t u32Length)
815{
816 uint8_t u8Sum = 0;
817 for (size_t i = 0; i < u32Length; ++i)
818 u8Sum += au8Data[i];
819 return -u8Sum;
820}
821
822
823/**
824 * Construct the MPS table. Only applicable if IOAPIC is active.
825 *
826 * @param pDevIns The device instance data.
827 * @param addr physical address in guest memory.
828 */
829static void pcbiosPlantMPStable(PPDMDEVINS pDevIns, uint8_t *pTable)
830{
831 /* configuration table */
832 PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
833 memcpy(pCfgTab->au8Signature, "PCMP", 4);
834 pCfgTab->u8SpecRev = 4; /* 1.4 */
835 memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
836 memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
837 pCfgTab->u32OemTablePtr = 0;
838 pCfgTab->u16OemTableSize = 0;
839 pCfgTab->u16EntryCount = 1 /* Processor */
840 + 1 /* ISA Bus */
841 + 1 /* I/O-APIC */
842 + 16 /* Interrupts */;
843 pCfgTab->u32AddrLocalApic = 0xfee00000;
844 pCfgTab->u16ExtTableLength = 0;
845 pCfgTab->u8ExtTableChecksxum = 0;
846 pCfgTab->u8Reserved = 0;
847
848 uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
849 uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
850 uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
851 PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
852 if (u32Eax >= 1)
853 {
854 PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
855 u32CPUSignature = u32Eax & 0xfff;
856 /* Local APIC will be enabled later so override it here. Since we provide
857 * an MP table we have an IOAPIC and therefore a Local APIC. */
858 u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
859 }
860
861 /* one processor so far */
862 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
863 pProcEntry->u8EntryType = 0; /* processor entry */
864 pProcEntry->u8LocalApicId = 0;
865 pProcEntry->u8LocalApicVersion = 0x11;
866 pProcEntry->u8CPUFlags = 2 /* bootstrap processor */ | 1 /* enabled */;
867 pProcEntry->u32CPUSignature = u32CPUSignature;
868 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
869 pProcEntry->u32Reserved[0] =
870 pProcEntry->u32Reserved[1] = 0;
871
872 /* ISA bus */
873 PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)(pProcEntry+1);
874 pBusEntry->u8EntryType = 1; /* bus entry */
875 pBusEntry->u8BusId = 0; /* this ID is referenced by the interrupt entries */
876 memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
877
878 /* PCI bus? */
879
880 /* I/O-APIC.
881 * MP spec: "The configuration table contains one or more entries for I/O APICs.
882 * ... At least one I/O APIC must be enabled." */
883 PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1);
884 pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
885 pIOAPICEntry->u8Id = 1; /* this ID is referenced by the interrupt entries */
886 pIOAPICEntry->u8Version = 0x11;
887 pIOAPICEntry->u8Flags = 1 /* enable */;
888 pIOAPICEntry->u32Addr = 0xfec00000;
889
890 PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
891 for (int i = 0; i < 16; i++, pIrqEntry++)
892 {
893 pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
894 pIrqEntry->u8Type = 0; /* INT, vectored interrupt */
895 pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
896 trigger mode = conforms to bus */
897 pIrqEntry->u8SrcBusId = 0; /* ISA bus */
898 pIrqEntry->u8SrcBusIrq = i;
899 pIrqEntry->u8DstIOAPICId = 1;
900 pIrqEntry->u8DstIOAPICInt = i;
901 }
902
903 pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
904 pCfgTab->u8Checksum = pcbiosChecksum(pTable, pCfgTab->u16Length);
905
906 AssertMsg(pCfgTab->u16Length < 0x1000 - 0x100,
907 ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
908 pCfgTab->u16Length, 0x1000-0x100));
909
910 MPSFLOATPTR floatPtr;
911 floatPtr.au8Signature[0] = '_';
912 floatPtr.au8Signature[1] = 'M';
913 floatPtr.au8Signature[2] = 'P';
914 floatPtr.au8Signature[3] = '_';
915 floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE;
916 floatPtr.u8Length = 1; /* structure size in paragraphs */
917 floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
918 floatPtr.u8Checksum = 0;
919 floatPtr.au8Feature[0] = 0;
920 floatPtr.au8Feature[1] = 0;
921 floatPtr.au8Feature[2] = 0;
922 floatPtr.au8Feature[3] = 0;
923 floatPtr.au8Feature[4] = 0;
924 floatPtr.u8Checksum = pcbiosChecksum((uint8_t*)&floatPtr, 16);
925 PDMDevHlpPhysWrite (pDevIns, 0x9fff0, &floatPtr, 16);
926}
927
928
929/**
930 * Reset notification.
931 *
932 * @returns VBox status.
933 * @param pDevIns The device instance data.
934 */
935static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
936{
937 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
938 LogFlow(("pcbiosReset:\n"));
939
940 pData->fDefaultLogo = false;
941 pData->offLogoData = 0;
942 /** @todo Should we perhaps do pcbiosInitComplete() on reset? */
943
944#if 1
945 /*
946 * Paranoia: Check that the BIOS ROM hasn't changed.
947 */
948 uint8_t abBuf[PAGE_SIZE];
949
950 /* the low ROM mapping. */
951 unsigned cb = RT_MIN(g_cbPcBiosBinary, 128 * _1K);
952 RTGCPHYS GCPhys = 0x00100000 - cb;
953 const uint8_t *pbVirgin = &g_abPcBiosBinary[g_cbPcBiosBinary - cb];
954 while (GCPhys < 0x00100000)
955 {
956 PDMDevHlpPhysRead(pDevIns, GCPhys, abBuf, PAGE_SIZE);
957 if (memcmp(abBuf, pbVirgin, PAGE_SIZE))
958 {
959 LogRel(("low ROM mismatch! GCPhys=%VGp - Ignore if you've loaded an old saved state with an different VirtualBox version.\n", GCPhys));
960 for (unsigned off = 0; off < PAGE_SIZE; off++)
961 if (abBuf[off] != pbVirgin[off])
962 LogRel(("%VGp: %02x expected %02x\n", GCPhys + off, abBuf[off], pbVirgin[off]));
963 AssertFailed();
964 }
965
966 /* next page */
967 GCPhys += PAGE_SIZE;
968 pbVirgin += PAGE_SIZE;
969 }
970
971 /* the high ROM mapping. */
972 GCPhys = UINT32_C(0xffffffff) - (g_cbPcBiosBinary - 1);
973 pbVirgin = &g_abPcBiosBinary[0];
974 while (pbVirgin < &g_abPcBiosBinary[g_cbPcBiosBinary])
975 {
976 PDMDevHlpPhysRead(pDevIns, GCPhys, abBuf, PAGE_SIZE);
977 if (memcmp(abBuf, pbVirgin, PAGE_SIZE))
978 {
979 LogRel(("high ROM mismatch! GCPhys=%VGp - Ignore if you've loaded an old saved state with an different VirtualBox version.\n", GCPhys));
980 for (unsigned off = 0; off < PAGE_SIZE; off++)
981 if (abBuf[off] != pbVirgin[off])
982 LogRel(("%VGp: %02x expected %02x\n", GCPhys + off, abBuf[off], pbVirgin[off]));
983 AssertFailed();
984 }
985
986 /* next page */
987 GCPhys += PAGE_SIZE;
988 pbVirgin += PAGE_SIZE;
989 }
990#endif
991
992 if (pData->u8IOAPIC)
993 pcbiosPlantMPStable(pDevIns, pData->au8DMIPage + 0x100);
994}
995
996
997/**
998 * Destruct a device instance.
999 *
1000 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1001 * resources can be freed correctly.
1002 *
1003 * @param pDevIns The device instance data.
1004 */
1005static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1006{
1007 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
1008 LogFlow(("pcbiosDestruct:\n"));
1009
1010 /*
1011 * Free MM heap pointers.
1012 */
1013 if (pData->pu8LanBoot)
1014 {
1015 MMR3HeapFree(pData->pu8LanBoot);
1016 pData->pu8LanBoot = NULL;
1017 }
1018
1019 if (pData->pszLanBootFile)
1020 {
1021 MMR3HeapFree(pData->pszLanBootFile);
1022 pData->pszLanBootFile = NULL;
1023 }
1024
1025 if (pData->pu8Logo)
1026 {
1027 MMR3HeapFree(pData->pu8Logo);
1028 pData->pu8Logo = NULL;
1029 }
1030
1031 if (pData->pszLogoFile)
1032 {
1033 MMR3HeapFree(pData->pszLogoFile);
1034 pData->pszLogoFile = NULL;
1035 }
1036
1037 return VINF_SUCCESS;
1038}
1039
1040
1041/**
1042 * Convert config value to DEVPCBIOSBOOT.
1043 *
1044 * @returns VBox status code.
1045 * @param pCfgHandle Configuration handle.
1046 * @param pszParam The name of the value to read.
1047 * @param penmBoot Where to store the boot method.
1048 */
1049static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfgHandle, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1050{
1051 char *psz;
1052 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszParam, &psz);
1053 if (VBOX_FAILURE(rc))
1054 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1055 N_("Configuration error: Querying \"%s\" as a string failed"),
1056 pszParam);
1057 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
1058 *penmBoot = DEVPCBIOSBOOT_DVD;
1059 else if (!strcmp(psz, "IDE"))
1060 *penmBoot = DEVPCBIOSBOOT_HD;
1061 else if (!strcmp(psz, "FLOPPY"))
1062 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1063 else if (!strcmp(psz, "LAN"))
1064 *penmBoot = DEVPCBIOSBOOT_LAN;
1065 else if (!strcmp(psz, "NONE"))
1066 *penmBoot = DEVPCBIOSBOOT_NONE;
1067 else
1068 {
1069 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1070 N_("Configuration error: The \"%s\" value \"%s\" is unknown.\n"),
1071 pszParam, psz);
1072 rc = VERR_INTERNAL_ERROR;
1073 }
1074 MMR3HeapFree(psz);
1075 return rc;
1076}
1077
1078/**
1079 * Construct a device instance for a VM.
1080 *
1081 * @returns VBox status.
1082 * @param pDevIns The device instance data.
1083 * If the registration structure is needed, pDevIns->pDevReg points to it.
1084 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1085 * The device number is also found in pDevIns->iInstance, but since it's
1086 * likely to be freqently used PDM passes it as parameter.
1087 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1088 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1089 * iInstance it's expected to be used a bit in this function.
1090 */
1091static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1092{
1093 unsigned i;
1094 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
1095 int rc;
1096 int cb;
1097
1098 Assert(iInstance == 0);
1099
1100 /*
1101 * Validate configuration.
1102 */
1103 if (!CFGMR3AreValuesValid(pCfgHandle,
1104 "BootDevice0\0"
1105 "BootDevice1\0"
1106 "BootDevice2\0"
1107 "BootDevice3\0"
1108 "RamSize\0"
1109 "HardDiskDevice\0"
1110 "FloppyDevice\0"
1111 "FadeIn\0"
1112 "FadeOut\0"
1113 "LogoTime\0"
1114 "LogoFile\0"
1115 "ShowBootMenu\0"
1116 "DelayBoot\0"
1117 "LanBootRom\0"
1118 "PXEDebug\0"
1119 "UUID\0"
1120 "IOAPIC\0"))
1121 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1122 N_("Invalid configuraton for device pcbios device"));
1123
1124 /*
1125 * Init the data.
1126 */
1127 rc = CFGMR3QueryU64(pCfgHandle, "RamSize", &pData->cbRam);
1128 if (VBOX_FAILURE(rc))
1129 return PDMDEV_SET_ERROR(pDevIns, rc,
1130 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1131
1132 rc = CFGMR3QueryU8 (pCfgHandle, "IOAPIC", &pData->u8IOAPIC);
1133 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1134 pData->u8IOAPIC = 1;
1135 else if (VBOX_FAILURE (rc))
1136 return PDMDEV_SET_ERROR(pDevIns, rc,
1137 N_("Configuration error: Failed to read \"IOAPIC\"."));
1138
1139 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1140 Assert(ELEMENTS(s_apszBootDevices) == ELEMENTS(pData->aenmBootDevice));
1141 for (i = 0; i < ELEMENTS(pData->aenmBootDevice); i++)
1142 {
1143 rc = pcbiosBootFromCfg(pDevIns, pCfgHandle, s_apszBootDevices[i], &pData->aenmBootDevice[i]);
1144 if (VBOX_FAILURE(rc))
1145 return rc;
1146 }
1147
1148 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HardDiskDevice", &pData->pszHDDevice);
1149 if (VBOX_FAILURE(rc))
1150 return PDMDEV_SET_ERROR(pDevIns, rc,
1151 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1152
1153 rc = CFGMR3QueryStringAlloc(pCfgHandle, "FloppyDevice", &pData->pszFDDevice);
1154 if (VBOX_FAILURE(rc))
1155 return PDMDEV_SET_ERROR(pDevIns, rc,
1156 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1157
1158 /*
1159 * Register I/O Ports and PC BIOS.
1160 */
1161 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1162 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1163 if (VBOX_FAILURE(rc))
1164 return rc;
1165 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1166 NULL, NULL, "Bochs PC BIOS - Shutdown");
1167 if (VBOX_FAILURE(rc))
1168 return rc;
1169
1170 /*
1171 * Query the machine's UUID for SMBIOS/DMI use.
1172 */
1173 RTUUID uuid;
1174 rc = CFGMR3QueryBytes(pCfgHandle, "UUID", &uuid, sizeof(uuid));
1175 if (VBOX_FAILURE(rc))
1176 return PDMDEV_SET_ERROR(pDevIns, rc,
1177 N_("Configuration error: Querying \"UUID\" failed"));
1178
1179
1180 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1181 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1182 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1183 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1184 pcbiosPlantDMITable(pData->au8DMIPage, &uuid);
1185 if (pData->u8IOAPIC)
1186 pcbiosPlantMPStable(pDevIns, pData->au8DMIPage + 0x100);
1187
1188 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, 0x1000, pData->au8DMIPage, false /* fShadow */, "DMI tables");
1189 if (VBOX_FAILURE(rc))
1190 return rc;
1191
1192 /*
1193 * Read the PXE debug logging option.
1194 */
1195 rc = CFGMR3QueryU8(pCfgHandle, "PXEDebug", &pData->u8PXEDebug);
1196 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1197 pData->u8PXEDebug = 0;
1198 else if (VBOX_FAILURE(rc))
1199 return PDMDEV_SET_ERROR(pDevIns, rc,
1200 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1201
1202 /*
1203 * Map the BIOS into memory.
1204 * There are two mappings:
1205 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1206 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1207 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1208 */
1209 AssertReleaseMsg(g_cbPcBiosBinary >= _64K, ("g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary));
1210 AssertReleaseMsg(RT_ALIGN_Z(g_cbPcBiosBinary, _64K) == g_cbPcBiosBinary,
1211 ("g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary));
1212 cb = RT_MIN(g_cbPcBiosBinary, 128 * _1K);
1213 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &g_abPcBiosBinary[g_cbPcBiosBinary - cb],
1214 false /* fShadow */, "PC BIOS - 0xfffff");
1215 if (VBOX_FAILURE(rc))
1216 return rc;
1217 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-g_cbPcBiosBinary, g_cbPcBiosBinary, &g_abPcBiosBinary[0],
1218 false /* fShadow */, "PC BIOS - 0xffffffff");
1219 if (VBOX_FAILURE(rc))
1220 return rc;
1221
1222 /*
1223 * Register the BIOS Logo port
1224 */
1225 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, logoIOPortWrite, logoIOPortRead, NULL, NULL, "PC BIOS - Logo port");
1226 if (VBOX_FAILURE(rc))
1227 return rc;
1228
1229 /*
1230 * Construct the logo header.
1231 */
1232 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0 };
1233
1234 rc = CFGMR3QueryU8(pCfgHandle, "FadeIn", &LogoHdr.u8FadeIn);
1235 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1236 LogoHdr.u8FadeIn = 1;
1237 else if (VBOX_FAILURE(rc))
1238 return PDMDEV_SET_ERROR(pDevIns, rc,
1239 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
1240
1241 rc = CFGMR3QueryU8(pCfgHandle, "FadeOut", &LogoHdr.u8FadeOut);
1242 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1243 LogoHdr.u8FadeOut = 1;
1244 else if (VBOX_FAILURE(rc))
1245 return PDMDEV_SET_ERROR(pDevIns, rc,
1246 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
1247
1248 rc = CFGMR3QueryU16(pCfgHandle, "LogoTime", &LogoHdr.u16LogoMillies);
1249 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1250 LogoHdr.u16LogoMillies = 1;
1251 else if (VBOX_FAILURE(rc))
1252 return PDMDEV_SET_ERROR(pDevIns, rc,
1253 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
1254
1255 rc = CFGMR3QueryU8(pCfgHandle, "ShowBootMenu", &LogoHdr.u8ShowBootMenu);
1256 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1257 LogoHdr.u8ShowBootMenu = 0;
1258 else if (VBOX_FAILURE(rc))
1259 return PDMDEV_SET_ERROR(pDevIns, rc,
1260 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
1261
1262 /*
1263 * Get the Logo file name.
1264 */
1265 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LogoFile", &pData->pszLogoFile);
1266 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1267 pData->pszLogoFile = NULL;
1268 else if (VBOX_FAILURE(rc))
1269 return PDMDEV_SET_ERROR(pDevIns, rc,
1270 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
1271 else if (!*pData->pszLogoFile)
1272 {
1273 MMR3HeapFree(pData->pszLogoFile);
1274 pData->pszLogoFile = NULL;
1275 }
1276
1277 /*
1278 * Determine the logo size, open any specified logo file in the process.
1279 */
1280 LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1281 RTFILE FileLogo = NIL_RTFILE;
1282 if (pData->pszLogoFile)
1283 {
1284 rc = RTFileOpen(&FileLogo, pData->pszLogoFile,
1285 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1286 if (VBOX_SUCCESS(rc))
1287 {
1288 uint64_t cbFile;
1289 rc = RTFileGetSize(FileLogo, &cbFile);
1290 if (VBOX_SUCCESS(rc))
1291 {
1292 if ( cbFile > 0
1293 && cbFile < LOGO_MAX_SIZE)
1294 LogoHdr.cbLogo = (uint32_t)cbFile;
1295 else
1296 rc = VERR_TOO_MUCH_DATA;
1297 }
1298 }
1299 if (VBOX_FAILURE(rc))
1300 {
1301 /*
1302 * Ignore failure and fall back to the default logo.
1303 */
1304 LogRel(("pcbiosConstruct: Failed to open logo file '%s', rc=%Vrc!\n", pData->pszLogoFile, rc));
1305 RTFileClose(FileLogo);
1306 FileLogo = NIL_RTFILE;
1307 MMR3HeapFree(pData->pszLogoFile);
1308 pData->pszLogoFile = NULL;
1309 }
1310 }
1311
1312 /*
1313 * Allocate buffer for the logo data.
1314 * RT_MAX() is applied to let us fall back to default logo on read failure.
1315 */
1316 pData->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
1317 pData->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pData->cbLogo, g_cbPcDefBiosLogo + sizeof(LogoHdr)));
1318 if (pData->pu8Logo)
1319 {
1320 /*
1321 * Write the logo header.
1322 */
1323 PLOGOHDR pLogoHdr = (PLOGOHDR)pData->pu8Logo;
1324 *pLogoHdr = LogoHdr;
1325
1326 /*
1327 * Write the logo bitmap.
1328 */
1329 if (pData->pszLogoFile)
1330 {
1331 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
1332 if (VBOX_FAILURE(rc))
1333 {
1334 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", LogoHdr.cbLogo, rc));
1335 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1336 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1337 }
1338 }
1339 else
1340 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1341
1342 /*
1343 * Call reset to set values and stuff.
1344 */
1345 pcbiosReset(pDevIns);
1346 rc = VINF_SUCCESS;
1347 }
1348 else
1349 rc = VERR_NO_MEMORY;
1350
1351 /* cleanup */
1352 if (FileLogo != NIL_RTFILE)
1353 RTFileClose(FileLogo);
1354
1355 /*
1356 * Get the LAN boot ROM file name.
1357 */
1358 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LanBootRom", &pData->pszLanBootFile);
1359 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1360 {
1361 pData->pszLanBootFile = NULL;
1362 rc = VINF_SUCCESS;
1363 }
1364 else if (VBOX_FAILURE(rc))
1365 return PDMDEV_SET_ERROR(pDevIns, rc,
1366 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1367 else if (!*pData->pszLanBootFile)
1368 {
1369 MMR3HeapFree(pData->pszLanBootFile);
1370 pData->pszLanBootFile = NULL;
1371 }
1372
1373 const uint8_t *pu8LanBoot = NULL;
1374 uint64_t cbFileLanBoot;
1375#ifdef VBOX_DO_NOT_LINK_LANBOOT
1376 /*
1377 * Determine the LAN boot ROM size, open specified ROM file in the process.
1378 */
1379 RTFILE FileLanBoot = NIL_RTFILE;
1380 if (pData->pszLanBootFile)
1381 {
1382 rc = RTFileOpen(&FileLanBoot, pData->pszLanBootFile,
1383 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1384 if (VBOX_SUCCESS(rc))
1385 {
1386 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1387 if (VBOX_SUCCESS(rc))
1388 {
1389 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1390 || cbFileLanBoot > _32K)
1391 rc = VERR_TOO_MUCH_DATA;
1392 }
1393 }
1394 if (VBOX_FAILURE(rc))
1395 {
1396 /*
1397 * Ignore failure and fall back to no LAN boot ROM.
1398 */
1399 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Vrc!\n", pData->pszLanBootFile, rc));
1400 RTFileClose(FileLanBoot);
1401 FileLanBoot = NIL_RTFILE;
1402 MMR3HeapFree(pData->pszLanBootFile);
1403 pData->pszLanBootFile = NULL;
1404 }
1405 }
1406
1407 /*
1408 * Get the LAN boot ROM data.
1409 */
1410 if (pData->pszLanBootFile)
1411 {
1412 /*
1413 * Allocate buffer for the LAN boot ROM data.
1414 */
1415 pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1416 pData->pu8LanBoot = pu8LanBoot;
1417 if (pu8LanBoot)
1418 {
1419 rc = RTFileRead(FileLanBoot, pData->pu8LanBoot, cbFileLanBoot, NULL);
1420 if (VBOX_FAILURE(rc))
1421 {
1422 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", cbFileLanBoot, rc));
1423 MMR3HeapFree(pu8LanBoot);
1424 pu8LanBoot = NULL;
1425 pData->pu8LanBoot = NULL;
1426 }
1427 rc = VINF_SUCCESS;
1428 }
1429 else
1430 rc = VERR_NO_MEMORY;
1431 }
1432 else
1433 pData->pu8LanBoot = NULL;
1434
1435 /* cleanup */
1436 if (FileLanBoot != NIL_RTFILE)
1437 RTFileClose(FileLanBoot);
1438
1439#else /* !VBOX_DO_NOT_LINK_LANBOOT */
1440 pData->pu8LanBoot = NULL;
1441 pu8LanBoot = g_abNetBiosBinary;
1442 cbFileLanBoot = g_cbNetBiosBinary;
1443#endif /* !VBOX_DO_NOT_LINK_LANBOOT */
1444
1445 /*
1446 * Map the Network Boot ROM into memory.
1447 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1448 * the (up to) 32 kb ROM image.
1449 */
1450 if (pu8LanBoot)
1451 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbFileLanBoot, pu8LanBoot,
1452 true /* fShadow */, "Net Boot ROM");
1453
1454 rc = CFGMR3QueryU8(pCfgHandle, "DelayBoot", &pData->uBootDelay);
1455 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1456 {
1457 pData->uBootDelay = 0;
1458 rc = VINF_SUCCESS;
1459 }
1460 else
1461 {
1462 if (VBOX_FAILURE(rc))
1463 return PDMDEV_SET_ERROR(pDevIns, rc,
1464 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1465 if (pData->uBootDelay > 15)
1466 pData->uBootDelay = 15;
1467 }
1468
1469 return rc;
1470}
1471
1472
1473/**
1474 * The device registration structure.
1475 */
1476const PDMDEVREG g_DevicePcBios =
1477{
1478 /* u32Version */
1479 PDM_DEVREG_VERSION,
1480 /* szDeviceName */
1481 "pcbios",
1482 /* szGCMod */
1483 "",
1484 /* szR0Mod */
1485 "",
1486 /* pszDescription */
1487 "PC BIOS Device",
1488 /* fFlags */
1489 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1490 /* fClass */
1491 PDM_DEVREG_CLASS_ARCH_BIOS,
1492 /* cMaxInstances */
1493 1,
1494 /* cbInstance */
1495 sizeof(DEVPCBIOS),
1496 /* pfnConstruct */
1497 pcbiosConstruct,
1498 /* pfnDestruct */
1499 pcbiosDestruct,
1500 /* pfnRelocate */
1501 NULL,
1502 /* pfnIOCtl */
1503 NULL,
1504 /* pfnPowerOn */
1505 NULL,
1506 /* pfnReset */
1507 pcbiosReset,
1508 /* pfnSuspend */
1509 NULL,
1510 /* pfnResume */
1511 NULL,
1512 /* pfnAttach */
1513 NULL,
1514 /* pfnDetach */
1515 NULL,
1516 /* pfnQueryInterface. */
1517 NULL,
1518 /* pfnInitComplete. */
1519 pcbiosInitComplete
1520};
1521
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