VirtualBox

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

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

Update PDMDEVREG initialization comment so they refer to pfnMemSetup instead of pfnIOCtl.

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