VirtualBox

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

Last change on this file since 76569 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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