VirtualBox

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

Last change on this file since 28952 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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