VirtualBox

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

Last change on this file since 62556 was 62509, checked in by vboxsync, 8 years ago

(C) 2016

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