VirtualBox

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

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

configure CPU count in VMM, use per-CPU MMIO regions for APIC

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

© 2023 Oracle
ContactPrivacy policyTerms of Use