VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use