VirtualBox

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

Last change on this file since 90447 was 84752, checked in by vboxsync, 4 years ago

BIOS: On boot failure, call into INT 18h and report that to the host through the shutdown interface (see bugref:6549).

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