VirtualBox

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

Last change on this file since 49469 was 48947, checked in by vboxsync, 11 years ago

Devices: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 50.0 KB
Line 
1/* $Id: DevPcBios.cpp 48947 2013-10-07 21:41:00Z 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 LogFlow(("pcbiosInitComplete:\n"));
505
506 /*
507 * Memory sizes.
508 */
509 /* base memory. */
510 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 */
511 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
512 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
513
514 /* Extended memory, up to 65MB */
515 u32 = pThis->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pThis->cbRam - _1M) / _1K;
516 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
517 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
518 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
519 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
520
521 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
522 and below 4GB (as it can only hold 4GB+16M). We have to chop off the
523 top 2MB or it conflict with what the ACPI tables return. (Should these
524 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
525 with the high BIOS mapping.) */
526 uint64_t const offRamHole = _4G - pThis->cbRamHole;
527 if (pThis->cbRam > 16 * _1M)
528 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
529 else
530 u32 = 0;
531 pcbiosCmosWrite(pDevIns, 0x34, u32 & 0xff);
532 pcbiosCmosWrite(pDevIns, 0x35, u32 >> 8);
533
534 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
535 Bochs got these in a different location which we've already used for SATA,
536 it also lacks the last two. */
537 uint64_t c64KBAbove4GB;
538 if (pThis->cbRam <= offRamHole)
539 c64KBAbove4GB = 0;
540 else
541 {
542 c64KBAbove4GB = (pThis->cbRam - offRamHole) / _64K;
543 /* Make sure it doesn't hit the limits of the current BIOS code. */
544 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
545 }
546 pcbiosCmosWrite(pDevIns, 0x61, c64KBAbove4GB & 0xff);
547 pcbiosCmosWrite(pDevIns, 0x62, (c64KBAbove4GB >> 8) & 0xff);
548 pcbiosCmosWrite(pDevIns, 0x63, (c64KBAbove4GB >> 16) & 0xff);
549 pcbiosCmosWrite(pDevIns, 0x64, (c64KBAbove4GB >> 24) & 0xff);
550 pcbiosCmosWrite(pDevIns, 0x65, (c64KBAbove4GB >> 32) & 0xff);
551
552 /*
553 * Number of CPUs.
554 */
555 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
556
557 /*
558 * Bochs BIOS specifics - boot device.
559 * We do both new and old (ami-style) settings.
560 * See rombios.c line ~7215 (int19_function).
561 */
562
563 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
564 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
565 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
566 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
567 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
568 pcbiosCmosWrite(pDevIns, 0x38, reg38);
569 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
570
571 /*
572 * PXE debug option.
573 */
574 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
575
576 /*
577 * Network boot device list.
578 */
579 for (i = 0; i < NET_BOOT_DEVS; ++i)
580 {
581 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, pThis->au16NetBootDev[i] & 0xff);
582 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, pThis->au16NetBootDev[i] >> 8);
583 }
584
585 /*
586 * Floppy drive type.
587 */
588 uint32_t cFDs = 0;
589 u32 = 0;
590 for (i = 0; i < 2; i++)
591 {
592 PPDMIBASE pBase;
593 int rc = PDMR3QueryLun(pUVM, pThis->pszFDDevice, 0, i, &pBase);
594 if (RT_SUCCESS(rc))
595 {
596 PPDMIBLOCKBIOS pFD = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
597 if (pFD)
598 {
599 cFDs++;
600 unsigned cShift = i == 0 ? 4 : 0;
601 switch (pFD->pfnGetType(pFD))
602 {
603 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << cShift; break;
604 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << cShift; break;
605 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << cShift; break;
606 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << cShift; break;
607 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << cShift; break;
608 case PDMBLOCKTYPE_FLOPPY_FAKE_15_6: u32 |= 14 << cShift; break;
609 case PDMBLOCKTYPE_FLOPPY_FAKE_63_5: u32 |= 15 << cShift; break;
610 default: AssertFailed(); break;
611 }
612 }
613 }
614 }
615 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
616
617 /*
618 * Equipment byte.
619 */
620 if (cFDs > 0)
621 u32 = 0x01; /* floppy installed, 2 drives. */
622 else
623 u32 = 0x00; /* floppy not installed. */
624 u32 |= RT_BIT(1); /* math coprocessor installed */
625 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
626 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
627 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
628
629 /*
630 * Harddisks.
631 */
632 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
633 {
634 PPDMIBASE pBase;
635 int rc = PDMR3QueryLun(pUVM, pThis->pszHDDevice, 0, i, &pBase);
636 if (RT_SUCCESS(rc))
637 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
638 if ( apHDs[i]
639 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
640 || !apHDs[i]->pfnIsVisible(apHDs[i])))
641 apHDs[i] = NULL;
642 if (apHDs[i])
643 {
644 PDMMEDIAGEOMETRY LCHSGeometry;
645 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
646 AssertRC(rc2);
647
648 if (i < 4)
649 {
650 /* Award BIOS extended drive types for first to fourth disk.
651 * Used by the BIOS for setting the logical geometry. */
652 int offType, offInfo;
653 switch (i)
654 {
655 case 0:
656 offType = 0x19;
657 offInfo = 0x1e;
658 break;
659 case 1:
660 offType = 0x1a;
661 offInfo = 0x26;
662 break;
663 case 2:
664 offType = 0x00;
665 offInfo = 0x67;
666 break;
667 case 3:
668 default:
669 offType = 0x00;
670 offInfo = 0x70;
671 break;
672 }
673 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo,
674 &LCHSGeometry);
675 }
676 LogRel(("DevPcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
677 }
678 }
679
680 /* 0Fh means extended and points to 19h, 1Ah */
681 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
682 pcbiosCmosWrite(pDevIns, 0x12, u32);
683
684 /*
685 * Sata Harddisks.
686 */
687 if (pThis->pszSataDevice)
688 {
689 /* Clear pointers to IDE controller. */
690 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
691 apHDs[i] = NULL;
692
693 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
694 {
695 PPDMIBASE pBase;
696 int rc = PDMR3QueryLun(pUVM, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
697 if (RT_SUCCESS(rc))
698 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
699 if ( apHDs[i]
700 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
701 || !apHDs[i]->pfnIsVisible(apHDs[i])))
702 apHDs[i] = NULL;
703 if (apHDs[i])
704 {
705 PDMMEDIAGEOMETRY LCHSGeometry;
706 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
707 AssertRC(rc);
708
709 if (i < 4)
710 {
711 /* Award BIOS extended drive types for first to fourth disk.
712 * Used by the BIOS for setting the logical geometry. */
713 int offInfo;
714 switch (i)
715 {
716 case 0:
717 offInfo = 0x40;
718 break;
719 case 1:
720 offInfo = 0x48;
721 break;
722 case 2:
723 offInfo = 0x50;
724 break;
725 case 3:
726 default:
727 offInfo = 0x58;
728 break;
729 }
730 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo,
731 &LCHSGeometry);
732 }
733 LogRel(("DevPcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
734 }
735 }
736 }
737
738 /* Calculate and store AT-style CMOS checksum. */
739 uint16_t cksum = 0;
740 for (i = 0x10; i < 0x2e; ++i)
741 cksum += pcbiosCmosRead(pDevIns, i);
742 pcbiosCmosWrite(pDevIns, 0x2e, cksum >> 8);
743 pcbiosCmosWrite(pDevIns, 0x2f, cksum & 0xff);
744
745 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
746 return VINF_SUCCESS;
747}
748
749
750/**
751 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
752 */
753static DECLCALLBACK(void) pcbiosMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
754{
755 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
756 LogFlow(("pcbiosMemSetup:\n"));
757
758 if (pThis->u8IOAPIC)
759 FwCommonPlantMpsFloatPtr(pDevIns);
760
761 /*
762 * Re-shadow the LAN ROM image and make it RAM/RAM.
763 *
764 * This is normally done by the BIOS code, but since we're currently lacking
765 * the chipset support for this we do it here (and in the constructor).
766 */
767 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, PAGE_SIZE) >> PAGE_SHIFT;
768 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
769 while (cPages > 0)
770 {
771 uint8_t abPage[PAGE_SIZE];
772 int rc;
773
774 /* Read the (original) ROM page and write it back to the RAM page. */
775 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
776 AssertLogRelRC(rc);
777
778 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
779 AssertLogRelRC(rc);
780 if (RT_FAILURE(rc))
781 memset(abPage, 0xcc, sizeof(abPage));
782
783 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
784 AssertLogRelRC(rc);
785
786 /* Switch to the RAM/RAM mode. */
787 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
788 AssertLogRelRC(rc);
789
790 /* Advance */
791 GCPhys += PAGE_SIZE;
792 cPages--;
793 }
794}
795
796
797/**
798 * @interface_method_impl{PDMDEVREG,pfnDestruct}
799 */
800static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
801{
802 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
803 LogFlow(("pcbiosDestruct:\n"));
804 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
805
806 /*
807 * Free MM heap pointers.
808 */
809 if (pThis->pu8PcBios)
810 {
811 MMR3HeapFree(pThis->pu8PcBios);
812 pThis->pu8PcBios = NULL;
813 }
814
815 if (pThis->pszPcBiosFile)
816 {
817 MMR3HeapFree(pThis->pszPcBiosFile);
818 pThis->pszPcBiosFile = NULL;
819 }
820
821 if (pThis->pu8LanBoot)
822 {
823 MMR3HeapFree(pThis->pu8LanBoot);
824 pThis->pu8LanBoot = NULL;
825 }
826
827 if (pThis->pszLanBootFile)
828 {
829 MMR3HeapFree(pThis->pszLanBootFile);
830 pThis->pszLanBootFile = NULL;
831 }
832
833 if (pThis->pszHDDevice)
834 {
835 MMR3HeapFree(pThis->pszHDDevice);
836 pThis->pszHDDevice = NULL;
837 }
838
839 if (pThis->pszFDDevice)
840 {
841 MMR3HeapFree(pThis->pszFDDevice);
842 pThis->pszFDDevice = NULL;
843 }
844
845 if (pThis->pszSataDevice)
846 {
847 MMR3HeapFree(pThis->pszSataDevice);
848 pThis->pszSataDevice = NULL;
849 }
850
851 return VINF_SUCCESS;
852}
853
854
855/**
856 * Convert config value to DEVPCBIOSBOOT.
857 *
858 * @returns VBox status code.
859 * @param pCfg Configuration handle.
860 * @param pszParam The name of the value to read.
861 * @param penmBoot Where to store the boot method.
862 */
863static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
864{
865 char *psz;
866 int rc = CFGMR3QueryStringAlloc(pCfg, pszParam, &psz);
867 if (RT_FAILURE(rc))
868 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
869 N_("Configuration error: Querying \"%s\" as a string failed"),
870 pszParam);
871 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
872 *penmBoot = DEVPCBIOSBOOT_DVD;
873 else if (!strcmp(psz, "IDE"))
874 *penmBoot = DEVPCBIOSBOOT_HD;
875 else if (!strcmp(psz, "FLOPPY"))
876 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
877 else if (!strcmp(psz, "LAN"))
878 *penmBoot = DEVPCBIOSBOOT_LAN;
879 else if (!strcmp(psz, "NONE"))
880 *penmBoot = DEVPCBIOSBOOT_NONE;
881 else
882 {
883 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
884 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
885 pszParam, psz);
886 rc = VERR_INTERNAL_ERROR;
887 }
888 MMR3HeapFree(psz);
889 return rc;
890}
891
892/**
893 * @interface_method_impl{PDMDEVREG,pfnConstruct}
894 */
895static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
896{
897 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
898 int rc;
899 int cb;
900
901 Assert(iInstance == 0);
902 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
903
904 /*
905 * Validate configuration.
906 */
907 if (!CFGMR3AreValuesValid(pCfg,
908 "BootDevice0\0"
909 "BootDevice1\0"
910 "BootDevice2\0"
911 "BootDevice3\0"
912 "RamSize\0"
913 "RamHoleSize\0"
914 "HardDiskDevice\0"
915 "SataHardDiskDevice\0"
916 "SataLUN1\0"
917 "SataLUN2\0"
918 "SataLUN3\0"
919 "SataLUN4\0"
920 "FloppyDevice\0"
921 "DelayBoot\0"
922 "BiosRom\0"
923 "LanBootRom\0"
924 "PXEDebug\0"
925 "UUID\0"
926 "IOAPIC\0"
927 "NumCPUs\0"
928 "McfgBase\0"
929 "McfgLength\0"
930 "DmiBIOSFirmwareMajor\0"
931 "DmiBIOSFirmwareMinor\0"
932 "DmiBIOSReleaseDate\0"
933 "DmiBIOSReleaseMajor\0"
934 "DmiBIOSReleaseMinor\0"
935 "DmiBIOSVendor\0"
936 "DmiBIOSVersion\0"
937 "DmiSystemFamily\0"
938 "DmiSystemProduct\0"
939 "DmiSystemSerial\0"
940 "DmiSystemSKU\0"
941 "DmiSystemUuid\0"
942 "DmiSystemVendor\0"
943 "DmiSystemVersion\0"
944 "DmiBoardAssetTag\0"
945 "DmiBoardBoardType\0"
946 "DmiBoardLocInChass\0"
947 "DmiBoardProduct\0"
948 "DmiBoardSerial\0"
949 "DmiBoardVendor\0"
950 "DmiBoardVersion\0"
951 "DmiChassisAssetTag\0"
952 "DmiChassisSerial\0"
953 "DmiChassisType\0"
954 "DmiChassisVendor\0"
955 "DmiChassisVersion\0"
956 "DmiProcManufacturer\0"
957 "DmiProcVersion\0"
958 "DmiOEMVBoxVer\0"
959 "DmiOEMVBoxRev\0"
960 "DmiUseHostInfo\0"
961 "DmiExposeMemoryTable\0"
962 "DmiExposeProcInf\0"
963 ))
964 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
965 N_("Invalid configuration for device pcbios device"));
966
967 /*
968 * Init the data.
969 */
970 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
971 if (RT_FAILURE(rc))
972 return PDMDEV_SET_ERROR(pDevIns, rc,
973 N_("Configuration error: Querying \"RamSize\" as integer failed"));
974
975 rc = CFGMR3QueryU32Def(pCfg, "RamHoleSize", &pThis->cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
976 if (RT_FAILURE(rc))
977 return PDMDEV_SET_ERROR(pDevIns, rc,
978 N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
979
980 rc = CFGMR3QueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
981 if (RT_FAILURE(rc))
982 return PDMDEV_SET_ERROR(pDevIns, rc,
983 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
984
985 rc = CFGMR3QueryU32Def(pCfg, "McfgBase", &pThis->u32McfgBase, 0);
986 if (RT_FAILURE(rc))
987 return PDMDEV_SET_ERROR(pDevIns, rc,
988 N_("Configuration error: Querying \"\" as integer failed"));
989 rc = CFGMR3QueryU32Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
990 if (RT_FAILURE(rc))
991 return PDMDEV_SET_ERROR(pDevIns, rc,
992 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
993
994
995 LogRel(("[SMP] BIOS with %u CPUs\n", pThis->cCpus));
996
997 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
998 if (RT_FAILURE (rc))
999 return PDMDEV_SET_ERROR(pDevIns, rc,
1000 N_("Configuration error: Failed to read \"IOAPIC\""));
1001
1002 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1003 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1004 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1005 {
1006 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1007 if (RT_FAILURE(rc))
1008 return rc;
1009 }
1010
1011 rc = CFGMR3QueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1012 if (RT_FAILURE(rc))
1013 return PDMDEV_SET_ERROR(pDevIns, rc,
1014 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1015
1016 rc = CFGMR3QueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1017 if (RT_FAILURE(rc))
1018 return PDMDEV_SET_ERROR(pDevIns, rc,
1019 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1020
1021 rc = CFGMR3QueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1022 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1023 pThis->pszSataDevice = NULL;
1024 else if (RT_FAILURE(rc))
1025 return PDMDEV_SET_ERROR(pDevIns, rc,
1026 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1027
1028 if (pThis->pszSataDevice)
1029 {
1030 static const char * const s_apszSataDisks[] =
1031 { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
1032 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1033 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1034 {
1035 rc = CFGMR3QueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1036 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1037 pThis->iSataHDLUN[i] = i;
1038 else if (RT_FAILURE(rc))
1039 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1040 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1041 }
1042 }
1043 /*
1044 * Register I/O Ports and PC BIOS.
1045 */
1046 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1047 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1048 if (RT_FAILURE(rc))
1049 return rc;
1050 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1051 NULL, NULL, "Bochs PC BIOS - Shutdown");
1052 if (RT_FAILURE(rc))
1053 return rc;
1054
1055 /*
1056 * Read the PXE debug logging option.
1057 */
1058 rc = CFGMR3QueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1059 if (RT_FAILURE(rc))
1060 return PDMDEV_SET_ERROR(pDevIns, rc,
1061 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1062
1063 /* Clear the net boot device list. All bits set invokes old behavior,
1064 * as if no second CMOS bank was present.
1065 */
1066 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1067
1068 /*
1069 * Determine the network boot order.
1070 */
1071 PCFGMNODE pCfgNetBoot = CFGMR3GetChild(pCfg, "NetBoot");
1072 if (pCfgNetBoot == NULL)
1073 {
1074 /* Do nothing. */
1075 rc = VINF_SUCCESS;
1076 }
1077 else
1078 {
1079 PCFGMNODE pCfgNetBootDevice;
1080 uint8_t u8PciBus;
1081 uint8_t u8PciDev;
1082 uint8_t u8PciFn;
1083 uint16_t u16BusDevFn;
1084 char szIndex[] = "?";
1085
1086 Assert(pCfgNetBoot);
1087 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1088 {
1089 szIndex[0] = '0' + i;
1090 pCfgNetBootDevice = CFGMR3GetChild(pCfgNetBoot, szIndex);
1091
1092 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1093 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1094 {
1095 /* Do nothing and stop iterating. */
1096 rc = VINF_SUCCESS;
1097 break;
1098 }
1099 else if (RT_FAILURE(rc))
1100 return PDMDEV_SET_ERROR(pDevIns, rc,
1101 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1102 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1103 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1104 {
1105 /* Do nothing and stop iterating. */
1106 rc = VINF_SUCCESS;
1107 break;
1108 }
1109 else if (RT_FAILURE(rc))
1110 return PDMDEV_SET_ERROR(pDevIns, rc,
1111 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1112 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1113 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1114 {
1115 /* Do nothing and stop iterating. */
1116 rc = VINF_SUCCESS;
1117 break;
1118 }
1119 else if (RT_FAILURE(rc))
1120 return PDMDEV_SET_ERROR(pDevIns, rc,
1121 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1122 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1123 pThis->au16NetBootDev[i] = u16BusDevFn;
1124 }
1125 }
1126
1127 /*
1128 * Get the system BIOS ROM file name.
1129 */
1130 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1131 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1132 {
1133 pThis->pszPcBiosFile = NULL;
1134 rc = VINF_SUCCESS;
1135 }
1136 else if (RT_FAILURE(rc))
1137 return PDMDEV_SET_ERROR(pDevIns, rc,
1138 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1139 else if (!*pThis->pszPcBiosFile)
1140 {
1141 MMR3HeapFree(pThis->pszPcBiosFile);
1142 pThis->pszPcBiosFile = NULL;
1143 }
1144
1145 if (pThis->pszPcBiosFile)
1146 {
1147 /*
1148 * Load the BIOS ROM.
1149 */
1150 RTFILE hFilePcBios;
1151 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1152 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1153 if (RT_SUCCESS(rc))
1154 {
1155 /* Figure the size and check restrictions. */
1156 uint64_t cbPcBios;
1157 rc = RTFileGetSize(hFilePcBios, &cbPcBios);
1158 if (RT_SUCCESS(rc))
1159 {
1160 pThis->cbPcBios = (uint32_t)cbPcBios;
1161 if ( RT_ALIGN(pThis->cbPcBios, _64K) == pThis->cbPcBios
1162 && pThis->cbPcBios == cbPcBios
1163 && pThis->cbPcBios <= 32 * _64K
1164 && pThis->cbPcBios >= _64K)
1165 {
1166 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1167 if (pThis->pu8PcBios)
1168 {
1169 rc = RTFileRead(hFilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1170 if (RT_FAILURE(rc))
1171 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1172 N_("Error reading the BIOS image ('%s)"), pThis->pszPcBiosFile);
1173 }
1174 else
1175 rc = PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1176 N_("Failed to allocate %#x bytes for loading the BIOS image"),
1177 pThis->cbPcBios);
1178 }
1179 else
1180 rc = PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
1181 N_("Invalid system BIOS file size ('%s'): %#llx (%llu)"),
1182 pThis->pszPcBiosFile, cbPcBios, cbPcBios);
1183 }
1184 else
1185 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1186 N_("Failed to query the system BIOS file size ('%s')"),
1187 pThis->pszPcBiosFile);
1188 RTFileClose(hFilePcBios);
1189 }
1190 else
1191 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1192 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1193 if (RT_FAILURE(rc))
1194 return rc;
1195
1196 LogRel(("DevPcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1197 }
1198 else
1199 {
1200 /*
1201 * Use the embedded BIOS ROM image.
1202 */
1203 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, g_cbPcBiosBinary);
1204 if (pThis->pu8PcBios)
1205 {
1206 pThis->cbPcBios = g_cbPcBiosBinary;
1207 memcpy(pThis->pu8PcBios, g_abPcBiosBinary, pThis->cbPcBios);
1208 }
1209 else
1210 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1211 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"),
1212 g_cbPcBiosBinary);
1213 }
1214 const uint8_t *pu8PcBiosBinary = pThis->pu8PcBios;
1215 uint32_t cbPcBiosBinary = pThis->cbPcBios;
1216
1217 /*
1218 * Query the machine's UUID for SMBIOS/DMI use.
1219 */
1220 RTUUID uuid;
1221 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1222 if (RT_FAILURE(rc))
1223 return PDMDEV_SET_ERROR(pDevIns, rc,
1224 N_("Configuration error: Querying \"UUID\" failed"));
1225
1226 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1227 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1228 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1229 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1230 uint16_t cbDmiTables = 0;
1231 uint16_t cNumDmiTables = 0;
1232 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1233 &uuid, pCfg, pThis->cCpus, &cbDmiTables, &cNumDmiTables);
1234 if (RT_FAILURE(rc))
1235 return rc;
1236
1237 for (unsigned i = 0; i < pThis->cbPcBios; i += 16)
1238 {
1239 /* If the DMI table is located at the expected place, patch the DMI table length and the checksum. */
1240 if ( pThis->pu8PcBios[i + 0x00] == '_'
1241 && pThis->pu8PcBios[i + 0x01] == 'D'
1242 && pThis->pu8PcBios[i + 0x02] == 'M'
1243 && pThis->pu8PcBios[i + 0x03] == 'I'
1244 && pThis->pu8PcBios[i + 0x04] == '_'
1245 && *(uint16_t*)&pThis->pu8PcBios[i + 0x06] == 0)
1246 {
1247 *(uint16_t*)&pThis->pu8PcBios[i + 0x06] = RT_H2LE_U16(cbDmiTables);
1248 *(uint16_t*)&pThis->pu8PcBios[i + 0x0C] = RT_H2LE_U16(cNumDmiTables);
1249 uint8_t u8Sum = 0;
1250 for (unsigned j = 0; j < pThis->cbPcBios; j++)
1251 if (j != i + 0x05)
1252 u8Sum += pThis->pu8PcBios[j];
1253 pThis->pu8PcBios[i + 0x05] = -u8Sum;
1254 break;
1255 }
1256 }
1257
1258 if (pThis->u8IOAPIC)
1259 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1260 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1261
1262 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
1263 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1264 if (RT_FAILURE(rc))
1265 return rc;
1266
1267 /*
1268 * Map the BIOS into memory.
1269 * There are two mappings:
1270 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1271 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1272 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1273 */
1274 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1275 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1276 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1277 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1278 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb], cb,
1279 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xfffff");
1280 if (RT_FAILURE(rc))
1281 return rc;
1282 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary, cbPcBiosBinary,
1283 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xffffffff");
1284 if (RT_FAILURE(rc))
1285 return rc;
1286
1287 /*
1288 * Get the LAN boot ROM file name.
1289 */
1290 rc = CFGMR3QueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1291 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1292 {
1293 pThis->pszLanBootFile = NULL;
1294 rc = VINF_SUCCESS;
1295 }
1296 else if (RT_FAILURE(rc))
1297 return PDMDEV_SET_ERROR(pDevIns, rc,
1298 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1299 else if (!*pThis->pszLanBootFile)
1300 {
1301 MMR3HeapFree(pThis->pszLanBootFile);
1302 pThis->pszLanBootFile = NULL;
1303 }
1304
1305 uint64_t cbFileLanBoot;
1306 const uint8_t *pu8LanBootBinary = NULL;
1307 uint64_t cbLanBootBinary;
1308
1309 /*
1310 * Determine the LAN boot ROM size, open specified ROM file in the process.
1311 */
1312 RTFILE FileLanBoot = NIL_RTFILE;
1313 if (pThis->pszLanBootFile)
1314 {
1315 rc = RTFileOpen(&FileLanBoot, pThis->pszLanBootFile,
1316 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1317 if (RT_SUCCESS(rc))
1318 {
1319 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1320 if (RT_SUCCESS(rc))
1321 {
1322 if (cbFileLanBoot > _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff))
1323 rc = VERR_TOO_MUCH_DATA;
1324 }
1325 }
1326 if (RT_FAILURE(rc))
1327 {
1328 /*
1329 * Ignore failure and fall back to the built-in LAN boot ROM.
1330 */
1331 LogRel(("DevPcBios: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1332 RTFileClose(FileLanBoot);
1333 FileLanBoot = NIL_RTFILE;
1334 MMR3HeapFree(pThis->pszLanBootFile);
1335 pThis->pszLanBootFile = NULL;
1336 }
1337 }
1338
1339 /*
1340 * Get the LAN boot ROM data.
1341 */
1342 if (pThis->pszLanBootFile)
1343 {
1344 LogRel(("DevPcBios: Using LAN ROM '%s' with a size of %#x bytes\n", pThis->pszLanBootFile, cbFileLanBoot));
1345 /*
1346 * Allocate buffer for the LAN boot ROM data.
1347 */
1348 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, cbFileLanBoot);
1349 if (pThis->pu8LanBoot)
1350 {
1351 rc = RTFileRead(FileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1352 if (RT_FAILURE(rc))
1353 {
1354 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", cbFileLanBoot, rc));
1355 MMR3HeapFree(pThis->pu8LanBoot);
1356 pThis->pu8LanBoot = NULL;
1357 }
1358 rc = VINF_SUCCESS;
1359 }
1360 else
1361 rc = VERR_NO_MEMORY;
1362 }
1363 else
1364 pThis->pu8LanBoot = NULL;
1365
1366 /* cleanup */
1367 if (FileLanBoot != NIL_RTFILE)
1368 RTFileClose(FileLanBoot);
1369
1370 /* If we were unable to get the data from file for whatever reason, fall
1371 * back to the built-in LAN boot ROM image.
1372 */
1373 if (pThis->pu8LanBoot == NULL)
1374 {
1375#ifdef VBOX_WITH_PXE_ROM
1376 pu8LanBootBinary = g_abNetBiosBinary;
1377 cbLanBootBinary = g_cbNetBiosBinary;
1378#endif
1379 }
1380 else
1381 {
1382 pu8LanBootBinary = pThis->pu8LanBoot;
1383 cbLanBootBinary = cbFileLanBoot;
1384 }
1385
1386 /*
1387 * Map the Network Boot ROM into memory.
1388 * Currently there is a fixed mapping: 0x000e2000 to 0x000effff contains
1389 * the (up to) 56 kb ROM image. The mapping size is fixed to trouble with
1390 * the saved state (in PGM).
1391 */
1392 if (pu8LanBootBinary)
1393 {
1394 pThis->cbLanBoot = cbLanBootBinary;
1395
1396 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4,
1397 RT_MAX(cbLanBootBinary, _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff)),
1398 pu8LanBootBinary, cbLanBootBinary,
1399 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1400 AssertRCReturn(rc, rc);
1401 }
1402
1403 rc = CFGMR3QueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1404 if (RT_FAILURE(rc))
1405 return PDMDEV_SET_ERROR(pDevIns, rc,
1406 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1407 if (pThis->uBootDelay > 15)
1408 pThis->uBootDelay = 15;
1409
1410 return VINF_SUCCESS;
1411}
1412
1413
1414/**
1415 * The device registration structure.
1416 */
1417const PDMDEVREG g_DevicePcBios =
1418{
1419 /* u32Version */
1420 PDM_DEVREG_VERSION,
1421 /* szName */
1422 "pcbios",
1423 /* szRCMod */
1424 "",
1425 /* szR0Mod */
1426 "",
1427 /* pszDescription */
1428 "PC BIOS Device",
1429 /* fFlags */
1430 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1431 /* fClass */
1432 PDM_DEVREG_CLASS_ARCH_BIOS,
1433 /* cMaxInstances */
1434 1,
1435 /* cbInstance */
1436 sizeof(DEVPCBIOS),
1437 /* pfnConstruct */
1438 pcbiosConstruct,
1439 /* pfnDestruct */
1440 pcbiosDestruct,
1441 /* pfnRelocate */
1442 NULL,
1443 /* pfnMemSetup */
1444 pcbiosMemSetup,
1445 /* pfnPowerOn */
1446 NULL,
1447 /* pfnReset */
1448 NULL,
1449 /* pfnSuspend */
1450 NULL,
1451 /* pfnResume */
1452 NULL,
1453 /* pfnAttach */
1454 NULL,
1455 /* pfnDetach */
1456 NULL,
1457 /* pfnQueryInterface. */
1458 NULL,
1459 /* pfnInitComplete. */
1460 pcbiosInitComplete,
1461 /* pfnPowerOff */
1462 NULL,
1463 /* pfnSoftReset */
1464 NULL,
1465 /* u32VersionEnd */
1466 PDM_DEVREG_VERSION
1467};
1468
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