VirtualBox

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

Last change on this file since 17555 was 17545, checked in by vboxsync, 16 years ago

DevPcBios.cpp,rombios.c: 4GB changes, synced the 0E820 code with current bochs code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 62.2 KB
Line 
1/* $Id: DevPcBios.cpp 17545 2009-03-08 13:12:25Z 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
29#include <VBox/log.h>
30#include <iprt/assert.h>
31#include <iprt/alloc.h>
32#include <iprt/file.h>
33#include <iprt/string.h>
34#include <iprt/uuid.h>
35#include <VBox/err.h>
36#include <VBox/param.h>
37
38#include "../Builtins.h"
39#include "../Builtins2.h"
40#include "DevPcBios.h"
41
42
43/** @page pg_devbios_cmos_assign CMOS Assignments (BIOS)
44 *
45 * The BIOS uses a CMOS to store configuration data.
46 * It is currently used as follows:
47 *
48 * @verbatim
49 Base memory:
50 0x15
51 0x16
52 Extended memory:
53 0x17
54 0x18
55 0x30
56 0x31
57 Amount of memory above 16M:
58 0x34
59 0x35
60 Boot device (BOCHS bios specific):
61 0x3d
62 0x38
63 0x3c
64 PXE debug:
65 0x3f
66 Floppy drive type:
67 0x10
68 Equipment byte:
69 0x14
70 First HDD:
71 0x19
72 0x1e - 0x25
73 Second HDD:
74 0x1a
75 0x26 - 0x2d
76 Third HDD:
77 0x67 - 0x6e
78 Fourth HDD:
79 0x70 - 0x77
80 Extended:
81 0x12
82 First Sata HDD:
83 0x40 - 0x47
84 Second Sata HDD:
85 0x48 - 0x4f
86 Third Sata HDD:
87 0x50 - 0x57
88 Fourth Sata HDD:
89 0x58 - 0x5f
90 Number of CPUs:
91 0x60
92 RAM above 4G (in 64KB units):
93 0x61 - 0x65
94@endverbatim
95 *
96 * @todo Mark which bits are compatible with which BIOSes and
97 * which are our own definitions.
98 *
99 * @todo r=bird: Is the 0x61 - 0x63 range defined by AMI,
100 * PHOENIX or AWARD? If not I'd say 64MB units is a bit
101 * too big, besides it forces unnecessary math stuff onto
102 * the BIOS.
103 * nike: The way how values encoded are defined by Bochs/QEmu BIOS,
104 * although for them position in CMOS is different:
105 * 0x5b - 0x5c: RAM above 4G
106 * 0x5f: number of CPUs
107 * Unfortunately for us those positions in our CMOS are already taken
108 * by 4th SATA drive configuration.
109 *
110 */
111
112
113/*******************************************************************************
114* Structures and Typedefs *
115*******************************************************************************/
116
117/**
118 * The boot device.
119 */
120typedef enum DEVPCBIOSBOOT
121{
122 DEVPCBIOSBOOT_NONE,
123 DEVPCBIOSBOOT_FLOPPY,
124 DEVPCBIOSBOOT_HD,
125 DEVPCBIOSBOOT_DVD,
126 DEVPCBIOSBOOT_LAN
127} DEVPCBIOSBOOT;
128
129/**
130 * PC Bios instance data structure.
131 */
132typedef struct DEVPCBIOS
133{
134 /** Pointer back to the device instance. */
135 PPDMDEVINS pDevIns;
136
137 /** Boot devices (ordered). */
138 DEVPCBIOSBOOT aenmBootDevice[4];
139 /** RAM size (in bytes). */
140 uint64_t cbRam;
141 /** RAM hole size (in bytes). */
142 uint32_t cbRamHole;
143 /** Bochs shutdown index. */
144 uint32_t iShutdown;
145 /** Floppy device. */
146 char *pszFDDevice;
147 /** Harddisk device. */
148 char *pszHDDevice;
149 /** Sata harddisk device. */
150 char *pszSataDevice;
151 /** LUN of the four harddisks which are emulated as IDE. */
152 uint32_t iSataHDLUN[4];
153 /** Bios message buffer. */
154 char szMsg[256];
155 /** Bios message buffer index. */
156 uint32_t iMsg;
157 /** The system BIOS ROM data. */
158 uint8_t *pu8PcBios;
159 /** The size of the system BIOS ROM. */
160 uint64_t cbPcBios;
161 /** The name of the BIOS ROM file. */
162 char *pszPcBiosFile;
163 /** The LAN boot ROM data. */
164 uint8_t *pu8LanBoot;
165 /** The name of the LAN boot ROM file. */
166 char *pszLanBootFile;
167 /** The DMI tables. */
168 uint8_t au8DMIPage[0x1000];
169 /** The boot countdown (in seconds). */
170 uint8_t uBootDelay;
171 /** I/O-APIC enabled? */
172 uint8_t u8IOAPIC;
173 /** PXE debug logging enabled? */
174 uint8_t u8PXEDebug;
175 /** Number of logical CPUs in guest */
176 uint16_t cCpus;
177} DEVPCBIOS, *PDEVPCBIOS;
178
179#pragma pack(1)
180
181/** DMI header */
182typedef struct DMIHDR
183{
184 uint8_t u8Type;
185 uint8_t u8Length;
186 uint16_t u16Handle;
187} *PDMIHDR;
188AssertCompileSize(DMIHDR, 4);
189
190/** DMI BIOS information */
191typedef struct DMIBIOSINF
192{
193 DMIHDR header;
194 uint8_t u8Vendor;
195 uint8_t u8Version;
196 uint16_t u16Start;
197 uint8_t u8Release;
198 uint8_t u8ROMSize;
199 uint64_t u64Characteristics;
200 uint8_t u8CharacteristicsByte1;
201 uint8_t u8CharacteristicsByte2;
202 uint8_t u8ReleaseMajor;
203 uint8_t u8ReleaseMinor;
204 uint8_t u8FirmwareMajor;
205 uint8_t u8FirmwareMinor;
206} *PDMIBIOSINF;
207AssertCompileSize(DMIBIOSINF, 0x18);
208
209/** DMI system information */
210typedef struct DMISYSTEMINF
211{
212 DMIHDR header;
213 uint8_t u8Manufacturer;
214 uint8_t u8ProductName;
215 uint8_t u8Version;
216 uint8_t u8SerialNumber;
217 uint8_t au8Uuid[16];
218 uint8_t u8WakeupType;
219 uint8_t u8SKUNumber;
220 uint8_t u8Family;
221} *PDMISYSTEMINF;
222AssertCompileSize(DMISYSTEMINF, 0x1b);
223
224/** MPS floating pointer structure */
225typedef struct MPSFLOATPTR
226{
227 uint8_t au8Signature[4];
228 uint32_t u32MPSAddr;
229 uint8_t u8Length;
230 uint8_t u8SpecRev;
231 uint8_t u8Checksum;
232 uint8_t au8Feature[5];
233} *PMPSFLOATPTR;
234AssertCompileSize(MPSFLOATPTR, 16);
235
236/** MPS config table header */
237typedef struct MPSCFGTBLHEADER
238{
239 uint8_t au8Signature[4];
240 uint16_t u16Length;
241 uint8_t u8SpecRev;
242 uint8_t u8Checksum;
243 uint8_t au8OemId[8];
244 uint8_t au8ProductId[12];
245 uint32_t u32OemTablePtr;
246 uint16_t u16OemTableSize;
247 uint16_t u16EntryCount;
248 uint32_t u32AddrLocalApic;
249 uint16_t u16ExtTableLength;
250 uint8_t u8ExtTableChecksxum;
251 uint8_t u8Reserved;
252} *PMPSCFGTBLHEADER;
253AssertCompileSize(MPSCFGTBLHEADER, 0x2c);
254
255/** MPS processor entry */
256typedef struct MPSPROCENTRY
257{
258 uint8_t u8EntryType;
259 uint8_t u8LocalApicId;
260 uint8_t u8LocalApicVersion;
261 uint8_t u8CPUFlags;
262 uint32_t u32CPUSignature;
263 uint32_t u32CPUFeatureFlags;
264 uint32_t u32Reserved[2];
265} *PMPSPROCENTRY;
266AssertCompileSize(MPSPROCENTRY, 20);
267
268/** MPS bus entry */
269typedef struct MPSBUSENTRY
270{
271 uint8_t u8EntryType;
272 uint8_t u8BusId;
273 uint8_t au8BusTypeStr[6];
274} *PMPSBUSENTRY;
275AssertCompileSize(MPSBUSENTRY, 8);
276
277/** MPS I/O-APIC entry */
278typedef struct MPSIOAPICENTRY
279{
280 uint8_t u8EntryType;
281 uint8_t u8Id;
282 uint8_t u8Version;
283 uint8_t u8Flags;
284 uint32_t u32Addr;
285} *PMPSIOAPICENTRY;
286AssertCompileSize(MPSIOAPICENTRY, 8);
287
288/** MPS I/O-Interrupt entry */
289typedef struct MPSIOINTERRUPTENTRY
290{
291 uint8_t u8EntryType;
292 uint8_t u8Type;
293 uint16_t u16Flags;
294 uint8_t u8SrcBusId;
295 uint8_t u8SrcBusIrq;
296 uint8_t u8DstIOAPICId;
297 uint8_t u8DstIOAPICInt;
298} *PMPSIOIRQENTRY;
299AssertCompileSize(MPSIOINTERRUPTENTRY, 8);
300
301#pragma pack()
302
303/* Attempt to guess the LCHS disk geometry from the MS-DOS master boot
304 * record (partition table). */
305static int biosGuessDiskLCHS(PPDMIBLOCK pBlock, PPDMMEDIAGEOMETRY pLCHSGeometry)
306{
307 uint8_t aMBR[512], *p;
308 int rc;
309 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
310
311 if (!pBlock)
312 return VERR_INVALID_PARAMETER;
313 rc = pBlock->pfnRead(pBlock, 0, aMBR, sizeof(aMBR));
314 if (RT_FAILURE(rc))
315 return rc;
316 /* Test MBR magic number. */
317 if (aMBR[510] != 0x55 || aMBR[511] != 0xaa)
318 return VERR_INVALID_PARAMETER;
319 for (uint32_t i = 0; i < 4; i++)
320 {
321 /* Figure out the start of a partition table entry. */
322 p = &aMBR[0x1be + i * 16];
323 iEndHead = p[5];
324 iEndSector = p[6] & 63;
325 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector & iEndHead)
326 {
327 /* Assumption: partition terminates on a cylinder boundary. */
328 cLCHSHeads = iEndHead + 1;
329 cLCHSSectors = iEndSector;
330 cLCHSCylinders = RT_MIN(1024, pBlock->pfnGetSize(pBlock) / (512 * cLCHSHeads * cLCHSSectors));
331 if (cLCHSCylinders >= 1)
332 {
333 pLCHSGeometry->cCylinders = cLCHSCylinders;
334 pLCHSGeometry->cHeads = cLCHSHeads;
335 pLCHSGeometry->cSectors = cLCHSSectors;
336 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
337 return VINF_SUCCESS;
338 }
339 }
340 }
341 return VERR_INVALID_PARAMETER;
342}
343
344
345/**
346 * Write to CMOS memory.
347 * This is used by the init complete code.
348 */
349static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
350{
351 Assert(off < 128);
352 Assert(u32Val < 256);
353
354#if 1
355 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
356 AssertRC(rc);
357#else
358 PVM pVM = PDMDevHlpGetVM(pDevIns);
359 IOMIOPortWrite(pVM, 0x70, off, 1);
360 IOMIOPortWrite(pVM, 0x71, u32Val, 1);
361 IOMIOPortWrite(pVM, 0x70, 0, 1);
362#endif
363}
364
365/* -=-=-=-=-=-=- based on code from pc.c -=-=-=-=-=-=- */
366
367/**
368 * Initializes the CMOS data for one harddisk.
369 */
370static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
371{
372 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
373 if (offType)
374 pcbiosCmosWrite(pDevIns, offType, 48);
375 /* Cylinders low */
376 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(pLCHSGeometry->cCylinders, 1024) & 0xff);
377 /* Cylinders high */
378 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(pLCHSGeometry->cCylinders, 1024) >> 8);
379 /* Heads */
380 pcbiosCmosWrite(pDevIns, offInfo + 2, pLCHSGeometry->cHeads);
381 /* Landing zone low */
382 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
383 /* Landing zone high */
384 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
385 /* Write precomp low */
386 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
387 /* Write precomp high */
388 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
389 /* Sectors */
390 pcbiosCmosWrite(pDevIns, offInfo + 7, pLCHSGeometry->cSectors);
391}
392
393/**
394 * Set logical CHS geometry for a hard disk
395 *
396 * @returns VBox status code.
397 * @param pBase Base interface for the device.
398 * @param pHardDisk The hard disk.
399 * @param pLCHSGeometry Where to store the geometry settings.
400 */
401static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIBLOCKBIOS pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
402{
403 PDMMEDIAGEOMETRY LCHSGeometry;
404 int rc = VINF_SUCCESS;
405
406 rc = pHardDisk->pfnGetLCHSGeometry(pHardDisk, &LCHSGeometry);
407 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
408 || LCHSGeometry.cCylinders == 0
409 || LCHSGeometry.cCylinders > 1024
410 || LCHSGeometry.cHeads == 0
411 || LCHSGeometry.cHeads > 255
412 || LCHSGeometry.cSectors == 0
413 || LCHSGeometry.cSectors > 63)
414 {
415 PPDMIBLOCK pBlock;
416 pBlock = (PPDMIBLOCK)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK);
417 /* No LCHS geometry, autodetect and set. */
418 rc = biosGuessDiskLCHS(pBlock, &LCHSGeometry);
419 if (RT_FAILURE(rc))
420 {
421 /* Try if PCHS geometry works, otherwise fall back. */
422 rc = pHardDisk->pfnGetPCHSGeometry(pHardDisk, &LCHSGeometry);
423 }
424 if ( RT_FAILURE(rc)
425 || LCHSGeometry.cCylinders == 0
426 || LCHSGeometry.cCylinders > 1024
427 || LCHSGeometry.cHeads == 0
428 || LCHSGeometry.cHeads > 16
429 || LCHSGeometry.cSectors == 0
430 || LCHSGeometry.cSectors > 63)
431 {
432 uint64_t cSectors = pBlock->pfnGetSize(pBlock) / 512;
433 if (cSectors / 16 / 63 <= 1024)
434 {
435 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
436 LCHSGeometry.cHeads = 16;
437 }
438 else if (cSectors / 32 / 63 <= 1024)
439 {
440 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
441 LCHSGeometry.cHeads = 32;
442 }
443 else if (cSectors / 64 / 63 <= 1024)
444 {
445 LCHSGeometry.cCylinders = cSectors / 64 / 63;
446 LCHSGeometry.cHeads = 64;
447 }
448 else if (cSectors / 128 / 63 <= 1024)
449 {
450 LCHSGeometry.cCylinders = cSectors / 128 / 63;
451 LCHSGeometry.cHeads = 128;
452 }
453 else
454 {
455 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
456 LCHSGeometry.cHeads = 255;
457 }
458 LCHSGeometry.cSectors = 63;
459
460 }
461 rc = pHardDisk->pfnSetLCHSGeometry(pHardDisk, &LCHSGeometry);
462 if (rc == VERR_VD_IMAGE_READ_ONLY)
463 {
464 LogRel(("DevPcBios: ATA failed to update LCHS geometry, read only\n"));
465 rc = VINF_SUCCESS;
466 }
467 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
468 {
469 LogRel(("DevPcBios: ATA failed to update LCHS geometry, backend refused\n"));
470 rc = VINF_SUCCESS;
471 }
472 }
473
474 *pLCHSGeometry = LCHSGeometry;
475
476 return rc;
477}
478
479/**
480 * Get BIOS boot code from enmBootDevice in order
481 *
482 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
483 */
484static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
485{
486 switch (pThis->aenmBootDevice[iOrder])
487 {
488 case DEVPCBIOSBOOT_NONE:
489 return 0;
490 case DEVPCBIOSBOOT_FLOPPY:
491 return 1;
492 case DEVPCBIOSBOOT_HD:
493 return 2;
494 case DEVPCBIOSBOOT_DVD:
495 return 3;
496 case DEVPCBIOSBOOT_LAN:
497 return 4;
498 default:
499 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
500 return 0;
501 }
502}
503
504
505/**
506 * Init complete notification.
507 * This routine will write information needed by the bios to the CMOS.
508 *
509 * @returns VBOX status code.
510 * @param pDevIns The device instance.
511 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
512 * a description of standard and non-standard CMOS registers.
513 */
514static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
515{
516 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
517 uint32_t u32;
518 unsigned i;
519 PVM pVM = PDMDevHlpGetVM(pDevIns);
520 PPDMIBLOCKBIOS apHDs[4] = {0};
521 PPDMIBLOCKBIOS apFDs[2] = {0};
522 AssertRelease(pVM);
523 LogFlow(("pcbiosInitComplete:\n"));
524
525 /*
526 * Memory sizes.
527 */
528 /* base memory. */
529 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 */
530 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
531 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
532
533 /* Extended memory, up to 65MB */
534 u32 = pThis->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pThis->cbRam - _1M) / _1K;
535 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
536 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
537 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
538 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
539
540 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
541 and below 4GB (as it can only hold 4GB+16M). For 0E820h to work right,
542 this has to indicate less than 0xfffc0000 bytes or it'll conflict with
543 the high BIOS area. */
544 uint64_t const offRamHole = _4G - pThis->cbRamHole;
545 if (pThis->cbRam > 16 * _1M)
546 {
547 u32 = (uint32_t)( (RT_MIN(pThis->cbRam, offRamHole) - 16 * _1M) / _64K );
548 u32 = RT_MIN(u32, 0xfefc);
549 }
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
1228/**
1229 * Destruct a device instance.
1230 *
1231 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1232 * resources can be freed correctly.
1233 *
1234 * @param pDevIns The device instance data.
1235 */
1236static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1237{
1238 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1239 LogFlow(("pcbiosDestruct:\n"));
1240
1241 /*
1242 * Free MM heap pointers.
1243 */
1244 if (pThis->pu8PcBios)
1245 {
1246 MMR3HeapFree(pThis->pu8PcBios);
1247 pThis->pu8PcBios = NULL;
1248 }
1249
1250 if (pThis->pszPcBiosFile)
1251 {
1252 MMR3HeapFree(pThis->pszPcBiosFile);
1253 pThis->pszPcBiosFile = NULL;
1254 }
1255
1256 if (pThis->pu8LanBoot)
1257 {
1258 MMR3HeapFree(pThis->pu8LanBoot);
1259 pThis->pu8LanBoot = NULL;
1260 }
1261
1262 if (pThis->pszLanBootFile)
1263 {
1264 MMR3HeapFree(pThis->pszLanBootFile);
1265 pThis->pszLanBootFile = NULL;
1266 }
1267
1268 return VINF_SUCCESS;
1269}
1270
1271
1272/**
1273 * Convert config value to DEVPCBIOSBOOT.
1274 *
1275 * @returns VBox status code.
1276 * @param pCfgHandle Configuration handle.
1277 * @param pszParam The name of the value to read.
1278 * @param penmBoot Where to store the boot method.
1279 */
1280static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfgHandle, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1281{
1282 char *psz;
1283 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszParam, &psz);
1284 if (RT_FAILURE(rc))
1285 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1286 N_("Configuration error: Querying \"%s\" as a string failed"),
1287 pszParam);
1288 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
1289 *penmBoot = DEVPCBIOSBOOT_DVD;
1290 else if (!strcmp(psz, "IDE"))
1291 *penmBoot = DEVPCBIOSBOOT_HD;
1292 else if (!strcmp(psz, "FLOPPY"))
1293 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1294 else if (!strcmp(psz, "LAN"))
1295 *penmBoot = DEVPCBIOSBOOT_LAN;
1296 else if (!strcmp(psz, "NONE"))
1297 *penmBoot = DEVPCBIOSBOOT_NONE;
1298 else
1299 {
1300 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1301 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
1302 pszParam, psz);
1303 rc = VERR_INTERNAL_ERROR;
1304 }
1305 MMR3HeapFree(psz);
1306 return rc;
1307}
1308
1309/**
1310 * Construct a device instance for a VM.
1311 *
1312 * @returns VBox status.
1313 * @param pDevIns The device instance data.
1314 * If the registration structure is needed, pDevIns->pDevReg points to it.
1315 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1316 * The device number is also found in pDevIns->iInstance, but since it's
1317 * likely to be freqently used PDM passes it as parameter.
1318 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1319 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1320 * iInstance it's expected to be used a bit in this function.
1321 */
1322static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1323{
1324 unsigned i;
1325 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1326 int rc;
1327 int cb;
1328
1329 Assert(iInstance == 0);
1330
1331 /*
1332 * Validate configuration.
1333 */
1334 if (!CFGMR3AreValuesValid(pCfgHandle,
1335 "BootDevice0\0"
1336 "BootDevice1\0"
1337 "BootDevice2\0"
1338 "BootDevice3\0"
1339 "RamSize\0"
1340 "RamHoleSize\0"
1341 "HardDiskDevice\0"
1342 "SataHardDiskDevice\0"
1343 "SataPrimaryMasterLUN\0"
1344 "SataPrimarySlaveLUN\0"
1345 "SataSecondaryMasterLUN\0"
1346 "SataSecondarySlaveLUN\0"
1347 "FloppyDevice\0"
1348 "DelayBoot\0"
1349 "BiosRom\0"
1350 "LanBootRom\0"
1351 "PXEDebug\0"
1352 "UUID\0"
1353 "IOAPIC\0"
1354 "NumCPUs\0"
1355 "DmiBIOSVendor\0"
1356 "DmiBIOSVersion\0"
1357 "DmiBIOSReleaseDate\0"
1358 "DmiBIOSReleaseMajor\0"
1359 "DmiBIOSReleaseMinor\0"
1360 "DmiBIOSFirmwareMajor\0"
1361 "DmiBIOSFirmwareMinor\0"
1362 "DmiSystemFamily\0"
1363 "DmiSystemProduct\0"
1364 "DmiSystemSerial\0"
1365 "DmiSystemUuid\0"
1366 "DmiSystemVendor\0"
1367 "DmiSystemVersion\0"))
1368 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1369 N_("Invalid configuraton for device pcbios device"));
1370
1371 /*
1372 * Init the data.
1373 */
1374 rc = CFGMR3QueryU64(pCfgHandle, "RamSize", &pThis->cbRam);
1375 if (RT_FAILURE(rc))
1376 return PDMDEV_SET_ERROR(pDevIns, rc,
1377 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1378
1379 rc = CFGMR3QueryU32Def(pCfgHandle, "RamHoleSize", &pThis->cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
1380 if (RT_FAILURE(rc))
1381 return PDMDEV_SET_ERROR(pDevIns, rc,
1382 N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
1383
1384 rc = CFGMR3QueryU16Def(pCfgHandle, "NumCPUs", &pThis->cCpus, 1);
1385 if (RT_FAILURE(rc))
1386 return PDMDEV_SET_ERROR(pDevIns, rc,
1387 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1388
1389#ifdef VBOX_WITH_SMP_GUESTS
1390 LogRel(("[SMP] BIOS with %u CPUs\n", pThis->cCpus));
1391#else
1392 /** @todo: move this check up in configuration chain */
1393 if (pThis->cCpus != 1)
1394 {
1395 LogRel(("WARNING: guest SMP not supported in this build, going UP\n"));
1396 pThis->cCpus = 1;
1397 }
1398#endif
1399
1400 rc = CFGMR3QueryU8Def(pCfgHandle, "IOAPIC", &pThis->u8IOAPIC, 1);
1401 if (RT_FAILURE (rc))
1402 return PDMDEV_SET_ERROR(pDevIns, rc,
1403 N_("Configuration error: Failed to read \"IOAPIC\""));
1404
1405 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1406 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1407 for (i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1408 {
1409 rc = pcbiosBootFromCfg(pDevIns, pCfgHandle, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1410 if (RT_FAILURE(rc))
1411 return rc;
1412 }
1413
1414 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HardDiskDevice", &pThis->pszHDDevice);
1415 if (RT_FAILURE(rc))
1416 return PDMDEV_SET_ERROR(pDevIns, rc,
1417 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1418
1419 rc = CFGMR3QueryStringAlloc(pCfgHandle, "FloppyDevice", &pThis->pszFDDevice);
1420 if (RT_FAILURE(rc))
1421 return PDMDEV_SET_ERROR(pDevIns, rc,
1422 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1423
1424 rc = CFGMR3QueryStringAlloc(pCfgHandle, "SataHardDiskDevice", &pThis->pszSataDevice);
1425 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1426 pThis->pszSataDevice = NULL;
1427 else if (RT_FAILURE(rc))
1428 return PDMDEV_SET_ERROR(pDevIns, rc,
1429 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1430
1431 if (pThis->pszSataDevice)
1432 {
1433 static const char * const s_apszSataDisks[] =
1434 { "SataPrimaryMasterLUN", "SataPrimarySlaveLUN", "SataSecondaryMasterLUN", "SataSecondarySlaveLUN" };
1435 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1436 for (i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1437 {
1438 rc = CFGMR3QueryU32(pCfgHandle, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1439 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1440 pThis->iSataHDLUN[i] = i;
1441 else if (RT_FAILURE(rc))
1442 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1443 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1444 }
1445 }
1446 /*
1447 * Register I/O Ports and PC BIOS.
1448 */
1449 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1450 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1451 if (RT_FAILURE(rc))
1452 return rc;
1453 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1454 NULL, NULL, "Bochs PC BIOS - Shutdown");
1455 if (RT_FAILURE(rc))
1456 return rc;
1457
1458 /*
1459 * Query the machine's UUID for SMBIOS/DMI use.
1460 */
1461 RTUUID uuid;
1462 rc = CFGMR3QueryBytes(pCfgHandle, "UUID", &uuid, sizeof(uuid));
1463 if (RT_FAILURE(rc))
1464 return PDMDEV_SET_ERROR(pDevIns, rc,
1465 N_("Configuration error: Querying \"UUID\" failed"));
1466
1467
1468 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1469 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1470 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1471 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1472 rc = pcbiosPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE, &uuid, pCfgHandle);
1473 if (RT_FAILURE(rc))
1474 return rc;
1475 if (pThis->u8IOAPIC)
1476 pcbiosPlantMPStable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1477
1478 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, false /* fShadow */, "DMI tables");
1479 if (RT_FAILURE(rc))
1480 return rc;
1481
1482 /*
1483 * Read the PXE debug logging option.
1484 */
1485 rc = CFGMR3QueryU8Def(pCfgHandle, "PXEDebug", &pThis->u8PXEDebug, false);
1486 if (RT_FAILURE(rc))
1487 return PDMDEV_SET_ERROR(pDevIns, rc,
1488 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1489
1490 /*
1491 * Get the system BIOS ROM file name.
1492 */
1493 rc = CFGMR3QueryStringAlloc(pCfgHandle, "BiosRom", &pThis->pszPcBiosFile);
1494 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1495 {
1496 pThis->pszPcBiosFile = NULL;
1497 rc = VINF_SUCCESS;
1498 }
1499 else if (RT_FAILURE(rc))
1500 return PDMDEV_SET_ERROR(pDevIns, rc,
1501 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1502 else if (!*pThis->pszPcBiosFile)
1503 {
1504 MMR3HeapFree(pThis->pszPcBiosFile);
1505 pThis->pszPcBiosFile = NULL;
1506 }
1507
1508 const uint8_t *pu8PcBiosBinary = NULL;
1509 uint64_t cbPcBiosBinary;
1510 /*
1511 * Determine the system BIOS ROM size, open specified ROM file in the process.
1512 */
1513 RTFILE FilePcBios = NIL_RTFILE;
1514 if (pThis->pszPcBiosFile)
1515 {
1516 rc = RTFileOpen(&FilePcBios, pThis->pszPcBiosFile,
1517 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1518 if (RT_SUCCESS(rc))
1519 {
1520 rc = RTFileGetSize(FilePcBios, &pThis->cbPcBios);
1521 if (RT_SUCCESS(rc))
1522 {
1523 /* The following checks should be in sync the AssertReleaseMsg's below. */
1524 if ( RT_ALIGN(pThis->cbPcBios, _64K) != pThis->cbPcBios
1525 || pThis->cbPcBios > 32 * _64K
1526 || pThis->cbPcBios < _64K)
1527 rc = VERR_TOO_MUCH_DATA;
1528 }
1529 }
1530 if (RT_FAILURE(rc))
1531 {
1532 /*
1533 * In case of failure simply fall back to the built-in BIOS ROM.
1534 */
1535 Log(("pcbiosConstruct: Failed to open system BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszPcBiosFile, rc));
1536 RTFileClose(FilePcBios);
1537 FilePcBios = NIL_RTFILE;
1538 MMR3HeapFree(pThis->pszPcBiosFile);
1539 pThis->pszPcBiosFile = NULL;
1540 }
1541 }
1542
1543 /*
1544 * Attempt to get the system BIOS ROM data from file.
1545 */
1546 if (pThis->pszPcBiosFile)
1547 {
1548 /*
1549 * Allocate buffer for the system BIOS ROM data.
1550 */
1551 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1552 if (pThis->pu8PcBios)
1553 {
1554 rc = RTFileRead(FilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1555 if (RT_FAILURE(rc))
1556 {
1557 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbPcBios, rc));
1558 MMR3HeapFree(pThis->pu8PcBios);
1559 pThis->pu8PcBios = NULL;
1560 }
1561 rc = VINF_SUCCESS;
1562 }
1563 else
1564 rc = VERR_NO_MEMORY;
1565 }
1566 else
1567 pThis->pu8PcBios = NULL;
1568
1569 /* cleanup */
1570 if (FilePcBios != NIL_RTFILE)
1571 RTFileClose(FilePcBios);
1572
1573 /* If we were unable to get the data from file for whatever reason, fall
1574 * back to the built-in ROM image.
1575 */
1576 if (pThis->pu8PcBios == NULL)
1577 {
1578 pu8PcBiosBinary = g_abPcBiosBinary;
1579 cbPcBiosBinary = g_cbPcBiosBinary;
1580 }
1581 else
1582 {
1583 pu8PcBiosBinary = pThis->pu8PcBios;
1584 cbPcBiosBinary = pThis->cbPcBios;
1585 }
1586
1587 /*
1588 * Map the BIOS into memory.
1589 * There are two mappings:
1590 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1591 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1592 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1593 */
1594 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1595 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1596 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1597 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1598 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb],
1599 false /* fShadow */, "PC BIOS - 0xfffff");
1600 if (RT_FAILURE(rc))
1601 return rc;
1602 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary,
1603 false /* fShadow */, "PC BIOS - 0xffffffff");
1604 if (RT_FAILURE(rc))
1605 return rc;
1606
1607#ifdef VBOX_WITH_VMI
1608 /*
1609 * Map the VMI BIOS into memory.
1610 */
1611 AssertReleaseMsg(g_cbVmiBiosBinary == _4K, ("cbVmiBiosBinary=%#x\n", g_cbVmiBiosBinary));
1612 rc = PDMDevHlpROMRegister(pDevIns, VBOX_VMI_BIOS_BASE, g_cbVmiBiosBinary, g_abVmiBiosBinary, false, "VMI BIOS");
1613 if (RT_FAILURE(rc))
1614 return rc;
1615#endif /* VBOX_WITH_VMI */
1616
1617 /*
1618 * Call reset to set values and stuff.
1619 */
1620 pcbiosReset(pDevIns);
1621
1622 /*
1623 * Get the LAN boot ROM file name.
1624 */
1625 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LanBootRom", &pThis->pszLanBootFile);
1626 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1627 {
1628 pThis->pszLanBootFile = NULL;
1629 rc = VINF_SUCCESS;
1630 }
1631 else if (RT_FAILURE(rc))
1632 return PDMDEV_SET_ERROR(pDevIns, rc,
1633 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1634 else if (!*pThis->pszLanBootFile)
1635 {
1636 MMR3HeapFree(pThis->pszLanBootFile);
1637 pThis->pszLanBootFile = NULL;
1638 }
1639
1640 uint64_t cbFileLanBoot;
1641 const uint8_t *pu8LanBootBinary = NULL;
1642 uint64_t cbLanBootBinary;
1643
1644 /*
1645 * Determine the LAN boot ROM size, open specified ROM file in the process.
1646 */
1647 RTFILE FileLanBoot = NIL_RTFILE;
1648 if (pThis->pszLanBootFile)
1649 {
1650 rc = RTFileOpen(&FileLanBoot, pThis->pszLanBootFile,
1651 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1652 if (RT_SUCCESS(rc))
1653 {
1654 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1655 if (RT_SUCCESS(rc))
1656 {
1657 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1658 || cbFileLanBoot > _64K)
1659 rc = VERR_TOO_MUCH_DATA;
1660 }
1661 }
1662 if (RT_FAILURE(rc))
1663 {
1664 /*
1665 * Ignore failure and fall back to the built-in LAN boot ROM.
1666 */
1667 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1668 RTFileClose(FileLanBoot);
1669 FileLanBoot = NIL_RTFILE;
1670 MMR3HeapFree(pThis->pszLanBootFile);
1671 pThis->pszLanBootFile = NULL;
1672 }
1673 }
1674
1675 /*
1676 * Get the LAN boot ROM data.
1677 */
1678 if (pThis->pszLanBootFile)
1679 {
1680 /*
1681 * Allocate buffer for the LAN boot ROM data.
1682 */
1683 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1684 if (pThis->pu8LanBoot)
1685 {
1686 rc = RTFileRead(FileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1687 if (RT_FAILURE(rc))
1688 {
1689 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", cbFileLanBoot, rc));
1690 MMR3HeapFree(pThis->pu8LanBoot);
1691 pThis->pu8LanBoot = NULL;
1692 }
1693 rc = VINF_SUCCESS;
1694 }
1695 else
1696 rc = VERR_NO_MEMORY;
1697 }
1698 else
1699 pThis->pu8LanBoot = NULL;
1700
1701 /* cleanup */
1702 if (FileLanBoot != NIL_RTFILE)
1703 RTFileClose(FileLanBoot);
1704
1705 /* If we were unable to get the data from file for whatever reason, fall
1706 * back to the built-in LAN boot ROM image.
1707 */
1708 if (pThis->pu8LanBoot == NULL)
1709 {
1710 pu8LanBootBinary = g_abNetBiosBinary;
1711 cbLanBootBinary = g_cbNetBiosBinary;
1712 }
1713 else
1714 {
1715 pu8LanBootBinary = pThis->pu8LanBoot;
1716 cbLanBootBinary = cbFileLanBoot;
1717 }
1718
1719 /*
1720 * Map the Network Boot ROM into memory.
1721 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1722 * the (up to) 32 kb ROM image.
1723 */
1724 if (pu8LanBootBinary)
1725 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbLanBootBinary, pu8LanBootBinary,
1726 true /* fShadow */, "Net Boot ROM");
1727
1728 rc = CFGMR3QueryU8Def(pCfgHandle, "DelayBoot", &pThis->uBootDelay, 0);
1729 if (RT_FAILURE(rc))
1730 return PDMDEV_SET_ERROR(pDevIns, rc,
1731 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1732 if (pThis->uBootDelay > 15)
1733 pThis->uBootDelay = 15;
1734
1735 return rc;
1736}
1737
1738
1739/**
1740 * The device registration structure.
1741 */
1742const PDMDEVREG g_DevicePcBios =
1743{
1744 /* u32Version */
1745 PDM_DEVREG_VERSION,
1746 /* szDeviceName */
1747 "pcbios",
1748 /* szRCMod */
1749 "",
1750 /* szR0Mod */
1751 "",
1752 /* pszDescription */
1753 "PC BIOS Device",
1754 /* fFlags */
1755 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1756 /* fClass */
1757 PDM_DEVREG_CLASS_ARCH_BIOS,
1758 /* cMaxInstances */
1759 1,
1760 /* cbInstance */
1761 sizeof(DEVPCBIOS),
1762 /* pfnConstruct */
1763 pcbiosConstruct,
1764 /* pfnDestruct */
1765 pcbiosDestruct,
1766 /* pfnRelocate */
1767 NULL,
1768 /* pfnIOCtl */
1769 NULL,
1770 /* pfnPowerOn */
1771 NULL,
1772 /* pfnReset */
1773 pcbiosReset,
1774 /* pfnSuspend */
1775 NULL,
1776 /* pfnResume */
1777 NULL,
1778 /* pfnAttach */
1779 NULL,
1780 /* pfnDetach */
1781 NULL,
1782 /* pfnQueryInterface. */
1783 NULL,
1784 /* pfnInitComplete. */
1785 pcbiosInitComplete,
1786 /* pfnPowerOff */
1787 NULL,
1788 /* pfnSoftReset */
1789 NULL,
1790 /* u32VersionEnd */
1791 PDM_DEVREG_VERSION
1792};
1793
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