VirtualBox

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

Last change on this file since 21205 was 20924, checked in by vboxsync, 15 years ago

DMI processor structure (unused)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use