VirtualBox

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

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

SMP change

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