VirtualBox

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

Last change on this file since 8998 was 8942, checked in by vboxsync, 17 years ago

comment

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