VirtualBox

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

Last change on this file since 27491 was 27470, checked in by vboxsync, 15 years ago

DevRTC: fixed assertion (addendum to r58944)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 47.0 KB
Line 
1/* $Id: DevPcBios.cpp 27470 2010-03-18 09:51:21Z 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 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
772 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
773
774 /*
775 * Re-shadow the LAN ROM image and make it RAM/RAM.
776 *
777 * This is normally done by the BIOS code, but since we're currently lacking
778 * the chipset support for this we do it here (and in the constructor).
779 */
780 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, PAGE_SIZE) >> PAGE_SHIFT;
781 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
782 while (cPages > 0)
783 {
784 uint8_t abPage[PAGE_SIZE];
785 int rc;
786
787 /* Read the (original) ROM page and write it back to the RAM page. */
788 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
789 AssertLogRelRC(rc);
790
791 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
792 AssertLogRelRC(rc);
793 if (RT_FAILURE(rc))
794 memset(abPage, 0xcc, sizeof(abPage));
795
796 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
797 AssertLogRelRC(rc);
798
799 /* Switch to the RAM/RAM mode. */
800 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
801 AssertLogRelRC(rc);
802
803 /* Advance */
804 GCPhys += PAGE_SIZE;
805 cPages--;
806 }
807}
808
809
810/**
811 * Destruct a device instance.
812 *
813 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
814 * resources can be freed correctly.
815 *
816 * @param pDevIns The device instance data.
817 */
818static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
819{
820 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
821 LogFlow(("pcbiosDestruct:\n"));
822 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
823
824 /*
825 * Free MM heap pointers.
826 */
827 if (pThis->pu8PcBios)
828 {
829 MMR3HeapFree(pThis->pu8PcBios);
830 pThis->pu8PcBios = NULL;
831 }
832
833 if (pThis->pszPcBiosFile)
834 {
835 MMR3HeapFree(pThis->pszPcBiosFile);
836 pThis->pszPcBiosFile = NULL;
837 }
838
839 if (pThis->pu8LanBoot)
840 {
841 MMR3HeapFree(pThis->pu8LanBoot);
842 pThis->pu8LanBoot = NULL;
843 }
844
845 if (pThis->pszLanBootFile)
846 {
847 MMR3HeapFree(pThis->pszLanBootFile);
848 pThis->pszLanBootFile = NULL;
849 }
850
851 return VINF_SUCCESS;
852}
853
854
855/**
856 * Convert config value to DEVPCBIOSBOOT.
857 *
858 * @returns VBox status code.
859 * @param pCfg Configuration handle.
860 * @param pszParam The name of the value to read.
861 * @param penmBoot Where to store the boot method.
862 */
863static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
864{
865 char *psz;
866 int rc = CFGMR3QueryStringAlloc(pCfg, pszParam, &psz);
867 if (RT_FAILURE(rc))
868 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
869 N_("Configuration error: Querying \"%s\" as a string failed"),
870 pszParam);
871 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
872 *penmBoot = DEVPCBIOSBOOT_DVD;
873 else if (!strcmp(psz, "IDE"))
874 *penmBoot = DEVPCBIOSBOOT_HD;
875 else if (!strcmp(psz, "FLOPPY"))
876 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
877 else if (!strcmp(psz, "LAN"))
878 *penmBoot = DEVPCBIOSBOOT_LAN;
879 else if (!strcmp(psz, "NONE"))
880 *penmBoot = DEVPCBIOSBOOT_NONE;
881 else
882 {
883 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
884 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
885 pszParam, psz);
886 rc = VERR_INTERNAL_ERROR;
887 }
888 MMR3HeapFree(psz);
889 return rc;
890}
891
892/**
893 * @interface_method_impl{PDMDEVREG,pfnConstruct}
894 */
895static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
896{
897 unsigned i;
898 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
899 int rc;
900 int cb;
901
902 Assert(iInstance == 0);
903 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
904
905 /*
906 * Validate configuration.
907 */
908 if (!CFGMR3AreValuesValid(pCfg,
909 "BootDevice0\0"
910 "BootDevice1\0"
911 "BootDevice2\0"
912 "BootDevice3\0"
913 "RamSize\0"
914 "RamHoleSize\0"
915 "HardDiskDevice\0"
916 "SataHardDiskDevice\0"
917 "SataPrimaryMasterLUN\0"
918 "SataPrimarySlaveLUN\0"
919 "SataSecondaryMasterLUN\0"
920 "SataSecondarySlaveLUN\0"
921 "FloppyDevice\0"
922 "DelayBoot\0"
923 "BiosRom\0"
924 "LanBootRom\0"
925 "PXEDebug\0"
926 "UUID\0"
927 "IOAPIC\0"
928 "NumCPUs\0"
929 "DmiBIOSVendor\0"
930 "DmiBIOSVersion\0"
931 "DmiBIOSReleaseDate\0"
932 "DmiBIOSReleaseMajor\0"
933 "DmiBIOSReleaseMinor\0"
934 "DmiBIOSFirmwareMajor\0"
935 "DmiBIOSFirmwareMinor\0"
936 "DmiSystemFamily\0"
937 "DmiSystemProduct\0"
938 "DmiSystemSerial\0"
939 "DmiSystemUuid\0"
940 "DmiSystemVendor\0"
941 "DmiSystemVersion\0"
942 "DmiChassisVendor\0"
943 "DmiChassisVersion\0"
944 "DmiChassisSerial\0"
945 "DmiChassisAssetTag\0"
946#ifdef VBOX_WITH_DMI_OEMSTRINGS
947 "DmiOEMVBoxVer\0"
948 "DmiOEMVBoxRev\0"
949#endif
950 "DmiUseHostInfo\0"
951 ))
952 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
953 N_("Invalid configuration for device pcbios device"));
954
955 /*
956 * Init the data.
957 */
958 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
959 if (RT_FAILURE(rc))
960 return PDMDEV_SET_ERROR(pDevIns, rc,
961 N_("Configuration error: Querying \"RamSize\" as integer failed"));
962
963 rc = CFGMR3QueryU32Def(pCfg, "RamHoleSize", &pThis->cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
964 if (RT_FAILURE(rc))
965 return PDMDEV_SET_ERROR(pDevIns, rc,
966 N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
967
968 rc = CFGMR3QueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
969 if (RT_FAILURE(rc))
970 return PDMDEV_SET_ERROR(pDevIns, rc,
971 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
972
973 LogRel(("[SMP] BIOS with %u CPUs\n", pThis->cCpus));
974
975 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
976 if (RT_FAILURE (rc))
977 return PDMDEV_SET_ERROR(pDevIns, rc,
978 N_("Configuration error: Failed to read \"IOAPIC\""));
979
980 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
981 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
982 for (i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
983 {
984 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
985 if (RT_FAILURE(rc))
986 return rc;
987 }
988
989 rc = CFGMR3QueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
990 if (RT_FAILURE(rc))
991 return PDMDEV_SET_ERROR(pDevIns, rc,
992 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
993
994 rc = CFGMR3QueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
995 if (RT_FAILURE(rc))
996 return PDMDEV_SET_ERROR(pDevIns, rc,
997 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
998
999 rc = CFGMR3QueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1000 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1001 pThis->pszSataDevice = NULL;
1002 else if (RT_FAILURE(rc))
1003 return PDMDEV_SET_ERROR(pDevIns, rc,
1004 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1005
1006 if (pThis->pszSataDevice)
1007 {
1008 static const char * const s_apszSataDisks[] =
1009 { "SataPrimaryMasterLUN", "SataPrimarySlaveLUN", "SataSecondaryMasterLUN", "SataSecondarySlaveLUN" };
1010 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1011 for (i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1012 {
1013 rc = CFGMR3QueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1014 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1015 pThis->iSataHDLUN[i] = i;
1016 else if (RT_FAILURE(rc))
1017 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1018 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1019 }
1020 }
1021 /*
1022 * Register I/O Ports and PC BIOS.
1023 */
1024 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1025 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1026 if (RT_FAILURE(rc))
1027 return rc;
1028 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1029 NULL, NULL, "Bochs PC BIOS - Shutdown");
1030 if (RT_FAILURE(rc))
1031 return rc;
1032
1033 /*
1034 * Query the machine's UUID for SMBIOS/DMI use.
1035 */
1036 RTUUID uuid;
1037 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1038 if (RT_FAILURE(rc))
1039 return PDMDEV_SET_ERROR(pDevIns, rc,
1040 N_("Configuration error: Querying \"UUID\" failed"));
1041
1042
1043 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1044 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1045 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1046 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1047 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage,
1048 VBOX_DMI_TABLE_SIZE, &uuid, pCfg, /*fPutSmbiosHeaders=*/false);
1049 if (RT_FAILURE(rc))
1050 return rc;
1051 if (pThis->u8IOAPIC)
1052 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1053 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1054
1055 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage,
1056 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1057 if (RT_FAILURE(rc))
1058 return rc;
1059
1060 /*
1061 * Read the PXE debug logging option.
1062 */
1063 rc = CFGMR3QueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1064 if (RT_FAILURE(rc))
1065 return PDMDEV_SET_ERROR(pDevIns, rc,
1066 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1067
1068 /* Clear the net boot device list. All bits set invokes old behavior,
1069 * as if no second CMOS bank was present.
1070 */
1071 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1072
1073 /*
1074 * Determine the network boot order.
1075 */
1076 PCFGMNODE pCfgNetBoot = CFGMR3GetChild(pCfg, "NetBoot");
1077 if (pCfgNetBoot == NULL)
1078 {
1079 /* Do nothing. */
1080 rc = VINF_SUCCESS;
1081 }
1082 else
1083 {
1084 PCFGMNODE pCfgNetBootDevice;
1085 uint16_t u16BusDevFn;
1086 char szIndex[] = "?";
1087
1088 Assert(pCfgNetBoot);
1089 for (i = 0; i < NET_BOOT_DEVS; ++i)
1090 {
1091 szIndex[0] = '0' + i;
1092 pCfgNetBootDevice = CFGMR3GetChild(pCfgNetBoot, szIndex);
1093 rc = CFGMR3QueryU16(pCfgNetBootDevice, "BusDevFn", &u16BusDevFn);
1094 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1095 {
1096 /* Do nothing and stop iterating. */
1097 rc = VINF_SUCCESS;
1098 break;
1099 }
1100 else if (RT_FAILURE(rc))
1101 return PDMDEV_SET_ERROR(pDevIns, rc,
1102 N_("Configuration error: Querying \"Netboot/x/BusDevFn\" as integer failed"));
1103 pThis->au16NetBootDev[i] = u16BusDevFn;
1104 }
1105 }
1106
1107 /*
1108 * Get the system BIOS ROM file name.
1109 */
1110 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1111 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1112 {
1113 pThis->pszPcBiosFile = NULL;
1114 rc = VINF_SUCCESS;
1115 }
1116 else if (RT_FAILURE(rc))
1117 return PDMDEV_SET_ERROR(pDevIns, rc,
1118 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1119 else if (!*pThis->pszPcBiosFile)
1120 {
1121 MMR3HeapFree(pThis->pszPcBiosFile);
1122 pThis->pszPcBiosFile = NULL;
1123 }
1124
1125 const uint8_t *pu8PcBiosBinary = NULL;
1126 uint64_t cbPcBiosBinary;
1127 /*
1128 * Determine the system BIOS ROM size, open specified ROM file in the process.
1129 */
1130 RTFILE FilePcBios = NIL_RTFILE;
1131 if (pThis->pszPcBiosFile)
1132 {
1133 rc = RTFileOpen(&FilePcBios, pThis->pszPcBiosFile,
1134 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1135 if (RT_SUCCESS(rc))
1136 {
1137 rc = RTFileGetSize(FilePcBios, &pThis->cbPcBios);
1138 if (RT_SUCCESS(rc))
1139 {
1140 /* The following checks should be in sync the AssertReleaseMsg's below. */
1141 if ( RT_ALIGN(pThis->cbPcBios, _64K) != pThis->cbPcBios
1142 || pThis->cbPcBios > 32 * _64K
1143 || pThis->cbPcBios < _64K)
1144 rc = VERR_TOO_MUCH_DATA;
1145 }
1146 }
1147 if (RT_FAILURE(rc))
1148 {
1149 /*
1150 * In case of failure simply fall back to the built-in BIOS ROM.
1151 */
1152 Log(("pcbiosConstruct: Failed to open system BIOS ROM file '%s', rc=%Rrc!\n", pThis->pszPcBiosFile, rc));
1153 RTFileClose(FilePcBios);
1154 FilePcBios = NIL_RTFILE;
1155 MMR3HeapFree(pThis->pszPcBiosFile);
1156 pThis->pszPcBiosFile = NULL;
1157 }
1158 }
1159
1160 /*
1161 * Attempt to get the system BIOS ROM data from file.
1162 */
1163 if (pThis->pszPcBiosFile)
1164 {
1165 /*
1166 * Allocate buffer for the system BIOS ROM data.
1167 */
1168 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1169 if (pThis->pu8PcBios)
1170 {
1171 rc = RTFileRead(FilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1172 if (RT_FAILURE(rc))
1173 {
1174 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", pThis->cbPcBios, rc));
1175 MMR3HeapFree(pThis->pu8PcBios);
1176 pThis->pu8PcBios = NULL;
1177 }
1178 rc = VINF_SUCCESS;
1179 }
1180 else
1181 rc = VERR_NO_MEMORY;
1182 }
1183 else
1184 pThis->pu8PcBios = NULL;
1185
1186 /* cleanup */
1187 if (FilePcBios != NIL_RTFILE)
1188 RTFileClose(FilePcBios);
1189
1190 /* If we were unable to get the data from file for whatever reason, fall
1191 back to the built-in ROM image. */
1192 uint32_t fFlags = 0;
1193 if (pThis->pu8PcBios == NULL)
1194 {
1195 pu8PcBiosBinary = g_abPcBiosBinary;
1196 cbPcBiosBinary = g_cbPcBiosBinary;
1197 fFlags = PGMPHYS_ROM_FLAGS_PERMANENT_BINARY;
1198 }
1199 else
1200 {
1201 pu8PcBiosBinary = pThis->pu8PcBios;
1202 cbPcBiosBinary = pThis->cbPcBios;
1203 }
1204
1205 /*
1206 * Map the BIOS into memory.
1207 * There are two mappings:
1208 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1209 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1210 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1211 */
1212 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1213 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1214 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1215 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1216 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb],
1217 fFlags, "PC BIOS - 0xfffff");
1218 if (RT_FAILURE(rc))
1219 return rc;
1220 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary,
1221 fFlags, "PC BIOS - 0xffffffff");
1222 if (RT_FAILURE(rc))
1223 return rc;
1224
1225#ifdef VBOX_WITH_VMI
1226 /*
1227 * Map the VMI BIOS into memory.
1228 */
1229 AssertReleaseMsg(g_cbVmiBiosBinary == _4K, ("cbVmiBiosBinary=%#x\n", g_cbVmiBiosBinary));
1230 rc = PDMDevHlpROMRegister(pDevIns, VBOX_VMI_BIOS_BASE, g_cbVmiBiosBinary, g_abVmiBiosBinary,
1231 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "VMI BIOS");
1232 if (RT_FAILURE(rc))
1233 return rc;
1234#endif /* VBOX_WITH_VMI */
1235
1236 /*
1237 * Call reset to set values and stuff.
1238 */
1239 pcbiosReset(pDevIns);
1240
1241 /*
1242 * Get the LAN boot ROM file name.
1243 */
1244 rc = CFGMR3QueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1245 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1246 {
1247 pThis->pszLanBootFile = NULL;
1248 rc = VINF_SUCCESS;
1249 }
1250 else if (RT_FAILURE(rc))
1251 return PDMDEV_SET_ERROR(pDevIns, rc,
1252 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1253 else if (!*pThis->pszLanBootFile)
1254 {
1255 MMR3HeapFree(pThis->pszLanBootFile);
1256 pThis->pszLanBootFile = NULL;
1257 }
1258
1259 uint64_t cbFileLanBoot;
1260 const uint8_t *pu8LanBootBinary = NULL;
1261 uint64_t cbLanBootBinary;
1262
1263 /*
1264 * Determine the LAN boot ROM size, open specified ROM file in the process.
1265 */
1266 RTFILE FileLanBoot = NIL_RTFILE;
1267 if (pThis->pszLanBootFile)
1268 {
1269 rc = RTFileOpen(&FileLanBoot, pThis->pszLanBootFile,
1270 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1271 if (RT_SUCCESS(rc))
1272 {
1273 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1274 if (RT_SUCCESS(rc))
1275 {
1276 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1277 || cbFileLanBoot > _64K)
1278 rc = VERR_TOO_MUCH_DATA;
1279 }
1280 }
1281 if (RT_FAILURE(rc))
1282 {
1283 /*
1284 * Ignore failure and fall back to the built-in LAN boot ROM.
1285 */
1286 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1287 RTFileClose(FileLanBoot);
1288 FileLanBoot = NIL_RTFILE;
1289 MMR3HeapFree(pThis->pszLanBootFile);
1290 pThis->pszLanBootFile = NULL;
1291 }
1292 }
1293
1294 /*
1295 * Get the LAN boot ROM data.
1296 */
1297 if (pThis->pszLanBootFile)
1298 {
1299 /*
1300 * Allocate buffer for the LAN boot ROM data.
1301 */
1302 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1303 if (pThis->pu8LanBoot)
1304 {
1305 rc = RTFileRead(FileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1306 if (RT_FAILURE(rc))
1307 {
1308 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", cbFileLanBoot, rc));
1309 MMR3HeapFree(pThis->pu8LanBoot);
1310 pThis->pu8LanBoot = NULL;
1311 }
1312 rc = VINF_SUCCESS;
1313 }
1314 else
1315 rc = VERR_NO_MEMORY;
1316 }
1317 else
1318 pThis->pu8LanBoot = NULL;
1319
1320 /* cleanup */
1321 if (FileLanBoot != NIL_RTFILE)
1322 RTFileClose(FileLanBoot);
1323
1324 /* If we were unable to get the data from file for whatever reason, fall
1325 * back to the built-in LAN boot ROM image.
1326 */
1327 if (pThis->pu8LanBoot == NULL)
1328 {
1329 pu8LanBootBinary = g_abNetBiosBinary;
1330 cbLanBootBinary = g_cbNetBiosBinary;
1331 }
1332 else
1333 {
1334 pu8LanBootBinary = pThis->pu8LanBoot;
1335 cbLanBootBinary = cbFileLanBoot;
1336 }
1337
1338 /*
1339 * Map the Network Boot ROM into memory.
1340 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1341 * the (up to) 32 kb ROM image.
1342 */
1343 if (pu8LanBootBinary)
1344 {
1345 pThis->cbLanBoot = cbLanBootBinary;
1346
1347 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbLanBootBinary, pu8LanBootBinary,
1348 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1349 if (RT_SUCCESS(rc))
1350 {
1351 rc = PDMDevHlpROMProtectShadow(pDevIns, VBOX_LANBOOT_SEG << 4, cbLanBootBinary, PGMROMPROT_READ_RAM_WRITE_RAM);
1352 AssertRCReturn(rc, rc);
1353 rc = PDMDevHlpPhysWrite(pDevIns, VBOX_LANBOOT_SEG << 4, pu8LanBootBinary, cbLanBootBinary);
1354 AssertRCReturn(rc, rc);
1355 }
1356 }
1357
1358 rc = CFGMR3QueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1359 if (RT_FAILURE(rc))
1360 return PDMDEV_SET_ERROR(pDevIns, rc,
1361 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1362 if (pThis->uBootDelay > 15)
1363 pThis->uBootDelay = 15;
1364
1365 return rc;
1366}
1367
1368
1369/**
1370 * The device registration structure.
1371 */
1372const PDMDEVREG g_DevicePcBios =
1373{
1374 /* u32Version */
1375 PDM_DEVREG_VERSION,
1376 /* szName */
1377 "pcbios",
1378 /* szRCMod */
1379 "",
1380 /* szR0Mod */
1381 "",
1382 /* pszDescription */
1383 "PC BIOS Device",
1384 /* fFlags */
1385 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1386 /* fClass */
1387 PDM_DEVREG_CLASS_ARCH_BIOS,
1388 /* cMaxInstances */
1389 1,
1390 /* cbInstance */
1391 sizeof(DEVPCBIOS),
1392 /* pfnConstruct */
1393 pcbiosConstruct,
1394 /* pfnDestruct */
1395 pcbiosDestruct,
1396 /* pfnRelocate */
1397 NULL,
1398 /* pfnIOCtl */
1399 NULL,
1400 /* pfnPowerOn */
1401 NULL,
1402 /* pfnReset */
1403 pcbiosReset,
1404 /* pfnSuspend */
1405 NULL,
1406 /* pfnResume */
1407 NULL,
1408 /* pfnAttach */
1409 NULL,
1410 /* pfnDetach */
1411 NULL,
1412 /* pfnQueryInterface. */
1413 NULL,
1414 /* pfnInitComplete. */
1415 pcbiosInitComplete,
1416 /* pfnPowerOff */
1417 NULL,
1418 /* pfnSoftReset */
1419 NULL,
1420 /* u32VersionEnd */
1421 PDM_DEVREG_VERSION
1422};
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