VirtualBox

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

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

Warning fix.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use