VirtualBox

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

Last change on this file since 44534 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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