VirtualBox

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

Last change on this file since 14976 was 12977, checked in by vboxsync, 16 years ago

#1865: Updated PDMDEVREG with pfnSoftReset, u32VersionEnd and some flag/name cleanups.

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

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