VirtualBox

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

Last change on this file since 91881 was 91566, checked in by vboxsync, 3 years ago

Device/DevPcBios: Interim solution for signalling boot failure through the API (as a runtime error). Needs to be complemented by similar code in DevEFI.

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