VirtualBox

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

Last change on this file since 71216 was 67585, checked in by vboxsync, 7 years ago

DevEFI/DevACPI/DevPcBios: bugref:8768: necessary adaptions of the PCI memory hole according to r113565 (2MB => 32MB)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 64.7 KB
Line 
1/* $Id: DevPcBios.cpp 67585 2017-06-23 15:29:50Z vboxsync $ */
2/** @file
3 * DevPcBios - PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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 if (u32 == '\n' || u32 == '\r')
281 {
282 pThis->szMsg[pThis->iMsg] = '\0';
283 if (pThis->iMsg)
284 Log(("pcbios: %s\n", pThis->szMsg));
285 pThis->iMsg = 0;
286 }
287 else
288 {
289 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
290 {
291 pThis->szMsg[pThis->iMsg] = '\0';
292 Log(("pcbios: %s\n", pThis->szMsg));
293 pThis->iMsg = 0;
294 }
295 pThis->szMsg[pThis->iMsg] = (char )u32;
296 pThis->szMsg[++pThis->iMsg] = '\0';
297 }
298 return VINF_SUCCESS;
299 }
300
301 /*
302 * Bochs BIOS shutdown request.
303 */
304 if (cb == 1 && Port == pThis->ShutdownPort)
305 {
306 static const unsigned char szShutdown[] = "Shutdown";
307 if (u32 == szShutdown[pThis->iShutdown])
308 {
309 pThis->iShutdown++;
310 if (pThis->iShutdown == 8)
311 {
312 pThis->iShutdown = 0;
313 LogRel(("PcBios: APM shutdown request\n"));
314 return PDMDevHlpVMPowerOff(pDevIns);
315 }
316 }
317 else
318 pThis->iShutdown = 0;
319 return VINF_SUCCESS;
320 }
321
322 /* not in use. */
323 return VINF_SUCCESS;
324}
325
326
327/**
328 * Register the Bochs shutdown port.
329 * This is used by pcbiosConstruct, pcbiosReset and pcbiosLoadExec.
330 */
331static int pcbiosRegisterShutdown(PPDMDEVINS pDevIns, PDEVPCBIOS pThis, bool fNewShutdownPort)
332{
333 if (pThis->ShutdownPort != 0)
334 {
335 int rc = PDMDevHlpIOPortDeregister(pDevIns, pThis->ShutdownPort, 1);
336 AssertRC(rc);
337 }
338 pThis->fNewShutdownPort = fNewShutdownPort;
339 if (fNewShutdownPort)
340 pThis->ShutdownPort = VBOX_BIOS_SHUTDOWN_PORT;
341 else
342 pThis->ShutdownPort = VBOX_BIOS_OLD_SHUTDOWN_PORT;
343 return PDMDevHlpIOPortRegister(pDevIns, pThis->ShutdownPort, 1, NULL,
344 pcbiosIOPortWrite, pcbiosIOPortRead,
345 NULL, NULL, "Bochs PC BIOS - Shutdown");
346}
347
348
349/**
350 * @callback_method_impl{FNSSMDEVSAVEEXEC}
351 */
352static DECLCALLBACK(int) pcbiosSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
353{
354 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
355 SSMR3PutStruct(pSSM, pThis, g_aPcBiosFields);
356 return VINF_SUCCESS;
357}
358
359
360/**
361 * @callback_method_impl{FNSSMDEVLOADPREP,
362 * Clears the fNewShutdownPort flag prior to loading the state so that old
363 * saved VM states keeps using the old port address (no pcbios state)}
364 */
365static DECLCALLBACK(int) pcbiosLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
366{
367 RT_NOREF(pSSM);
368 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
369
370 /* Since there are legacy saved state files without any SSM data for PCBIOS
371 * this is the only way to handle them correctly. */
372 pThis->fNewShutdownPort = false;
373
374 return VINF_SUCCESS;
375}
376
377
378/**
379 * @callback_method_impl{FNSSMDEVLOADEXEC}
380 */
381static DECLCALLBACK(int) pcbiosLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
382{
383 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
384
385 if (uVersion > PCBIOS_SSM_VERSION)
386 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
387 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
388
389 return SSMR3GetStruct(pSSM, pThis, g_aPcBiosFields);
390}
391
392
393/**
394 * @callback_method_impl{FNSSMDEVLOADDONE,
395 * Updates the shutdown port registration to match the flag loaded (or not).}
396 */
397static DECLCALLBACK(int) pcbiosLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
398{
399 RT_NOREF(pSSM);
400 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
401 return pcbiosRegisterShutdown(pDevIns, pThis, pThis->fNewShutdownPort);
402}
403
404
405/**
406 * Write to CMOS memory.
407 * This is used by the init complete code.
408 */
409static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
410{
411 Assert(off < 256);
412 Assert(u32Val < 256);
413
414 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
415 AssertRC(rc);
416}
417
418
419/**
420 * Read from CMOS memory.
421 * This is used by the init complete code.
422 */
423static uint8_t pcbiosCmosRead(PPDMDEVINS pDevIns, unsigned off)
424{
425 Assert(off < 256);
426
427 uint8_t u8val;
428 int rc = PDMDevHlpCMOSRead(pDevIns, off, &u8val);
429 AssertRC(rc);
430
431 return u8val;
432}
433
434
435/**
436 * @interface_method_impl{PDMFWREG,pfnIsHardReset}
437 */
438static DECLCALLBACK(bool) pcbiosFw_IsHardReset(PPDMDEVINS pDevIns, uint32_t fFlags)
439{
440 RT_NOREF1(fFlags);
441 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
442 if (pThis->fCheckShutdownStatusForSoftReset)
443 {
444 uint8_t bShutdownStatus = pcbiosCmosRead(pDevIns, 0xf);
445 if ( bShutdownStatus == 0x5
446 || bShutdownStatus == 0x9
447 || bShutdownStatus == 0xa)
448 {
449 const uint32_t cMaxLogged = 10;
450 if (pThis->cLoggedSoftResets < cMaxLogged)
451 {
452 RTFAR16 Far16 = { 0xfeed, 0xface };
453 PDMDevHlpPhysRead(pDevIns, 0x467, &Far16, sizeof(Far16));
454 pThis->cLoggedSoftResets++;
455 LogRel(("PcBios: Soft reset #%u - shutdown status %#x, warm reset vector (0040:0067) is %04x:%04x%s\n",
456 pThis->cLoggedSoftResets, bShutdownStatus, Far16.sel, Far16.sel,
457 pThis->cLoggedSoftResets < cMaxLogged ? "." : " - won't log any more!"));
458 }
459 return false;
460 }
461 }
462 return true;
463}
464
465
466/**
467 * @interface_method_impl{PDMDEVREG,pfnReset}
468 */
469static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
470{
471 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
472
473 if (pThis->fClearShutdownStatusOnHardReset)
474 {
475 uint8_t bShutdownStatus = pcbiosCmosRead(pDevIns, 0xf);
476 if (bShutdownStatus != 0)
477 {
478 LogRel(("PcBios: Clearing shutdown status code %02x.\n", bShutdownStatus));
479 pcbiosCmosWrite(pDevIns, 0xf, 0);
480 }
481 }
482
483 /* After reset the new BIOS code is active, use the new shutdown port. */
484 pcbiosRegisterShutdown(pDevIns, pThis, true /* fNewShutdownPort */);
485}
486
487
488/**
489 * Attempt to guess the LCHS disk geometry from the MS-DOS master boot record
490 * (partition table).
491 *
492 * @returns VBox status code.
493 * @param pMedia The media device interface of the disk.
494 * @param pLCHSGeometry Where to return the disk geometry on success
495 */
496static int biosGuessDiskLCHS(PPDMIMEDIA pMedia, PPDMMEDIAGEOMETRY pLCHSGeometry)
497{
498 uint8_t aMBR[512], *p;
499 int rc;
500 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
501
502 if (!pMedia)
503 return VERR_INVALID_PARAMETER;
504 rc = pMedia->pfnReadPcBios(pMedia, 0, aMBR, sizeof(aMBR));
505 if (RT_FAILURE(rc))
506 return rc;
507 /* Test MBR magic number. */
508 if (aMBR[510] != 0x55 || aMBR[511] != 0xaa)
509 return VERR_INVALID_PARAMETER;
510 for (uint32_t i = 0; i < 4; i++)
511 {
512 /* Figure out the start of a partition table entry. */
513 p = &aMBR[0x1be + i * 16];
514 iEndHead = p[5];
515 iEndSector = p[6] & 63;
516 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector & iEndHead)
517 {
518 /* Assumption: partition terminates on a cylinder boundary. */
519 cLCHSHeads = iEndHead + 1;
520 cLCHSSectors = iEndSector;
521 cLCHSCylinders = RT_MIN(1024, pMedia->pfnGetSize(pMedia) / (512 * cLCHSHeads * cLCHSSectors));
522 if (cLCHSCylinders >= 1)
523 {
524 pLCHSGeometry->cCylinders = cLCHSCylinders;
525 pLCHSGeometry->cHeads = cLCHSHeads;
526 pLCHSGeometry->cSectors = cLCHSSectors;
527 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
528 return VINF_SUCCESS;
529 }
530 }
531 }
532 return VERR_INVALID_PARAMETER;
533}
534
535
536/**
537 * Initializes the CMOS data for one harddisk.
538 */
539static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
540{
541 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
542 if (offType)
543 pcbiosCmosWrite(pDevIns, offType, 47);
544 /* Cylinders low */
545 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(pLCHSGeometry->cCylinders, 1024) & 0xff);
546 /* Cylinders high */
547 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(pLCHSGeometry->cCylinders, 1024) >> 8);
548 /* Heads */
549 pcbiosCmosWrite(pDevIns, offInfo + 2, pLCHSGeometry->cHeads);
550 /* Landing zone low */
551 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
552 /* Landing zone high */
553 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
554 /* Write precomp low */
555 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
556 /* Write precomp high */
557 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
558 /* Sectors */
559 pcbiosCmosWrite(pDevIns, offInfo + 7, pLCHSGeometry->cSectors);
560}
561
562
563/**
564 * Set logical CHS geometry for a hard disk
565 *
566 * @returns VBox status code.
567 * @param pBase Base interface for the device.
568 * @param pHardDisk The hard disk.
569 * @param pLCHSGeometry Where to store the geometry settings.
570 */
571static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIMEDIA pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
572{
573 RT_NOREF1(pBase);
574
575 PDMMEDIAGEOMETRY LCHSGeometry;
576 int rc = pHardDisk->pfnBiosGetLCHSGeometry(pHardDisk, &LCHSGeometry);
577 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
578 || LCHSGeometry.cCylinders == 0
579 || LCHSGeometry.cHeads == 0
580 || LCHSGeometry.cHeads > 255
581 || LCHSGeometry.cSectors == 0
582 || LCHSGeometry.cSectors > 63)
583 {
584 /* No LCHS geometry, autodetect and set. */
585 rc = biosGuessDiskLCHS(pHardDisk, &LCHSGeometry);
586 if (RT_FAILURE(rc))
587 {
588 /* Try if PCHS geometry works, otherwise fall back. */
589 rc = pHardDisk->pfnBiosGetPCHSGeometry(pHardDisk, &LCHSGeometry);
590 }
591 if ( RT_FAILURE(rc)
592 || LCHSGeometry.cCylinders == 0
593 || LCHSGeometry.cCylinders > 1024
594 || LCHSGeometry.cHeads == 0
595 || LCHSGeometry.cHeads > 16
596 || LCHSGeometry.cSectors == 0
597 || LCHSGeometry.cSectors > 63)
598 {
599 uint64_t cSectors = pHardDisk->pfnGetSize(pHardDisk) / 512;
600 if (cSectors / 16 / 63 <= 1024)
601 {
602 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
603 LCHSGeometry.cHeads = 16;
604 }
605 else if (cSectors / 32 / 63 <= 1024)
606 {
607 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
608 LCHSGeometry.cHeads = 32;
609 }
610 else if (cSectors / 64 / 63 <= 1024)
611 {
612 LCHSGeometry.cCylinders = cSectors / 64 / 63;
613 LCHSGeometry.cHeads = 64;
614 }
615 else if (cSectors / 128 / 63 <= 1024)
616 {
617 LCHSGeometry.cCylinders = cSectors / 128 / 63;
618 LCHSGeometry.cHeads = 128;
619 }
620 else
621 {
622 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
623 LCHSGeometry.cHeads = 255;
624 }
625 LCHSGeometry.cSectors = 63;
626
627 }
628 rc = pHardDisk->pfnBiosSetLCHSGeometry(pHardDisk, &LCHSGeometry);
629 if (rc == VERR_VD_IMAGE_READ_ONLY)
630 {
631 LogRel(("PcBios: ATA failed to update LCHS geometry, read only\n"));
632 rc = VINF_SUCCESS;
633 }
634 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
635 {
636 LogRel(("PcBios: ATA failed to update LCHS geometry, backend refused\n"));
637 rc = VINF_SUCCESS;
638 }
639 }
640
641 *pLCHSGeometry = LCHSGeometry;
642
643 return rc;
644}
645
646
647/**
648 * Get logical CHS geometry for a hard disk, intended for SCSI/SAS drives
649 * with no physical geometry.
650 *
651 * @returns VBox status code.
652 * @param pHardDisk The hard disk.
653 * @param pLCHSGeometry Where to store the geometry settings.
654 */
655static int getLogicalDiskGeometry(PPDMIMEDIA pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
656{
657 PDMMEDIAGEOMETRY LCHSGeometry;
658 int rc = VINF_SUCCESS;
659
660 rc = pHardDisk->pfnBiosGetLCHSGeometry(pHardDisk, &LCHSGeometry);
661 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
662 || LCHSGeometry.cCylinders == 0
663 || LCHSGeometry.cHeads == 0
664 || LCHSGeometry.cHeads > 255
665 || LCHSGeometry.cSectors == 0
666 || LCHSGeometry.cSectors > 63)
667 {
668 /* Unlike the ATA case, if the image does not provide valid logical
669 * geometry, we leave things alone and let the BIOS decide what the
670 * logical geometry should be.
671 */
672 rc = VERR_PDM_GEOMETRY_NOT_SET;
673 }
674 else
675 *pLCHSGeometry = LCHSGeometry;
676
677 return rc;
678}
679
680
681/**
682 * Get BIOS boot code from enmBootDevice in order
683 *
684 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
685 */
686static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
687{
688 switch (pThis->aenmBootDevice[iOrder])
689 {
690 case DEVPCBIOSBOOT_NONE:
691 return 0;
692 case DEVPCBIOSBOOT_FLOPPY:
693 return 1;
694 case DEVPCBIOSBOOT_HD:
695 return 2;
696 case DEVPCBIOSBOOT_DVD:
697 return 3;
698 case DEVPCBIOSBOOT_LAN:
699 return 4;
700 default:
701 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
702 return 0;
703 }
704}
705
706
707/**
708 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
709 *
710 * This routine will write information needed by the bios to the CMOS.
711 *
712 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
713 * a description of standard and non-standard CMOS registers.
714 */
715static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
716{
717 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
718 uint32_t u32;
719 unsigned i;
720 PUVM pUVM = PDMDevHlpGetUVM(pDevIns); AssertRelease(pUVM);
721 PPDMIMEDIA apHDs[4] = {0};
722 LogFlow(("pcbiosInitComplete:\n"));
723
724 PVM pVM = PDMDevHlpGetVM(pDevIns);
725 uint64_t const cbRamSize = MMR3PhysGetRamSize(pVM);
726 uint32_t const cbBelow4GB = MMR3PhysGetRamSizeBelow4GB(pVM);
727 uint64_t const cbAbove4GB = MMR3PhysGetRamSizeAbove4GB(pVM);
728
729 /*
730 * Memory sizes.
731 */
732 /* base memory. */
733 u32 = cbRamSize > 640 ? 640 : (uint32_t)cbRamSize / _1K; /* <-- this test is wrong, but it doesn't matter since we never assign less than 1MB */
734 pcbiosCmosWrite(pDevIns, 0x15, RT_BYTE1(u32)); /* 15h - Base Memory in K, Low Byte */
735 pcbiosCmosWrite(pDevIns, 0x16, RT_BYTE2(u32)); /* 16h - Base Memory in K, High Byte */
736
737 /* Extended memory, up to 65MB */
738 u32 = cbRamSize >= 65 * _1M ? 0xffff : ((uint32_t)cbRamSize - _1M) / _1K;
739 pcbiosCmosWrite(pDevIns, 0x17, RT_BYTE1(u32)); /* 17h - Extended Memory in K, Low Byte */
740 pcbiosCmosWrite(pDevIns, 0x18, RT_BYTE2(u32)); /* 18h - Extended Memory in K, High Byte */
741 pcbiosCmosWrite(pDevIns, 0x30, RT_BYTE1(u32)); /* 30h - Extended Memory in K, Low Byte */
742 pcbiosCmosWrite(pDevIns, 0x31, RT_BYTE2(u32)); /* 31h - Extended Memory in K, High Byte */
743
744 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
745 and below 4GB (as it can only hold 4GB-16M). We have to chop off the
746 top 32MB or it conflict with what the ACPI tables return. (Should these
747 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
748 with the high BIOS mapping.) */
749 if (cbRamSize > 16 * _1M)
750 u32 = (RT_MIN(cbBelow4GB, UINT32_C(0xfe000000)) - 16U * _1M) / _64K;
751 else
752 u32 = 0;
753 pcbiosCmosWrite(pDevIns, 0x34, RT_BYTE1(u32));
754 pcbiosCmosWrite(pDevIns, 0x35, RT_BYTE2(u32));
755
756 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
757 Bochs got these in a different location which we've already used for SATA,
758 it also lacks the last two. */
759 uint64_t c64KBAbove4GB = cbAbove4GB / _64K;
760 /* Make sure it doesn't hit the limits of the current BIOS code (RAM limit of ~255TB). */
761 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
762 pcbiosCmosWrite(pDevIns, 0x61, RT_BYTE1(c64KBAbove4GB));
763 pcbiosCmosWrite(pDevIns, 0x62, RT_BYTE2(c64KBAbove4GB));
764 pcbiosCmosWrite(pDevIns, 0x63, RT_BYTE3(c64KBAbove4GB));
765 pcbiosCmosWrite(pDevIns, 0x64, RT_BYTE4(c64KBAbove4GB));
766 pcbiosCmosWrite(pDevIns, 0x65, RT_BYTE5(c64KBAbove4GB));
767
768 /*
769 * Number of CPUs.
770 */
771 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
772
773 /*
774 * APIC mode.
775 */
776 pcbiosCmosWrite(pDevIns, 0x78, pThis->u8APICMode);
777
778 /*
779 * Bochs BIOS specifics - boot device.
780 * We do both new and old (ami-style) settings.
781 * See rombios.c line ~7215 (int19_function).
782 */
783
784 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
785 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
786 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
787 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
788 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
789 pcbiosCmosWrite(pDevIns, 0x38, reg38);
790 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
791
792 /*
793 * PXE debug option.
794 */
795 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
796
797 /*
798 * Network boot device list.
799 */
800 for (i = 0; i < NET_BOOT_DEVS; ++i)
801 {
802 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, RT_BYTE1(pThis->au16NetBootDev[i]));
803 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, RT_BYTE2(pThis->au16NetBootDev[i]));
804 }
805
806 /*
807 * Floppy drive type.
808 */
809 uint32_t cFDs = 0;
810 u32 = 0;
811 for (i = 0; i < 2; i++)
812 {
813 PPDMIBASE pBase;
814 int rc = PDMR3QueryLun(pUVM, pThis->pszFDDevice, 0, i, &pBase);
815 if (RT_SUCCESS(rc))
816 {
817 PPDMIMEDIA pFD = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
818 if (pFD)
819 {
820 cFDs++;
821 unsigned cShift = i == 0 ? 4 : 0;
822 switch (pFD->pfnGetType(pFD))
823 {
824 case PDMMEDIATYPE_FLOPPY_360: u32 |= 1 << cShift; break;
825 case PDMMEDIATYPE_FLOPPY_1_20: u32 |= 2 << cShift; break;
826 case PDMMEDIATYPE_FLOPPY_720: u32 |= 3 << cShift; break;
827 case PDMMEDIATYPE_FLOPPY_1_44: u32 |= 4 << cShift; break;
828 case PDMMEDIATYPE_FLOPPY_2_88: u32 |= 5 << cShift; break;
829 case PDMMEDIATYPE_FLOPPY_FAKE_15_6: u32 |= 14 << cShift; break;
830 case PDMMEDIATYPE_FLOPPY_FAKE_63_5: u32 |= 15 << cShift; break;
831 default: AssertFailed(); break;
832 }
833 }
834 }
835 }
836 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
837
838 /*
839 * Equipment byte.
840 */
841 if (cFDs > 0)
842 u32 = ((cFDs - 1) << 6) | 0x01; /* floppy installed, additional drives. */
843 else
844 u32 = 0x00; /* floppy not installed. */
845 u32 |= RT_BIT(1); /* math coprocessor installed */
846 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
847 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
848 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
849
850 /*
851 * IDE harddisks.
852 */
853 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
854 {
855 PPDMIBASE pBase;
856 int rc = PDMR3QueryLun(pUVM, pThis->pszHDDevice, 0, i, &pBase);
857 if (RT_SUCCESS(rc))
858 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
859 if ( apHDs[i]
860 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
861 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
862 apHDs[i] = NULL;
863 if (apHDs[i])
864 {
865 PDMMEDIAGEOMETRY LCHSGeometry;
866 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
867 AssertRC(rc2);
868
869 if (i < 4)
870 {
871 /* Award BIOS extended drive types for first to fourth disk.
872 * Used by the BIOS for setting the logical geometry. */
873 int offType, offInfo;
874 switch (i)
875 {
876 case 0:
877 offType = 0x19;
878 offInfo = 0x1e;
879 break;
880 case 1:
881 offType = 0x1a;
882 offInfo = 0x26;
883 break;
884 case 2:
885 offType = 0x00;
886 offInfo = 0x67;
887 break;
888 case 3:
889 default:
890 offType = 0x00;
891 offInfo = 0x70;
892 break;
893 }
894 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo,
895 &LCHSGeometry);
896 }
897 LogRel(("PcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
898 }
899 }
900
901 /* 0Fh means extended and points to 19h, 1Ah */
902 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
903 pcbiosCmosWrite(pDevIns, 0x12, u32);
904
905 /*
906 * SATA harddisks.
907 */
908 if (pThis->pszSataDevice)
909 {
910 /* Clear pointers to the block devices. */
911 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
912 apHDs[i] = NULL;
913
914 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
915 {
916 PPDMIBASE pBase;
917 int rc = PDMR3QueryLun(pUVM, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
918 if (RT_SUCCESS(rc))
919 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
920 if ( apHDs[i]
921 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
922 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
923 apHDs[i] = NULL;
924 if (apHDs[i])
925 {
926 PDMMEDIAGEOMETRY LCHSGeometry;
927 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
928 AssertRC(rc);
929
930 if (i < 4)
931 {
932 /* Award BIOS extended drive types for first to fourth disk.
933 * Used by the BIOS for setting the logical geometry. */
934 int offInfo;
935 switch (i)
936 {
937 case 0:
938 offInfo = 0x40;
939 break;
940 case 1:
941 offInfo = 0x48;
942 break;
943 case 2:
944 offInfo = 0x50;
945 break;
946 case 3:
947 default:
948 offInfo = 0x58;
949 break;
950 }
951 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo,
952 &LCHSGeometry);
953 }
954 LogRel(("PcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
955 }
956 }
957 }
958
959 /*
960 * SCSI harddisks. Not handled quite the same as SATA.
961 */
962 if (pThis->pszScsiDevice)
963 {
964 /* Clear pointers to the block devices. */
965 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
966 apHDs[i] = NULL;
967
968 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
969 {
970 PPDMIBASE pBase;
971 int rc = PDMR3QueryLun(pUVM, pThis->pszScsiDevice, 0, pThis->iScsiHDLUN[i], &pBase);
972 if (RT_SUCCESS(rc))
973 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
974 if ( apHDs[i]
975 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
976 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
977 apHDs[i] = NULL;
978 if (apHDs[i])
979 {
980 PDMMEDIAGEOMETRY LCHSGeometry;
981 rc = getLogicalDiskGeometry(apHDs[i], &LCHSGeometry);
982
983 if (i < 4 && RT_SUCCESS(rc))
984 {
985 /* Extended drive information (for SCSI disks).
986 * Used by the BIOS for setting the logical geometry, but
987 * only if the image provided valid data.
988 */
989 int offInfo;
990 switch (i)
991 {
992 case 0:
993 offInfo = 0x90;
994 break;
995 case 1:
996 offInfo = 0x98;
997 break;
998 case 2:
999 offInfo = 0xa0;
1000 break;
1001 case 3:
1002 default:
1003 offInfo = 0xa8;
1004 break;
1005 }
1006 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo, &LCHSGeometry);
1007 LogRel(("PcBios: SCSI LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1008 }
1009 else
1010 LogRel(("PcBios: SCSI LUN#%d LCHS not provided\n", i));
1011 }
1012 }
1013 }
1014
1015 /* Calculate and store AT-style CMOS checksum. */
1016 uint16_t cksum = 0;
1017 for (i = 0x10; i < 0x2e; ++i)
1018 cksum += pcbiosCmosRead(pDevIns, i);
1019 pcbiosCmosWrite(pDevIns, 0x2e, RT_BYTE1(cksum));
1020 pcbiosCmosWrite(pDevIns, 0x2f, RT_BYTE2(cksum));
1021
1022 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
1023 return VINF_SUCCESS;
1024}
1025
1026
1027/**
1028 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1029 */
1030static DECLCALLBACK(void) pcbiosMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1031{
1032 RT_NOREF1(enmCtx);
1033 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1034 LogFlow(("pcbiosMemSetup:\n"));
1035
1036 if (pThis->u8IOAPIC)
1037 FwCommonPlantMpsFloatPtr(pDevIns);
1038
1039 /*
1040 * Re-shadow the LAN ROM image and make it RAM/RAM.
1041 *
1042 * This is normally done by the BIOS code, but since we're currently lacking
1043 * the chipset support for this we do it here (and in the constructor).
1044 */
1045 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, PAGE_SIZE) >> PAGE_SHIFT;
1046 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
1047 while (cPages > 0)
1048 {
1049 uint8_t abPage[PAGE_SIZE];
1050 int rc;
1051
1052 /* Read the (original) ROM page and write it back to the RAM page. */
1053 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1054 AssertLogRelRC(rc);
1055
1056 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
1057 AssertLogRelRC(rc);
1058 if (RT_FAILURE(rc))
1059 memset(abPage, 0xcc, sizeof(abPage));
1060
1061 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
1062 AssertLogRelRC(rc);
1063
1064 /* Switch to the RAM/RAM mode. */
1065 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1066 AssertLogRelRC(rc);
1067
1068 /* Advance */
1069 GCPhys += PAGE_SIZE;
1070 cPages--;
1071 }
1072}
1073
1074
1075/**
1076 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1077 */
1078static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1079{
1080 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1081 LogFlow(("pcbiosDestruct:\n"));
1082 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1083
1084 /*
1085 * Free MM heap pointers.
1086 */
1087 if (pThis->pu8PcBios)
1088 {
1089 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8PcBios);
1090 pThis->pu8PcBios = NULL;
1091 }
1092
1093 if (pThis->pszPcBiosFile)
1094 {
1095 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1096 pThis->pszPcBiosFile = NULL;
1097 }
1098
1099 if (pThis->pu8LanBoot)
1100 {
1101 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8LanBoot);
1102 pThis->pu8LanBoot = NULL;
1103 }
1104
1105 if (pThis->pszLanBootFile)
1106 {
1107 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1108 pThis->pszLanBootFile = NULL;
1109 }
1110
1111 if (pThis->pszHDDevice)
1112 {
1113 PDMDevHlpMMHeapFree(pDevIns, pThis->pszHDDevice);
1114 pThis->pszHDDevice = NULL;
1115 }
1116
1117 if (pThis->pszFDDevice)
1118 {
1119 PDMDevHlpMMHeapFree(pDevIns, pThis->pszFDDevice);
1120 pThis->pszFDDevice = NULL;
1121 }
1122
1123 if (pThis->pszSataDevice)
1124 {
1125 PDMDevHlpMMHeapFree(pDevIns, pThis->pszSataDevice);
1126 pThis->pszSataDevice = NULL;
1127 }
1128
1129 if (pThis->pszScsiDevice)
1130 {
1131 PDMDevHlpMMHeapFree(pDevIns, pThis->pszScsiDevice);
1132 pThis->pszScsiDevice = NULL;
1133 }
1134
1135 return VINF_SUCCESS;
1136}
1137
1138
1139/**
1140 * Convert config value to DEVPCBIOSBOOT.
1141 *
1142 * @returns VBox status code.
1143 * @param pDevIns Device instance data.
1144 * @param pCfg Configuration handle.
1145 * @param pszParam The name of the value to read.
1146 * @param penmBoot Where to store the boot method.
1147 */
1148static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1149{
1150 char *psz;
1151 int rc = CFGMR3QueryStringAlloc(pCfg, pszParam, &psz);
1152 if (RT_FAILURE(rc))
1153 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1154 N_("Configuration error: Querying \"%s\" as a string failed"),
1155 pszParam);
1156 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
1157 *penmBoot = DEVPCBIOSBOOT_DVD;
1158 else if (!strcmp(psz, "IDE"))
1159 *penmBoot = DEVPCBIOSBOOT_HD;
1160 else if (!strcmp(psz, "FLOPPY"))
1161 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1162 else if (!strcmp(psz, "LAN"))
1163 *penmBoot = DEVPCBIOSBOOT_LAN;
1164 else if (!strcmp(psz, "NONE"))
1165 *penmBoot = DEVPCBIOSBOOT_NONE;
1166 else
1167 {
1168 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1169 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
1170 pszParam, psz);
1171 rc = VERR_INTERNAL_ERROR;
1172 }
1173 MMR3HeapFree(psz);
1174 return rc;
1175}
1176
1177/**
1178 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1179 */
1180static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1181{
1182 RT_NOREF1(iInstance);
1183 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
1184 int rc;
1185 int cb;
1186
1187 Assert(iInstance == 0);
1188 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1189
1190 /*
1191 * Validate configuration.
1192 */
1193 if (!CFGMR3AreValuesValid(pCfg,
1194 "BootDevice0\0"
1195 "BootDevice1\0"
1196 "BootDevice2\0"
1197 "BootDevice3\0"
1198 "HardDiskDevice\0"
1199 "SataHardDiskDevice\0"
1200 "SataLUN1\0"
1201 "SataLUN2\0"
1202 "SataLUN3\0"
1203 "SataLUN4\0"
1204 "ScsiHardDiskDevice\0"
1205 "ScsiLUN1\0"
1206 "ScsiLUN2\0"
1207 "ScsiLUN3\0"
1208 "ScsiLUN4\0"
1209 "FloppyDevice\0"
1210 "DelayBoot\0"
1211 "BiosRom\0"
1212 "LanBootRom\0"
1213 "PXEDebug\0"
1214 "UUID\0"
1215 "IOAPIC\0"
1216 "APIC\0"
1217 "NumCPUs\0"
1218 "McfgBase\0"
1219 "McfgLength\0"
1220 "DmiBIOSFirmwareMajor\0"
1221 "DmiBIOSFirmwareMinor\0"
1222 "DmiBIOSReleaseDate\0"
1223 "DmiBIOSReleaseMajor\0"
1224 "DmiBIOSReleaseMinor\0"
1225 "DmiBIOSVendor\0"
1226 "DmiBIOSVersion\0"
1227 "DmiSystemFamily\0"
1228 "DmiSystemProduct\0"
1229 "DmiSystemSerial\0"
1230 "DmiSystemSKU\0"
1231 "DmiSystemUuid\0"
1232 "DmiSystemVendor\0"
1233 "DmiSystemVersion\0"
1234 "DmiBoardAssetTag\0"
1235 "DmiBoardBoardType\0"
1236 "DmiBoardLocInChass\0"
1237 "DmiBoardProduct\0"
1238 "DmiBoardSerial\0"
1239 "DmiBoardVendor\0"
1240 "DmiBoardVersion\0"
1241 "DmiChassisAssetTag\0"
1242 "DmiChassisSerial\0"
1243 "DmiChassisType\0"
1244 "DmiChassisVendor\0"
1245 "DmiChassisVersion\0"
1246 "DmiProcManufacturer\0"
1247 "DmiProcVersion\0"
1248 "DmiOEMVBoxVer\0"
1249 "DmiOEMVBoxRev\0"
1250 "DmiUseHostInfo\0"
1251 "DmiExposeMemoryTable\0"
1252 "DmiExposeProcInf\0"
1253 "CheckShutdownStatusForSoftReset\0"
1254 "ClearShutdownStatusOnHardReset\0"
1255 ))
1256 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1257 N_("Invalid configuration for device pcbios device"));
1258
1259 /*
1260 * Init the data.
1261 */
1262 rc = CFGMR3QueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1263 if (RT_FAILURE(rc))
1264 return PDMDEV_SET_ERROR(pDevIns, rc,
1265 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1266
1267 rc = CFGMR3QueryU64Def(pCfg, "McfgBase", &pThis->u64McfgBase, 0);
1268 if (RT_FAILURE(rc))
1269 return PDMDEV_SET_ERROR(pDevIns, rc,
1270 N_("Configuration error: Querying \"\" as integer failed"));
1271 rc = CFGMR3QueryU64Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1272 if (RT_FAILURE(rc))
1273 return PDMDEV_SET_ERROR(pDevIns, rc,
1274 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1275
1276
1277 LogRel(("PcBios: [SMP] BIOS with %u CPUs\n", pThis->cCpus));
1278
1279 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1280 if (RT_FAILURE (rc))
1281 return PDMDEV_SET_ERROR(pDevIns, rc,
1282 N_("Configuration error: Failed to read \"IOAPIC\""));
1283
1284 rc = CFGMR3QueryU8Def(pCfg, "APIC", &pThis->u8APICMode, 1);
1285 if (RT_FAILURE (rc))
1286 return PDMDEV_SET_ERROR(pDevIns, rc,
1287 N_("Configuration error: Failed to read \"APIC\""));
1288
1289 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1290 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1291 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1292 {
1293 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1294 if (RT_FAILURE(rc))
1295 return rc;
1296 }
1297
1298 rc = CFGMR3QueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1299 if (RT_FAILURE(rc))
1300 return PDMDEV_SET_ERROR(pDevIns, rc,
1301 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1302
1303 rc = CFGMR3QueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1304 if (RT_FAILURE(rc))
1305 return PDMDEV_SET_ERROR(pDevIns, rc,
1306 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1307
1308 rc = CFGMR3QueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1309 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1310 pThis->pszSataDevice = NULL;
1311 else if (RT_FAILURE(rc))
1312 return PDMDEV_SET_ERROR(pDevIns, rc,
1313 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1314
1315 if (pThis->pszSataDevice)
1316 {
1317 static const char * const s_apszSataDisks[] =
1318 { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
1319 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1320 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1321 {
1322 rc = CFGMR3QueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1323 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1324 pThis->iSataHDLUN[i] = i;
1325 else if (RT_FAILURE(rc))
1326 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1327 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1328 }
1329 }
1330
1331 /* Repeat the exercise for SCSI drives. */
1332 rc = CFGMR3QueryStringAlloc(pCfg, "ScsiHardDiskDevice", &pThis->pszScsiDevice);
1333 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1334 pThis->pszScsiDevice = NULL;
1335 else if (RT_FAILURE(rc))
1336 return PDMDEV_SET_ERROR(pDevIns, rc,
1337 N_("Configuration error: Querying \"ScsiHardDiskDevice\" as a string failed"));
1338
1339 if (pThis->pszScsiDevice)
1340 {
1341 static const char * const s_apszScsiDisks[] =
1342 { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
1343 Assert(RT_ELEMENTS(s_apszScsiDisks) == RT_ELEMENTS(pThis->iScsiHDLUN));
1344 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iScsiHDLUN); i++)
1345 {
1346 rc = CFGMR3QueryU32(pCfg, s_apszScsiDisks[i], &pThis->iScsiHDLUN[i]);
1347 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1348 pThis->iScsiHDLUN[i] = i;
1349 else if (RT_FAILURE(rc))
1350 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1351 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszScsiDisks);
1352 }
1353 }
1354
1355
1356 /*
1357 * Register I/O Ports and PC BIOS.
1358 */
1359 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1360 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1361 if (RT_FAILURE(rc))
1362 return rc;
1363 rc = pcbiosRegisterShutdown(pDevIns, pThis, true /* fNewShutdownPort */);
1364 if (RT_FAILURE(rc))
1365 return rc;
1366
1367 /*
1368 * Register SSM handlers, for remembering which shutdown port to use.
1369 */
1370 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCBIOS_SSM_VERSION, 1 /* cbGuess */, NULL,
1371 NULL, NULL, NULL,
1372 NULL, pcbiosSaveExec, NULL,
1373 pcbiosLoadPrep, pcbiosLoadExec, pcbiosLoadDone);
1374
1375 /*
1376 * Read the PXE debug logging option.
1377 */
1378 rc = CFGMR3QueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1379 if (RT_FAILURE(rc))
1380 return PDMDEV_SET_ERROR(pDevIns, rc,
1381 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1382
1383 /* Clear the net boot device list. All bits set invokes old behavior,
1384 * as if no second CMOS bank was present.
1385 */
1386 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1387
1388 /*
1389 * Determine the network boot order.
1390 */
1391 PCFGMNODE pCfgNetBoot = CFGMR3GetChild(pCfg, "NetBoot");
1392 if (pCfgNetBoot == NULL)
1393 {
1394 /* Do nothing. */
1395 rc = VINF_SUCCESS;
1396 }
1397 else
1398 {
1399 PCFGMNODE pCfgNetBootDevice;
1400 uint8_t u8PciBus;
1401 uint8_t u8PciDev;
1402 uint8_t u8PciFn;
1403 uint16_t u16BusDevFn;
1404 char szIndex[] = "?";
1405
1406 Assert(pCfgNetBoot);
1407 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1408 {
1409 szIndex[0] = '0' + i;
1410 pCfgNetBootDevice = CFGMR3GetChild(pCfgNetBoot, szIndex);
1411
1412 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1413 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1414 {
1415 /* Do nothing and stop iterating. */
1416 rc = VINF_SUCCESS;
1417 break;
1418 }
1419 else if (RT_FAILURE(rc))
1420 return PDMDEV_SET_ERROR(pDevIns, rc,
1421 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1422 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1423 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1424 {
1425 /* Do nothing and stop iterating. */
1426 rc = VINF_SUCCESS;
1427 break;
1428 }
1429 else if (RT_FAILURE(rc))
1430 return PDMDEV_SET_ERROR(pDevIns, rc,
1431 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1432 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1433 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1434 {
1435 /* Do nothing and stop iterating. */
1436 rc = VINF_SUCCESS;
1437 break;
1438 }
1439 else if (RT_FAILURE(rc))
1440 return PDMDEV_SET_ERROR(pDevIns, rc,
1441 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1442 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1443 pThis->au16NetBootDev[i] = u16BusDevFn;
1444 }
1445 }
1446
1447 /*
1448 * Get the system BIOS ROM file name.
1449 */
1450 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1451 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1452 {
1453 pThis->pszPcBiosFile = NULL;
1454 rc = VINF_SUCCESS;
1455 }
1456 else if (RT_FAILURE(rc))
1457 return PDMDEV_SET_ERROR(pDevIns, rc,
1458 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1459 else if (!*pThis->pszPcBiosFile)
1460 {
1461 MMR3HeapFree(pThis->pszPcBiosFile);
1462 pThis->pszPcBiosFile = NULL;
1463 }
1464
1465 /*
1466 * Get the CPU arch so we can load the appropriate ROMs.
1467 */
1468 PVM pVM = PDMDevHlpGetVM(pDevIns);
1469 CPUMMICROARCH const enmMicroarch = pVM ? pVM->cpum.ro.GuestFeatures.enmMicroarch : kCpumMicroarch_Intel_P6;
1470
1471 if (pThis->pszPcBiosFile)
1472 {
1473 /*
1474 * Load the BIOS ROM.
1475 */
1476 RTFILE hFilePcBios;
1477 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1478 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1479 if (RT_SUCCESS(rc))
1480 {
1481 /* Figure the size and check restrictions. */
1482 uint64_t cbPcBios;
1483 rc = RTFileGetSize(hFilePcBios, &cbPcBios);
1484 if (RT_SUCCESS(rc))
1485 {
1486 pThis->cbPcBios = (uint32_t)cbPcBios;
1487 if ( RT_ALIGN(pThis->cbPcBios, _64K) == pThis->cbPcBios
1488 && pThis->cbPcBios == cbPcBios
1489 && pThis->cbPcBios <= 32 * _64K
1490 && pThis->cbPcBios >= _64K)
1491 {
1492 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1493 if (pThis->pu8PcBios)
1494 {
1495 rc = RTFileRead(hFilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1496 if (RT_FAILURE(rc))
1497 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1498 N_("Error reading the BIOS image ('%s)"), pThis->pszPcBiosFile);
1499 }
1500 else
1501 rc = PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1502 N_("Failed to allocate %#x bytes for loading the BIOS image"),
1503 pThis->cbPcBios);
1504 }
1505 else
1506 rc = PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
1507 N_("Invalid system BIOS file size ('%s'): %#llx (%llu)"),
1508 pThis->pszPcBiosFile, cbPcBios, cbPcBios);
1509 }
1510 else
1511 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1512 N_("Failed to query the system BIOS file size ('%s')"),
1513 pThis->pszPcBiosFile);
1514 RTFileClose(hFilePcBios);
1515 }
1516 else
1517 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1518 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1519 if (RT_FAILURE(rc))
1520 return rc;
1521
1522 LogRel(("PcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1523 }
1524 else
1525 {
1526 /*
1527 * Use one of the embedded BIOS ROM images.
1528 */
1529 uint8_t const *pbBios;
1530 uint32_t cbBios;
1531 if ( enmMicroarch == kCpumMicroarch_Intel_8086
1532 || enmMicroarch == kCpumMicroarch_Intel_80186
1533 || enmMicroarch == kCpumMicroarch_NEC_V20
1534 || enmMicroarch == kCpumMicroarch_NEC_V30)
1535 {
1536 pbBios = g_abPcBiosBinary8086;
1537 cbBios = g_cbPcBiosBinary8086;
1538 LogRel(("PcBios: Using the 8086 BIOS image!\n"));
1539 }
1540 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
1541 {
1542 pbBios = g_abPcBiosBinary286;
1543 cbBios = g_cbPcBiosBinary286;
1544 LogRel(("PcBios: Using the 286 BIOS image!\n"));
1545 }
1546 else
1547 {
1548 pbBios = g_abPcBiosBinary386;
1549 cbBios = g_cbPcBiosBinary386;
1550 LogRel(("PcBios: Using the 386+ BIOS image.\n"));
1551 }
1552 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbBios);
1553 if (pThis->pu8PcBios)
1554 {
1555 pThis->cbPcBios = cbBios;
1556 memcpy(pThis->pu8PcBios, pbBios, cbBios);
1557 }
1558 else
1559 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1560 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"), cbBios);
1561 }
1562 const uint8_t *pu8PcBiosBinary = pThis->pu8PcBios;
1563 uint32_t cbPcBiosBinary = pThis->cbPcBios;
1564
1565 /*
1566 * Query the machine's UUID for SMBIOS/DMI use.
1567 */
1568 RTUUID uuid;
1569 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1570 if (RT_FAILURE(rc))
1571 return PDMDEV_SET_ERROR(pDevIns, rc,
1572 N_("Configuration error: Querying \"UUID\" failed"));
1573
1574 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1575 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1576 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1577 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1578 uint16_t cbDmiTables = 0;
1579 uint16_t cNumDmiTables = 0;
1580 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1581 &uuid, pCfg, pThis->cCpus, &cbDmiTables, &cNumDmiTables);
1582 if (RT_FAILURE(rc))
1583 return rc;
1584
1585 for (unsigned i = 0; i < pThis->cbPcBios; i += 16)
1586 {
1587 /* If the DMI table is located at the expected place, patch the DMI table length and the checksum. */
1588 if ( pThis->pu8PcBios[i + 0x00] == '_'
1589 && pThis->pu8PcBios[i + 0x01] == 'D'
1590 && pThis->pu8PcBios[i + 0x02] == 'M'
1591 && pThis->pu8PcBios[i + 0x03] == 'I'
1592 && pThis->pu8PcBios[i + 0x04] == '_'
1593 && *(uint16_t*)&pThis->pu8PcBios[i + 0x06] == 0)
1594 {
1595 *(uint16_t*)&pThis->pu8PcBios[i + 0x06] = RT_H2LE_U16(cbDmiTables);
1596 *(uint16_t*)&pThis->pu8PcBios[i + 0x0C] = RT_H2LE_U16(cNumDmiTables);
1597 uint8_t u8Sum = 0;
1598 for (unsigned j = 0; j < pThis->cbPcBios; j++)
1599 if (j != i + 0x05)
1600 u8Sum += pThis->pu8PcBios[j];
1601 pThis->pu8PcBios[i + 0x05] = -u8Sum;
1602 break;
1603 }
1604 }
1605
1606 if (pThis->u8IOAPIC)
1607 {
1608 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1609 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
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