VirtualBox

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

Last change on this file since 101427 was 100456, checked in by vboxsync, 17 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
Line 
1/* $Id: DevPcBios.cpp 100456 2023-07-10 13:46:21Z 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 BIOS control (used by APM). */
230 RTIOPORT ControlPort;
231 /** True=use new port number for BIOS control (used by APM). */
232 bool fNewControlPort;
233 bool afPadding[3+4];
234 /** The BIOS I/O port, either at 0x040f or 0x8900
235 * (old saved state). */
236 IOMMMIOHANDLE hIoPortControl;
237} DEVPCBIOS;
238/** Pointer to the BIOS device state. */
239typedef DEVPCBIOS *PDEVPCBIOS;
240
241
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{
255 SSMFIELD_ENTRY( DEVPCBIOS, fNewControlPort),
256 SSMFIELD_ENTRY_TERM()
257};
258
259
260/**
261 * @callback_method_impl{FNIOMIOPORTNEWIN, Bochs Debug.}
262 */
263static DECLCALLBACK(VBOXSTRICTRC)
264pcbiosIOPortDebugRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
265{
266 RT_NOREF5(pDevIns, pvUser, offPort, pu32, cb);
267 return VERR_IOM_IOPORT_UNUSED;
268}
269
270
271/**
272 * @callback_method_impl{FNIOMIOPORTNEWOUT, Bochs Debug.}
273 */
274static DECLCALLBACK(VBOXSTRICTRC)
275pcbiosIOPortDebugWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
276{
277 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
278 RT_NOREF(pvUser);
279 Assert(offPort < 4);
280
281 /*
282 * Bochs BIOS char printing.
283 */
284 if ( cb == 1
285 && ( offPort == 2
286 || offPort == 3))
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. */
298 uint32_t iMsg = pThis->iMsg;
299 if (u32 == '\n' || u32 == '\r')
300 {
301 AssertStmt(iMsg < sizeof(pThis->szMsg), iMsg = sizeof(pThis->szMsg) - 1);
302 pThis->szMsg[iMsg] = '\0';
303 if (iMsg)
304 Log(("pcbios: %s\n", pThis->szMsg));
305 iMsg = 0;
306 }
307 else
308 {
309 if (iMsg >= sizeof(pThis->szMsg) - 1)
310 {
311 pThis->szMsg[iMsg] = '\0';
312 Log(("pcbios: %s\n", pThis->szMsg));
313 iMsg = 0;
314 }
315 pThis->szMsg[iMsg] = (char)u32;
316 pThis->szMsg[++iMsg] = '\0';
317 }
318 pThis->iMsg = iMsg;
319 return VINF_SUCCESS;
320 }
321
322 /* not in use. */
323 return VINF_SUCCESS;
324}
325
326
327/**
328 * @callback_method_impl{FNIOMIOPORTNEWIN, BIOS control port.}
329 */
330static DECLCALLBACK(VBOXSTRICTRC)
331pcbiosIOPortControlRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
332{
333 RT_NOREF5(pDevIns, pvUser, offPort, pu32, cb);
334 return VERR_IOM_IOPORT_UNUSED;
335}
336
337
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
369/**
370 * @callback_method_impl{FNIOMIOPORTNEWOUT, BIOS control port.}
371 */
372static DECLCALLBACK(VBOXSTRICTRC)
373pcbiosIOPortControlWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
374{
375 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
376 RT_NOREF(pvUser, offPort);
377 Assert(offPort == 0);
378
379 if (cb == 1)
380 {
381 static const unsigned char s_szShutdown[] = "Shutdown";
382 static const unsigned char s_szBootfail[] = "Bootfail";
383 static const unsigned char s_szProchalt[] = "Prochalt";
384 AssertCompile(sizeof(s_szShutdown) == sizeof(s_szBootfail));
385 AssertCompile(sizeof(s_szShutdown) == sizeof(s_szProchalt));
386
387 if (pThis->iControl < sizeof(s_szShutdown)) /* paranoia */
388 {
389 if (u32 == s_szShutdown[pThis->iControl])
390 {
391
392 pThis->iControl++;
393 if (pThis->iControl >= 8)
394 {
395 pThis->iControl = 0;
396 return pcbiosApmShutdown(pDevIns);
397 }
398 }
399 else if (u32 == s_szBootfail[pThis->iControl])
400 {
401 pThis->iControl++;
402 if (pThis->iControl >= 8)
403 {
404 pThis->iControl = 0;
405 pcbiosReportBootFail(pDevIns);
406 }
407 }
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 }
418 else
419 pThis->iControl = 0;
420 }
421 else
422 pThis->iControl = 0;
423 }
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 }
442 /* else: not in use. */
443
444 return VINF_SUCCESS;
445}
446
447
448/**
449 * Register the BIOS control port.
450 * This is used by pcbiosConstruct, pcbiosReset and pcbiosLoadExec.
451 */
452static int pcbiosRegisterControl(PPDMDEVINS pDevIns, PDEVPCBIOS pThis, bool fNewControlPort)
453{
454 if (pThis->ControlPort != 0)
455 {
456 int rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortControl);
457 AssertRC(rc);
458 }
459
460 pThis->fNewControlPort = fNewControlPort;
461 if (fNewControlPort)
462 pThis->ControlPort = VBOX_BIOS_SHUTDOWN_PORT;
463 else
464 pThis->ControlPort = VBOX_BIOS_OLD_SHUTDOWN_PORT;
465 return PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortControl, pThis->ControlPort);
466}
467
468
469/**
470 * @callback_method_impl{FNSSMDEVSAVEEXEC}
471 */
472static DECLCALLBACK(int) pcbiosSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
473{
474 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
475 return pDevIns->pHlpR3->pfnSSMPutStruct(pSSM, pThis, g_aPcBiosFields);
476}
477
478
479/**
480 * @callback_method_impl{FNSSMDEVLOADPREP,
481 * Clears the fNewControlPort flag prior to loading the state so that old
482 * saved VM states keeps using the old port address (no pcbios state)}
483 */
484static DECLCALLBACK(int) pcbiosLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
485{
486 RT_NOREF(pSSM);
487 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
488
489 /* Since there are legacy saved state files without any SSM data for PCBIOS
490 * this is the only way to handle them correctly. */
491 pThis->fNewControlPort = false;
492
493 return VINF_SUCCESS;
494}
495
496
497/**
498 * @callback_method_impl{FNSSMDEVLOADEXEC}
499 */
500static DECLCALLBACK(int) pcbiosLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
501{
502 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
503
504 if (uVersion > PCBIOS_SSM_VERSION)
505 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
506 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
507
508 return pDevIns->pHlpR3->pfnSSMGetStruct(pSSM, pThis, g_aPcBiosFields);
509}
510
511
512/**
513 * @callback_method_impl{FNSSMDEVLOADDONE,
514 * Updates the shutdown port registration to match the flag loaded (or not).}
515 */
516static DECLCALLBACK(int) pcbiosLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
517{
518 RT_NOREF(pSSM);
519 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
520 return pcbiosRegisterControl(pDevIns, pThis, pThis->fNewControlPort);
521}
522
523
524/**
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{
559 RT_NOREF1(fFlags);
560 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
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",
575 pThis->cLoggedSoftResets, bShutdownStatus, Far16.sel, Far16.off,
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{
590 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
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 }
601
602 /* After reset the new BIOS code is active, use the new control port. */
603 pcbiosRegisterControl(pDevIns, pThis, true /* fNewControlPort */);
604}
605
606
607/**
608 * Attempt to guess the LCHS disk geometry from the MS-DOS master boot record
609 * (partition table).
610 *
611 * @returns VBox status code.
612 * @param pMedia The media device interface of the disk.
613 * @param pLCHSGeometry Where to return the disk geometry on success
614 */
615static int biosGuessDiskLCHS(PPDMIMEDIA pMedia, PPDMMEDIAGEOMETRY pLCHSGeometry)
616{
617 uint8_t aMBR[512], *p;
618 int rc;
619 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
620
621 if (!pMedia)
622 return VERR_INVALID_PARAMETER;
623 rc = pMedia->pfnReadPcBios(pMedia, 0, aMBR, sizeof(aMBR));
624 if (RT_FAILURE(rc))
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;
635 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector && iEndHead)
636 {
637 /* Assumption: partition terminates on a cylinder boundary. */
638 cLCHSHeads = iEndHead + 1;
639 cLCHSSectors = iEndSector;
640 cLCHSCylinders = RT_MIN(1024, pMedia->pfnGetSize(pMedia) / (512 * cLCHSHeads * cLCHSSectors));
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
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
686/**
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{
693 unsigned type;
694 unsigned typeLow;
695 bool fCompatGeom = false;
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/**
740 * Initializes the CMOS data for one harddisk.
741 */
742static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
743{
744 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
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. */
759 pcbiosCmosWrite(pDevIns, offType, 47);
760 }
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);
777}
778
779
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 */
788static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIMEDIA pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
789{
790 RT_NOREF1(pBase);
791
792 PDMMEDIAGEOMETRY LCHSGeometry;
793 int rc = pHardDisk->pfnBiosGetLCHSGeometry(pHardDisk, &LCHSGeometry);
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. */
802 rc = biosGuessDiskLCHS(pHardDisk, &LCHSGeometry);
803 if (RT_FAILURE(rc))
804 {
805 /* Try if PCHS geometry works, otherwise fall back. */
806 rc = pHardDisk->pfnBiosGetPCHSGeometry(pHardDisk, &LCHSGeometry);
807 }
808 if ( RT_FAILURE(rc)
809 || LCHSGeometry.cCylinders == 0
810 || LCHSGeometry.cCylinders > 1024
811 || LCHSGeometry.cHeads == 0
812 || LCHSGeometry.cHeads > 255
813 || LCHSGeometry.cSectors == 0
814 || LCHSGeometry.cSectors > 63)
815 {
816 uint64_t cSectors = pHardDisk->pfnGetSize(pHardDisk) / 512;
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 }
845 rc = pHardDisk->pfnBiosSetLCHSGeometry(pHardDisk, &LCHSGeometry);
846 if (rc == VERR_VD_IMAGE_READ_ONLY)
847 {
848 LogRel(("PcBios: ATA failed to update LCHS geometry, read only\n"));
849 rc = VINF_SUCCESS;
850 }
851 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
852 {
853 LogRel(("PcBios: ATA failed to update LCHS geometry, backend refused\n"));
854 rc = VINF_SUCCESS;
855 }
856 }
857
858 *pLCHSGeometry = LCHSGeometry;
859
860 return rc;
861}
862
863
864/**
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 */
872static int getLogicalDiskGeometry(PPDMIMEDIA pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
873{
874 PDMMEDIAGEOMETRY LCHSGeometry;
875 int rc = VINF_SUCCESS;
876
877 rc = pHardDisk->pfnBiosGetLCHSGeometry(pHardDisk, &LCHSGeometry);
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/**
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 */
903static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
904{
905 switch (pThis->aenmBootDevice[iOrder])
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:
918 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
919 return 0;
920 }
921}
922
923
924/**
925 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
926 *
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{
934 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
935 uint32_t u32;
936 unsigned i;
937 PPDMIMEDIA apHDs[4] = {0};
938 LogFlow(("pcbiosInitComplete:\n"));
939
940 uint64_t const cbRamSize = PDMDevHlpMMPhysGetRamSize(pDevIns);
941 uint32_t const cbBelow4GB = PDMDevHlpMMPhysGetRamSizeBelow4GB(pDevIns);
942 uint64_t const cbAbove4GB = PDMDevHlpMMPhysGetRamSizeAbove4GB(pDevIns);
943
944 /*
945 * Memory sizes.
946 */
947 /* base memory. */
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 */
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 */
951
952 /* Extended memory, up to 65MB */
953 u32 = cbRamSize >= 65 * _1M ? 0xffff : ((uint32_t)cbRamSize - _1M) / _1K;
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 */
958
959 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
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
962 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
963 with the high BIOS mapping.) */
964 if (cbRamSize > 16 * _1M)
965 u32 = (RT_MIN(cbBelow4GB, UINT32_C(0xfe000000)) - 16U * _1M) / _64K;
966 else
967 u32 = 0;
968 pcbiosCmosWrite(pDevIns, 0x34, RT_BYTE1(u32));
969 pcbiosCmosWrite(pDevIns, 0x35, RT_BYTE2(u32));
970
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. */
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);
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));
982
983 /*
984 * Number of CPUs.
985 */
986 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
987
988 /*
989 * APIC mode.
990 */
991 pcbiosCmosWrite(pDevIns, 0x78, pThis->u8APICMode);
992
993 /*
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
999 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
1000 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
1001 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
1002 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
1003 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
1004 pcbiosCmosWrite(pDevIns, 0x38, reg38);
1005 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
1006
1007 /*
1008 * PXE debug option.
1009 */
1010 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
1011
1012 /*
1013 * Network boot device list.
1014 */
1015 for (i = 0; i < NET_BOOT_DEVS; ++i)
1016 {
1017 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, RT_BYTE1(pThis->au16NetBootDev[i]));
1018 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, RT_BYTE2(pThis->au16NetBootDev[i]));
1019 }
1020
1021 /*
1022 * Floppy drive type.
1023 */
1024 uint32_t cFDs = 0;
1025 u32 = 0;
1026 for (i = 0; i < 2; i++)
1027 {
1028 PPDMIBASE pBase;
1029 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszFDDevice, 0, i, &pBase);
1030 if (RT_SUCCESS(rc))
1031 {
1032 PPDMIMEDIA pFD = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1033 if (pFD)
1034 {
1035 cFDs++;
1036 unsigned cShift = i == 0 ? 4 : 0;
1037 switch (pFD->pfnGetType(pFD))
1038 {
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;
1046 default: AssertFailed(); break;
1047 }
1048 }
1049 }
1050 }
1051 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
1052
1053 /*
1054 * Equipment byte.
1055 */
1056 if (cFDs > 0)
1057 u32 = ((cFDs - 1) << 6) | 0x01; /* floppy installed, additional drives. */
1058 else
1059 u32 = 0x00; /* floppy not installed. */
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) */
1063 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
1064
1065 /*
1066 * IDE harddisks.
1067 */
1068 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1069 {
1070 PPDMIBASE pBase;
1071 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszHDDevice, 0, i, &pBase);
1072 if (RT_SUCCESS(rc))
1073 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1074 if ( apHDs[i]
1075 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
1076 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
1077 apHDs[i] = NULL;
1078 if (apHDs[i])
1079 {
1080 PDMMEDIAGEOMETRY LCHSGeometry;
1081 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
1082 AssertRC(rc2);
1083
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 }
1109 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo, &LCHSGeometry);
1110 if (i < 2)
1111 pcbiosCmosTryPCATHardDisk(pDevIns, i, &LCHSGeometry);
1112 }
1113 LogRel(("PcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1114 }
1115 }
1116
1117 /*
1118 * SATA harddisks.
1119 */
1120 if (pThis->pszSataDevice)
1121 {
1122 /* Clear pointers to the block devices. */
1123 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1124 apHDs[i] = NULL;
1125
1126 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1127 {
1128 PPDMIBASE pBase;
1129 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
1130 if (RT_SUCCESS(rc))
1131 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1132 if ( apHDs[i]
1133 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
1134 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
1135 apHDs[i] = NULL;
1136 if (apHDs[i])
1137 {
1138 PDMMEDIAGEOMETRY LCHSGeometry;
1139 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
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 }
1166 LogRel(("PcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1167 }
1168 }
1169 }
1170
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;
1183 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszScsiDevice, 0, pThis->iScsiHDLUN[i], &pBase);
1184 if (RT_SUCCESS(rc))
1185 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1186 if ( apHDs[i]
1187 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
1188 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
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);
1219 LogRel(("PcBios: SCSI LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1220 }
1221 else
1222 LogRel(("PcBios: SCSI LUN#%d LCHS not provided\n", i));
1223 }
1224 }
1225 }
1226
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);
1231 pcbiosCmosWrite(pDevIns, 0x2e, RT_BYTE1(cksum));
1232 pcbiosCmosWrite(pDevIns, 0x2f, RT_BYTE2(cksum));
1233
1234 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
1235 return VINF_SUCCESS;
1236}
1237
1238
1239/**
1240 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1241 */
1242static DECLCALLBACK(void) pcbiosMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1243{
1244 RT_NOREF1(enmCtx);
1245 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1246 LogFlow(("pcbiosMemSetup:\n"));
1247
1248 if (pThis->u8IOAPIC)
1249 FwCommonPlantMpsFloatPtr(pDevIns, pThis->u32MPTableAddr);
1250
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 */
1257 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, GUEST_PAGE_SIZE) >> GUEST_PAGE_SHIFT;
1258 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
1259 while (cPages > 0)
1260 {
1261 uint8_t abPage[GUEST_PAGE_SIZE];
1262 int rc;
1263
1264 /* Read the (original) ROM page and write it back to the RAM page. */
1265 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1266 AssertLogRelRC(rc);
1267
1268 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
1269 AssertLogRelRC(rc);
1270 if (RT_FAILURE(rc))
1271 memset(abPage, 0xcc, sizeof(abPage));
1272
1273 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
1274 AssertLogRelRC(rc);
1275
1276 /* Switch to the RAM/RAM mode. */
1277 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1278 AssertLogRelRC(rc);
1279
1280 /* Advance */
1281 GCPhys += GUEST_PAGE_SIZE;
1282 cPages--;
1283 }
1284}
1285
1286
1287/**
1288 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1289 */
1290static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1291{
1292 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1293 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1294 LogFlow(("pcbiosDestruct:\n"));
1295
1296 /*
1297 * Free MM heap pointers.
1298 */
1299 if (pThis->pu8PcBios)
1300 {
1301 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8PcBios);
1302 pThis->pu8PcBios = NULL;
1303 }
1304
1305 if (pThis->pszPcBiosFile)
1306 {
1307 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1308 pThis->pszPcBiosFile = NULL;
1309 }
1310
1311 if (pThis->pu8LanBoot)
1312 {
1313 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8LanBoot);
1314 pThis->pu8LanBoot = NULL;
1315 }
1316
1317 if (pThis->pszLanBootFile)
1318 {
1319 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1320 pThis->pszLanBootFile = NULL;
1321 }
1322
1323 if (pThis->pszHDDevice)
1324 {
1325 PDMDevHlpMMHeapFree(pDevIns, pThis->pszHDDevice);
1326 pThis->pszHDDevice = NULL;
1327 }
1328
1329 if (pThis->pszFDDevice)
1330 {
1331 PDMDevHlpMMHeapFree(pDevIns, pThis->pszFDDevice);
1332 pThis->pszFDDevice = NULL;
1333 }
1334
1335 if (pThis->pszSataDevice)
1336 {
1337 PDMDevHlpMMHeapFree(pDevIns, pThis->pszSataDevice);
1338 pThis->pszSataDevice = NULL;
1339 }
1340
1341 if (pThis->pszScsiDevice)
1342 {
1343 PDMDevHlpMMHeapFree(pDevIns, pThis->pszScsiDevice);
1344 pThis->pszScsiDevice = NULL;
1345 }
1346
1347 return VINF_SUCCESS;
1348}
1349
1350
1351/**
1352 * Convert config value to DEVPCBIOSBOOT.
1353 *
1354 * @returns VBox status code.
1355 * @param pDevIns Device instance data.
1356 * @param pCfg Configuration handle.
1357 * @param pszParam The name of the value to read.
1358 * @param penmBoot Where to store the boot method.
1359 */
1360static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1361{
1362 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1363
1364 char szBuf[64];
1365 int rc = pHlp->pfnCFGMQueryString(pCfg, pszParam, szBuf, sizeof(szBuf));
1366 if (RT_FAILURE(rc))
1367 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1368 N_("Configuration error: Querying \"%s\" as a string failed"), pszParam);
1369
1370 if (!strcmp(szBuf, "DVD") || !strcmp(szBuf, "CDROM"))
1371 *penmBoot = DEVPCBIOSBOOT_DVD;
1372 else if (!strcmp(szBuf, "IDE"))
1373 *penmBoot = DEVPCBIOSBOOT_HD;
1374 else if (!strcmp(szBuf, "FLOPPY"))
1375 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1376 else if (!strcmp(szBuf, "LAN"))
1377 *penmBoot = DEVPCBIOSBOOT_LAN;
1378 else if (!strcmp(szBuf, "NONE"))
1379 *penmBoot = DEVPCBIOSBOOT_NONE;
1380 else
1381 rc = PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1382 N_("Configuration error: The \"%s\" value \"%s\" is unknown"), pszParam, szBuf);
1383 return rc;
1384}
1385
1386
1387/**
1388 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1389 */
1390static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1391{
1392 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1393 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1394 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1395 int rc;
1396 int cb;
1397 Assert(iInstance == 0); RT_NOREF(iInstance);
1398
1399 /*
1400 * Validate configuration.
1401 */
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"
1465 ,
1466 "NetBoot");
1467 /*
1468 * Init the data.
1469 */
1470 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1471 if (RT_FAILURE(rc))
1472 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1473
1474 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThis->u64McfgBase, 0);
1475 if (RT_FAILURE(rc))
1476 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"\" as integer failed"));
1477 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1478 if (RT_FAILURE(rc))
1479 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1480
1481
1482 LogRel(("PcBios: [SMP] BIOS with %u CPUs\n", pThis->cCpus));
1483
1484 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1485 if (RT_FAILURE (rc))
1486 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"IOAPIC\""));
1487
1488 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThis->u8APICMode, 1);
1489 if (RT_FAILURE (rc))
1490 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"APIC\""));
1491
1492 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1493 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1494 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1495 {
1496 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1497 if (RT_FAILURE(rc))
1498 return rc;
1499 }
1500
1501 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1502 if (RT_FAILURE(rc))
1503 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1504
1505 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1506 if (RT_FAILURE(rc))
1507 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1508
1509 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1510 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1511 pThis->pszSataDevice = NULL;
1512 else if (RT_FAILURE(rc))
1513 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1514
1515 if (pThis->pszSataDevice)
1516 {
1517 static const char * const s_apszSataDisks[] = { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
1518 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1519 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1520 {
1521 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1522 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1523 pThis->iSataHDLUN[i] = i;
1524 else if (RT_FAILURE(rc))
1525 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1526 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1527 }
1528 }
1529
1530 /* Repeat the exercise for SCSI drives. */
1531 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "ScsiHardDiskDevice", &pThis->pszScsiDevice);
1532 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1533 pThis->pszScsiDevice = NULL;
1534 else if (RT_FAILURE(rc))
1535 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ScsiHardDiskDevice\" as a string failed"));
1536
1537 if (pThis->pszScsiDevice)
1538 {
1539 static const char * const s_apszScsiDisks[] = { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
1540 Assert(RT_ELEMENTS(s_apszScsiDisks) == RT_ELEMENTS(pThis->iScsiHDLUN));
1541 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iScsiHDLUN); i++)
1542 {
1543 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszScsiDisks[i], &pThis->iScsiHDLUN[i]);
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
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"));
1556
1557
1558 /*
1559 * Register the I/O Ports.
1560 */
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
1566 rc = PDMDevHlpIoPortCreateIsa(pDevIns, 1 /*cPorts*/, pcbiosIOPortControlWrite, pcbiosIOPortControlRead, NULL /*pvUser*/,
1567 "PC BIOS - Control", NULL /*paExtDescs*/, &pThis->hIoPortControl);
1568 AssertRCReturn(rc, rc);
1569 rc = pcbiosRegisterControl(pDevIns, pThis, true /* fNewControlPort */);
1570 AssertRCReturn(rc, rc);
1571
1572 /*
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
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
1585 /*
1586 * Determine the network boot order.
1587 */
1588 PCFGMNODE pCfgNetBoot = pHlp->pfnCFGMGetChild(pCfg, "NetBoot");
1589 if (pCfgNetBoot == NULL)
1590 {
1591 /* Do nothing. */
1592 rc = VINF_SUCCESS;
1593 }
1594 else
1595 {
1596 PCFGMNODE pCfgNetBootDevice;
1597 uint8_t u8PciBus;
1598 uint8_t u8PciDev;
1599 uint8_t u8PciFn;
1600 uint16_t u16BusDevFn;
1601 char szIndex[] = "?";
1602
1603 Assert(pCfgNetBoot);
1604 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1605 {
1606 szIndex[0] = '0' + i;
1607 pCfgNetBootDevice = pHlp->pfnCFGMGetChild(pCfgNetBoot, szIndex);
1608
1609 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
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"));
1619 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
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,
1628 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1629 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
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"));
1639 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1640 pThis->au16NetBootDev[i] = u16BusDevFn;
1641 }
1642 }
1643
1644 /*
1645 * Get the system BIOS ROM file name.
1646 */
1647 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1648 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1649 {
1650 pThis->pszPcBiosFile = NULL;
1651 rc = VINF_SUCCESS;
1652 }
1653 else if (RT_FAILURE(rc))
1654 return PDMDEV_SET_ERROR(pDevIns, rc,
1655 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1656 else if (!*pThis->pszPcBiosFile)
1657 {
1658 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1659 pThis->pszPcBiosFile = NULL;
1660 }
1661
1662 /*
1663 * Get the CPU arch so we can load the appropriate ROMs.
1664 */
1665 CPUMMICROARCH const enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
1666
1667 if (pThis->pszPcBiosFile)
1668 {
1669 /*
1670 * Load the BIOS ROM.
1671 */
1672 RTFILE hFilePcBios;
1673 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1674 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1675 if (RT_SUCCESS(rc))
1676 {
1677 /* Figure the size and check restrictions. */
1678 uint64_t cbPcBios;
1679 rc = RTFileQuerySize(hFilePcBios, &cbPcBios);
1680 if (RT_SUCCESS(rc))
1681 {
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);
1705 }
1706 else
1707 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1708 N_("Failed to query the system BIOS file size ('%s')"),
1709 pThis->pszPcBiosFile);
1710 RTFileClose(hFilePcBios);
1711 }
1712 else
1713 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1714 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1715 if (RT_FAILURE(rc))
1716 return rc;
1717
1718 LogRel(("PcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1719 }
1720 else
1721 {
1722 /*
1723 * Use one of the embedded BIOS ROM images.
1724 */
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);
1749 if (pThis->pu8PcBios)
1750 {
1751 pThis->cbPcBios = cbBios;
1752 memcpy(pThis->pu8PcBios, pbBios, cbBios);
1753 }
1754 else
1755 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1756 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"), cbBios);
1757 }
1758 const uint8_t *pu8PcBiosBinary = pThis->pu8PcBios;
1759 uint32_t cbPcBiosBinary = pThis->cbPcBios;
1760
1761 /*
1762 * Query the machine's UUID for SMBIOS/DMI use.
1763 */
1764 RTUUID uuid;
1765 rc = pHlp->pfnCFGMQueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1766 if (RT_FAILURE(rc))
1767 return PDMDEV_SET_ERROR(pDevIns, rc,
1768 N_("Configuration error: Querying \"UUID\" failed"));
1769
1770 bool fUuidLe;
1771 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UuidLe", &fUuidLe, false);
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
1791 uint16_t cbDmiTables = 0;
1792 uint16_t cDmiTables = 0;
1793 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1794 &uuid, pCfg, pThis->cCpus, &cbDmiTables, &cDmiTables,
1795 false /*fUefi*/);
1796 if (RT_FAILURE(rc))
1797 return rc;
1798
1799 /* Look for _SM_/_DMI_ anchor strings within the BIOS and replace the table headers. */
1800 unsigned offAnchor = ~0U;
1801 unsigned const cbToSearch = pThis->cbPcBios - 32;
1802 for (unsigned off = 0; off <= cbToSearch; off += 16)
1803 {
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
1815 {
1816 offAnchor = off;
1817 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->pu8PcBios + off, cbDmiTables, cDmiTables);
1818 break;
1819 }
1820 }
1821 AssertLogRel(offAnchor <= cbToSearch);
1822
1823 if (pThis->u8IOAPIC)
1824 {
1825 pThis->u32MPTableAddr = VBOX_DMI_TABLE_BASE + VBOX_DMI_TABLE_SIZE;
1826 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage /* aka VBOX_DMI_TABLE_BASE */ + VBOX_DMI_TABLE_SIZE,
1827 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1828 LogRel(("PcBios: MPS table at %08x\n", pThis->u32MPTableAddr));
1829 }
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 /*
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 */
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. */
1847 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb], cb,
1848 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xfffff");
1849 if (RT_FAILURE(rc))
1850 return rc;
1851 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary, cbPcBiosBinary,
1852 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xffffffff");
1853 if (RT_FAILURE(rc))
1854 return rc;
1855
1856 /*
1857 * Get the LAN boot ROM file name.
1858 */
1859 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1860 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1861 {
1862 pThis->pszLanBootFile = NULL;
1863 rc = VINF_SUCCESS;
1864 }
1865 else if (RT_FAILURE(rc))
1866 return PDMDEV_SET_ERROR(pDevIns, rc,
1867 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1868 else if (!*pThis->pszLanBootFile)
1869 {
1870 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1871 pThis->pszLanBootFile = NULL;
1872 }
1873
1874 /*
1875 * Not loading LAN ROM for old CPUs.
1876 */
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)
1882 {
1883 const uint8_t *pu8LanBootBinary = NULL;
1884 uint64_t cbLanBootBinary;
1885 uint64_t cbFileLanBoot = 0;
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)
1892 {
1893 RTFILE hFileLanBoot = NIL_RTFILE;
1894 rc = RTFileOpen(&hFileLanBoot, pThis->pszLanBootFile,
1895 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1896 if (RT_SUCCESS(rc))
1897 {
1898 rc = RTFileQuerySize(hFileLanBoot, &cbFileLanBoot);
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);
1921 }
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));
1930 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1931 pThis->pszLanBootFile = NULL;
1932 }
1933 }
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)
1939 {
1940#ifdef VBOX_WITH_PXE_ROM
1941 pu8LanBootBinary = g_abNetBiosBinary;
1942 cbLanBootBinary = g_cbNetBiosBinary;
1943#endif
1944 }
1945 else
1946 {
1947 pu8LanBootBinary = pThis->pu8LanBoot;
1948 cbLanBootBinary = cbFileLanBoot;
1949 }
1950
1951 /*
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).
1957 */
1958 if (pu8LanBootBinary)
1959 {
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);
1967 }
1968 }
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
1972 else
1973 LogRel(("PcBios: Skipping built in ROM due to ancient target CPU.\n"));
1974#endif
1975
1976 /*
1977 * Configure Boot delay.
1978 */
1979 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1980 if (RT_FAILURE(rc))
1981 return PDMDEV_SET_ERROR(pDevIns, rc,
1982 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1983 if (pThis->uBootDelay > 15)
1984 pThis->uBootDelay = 15;
1985
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. */
1995 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CheckShutdownStatusForSoftReset", &pThis->fCheckShutdownStatusForSoftReset, true);
1996 AssertLogRelRCReturn(rc, rc);
1997
1998 /** @cfgm{ClearShutdownStatusOnHardReset, boolean, true}
1999 * Whether to clear the shutdown status code (CMOS register 0Fh) on hard reset. */
2000 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ClearShutdownStatusOnHardReset", &pThis->fClearShutdownStatusOnHardReset, true);
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
2010 return VINF_SUCCESS;
2011}
2012
2013
2014/**
2015 * The device registration structure.
2016 */
2017const PDMDEVREG g_DevicePcBios =
2018{
2019 /* .u32Version = */ PDM_DEVREG_VERSION,
2020 /* .uReserved0 = */ 0,
2021 /* .szName = */ "pcbios",
2022 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2023 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
2024 /* .cMaxInstances = */ 1,
2025 /* .uSharedVersion = */ 42,
2026 /* .cbInstanceShared = */ sizeof(DEVPCBIOS),
2027 /* .cbInstanceCC = */ 0,
2028 /* .cbInstanceRC = */ 0,
2029 /* .cMaxPciDevices = */ 0,
2030 /* .cMaxMsixVectors = */ 0,
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
2085};
2086
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette