VirtualBox

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

Last change on this file since 26663 was 26601, checked in by vboxsync, 15 years ago

host DMI info support

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