VirtualBox

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

Last change on this file since 46757 was 46356, checked in by vboxsync, 12 years ago

Devices/PC/DMI: added DmiChassisType and fixed documentation of DmiBoardBoardType -- missing adapations

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