VirtualBox

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

Last change on this file since 82781 was 81925, checked in by vboxsync, 5 years ago

DevPcBios: Set the new-style device flag. Nits. bugref:9218

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

© 2023 Oracle
ContactPrivacy policyTerms of Use