VirtualBox

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

Last change on this file since 94800 was 94309, checked in by vboxsync, 3 years ago

Device/DevPcBios: Fix long standing issue which resulted in rejecting the detected LCHS geometry when the head count was above 16.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 69.2 KB
Line 
1/* $Id: DevPcBios.cpp 94309 2022-03-18 21:23:01Z vboxsync $ */
2/** @file
3 * DevPcBios - PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 > 255
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 PPDMIMEDIA apHDs[4] = {0};
772 LogFlow(("pcbiosInitComplete:\n"));
773
774 uint64_t const cbRamSize = PDMDevHlpMMPhysGetRamSize(pDevIns);
775 uint32_t const cbBelow4GB = PDMDevHlpMMPhysGetRamSizeBelow4GB(pDevIns);
776 uint64_t const cbAbove4GB = PDMDevHlpMMPhysGetRamSizeAbove4GB(pDevIns);
777
778 /*
779 * Memory sizes.
780 */
781 /* base memory. */
782 u32 = cbRamSize > 640 ? 640 : (uint32_t)cbRamSize / _1K; /* <-- this test is wrong, but it doesn't matter since we never assign less than 1MB */
783 pcbiosCmosWrite(pDevIns, 0x15, RT_BYTE1(u32)); /* 15h - Base Memory in K, Low Byte */
784 pcbiosCmosWrite(pDevIns, 0x16, RT_BYTE2(u32)); /* 16h - Base Memory in K, High Byte */
785
786 /* Extended memory, up to 65MB */
787 u32 = cbRamSize >= 65 * _1M ? 0xffff : ((uint32_t)cbRamSize - _1M) / _1K;
788 pcbiosCmosWrite(pDevIns, 0x17, RT_BYTE1(u32)); /* 17h - Extended Memory in K, Low Byte */
789 pcbiosCmosWrite(pDevIns, 0x18, RT_BYTE2(u32)); /* 18h - Extended Memory in K, High Byte */
790 pcbiosCmosWrite(pDevIns, 0x30, RT_BYTE1(u32)); /* 30h - Extended Memory in K, Low Byte */
791 pcbiosCmosWrite(pDevIns, 0x31, RT_BYTE2(u32)); /* 31h - Extended Memory in K, High Byte */
792
793 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
794 and below 4GB (as it can only hold 4GB-16M). We have to chop off the
795 top 32MB or it conflict with what the ACPI tables return. (Should these
796 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
797 with the high BIOS mapping.) */
798 if (cbRamSize > 16 * _1M)
799 u32 = (RT_MIN(cbBelow4GB, UINT32_C(0xfe000000)) - 16U * _1M) / _64K;
800 else
801 u32 = 0;
802 pcbiosCmosWrite(pDevIns, 0x34, RT_BYTE1(u32));
803 pcbiosCmosWrite(pDevIns, 0x35, RT_BYTE2(u32));
804
805 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
806 Bochs got these in a different location which we've already used for SATA,
807 it also lacks the last two. */
808 uint64_t c64KBAbove4GB = cbAbove4GB / _64K;
809 /* Make sure it doesn't hit the limits of the current BIOS code (RAM limit of ~255TB). */
810 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
811 pcbiosCmosWrite(pDevIns, 0x61, RT_BYTE1(c64KBAbove4GB));
812 pcbiosCmosWrite(pDevIns, 0x62, RT_BYTE2(c64KBAbove4GB));
813 pcbiosCmosWrite(pDevIns, 0x63, RT_BYTE3(c64KBAbove4GB));
814 pcbiosCmosWrite(pDevIns, 0x64, RT_BYTE4(c64KBAbove4GB));
815 pcbiosCmosWrite(pDevIns, 0x65, RT_BYTE5(c64KBAbove4GB));
816
817 /*
818 * Number of CPUs.
819 */
820 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
821
822 /*
823 * APIC mode.
824 */
825 pcbiosCmosWrite(pDevIns, 0x78, pThis->u8APICMode);
826
827 /*
828 * Bochs BIOS specifics - boot device.
829 * We do both new and old (ami-style) settings.
830 * See rombios.c line ~7215 (int19_function).
831 */
832
833 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
834 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
835 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
836 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
837 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
838 pcbiosCmosWrite(pDevIns, 0x38, reg38);
839 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
840
841 /*
842 * PXE debug option.
843 */
844 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
845
846 /*
847 * Network boot device list.
848 */
849 for (i = 0; i < NET_BOOT_DEVS; ++i)
850 {
851 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, RT_BYTE1(pThis->au16NetBootDev[i]));
852 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, RT_BYTE2(pThis->au16NetBootDev[i]));
853 }
854
855 /*
856 * Floppy drive type.
857 */
858 uint32_t cFDs = 0;
859 u32 = 0;
860 for (i = 0; i < 2; i++)
861 {
862 PPDMIBASE pBase;
863 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszFDDevice, 0, i, &pBase);
864 if (RT_SUCCESS(rc))
865 {
866 PPDMIMEDIA pFD = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
867 if (pFD)
868 {
869 cFDs++;
870 unsigned cShift = i == 0 ? 4 : 0;
871 switch (pFD->pfnGetType(pFD))
872 {
873 case PDMMEDIATYPE_FLOPPY_360: u32 |= 1 << cShift; break;
874 case PDMMEDIATYPE_FLOPPY_1_20: u32 |= 2 << cShift; break;
875 case PDMMEDIATYPE_FLOPPY_720: u32 |= 3 << cShift; break;
876 case PDMMEDIATYPE_FLOPPY_1_44: u32 |= 4 << cShift; break;
877 case PDMMEDIATYPE_FLOPPY_2_88: u32 |= 5 << cShift; break;
878 case PDMMEDIATYPE_FLOPPY_FAKE_15_6: u32 |= 14 << cShift; break;
879 case PDMMEDIATYPE_FLOPPY_FAKE_63_5: u32 |= 15 << cShift; break;
880 default: AssertFailed(); break;
881 }
882 }
883 }
884 }
885 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
886
887 /*
888 * Equipment byte.
889 */
890 if (cFDs > 0)
891 u32 = ((cFDs - 1) << 6) | 0x01; /* floppy installed, additional drives. */
892 else
893 u32 = 0x00; /* floppy not installed. */
894 u32 |= RT_BIT(1); /* math coprocessor installed */
895 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
896 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
897 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
898
899 /*
900 * IDE harddisks.
901 */
902 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
903 {
904 PPDMIBASE pBase;
905 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszHDDevice, 0, i, &pBase);
906 if (RT_SUCCESS(rc))
907 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
908 if ( apHDs[i]
909 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
910 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
911 apHDs[i] = NULL;
912 if (apHDs[i])
913 {
914 PDMMEDIAGEOMETRY LCHSGeometry;
915 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
916 AssertRC(rc2);
917
918 if (i < 4)
919 {
920 /* Award BIOS extended drive types for first to fourth disk.
921 * Used by the BIOS for setting the logical geometry. */
922 int offType, offInfo;
923 switch (i)
924 {
925 case 0:
926 offType = 0x19;
927 offInfo = 0x1e;
928 break;
929 case 1:
930 offType = 0x1a;
931 offInfo = 0x26;
932 break;
933 case 2:
934 offType = 0x00;
935 offInfo = 0x67;
936 break;
937 case 3:
938 default:
939 offType = 0x00;
940 offInfo = 0x70;
941 break;
942 }
943 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo,
944 &LCHSGeometry);
945 }
946 LogRel(("PcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
947 }
948 }
949
950 /* 0Fh means extended and points to 19h, 1Ah */
951 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
952 pcbiosCmosWrite(pDevIns, 0x12, u32);
953
954 /*
955 * SATA harddisks.
956 */
957 if (pThis->pszSataDevice)
958 {
959 /* Clear pointers to the block devices. */
960 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
961 apHDs[i] = NULL;
962
963 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
964 {
965 PPDMIBASE pBase;
966 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
967 if (RT_SUCCESS(rc))
968 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
969 if ( apHDs[i]
970 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
971 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
972 apHDs[i] = NULL;
973 if (apHDs[i])
974 {
975 PDMMEDIAGEOMETRY LCHSGeometry;
976 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
977 AssertRC(rc);
978
979 if (i < 4)
980 {
981 /* Award BIOS extended drive types for first to fourth disk.
982 * Used by the BIOS for setting the logical geometry. */
983 int offInfo;
984 switch (i)
985 {
986 case 0:
987 offInfo = 0x40;
988 break;
989 case 1:
990 offInfo = 0x48;
991 break;
992 case 2:
993 offInfo = 0x50;
994 break;
995 case 3:
996 default:
997 offInfo = 0x58;
998 break;
999 }
1000 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo,
1001 &LCHSGeometry);
1002 }
1003 LogRel(("PcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1004 }
1005 }
1006 }
1007
1008 /*
1009 * SCSI harddisks. Not handled quite the same as SATA.
1010 */
1011 if (pThis->pszScsiDevice)
1012 {
1013 /* Clear pointers to the block devices. */
1014 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1015 apHDs[i] = NULL;
1016
1017 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
1018 {
1019 PPDMIBASE pBase;
1020 int rc = PDMDevHlpQueryLun(pDevIns, pThis->pszScsiDevice, 0, pThis->iScsiHDLUN[i], &pBase);
1021 if (RT_SUCCESS(rc))
1022 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
1023 if ( apHDs[i]
1024 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMMEDIATYPE_HARD_DISK
1025 || !apHDs[i]->pfnBiosIsVisible(apHDs[i])))
1026 apHDs[i] = NULL;
1027 if (apHDs[i])
1028 {
1029 PDMMEDIAGEOMETRY LCHSGeometry;
1030 rc = getLogicalDiskGeometry(apHDs[i], &LCHSGeometry);
1031
1032 if (i < 4 && RT_SUCCESS(rc))
1033 {
1034 /* Extended drive information (for SCSI disks).
1035 * Used by the BIOS for setting the logical geometry, but
1036 * only if the image provided valid data.
1037 */
1038 int offInfo;
1039 switch (i)
1040 {
1041 case 0:
1042 offInfo = 0x90;
1043 break;
1044 case 1:
1045 offInfo = 0x98;
1046 break;
1047 case 2:
1048 offInfo = 0xa0;
1049 break;
1050 case 3:
1051 default:
1052 offInfo = 0xa8;
1053 break;
1054 }
1055 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo, &LCHSGeometry);
1056 LogRel(("PcBios: SCSI LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
1057 }
1058 else
1059 LogRel(("PcBios: SCSI LUN#%d LCHS not provided\n", i));
1060 }
1061 }
1062 }
1063
1064 /* Calculate and store AT-style CMOS checksum. */
1065 uint16_t cksum = 0;
1066 for (i = 0x10; i < 0x2e; ++i)
1067 cksum += pcbiosCmosRead(pDevIns, i);
1068 pcbiosCmosWrite(pDevIns, 0x2e, RT_BYTE1(cksum));
1069 pcbiosCmosWrite(pDevIns, 0x2f, RT_BYTE2(cksum));
1070
1071 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
1072 return VINF_SUCCESS;
1073}
1074
1075
1076/**
1077 * @interface_method_impl{PDMDEVREG,pfnMemSetup}
1078 */
1079static DECLCALLBACK(void) pcbiosMemSetup(PPDMDEVINS pDevIns, PDMDEVMEMSETUPCTX enmCtx)
1080{
1081 RT_NOREF1(enmCtx);
1082 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1083 LogFlow(("pcbiosMemSetup:\n"));
1084
1085 if (pThis->u8IOAPIC)
1086 FwCommonPlantMpsFloatPtr(pDevIns, pThis->u32MPTableAddr);
1087
1088 /*
1089 * Re-shadow the LAN ROM image and make it RAM/RAM.
1090 *
1091 * This is normally done by the BIOS code, but since we're currently lacking
1092 * the chipset support for this we do it here (and in the constructor).
1093 */
1094 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, GUEST_PAGE_SIZE) >> GUEST_PAGE_SHIFT;
1095 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
1096 while (cPages > 0)
1097 {
1098 uint8_t abPage[GUEST_PAGE_SIZE];
1099 int rc;
1100
1101 /* Read the (original) ROM page and write it back to the RAM page. */
1102 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
1103 AssertLogRelRC(rc);
1104
1105 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
1106 AssertLogRelRC(rc);
1107 if (RT_FAILURE(rc))
1108 memset(abPage, 0xcc, sizeof(abPage));
1109
1110 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, GUEST_PAGE_SIZE);
1111 AssertLogRelRC(rc);
1112
1113 /* Switch to the RAM/RAM mode. */
1114 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, GUEST_PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
1115 AssertLogRelRC(rc);
1116
1117 /* Advance */
1118 GCPhys += GUEST_PAGE_SIZE;
1119 cPages--;
1120 }
1121}
1122
1123
1124/**
1125 * @interface_method_impl{PDMDEVREG,pfnDestruct}
1126 */
1127static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1128{
1129 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
1130 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1131 LogFlow(("pcbiosDestruct:\n"));
1132
1133 /*
1134 * Free MM heap pointers.
1135 */
1136 if (pThis->pu8PcBios)
1137 {
1138 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8PcBios);
1139 pThis->pu8PcBios = NULL;
1140 }
1141
1142 if (pThis->pszPcBiosFile)
1143 {
1144 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1145 pThis->pszPcBiosFile = NULL;
1146 }
1147
1148 if (pThis->pu8LanBoot)
1149 {
1150 PDMDevHlpMMHeapFree(pDevIns, pThis->pu8LanBoot);
1151 pThis->pu8LanBoot = NULL;
1152 }
1153
1154 if (pThis->pszLanBootFile)
1155 {
1156 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1157 pThis->pszLanBootFile = NULL;
1158 }
1159
1160 if (pThis->pszHDDevice)
1161 {
1162 PDMDevHlpMMHeapFree(pDevIns, pThis->pszHDDevice);
1163 pThis->pszHDDevice = NULL;
1164 }
1165
1166 if (pThis->pszFDDevice)
1167 {
1168 PDMDevHlpMMHeapFree(pDevIns, pThis->pszFDDevice);
1169 pThis->pszFDDevice = NULL;
1170 }
1171
1172 if (pThis->pszSataDevice)
1173 {
1174 PDMDevHlpMMHeapFree(pDevIns, pThis->pszSataDevice);
1175 pThis->pszSataDevice = NULL;
1176 }
1177
1178 if (pThis->pszScsiDevice)
1179 {
1180 PDMDevHlpMMHeapFree(pDevIns, pThis->pszScsiDevice);
1181 pThis->pszScsiDevice = NULL;
1182 }
1183
1184 return VINF_SUCCESS;
1185}
1186
1187
1188/**
1189 * Convert config value to DEVPCBIOSBOOT.
1190 *
1191 * @returns VBox status code.
1192 * @param pDevIns Device instance data.
1193 * @param pCfg Configuration handle.
1194 * @param pszParam The name of the value to read.
1195 * @param penmBoot Where to store the boot method.
1196 */
1197static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1198{
1199 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1200
1201 char szBuf[64];
1202 int rc = pHlp->pfnCFGMQueryString(pCfg, pszParam, szBuf, sizeof(szBuf));
1203 if (RT_FAILURE(rc))
1204 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1205 N_("Configuration error: Querying \"%s\" as a string failed"), pszParam);
1206
1207 if (!strcmp(szBuf, "DVD") || !strcmp(szBuf, "CDROM"))
1208 *penmBoot = DEVPCBIOSBOOT_DVD;
1209 else if (!strcmp(szBuf, "IDE"))
1210 *penmBoot = DEVPCBIOSBOOT_HD;
1211 else if (!strcmp(szBuf, "FLOPPY"))
1212 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1213 else if (!strcmp(szBuf, "LAN"))
1214 *penmBoot = DEVPCBIOSBOOT_LAN;
1215 else if (!strcmp(szBuf, "NONE"))
1216 *penmBoot = DEVPCBIOSBOOT_NONE;
1217 else
1218 rc = PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1219 N_("Configuration error: The \"%s\" value \"%s\" is unknown"), pszParam, szBuf);
1220 return rc;
1221}
1222
1223
1224/**
1225 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1226 */
1227static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1228{
1229 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1230 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1231 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1232 int rc;
1233 int cb;
1234 Assert(iInstance == 0); RT_NOREF(iInstance);
1235
1236 /*
1237 * Validate configuration.
1238 */
1239 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
1240 "BootDevice0"
1241 "|BootDevice1"
1242 "|BootDevice2"
1243 "|BootDevice3"
1244 "|HardDiskDevice"
1245 "|SataHardDiskDevice"
1246 "|SataLUN1"
1247 "|SataLUN2"
1248 "|SataLUN3"
1249 "|SataLUN4"
1250 "|ScsiHardDiskDevice"
1251 "|ScsiLUN1"
1252 "|ScsiLUN2"
1253 "|ScsiLUN3"
1254 "|ScsiLUN4"
1255 "|FloppyDevice"
1256 "|DelayBoot"
1257 "|BiosRom"
1258 "|LanBootRom"
1259 "|PXEDebug"
1260 "|UUID"
1261 "|UuidLe"
1262 "|IOAPIC"
1263 "|APIC"
1264 "|NumCPUs"
1265 "|McfgBase"
1266 "|McfgLength"
1267 "|DmiBIOSFirmwareMajor"
1268 "|DmiBIOSFirmwareMinor"
1269 "|DmiBIOSReleaseDate"
1270 "|DmiBIOSReleaseMajor"
1271 "|DmiBIOSReleaseMinor"
1272 "|DmiBIOSVendor"
1273 "|DmiBIOSVersion"
1274 "|DmiSystemFamily"
1275 "|DmiSystemProduct"
1276 "|DmiSystemSerial"
1277 "|DmiSystemSKU"
1278 "|DmiSystemUuid"
1279 "|DmiSystemVendor"
1280 "|DmiSystemVersion"
1281 "|DmiBoardAssetTag"
1282 "|DmiBoardBoardType"
1283 "|DmiBoardLocInChass"
1284 "|DmiBoardProduct"
1285 "|DmiBoardSerial"
1286 "|DmiBoardVendor"
1287 "|DmiBoardVersion"
1288 "|DmiChassisAssetTag"
1289 "|DmiChassisSerial"
1290 "|DmiChassisType"
1291 "|DmiChassisVendor"
1292 "|DmiChassisVersion"
1293 "|DmiProcManufacturer"
1294 "|DmiProcVersion"
1295 "|DmiOEMVBoxVer"
1296 "|DmiOEMVBoxRev"
1297 "|DmiUseHostInfo"
1298 "|DmiExposeMemoryTable"
1299 "|DmiExposeProcInf"
1300 "|CheckShutdownStatusForSoftReset"
1301 "|ClearShutdownStatusOnHardReset"
1302 ,
1303 "NetBoot");
1304 /*
1305 * Init the data.
1306 */
1307 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1308 if (RT_FAILURE(rc))
1309 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1310
1311 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThis->u64McfgBase, 0);
1312 if (RT_FAILURE(rc))
1313 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"\" as integer failed"));
1314 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1315 if (RT_FAILURE(rc))
1316 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1317
1318
1319 LogRel(("PcBios: [SMP] BIOS with %u CPUs\n", pThis->cCpus));
1320
1321 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1322 if (RT_FAILURE (rc))
1323 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"IOAPIC\""));
1324
1325 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThis->u8APICMode, 1);
1326 if (RT_FAILURE (rc))
1327 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"APIC\""));
1328
1329 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1330 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1331 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1332 {
1333 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1334 if (RT_FAILURE(rc))
1335 return rc;
1336 }
1337
1338 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1339 if (RT_FAILURE(rc))
1340 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1341
1342 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1343 if (RT_FAILURE(rc))
1344 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1345
1346 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1347 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1348 pThis->pszSataDevice = NULL;
1349 else if (RT_FAILURE(rc))
1350 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1351
1352 if (pThis->pszSataDevice)
1353 {
1354 static const char * const s_apszSataDisks[] = { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
1355 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1356 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1357 {
1358 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1359 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1360 pThis->iSataHDLUN[i] = i;
1361 else if (RT_FAILURE(rc))
1362 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1363 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1364 }
1365 }
1366
1367 /* Repeat the exercise for SCSI drives. */
1368 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "ScsiHardDiskDevice", &pThis->pszScsiDevice);
1369 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1370 pThis->pszScsiDevice = NULL;
1371 else if (RT_FAILURE(rc))
1372 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ScsiHardDiskDevice\" as a string failed"));
1373
1374 if (pThis->pszScsiDevice)
1375 {
1376 static const char * const s_apszScsiDisks[] = { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
1377 Assert(RT_ELEMENTS(s_apszScsiDisks) == RT_ELEMENTS(pThis->iScsiHDLUN));
1378 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iScsiHDLUN); i++)
1379 {
1380 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszScsiDisks[i], &pThis->iScsiHDLUN[i]);
1381 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1382 pThis->iScsiHDLUN[i] = i;
1383 else if (RT_FAILURE(rc))
1384 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1385 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszScsiDisks);
1386 }
1387 }
1388
1389 /* PXE debug logging option. */
1390 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1391 if (RT_FAILURE(rc))
1392 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1393
1394
1395 /*
1396 * Register the I/O Ports.
1397 */
1398 IOMIOPORTHANDLE hIoPorts;
1399 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x400 /*uPort*/, 4 /*cPorts*/, pcbiosIOPortDebugWrite, pcbiosIOPortDebugRead,
1400 "Bochs PC BIOS - Panic & Debug", NULL, &hIoPorts);
1401 AssertRCReturn(rc, rc);
1402
1403 rc = PDMDevHlpIoPortCreateIsa(pDevIns, 1 /*cPorts*/, pcbiosIOPortShutdownWrite, pcbiosIOPortShutdownRead, NULL /*pvUser*/,
1404 "Bochs PC BIOS - Shutdown", NULL /*paExtDescs*/, &pThis->hIoPortShutdown);
1405 AssertRCReturn(rc, rc);
1406 rc = pcbiosRegisterShutdown(pDevIns, pThis, true /* fNewShutdownPort */);
1407 AssertRCReturn(rc, rc);
1408
1409 /*
1410 * Register SSM handlers, for remembering which shutdown port to use.
1411 */
1412 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCBIOS_SSM_VERSION, 1 /* cbGuess */, NULL,
1413 NULL, NULL, NULL,
1414 NULL, pcbiosSaveExec, NULL,
1415 pcbiosLoadPrep, pcbiosLoadExec, pcbiosLoadDone);
1416
1417 /* Clear the net boot device list. All bits set invokes old behavior,
1418 * as if no second CMOS bank was present.
1419 */
1420 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1421
1422 /*
1423 * Determine the network boot order.
1424 */
1425 PCFGMNODE pCfgNetBoot = pHlp->pfnCFGMGetChild(pCfg, "NetBoot");
1426 if (pCfgNetBoot == NULL)
1427 {
1428 /* Do nothing. */
1429 rc = VINF_SUCCESS;
1430 }
1431 else
1432 {
1433 PCFGMNODE pCfgNetBootDevice;
1434 uint8_t u8PciBus;
1435 uint8_t u8PciDev;
1436 uint8_t u8PciFn;
1437 uint16_t u16BusDevFn;
1438 char szIndex[] = "?";
1439
1440 Assert(pCfgNetBoot);
1441 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1442 {
1443 szIndex[0] = '0' + i;
1444 pCfgNetBootDevice = pHlp->pfnCFGMGetChild(pCfgNetBoot, szIndex);
1445
1446 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1447 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1448 {
1449 /* Do nothing and stop iterating. */
1450 rc = VINF_SUCCESS;
1451 break;
1452 }
1453 else if (RT_FAILURE(rc))
1454 return PDMDEV_SET_ERROR(pDevIns, rc,
1455 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1456 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1457 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1458 {
1459 /* Do nothing and stop iterating. */
1460 rc = VINF_SUCCESS;
1461 break;
1462 }
1463 else if (RT_FAILURE(rc))
1464 return PDMDEV_SET_ERROR(pDevIns, rc,
1465 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1466 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1467 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1468 {
1469 /* Do nothing and stop iterating. */
1470 rc = VINF_SUCCESS;
1471 break;
1472 }
1473 else if (RT_FAILURE(rc))
1474 return PDMDEV_SET_ERROR(pDevIns, rc,
1475 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1476 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1477 pThis->au16NetBootDev[i] = u16BusDevFn;
1478 }
1479 }
1480
1481 /*
1482 * Get the system BIOS ROM file name.
1483 */
1484 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1485 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1486 {
1487 pThis->pszPcBiosFile = NULL;
1488 rc = VINF_SUCCESS;
1489 }
1490 else if (RT_FAILURE(rc))
1491 return PDMDEV_SET_ERROR(pDevIns, rc,
1492 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1493 else if (!*pThis->pszPcBiosFile)
1494 {
1495 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1496 pThis->pszPcBiosFile = NULL;
1497 }
1498
1499 /*
1500 * Get the CPU arch so we can load the appropriate ROMs.
1501 */
1502 CPUMMICROARCH const enmMicroarch = PDMDevHlpCpuGetGuestMicroarch(pDevIns);
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