VirtualBox

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

Last change on this file since 25893 was 25780, checked in by vboxsync, 15 years ago

Misc: -Wshadow warnings.

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