VirtualBox

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

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

Storage: Eradicated the last bits using the old VDI only backend, keeping only the testcases for now (no longer built).

Completely removed old iSCSI driver.

Added intnet option to addiscsidisk and adjusted documentation.

Made backend name comparisons case-insensitive.

Detect VMDK files not according to VMDK 1.0 and reject with clear error message.

Changed format probing logic to not fall through to the "unsupported" case if it's a known format, i.e. has valid header.

VBoxManage converthd generic format converter made official.

Added format flag to VBoxManage createhd, allows creating VMDK files.

VBoxManage convertdd reimplemented based on new framework, supporting any image format.

VBoxManage internalcommands sethduuid reimplemented based on new framework, supporting any image format.

Cleaned up error codes.

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

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