VirtualBox

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

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

DevPcBios: CMOS layout.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use