VirtualBox

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

Last change on this file since 43712 was 43712, checked in by vboxsync, 12 years ago

BIOS/EFI: fixed broken DMI information

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 51.0 KB
Line 
1/* $Id: DevPcBios.cpp 43712 2012-10-23 14:02:24Z vboxsync $ */
2/** @file
3 * PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Oracle Corporation
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
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DEV_PC_BIOS
22#include <VBox/vmm/pdmdev.h>
23#include <VBox/vmm/mm.h>
24#include <VBox/vmm/pgm.h>
25
26#include <VBox/log.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/buildconfig.h>
30#include <iprt/file.h>
31#include <iprt/mem.h>
32#include <iprt/string.h>
33#include <iprt/uuid.h>
34#include <VBox/err.h>
35#include <VBox/param.h>
36
37#include "VBoxDD.h"
38#include "VBoxDD2.h"
39#include "DevPcBios.h"
40#include "DevFwCommon.h"
41
42#define NET_BOOT_DEVS 4
43
44
45/** @page pg_devbios_cmos_assign CMOS Assignments (BIOS)
46 *
47 * The BIOS uses a CMOS to store configuration data.
48 * It is currently used as follows:
49 *
50 * @verbatim
51 First CMOS bank (offsets 0x00 to 0x7f):
52 Floppy drive type:
53 0x10
54 Hard disk type (old):
55 0x12
56 Equipment byte:
57 0x14
58 Base memory:
59 0x15
60 0x16
61 Extended memory:
62 0x17
63 0x18
64 0x30
65 0x31
66 First IDE HDD:
67 0x19
68 0x1e - 0x25
69 Second IDE HDD:
70 0x1a
71 0x26 - 0x2d
72 Checksum of 0x10-0x2d:
73 0x2e
74 0x2f
75 Amount of memory above 16M and below 4GB in 64KB units:
76 0x34
77 0x35
78 Boot device (BOCHS BIOS specific):
79 0x38
80 0x3c
81 0x3d
82 PXE debug:
83 0x3f
84 First SATA HDD:
85 0x40 - 0x47
86 Second SATA HDD:
87 0x48 - 0x4f
88 Third SATA HDD:
89 0x50 - 0x57
90 Fourth SATA HDD:
91 0x58 - 0x5f
92 Number of CPUs:
93 0x60
94 RAM above 4G in 64KB units:
95 0x61 - 0x65
96 Third IDE HDD:
97 0x67 - 0x6e
98 Fourth IDE HDD:
99 0x70 - 0x77
100
101 Second CMOS bank (offsets 0x80 to 0xff):
102 Reserved for internal use by PXE ROM:
103 0x80 - 0x81
104 First net boot device PCI bus/dev/fn:
105 0x82 - 0x83
106 Second to third net boot devices:
107 0x84 - 0x89
108@endverbatim
109 *
110 * @todo Mark which bits are compatible with which BIOSes and
111 * which are our own definitions.
112 */
113
114
115/*******************************************************************************
116* Structures and Typedefs *
117*******************************************************************************/
118
119/**
120 * The boot device.
121 */
122typedef enum DEVPCBIOSBOOT
123{
124 DEVPCBIOSBOOT_NONE,
125 DEVPCBIOSBOOT_FLOPPY,
126 DEVPCBIOSBOOT_HD,
127 DEVPCBIOSBOOT_DVD,
128 DEVPCBIOSBOOT_LAN
129} DEVPCBIOSBOOT;
130
131/**
132 * PC Bios instance data structure.
133 */
134typedef struct DEVPCBIOS
135{
136 /** Pointer back to the device instance. */
137 PPDMDEVINS pDevIns;
138
139 /** Boot devices (ordered). */
140 DEVPCBIOSBOOT aenmBootDevice[4];
141 /** RAM size (in bytes). */
142 uint64_t cbRam;
143 /** RAM hole size (in bytes). */
144 uint32_t cbRamHole;
145 /** Bochs shutdown index. */
146 uint32_t iShutdown;
147 /** Floppy device. */
148 char *pszFDDevice;
149 /** Harddisk device. */
150 char *pszHDDevice;
151 /** Sata harddisk device. */
152 char *pszSataDevice;
153 /** LUN of the four harddisks which are emulated as IDE. */
154 uint32_t iSataHDLUN[4];
155 /** Bios message buffer. */
156 char szMsg[256];
157 /** Bios message buffer index. */
158 uint32_t iMsg;
159 /** The system BIOS ROM data. */
160 uint8_t *pu8PcBios;
161 /** The size of the system BIOS ROM. */
162 uint32_t cbPcBios;
163 /** The name of the BIOS ROM file. */
164 char *pszPcBiosFile;
165 /** The LAN boot ROM data. */
166 uint8_t *pu8LanBoot;
167 /** The name of the LAN boot ROM file. */
168 char *pszLanBootFile;
169 /** The size of the LAN boot ROM. */
170 uint64_t cbLanBoot;
171 /** The DMI tables. */
172 uint8_t au8DMIPage[0x1000];
173 /** The boot countdown (in seconds). */
174 uint8_t uBootDelay;
175 /** I/O-APIC enabled? */
176 uint8_t u8IOAPIC;
177 /** PXE debug logging enabled? */
178 uint8_t u8PXEDebug;
179 /** PXE boot PCI bus/dev/fn list. */
180 uint16_t au16NetBootDev[NET_BOOT_DEVS];
181 /** Number of logical CPUs in guest */
182 uint16_t cCpus;
183 uint32_t u32McfgBase;
184 uint32_t cbMcfgLength;
185} DEVPCBIOS, *PDEVPCBIOS;
186
187
188/* Attempt to guess the LCHS disk geometry from the MS-DOS master boot
189 * record (partition table). */
190static int biosGuessDiskLCHS(PPDMIBLOCK pBlock, PPDMMEDIAGEOMETRY pLCHSGeometry)
191{
192 uint8_t aMBR[512], *p;
193 int rc;
194 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
195
196 if (!pBlock)
197 return VERR_INVALID_PARAMETER;
198 rc = pBlock->pfnRead(pBlock, 0, aMBR, sizeof(aMBR));
199 if (RT_FAILURE(rc))
200 return rc;
201 /* Test MBR magic number. */
202 if (aMBR[510] != 0x55 || aMBR[511] != 0xaa)
203 return VERR_INVALID_PARAMETER;
204 for (uint32_t i = 0; i < 4; i++)
205 {
206 /* Figure out the start of a partition table entry. */
207 p = &aMBR[0x1be + i * 16];
208 iEndHead = p[5];
209 iEndSector = p[6] & 63;
210 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector & iEndHead)
211 {
212 /* Assumption: partition terminates on a cylinder boundary. */
213 cLCHSHeads = iEndHead + 1;
214 cLCHSSectors = iEndSector;
215 cLCHSCylinders = RT_MIN(1024, pBlock->pfnGetSize(pBlock) / (512 * cLCHSHeads * cLCHSSectors));
216 if (cLCHSCylinders >= 1)
217 {
218 pLCHSGeometry->cCylinders = cLCHSCylinders;
219 pLCHSGeometry->cHeads = cLCHSHeads;
220 pLCHSGeometry->cSectors = cLCHSSectors;
221 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
222 return VINF_SUCCESS;
223 }
224 }
225 }
226 return VERR_INVALID_PARAMETER;
227}
228
229
230/**
231 * Write to CMOS memory.
232 * This is used by the init complete code.
233 */
234static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
235{
236 Assert(off < 256);
237 Assert(u32Val < 256);
238
239 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
240 AssertRC(rc);
241}
242
243/**
244 * Read from CMOS memory.
245 * This is used by the init complete code.
246 */
247static uint8_t pcbiosCmosRead(PPDMDEVINS pDevIns, int off)
248{
249 uint8_t u8val;
250
251 Assert(off < 256);
252
253 int rc = PDMDevHlpCMOSRead(pDevIns, off, &u8val);
254 AssertRC(rc);
255
256 return u8val;
257}
258
259/* -=-=-=-=-=-=- based on code from pc.c -=-=-=-=-=-=- */
260
261/**
262 * Initializes the CMOS data for one harddisk.
263 */
264static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
265{
266 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
267 if (offType)
268 pcbiosCmosWrite(pDevIns, offType, 47);
269 /* Cylinders low */
270 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(pLCHSGeometry->cCylinders, 1024) & 0xff);
271 /* Cylinders high */
272 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(pLCHSGeometry->cCylinders, 1024) >> 8);
273 /* Heads */
274 pcbiosCmosWrite(pDevIns, offInfo + 2, pLCHSGeometry->cHeads);
275 /* Landing zone low */
276 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
277 /* Landing zone high */
278 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
279 /* Write precomp low */
280 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
281 /* Write precomp high */
282 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
283 /* Sectors */
284 pcbiosCmosWrite(pDevIns, offInfo + 7, pLCHSGeometry->cSectors);
285}
286
287/**
288 * Set logical CHS geometry for a hard disk
289 *
290 * @returns VBox status code.
291 * @param pBase Base interface for the device.
292 * @param pHardDisk The hard disk.
293 * @param pLCHSGeometry Where to store the geometry settings.
294 */
295static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIBLOCKBIOS pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
296{
297 PDMMEDIAGEOMETRY LCHSGeometry;
298 int rc = VINF_SUCCESS;
299
300 rc = pHardDisk->pfnGetLCHSGeometry(pHardDisk, &LCHSGeometry);
301 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
302 || LCHSGeometry.cCylinders == 0
303 || LCHSGeometry.cHeads == 0
304 || LCHSGeometry.cHeads > 255
305 || LCHSGeometry.cSectors == 0
306 || LCHSGeometry.cSectors > 63)
307 {
308 PPDMIBLOCK pBlock;
309 pBlock = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCK);
310 /* No LCHS geometry, autodetect and set. */
311 rc = biosGuessDiskLCHS(pBlock, &LCHSGeometry);
312 if (RT_FAILURE(rc))
313 {
314 /* Try if PCHS geometry works, otherwise fall back. */
315 rc = pHardDisk->pfnGetPCHSGeometry(pHardDisk, &LCHSGeometry);
316 }
317 if ( RT_FAILURE(rc)
318 || LCHSGeometry.cCylinders == 0
319 || LCHSGeometry.cCylinders > 1024
320 || LCHSGeometry.cHeads == 0
321 || LCHSGeometry.cHeads > 16
322 || LCHSGeometry.cSectors == 0
323 || LCHSGeometry.cSectors > 63)
324 {
325 uint64_t cSectors = pBlock->pfnGetSize(pBlock) / 512;
326 if (cSectors / 16 / 63 <= 1024)
327 {
328 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
329 LCHSGeometry.cHeads = 16;
330 }
331 else if (cSectors / 32 / 63 <= 1024)
332 {
333 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
334 LCHSGeometry.cHeads = 32;
335 }
336 else if (cSectors / 64 / 63 <= 1024)
337 {
338 LCHSGeometry.cCylinders = cSectors / 64 / 63;
339 LCHSGeometry.cHeads = 64;
340 }
341 else if (cSectors / 128 / 63 <= 1024)
342 {
343 LCHSGeometry.cCylinders = cSectors / 128 / 63;
344 LCHSGeometry.cHeads = 128;
345 }
346 else
347 {
348 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
349 LCHSGeometry.cHeads = 255;
350 }
351 LCHSGeometry.cSectors = 63;
352
353 }
354 rc = pHardDisk->pfnSetLCHSGeometry(pHardDisk, &LCHSGeometry);
355 if (rc == VERR_VD_IMAGE_READ_ONLY)
356 {
357 LogRel(("DevPcBios: ATA failed to update LCHS geometry, read only\n"));
358 rc = VINF_SUCCESS;
359 }
360 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
361 {
362 LogRel(("DevPcBios: ATA failed to update LCHS geometry, backend refused\n"));
363 rc = VINF_SUCCESS;
364 }
365 }
366
367 *pLCHSGeometry = LCHSGeometry;
368
369 return rc;
370}
371
372/**
373 * Get BIOS boot code from enmBootDevice in order
374 *
375 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
376 */
377static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
378{
379 switch (pThis->aenmBootDevice[iOrder])
380 {
381 case DEVPCBIOSBOOT_NONE:
382 return 0;
383 case DEVPCBIOSBOOT_FLOPPY:
384 return 1;
385 case DEVPCBIOSBOOT_HD:
386 return 2;
387 case DEVPCBIOSBOOT_DVD:
388 return 3;
389 case DEVPCBIOSBOOT_LAN:
390 return 4;
391 default:
392 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
393 return 0;
394 }
395}
396
397
398/**
399 * Init complete notification.
400 * This routine will write information needed by the bios to the CMOS.
401 *
402 * @returns VBOX status code.
403 * @param pDevIns The device instance.
404 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
405 * a description of standard and non-standard CMOS registers.
406 */
407static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
408{
409 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
410 uint32_t u32;
411 unsigned i;
412 PVM pVM = PDMDevHlpGetVM(pDevIns);
413 PPDMIBLOCKBIOS apHDs[4] = {0};
414 PPDMIBLOCKBIOS apFDs[2] = {0};
415 AssertRelease(pVM);
416 LogFlow(("pcbiosInitComplete:\n"));
417
418 /*
419 * Memory sizes.
420 */
421 /* base memory. */
422 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 */
423 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
424 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
425
426 /* Extended memory, up to 65MB */
427 u32 = pThis->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pThis->cbRam - _1M) / _1K;
428 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
429 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
430 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
431 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
432
433 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
434 and below 4GB (as it can only hold 4GB+16M). We have to chop off the
435 top 2MB or it conflict with what the ACPI tables return. (Should these
436 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
437 with the high BIOS mapping.) */
438 uint64_t const offRamHole = _4G - pThis->cbRamHole;
439 if (pThis->cbRam > 16 * _1M)
440 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
441 else
442 u32 = 0;
443 pcbiosCmosWrite(pDevIns, 0x34, u32 & 0xff);
444 pcbiosCmosWrite(pDevIns, 0x35, u32 >> 8);
445
446 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
447 Bochs got these in a different location which we've already used for SATA,
448 it also lacks the last two. */
449 uint64_t c64KBAbove4GB;
450 if (pThis->cbRam <= offRamHole)
451 c64KBAbove4GB = 0;
452 else
453 {
454 c64KBAbove4GB = (pThis->cbRam - offRamHole) / _64K;
455 /* Make sure it doesn't hit the limits of the current BIOS code. */
456 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
457 }
458 pcbiosCmosWrite(pDevIns, 0x61, c64KBAbove4GB & 0xff);
459 pcbiosCmosWrite(pDevIns, 0x62, (c64KBAbove4GB >> 8) & 0xff);
460 pcbiosCmosWrite(pDevIns, 0x63, (c64KBAbove4GB >> 16) & 0xff);
461 pcbiosCmosWrite(pDevIns, 0x64, (c64KBAbove4GB >> 24) & 0xff);
462 pcbiosCmosWrite(pDevIns, 0x65, (c64KBAbove4GB >> 32) & 0xff);
463
464 /*
465 * Number of CPUs.
466 */
467 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
468
469 /*
470 * Bochs BIOS specifics - boot device.
471 * We do both new and old (ami-style) settings.
472 * See rombios.c line ~7215 (int19_function).
473 */
474
475 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
476 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
477 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
478 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
479 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
480 pcbiosCmosWrite(pDevIns, 0x38, reg38);
481 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
482
483 /*
484 * PXE debug option.
485 */
486 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
487
488 /*
489 * Network boot device list.
490 */
491 for (i = 0; i < NET_BOOT_DEVS; ++i)
492 {
493 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, pThis->au16NetBootDev[i] & 0xff);
494 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, pThis->au16NetBootDev[i] >> 8);
495 }
496
497 /*
498 * Floppy drive type.
499 */
500 for (i = 0; i < RT_ELEMENTS(apFDs); i++)
501 {
502 PPDMIBASE pBase;
503 int rc = PDMR3QueryLun(pVM, pThis->pszFDDevice, 0, i, &pBase);
504 if (RT_SUCCESS(rc))
505 apFDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
506 }
507 u32 = 0;
508 if (apFDs[0])
509 switch (apFDs[0]->pfnGetType(apFDs[0]))
510 {
511 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << 4; break;
512 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << 4; break;
513 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << 4; break;
514 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << 4; break;
515 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << 4; break;
516 default: AssertFailed(); break;
517 }
518 if (apFDs[1])
519 switch (apFDs[1]->pfnGetType(apFDs[1]))
520 {
521 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1; break;
522 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2; break;
523 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3; break;
524 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4; break;
525 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5; break;
526 default: AssertFailed(); break;
527 }
528 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
529
530 /*
531 * Equipment byte.
532 */
533 u32 = !!apFDs[0] + !!apFDs[1];
534 switch (u32)
535 {
536 case 1: u32 = 0x01; break; /* floppy installed, 2 drives. */
537 default:u32 = 0; break; /* floppy not installed. */
538 }
539 u32 |= RT_BIT(1); /* math coprocessor installed */
540 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
541 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
542 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
543
544 /*
545 * Harddisks.
546 */
547 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
548 {
549 PPDMIBASE pBase;
550 int rc = PDMR3QueryLun(pVM, pThis->pszHDDevice, 0, i, &pBase);
551 if (RT_SUCCESS(rc))
552 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
553 if ( apHDs[i]
554 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
555 || !apHDs[i]->pfnIsVisible(apHDs[i])))
556 apHDs[i] = NULL;
557 if (apHDs[i])
558 {
559 PDMMEDIAGEOMETRY LCHSGeometry;
560 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
561 AssertRC(rc2);
562
563 if (i < 4)
564 {
565 /* Award BIOS extended drive types for first to fourth disk.
566 * Used by the BIOS for setting the logical geometry. */
567 int offType, offInfo;
568 switch (i)
569 {
570 case 0:
571 offType = 0x19;
572 offInfo = 0x1e;
573 break;
574 case 1:
575 offType = 0x1a;
576 offInfo = 0x26;
577 break;
578 case 2:
579 offType = 0x00;
580 offInfo = 0x67;
581 break;
582 case 3:
583 default:
584 offType = 0x00;
585 offInfo = 0x70;
586 break;
587 }
588 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo,
589 &LCHSGeometry);
590 }
591 LogRel(("DevPcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
592 }
593 }
594
595 /* 0Fh means extended and points to 19h, 1Ah */
596 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
597 pcbiosCmosWrite(pDevIns, 0x12, u32);
598
599 /*
600 * Sata Harddisks.
601 */
602 if (pThis->pszSataDevice)
603 {
604 /* Clear pointers to IDE controller. */
605 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
606 apHDs[i] = NULL;
607
608 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
609 {
610 PPDMIBASE pBase;
611 int rc = PDMR3QueryLun(pVM, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
612 if (RT_SUCCESS(rc))
613 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
614 if ( apHDs[i]
615 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
616 || !apHDs[i]->pfnIsVisible(apHDs[i])))
617 apHDs[i] = NULL;
618 if (apHDs[i])
619 {
620 PDMMEDIAGEOMETRY LCHSGeometry;
621 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
622 AssertRC(rc);
623
624 if (i < 4)
625 {
626 /* Award BIOS extended drive types for first to fourth disk.
627 * Used by the BIOS for setting the logical geometry. */
628 int offInfo;
629 switch (i)
630 {
631 case 0:
632 offInfo = 0x40;
633 break;
634 case 1:
635 offInfo = 0x48;
636 break;
637 case 2:
638 offInfo = 0x50;
639 break;
640 case 3:
641 default:
642 offInfo = 0x58;
643 break;
644 }
645 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo,
646 &LCHSGeometry);
647 }
648 LogRel(("DevPcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
649 }
650 }
651 }
652
653 /* Calculate and store AT-style CMOS checksum. */
654 uint16_t cksum = 0;
655 for (i = 0x10; i < 0x2e; ++i)
656 cksum += pcbiosCmosRead(pDevIns, i);
657 pcbiosCmosWrite(pDevIns, 0x2e, cksum >> 8);
658 pcbiosCmosWrite(pDevIns, 0x2f, cksum & 0xff);
659
660 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
661 return VINF_SUCCESS;
662}
663
664/**
665 * Port I/O Handler for IN operations.
666 *
667 * @returns VBox status code.
668 *
669 * @param pDevIns The device instance.
670 * @param pvUser User argument - ignored.
671 * @param Port Port number used for the IN operation.
672 * @param pu32 Where to store the result.
673 * @param cb Number of bytes read.
674 */
675static DECLCALLBACK(int) pcbiosIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
676{
677 return VERR_IOM_IOPORT_UNUSED;
678}
679
680
681/**
682 * Port I/O Handler for OUT operations.
683 *
684 * @returns VBox status code.
685 *
686 * @param pDevIns The device instance.
687 * @param pvUser User argument - ignored.
688 * @param Port Port number used for the IN operation.
689 * @param u32 The value to output.
690 * @param cb The value size in bytes.
691 */
692static DECLCALLBACK(int) pcbiosIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
693{
694 /*
695 * Bochs BIOS Panic
696 */
697 if ( cb == 2
698 && ( Port == 0x400
699 || Port == 0x401))
700 {
701 Log(("pcbios: PC BIOS panic at rombios.c(%d)\n", u32));
702 AssertReleaseMsgFailed(("PC BIOS panic at rombios.c(%d)\n", u32));
703 return VERR_INTERNAL_ERROR;
704 }
705
706 /*
707 * Bochs BIOS char printing.
708 */
709 if ( cb == 1
710 && ( Port == 0x402
711 || Port == 0x403))
712 {
713 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
714 /* The raw version. */
715 switch (u32)
716 {
717 case '\r': Log2(("pcbios: <return>\n")); break;
718 case '\n': Log2(("pcbios: <newline>\n")); break;
719 case '\t': Log2(("pcbios: <tab>\n")); break;
720 default: Log2(("pcbios: %c (%02x)\n", u32, u32)); break;
721 }
722
723 /* The readable, buffered version. */
724 if (u32 == '\n' || u32 == '\r')
725 {
726 pThis->szMsg[pThis->iMsg] = '\0';
727 if (pThis->iMsg)
728 Log(("pcbios: %s\n", pThis->szMsg));
729 pThis->iMsg = 0;
730 }
731 else
732 {
733 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
734 {
735 pThis->szMsg[pThis->iMsg] = '\0';
736 Log(("pcbios: %s\n", pThis->szMsg));
737 pThis->iMsg = 0;
738 }
739 pThis->szMsg[pThis->iMsg] = (char )u32;
740 pThis->szMsg[++pThis->iMsg] = '\0';
741 }
742 return VINF_SUCCESS;
743 }
744
745 /*
746 * Bochs BIOS shutdown request.
747 */
748 if (cb == 1 && Port == 0x8900)
749 {
750 static const unsigned char szShutdown[] = "Shutdown";
751 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
752 if (u32 == szShutdown[pThis->iShutdown])
753 {
754 pThis->iShutdown++;
755 if (pThis->iShutdown == 8)
756 {
757 pThis->iShutdown = 0;
758 LogRel(("DevPcBios: 8900h shutdown request.\n"));
759 return PDMDevHlpVMPowerOff(pDevIns);
760 }
761 }
762 else
763 pThis->iShutdown = 0;
764 return VINF_SUCCESS;
765 }
766
767 /* not in use. */
768 return VINF_SUCCESS;
769}
770
771/**
772 * Reset notification.
773 *
774 * @returns VBox status.
775 * @param pDevIns The device instance data.
776 */
777static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
778{
779 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
780 LogFlow(("pcbiosReset:\n"));
781
782 if (pThis->u8IOAPIC)
783 FwCommonPlantMpsFloatPtr(pDevIns);
784
785 /*
786 * Re-shadow the LAN ROM image and make it RAM/RAM.
787 *
788 * This is normally done by the BIOS code, but since we're currently lacking
789 * the chipset support for this we do it here (and in the constructor).
790 */
791 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, PAGE_SIZE) >> PAGE_SHIFT;
792 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
793 while (cPages > 0)
794 {
795 uint8_t abPage[PAGE_SIZE];
796 int rc;
797
798 /* Read the (original) ROM page and write it back to the RAM page. */
799 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
800 AssertLogRelRC(rc);
801
802 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
803 AssertLogRelRC(rc);
804 if (RT_FAILURE(rc))
805 memset(abPage, 0xcc, sizeof(abPage));
806
807 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
808 AssertLogRelRC(rc);
809
810 /* Switch to the RAM/RAM mode. */
811 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
812 AssertLogRelRC(rc);
813
814 /* Advance */
815 GCPhys += PAGE_SIZE;
816 cPages--;
817 }
818}
819
820
821/**
822 * Destruct a device instance.
823 *
824 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
825 * resources can be freed correctly.
826 *
827 * @param pDevIns The device instance data.
828 */
829static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
830{
831 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
832 LogFlow(("pcbiosDestruct:\n"));
833 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
834
835 /*
836 * Free MM heap pointers.
837 */
838 if (pThis->pu8PcBios)
839 {
840 MMR3HeapFree(pThis->pu8PcBios);
841 pThis->pu8PcBios = NULL;
842 }
843
844 if (pThis->pszPcBiosFile)
845 {
846 MMR3HeapFree(pThis->pszPcBiosFile);
847 pThis->pszPcBiosFile = NULL;
848 }
849
850 if (pThis->pu8LanBoot)
851 {
852 MMR3HeapFree(pThis->pu8LanBoot);
853 pThis->pu8LanBoot = NULL;
854 }
855
856 if (pThis->pszLanBootFile)
857 {
858 MMR3HeapFree(pThis->pszLanBootFile);
859 pThis->pszLanBootFile = NULL;
860 }
861
862 if (pThis->pszHDDevice)
863 {
864 MMR3HeapFree(pThis->pszHDDevice);
865 pThis->pszHDDevice = NULL;
866 }
867
868 if (pThis->pszFDDevice)
869 {
870 MMR3HeapFree(pThis->pszFDDevice);
871 pThis->pszFDDevice = NULL;
872 }
873
874 if (pThis->pszSataDevice)
875 {
876 MMR3HeapFree(pThis->pszSataDevice);
877 pThis->pszSataDevice = NULL;
878 }
879
880 return VINF_SUCCESS;
881}
882
883
884/**
885 * Convert config value to DEVPCBIOSBOOT.
886 *
887 * @returns VBox status code.
888 * @param pCfg Configuration handle.
889 * @param pszParam The name of the value to read.
890 * @param penmBoot Where to store the boot method.
891 */
892static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
893{
894 char *psz;
895 int rc = CFGMR3QueryStringAlloc(pCfg, pszParam, &psz);
896 if (RT_FAILURE(rc))
897 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
898 N_("Configuration error: Querying \"%s\" as a string failed"),
899 pszParam);
900 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
901 *penmBoot = DEVPCBIOSBOOT_DVD;
902 else if (!strcmp(psz, "IDE"))
903 *penmBoot = DEVPCBIOSBOOT_HD;
904 else if (!strcmp(psz, "FLOPPY"))
905 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
906 else if (!strcmp(psz, "LAN"))
907 *penmBoot = DEVPCBIOSBOOT_LAN;
908 else if (!strcmp(psz, "NONE"))
909 *penmBoot = DEVPCBIOSBOOT_NONE;
910 else
911 {
912 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
913 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
914 pszParam, psz);
915 rc = VERR_INTERNAL_ERROR;
916 }
917 MMR3HeapFree(psz);
918 return rc;
919}
920
921/**
922 * @interface_method_impl{PDMDEVREG,pfnConstruct}
923 */
924static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
925{
926 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
927 int rc;
928 int cb;
929
930 Assert(iInstance == 0);
931 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
932
933 /*
934 * Validate configuration.
935 */
936 if (!CFGMR3AreValuesValid(pCfg,
937 "BootDevice0\0"
938 "BootDevice1\0"
939 "BootDevice2\0"
940 "BootDevice3\0"
941 "RamSize\0"
942 "RamHoleSize\0"
943 "HardDiskDevice\0"
944 "SataHardDiskDevice\0"
945 "SataPrimaryMasterLUN\0"
946 "SataPrimarySlaveLUN\0"
947 "SataSecondaryMasterLUN\0"
948 "SataSecondarySlaveLUN\0"
949 "FloppyDevice\0"
950 "DelayBoot\0"
951 "BiosRom\0"
952 "LanBootRom\0"
953 "PXEDebug\0"
954 "UUID\0"
955 "IOAPIC\0"
956 "NumCPUs\0"
957 "McfgBase\0"
958 "McfgLength\0"
959 "DmiBIOSFirmwareMajor\0"
960 "DmiBIOSFirmwareMinor\0"
961 "DmiBIOSReleaseDate\0"
962 "DmiBIOSReleaseMajor\0"
963 "DmiBIOSReleaseMinor\0"
964 "DmiBIOSVendor\0"
965 "DmiBIOSVersion\0"
966 "DmiSystemFamily\0"
967 "DmiSystemProduct\0"
968 "DmiSystemSerial\0"
969 "DmiSystemSKU\0"
970 "DmiSystemUuid\0"
971 "DmiSystemVendor\0"
972 "DmiSystemVersion\0"
973 "DmiBoardAssetTag\0"
974 "DmiBoardBoardType\0"
975 "DmiBoardLocInChass\0"
976 "DmiBoardProduct\0"
977 "DmiBoardSerial\0"
978 "DmiBoardVendor\0"
979 "DmiBoardVersion\0"
980 "DmiChassisAssetTag\0"
981 "DmiChassisSerial\0"
982 "DmiChassisVendor\0"
983 "DmiChassisVersion\0"
984 "DmiProcManufacturer\0"
985 "DmiProcVersion\0"
986 "DmiOEMVBoxVer\0"
987 "DmiOEMVBoxRev\0"
988 "DmiUseHostInfo\0"
989 "DmiExposeMemoryTable\0"
990 "DmiExposeProcInf\0"
991 ))
992 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
993 N_("Invalid configuration for device pcbios device"));
994
995 /*
996 * Init the data.
997 */
998 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
999 if (RT_FAILURE(rc))
1000 return PDMDEV_SET_ERROR(pDevIns, rc,
1001 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1002
1003 rc = CFGMR3QueryU32Def(pCfg, "RamHoleSize", &pThis->cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
1004 if (RT_FAILURE(rc))
1005 return PDMDEV_SET_ERROR(pDevIns, rc,
1006 N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
1007
1008 rc = CFGMR3QueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1009 if (RT_FAILURE(rc))
1010 return PDMDEV_SET_ERROR(pDevIns, rc,
1011 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1012
1013 rc = CFGMR3QueryU32Def(pCfg, "McfgBase", &pThis->u32McfgBase, 0);
1014 if (RT_FAILURE(rc))
1015 return PDMDEV_SET_ERROR(pDevIns, rc,
1016 N_("Configuration error: Querying \"\" as integer failed"));
1017 rc = CFGMR3QueryU32Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1018 if (RT_FAILURE(rc))
1019 return PDMDEV_SET_ERROR(pDevIns, rc,
1020 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1021
1022
1023 LogRel(("[SMP] BIOS with %u CPUs\n", pThis->cCpus));
1024
1025 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1026 if (RT_FAILURE (rc))
1027 return PDMDEV_SET_ERROR(pDevIns, rc,
1028 N_("Configuration error: Failed to read \"IOAPIC\""));
1029
1030 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1031 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1032 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1033 {
1034 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1035 if (RT_FAILURE(rc))
1036 return rc;
1037 }
1038
1039 rc = CFGMR3QueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1040 if (RT_FAILURE(rc))
1041 return PDMDEV_SET_ERROR(pDevIns, rc,
1042 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1043
1044 rc = CFGMR3QueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1045 if (RT_FAILURE(rc))
1046 return PDMDEV_SET_ERROR(pDevIns, rc,
1047 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1048
1049 rc = CFGMR3QueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1050 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1051 pThis->pszSataDevice = NULL;
1052 else if (RT_FAILURE(rc))
1053 return PDMDEV_SET_ERROR(pDevIns, rc,
1054 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1055
1056 if (pThis->pszSataDevice)
1057 {
1058 static const char * const s_apszSataDisks[] =
1059 { "SataPrimaryMasterLUN", "SataPrimarySlaveLUN", "SataSecondaryMasterLUN", "SataSecondarySlaveLUN" };
1060 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1061 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1062 {
1063 rc = CFGMR3QueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1064 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1065 pThis->iSataHDLUN[i] = i;
1066 else if (RT_FAILURE(rc))
1067 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1068 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1069 }
1070 }
1071 /*
1072 * Register I/O Ports and PC BIOS.
1073 */
1074 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1075 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1076 if (RT_FAILURE(rc))
1077 return rc;
1078 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1079 NULL, NULL, "Bochs PC BIOS - Shutdown");
1080 if (RT_FAILURE(rc))
1081 return rc;
1082
1083 /*
1084 * Read the PXE debug logging option.
1085 */
1086 rc = CFGMR3QueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1087 if (RT_FAILURE(rc))
1088 return PDMDEV_SET_ERROR(pDevIns, rc,
1089 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1090
1091 /* Clear the net boot device list. All bits set invokes old behavior,
1092 * as if no second CMOS bank was present.
1093 */
1094 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1095
1096 /*
1097 * Determine the network boot order.
1098 */
1099 PCFGMNODE pCfgNetBoot = CFGMR3GetChild(pCfg, "NetBoot");
1100 if (pCfgNetBoot == NULL)
1101 {
1102 /* Do nothing. */
1103 rc = VINF_SUCCESS;
1104 }
1105 else
1106 {
1107 PCFGMNODE pCfgNetBootDevice;
1108 uint8_t u8PciBus;
1109 uint8_t u8PciDev;
1110 uint8_t u8PciFn;
1111 uint16_t u16BusDevFn;
1112 char szIndex[] = "?";
1113
1114 Assert(pCfgNetBoot);
1115 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1116 {
1117 szIndex[0] = '0' + i;
1118 pCfgNetBootDevice = CFGMR3GetChild(pCfgNetBoot, szIndex);
1119
1120 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1121 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1122 {
1123 /* Do nothing and stop iterating. */
1124 rc = VINF_SUCCESS;
1125 break;
1126 }
1127 else if (RT_FAILURE(rc))
1128 return PDMDEV_SET_ERROR(pDevIns, rc,
1129 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1130 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1131 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1132 {
1133 /* Do nothing and stop iterating. */
1134 rc = VINF_SUCCESS;
1135 break;
1136 }
1137 else if (RT_FAILURE(rc))
1138 return PDMDEV_SET_ERROR(pDevIns, rc,
1139 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1140 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1141 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1142 {
1143 /* Do nothing and stop iterating. */
1144 rc = VINF_SUCCESS;
1145 break;
1146 }
1147 else if (RT_FAILURE(rc))
1148 return PDMDEV_SET_ERROR(pDevIns, rc,
1149 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1150 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1151 pThis->au16NetBootDev[i] = u16BusDevFn;
1152 }
1153 }
1154
1155 /*
1156 * Get the system BIOS ROM file name.
1157 */
1158 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1159 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1160 {
1161 pThis->pszPcBiosFile = NULL;
1162 rc = VINF_SUCCESS;
1163 }
1164 else if (RT_FAILURE(rc))
1165 return PDMDEV_SET_ERROR(pDevIns, rc,
1166 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1167 else if (!*pThis->pszPcBiosFile)
1168 {
1169 MMR3HeapFree(pThis->pszPcBiosFile);
1170 pThis->pszPcBiosFile = NULL;
1171 }
1172
1173 if (pThis->pszPcBiosFile)
1174 {
1175 /*
1176 * Load the BIOS ROM.
1177 */
1178 RTFILE hFilePcBios;
1179 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1180 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1181 if (RT_SUCCESS(rc))
1182 {
1183 /* Figure the size and check restrictions. */
1184 uint64_t cbPcBios;
1185 rc = RTFileGetSize(hFilePcBios, &cbPcBios);
1186 if (RT_SUCCESS(rc))
1187 {
1188 pThis->cbPcBios = (uint32_t)cbPcBios;
1189 if ( RT_ALIGN(pThis->cbPcBios, _64K) == pThis->cbPcBios
1190 && pThis->cbPcBios == cbPcBios
1191 && pThis->cbPcBios <= 32 * _64K
1192 && pThis->cbPcBios >= _64K)
1193 {
1194 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1195 if (pThis->pu8PcBios)
1196 {
1197 rc = RTFileRead(hFilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1198 if (RT_FAILURE(rc))
1199 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1200 N_("Error reading the BIOS image ('%s)"), pThis->pszPcBiosFile);
1201 }
1202 else
1203 rc = PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1204 N_("Failed to allocate %#x bytes for loading the BIOS image"),
1205 pThis->cbPcBios);
1206 }
1207 else
1208 rc = PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
1209 N_("Invalid system BIOS file size ('%s'): %#llx (%llu)"),
1210 pThis->pszPcBiosFile, cbPcBios, cbPcBios);
1211 }
1212 else
1213 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1214 N_("Failed to query the system BIOS file size ('%s')"),
1215 pThis->pszPcBiosFile);
1216 RTFileClose(hFilePcBios);
1217 }
1218 else
1219 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1220 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1221 if (RT_FAILURE(rc))
1222 return rc;
1223
1224 LogRel(("DevPcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1225 }
1226 else
1227 {
1228 /*
1229 * Use the embedded BIOS ROM image.
1230 */
1231 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, g_cbPcBiosBinary);
1232 if (pThis->pu8PcBios)
1233 {
1234 pThis->cbPcBios = g_cbPcBiosBinary;
1235 memcpy(pThis->pu8PcBios, g_abPcBiosBinary, pThis->cbPcBios);
1236 }
1237 else
1238 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1239 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"),
1240 g_cbPcBiosBinary);
1241 }
1242 const uint8_t *pu8PcBiosBinary = pThis->pu8PcBios;
1243 uint32_t cbPcBiosBinary = pThis->cbPcBios;
1244
1245 /*
1246 * Query the machine's UUID for SMBIOS/DMI use.
1247 */
1248 RTUUID uuid;
1249 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1250 if (RT_FAILURE(rc))
1251 return PDMDEV_SET_ERROR(pDevIns, rc,
1252 N_("Configuration error: Querying \"UUID\" failed"));
1253
1254 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1255 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1256 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1257 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1258 uint16_t cbDmiTables = 0;
1259 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1260 &uuid, pCfg, pThis->cCpus, &cbDmiTables);
1261 if (RT_FAILURE(rc))
1262 return rc;
1263
1264 for (unsigned i = 0; i < pThis->cbPcBios; i += 16)
1265 {
1266 /* If the DMI table is located at the expected place, patch the DMI table length and the checksum. */
1267 if ( pThis->pu8PcBios[i + 0x00] == '_'
1268 && pThis->pu8PcBios[i + 0x01] == 'D'
1269 && pThis->pu8PcBios[i + 0x02] == 'M'
1270 && pThis->pu8PcBios[i + 0x03] == 'I'
1271 && pThis->pu8PcBios[i + 0x04] == '_'
1272 && *(uint16_t*)&pThis->pu8PcBios[i + 0x06] == 0)
1273 {
1274 *(uint16_t*)&pThis->pu8PcBios[i + 0x06] = cbDmiTables;
1275 uint8_t u8Sum = 0;
1276 for (unsigned j = 0; j < pThis->cbPcBios; j++)
1277 if (j != i + 0x05)
1278 u8Sum += pThis->pu8PcBios[j];
1279 pThis->pu8PcBios[i + 0x05] = -u8Sum;
1280 break;
1281 }
1282 }
1283
1284 if (pThis->u8IOAPIC)
1285 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1286 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1287
1288 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
1289 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1290 if (RT_FAILURE(rc))
1291 return rc;
1292
1293 /*
1294 * Map the BIOS into memory.
1295 * There are two mappings:
1296 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1297 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1298 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1299 */
1300 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1301 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1302 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1303 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1304 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb], cb,
1305 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xfffff");
1306 if (RT_FAILURE(rc))
1307 return rc;
1308 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary, cbPcBiosBinary,
1309 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xffffffff");
1310 if (RT_FAILURE(rc))
1311 return rc;
1312
1313 /*
1314 * Get the LAN boot ROM file name.
1315 */
1316 rc = CFGMR3QueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1317 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1318 {
1319 pThis->pszLanBootFile = NULL;
1320 rc = VINF_SUCCESS;
1321 }
1322 else if (RT_FAILURE(rc))
1323 return PDMDEV_SET_ERROR(pDevIns, rc,
1324 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1325 else if (!*pThis->pszLanBootFile)
1326 {
1327 MMR3HeapFree(pThis->pszLanBootFile);
1328 pThis->pszLanBootFile = NULL;
1329 }
1330
1331 uint64_t cbFileLanBoot;
1332 const uint8_t *pu8LanBootBinary = NULL;
1333 uint64_t cbLanBootBinary;
1334
1335 /*
1336 * Determine the LAN boot ROM size, open specified ROM file in the process.
1337 */
1338 RTFILE FileLanBoot = NIL_RTFILE;
1339 if (pThis->pszLanBootFile)
1340 {
1341 rc = RTFileOpen(&FileLanBoot, pThis->pszLanBootFile,
1342 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1343 if (RT_SUCCESS(rc))
1344 {
1345 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1346 if (RT_SUCCESS(rc))
1347 {
1348 if (cbFileLanBoot > _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff))
1349 rc = VERR_TOO_MUCH_DATA;
1350 }
1351 }
1352 if (RT_FAILURE(rc))
1353 {
1354 /*
1355 * Ignore failure and fall back to the built-in LAN boot ROM.
1356 */
1357 LogRel(("DevPcBios: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1358 RTFileClose(FileLanBoot);
1359 FileLanBoot = NIL_RTFILE;
1360 MMR3HeapFree(pThis->pszLanBootFile);
1361 pThis->pszLanBootFile = NULL;
1362 }
1363 }
1364
1365 /*
1366 * Get the LAN boot ROM data.
1367 */
1368 if (pThis->pszLanBootFile)
1369 {
1370 LogRel(("DevPcBios: Using LAN ROM '%s' with a size of %#x bytes\n", pThis->pszLanBootFile, cbFileLanBoot));
1371 /*
1372 * Allocate buffer for the LAN boot ROM data.
1373 */
1374 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, cbFileLanBoot);
1375 if (pThis->pu8LanBoot)
1376 {
1377 rc = RTFileRead(FileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1378 if (RT_FAILURE(rc))
1379 {
1380 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", cbFileLanBoot, rc));
1381 MMR3HeapFree(pThis->pu8LanBoot);
1382 pThis->pu8LanBoot = NULL;
1383 }
1384 rc = VINF_SUCCESS;
1385 }
1386 else
1387 rc = VERR_NO_MEMORY;
1388 }
1389 else
1390 pThis->pu8LanBoot = NULL;
1391
1392 /* cleanup */
1393 if (FileLanBoot != NIL_RTFILE)
1394 RTFileClose(FileLanBoot);
1395
1396 /* If we were unable to get the data from file for whatever reason, fall
1397 * back to the built-in LAN boot ROM image.
1398 */
1399 if (pThis->pu8LanBoot == NULL)
1400 {
1401#ifdef VBOX_WITH_PXE_ROM
1402 pu8LanBootBinary = g_abNetBiosBinary;
1403 cbLanBootBinary = g_cbNetBiosBinary;
1404#endif
1405 }
1406 else
1407 {
1408 pu8LanBootBinary = pThis->pu8LanBoot;
1409 cbLanBootBinary = cbFileLanBoot;
1410 }
1411
1412 /*
1413 * Map the Network Boot ROM into memory.
1414 * Currently there is a fixed mapping: 0x000e2000 to 0x000effff contains
1415 * the (up to) 56 kb ROM image. The mapping size is fixed to trouble with
1416 * the saved state (in PGM).
1417 */
1418 if (pu8LanBootBinary)
1419 {
1420 pThis->cbLanBoot = cbLanBootBinary;
1421
1422 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4,
1423 RT_MAX(cbLanBootBinary, _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff)),
1424 pu8LanBootBinary, cbLanBootBinary,
1425 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1426 AssertRCReturn(rc, rc);
1427 }
1428
1429 rc = CFGMR3QueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1430 if (RT_FAILURE(rc))
1431 return PDMDEV_SET_ERROR(pDevIns, rc,
1432 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1433 if (pThis->uBootDelay > 15)
1434 pThis->uBootDelay = 15;
1435
1436 /*
1437 * Call reset plant tables and shadow the PXE ROM.
1438 */
1439 pcbiosReset(pDevIns);
1440
1441 return VINF_SUCCESS;
1442}
1443
1444
1445/**
1446 * The device registration structure.
1447 */
1448const PDMDEVREG g_DevicePcBios =
1449{
1450 /* u32Version */
1451 PDM_DEVREG_VERSION,
1452 /* szName */
1453 "pcbios",
1454 /* szRCMod */
1455 "",
1456 /* szR0Mod */
1457 "",
1458 /* pszDescription */
1459 "PC BIOS Device",
1460 /* fFlags */
1461 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1462 /* fClass */
1463 PDM_DEVREG_CLASS_ARCH_BIOS,
1464 /* cMaxInstances */
1465 1,
1466 /* cbInstance */
1467 sizeof(DEVPCBIOS),
1468 /* pfnConstruct */
1469 pcbiosConstruct,
1470 /* pfnDestruct */
1471 pcbiosDestruct,
1472 /* pfnRelocate */
1473 NULL,
1474 /* pfnIOCtl */
1475 NULL,
1476 /* pfnPowerOn */
1477 NULL,
1478 /* pfnReset */
1479 pcbiosReset,
1480 /* pfnSuspend */
1481 NULL,
1482 /* pfnResume */
1483 NULL,
1484 /* pfnAttach */
1485 NULL,
1486 /* pfnDetach */
1487 NULL,
1488 /* pfnQueryInterface. */
1489 NULL,
1490 /* pfnInitComplete. */
1491 pcbiosInitComplete,
1492 /* pfnPowerOff */
1493 NULL,
1494 /* pfnSoftReset */
1495 NULL,
1496 /* u32VersionEnd */
1497 PDM_DEVREG_VERSION
1498};
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