VirtualBox

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

Last change on this file since 28487 was 28424, checked in by vboxsync, 15 years ago

firmware: Factored out the SMBIOS & DMI header planting from FwCommonPlantDMITable.

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

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