VirtualBox

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

Last change on this file since 107483 was 107483, checked in by vboxsync, 3 weeks ago

Devices/PC/DevPcBios.cpp: Fix some parfait warnings, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 74.6 KB
Line 
1/* $Id: DevPcBios.cpp 107483 2025-01-08 09:17:26Z vboxsync $ */
2/** @file
3 * DevPcBios - PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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
641 uint64_t const cbMedia = pMedia->pfnGetSize(pMedia);
642 cLCHSCylinders = RT_MIN(1024, cbMedia / (512 * cLCHSHeads * cLCHSSectors));
643 if (cLCHSCylinders >= 1)
644 {
645 pLCHSGeometry->cCylinders = cLCHSCylinders;
646 pLCHSGeometry->cHeads = cLCHSHeads;
647 pLCHSGeometry->cSectors = cLCHSSectors;
648 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
649 return VINF_SUCCESS;
650 }
651 }
652 }
653 return VERR_INVALID_PARAMETER;
654}
655
656
657/* Several common PC/AT BIOS drive types. Corresponds to IBM BIOS drive tables. */
658PDMMEDIAGEOMETRY aGeomPCAT[] = {
659 /* cyls heads sectors */
660 { 0, 0, 0 }, /* Type 0 is not used */
661 { 306, 4, 17 }, /* Type 1, 10MB */
662 { 615, 4, 17 }, /* Type 2, 20MB */
663 { 615, 6, 17 }, /* Type 3, 30MB */
664 { 940, 8, 17 }, /* Type 4, 62MB */
665 { 940, 6, 17 }, /* Type 5, 47MB */
666 { 615, 4, 17 }, /* Type 6, 20MB (different WPCOMP from Type 2) */
667 { 462, 8, 17 }, /* Type 7, 30MB */
668 { 733, 5, 17 }, /* Type 8, 30MB */
669 { 900, 15, 17 }, /* Type 9, 117MB */
670
671 { 820, 3, 17 }, /* Type 10, 21MB */
672 { 855, 5, 17 }, /* Type 11, 37MB */
673 { 855, 7, 17 }, /* Type 12, 52MB */
674 { 306, 8, 17 }, /* Type 13, 21MB */
675 { 733, 7, 17 }, /* Type 14, 45MB */
676 { 0, 0, 0 }, /* Type 15 is not used */
677 { 612, 4, 17 }, /* Type 16, 21MB */
678 { 977, 5, 17 }, /* Type 17, 43MB */
679 { 977, 7, 17 }, /* Type 18, 60MB */
680 { 1024, 7, 17 }, /* Type 19, 62MB */
681
682 { 733, 5, 17 }, /* Type 20, 32MB */
683 { 733, 7, 17 }, /* Type 21, 45MB */
684 { 733, 5, 17 }, /* Type 22, 32MB */
685 { 306, 4, 17 }, /* Type 23, 10MB */
686};
687
688/**
689 * Attempts to initialize CMOS data for a hard disk matching one of
690 * the PC/AT BIOS types. Only applicable to the first two drives.
691 * Returns true if drive is one of the few recognized types.
692 */
693static bool pcbiosCmosTryPCATHardDisk(PPDMDEVINS pDevIns, int drive, PCPDMMEDIAGEOMETRY pLCHSGeometry)
694{
695 unsigned type;
696 unsigned typeLow;
697 bool fCompatGeom = false;
698
699 Assert((drive == 0) || (drive == 1));
700
701 /* See if drive geometry is one of the ancient PC/AT BIOS types. */
702 for (type = 0; type < RT_ELEMENTS(aGeomPCAT); ++type) {
703 if ( (aGeomPCAT[type].cCylinders == pLCHSGeometry->cCylinders)
704 && (aGeomPCAT[type].cHeads == pLCHSGeometry->cHeads )
705 && (aGeomPCAT[type].cSectors == pLCHSGeometry->cSectors )) {
706 LogRel(("PcBios: Recognized ATA hard disk %d as PC/AT BIOS type %d\n", drive, type));
707 fCompatGeom = true;
708 break;
709 }
710 }
711
712 if (fCompatGeom) {
713 uint32_t u32;
714
715 /* For types below 15, the type is in CMOS byte 0x12.
716 * NB: The type for drive 0 is in the high nibble, drive 1
717 * is in the low nibble.
718 * For drive types above 15, CMOS byte 0x12 is set to 15
719 * and actual drive type is in byte 0x19 (drive 0) or
720 * byte 0x1a (drive 1).
721 */
722 typeLow = type < 15 ? type : 15;
723
724 /* Always update CMOS byte 0x12. */
725 u32 = pcbiosCmosRead(pDevIns, 0x12);
726 u32 &= 0x0f << (4 * drive);
727 u32 |= typeLow << (4 - 4 * drive);
728 pcbiosCmosWrite(pDevIns, 0x12, u32);
729
730 /* For higher drive types, also update CMOS byte 0x19/0x1a. */
731 if (type > 15) {
732 u32 = type;
733 pcbiosCmosWrite(pDevIns, 0x19 + drive, u32);
734 }
735 }
736
737 return fCompatGeom;
738}
739
740
741/**
742 * Initializes the CMOS data for one harddisk.
743 */
744static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
745{
746 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
747 if (offType) {
748 uint32_t u32;
749 int drive;
750
751 Assert(offType == 0x1a || offType == 0x19);
752 drive = offType == 0x19 ? 0 : 1;
753
754 /* Update CMOS byte 12h. It will always be set to type 0fh for this disk. */
755 u32 = pcbiosCmosRead(pDevIns, 0x12);
756 u32 &= 0x0f << (4 * drive);
757 u32 |= 0x0f << (4 - 4 * drive);
758 pcbiosCmosWrite(pDevIns, 0x12, u32);
759
760 /* Now write the extended drive type at offset 19h or 1Ah. */
761 pcbiosCmosWrite(pDevIns, offType, 47);
762 }
763 /* Cylinders low */
764 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(pLCHSGeometry->cCylinders, 1024) & 0xff);
765 /* Cylinders high */
766 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(pLCHSGeometry->cCylinders, 1024) >> 8);
767 /* Heads */
768 pcbiosCmosWrite(pDevIns, offInfo + 2, pLCHSGeometry->cHeads);
769 /* Landing zone low */
770 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
771 /* Landing zone high */
772 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
773 /* Write precomp low */
774 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
775 /* Write precomp high */
776 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
777 /* Sectors */
778 pcbiosCmosWrite(pDevIns, offInfo + 7, pLCHSGeometry->cSectors);
779}
780
781
782/**
783 * Set logical CHS geometry for a hard disk
784 *
785 * @returns VBox status code.
786 * @param pBase Base interface for the device.
787 * @param pHardDisk The hard disk.
788 * @param pLCHSGeometry Where to store the geometry settings.
789 */
790static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIMEDIA pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
791{
792 RT_NOREF1(pBase);
793
794 PDMMEDIAGEOMETRY LCHSGeometry;
795 int rc = pHardDisk->pfnBiosGetLCHSGeometry(pHardDisk, &LCHSGeometry);
796 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
797 || LCHSGeometry.cCylinders == 0
798 || LCHSGeometry.cHeads == 0
799 || LCHSGeometry.cHeads > 255
800 || LCHSGeometry.cSectors == 0
801 || LCHSGeometry.cSectors > 63)
802 {
803 /* No LCHS geometry, autodetect and set. */
804 rc = biosGuessDiskLCHS(pHardDisk, &LCHSGeometry);
805 if (RT_FAILURE(rc))
806 {
807 /* Try if PCHS geometry works, otherwise fall back. */
808 rc = pHardDisk->pfnBiosGetPCHSGeometry(pHardDisk, &LCHSGeometry);
809 }
810 if ( RT_FAILURE(rc)
811 || LCHSGeometry.cCylinders == 0
812 || LCHSGeometry.cCylinders > 1024
813 || LCHSGeometry.cHeads == 0
814 || LCHSGeometry.cHeads > 255
815 || LCHSGeometry.cSectors == 0
816 || LCHSGeometry.cSectors > 63)
817 {
818 uint64_t cSectors = pHardDisk->pfnGetSize(pHardDisk) / 512;
819 if (cSectors / 16 / 63 <= 1024)
820 {
821 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
822 LCHSGeometry.cHeads = 16;
823 }
824 else if (cSectors / 32 / 63 <= 1024)
825 {
826 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
827 LCHSGeometry.cHeads = 32;
828 }
829 else if (cSectors / 64 / 63 <= 1024)
830 {
831 LCHSGeometry.cCylinders = cSectors / 64 / 63;
832 LCHSGeometry.cHeads = 64;
833 }
834 else if (cSectors / 128 / 63 <= 1024)
835 {
836 LCHSGeometry.cCylinders = cSectors / 128 / 63;
837 LCHSGeometry.cHeads = 128;
838 }
839 else
840 {
841 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
842 LCHSGeometry.cHeads = 255;
843 }
844 LCHSGeometry.cSectors = 63;
845
846 }
847 rc = pHardDisk->pfnBiosSetLCHSGeometry(pHardDisk, &LCHSGeometry);
848 if (rc == VERR_VD_IMAGE_READ_ONLY)
849 {
850 LogRel(("PcBios: ATA failed to update LCHS geometry, read only\n"));
851 rc = VINF_SUCCESS;
852 }
853 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
854 {
855 LogRel(("PcBios: ATA failed to update LCHS geometry, backend refused\n"));
856 rc = VINF_SUCCESS;
857 }
858 }
859
860 *pLCHSGeometry = LCHSGeometry;
861
862 return rc;
863}
864
865
866/**
867 * Get logical CHS geometry for a hard disk, intended for SCSI/SAS drives
868 * with no physical geometry.
869 *
870 * @returns VBox status code.
871 * @param pHardDisk The hard disk.
872 * @param pLCHSGeometry Where to store the geometry settings.
873 */
874static int getLogicalDiskGeometry(PPDMIMEDIA pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
875{
876 PDMMEDIAGEOMETRY LCHSGeometry;
877 int rc = VINF_SUCCESS;
878
879 rc = pHardDisk->pfnBiosGetLCHSGeometry(pHardDisk, &LCHSGeometry);
880 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
881 || LCHSGeometry.cCylinders == 0
882 || LCHSGeometry.cHeads == 0
883 || LCHSGeometry.cHeads > 255
884 || LCHSGeometry.cSectors == 0
885 || LCHSGeometry.cSectors > 63)
886 {
887 /* Unlike the ATA case, if the image does not provide valid logical
888 * geometry, we leave things alone and let the BIOS decide what the
889 * logical geometry should be.
890 */
891 rc = VERR_PDM_GEOMETRY_NOT_SET;
892 }
893 else
894 *pLCHSGeometry = LCHSGeometry;
895
896 return rc;
897}
898
899
900/**
901 * Get BIOS boot code from enmBootDevice in order
902 *
903 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
904 */
905static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
906{
907 switch (pThis->aenmBootDevice[iOrder])
908 {
909 case DEVPCBIOSBOOT_NONE:
910 return 0;
911 case DEVPCBIOSBOOT_FLOPPY:
912 return 1;
913 case DEVPCBIOSBOOT_HD:
914 return 2;
915 case DEVPCBIOSBOOT_DVD:
916 return 3;
917 case DEVPCBIOSBOOT_LAN:
918 return 4;
919 default:
920 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
921 return 0;
922 }
923}
924
925
926/**
927 * @interface_method_impl{PDMDEVREG,pfnInitComplete}
928 *
929 * This routine will write information needed by the bios to the CMOS.
930 *
931 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
932 * a description of standard and non-standard CMOS registers.
933 */
934static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
935{
936 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
937 uint32_t u32;
938 unsigned i;
939 PPDMIMEDIA apHDs[4] = {0};
940 LogFlow(("pcbiosInitComplete:\n"));
941
942 uint64_t const cbRamSize = PDMDevHlpMMPhysGetRamSize(pDevIns);
943 uint32_t const cbBelow4GB = PDMDevHlpMMPhysGetRamSizeBelow4GB(pDevIns);
944 uint64_t const cbAbove4GB = PDMDevHlpMMPhysGetRamSizeAbove4GB(pDevIns);
945
946 /*
947 * Memory sizes.
948 */
949 /* base memory. */
950 u32 = cbRamSize > 640 ? 640 : (uint32_t)cbRamSize / _1K; /* <-- this test is wrong, but it doesn't matter since we never assign less than 1MB */
951 pcbiosCmosWrite(pDevIns, 0x15, RT_BYTE1(u32)); /* 15h - Base Memory in K, Low Byte */
952 pcbiosCmosWrite(pDevIns, 0x16, RT_BYTE2(u32)); /* 16h - Base Memory in K, High Byte */
953
954 /* Extended memory, up to 65MB */
955 u32 = cbRamSize >= 65 * _1M ? 0xffff : ((uint32_t)cbRamSize - _1M) / _1K;
956 pcbiosCmosWrite(pDevIns, 0x17, RT_BYTE1(u32)); /* 17h - Extended Memory in K, Low Byte */
957 pcbiosCmosWrite(pDevIns, 0x18, RT_BYTE2(u32)); /* 18h - Extended Memory in K, High Byte */
958 pcbiosCmosWrite(pDevIns, 0x30, RT_BYTE1(u32)); /* 30h - Extended Memory in K, Low Byte */
959 pcbiosCmosWrite(pDevIns, 0x31, RT_BYTE2(u32)); /* 31h - Extended Memory in K, High Byte */
960
961 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
962 and below 4GB (as it can only hold 4GB-16M). We have to chop off the
963 top 32MB or it conflict with what the ACPI tables return. (Should these
964 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
965 with the high BIOS mapping.) */
966 if (cbRamSize > 16 * _1M)
967 u32 = (RT_MIN(cbBelow4GB, UINT32_C(0xfe000000)) - 16U * _1M) / _64K;
968 else
969 u32 = 0;
970 pcbiosCmosWrite(pDevIns, 0x34, RT_BYTE1(u32));
971 pcbiosCmosWrite(pDevIns, 0x35, RT_BYTE2(u32));
972
973 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
974 Bochs got these in a different location which we've already used for SATA,
975 it also lacks the last two. */
976 uint64_t c64KBAbove4GB = cbAbove4GB / _64K;
977 /* Make sure it doesn't hit the limits of the current BIOS code (RAM limit of ~255TB). */
978 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
979 pcbiosCmosWrite(pDevIns, 0x61, RT_BYTE1(c64KBAbove4GB));
980 pcbiosCmosWrite(pDevIns, 0x62, RT_BYTE2(c64KBAbove4GB));
981 pcbiosCmosWrite(pDevIns, 0x63, RT_BYTE3(c64KBAbove4GB));
982 pcbiosCmosWrite(pDevIns, 0x64, RT_BYTE4(c64KBAbove4GB));
983 pcbiosCmosWrite(pDevIns, 0x65, RT_BYTE5(c64KBAbove4GB));
984
985 /*
986 * Number of CPUs.
987 */
988 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
989
990 /*
991 * APIC mode.
992 */
993 pcbiosCmosWrite(pDevIns, 0x78, pThis->u8APICMode);
994
995 /*
996 * Bochs BIOS specifics - boot device.
997 * We do both new and old (ami-style) settings.
998 * See rombios.c line ~7215 (int19_function).
999 */
1000
1001 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
1002 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
1003 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
1004 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
1005 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
1006 pcbiosCmosWrite(pDevIns, 0x38, reg38);
1007 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
1008
1009 /*
1010 * PXE debug option.
1011 */
1012 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
1013
1014 /*
1015 * Network boot device list.
1016 */
1017 for (i = 0; i < NET_BOOT_DEVS; ++i)
1018 {
1019 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, RT_BYTE1(pThis->au16NetBootDev[i]));
1020 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, RT_BYTE2(pThis->au16NetBootDev[i]));
1021 }
1022
1023 /*
1024 * Floppy drive type.
1025 */
1026 uint32_t cFDs = 0;
1027 u32 = 0;
1028 for (i = 0; i < 2; i++)
1029 {
1030 PPDMIBASE pBase;
1031 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszFDDevice, 0, i, &pBase);
1032 if (RT_SUCCESS(rc))
1033 {
1034 PPDMIMEDIA pFD = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1035 if (pFD)
1036 {
1037 cFDs++;
1038 unsigned cShift = i == 0 ? 4 : 0;
1039 switch (pFD->pfnGetType(pFD))
1040 {
1041 case PDMMEDIATYPE_FLOPPY_360: u32 |= 1 << cShift; break;
1042 case PDMMEDIATYPE_FLOPPY_1_20: u32 |= 2 << cShift; break;
1043 case PDMMEDIATYPE_FLOPPY_720: u32 |= 3 << cShift; break;
1044 case PDMMEDIATYPE_FLOPPY_1_44: u32 |= 4 << cShift; break;
1045 case PDMMEDIATYPE_FLOPPY_2_88: u32 |= 5 << cShift; break;
1046 case PDMMEDIATYPE_FLOPPY_FAKE_15_6: u32 |= 14 << cShift; break;
1047 case PDMMEDIATYPE_FLOPPY_FAKE_63_5: u32 |= 15 << cShift; break;
1048 default: AssertFailed(); break;
1049 }
1050 }
1051 }
1052 }
1053 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
1054
1055 /*
1056 * Equipment byte.
1057 */
1058 if (cFDs > 0)
1059 u32 = ((cFDs - 1) << 6) | 0x01; /* floppy installed, additional drives. */
1060 else
1061 u32 = 0x00; /* floppy not installed. */
1062 u32 |= RT_BIT(1); /* math coprocessor installed */
1063 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
1064 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
1065 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
1066
1067 /*
1068 * IDE harddisks.
1069 */
1070 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1071 {
1072 PPDMIBASE pBase;
1073 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszHDDevice, 0, i, &pBase);
1074 if (RT_SUCCESS(rc))
1075 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1076 if ( apHDs[i]
1077 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
1078 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
1079 apHDs[i] = NULL;
1080 if (apHDs[i])
1081 {
1082 PDMMEDIAGEOMETRY LCHSGeometry;
1083 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
1084 AssertRC(rc2);
1085
1086 Assert(i < 4);
1087 /* Award BIOS extended drive types for first to fourth disk.
1088 * Used by the BIOS for setting the logical geometry. */
1089 int offType, offInfo;
1090 switch (i)
1091 {
1092 case 0:
1093 offType = 0x19;
1094 offInfo = 0x1e;
1095 break;
1096 case 1:
1097 offType = 0x1a;
1098 offInfo = 0x26;
1099 break;
1100 case 2:
1101 offType = 0x00;
1102 offInfo = 0x67;
1103 break;
1104 case 3:
1105 default:
1106 offType = 0x00;
1107 offInfo = 0x70;
1108 break;
1109 }
1110 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo, &LCHSGeometry);
1111 if (i < 2)
1112 pcbiosCmosTryPCATHardDisk(pDevIns, i, &LCHSGeometry);
1113
1114 LogRel(("PcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1115 }
1116 }
1117
1118 /*
1119 * SATA harddisks.
1120 */
1121 if (pThis->pszSataDevice)
1122 {
1123 /* Clear pointers to the block devices. */
1124 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1125 apHDs[i] = NULL;
1126
1127 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1128 {
1129 PPDMIBASE pBase;
1130 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
1131 if (RT_SUCCESS(rc))
1132 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1133 if ( apHDs[i]
1134 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
1135 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
1136 apHDs[i] = NULL;
1137 if (apHDs[i])
1138 {
1139 PDMMEDIAGEOMETRY LCHSGeometry;
1140 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
1141 AssertRC(rc);
1142
1143 Assert(i < 4);
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 (RT_SUCCESS(rc))
1196 {
1197 Assert(i < 4);
1198
1199 /* Extended drive information (for SCSI disks).
1200 * Used by the BIOS for setting the logical geometry, but
1201 * only if the image provided valid data.
1202 */
1203 int offInfo;
1204 switch (i)
1205 {
1206 case 0:
1207 offInfo = 0x90;
1208 break;
1209 case 1:
1210 offInfo = 0x98;
1211 break;
1212 case 2:
1213 offInfo = 0xa0;
1214 break;
1215 case 3:
1216 default:
1217 offInfo = 0xa8;
1218 break;
1219 }
1220 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo, &LCHSGeometry);
1221 LogRel(("PcBios: SCSI LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1222 }
1223 else
1224 LogRel(("PcBios: SCSI LUN#%d LCHS not provided\n", i));
1225 }
1226 }
1227 }
1228
1229 /* Calculate and store AT-style CMOS checksum. */
1230 uint16_t cksum = 0;
1231 for (i = 0x10; i < 0x2e; ++i)
1232 cksum += pcbiosCmosRead(pDevIns, i);
1233 pcbiosCmosWrite(pDevIns, 0x2e, RT_BYTE1(cksum));
1234 pcbiosCmosWrite(pDevIns, 0x2f, RT_BYTE2(cksum));
1235
1236 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
1237 return VINF_SUCCESS;
1238}
1239
1240
1241/**
1242 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1243 */
1244static DECLCALLBACK(void) pcbiosMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1245{
1246 RT_NOREF1(enmCtx);
1247 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1248 LogFlow(("pcbiosMemSetup:\n"));
1249
1250 if (pThis->u8IOAPIC)
1251 FwCommonPlantMpsFloatPtr(pDevIns, pThis->u32MPTableAddr);
1252
1253 /*
1254 * Re-shadow the LAN ROM image and make it RAM/RAM.
1255 *
1256 * This is normally done by the BIOS code, but since we're currently lacking
1257 * the chipset support for this we do it here (and in the constructor).
1258 */
1259 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, GUEST_PAGE_SIZE) >> GUEST_PAGE_SHIFT;
1260 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
1261 while (cPages > 0)
1262 {
1263 uint8_t abPage[GUEST_PAGE_SIZE];
1264 int rc;
1265
1266 /* Read the (original) ROM page and write it back to the RAM page. */
1267 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1268 AssertLogRelRC(rc);
1269
1270 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
1271 AssertLogRelRC(rc);
1272 if (RT_FAILURE(rc))
1273 memset(abPage, 0xcc, sizeof(abPage));
1274
1275 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
1276 AssertLogRelRC(rc);
1277
1278 /* Switch to the RAM/RAM mode. */
1279 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1280 AssertLogRelRC(rc);
1281
1282 /* Advance */
1283 GCPhys += GUEST_PAGE_SIZE;
1284 cPages--;
1285 }
1286}
1287
1288
1289/**
1290 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1291 */
1292static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1293{
1294 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1295 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1296 LogFlow(("pcbiosDestruct:\n"));
1297
1298 /*
1299 * Free MM heap pointers.
1300 */
1301 if (pThis->pu8PcBios)
1302 {
1303 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8PcBios);
1304 pThis->pu8PcBios = NULL;
1305 }
1306
1307 if (pThis->pszPcBiosFile)
1308 {
1309 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1310 pThis->pszPcBiosFile = NULL;
1311 }
1312
1313 if (pThis->pu8LanBoot)
1314 {
1315 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8LanBoot);
1316 pThis->pu8LanBoot = NULL;
1317 }
1318
1319 if (pThis->pszLanBootFile)
1320 {
1321 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1322 pThis->pszLanBootFile = NULL;
1323 }
1324
1325 if (pThis->pszHDDevice)
1326 {
1327 PDMDevHlpMMHeapFree(pDevIns, pThis->pszHDDevice);
1328 pThis->pszHDDevice = NULL;
1329 }
1330
1331 if (pThis->pszFDDevice)
1332 {
1333 PDMDevHlpMMHeapFree(pDevIns, pThis->pszFDDevice);
1334 pThis->pszFDDevice = NULL;
1335 }
1336
1337 if (pThis->pszSataDevice)
1338 {
1339 PDMDevHlpMMHeapFree(pDevIns, pThis->pszSataDevice);
1340 pThis->pszSataDevice = NULL;
1341 }
1342
1343 if (pThis->pszScsiDevice)
1344 {
1345 PDMDevHlpMMHeapFree(pDevIns, pThis->pszScsiDevice);
1346 pThis->pszScsiDevice = NULL;
1347 }
1348
1349 return VINF_SUCCESS;
1350}
1351
1352
1353/**
1354 * Convert config value to DEVPCBIOSBOOT.
1355 *
1356 * @returns VBox status code.
1357 * @param pDevIns Device instance data.
1358 * @param pCfg Configuration handle.
1359 * @param pszParam The name of the value to read.
1360 * @param penmBoot Where to store the boot method.
1361 */
1362static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1363{
1364 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1365
1366 char szBuf[64];
1367 int rc = pHlp->pfnCFGMQueryString(pCfg, pszParam, szBuf, sizeof(szBuf));
1368 if (RT_FAILURE(rc))
1369 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1370 N_("Configuration error: Querying \"%s\" as a string failed"), pszParam);
1371
1372 if (!strcmp(szBuf, "DVD") || !strcmp(szBuf, "CDROM"))
1373 *penmBoot = DEVPCBIOSBOOT_DVD;
1374 else if (!strcmp(szBuf, "IDE"))
1375 *penmBoot = DEVPCBIOSBOOT_HD;
1376 else if (!strcmp(szBuf, "FLOPPY"))
1377 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1378 else if (!strcmp(szBuf, "LAN"))
1379 *penmBoot = DEVPCBIOSBOOT_LAN;
1380 else if (!strcmp(szBuf, "NONE"))
1381 *penmBoot = DEVPCBIOSBOOT_NONE;
1382 else
1383 rc = PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1384 N_("Configuration error: The \"%s\" value \"%s\" is unknown"), pszParam, szBuf);
1385 return rc;
1386}
1387
1388
1389/**
1390 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1391 */
1392static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1393{
1394 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1395 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1396 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1397 int rc;
1398 int cb;
1399 Assert(iInstance == 0); RT_NOREF(iInstance);
1400
1401 /*
1402 * Validate configuration.
1403 */
1404 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
1405 "BootDevice0"
1406 "|BootDevice1"
1407 "|BootDevice2"
1408 "|BootDevice3"
1409 "|HardDiskDevice"
1410 "|SataHardDiskDevice"
1411 "|SataLUN1"
1412 "|SataLUN2"
1413 "|SataLUN3"
1414 "|SataLUN4"
1415 "|ScsiHardDiskDevice"
1416 "|ScsiLUN1"
1417 "|ScsiLUN2"
1418 "|ScsiLUN3"
1419 "|ScsiLUN4"
1420 "|FloppyDevice"
1421 "|DelayBoot"
1422 "|BiosRom"
1423 "|LanBootRom"
1424 "|PXEDebug"
1425 "|UUID"
1426 "|UuidLe"
1427 "|IOAPIC"
1428 "|APIC"
1429 "|NumCPUs"
1430 "|McfgBase"
1431 "|McfgLength"
1432 "|DmiBIOSFirmwareMajor"
1433 "|DmiBIOSFirmwareMinor"
1434 "|DmiBIOSReleaseDate"
1435 "|DmiBIOSReleaseMajor"
1436 "|DmiBIOSReleaseMinor"
1437 "|DmiBIOSVendor"
1438 "|DmiBIOSVersion"
1439 "|DmiSystemFamily"
1440 "|DmiSystemProduct"
1441 "|DmiSystemSerial"
1442 "|DmiSystemSKU"
1443 "|DmiSystemUuid"
1444 "|DmiSystemVendor"
1445 "|DmiSystemVersion"
1446 "|DmiBoardAssetTag"
1447 "|DmiBoardBoardType"
1448 "|DmiBoardLocInChass"
1449 "|DmiBoardProduct"
1450 "|DmiBoardSerial"
1451 "|DmiBoardVendor"
1452 "|DmiBoardVersion"
1453 "|DmiChassisAssetTag"
1454 "|DmiChassisSerial"
1455 "|DmiChassisType"
1456 "|DmiChassisVendor"
1457 "|DmiChassisVersion"
1458 "|DmiProcManufacturer"
1459 "|DmiProcVersion"
1460 "|DmiOEMVBoxVer"
1461 "|DmiOEMVBoxRev"
1462 "|DmiUseHostInfo"
1463 "|DmiExposeMemoryTable"
1464 "|DmiExposeProcInf"
1465 "|CheckShutdownStatusForSoftReset"
1466 "|ClearShutdownStatusOnHardReset"
1467 ,
1468 "NetBoot");
1469 /*
1470 * Init the data.
1471 */
1472 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1473 if (RT_FAILURE(rc))
1474 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1475
1476 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThis->u64McfgBase, 0);
1477 if (RT_FAILURE(rc))
1478 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"\" as integer failed"));
1479 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1480 if (RT_FAILURE(rc))
1481 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1482
1483
1484 LogRel(("PcBios: [SMP] BIOS with %u CPUs\n", pThis->cCpus));
1485
1486 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1487 if (RT_FAILURE (rc))
1488 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"IOAPIC\""));
1489
1490 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThis->u8APICMode, 1);
1491 if (RT_FAILURE (rc))
1492 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"APIC\""));
1493
1494 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1495 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1496 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1497 {
1498 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1499 if (RT_FAILURE(rc))
1500 return rc;
1501 }
1502
1503 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1504 if (RT_FAILURE(rc))
1505 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1506
1507 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1508 if (RT_FAILURE(rc))
1509 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1510
1511 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1512 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1513 pThis->pszSataDevice = NULL;
1514 else if (RT_FAILURE(rc))
1515 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1516
1517 if (pThis->pszSataDevice)
1518 {
1519 static const char * const s_apszSataDisks[] = { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
1520 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1521 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1522 {
1523 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1524 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1525 pThis->iSataHDLUN[i] = i;
1526 else if (RT_FAILURE(rc))
1527 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1528 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1529 }
1530 }
1531
1532 /* Repeat the exercise for SCSI drives. */
1533 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "ScsiHardDiskDevice", &pThis->pszScsiDevice);
1534 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1535 pThis->pszScsiDevice = NULL;
1536 else if (RT_FAILURE(rc))
1537 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ScsiHardDiskDevice\" as a string failed"));
1538
1539 if (pThis->pszScsiDevice)
1540 {
1541 static const char * const s_apszScsiDisks[] = { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
1542 Assert(RT_ELEMENTS(s_apszScsiDisks) == RT_ELEMENTS(pThis->iScsiHDLUN));
1543 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iScsiHDLUN); i++)
1544 {
1545 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszScsiDisks[i], &pThis->iScsiHDLUN[i]);
1546 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1547 pThis->iScsiHDLUN[i] = i;
1548 else if (RT_FAILURE(rc))
1549 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1550 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszScsiDisks);
1551 }
1552 }
1553
1554 /* PXE debug logging option. */
1555 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1556 if (RT_FAILURE(rc))
1557 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1558
1559
1560 /*
1561 * Register the I/O Ports.
1562 */
1563 IOMIOPORTHANDLE hIoPorts;
1564 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x400 /*uPort*/, 4 /*cPorts*/, pcbiosIOPortDebugWrite, pcbiosIOPortDebugRead,
1565 "Bochs PC BIOS - Panic & Debug", NULL, &hIoPorts);
1566 AssertRCReturn(rc, rc);
1567
1568 rc = PDMDevHlpIoPortCreateIsa(pDevIns, 1 /*cPorts*/, pcbiosIOPortControlWrite, pcbiosIOPortControlRead, NULL /*pvUser*/,
1569 "PC BIOS - Control", NULL /*paExtDescs*/, &pThis->hIoPortControl);
1570 AssertRCReturn(rc, rc);
1571 rc = pcbiosRegisterControl(pDevIns, pThis, true /* fNewControlPort */);
1572 AssertRCReturn(rc, rc);
1573
1574 /*
1575 * Register SSM handlers, for remembering which shutdown port to use.
1576 */
1577 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCBIOS_SSM_VERSION, 1 /* cbGuess */, NULL,
1578 NULL, NULL, NULL,
1579 NULL, pcbiosSaveExec, NULL,
1580 pcbiosLoadPrep, pcbiosLoadExec, pcbiosLoadDone);
1581 AssertRCReturn(rc, rc);
1582
1583 /* Clear the net boot device list. All bits set invokes old behavior,
1584 * as if no second CMOS bank was present.
1585 */
1586 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1587
1588 /*
1589 * Determine the network boot order.
1590 */
1591 PCFGMNODE pCfgNetBoot = pHlp->pfnCFGMGetChild(pCfg, "NetBoot");
1592 if (pCfgNetBoot == NULL)
1593 {
1594 /* Do nothing. */
1595 rc = VINF_SUCCESS;
1596 }
1597 else
1598 {
1599 PCFGMNODE pCfgNetBootDevice;
1600 uint8_t u8PciBus;
1601 uint8_t u8PciDev;
1602 uint8_t u8PciFn;
1603 uint16_t u16BusDevFn;
1604 char szIndex[] = "?";
1605
1606 Assert(pCfgNetBoot);
1607 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1608 {
1609 szIndex[0] = '0' + i;
1610 pCfgNetBootDevice = pHlp->pfnCFGMGetChild(pCfgNetBoot, szIndex);
1611
1612 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1613 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1614 {
1615 /* Do nothing and stop iterating. */
1616 rc = VINF_SUCCESS;
1617 break;
1618 }
1619 else if (RT_FAILURE(rc))
1620 return PDMDEV_SET_ERROR(pDevIns, rc,
1621 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1622 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1623 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1624 {
1625 /* Do nothing and stop iterating. */
1626 rc = VINF_SUCCESS;
1627 break;
1628 }
1629 else if (RT_FAILURE(rc))
1630 return PDMDEV_SET_ERROR(pDevIns, rc,
1631 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1632 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1633 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1634 {
1635 /* Do nothing and stop iterating. */
1636 rc = VINF_SUCCESS;
1637 break;
1638 }
1639 else if (RT_FAILURE(rc))
1640 return PDMDEV_SET_ERROR(pDevIns, rc,
1641 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1642 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1643 pThis->au16NetBootDev[i] = u16BusDevFn;
1644 }
1645 }
1646
1647 /*
1648 * Get the system BIOS ROM file name.
1649 */
1650 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1651 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1652 {
1653 pThis->pszPcBiosFile = NULL;
1654 rc = VINF_SUCCESS;
1655 }
1656 else if (RT_FAILURE(rc))
1657 return PDMDEV_SET_ERROR(pDevIns, rc,
1658 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1659 else if (!*pThis->pszPcBiosFile)
1660 {
1661 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1662 pThis->pszPcBiosFile = NULL;
1663 }
1664
1665 /*
1666 * Get the CPU arch so we can load the appropriate ROMs.
1667 */
1668 CPUMMICROARCH const enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
1669
1670 if (pThis->pszPcBiosFile)
1671 {
1672 /*
1673 * Load the BIOS ROM.
1674 */
1675 RTFILE hFilePcBios;
1676 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1677 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1678 if (RT_SUCCESS(rc))
1679 {
1680 /* Figure the size and check restrictions. */
1681 uint64_t cbPcBios;
1682 rc = RTFileQuerySize(hFilePcBios, &cbPcBios);
1683 if (RT_SUCCESS(rc))
1684 {
1685 pThis->cbPcBios = (uint32_t)cbPcBios;
1686 if ( RT_ALIGN(pThis->cbPcBios, _64K) == pThis->cbPcBios
1687 && pThis->cbPcBios == cbPcBios
1688 && pThis->cbPcBios <= 32 * _64K
1689 && pThis->cbPcBios >= _64K)
1690 {
1691 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1692 if (pThis->pu8PcBios)
1693 {
1694 rc = RTFileRead(hFilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1695 if (RT_FAILURE(rc))
1696 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1697 N_("Error reading the BIOS image ('%s)"), pThis->pszPcBiosFile);
1698 }
1699 else
1700 rc = PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1701 N_("Failed to allocate %#x bytes for loading the BIOS image"),
1702 pThis->cbPcBios);
1703 }
1704 else
1705 rc = PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
1706 N_("Invalid system BIOS file size ('%s'): %#llx (%llu)"),
1707 pThis->pszPcBiosFile, cbPcBios, cbPcBios);
1708 }
1709 else
1710 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1711 N_("Failed to query the system BIOS file size ('%s')"),
1712 pThis->pszPcBiosFile);
1713 RTFileClose(hFilePcBios);
1714 }
1715 else
1716 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1717 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1718 if (RT_FAILURE(rc))
1719 return rc;
1720
1721 LogRel(("PcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1722 }
1723 else
1724 {
1725 /*
1726 * Use one of the embedded BIOS ROM images.
1727 */
1728 uint8_t const *pbBios;
1729 uint32_t cbBios;
1730 if ( enmMicroarch == kCpumMicroarch_Intel_8086
1731 || enmMicroarch == kCpumMicroarch_Intel_80186
1732 || enmMicroarch == kCpumMicroarch_NEC_V20
1733 || enmMicroarch == kCpumMicroarch_NEC_V30)
1734 {
1735 pbBios = g_abPcBiosBinary8086;
1736 cbBios = g_cbPcBiosBinary8086;
1737 LogRel(("PcBios: Using the 8086 BIOS image!\n"));
1738 }
1739 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
1740 {
1741 pbBios = g_abPcBiosBinary286;
1742 cbBios = g_cbPcBiosBinary286;
1743 LogRel(("PcBios: Using the 286 BIOS image!\n"));
1744 }
1745 else
1746 {
1747 pbBios = g_abPcBiosBinary386;
1748 cbBios = g_cbPcBiosBinary386;
1749 LogRel(("PcBios: Using the 386+ BIOS image.\n"));
1750 }
1751 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbBios);
1752 if (pThis->pu8PcBios)
1753 {
1754 pThis->cbPcBios = cbBios;
1755 memcpy(pThis->pu8PcBios, pbBios, cbBios);
1756 }
1757 else
1758 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1759 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"), cbBios);
1760 }
1761 const uint8_t *pu8PcBiosBinary = pThis->pu8PcBios;
1762 uint32_t cbPcBiosBinary = pThis->cbPcBios;
1763
1764 /*
1765 * Query the machine's UUID for SMBIOS/DMI use.
1766 */
1767 RTUUID uuid;
1768 rc = pHlp->pfnCFGMQueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1769 if (RT_FAILURE(rc))
1770 return PDMDEV_SET_ERROR(pDevIns, rc,
1771 N_("Configuration error: Querying \"UUID\" failed"));
1772
1773 bool fUuidLe;
1774 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UuidLe", &fUuidLe, false);
1775 if (RT_FAILURE(rc))
1776 return PDMDEV_SET_ERROR(pDevIns, rc,
1777 N_("Configuration error: Querying \"UuidLe\" failed"));
1778
1779 if (!fUuidLe)
1780 {
1781 /*
1782 * UUIDs are stored little endian actually (see chapter 7.2.1 System — UUID
1783 * of the DMI/SMBIOS spec) but to not force reactivation of existing guests we have
1784 * to carry this bug along... (see also DevEFI.cpp when changing this)
1785 *
1786 * Convert the UUID to network byte order. Not entirely straightforward as
1787 * parts are MSB already...
1788 */
1789 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1790 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1791 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1792 }
1793
1794 uint16_t cbDmiTables = 0;
1795 uint16_t cDmiTables = 0;
1796 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1797 &uuid, pCfg, pThis->cCpus, &cbDmiTables, &cDmiTables,
1798 false /*fUefi*/);
1799 if (RT_FAILURE(rc))
1800 return rc;
1801
1802 /* Look for _SM_/_DMI_ anchor strings within the BIOS and replace the table headers. */
1803 unsigned offAnchor = ~0U;
1804 unsigned const cbToSearch = pThis->cbPcBios - 32;
1805 for (unsigned off = 0; off <= cbToSearch; off += 16)
1806 {
1807 if ( pThis->pu8PcBios[off + 0x00] != '_'
1808 || pThis->pu8PcBios[off + 0x01] != 'S'
1809 || pThis->pu8PcBios[off + 0x02] != 'M'
1810 || pThis->pu8PcBios[off + 0x03] != '_'
1811 || pThis->pu8PcBios[off + 0x10] != '_'
1812 || pThis->pu8PcBios[off + 0x11] != 'D'
1813 || pThis->pu8PcBios[off + 0x12] != 'M'
1814 || pThis->pu8PcBios[off + 0x13] != 'I'
1815 || pThis->pu8PcBios[off + 0x14] != '_')
1816 { /* likely */ }
1817 else
1818 {
1819 offAnchor = off;
1820 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->pu8PcBios + off, cbDmiTables, cDmiTables);
1821 break;
1822 }
1823 }
1824 AssertLogRel(offAnchor <= cbToSearch);
1825
1826 if (pThis->u8IOAPIC)
1827 {
1828 pThis->u32MPTableAddr = VBOX_DMI_TABLE_BASE + VBOX_DMI_TABLE_SIZE;
1829 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage /* aka VBOX_DMI_TABLE_BASE */ + VBOX_DMI_TABLE_SIZE,
1830 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1831 LogRel(("PcBios: MPS table at %08x\n", pThis->u32MPTableAddr));
1832 }
1833
1834 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
1835 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1836 if (RT_FAILURE(rc))
1837 return rc;
1838
1839 /*
1840 * Map the BIOS into memory.
1841 * There are two mappings:
1842 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1843 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1844 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1845 */
1846 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1847 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1848 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1849 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1850 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb], cb,
1851 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xfffff");
1852 if (RT_FAILURE(rc))
1853 return rc;
1854 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary, cbPcBiosBinary,
1855 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xffffffff");
1856 if (RT_FAILURE(rc))
1857 return rc;
1858
1859 /*
1860 * Get the LAN boot ROM file name.
1861 */
1862 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1863 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1864 {
1865 pThis->pszLanBootFile = NULL;
1866 rc = VINF_SUCCESS;
1867 }
1868 else if (RT_FAILURE(rc))
1869 return PDMDEV_SET_ERROR(pDevIns, rc,
1870 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1871 else if (!*pThis->pszLanBootFile)
1872 {
1873 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1874 pThis->pszLanBootFile = NULL;
1875 }
1876
1877 /*
1878 * Not loading LAN ROM for old CPUs.
1879 */
1880 if ( enmMicroarch != kCpumMicroarch_Intel_8086
1881 && enmMicroarch != kCpumMicroarch_Intel_80186
1882 && enmMicroarch != kCpumMicroarch_NEC_V20
1883 && enmMicroarch != kCpumMicroarch_NEC_V30
1884 && enmMicroarch != kCpumMicroarch_Intel_80286)
1885 {
1886 const uint8_t *pu8LanBootBinary = NULL;
1887 uint64_t cbLanBootBinary;
1888 uint64_t cbFileLanBoot = 0;
1889
1890 /*
1891 * Open the LAN boot ROM and figure it size.
1892 * Determine the LAN boot ROM size, open specified ROM file in the process.
1893 */
1894 if (pThis->pszLanBootFile)
1895 {
1896 RTFILE hFileLanBoot = NIL_RTFILE;
1897 rc = RTFileOpen(&hFileLanBoot, pThis->pszLanBootFile,
1898 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1899 if (RT_SUCCESS(rc))
1900 {
1901 rc = RTFileQuerySize(hFileLanBoot, &cbFileLanBoot);
1902 if (RT_SUCCESS(rc))
1903 {
1904 if (cbFileLanBoot <= _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff))
1905 {
1906 LogRel(("PcBios: Using LAN ROM '%s' with a size of %#x bytes\n", pThis->pszLanBootFile, cbFileLanBoot));
1907
1908 /*
1909 * Allocate buffer for the LAN boot ROM data and load it.
1910 */
1911 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, cbFileLanBoot);
1912 if (pThis->pu8LanBoot)
1913 {
1914 rc = RTFileRead(hFileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1915 AssertLogRelRCReturnStmt(rc, RTFileClose(hFileLanBoot), rc);
1916 }
1917 else
1918 rc = VERR_NO_MEMORY;
1919 }
1920 else
1921 rc = VERR_TOO_MUCH_DATA;
1922 }
1923 RTFileClose(hFileLanBoot);
1924 }
1925 if (RT_FAILURE(rc))
1926 {
1927 /*
1928 * Play stupid and ignore failures, falling back to the built-in LAN boot ROM.
1929 */
1930 /** @todo r=bird: This should have some kind of rational. We don't usually
1931 * ignore the VM configuration. */
1932 LogRel(("PcBios: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1933 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1934 pThis->pszLanBootFile = NULL;
1935 }
1936 }
1937
1938 /* If we were unable to get the data from file for whatever reason, fall
1939 * back to the built-in LAN boot ROM image.
1940 */
1941 if (pThis->pu8LanBoot == NULL)
1942 {
1943#ifdef VBOX_WITH_PXE_ROM
1944 pu8LanBootBinary = g_abNetBiosBinary;
1945 cbLanBootBinary = g_cbNetBiosBinary;
1946#endif
1947 }
1948 else
1949 {
1950 pu8LanBootBinary = pThis->pu8LanBoot;
1951 cbLanBootBinary = cbFileLanBoot;
1952 }
1953
1954 /*
1955 * Map the Network Boot ROM into memory.
1956 *
1957 * Currently there is a fixed mapping: 0x000e2000 to 0x000effff contains
1958 * the (up to) 56 kb ROM image. The mapping size is fixed to trouble with
1959 * the saved state (in PGM).
1960 */
1961 if (pu8LanBootBinary)
1962 {
1963 pThis->cbLanBoot = cbLanBootBinary;
1964
1965 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4,
1966 RT_MAX(cbLanBootBinary, _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff)),
1967 pu8LanBootBinary, cbLanBootBinary,
1968 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1969 AssertRCReturn(rc, rc);
1970 }
1971 }
1972 else if (pThis->pszLanBootFile)
1973 LogRel(("PcBios: Skipping LAN ROM '%s' due to ancient target CPU.\n", pThis->pszLanBootFile));
1974#ifdef VBOX_WITH_PXE_ROM
1975 else
1976 LogRel(("PcBios: Skipping built in ROM due to ancient target CPU.\n"));
1977#endif
1978
1979 /*
1980 * Configure Boot delay.
1981 */
1982 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1983 if (RT_FAILURE(rc))
1984 return PDMDEV_SET_ERROR(pDevIns, rc,
1985 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1986 if (pThis->uBootDelay > 15)
1987 pThis->uBootDelay = 15;
1988
1989
1990 /*
1991 * Read shutdown status code config and register ourselves as the firmware device.
1992 */
1993
1994 /** @cfgm{CheckShutdownStatusForSoftReset, boolean, true}
1995 * Whether to consult the shutdown status code (CMOS register 0Fh) to
1996 * determine whether the guest intended a soft or hard reset. Currently only
1997 * shutdown status codes 05h, 09h and 0Ah are considered soft reset. */
1998 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CheckShutdownStatusForSoftReset", &pThis->fCheckShutdownStatusForSoftReset, true);
1999 AssertLogRelRCReturn(rc, rc);
2000
2001 /** @cfgm{ClearShutdownStatusOnHardReset, boolean, true}
2002 * Whether to clear the shutdown status code (CMOS register 0Fh) on hard reset. */
2003 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ClearShutdownStatusOnHardReset", &pThis->fClearShutdownStatusOnHardReset, true);
2004 AssertLogRelRCReturn(rc, rc);
2005
2006 LogRel(("PcBios: fCheckShutdownStatusForSoftReset=%RTbool fClearShutdownStatusOnHardReset=%RTbool\n",
2007 pThis->fCheckShutdownStatusForSoftReset, pThis->fClearShutdownStatusOnHardReset));
2008
2009 static PDMFWREG const s_FwReg = { PDM_FWREG_VERSION, pcbiosFw_IsHardReset, PDM_FWREG_VERSION };
2010 rc = PDMDevHlpFirmwareRegister(pDevIns, &s_FwReg, &pThis->pFwHlpR3);
2011 AssertLogRelRCReturn(rc, rc);
2012
2013 return VINF_SUCCESS;
2014}
2015
2016
2017/**
2018 * The device registration structure.
2019 */
2020const PDMDEVREG g_DevicePcBios =
2021{
2022 /* .u32Version = */ PDM_DEVREG_VERSION,
2023 /* .uReserved0 = */ 0,
2024 /* .szName = */ "pcbios",
2025 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
2026 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
2027 /* .cMaxInstances = */ 1,
2028 /* .uSharedVersion = */ 42,
2029 /* .cbInstanceShared = */ sizeof(DEVPCBIOS),
2030 /* .cbInstanceCC = */ 0,
2031 /* .cbInstanceRC = */ 0,
2032 /* .cMaxPciDevices = */ 0,
2033 /* .cMaxMsixVectors = */ 0,
2034 /* .pszDescription = */ "PC BIOS Device",
2035#if defined(IN_RING3)
2036 /* .pszRCMod = */ "",
2037 /* .pszR0Mod = */ "",
2038 /* .pfnConstruct = */ pcbiosConstruct,
2039 /* .pfnDestruct = */ pcbiosDestruct,
2040 /* .pfnRelocate = */ NULL,
2041 /* .pfnMemSetup = */ pcbiosMemSetup,
2042 /* .pfnPowerOn = */ NULL,
2043 /* .pfnReset = */ pcbiosReset,
2044 /* .pfnSuspend = */ NULL,
2045 /* .pfnResume = */ NULL,
2046 /* .pfnAttach = */ NULL,
2047 /* .pfnDetach = */ NULL,
2048 /* .pfnQueryInterface = */ NULL,
2049 /* .pfnInitComplete = */ pcbiosInitComplete,
2050 /* .pfnPowerOff = */ NULL,
2051 /* .pfnSoftReset = */ NULL,
2052 /* .pfnReserved0 = */ NULL,
2053 /* .pfnReserved1 = */ NULL,
2054 /* .pfnReserved2 = */ NULL,
2055 /* .pfnReserved3 = */ NULL,
2056 /* .pfnReserved4 = */ NULL,
2057 /* .pfnReserved5 = */ NULL,
2058 /* .pfnReserved6 = */ NULL,
2059 /* .pfnReserved7 = */ NULL,
2060#elif defined(IN_RING0)
2061 /* .pfnEarlyConstruct = */ NULL,
2062 /* .pfnConstruct = */ NULL,
2063 /* .pfnDestruct = */ NULL,
2064 /* .pfnFinalDestruct = */ NULL,
2065 /* .pfnRequest = */ NULL,
2066 /* .pfnReserved0 = */ NULL,
2067 /* .pfnReserved1 = */ NULL,
2068 /* .pfnReserved2 = */ NULL,
2069 /* .pfnReserved3 = */ NULL,
2070 /* .pfnReserved4 = */ NULL,
2071 /* .pfnReserved5 = */ NULL,
2072 /* .pfnReserved6 = */ NULL,
2073 /* .pfnReserved7 = */ NULL,
2074#elif defined(IN_RC)
2075 /* .pfnConstruct = */ NULL,
2076 /* .pfnReserved0 = */ NULL,
2077 /* .pfnReserved1 = */ NULL,
2078 /* .pfnReserved2 = */ NULL,
2079 /* .pfnReserved3 = */ NULL,
2080 /* .pfnReserved4 = */ NULL,
2081 /* .pfnReserved5 = */ NULL,
2082 /* .pfnReserved6 = */ NULL,
2083 /* .pfnReserved7 = */ NULL,
2084#else
2085# error "Not in IN_RING3, IN_RING0 or IN_RC!"
2086#endif
2087 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
2088};
2089
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