VirtualBox

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

Last change on this file since 34426 was 34426, checked in by vboxsync, 14 years ago

BIOS: report MCFG region via E820, if present

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