VirtualBox

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

Last change on this file since 18880 was 18663, checked in by vboxsync, 16 years ago

Devices: Clean out the VBOX_WITH_NEW_PHYS_CODE #ifdefs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 64.0 KB
Line 
1/* $Id: DevPcBios.cpp 18663 2009-04-02 19:08:14Z vboxsync $ */
2/** @file
3 * PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_DEV_PC_BIOS
26#include <VBox/pdmdev.h>
27#include <VBox/mm.h>
28#include <VBox/pgm.h>
29
30#include <VBox/log.h>
31#include <iprt/assert.h>
32#include <iprt/alloc.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <iprt/uuid.h>
36#include <VBox/err.h>
37#include <VBox/param.h>
38
39#include "../Builtins.h"
40#include "../Builtins2.h"
41#include "DevPcBios.h"
42
43
44/** @page pg_devbios_cmos_assign CMOS Assignments (BIOS)
45 *
46 * The BIOS uses a CMOS to store configuration data.
47 * It is currently used as follows:
48 *
49 * @verbatim
50 Base memory:
51 0x15
52 0x16
53 Extended memory:
54 0x17
55 0x18
56 0x30
57 0x31
58 Amount of memory above 16M and below 4GB in 64KB units:
59 0x34
60 0x35
61 Boot device (BOCHS bios specific):
62 0x3d
63 0x38
64 0x3c
65 PXE debug:
66 0x3f
67 Floppy drive type:
68 0x10
69 Equipment byte:
70 0x14
71 First HDD:
72 0x19
73 0x1e - 0x25
74 Second HDD:
75 0x1a
76 0x26 - 0x2d
77 Third HDD:
78 0x67 - 0x6e
79 Fourth HDD:
80 0x70 - 0x77
81 Extended:
82 0x12
83 First Sata HDD:
84 0x40 - 0x47
85 Second Sata HDD:
86 0x48 - 0x4f
87 Third Sata HDD:
88 0x50 - 0x57
89 Fourth Sata HDD:
90 0x58 - 0x5f
91 Number of CPUs:
92 0x60
93 RAM above 4G in 64KB units:
94 0x61 - 0x65
95@endverbatim
96 *
97 * @todo Mark which bits are compatible with which BIOSes and
98 * which are our own definitions.
99 *
100 * @todo r=bird: Is the 0x61 - 0x63 range defined by AMI,
101 * PHOENIX or AWARD? If not I'd say 64MB units is a bit
102 * too big, besides it forces unnecessary math stuff onto
103 * the BIOS.
104 * nike: The way how values encoded are defined by Bochs/QEmu BIOS,
105 * although for them position in CMOS is different:
106 * 0x5b - 0x5c: RAM above 4G
107 * 0x5f: number of CPUs
108 * Unfortunately for us those positions in our CMOS are already taken
109 * by 4th SATA drive configuration.
110 *
111 */
112
113
114/*******************************************************************************
115* Structures and Typedefs *
116*******************************************************************************/
117
118/**
119 * The boot device.
120 */
121typedef enum DEVPCBIOSBOOT
122{
123 DEVPCBIOSBOOT_NONE,
124 DEVPCBIOSBOOT_FLOPPY,
125 DEVPCBIOSBOOT_HD,
126 DEVPCBIOSBOOT_DVD,
127 DEVPCBIOSBOOT_LAN
128} DEVPCBIOSBOOT;
129
130/**
131 * PC Bios instance data structure.
132 */
133typedef struct DEVPCBIOS
134{
135 /** Pointer back to the device instance. */
136 PPDMDEVINS pDevIns;
137
138 /** Boot devices (ordered). */
139 DEVPCBIOSBOOT aenmBootDevice[4];
140 /** RAM size (in bytes). */
141 uint64_t cbRam;
142 /** RAM hole size (in bytes). */
143 uint32_t cbRamHole;
144 /** Bochs shutdown index. */
145 uint32_t iShutdown;
146 /** Floppy device. */
147 char *pszFDDevice;
148 /** Harddisk device. */
149 char *pszHDDevice;
150 /** Sata harddisk device. */
151 char *pszSataDevice;
152 /** LUN of the four harddisks which are emulated as IDE. */
153 uint32_t iSataHDLUN[4];
154 /** Bios message buffer. */
155 char szMsg[256];
156 /** Bios message buffer index. */
157 uint32_t iMsg;
158 /** The system BIOS ROM data. */
159 uint8_t *pu8PcBios;
160 /** The size of the system BIOS ROM. */
161 uint64_t cbPcBios;
162 /** The name of the BIOS ROM file. */
163 char *pszPcBiosFile;
164 /** The LAN boot ROM data. */
165 uint8_t *pu8LanBoot;
166 /** The name of the LAN boot ROM file. */
167 char *pszLanBootFile;
168 /** The size of the LAN boot ROM. */
169 uint64_t cbLanBoot;
170 /** The DMI tables. */
171 uint8_t au8DMIPage[0x1000];
172 /** The boot countdown (in seconds). */
173 uint8_t uBootDelay;
174 /** I/O-APIC enabled? */
175 uint8_t u8IOAPIC;
176 /** PXE debug logging enabled? */
177 uint8_t u8PXEDebug;
178 /** Number of logical CPUs in guest */
179 uint16_t cCpus;
180} DEVPCBIOS, *PDEVPCBIOS;
181
182#pragma pack(1)
183
184/** DMI header */
185typedef struct DMIHDR
186{
187 uint8_t u8Type;
188 uint8_t u8Length;
189 uint16_t u16Handle;
190} *PDMIHDR;
191AssertCompileSize(DMIHDR, 4);
192
193/** DMI BIOS information */
194typedef struct DMIBIOSINF
195{
196 DMIHDR header;
197 uint8_t u8Vendor;
198 uint8_t u8Version;
199 uint16_t u16Start;
200 uint8_t u8Release;
201 uint8_t u8ROMSize;
202 uint64_t u64Characteristics;
203 uint8_t u8CharacteristicsByte1;
204 uint8_t u8CharacteristicsByte2;
205 uint8_t u8ReleaseMajor;
206 uint8_t u8ReleaseMinor;
207 uint8_t u8FirmwareMajor;
208 uint8_t u8FirmwareMinor;
209} *PDMIBIOSINF;
210AssertCompileSize(DMIBIOSINF, 0x18);
211
212/** DMI system information */
213typedef struct DMISYSTEMINF
214{
215 DMIHDR header;
216 uint8_t u8Manufacturer;
217 uint8_t u8ProductName;
218 uint8_t u8Version;
219 uint8_t u8SerialNumber;
220 uint8_t au8Uuid[16];
221 uint8_t u8WakeupType;
222 uint8_t u8SKUNumber;
223 uint8_t u8Family;
224} *PDMISYSTEMINF;
225AssertCompileSize(DMISYSTEMINF, 0x1b);
226
227/** MPS floating pointer structure */
228typedef struct MPSFLOATPTR
229{
230 uint8_t au8Signature[4];
231 uint32_t u32MPSAddr;
232 uint8_t u8Length;
233 uint8_t u8SpecRev;
234 uint8_t u8Checksum;
235 uint8_t au8Feature[5];
236} *PMPSFLOATPTR;
237AssertCompileSize(MPSFLOATPTR, 16);
238
239/** MPS config table header */
240typedef struct MPSCFGTBLHEADER
241{
242 uint8_t au8Signature[4];
243 uint16_t u16Length;
244 uint8_t u8SpecRev;
245 uint8_t u8Checksum;
246 uint8_t au8OemId[8];
247 uint8_t au8ProductId[12];
248 uint32_t u32OemTablePtr;
249 uint16_t u16OemTableSize;
250 uint16_t u16EntryCount;
251 uint32_t u32AddrLocalApic;
252 uint16_t u16ExtTableLength;
253 uint8_t u8ExtTableChecksxum;
254 uint8_t u8Reserved;
255} *PMPSCFGTBLHEADER;
256AssertCompileSize(MPSCFGTBLHEADER, 0x2c);
257
258/** MPS processor entry */
259typedef struct MPSPROCENTRY
260{
261 uint8_t u8EntryType;
262 uint8_t u8LocalApicId;
263 uint8_t u8LocalApicVersion;
264 uint8_t u8CPUFlags;
265 uint32_t u32CPUSignature;
266 uint32_t u32CPUFeatureFlags;
267 uint32_t u32Reserved[2];
268} *PMPSPROCENTRY;
269AssertCompileSize(MPSPROCENTRY, 20);
270
271/** MPS bus entry */
272typedef struct MPSBUSENTRY
273{
274 uint8_t u8EntryType;
275 uint8_t u8BusId;
276 uint8_t au8BusTypeStr[6];
277} *PMPSBUSENTRY;
278AssertCompileSize(MPSBUSENTRY, 8);
279
280/** MPS I/O-APIC entry */
281typedef struct MPSIOAPICENTRY
282{
283 uint8_t u8EntryType;
284 uint8_t u8Id;
285 uint8_t u8Version;
286 uint8_t u8Flags;
287 uint32_t u32Addr;
288} *PMPSIOAPICENTRY;
289AssertCompileSize(MPSIOAPICENTRY, 8);
290
291/** MPS I/O-Interrupt entry */
292typedef struct MPSIOINTERRUPTENTRY
293{
294 uint8_t u8EntryType;
295 uint8_t u8Type;
296 uint16_t u16Flags;
297 uint8_t u8SrcBusId;
298 uint8_t u8SrcBusIrq;
299 uint8_t u8DstIOAPICId;
300 uint8_t u8DstIOAPICInt;
301} *PMPSIOIRQENTRY;
302AssertCompileSize(MPSIOINTERRUPTENTRY, 8);
303
304#pragma pack()
305
306/* Attempt to guess the LCHS disk geometry from the MS-DOS master boot
307 * record (partition table). */
308static int biosGuessDiskLCHS(PPDMIBLOCK pBlock, PPDMMEDIAGEOMETRY pLCHSGeometry)
309{
310 uint8_t aMBR[512], *p;
311 int rc;
312 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
313
314 if (!pBlock)
315 return VERR_INVALID_PARAMETER;
316 rc = pBlock->pfnRead(pBlock, 0, aMBR, sizeof(aMBR));
317 if (RT_FAILURE(rc))
318 return rc;
319 /* Test MBR magic number. */
320 if (aMBR[510] != 0x55 || aMBR[511] != 0xaa)
321 return VERR_INVALID_PARAMETER;
322 for (uint32_t i = 0; i < 4; i++)
323 {
324 /* Figure out the start of a partition table entry. */
325 p = &aMBR[0x1be + i * 16];
326 iEndHead = p[5];
327 iEndSector = p[6] & 63;
328 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector & iEndHead)
329 {
330 /* Assumption: partition terminates on a cylinder boundary. */
331 cLCHSHeads = iEndHead + 1;
332 cLCHSSectors = iEndSector;
333 cLCHSCylinders = RT_MIN(1024, pBlock->pfnGetSize(pBlock) / (512 * cLCHSHeads * cLCHSSectors));
334 if (cLCHSCylinders >= 1)
335 {
336 pLCHSGeometry->cCylinders = cLCHSCylinders;
337 pLCHSGeometry->cHeads = cLCHSHeads;
338 pLCHSGeometry->cSectors = cLCHSSectors;
339 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
340 return VINF_SUCCESS;
341 }
342 }
343 }
344 return VERR_INVALID_PARAMETER;
345}
346
347
348/**
349 * Write to CMOS memory.
350 * This is used by the init complete code.
351 */
352static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
353{
354 Assert(off < 128);
355 Assert(u32Val < 256);
356
357#if 1
358 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
359 AssertRC(rc);
360#else
361 PVM pVM = PDMDevHlpGetVM(pDevIns);
362 IOMIOPortWrite(pVM, 0x70, off, 1);
363 IOMIOPortWrite(pVM, 0x71, u32Val, 1);
364 IOMIOPortWrite(pVM, 0x70, 0, 1);
365#endif
366}
367
368/* -=-=-=-=-=-=- based on code from pc.c -=-=-=-=-=-=- */
369
370/**
371 * Initializes the CMOS data for one harddisk.
372 */
373static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
374{
375 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
376 if (offType)
377 pcbiosCmosWrite(pDevIns, offType, 48);
378 /* Cylinders low */
379 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(pLCHSGeometry->cCylinders, 1024) & 0xff);
380 /* Cylinders high */
381 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(pLCHSGeometry->cCylinders, 1024) >> 8);
382 /* Heads */
383 pcbiosCmosWrite(pDevIns, offInfo + 2, pLCHSGeometry->cHeads);
384 /* Landing zone low */
385 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
386 /* Landing zone high */
387 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
388 /* Write precomp low */
389 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
390 /* Write precomp high */
391 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
392 /* Sectors */
393 pcbiosCmosWrite(pDevIns, offInfo + 7, pLCHSGeometry->cSectors);
394}
395
396/**
397 * Set logical CHS geometry for a hard disk
398 *
399 * @returns VBox status code.
400 * @param pBase Base interface for the device.
401 * @param pHardDisk The hard disk.
402 * @param pLCHSGeometry Where to store the geometry settings.
403 */
404static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIBLOCKBIOS pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
405{
406 PDMMEDIAGEOMETRY LCHSGeometry;
407 int rc = VINF_SUCCESS;
408
409 rc = pHardDisk->pfnGetLCHSGeometry(pHardDisk, &LCHSGeometry);
410 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
411 || LCHSGeometry.cCylinders == 0
412 || LCHSGeometry.cHeads == 0
413 || LCHSGeometry.cHeads > 255
414 || LCHSGeometry.cSectors == 0
415 || LCHSGeometry.cSectors > 63)
416 {
417 PPDMIBLOCK pBlock;
418 pBlock = (PPDMIBLOCK)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK);
419 /* No LCHS geometry, autodetect and set. */
420 rc = biosGuessDiskLCHS(pBlock, &LCHSGeometry);
421 if (RT_FAILURE(rc))
422 {
423 /* Try if PCHS geometry works, otherwise fall back. */
424 rc = pHardDisk->pfnGetPCHSGeometry(pHardDisk, &LCHSGeometry);
425 }
426 if ( RT_FAILURE(rc)
427 || LCHSGeometry.cCylinders == 0
428 || LCHSGeometry.cCylinders > 1024
429 || LCHSGeometry.cHeads == 0
430 || LCHSGeometry.cHeads > 16
431 || LCHSGeometry.cSectors == 0
432 || LCHSGeometry.cSectors > 63)
433 {
434 uint64_t cSectors = pBlock->pfnGetSize(pBlock) / 512;
435 if (cSectors / 16 / 63 <= 1024)
436 {
437 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
438 LCHSGeometry.cHeads = 16;
439 }
440 else if (cSectors / 32 / 63 <= 1024)
441 {
442 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
443 LCHSGeometry.cHeads = 32;
444 }
445 else if (cSectors / 64 / 63 <= 1024)
446 {
447 LCHSGeometry.cCylinders = cSectors / 64 / 63;
448 LCHSGeometry.cHeads = 64;
449 }
450 else if (cSectors / 128 / 63 <= 1024)
451 {
452 LCHSGeometry.cCylinders = cSectors / 128 / 63;
453 LCHSGeometry.cHeads = 128;
454 }
455 else
456 {
457 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
458 LCHSGeometry.cHeads = 255;
459 }
460 LCHSGeometry.cSectors = 63;
461
462 }
463 rc = pHardDisk->pfnSetLCHSGeometry(pHardDisk, &LCHSGeometry);
464 if (rc == VERR_VD_IMAGE_READ_ONLY)
465 {
466 LogRel(("DevPcBios: ATA failed to update LCHS geometry, read only\n"));
467 rc = VINF_SUCCESS;
468 }
469 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
470 {
471 LogRel(("DevPcBios: ATA failed to update LCHS geometry, backend refused\n"));
472 rc = VINF_SUCCESS;
473 }
474 }
475
476 *pLCHSGeometry = LCHSGeometry;
477
478 return rc;
479}
480
481/**
482 * Get BIOS boot code from enmBootDevice in order
483 *
484 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
485 */
486static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
487{
488 switch (pThis->aenmBootDevice[iOrder])
489 {
490 case DEVPCBIOSBOOT_NONE:
491 return 0;
492 case DEVPCBIOSBOOT_FLOPPY:
493 return 1;
494 case DEVPCBIOSBOOT_HD:
495 return 2;
496 case DEVPCBIOSBOOT_DVD:
497 return 3;
498 case DEVPCBIOSBOOT_LAN:
499 return 4;
500 default:
501 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
502 return 0;
503 }
504}
505
506
507/**
508 * Init complete notification.
509 * This routine will write information needed by the bios to the CMOS.
510 *
511 * @returns VBOX status code.
512 * @param pDevIns The device instance.
513 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
514 * a description of standard and non-standard CMOS registers.
515 */
516static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
517{
518 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
519 uint32_t u32;
520 unsigned i;
521 PVM pVM = PDMDevHlpGetVM(pDevIns);
522 PPDMIBLOCKBIOS apHDs[4] = {0};
523 PPDMIBLOCKBIOS apFDs[2] = {0};
524 AssertRelease(pVM);
525 LogFlow(("pcbiosInitComplete:\n"));
526
527 /*
528 * Memory sizes.
529 */
530 /* base memory. */
531 u32 = pThis->cbRam > 640 ? 640 : (uint32_t)pThis->cbRam / _1K; /* <-- this test is wrong, but it doesn't matter since we never assign less than 1MB */
532 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
533 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
534
535 /* Extended memory, up to 65MB */
536 u32 = pThis->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pThis->cbRam - _1M) / _1K;
537 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
538 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
539 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
540 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
541
542 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
543 and below 4GB (as it can only hold 4GB+16M). We have to chop off the
544 top 2MB or it conflict with what the ACPI tables return. (Should these
545 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
546 with the high BIOS mapping.) */
547 uint64_t const offRamHole = _4G - pThis->cbRamHole;
548 if (pThis->cbRam > 16 * _1M)
549 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
550 else
551 u32 = 0;
552 pcbiosCmosWrite(pDevIns, 0x34, u32 & 0xff);
553 pcbiosCmosWrite(pDevIns, 0x35, u32 >> 8);
554
555 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
556 Bochs got these in a different location which we've already used for SATA,
557 it also lacks the last two. */
558 uint64_t c64KBAbove4GB;
559 if (pThis->cbRam <= offRamHole)
560 c64KBAbove4GB = 0;
561 else
562 {
563 c64KBAbove4GB = (pThis->cbRam - offRamHole) / _64K;
564 /* Make sure it doesn't hit the limits of the current BIOS code. */
565 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
566 }
567 pcbiosCmosWrite(pDevIns, 0x61, c64KBAbove4GB & 0xff);
568 pcbiosCmosWrite(pDevIns, 0x62, (c64KBAbove4GB >> 8) & 0xff);
569 pcbiosCmosWrite(pDevIns, 0x63, (c64KBAbove4GB >> 16) & 0xff);
570 pcbiosCmosWrite(pDevIns, 0x64, (c64KBAbove4GB >> 24) & 0xff);
571 pcbiosCmosWrite(pDevIns, 0x65, (c64KBAbove4GB >> 32) & 0xff);
572
573 /*
574 * Number of CPUs.
575 */
576 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
577
578 /*
579 * Bochs BIOS specifics - boot device.
580 * We do both new and old (ami-style) settings.
581 * See rombios.c line ~7215 (int19_function).
582 */
583
584 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
585 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
586 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
587 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
588 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
589 pcbiosCmosWrite(pDevIns, 0x38, reg38);
590 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
591
592 /*
593 * PXE debug option.
594 */
595 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
596
597 /*
598 * Floppy drive type.
599 */
600 for (i = 0; i < RT_ELEMENTS(apFDs); i++)
601 {
602 PPDMIBASE pBase;
603 int rc = PDMR3QueryLun(pVM, pThis->pszFDDevice, 0, i, &pBase);
604 if (RT_SUCCESS(rc))
605 apFDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
606 }
607 u32 = 0;
608 if (apFDs[0])
609 switch (apFDs[0]->pfnGetType(apFDs[0]))
610 {
611 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << 4; break;
612 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << 4; break;
613 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << 4; break;
614 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << 4; break;
615 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << 4; break;
616 default: AssertFailed(); break;
617 }
618 if (apFDs[1])
619 switch (apFDs[1]->pfnGetType(apFDs[1]))
620 {
621 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1; break;
622 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2; break;
623 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3; break;
624 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4; break;
625 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5; break;
626 default: AssertFailed(); break;
627 }
628 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
629
630 /*
631 * Equipment byte.
632 */
633 u32 = !!apFDs[0] + !!apFDs[1];
634 switch (u32)
635 {
636 case 1: u32 = 0x01; break; /* floppy installed, 2 drives. */
637 default:u32 = 0; break; /* floppy not installed. */
638 }
639 u32 |= RT_BIT(1); /* math coprocessor installed */
640 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
641 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
642 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
643
644 /*
645 * Harddisks.
646 */
647 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
648 {
649 PPDMIBASE pBase;
650 int rc = PDMR3QueryLun(pVM, pThis->pszHDDevice, 0, i, &pBase);
651 if (RT_SUCCESS(rc))
652 apHDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
653 if ( apHDs[i]
654 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
655 || !apHDs[i]->pfnIsVisible(apHDs[i])))
656 apHDs[i] = NULL;
657 if (apHDs[i])
658 {
659 PDMMEDIAGEOMETRY LCHSGeometry;
660 int rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
661 AssertRC(rc);
662
663 if (i < 4)
664 {
665 /* Award BIOS extended drive types for first to fourth disk.
666 * Used by the BIOS for setting the logical geometry. */
667 int offType, offInfo;
668 switch (i)
669 {
670 case 0:
671 offType = 0x19;
672 offInfo = 0x1e;
673 break;
674 case 1:
675 offType = 0x1a;
676 offInfo = 0x26;
677 break;
678 case 2:
679 offType = 0x00;
680 offInfo = 0x67;
681 break;
682 case 3:
683 default:
684 offType = 0x00;
685 offInfo = 0x70;
686 break;
687 }
688 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo,
689 &LCHSGeometry);
690 }
691 LogRel(("DevPcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
692 }
693 }
694
695 /* 0Fh means extended and points to 19h, 1Ah */
696 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
697 pcbiosCmosWrite(pDevIns, 0x12, u32);
698
699 /*
700 * Sata Harddisks.
701 */
702 if (pThis->pszSataDevice)
703 {
704 /* Clear pointers to IDE controller. */
705 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
706 apHDs[i] = NULL;
707
708 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
709 {
710 PPDMIBASE pBase;
711 int rc = PDMR3QueryLun(pVM, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
712 if (RT_SUCCESS(rc))
713 apHDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
714 if ( apHDs[i]
715 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
716 || !apHDs[i]->pfnIsVisible(apHDs[i])))
717 apHDs[i] = NULL;
718 if (apHDs[i])
719 {
720 PDMMEDIAGEOMETRY LCHSGeometry;
721 int rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
722 AssertRC(rc);
723
724 if (i < 4)
725 {
726 /* Award BIOS extended drive types for first to fourth disk.
727 * Used by the BIOS for setting the logical geometry. */
728 int offInfo;
729 switch (i)
730 {
731 case 0:
732 offInfo = 0x40;
733 break;
734 case 1:
735 offInfo = 0x48;
736 break;
737 case 2:
738 offInfo = 0x50;
739 break;
740 case 3:
741 default:
742 offInfo = 0x58;
743 break;
744 }
745 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo,
746 &LCHSGeometry);
747 }
748 LogRel(("DevPcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
749 }
750 }
751 }
752
753 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
754 return VINF_SUCCESS;
755}
756
757
758/**
759 * Port I/O Handler for IN operations.
760 *
761 * @returns VBox status code.
762 *
763 * @param pDevIns The device instance.
764 * @param pvUser User argument - ignored.
765 * @param Port Port number used for the IN operation.
766 * @param pu32 Where to store the result.
767 * @param cb Number of bytes read.
768 */
769static DECLCALLBACK(int) pcbiosIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
770{
771 NOREF(pDevIns);
772 NOREF(pvUser);
773 NOREF(Port);
774 NOREF(pu32);
775 NOREF(cb);
776 return VERR_IOM_IOPORT_UNUSED;
777}
778
779
780/**
781 * Port I/O Handler for OUT operations.
782 *
783 * @returns VBox status code.
784 *
785 * @param pDevIns The device instance.
786 * @param pvUser User argument - ignored.
787 * @param Port Port number used for the IN operation.
788 * @param u32 The value to output.
789 * @param cb The value size in bytes.
790 */
791static DECLCALLBACK(int) pcbiosIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
792{
793 /*
794 * Bochs BIOS Panic
795 */
796 if ( cb == 2
797 && ( Port == 0x400
798 || Port == 0x401))
799 {
800 Log(("pcbios: PC BIOS panic at rombios.c(%d)\n", u32));
801 AssertReleaseMsgFailed(("PC BIOS panic at rombios.c(%d)\n", u32));
802 return VERR_INTERNAL_ERROR;
803 }
804
805 /*
806 * Bochs BIOS char printing.
807 */
808 if ( cb == 1
809 && ( Port == 0x402
810 || Port == 0x403))
811 {
812 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
813 /* The raw version. */
814 switch (u32)
815 {
816 case '\r': Log2(("pcbios: <return>\n")); break;
817 case '\n': Log2(("pcbios: <newline>\n")); break;
818 case '\t': Log2(("pcbios: <tab>\n")); break;
819 default: Log2(("pcbios: %c (%02x)\n", u32, u32)); break;
820 }
821
822 /* The readable, buffered version. */
823 if (u32 == '\n' || u32 == '\r')
824 {
825 pThis->szMsg[pThis->iMsg] = '\0';
826 if (pThis->iMsg)
827 Log(("pcbios: %s\n", pThis->szMsg));
828 pThis->iMsg = 0;
829 }
830 else
831 {
832 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
833 {
834 pThis->szMsg[pThis->iMsg] = '\0';
835 Log(("pcbios: %s\n", pThis->szMsg));
836 pThis->iMsg = 0;
837 }
838 pThis->szMsg[pThis->iMsg] = (char )u32;
839 pThis->szMsg[++pThis->iMsg] = '\0';
840 }
841 return VINF_SUCCESS;
842 }
843
844 /*
845 * Bochs BIOS shutdown request.
846 */
847 if (cb == 1 && Port == 0x8900)
848 {
849 static const unsigned char szShutdown[] = "Shutdown";
850 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
851 if (u32 == szShutdown[pThis->iShutdown])
852 {
853 pThis->iShutdown++;
854 if (pThis->iShutdown == 8)
855 {
856 pThis->iShutdown = 0;
857 LogRel(("8900h shutdown request.\n"));
858 return PDMDevHlpVMPowerOff(pDevIns);
859 }
860 }
861 else
862 pThis->iShutdown = 0;
863 return VINF_SUCCESS;
864 }
865
866 /* not in use. */
867 return VINF_SUCCESS;
868}
869
870
871/**
872 * Construct the DMI table.
873 *
874 * @returns VBox status code.
875 * @param pDevIns The device instance.
876 * @param pTable Where to create the DMI table.
877 * @param cbMax The max size of the DMI table.
878 * @param pUuid Pointer to the UUID to use if the DmiUuid
879 * configuration string isn't present.
880 * @param pCfgHandle The handle to our config node.
881 */
882static int pcbiosPlantDMITable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, PRTUUID pUuid, PCFGMNODE pCfgHandle)
883{
884 char *pszStr = (char *)pTable;
885 int iStrNr;
886 int rc;
887 char *pszDmiBIOSVendor, *pszDmiBIOSVersion, *pszDmiBIOSReleaseDate;
888 int iDmiBIOSReleaseMajor, iDmiBIOSReleaseMinor, iDmiBIOSFirmwareMajor, iDmiBIOSFirmwareMinor;
889 char *pszDmiSystemVendor, *pszDmiSystemProduct, *pszDmiSystemVersion, *pszDmiSystemSerial, *pszDmiSystemUuid, *pszDmiSystemFamily;
890
891#define SETSTRING(memb, str) \
892 do { \
893 if (!str[0]) \
894 memb = 0; /* empty string */ \
895 else \
896 { \
897 memb = iStrNr++; \
898 size_t _len = strlen(str) + 1; \
899 size_t _max = (size_t)(pszStr + _len - (char *)pTable) + 5; /* +1 for strtab terminator +4 for end-of-table entry */ \
900 if (_max > cbMax) \
901 return PDMDevHlpVMSetError(pDevIns, VERR_TOO_MUCH_DATA, RT_SRC_POS, \
902 N_("One of the DMI strings is too long. Check all bios/Dmi* configuration entries. At least %zu bytes are needed but there is no space for more than %d bytes"), _max, cbMax); \
903 memcpy(pszStr, str, _len); \
904 pszStr += _len; \
905 } \
906 } while (0)
907#define READCFGSTR(name, variable, default_value) \
908 do { \
909 rc = CFGMR3QueryStringAlloc(pCfgHandle, name, & variable); \
910 if (rc == VERR_CFGM_VALUE_NOT_FOUND) \
911 variable = MMR3HeapStrDup(PDMDevHlpGetVM(pDevIns), MM_TAG_CFGM, default_value); \
912 else if (RT_FAILURE(rc)) \
913 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
914 N_("Configuration error: Querying \"" name "\" as a string failed")); \
915 else if (!strcmp(variable, "<EMPTY>")) \
916 variable[0] = '\0'; \
917 } while (0)
918#define READCFGINT(name, variable, default_value) \
919 do { \
920 rc = CFGMR3QueryS32(pCfgHandle, name, & variable); \
921 if (rc == VERR_CFGM_VALUE_NOT_FOUND) \
922 variable = default_value; \
923 else if (RT_FAILURE(rc)) \
924 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
925 N_("Configuration error: Querying \"" name "\" as a Int failed")); \
926 } while (0)
927
928
929 /*
930 * Don't change this information otherwise Windows guests will demand re-activation!
931 */
932 READCFGSTR("DmiBIOSVendor", pszDmiBIOSVendor, "innotek GmbH");
933 READCFGSTR("DmiBIOSVersion", pszDmiBIOSVersion, "VirtualBox");
934 READCFGSTR("DmiBIOSReleaseDate", pszDmiBIOSReleaseDate, "12/01/2006");
935 READCFGINT("DmiBIOSReleaseMajor", iDmiBIOSReleaseMajor, 0);
936 READCFGINT("DmiBIOSReleaseMinor", iDmiBIOSReleaseMinor, 0);
937 READCFGINT("DmiBIOSFirmwareMajor", iDmiBIOSFirmwareMajor, 0);
938 READCFGINT("DmiBIOSFirmwareMinor", iDmiBIOSFirmwareMinor, 0);
939 READCFGSTR("DmiSystemVendor", pszDmiSystemVendor, "innotek GmbH");
940 READCFGSTR("DmiSystemProduct", pszDmiSystemProduct, "VirtualBox");
941 READCFGSTR("DmiSystemVersion", pszDmiSystemVersion, "1.2");
942 READCFGSTR("DmiSystemSerial", pszDmiSystemSerial, "0");
943 rc = CFGMR3QueryStringAlloc(pCfgHandle, "DmiSystemUuid", &pszDmiSystemUuid);
944 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
945 pszDmiSystemUuid = NULL;
946 else if (RT_FAILURE(rc))
947 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
948 N_("Configuration error: Querying \"DmiUuid\" as a string failed"));
949 READCFGSTR("DmiSystemFamily", pszDmiSystemFamily, "Virtual Machine");
950
951 /* DMI BIOS information */
952 PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
953
954 pszStr = (char *)&pBIOSInf->u8ReleaseMajor;
955 pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8ReleaseMajor);
956
957 /* don't set these fields by default for legacy compatibility */
958 if (iDmiBIOSReleaseMajor != 0 || iDmiBIOSReleaseMinor != 0)
959 {
960 pszStr = (char *)&pBIOSInf->u8FirmwareMajor;
961 pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8FirmwareMajor);
962 pBIOSInf->u8ReleaseMajor = iDmiBIOSReleaseMajor;
963 pBIOSInf->u8ReleaseMinor = iDmiBIOSReleaseMinor;
964 if (iDmiBIOSFirmwareMajor != 0 || iDmiBIOSFirmwareMinor != 0)
965 {
966 pszStr = (char *)(pBIOSInf + 1);
967 pBIOSInf->header.u8Length = sizeof(DMIBIOSINF);
968 pBIOSInf->u8FirmwareMajor = iDmiBIOSFirmwareMajor;
969 pBIOSInf->u8FirmwareMinor = iDmiBIOSFirmwareMinor;
970 }
971 }
972
973 iStrNr = 1;
974 pBIOSInf->header.u8Type = 0; /* BIOS Information */
975 pBIOSInf->header.u16Handle = 0x0000;
976 SETSTRING(pBIOSInf->u8Vendor, pszDmiBIOSVendor);
977 SETSTRING(pBIOSInf->u8Version, pszDmiBIOSVersion);
978 pBIOSInf->u16Start = 0xE000;
979 SETSTRING(pBIOSInf->u8Release, pszDmiBIOSReleaseDate);
980 pBIOSInf->u8ROMSize = 1; /* 128K */
981 pBIOSInf->u64Characteristics = RT_BIT(4) /* ISA is supported */
982 | RT_BIT(7) /* PCI is supported */
983 | RT_BIT(15) /* Boot from CD is supported */
984 | RT_BIT(16) /* Selectable Boot is supported */
985 | RT_BIT(27) /* Int 9h, 8042 Keyboard services supported */
986 | RT_BIT(30) /* Int 10h, CGA/Mono Video Services supported */
987 /* any more?? */
988 ;
989 pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0) /* ACPI is supported */
990 /* any more?? */
991 ;
992 pBIOSInf->u8CharacteristicsByte2 = 0
993 /* any more?? */
994 ;
995 *pszStr++ = '\0';
996
997 /* DMI system information */
998 PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
999 pszStr = (char *)(pSystemInf + 1);
1000 iStrNr = 1;
1001 pSystemInf->header.u8Type = 1; /* System Information */
1002 pSystemInf->header.u8Length = sizeof(*pSystemInf);
1003 pSystemInf->header.u16Handle = 0x0001;
1004 SETSTRING(pSystemInf->u8Manufacturer, pszDmiSystemVendor);
1005 SETSTRING(pSystemInf->u8ProductName, pszDmiSystemProduct);
1006 SETSTRING(pSystemInf->u8Version, pszDmiSystemVersion);
1007 SETSTRING(pSystemInf->u8SerialNumber, pszDmiSystemSerial);
1008
1009 RTUUID uuid;
1010 if (pszDmiSystemUuid)
1011 {
1012 int rc = RTUuidFromStr(&uuid, pszDmiSystemUuid);
1013 if (RT_FAILURE(rc))
1014 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1015 N_("Invalid UUID for DMI tables specified"));
1016 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1017 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1018 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1019 pUuid = &uuid;
1020 }
1021 memcpy(pSystemInf->au8Uuid, pUuid, sizeof(RTUUID));
1022
1023 pSystemInf->u8WakeupType = 6; /* Power Switch */
1024 pSystemInf->u8SKUNumber = 0;
1025 SETSTRING(pSystemInf->u8Family, pszDmiSystemFamily);
1026 *pszStr++ = '\0';
1027
1028 /* End-of-table marker - includes padding to account for fixed table size. */
1029 PDMIHDR pEndOfTable = (PDMIHDR)pszStr;
1030 pEndOfTable->u8Type = 0x7f;
1031 pEndOfTable->u8Length = cbMax - ((char *)pszStr - (char *)pTable) - 2;
1032 pEndOfTable->u16Handle = 0xFFFF;
1033
1034 /* If more fields are added here, fix the size check in SETSTRING */
1035
1036#undef SETSTRING
1037#undef READCFG
1038
1039 MMR3HeapFree(pszDmiBIOSVendor);
1040 MMR3HeapFree(pszDmiBIOSVersion);
1041 MMR3HeapFree(pszDmiBIOSReleaseDate);
1042 MMR3HeapFree(pszDmiSystemVendor);
1043 MMR3HeapFree(pszDmiSystemProduct);
1044 MMR3HeapFree(pszDmiSystemVersion);
1045 MMR3HeapFree(pszDmiSystemSerial);
1046 MMR3HeapFree(pszDmiSystemUuid);
1047 MMR3HeapFree(pszDmiSystemFamily);
1048
1049 return VINF_SUCCESS;
1050}
1051AssertCompile(VBOX_DMI_TABLE_ENTR == 3);
1052
1053
1054/**
1055 * Calculate a simple checksum for the MPS table.
1056 *
1057 * @param data data
1058 * @param len size of data
1059 */
1060static uint8_t pcbiosChecksum(const uint8_t * const au8Data, uint32_t u32Length)
1061{
1062 uint8_t u8Sum = 0;
1063 for (size_t i = 0; i < u32Length; ++i)
1064 u8Sum += au8Data[i];
1065 return -u8Sum;
1066}
1067
1068
1069/**
1070 * Construct the MPS table. Only applicable if IOAPIC is active!
1071 *
1072 * See ``MultiProcessor Specificatiton Version 1.4 (May 1997)'':
1073 * ``1.3 Scope
1074 * ...
1075 * The hardware required to implement the MP specification is kept to a
1076 * minimum, as follows:
1077 * * One or more processors that are Intel architecture instruction set
1078 * compatible, such as the CPUs in the Intel486 or Pentium processor
1079 * family.
1080 * * One or more APICs, such as the Intel 82489DX Advanced Programmable
1081 * Interrupt Controller or the integrated APIC, such as that on the
1082 * Intel Pentium 735\90 and 815\100 processors, together with a discrete
1083 * I/O APIC unit.''
1084 * and later:
1085 * ``4.3.3 I/O APIC Entries
1086 * The configuration table contains one or more entries for I/O APICs.
1087 * ...
1088 * I/O APIC FLAGS: EN 3:0 1 If zero, this I/O APIC is unusable, and the
1089 * operating system should not attempt to access
1090 * this I/O APIC.
1091 * At least one I/O APIC must be enabled.''
1092 *
1093 * @param pDevIns The device instance data.
1094 * @param addr physical address in guest memory.
1095 */
1096static void pcbiosPlantMPStable(PPDMDEVINS pDevIns, uint8_t *pTable, uint16_t numCpus)
1097{
1098 /* configuration table */
1099 PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
1100 memcpy(pCfgTab->au8Signature, "PCMP", 4);
1101 pCfgTab->u8SpecRev = 4; /* 1.4 */
1102 memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
1103 memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
1104 pCfgTab->u32OemTablePtr = 0;
1105 pCfgTab->u16OemTableSize = 0;
1106 pCfgTab->u16EntryCount = numCpus /* Processors */
1107 + 1 /* ISA Bus */
1108 + 1 /* I/O-APIC */
1109 + 16 /* Interrupts */;
1110 pCfgTab->u32AddrLocalApic = 0xfee00000;
1111 pCfgTab->u16ExtTableLength = 0;
1112 pCfgTab->u8ExtTableChecksxum = 0;
1113 pCfgTab->u8Reserved = 0;
1114
1115 uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
1116 uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
1117 uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
1118 PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
1119 if (u32Eax >= 1)
1120 {
1121 PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
1122 u32CPUSignature = u32Eax & 0xfff;
1123 /* Local APIC will be enabled later so override it here. Since we provide
1124 * an MP table we have an IOAPIC and therefore a Local APIC. */
1125 u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
1126 }
1127#ifdef VBOX_WITH_SMP_GUESTS
1128 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
1129 for (int i = 0; i<numCpus; i++)
1130 {
1131 pProcEntry->u8EntryType = 0; /* processor entry */
1132 pProcEntry->u8LocalApicId = i;
1133 pProcEntry->u8LocalApicVersion = 0x11;
1134 pProcEntry->u8CPUFlags = (i == 0 ? 2 /* bootstrap processor */ : 0 /* application processor */) | 1 /* enabled */;
1135 pProcEntry->u32CPUSignature = u32CPUSignature;
1136 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
1137 pProcEntry->u32Reserved[0] =
1138 pProcEntry->u32Reserved[1] = 0;
1139 pProcEntry++;
1140 }
1141#else
1142 /* one processor so far */
1143 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
1144 pProcEntry->u8EntryType = 0; /* processor entry */
1145 pProcEntry->u8LocalApicId = 0;
1146 pProcEntry->u8LocalApicVersion = 0x11;
1147 pProcEntry->u8CPUFlags = 2 /* bootstrap processor */ | 1 /* enabled */;
1148 pProcEntry->u32CPUSignature = u32CPUSignature;
1149 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
1150 pProcEntry->u32Reserved[0] =
1151 pProcEntry->u32Reserved[1] = 0;
1152#endif
1153
1154 /* ISA bus */
1155 PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)(pProcEntry+1);
1156 pBusEntry->u8EntryType = 1; /* bus entry */
1157 pBusEntry->u8BusId = 0; /* this ID is referenced by the interrupt entries */
1158 memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
1159
1160 /* PCI bus? */
1161
1162 /* I/O-APIC.
1163 * MP spec: "The configuration table contains one or more entries for I/O APICs.
1164 * ... At least one I/O APIC must be enabled." */
1165 PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1);
1166 uint16_t apicId = numCpus;
1167 pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
1168 pIOAPICEntry->u8Id = apicId; /* this ID is referenced by the interrupt entries */
1169 pIOAPICEntry->u8Version = 0x11;
1170 pIOAPICEntry->u8Flags = 1 /* enable */;
1171 pIOAPICEntry->u32Addr = 0xfec00000;
1172
1173 PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
1174 for (int i = 0; i < 16; i++, pIrqEntry++)
1175 {
1176 pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
1177 pIrqEntry->u8Type = 0; /* INT, vectored interrupt */
1178 pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
1179 trigger mode = conforms to bus */
1180 pIrqEntry->u8SrcBusId = 0; /* ISA bus */
1181 pIrqEntry->u8SrcBusIrq = i;
1182 pIrqEntry->u8DstIOAPICId = apicId;
1183 pIrqEntry->u8DstIOAPICInt = i;
1184 }
1185
1186 pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
1187 pCfgTab->u8Checksum = pcbiosChecksum(pTable, pCfgTab->u16Length);
1188
1189 AssertMsg(pCfgTab->u16Length < 0x1000 - 0x100,
1190 ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
1191 pCfgTab->u16Length, 0x1000-0x100));
1192
1193 MPSFLOATPTR floatPtr;
1194 floatPtr.au8Signature[0] = '_';
1195 floatPtr.au8Signature[1] = 'M';
1196 floatPtr.au8Signature[2] = 'P';
1197 floatPtr.au8Signature[3] = '_';
1198 floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE;
1199 floatPtr.u8Length = 1; /* structure size in paragraphs */
1200 floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
1201 floatPtr.u8Checksum = 0;
1202 floatPtr.au8Feature[0] = 0;
1203 floatPtr.au8Feature[1] = 0;
1204 floatPtr.au8Feature[2] = 0;
1205 floatPtr.au8Feature[3] = 0;
1206 floatPtr.au8Feature[4] = 0;
1207 floatPtr.u8Checksum = pcbiosChecksum((uint8_t*)&floatPtr, 16);
1208 PDMDevHlpPhysWrite (pDevIns, 0x9fff0, &floatPtr, 16);
1209}
1210
1211
1212/**
1213 * Reset notification.
1214 *
1215 * @returns VBox status.
1216 * @param pDevIns The device instance data.
1217 */
1218static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
1219{
1220 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1221 LogFlow(("pcbiosReset:\n"));
1222
1223 if (pThis->u8IOAPIC)
1224 pcbiosPlantMPStable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1225
1226 /*
1227 * Re-shadow the LAN ROM image and make it RAM/RAM.
1228 *
1229 * This is normally done by the BIOS code, but since we're currently lacking
1230 * the chipset support for this we do it here (and in the constructor).
1231 */
1232 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, PAGE_SIZE) >> PAGE_SHIFT;
1233 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
1234 while (cPages > 0)
1235 {
1236 uint8_t abPage[PAGE_SIZE];
1237 int rc;
1238
1239 /* Read the (original) ROM page and write it back to the RAM page. */
1240 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1241 AssertLogRelRC(rc);
1242
1243 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
1244 AssertLogRelRC(rc);
1245 if (RT_FAILURE(rc))
1246 memset(abPage, 0xcc, sizeof(abPage));
1247
1248 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
1249 AssertLogRelRC(rc);
1250
1251 /* Switch to the RAM/RAM mode. */
1252 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1253 AssertLogRelRC(rc);
1254
1255 /* Advance */
1256 GCPhys += PAGE_SIZE;
1257 cPages--;
1258 }
1259}
1260
1261
1262/**
1263 * Destruct a device instance.
1264 *
1265 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1266 * resources can be freed correctly.
1267 *
1268 * @param pDevIns The device instance data.
1269 */
1270static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1271{
1272 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1273 LogFlow(("pcbiosDestruct:\n"));
1274
1275 /*
1276 * Free MM heap pointers.
1277 */
1278 if (pThis->pu8PcBios)
1279 {
1280 MMR3HeapFree(pThis->pu8PcBios);
1281 pThis->pu8PcBios = NULL;
1282 }
1283
1284 if (pThis->pszPcBiosFile)
1285 {
1286 MMR3HeapFree(pThis->pszPcBiosFile);
1287 pThis->pszPcBiosFile = NULL;
1288 }
1289
1290 if (pThis->pu8LanBoot)
1291 {
1292 MMR3HeapFree(pThis->pu8LanBoot);
1293 pThis->pu8LanBoot = NULL;
1294 }
1295
1296 if (pThis->pszLanBootFile)
1297 {
1298 MMR3HeapFree(pThis->pszLanBootFile);
1299 pThis->pszLanBootFile = NULL;
1300 }
1301
1302 return VINF_SUCCESS;
1303}
1304
1305
1306/**
1307 * Convert config value to DEVPCBIOSBOOT.
1308 *
1309 * @returns VBox status code.
1310 * @param pCfgHandle Configuration handle.
1311 * @param pszParam The name of the value to read.
1312 * @param penmBoot Where to store the boot method.
1313 */
1314static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfgHandle, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1315{
1316 char *psz;
1317 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszParam, &psz);
1318 if (RT_FAILURE(rc))
1319 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1320 N_("Configuration error: Querying \"%s\" as a string failed"),
1321 pszParam);
1322 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
1323 *penmBoot = DEVPCBIOSBOOT_DVD;
1324 else if (!strcmp(psz, "IDE"))
1325 *penmBoot = DEVPCBIOSBOOT_HD;
1326 else if (!strcmp(psz, "FLOPPY"))
1327 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1328 else if (!strcmp(psz, "LAN"))
1329 *penmBoot = DEVPCBIOSBOOT_LAN;
1330 else if (!strcmp(psz, "NONE"))
1331 *penmBoot = DEVPCBIOSBOOT_NONE;
1332 else
1333 {
1334 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1335 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
1336 pszParam, psz);
1337 rc = VERR_INTERNAL_ERROR;
1338 }
1339 MMR3HeapFree(psz);
1340 return rc;
1341}
1342
1343/**
1344 * Construct a device instance for a VM.
1345 *
1346 * @returns VBox status.
1347 * @param pDevIns The device instance data.
1348 * If the registration structure is needed, pDevIns->pDevReg points to it.
1349 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1350 * The device number is also found in pDevIns->iInstance, but since it's
1351 * likely to be freqently used PDM passes it as parameter.
1352 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1353 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1354 * iInstance it's expected to be used a bit in this function.
1355 */
1356static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1357{
1358 unsigned i;
1359 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1360 int rc;
1361 int cb;
1362
1363 Assert(iInstance == 0);
1364
1365 /*
1366 * Validate configuration.
1367 */
1368 if (!CFGMR3AreValuesValid(pCfgHandle,
1369 "BootDevice0\0"
1370 "BootDevice1\0"
1371 "BootDevice2\0"
1372 "BootDevice3\0"
1373 "RamSize\0"
1374 "RamHoleSize\0"
1375 "HardDiskDevice\0"
1376 "SataHardDiskDevice\0"
1377 "SataPrimaryMasterLUN\0"
1378 "SataPrimarySlaveLUN\0"
1379 "SataSecondaryMasterLUN\0"
1380 "SataSecondarySlaveLUN\0"
1381 "FloppyDevice\0"
1382 "DelayBoot\0"
1383 "BiosRom\0"
1384 "LanBootRom\0"
1385 "PXEDebug\0"
1386 "UUID\0"
1387 "IOAPIC\0"
1388 "NumCPUs\0"
1389 "DmiBIOSVendor\0"
1390 "DmiBIOSVersion\0"
1391 "DmiBIOSReleaseDate\0"
1392 "DmiBIOSReleaseMajor\0"
1393 "DmiBIOSReleaseMinor\0"
1394 "DmiBIOSFirmwareMajor\0"
1395 "DmiBIOSFirmwareMinor\0"
1396 "DmiSystemFamily\0"
1397 "DmiSystemProduct\0"
1398 "DmiSystemSerial\0"
1399 "DmiSystemUuid\0"
1400 "DmiSystemVendor\0"
1401 "DmiSystemVersion\0"))
1402 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1403 N_("Invalid configuraton for device pcbios device"));
1404
1405 /*
1406 * Init the data.
1407 */
1408 rc = CFGMR3QueryU64(pCfgHandle, "RamSize", &pThis->cbRam);
1409 if (RT_FAILURE(rc))
1410 return PDMDEV_SET_ERROR(pDevIns, rc,
1411 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1412
1413 rc = CFGMR3QueryU32Def(pCfgHandle, "RamHoleSize", &pThis->cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
1414 if (RT_FAILURE(rc))
1415 return PDMDEV_SET_ERROR(pDevIns, rc,
1416 N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
1417
1418 rc = CFGMR3QueryU16Def(pCfgHandle, "NumCPUs", &pThis->cCpus, 1);
1419 if (RT_FAILURE(rc))
1420 return PDMDEV_SET_ERROR(pDevIns, rc,
1421 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1422
1423#ifdef VBOX_WITH_SMP_GUESTS
1424 LogRel(("[SMP] BIOS with %u CPUs\n", pThis->cCpus));
1425#else
1426 /** @todo: move this check up in configuration chain */
1427 if (pThis->cCpus != 1)
1428 {
1429 LogRel(("WARNING: guest SMP not supported in this build, going UP\n"));
1430 pThis->cCpus = 1;
1431 }
1432#endif
1433
1434 rc = CFGMR3QueryU8Def(pCfgHandle, "IOAPIC", &pThis->u8IOAPIC, 1);
1435 if (RT_FAILURE (rc))
1436 return PDMDEV_SET_ERROR(pDevIns, rc,
1437 N_("Configuration error: Failed to read \"IOAPIC\""));
1438
1439 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1440 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1441 for (i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1442 {
1443 rc = pcbiosBootFromCfg(pDevIns, pCfgHandle, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1444 if (RT_FAILURE(rc))
1445 return rc;
1446 }
1447
1448 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HardDiskDevice", &pThis->pszHDDevice);
1449 if (RT_FAILURE(rc))
1450 return PDMDEV_SET_ERROR(pDevIns, rc,
1451 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1452
1453 rc = CFGMR3QueryStringAlloc(pCfgHandle, "FloppyDevice", &pThis->pszFDDevice);
1454 if (RT_FAILURE(rc))
1455 return PDMDEV_SET_ERROR(pDevIns, rc,
1456 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1457
1458 rc = CFGMR3QueryStringAlloc(pCfgHandle, "SataHardDiskDevice", &pThis->pszSataDevice);
1459 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1460 pThis->pszSataDevice = NULL;
1461 else if (RT_FAILURE(rc))
1462 return PDMDEV_SET_ERROR(pDevIns, rc,
1463 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1464
1465 if (pThis->pszSataDevice)
1466 {
1467 static const char * const s_apszSataDisks[] =
1468 { "SataPrimaryMasterLUN", "SataPrimarySlaveLUN", "SataSecondaryMasterLUN", "SataSecondarySlaveLUN" };
1469 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1470 for (i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1471 {
1472 rc = CFGMR3QueryU32(pCfgHandle, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1473 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1474 pThis->iSataHDLUN[i] = i;
1475 else if (RT_FAILURE(rc))
1476 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1477 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1478 }
1479 }
1480 /*
1481 * Register I/O Ports and PC BIOS.
1482 */
1483 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1484 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1485 if (RT_FAILURE(rc))
1486 return rc;
1487 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1488 NULL, NULL, "Bochs PC BIOS - Shutdown");
1489 if (RT_FAILURE(rc))
1490 return rc;
1491
1492 /*
1493 * Query the machine's UUID for SMBIOS/DMI use.
1494 */
1495 RTUUID uuid;
1496 rc = CFGMR3QueryBytes(pCfgHandle, "UUID", &uuid, sizeof(uuid));
1497 if (RT_FAILURE(rc))
1498 return PDMDEV_SET_ERROR(pDevIns, rc,
1499 N_("Configuration error: Querying \"UUID\" failed"));
1500
1501
1502 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1503 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1504 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1505 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1506 rc = pcbiosPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &uuid, pCfgHandle);
1507 if (RT_FAILURE(rc))
1508 return rc;
1509 if (pThis->u8IOAPIC)
1510 pcbiosPlantMPStable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1511
1512 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage,
1513 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1514 if (RT_FAILURE(rc))
1515 return rc;
1516
1517 /*
1518 * Read the PXE debug logging option.
1519 */
1520 rc = CFGMR3QueryU8Def(pCfgHandle, "PXEDebug", &pThis->u8PXEDebug, false);
1521 if (RT_FAILURE(rc))
1522 return PDMDEV_SET_ERROR(pDevIns, rc,
1523 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1524
1525 /*
1526 * Get the system BIOS ROM file name.
1527 */
1528 rc = CFGMR3QueryStringAlloc(pCfgHandle, "BiosRom", &pThis->pszPcBiosFile);
1529 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1530 {
1531 pThis->pszPcBiosFile = NULL;
1532 rc = VINF_SUCCESS;
1533 }
1534 else if (RT_FAILURE(rc))
1535 return PDMDEV_SET_ERROR(pDevIns, rc,
1536 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1537 else if (!*pThis->pszPcBiosFile)
1538 {
1539 MMR3HeapFree(pThis->pszPcBiosFile);
1540 pThis->pszPcBiosFile = NULL;
1541 }
1542
1543 const uint8_t *pu8PcBiosBinary = NULL;
1544 uint64_t cbPcBiosBinary;
1545 /*
1546 * Determine the system BIOS ROM size, open specified ROM file in the process.
1547 */
1548 RTFILE FilePcBios = NIL_RTFILE;
1549 if (pThis->pszPcBiosFile)
1550 {
1551 rc = RTFileOpen(&FilePcBios, pThis->pszPcBiosFile,
1552 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1553 if (RT_SUCCESS(rc))
1554 {
1555 rc = RTFileGetSize(FilePcBios, &pThis->cbPcBios);
1556 if (RT_SUCCESS(rc))
1557 {
1558 /* The following checks should be in sync the AssertReleaseMsg's below. */
1559 if ( RT_ALIGN(pThis->cbPcBios, _64K) != pThis->cbPcBios
1560 || pThis->cbPcBios > 32 * _64K
1561 || pThis->cbPcBios < _64K)
1562 rc = VERR_TOO_MUCH_DATA;
1563 }
1564 }
1565 if (RT_FAILURE(rc))
1566 {
1567 /*
1568 * In case of failure simply fall back to the built-in BIOS ROM.
1569 */
1570 Log(("pcbiosConstruct: Failed to open system BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszPcBiosFile, rc));
1571 RTFileClose(FilePcBios);
1572 FilePcBios = NIL_RTFILE;
1573 MMR3HeapFree(pThis->pszPcBiosFile);
1574 pThis->pszPcBiosFile = NULL;
1575 }
1576 }
1577
1578 /*
1579 * Attempt to get the system BIOS ROM data from file.
1580 */
1581 if (pThis->pszPcBiosFile)
1582 {
1583 /*
1584 * Allocate buffer for the system BIOS ROM data.
1585 */
1586 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1587 if (pThis->pu8PcBios)
1588 {
1589 rc = RTFileRead(FilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1590 if (RT_FAILURE(rc))
1591 {
1592 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbPcBios, rc));
1593 MMR3HeapFree(pThis->pu8PcBios);
1594 pThis->pu8PcBios = NULL;
1595 }
1596 rc = VINF_SUCCESS;
1597 }
1598 else
1599 rc = VERR_NO_MEMORY;
1600 }
1601 else
1602 pThis->pu8PcBios = NULL;
1603
1604 /* cleanup */
1605 if (FilePcBios != NIL_RTFILE)
1606 RTFileClose(FilePcBios);
1607
1608 /* If we were unable to get the data from file for whatever reason, fall
1609 back to the built-in ROM image. */
1610 uint32_t fFlags = 0;
1611 if (pThis->pu8PcBios == NULL)
1612 {
1613 pu8PcBiosBinary = g_abPcBiosBinary;
1614 cbPcBiosBinary = g_cbPcBiosBinary;
1615 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
1616 }
1617 else
1618 {
1619 pu8PcBiosBinary = pThis->pu8PcBios;
1620 cbPcBiosBinary = pThis->cbPcBios;
1621 }
1622
1623 /*
1624 * Map the BIOS into memory.
1625 * There are two mappings:
1626 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1627 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1628 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1629 */
1630 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1631 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1632 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1633 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1634 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb],
1635 fFlags, "PC BIOS - 0xfffff");
1636 if (RT_FAILURE(rc))
1637 return rc;
1638 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary,
1639 fFlags, "PC BIOS - 0xffffffff");
1640 if (RT_FAILURE(rc))
1641 return rc;
1642
1643#ifdef VBOX_WITH_VMI
1644 /*
1645 * Map the VMI BIOS into memory.
1646 */
1647 AssertReleaseMsg(g_cbVmiBiosBinary == _4K, ("cbVmiBiosBinary=%#x\n", g_cbVmiBiosBinary));
1648 rc = PDMDevHlpROMRegister(pDevIns, VBOX_VMI_BIOS_BASE, g_cbVmiBiosBinary, g_abVmiBiosBinary,
1649 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "VMI BIOS");
1650 if (RT_FAILURE(rc))
1651 return rc;
1652#endif /* VBOX_WITH_VMI */
1653
1654 /*
1655 * Call reset to set values and stuff.
1656 */
1657 pcbiosReset(pDevIns);
1658
1659 /*
1660 * Get the LAN boot ROM file name.
1661 */
1662 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LanBootRom", &pThis->pszLanBootFile);
1663 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1664 {
1665 pThis->pszLanBootFile = NULL;
1666 rc = VINF_SUCCESS;
1667 }
1668 else if (RT_FAILURE(rc))
1669 return PDMDEV_SET_ERROR(pDevIns, rc,
1670 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1671 else if (!*pThis->pszLanBootFile)
1672 {
1673 MMR3HeapFree(pThis->pszLanBootFile);
1674 pThis->pszLanBootFile = NULL;
1675 }
1676
1677 uint64_t cbFileLanBoot;
1678 const uint8_t *pu8LanBootBinary = NULL;
1679 uint64_t cbLanBootBinary;
1680
1681 /*
1682 * Determine the LAN boot ROM size, open specified ROM file in the process.
1683 */
1684 RTFILE FileLanBoot = NIL_RTFILE;
1685 if (pThis->pszLanBootFile)
1686 {
1687 rc = RTFileOpen(&FileLanBoot, pThis->pszLanBootFile,
1688 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1689 if (RT_SUCCESS(rc))
1690 {
1691 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1692 if (RT_SUCCESS(rc))
1693 {
1694 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1695 || cbFileLanBoot > _64K)
1696 rc = VERR_TOO_MUCH_DATA;
1697 }
1698 }
1699 if (RT_FAILURE(rc))
1700 {
1701 /*
1702 * Ignore failure and fall back to the built-in LAN boot ROM.
1703 */
1704 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1705 RTFileClose(FileLanBoot);
1706 FileLanBoot = NIL_RTFILE;
1707 MMR3HeapFree(pThis->pszLanBootFile);
1708 pThis->pszLanBootFile = NULL;
1709 }
1710 }
1711
1712 /*
1713 * Get the LAN boot ROM data.
1714 */
1715 if (pThis->pszLanBootFile)
1716 {
1717 /*
1718 * Allocate buffer for the LAN boot ROM data.
1719 */
1720 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1721 if (pThis->pu8LanBoot)
1722 {
1723 rc = RTFileRead(FileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1724 if (RT_FAILURE(rc))
1725 {
1726 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", cbFileLanBoot, rc));
1727 MMR3HeapFree(pThis->pu8LanBoot);
1728 pThis->pu8LanBoot = NULL;
1729 }
1730 rc = VINF_SUCCESS;
1731 }
1732 else
1733 rc = VERR_NO_MEMORY;
1734 }
1735 else
1736 pThis->pu8LanBoot = NULL;
1737
1738 /* cleanup */
1739 if (FileLanBoot != NIL_RTFILE)
1740 RTFileClose(FileLanBoot);
1741
1742 /* If we were unable to get the data from file for whatever reason, fall
1743 * back to the built-in LAN boot ROM image.
1744 */
1745 if (pThis->pu8LanBoot == NULL)
1746 {
1747 pu8LanBootBinary = g_abNetBiosBinary;
1748 cbLanBootBinary = g_cbNetBiosBinary;
1749 }
1750 else
1751 {
1752 pu8LanBootBinary = pThis->pu8LanBoot;
1753 cbLanBootBinary = cbFileLanBoot;
1754 }
1755
1756 /*
1757 * Map the Network Boot ROM into memory.
1758 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1759 * the (up to) 32 kb ROM image.
1760 */
1761 if (pu8LanBootBinary)
1762 {
1763 pThis->cbLanBoot = cbLanBootBinary;
1764
1765 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbLanBootBinary, pu8LanBootBinary,
1766 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1767 if (RT_SUCCESS(rc))
1768 {
1769 rc = PDMDevHlpROMProtectShadow(pDevIns, VBOX_LANBOOT_SEG << 4, cbLanBootBinary, PGMROMPROT_READ_RAM_WRITE_RAM);
1770 AssertRCReturn(rc, rc);
1771 rc = PDMDevHlpPhysWrite(pDevIns, VBOX_LANBOOT_SEG << 4, pu8LanBootBinary, cbLanBootBinary);
1772 AssertRCReturn(rc, rc);
1773 }
1774 }
1775
1776 rc = CFGMR3QueryU8Def(pCfgHandle, "DelayBoot", &pThis->uBootDelay, 0);
1777 if (RT_FAILURE(rc))
1778 return PDMDEV_SET_ERROR(pDevIns, rc,
1779 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1780 if (pThis->uBootDelay > 15)
1781 pThis->uBootDelay = 15;
1782
1783 return rc;
1784}
1785
1786
1787/**
1788 * The device registration structure.
1789 */
1790const PDMDEVREG g_DevicePcBios =
1791{
1792 /* u32Version */
1793 PDM_DEVREG_VERSION,
1794 /* szDeviceName */
1795 "pcbios",
1796 /* szRCMod */
1797 "",
1798 /* szR0Mod */
1799 "",
1800 /* pszDescription */
1801 "PC BIOS Device",
1802 /* fFlags */
1803 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1804 /* fClass */
1805 PDM_DEVREG_CLASS_ARCH_BIOS,
1806 /* cMaxInstances */
1807 1,
1808 /* cbInstance */
1809 sizeof(DEVPCBIOS),
1810 /* pfnConstruct */
1811 pcbiosConstruct,
1812 /* pfnDestruct */
1813 pcbiosDestruct,
1814 /* pfnRelocate */
1815 NULL,
1816 /* pfnIOCtl */
1817 NULL,
1818 /* pfnPowerOn */
1819 NULL,
1820 /* pfnReset */
1821 pcbiosReset,
1822 /* pfnSuspend */
1823 NULL,
1824 /* pfnResume */
1825 NULL,
1826 /* pfnAttach */
1827 NULL,
1828 /* pfnDetach */
1829 NULL,
1830 /* pfnQueryInterface. */
1831 NULL,
1832 /* pfnInitComplete. */
1833 pcbiosInitComplete,
1834 /* pfnPowerOff */
1835 NULL,
1836 /* pfnSoftReset */
1837 NULL,
1838 /* u32VersionEnd */
1839 PDM_DEVREG_VERSION
1840};
1841
Note: See TracBrowser for help on using the repository browser.

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