VirtualBox

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

Last change on this file was 100456, checked in by vboxsync, 10 months ago

BIOS: Added a way for the APM BIOS to halt the virtual CPU through port I/O instead of HLT to solve problems with obstinate guests (see bugref:6549).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use