VirtualBox

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

Last change on this file since 35853 was 35400, checked in by vboxsync, 14 years ago

introduced VBOX_WITH_PXE_ROM

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