VirtualBox

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

最後變更 在這個檔案從81920是 81920,由 vboxsync 提交於 5 年 前

DevPcBios: Converted and split up I/O port handlers. bugref:9218

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 68.1 KB
 
1/* $Id: DevPcBios.cpp 81920 2019-11-17 22:03:36Z 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.alldomusa.eu.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 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1207 */
1208static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1209{
1210 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1211 PDEVPCBIOS pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPCBIOS);
1212 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1213 int rc;
1214 int cb;
1215 Assert(iInstance == 0); RT_NOREF(iInstance);
1216
1217 /*
1218 * Validate configuration.
1219 */
1220 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
1221 "BootDevice0"
1222 "|BootDevice1"
1223 "|BootDevice2"
1224 "|BootDevice3"
1225 "|HardDiskDevice"
1226 "|SataHardDiskDevice"
1227 "|SataLUN1"
1228 "|SataLUN2"
1229 "|SataLUN3"
1230 "|SataLUN4"
1231 "|ScsiHardDiskDevice"
1232 "|ScsiLUN1"
1233 "|ScsiLUN2"
1234 "|ScsiLUN3"
1235 "|ScsiLUN4"
1236 "|FloppyDevice"
1237 "|DelayBoot"
1238 "|BiosRom"
1239 "|LanBootRom"
1240 "|PXEDebug"
1241 "|UUID"
1242 "|UuidLe"
1243 "|IOAPIC"
1244 "|APIC"
1245 "|NumCPUs"
1246 "|McfgBase"
1247 "|McfgLength"
1248 "|DmiBIOSFirmwareMajor"
1249 "|DmiBIOSFirmwareMinor"
1250 "|DmiBIOSReleaseDate"
1251 "|DmiBIOSReleaseMajor"
1252 "|DmiBIOSReleaseMinor"
1253 "|DmiBIOSVendor"
1254 "|DmiBIOSVersion"
1255 "|DmiSystemFamily"
1256 "|DmiSystemProduct"
1257 "|DmiSystemSerial"
1258 "|DmiSystemSKU"
1259 "|DmiSystemUuid"
1260 "|DmiSystemVendor"
1261 "|DmiSystemVersion"
1262 "|DmiBoardAssetTag"
1263 "|DmiBoardBoardType"
1264 "|DmiBoardLocInChass"
1265 "|DmiBoardProduct"
1266 "|DmiBoardSerial"
1267 "|DmiBoardVendor"
1268 "|DmiBoardVersion"
1269 "|DmiChassisAssetTag"
1270 "|DmiChassisSerial"
1271 "|DmiChassisType"
1272 "|DmiChassisVendor"
1273 "|DmiChassisVersion"
1274 "|DmiProcManufacturer"
1275 "|DmiProcVersion"
1276 "|DmiOEMVBoxVer"
1277 "|DmiOEMVBoxRev"
1278 "|DmiUseHostInfo"
1279 "|DmiExposeMemoryTable"
1280 "|DmiExposeProcInf"
1281 "|CheckShutdownStatusForSoftReset"
1282 "|ClearShutdownStatusOnHardReset"
1283 ,
1284 "NetBoot");
1285 /*
1286 * Init the data.
1287 */
1288 rc = pHlp->pfnCFGMQueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1289 if (RT_FAILURE(rc))
1290 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1291
1292 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgBase", &pThis->u64McfgBase, 0);
1293 if (RT_FAILURE(rc))
1294 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"\" as integer failed"));
1295 rc = pHlp->pfnCFGMQueryU64Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1296 if (RT_FAILURE(rc))
1297 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1298
1299
1300 LogRel(("PcBios: [SMP] BIOS with %u CPUs\n", pThis->cCpus));
1301
1302 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1303 if (RT_FAILURE (rc))
1304 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"IOAPIC\""));
1305
1306 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "APIC", &pThis->u8APICMode, 1);
1307 if (RT_FAILURE (rc))
1308 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"APIC\""));
1309
1310 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1311 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1312 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1313 {
1314 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1315 if (RT_FAILURE(rc))
1316 return rc;
1317 }
1318
1319 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1320 if (RT_FAILURE(rc))
1321 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1322
1323 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1324 if (RT_FAILURE(rc))
1325 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1326
1327 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1328 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1329 pThis->pszSataDevice = NULL;
1330 else if (RT_FAILURE(rc))
1331 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1332
1333 if (pThis->pszSataDevice)
1334 {
1335 static const char * const s_apszSataDisks[] = { "SataLUN1", "SataLUN2", "SataLUN3", "SataLUN4" };
1336 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1337 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1338 {
1339 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1340 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1341 pThis->iSataHDLUN[i] = i;
1342 else if (RT_FAILURE(rc))
1343 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1344 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1345 }
1346 }
1347
1348 /* Repeat the exercise for SCSI drives. */
1349 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "ScsiHardDiskDevice", &pThis->pszScsiDevice);
1350 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1351 pThis->pszScsiDevice = NULL;
1352 else if (RT_FAILURE(rc))
1353 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ScsiHardDiskDevice\" as a string failed"));
1354
1355 if (pThis->pszScsiDevice)
1356 {
1357 static const char * const s_apszScsiDisks[] = { "ScsiLUN1", "ScsiLUN2", "ScsiLUN3", "ScsiLUN4" };
1358 Assert(RT_ELEMENTS(s_apszScsiDisks) == RT_ELEMENTS(pThis->iScsiHDLUN));
1359 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iScsiHDLUN); i++)
1360 {
1361 rc = pHlp->pfnCFGMQueryU32(pCfg, s_apszScsiDisks[i], &pThis->iScsiHDLUN[i]);
1362 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1363 pThis->iScsiHDLUN[i] = i;
1364 else if (RT_FAILURE(rc))
1365 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1366 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszScsiDisks);
1367 }
1368 }
1369
1370 /* PXE debug logging option. */
1371 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1372 if (RT_FAILURE(rc))
1373 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1374
1375
1376 /*
1377 * Register the I/O Ports.
1378 */
1379 IOMIOPORTHANDLE hIoPorts;
1380 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, 0x400 /*uPort*/, 4 /*cPorts*/, pcbiosIOPortDebugWrite, pcbiosIOPortDebugRead,
1381 "Bochs PC BIOS - Panic & Debug", NULL, &hIoPorts);
1382 AssertRCReturn(rc, rc);
1383
1384 rc = PDMDevHlpIoPortCreateIsa(pDevIns, 1 /*cPorts*/, pcbiosIOPortShutdownWrite, pcbiosIOPortShutdownRead, NULL /*pvUser*/,
1385 "Bochs PC BIOS - Shutdown", NULL /*paExtDescs*/, &pThis->hIoPortShutdown);
1386 AssertRCReturn(rc, rc);
1387 rc = pcbiosRegisterShutdown(pDevIns, pThis, true /* fNewShutdownPort */);
1388 AssertRCReturn(rc, rc);
1389
1390 /*
1391 * Register SSM handlers, for remembering which shutdown port to use.
1392 */
1393 rc = PDMDevHlpSSMRegisterEx(pDevIns, PCBIOS_SSM_VERSION, 1 /* cbGuess */, NULL,
1394 NULL, NULL, NULL,
1395 NULL, pcbiosSaveExec, NULL,
1396 pcbiosLoadPrep, pcbiosLoadExec, pcbiosLoadDone);
1397
1398 /* Clear the net boot device list. All bits set invokes old behavior,
1399 * as if no second CMOS bank was present.
1400 */
1401 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1402
1403 /*
1404 * Determine the network boot order.
1405 */
1406 PCFGMNODE pCfgNetBoot = pHlp->pfnCFGMGetChild(pCfg, "NetBoot");
1407 if (pCfgNetBoot == NULL)
1408 {
1409 /* Do nothing. */
1410 rc = VINF_SUCCESS;
1411 }
1412 else
1413 {
1414 PCFGMNODE pCfgNetBootDevice;
1415 uint8_t u8PciBus;
1416 uint8_t u8PciDev;
1417 uint8_t u8PciFn;
1418 uint16_t u16BusDevFn;
1419 char szIndex[] = "?";
1420
1421 Assert(pCfgNetBoot);
1422 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1423 {
1424 szIndex[0] = '0' + i;
1425 pCfgNetBootDevice = pHlp->pfnCFGMGetChild(pCfgNetBoot, szIndex);
1426
1427 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1428 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1429 {
1430 /* Do nothing and stop iterating. */
1431 rc = VINF_SUCCESS;
1432 break;
1433 }
1434 else if (RT_FAILURE(rc))
1435 return PDMDEV_SET_ERROR(pDevIns, rc,
1436 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1437 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1438 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1439 {
1440 /* Do nothing and stop iterating. */
1441 rc = VINF_SUCCESS;
1442 break;
1443 }
1444 else if (RT_FAILURE(rc))
1445 return PDMDEV_SET_ERROR(pDevIns, rc,
1446 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1447 rc = pHlp->pfnCFGMQueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1448 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1449 {
1450 /* Do nothing and stop iterating. */
1451 rc = VINF_SUCCESS;
1452 break;
1453 }
1454 else if (RT_FAILURE(rc))
1455 return PDMDEV_SET_ERROR(pDevIns, rc,
1456 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1457 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1458 pThis->au16NetBootDev[i] = u16BusDevFn;
1459 }
1460 }
1461
1462 /*
1463 * Get the system BIOS ROM file name.
1464 */
1465 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1466 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1467 {
1468 pThis->pszPcBiosFile = NULL;
1469 rc = VINF_SUCCESS;
1470 }
1471 else if (RT_FAILURE(rc))
1472 return PDMDEV_SET_ERROR(pDevIns, rc,
1473 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1474 else if (!*pThis->pszPcBiosFile)
1475 {
1476 PDMDevHlpMMHeapFree(pDevIns, pThis->pszPcBiosFile);
1477 pThis->pszPcBiosFile = NULL;
1478 }
1479
1480 /*
1481 * Get the CPU arch so we can load the appropriate ROMs.
1482 */
1483 PVM pVM = PDMDevHlpGetVM(pDevIns);
1484 CPUMMICROARCH const enmMicroarch = pVM ? CPUMGetGuestMicroarch(pVM) : kCpumMicroarch_Intel_P6;
1485
1486 if (pThis->pszPcBiosFile)
1487 {
1488 /*
1489 * Load the BIOS ROM.
1490 */
1491 RTFILE hFilePcBios;
1492 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1493 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1494 if (RT_SUCCESS(rc))
1495 {
1496 /* Figure the size and check restrictions. */
1497 uint64_t cbPcBios;
1498 rc = RTFileQuerySize(hFilePcBios, &cbPcBios);
1499 if (RT_SUCCESS(rc))
1500 {
1501 pThis->cbPcBios = (uint32_t)cbPcBios;
1502 if ( RT_ALIGN(pThis->cbPcBios, _64K) == pThis->cbPcBios
1503 && pThis->cbPcBios == cbPcBios
1504 && pThis->cbPcBios <= 32 * _64K
1505 && pThis->cbPcBios >= _64K)
1506 {
1507 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1508 if (pThis->pu8PcBios)
1509 {
1510 rc = RTFileRead(hFilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1511 if (RT_FAILURE(rc))
1512 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1513 N_("Error reading the BIOS image ('%s)"), pThis->pszPcBiosFile);
1514 }
1515 else
1516 rc = PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1517 N_("Failed to allocate %#x bytes for loading the BIOS image"),
1518 pThis->cbPcBios);
1519 }
1520 else
1521 rc = PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
1522 N_("Invalid system BIOS file size ('%s'): %#llx (%llu)"),
1523 pThis->pszPcBiosFile, cbPcBios, cbPcBios);
1524 }
1525 else
1526 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1527 N_("Failed to query the system BIOS file size ('%s')"),
1528 pThis->pszPcBiosFile);
1529 RTFileClose(hFilePcBios);
1530 }
1531 else
1532 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1533 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1534 if (RT_FAILURE(rc))
1535 return rc;
1536
1537 LogRel(("PcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1538 }
1539 else
1540 {
1541 /*
1542 * Use one of the embedded BIOS ROM images.
1543 */
1544 uint8_t const *pbBios;
1545 uint32_t cbBios;
1546 if ( enmMicroarch == kCpumMicroarch_Intel_8086
1547 || enmMicroarch == kCpumMicroarch_Intel_80186
1548 || enmMicroarch == kCpumMicroarch_NEC_V20
1549 || enmMicroarch == kCpumMicroarch_NEC_V30)
1550 {
1551 pbBios = g_abPcBiosBinary8086;
1552 cbBios = g_cbPcBiosBinary8086;
1553 LogRel(("PcBios: Using the 8086 BIOS image!\n"));
1554 }
1555 else if (enmMicroarch == kCpumMicroarch_Intel_80286)
1556 {
1557 pbBios = g_abPcBiosBinary286;
1558 cbBios = g_cbPcBiosBinary286;
1559 LogRel(("PcBios: Using the 286 BIOS image!\n"));
1560 }
1561 else
1562 {
1563 pbBios = g_abPcBiosBinary386;
1564 cbBios = g_cbPcBiosBinary386;
1565 LogRel(("PcBios: Using the 386+ BIOS image.\n"));
1566 }
1567 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbBios);
1568 if (pThis->pu8PcBios)
1569 {
1570 pThis->cbPcBios = cbBios;
1571 memcpy(pThis->pu8PcBios, pbBios, cbBios);
1572 }
1573 else
1574 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1575 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"), cbBios);
1576 }
1577 const uint8_t *pu8PcBiosBinary = pThis->pu8PcBios;
1578 uint32_t cbPcBiosBinary = pThis->cbPcBios;
1579
1580 /*
1581 * Query the machine's UUID for SMBIOS/DMI use.
1582 */
1583 RTUUID uuid;
1584 rc = pHlp->pfnCFGMQueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1585 if (RT_FAILURE(rc))
1586 return PDMDEV_SET_ERROR(pDevIns, rc,
1587 N_("Configuration error: Querying \"UUID\" failed"));
1588
1589 bool fUuidLe;
1590 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "UuidLe", &fUuidLe, false);
1591 if (RT_FAILURE(rc))
1592 return PDMDEV_SET_ERROR(pDevIns, rc,
1593 N_("Configuration error: Querying \"UuidLe\" failed"));
1594
1595 if (!fUuidLe)
1596 {
1597 /*
1598 * UUIDs are stored little endian actually (see chapter 7.2.1 System — UUID
1599 * of the DMI/SMBIOS spec) but to not force reactivation of existing guests we have
1600 * to carry this bug along... (see also DevEFI.cpp when changing this)
1601 *
1602 * Convert the UUID to network byte order. Not entirely straightforward as
1603 * parts are MSB already...
1604 */
1605 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1606 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1607 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1608 }
1609
1610 uint16_t cbDmiTables = 0;
1611 uint16_t cNumDmiTables = 0;
1612 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1613 &uuid, pCfg, pThis->cCpus, &cbDmiTables, &cNumDmiTables);
1614 if (RT_FAILURE(rc))
1615 return rc;
1616
1617 /* Look for _SM_/_DMI_ anchor strings within the BIOS and replace the table headers. */
1618 for (unsigned i = 0; i < (pThis->cbPcBios - 16); i += 16)
1619 {
1620 if ( pThis->pu8PcBios[i + 0x00] == '_'
1621 && pThis->pu8PcBios[i + 0x01] == 'S'
1622 && pThis->pu8PcBios[i + 0x02] == 'M'
1623 && pThis->pu8PcBios[i + 0x03] == '_'
1624 && pThis->pu8PcBios[i + 0x10] == '_'
1625 && pThis->pu8PcBios[i + 0x11] == 'D'
1626 && pThis->pu8PcBios[i + 0x12] == 'M'
1627 && pThis->pu8PcBios[i + 0x13] == 'I'
1628 && pThis->pu8PcBios[i + 0x14] == '_')
1629 {
1630 FwCommonPlantSmbiosAndDmiHdrs(pDevIns, pThis->pu8PcBios + i, cbDmiTables, cNumDmiTables);
1631 break;
1632 }
1633 }
1634
1635 if (pThis->u8IOAPIC)
1636 {
1637 pThis->u32MPTableAddr = VBOX_DMI_TABLE_BASE + VBOX_DMI_TABLE_SIZE;
1638 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage /* aka VBOX_DMI_TABLE_BASE */ + VBOX_DMI_TABLE_SIZE,
1639 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1640 LogRel(("PcBios: MPS table at %08x\n", pThis->u32MPTableAddr));
1641 }
1642
1643 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
1644 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1645 if (RT_FAILURE(rc))
1646 return rc;
1647
1648 /*
1649 * Map the BIOS into memory.
1650 * There are two mappings:
1651 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1652 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1653 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1654 */
1655 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1656 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1657 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1658 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1659 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb], cb,
1660 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xfffff");
1661 if (RT_FAILURE(rc))
1662 return rc;
1663 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary, cbPcBiosBinary,
1664 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xffffffff");
1665 if (RT_FAILURE(rc))
1666 return rc;
1667
1668 /*
1669 * Get the LAN boot ROM file name.
1670 */
1671 rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1672 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1673 {
1674 pThis->pszLanBootFile = NULL;
1675 rc = VINF_SUCCESS;
1676 }
1677 else if (RT_FAILURE(rc))
1678 return PDMDEV_SET_ERROR(pDevIns, rc,
1679 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1680 else if (!*pThis->pszLanBootFile)
1681 {
1682 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1683 pThis->pszLanBootFile = NULL;
1684 }
1685
1686 /*
1687 * Not loading LAN ROM for old CPUs.
1688 */
1689 if ( enmMicroarch != kCpumMicroarch_Intel_8086
1690 && enmMicroarch != kCpumMicroarch_Intel_80186
1691 && enmMicroarch != kCpumMicroarch_NEC_V20
1692 && enmMicroarch != kCpumMicroarch_NEC_V30
1693 && enmMicroarch != kCpumMicroarch_Intel_80286)
1694 {
1695 const uint8_t *pu8LanBootBinary = NULL;
1696 uint64_t cbLanBootBinary;
1697 uint64_t cbFileLanBoot = 0;
1698
1699 /*
1700 * Open the LAN boot ROM and figure it size.
1701 * Determine the LAN boot ROM size, open specified ROM file in the process.
1702 */
1703 if (pThis->pszLanBootFile)
1704 {
1705 RTFILE hFileLanBoot = NIL_RTFILE;
1706 rc = RTFileOpen(&hFileLanBoot, pThis->pszLanBootFile,
1707 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1708 if (RT_SUCCESS(rc))
1709 {
1710 rc = RTFileQuerySize(hFileLanBoot, &cbFileLanBoot);
1711 if (RT_SUCCESS(rc))
1712 {
1713 if (cbFileLanBoot <= _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff))
1714 {
1715 LogRel(("PcBios: Using LAN ROM '%s' with a size of %#x bytes\n", pThis->pszLanBootFile, cbFileLanBoot));
1716
1717 /*
1718 * Allocate buffer for the LAN boot ROM data and load it.
1719 */
1720 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, cbFileLanBoot);
1721 if (pThis->pu8LanBoot)
1722 {
1723 rc = RTFileRead(hFileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1724 AssertLogRelRCReturnStmt(rc, RTFileClose(hFileLanBoot), rc);
1725 }
1726 else
1727 rc = VERR_NO_MEMORY;
1728 }
1729 else
1730 rc = VERR_TOO_MUCH_DATA;
1731 }
1732 RTFileClose(hFileLanBoot);
1733 }
1734 if (RT_FAILURE(rc))
1735 {
1736 /*
1737 * Play stupid and ignore failures, falling back to the built-in LAN boot ROM.
1738 */
1739 /** @todo r=bird: This should have some kind of rational. We don't usually
1740 * ignore the VM configuration. */
1741 LogRel(("PcBios: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1742 PDMDevHlpMMHeapFree(pDevIns, pThis->pszLanBootFile);
1743 pThis->pszLanBootFile = NULL;
1744 }
1745 }
1746
1747 /* If we were unable to get the data from file for whatever reason, fall
1748 * back to the built-in LAN boot ROM image.
1749 */
1750 if (pThis->pu8LanBoot == NULL)
1751 {
1752#ifdef VBOX_WITH_PXE_ROM
1753 pu8LanBootBinary = g_abNetBiosBinary;
1754 cbLanBootBinary = g_cbNetBiosBinary;
1755#endif
1756 }
1757 else
1758 {
1759 pu8LanBootBinary = pThis->pu8LanBoot;
1760 cbLanBootBinary = cbFileLanBoot;
1761 }
1762
1763 /*
1764 * Map the Network Boot ROM into memory.
1765 *
1766 * Currently there is a fixed mapping: 0x000e2000 to 0x000effff contains
1767 * the (up to) 56 kb ROM image. The mapping size is fixed to trouble with
1768 * the saved state (in PGM).
1769 */
1770 if (pu8LanBootBinary)
1771 {
1772 pThis->cbLanBoot = cbLanBootBinary;
1773
1774 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4,
1775 RT_MAX(cbLanBootBinary, _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff)),
1776 pu8LanBootBinary, cbLanBootBinary,
1777 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1778 AssertRCReturn(rc, rc);
1779 }
1780 }
1781 else if (pThis->pszLanBootFile)
1782 LogRel(("PcBios: Skipping LAN ROM '%s' due to ancient target CPU.\n", pThis->pszLanBootFile));
1783#ifdef VBOX_WITH_PXE_ROM
1784 else
1785 LogRel(("PcBios: Skipping built in ROM due to ancient target CPU.\n"));
1786#endif
1787
1788 /*
1789 * Configure Boot delay.
1790 */
1791 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1792 if (RT_FAILURE(rc))
1793 return PDMDEV_SET_ERROR(pDevIns, rc,
1794 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1795 if (pThis->uBootDelay > 15)
1796 pThis->uBootDelay = 15;
1797
1798
1799 /*
1800 * Read shutdown status code config and register ourselves as the firmware device.
1801 */
1802
1803 /** @cfgm{CheckShutdownStatusForSoftReset, boolean, true}
1804 * Whether to consult the shutdown status code (CMOS register 0Fh) to
1805 * determine whether the guest intended a soft or hard reset. Currently only
1806 * shutdown status codes 05h, 09h and 0Ah are considered soft reset. */
1807 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CheckShutdownStatusForSoftReset", &pThis->fCheckShutdownStatusForSoftReset, true);
1808 AssertLogRelRCReturn(rc, rc);
1809
1810 /** @cfgm{ClearShutdownStatusOnHardReset, boolean, true}
1811 * Whether to clear the shutdown status code (CMOS register 0Fh) on hard reset. */
1812 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ClearShutdownStatusOnHardReset", &pThis->fClearShutdownStatusOnHardReset, true);
1813 AssertLogRelRCReturn(rc, rc);
1814
1815 LogRel(("PcBios: fCheckShutdownStatusForSoftReset=%RTbool fClearShutdownStatusOnHardReset=%RTbool\n",
1816 pThis->fCheckShutdownStatusForSoftReset, pThis->fClearShutdownStatusOnHardReset));
1817
1818 static PDMFWREG const s_FwReg = { PDM_FWREG_VERSION, pcbiosFw_IsHardReset, PDM_FWREG_VERSION };
1819 rc = PDMDevHlpFirmwareRegister(pDevIns, &s_FwReg, &pThis->pFwHlpR3);
1820 AssertLogRelRCReturn(rc, rc);
1821
1822 return VINF_SUCCESS;
1823}
1824
1825
1826/**
1827 * The device registration structure.
1828 */
1829const PDMDEVREG g_DevicePcBios =
1830{
1831 /* .u32Version = */ PDM_DEVREG_VERSION,
1832 /* .uReserved0 = */ 0,
1833 /* .szName = */ "pcbios",
1834 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS,
1835 /* .fClass = */ PDM_DEVREG_CLASS_ARCH_BIOS,
1836 /* .cMaxInstances = */ 1,
1837 /* .uSharedVersion = */ 42,
1838 /* .cbInstanceShared = */ sizeof(DEVPCBIOS),
1839 /* .cbInstanceCC = */ 0,
1840 /* .cbInstanceRC = */ 0,
1841 /* .cMaxPciDevices = */ 0,
1842 /* .cMaxMsixVectors = */ 0,
1843 /* .pszDescription = */ "PC BIOS Device",
1844#if defined(IN_RING3)
1845 /* .pszRCMod = */ "",
1846 /* .pszR0Mod = */ "",
1847 /* .pfnConstruct = */ pcbiosConstruct,
1848 /* .pfnDestruct = */ pcbiosDestruct,
1849 /* .pfnRelocate = */ NULL,
1850 /* .pfnMemSetup = */ pcbiosMemSetup,
1851 /* .pfnPowerOn = */ NULL,
1852 /* .pfnReset = */ pcbiosReset,
1853 /* .pfnSuspend = */ NULL,
1854 /* .pfnResume = */ NULL,
1855 /* .pfnAttach = */ NULL,
1856 /* .pfnDetach = */ NULL,
1857 /* .pfnQueryInterface = */ NULL,
1858 /* .pfnInitComplete = */ pcbiosInitComplete,
1859 /* .pfnPowerOff = */ NULL,
1860 /* .pfnSoftReset = */ NULL,
1861 /* .pfnReserved0 = */ NULL,
1862 /* .pfnReserved1 = */ NULL,
1863 /* .pfnReserved2 = */ NULL,
1864 /* .pfnReserved3 = */ NULL,
1865 /* .pfnReserved4 = */ NULL,
1866 /* .pfnReserved5 = */ NULL,
1867 /* .pfnReserved6 = */ NULL,
1868 /* .pfnReserved7 = */ NULL,
1869#elif defined(IN_RING0)
1870 /* .pfnEarlyConstruct = */ NULL,
1871 /* .pfnConstruct = */ NULL,
1872 /* .pfnDestruct = */ NULL,
1873 /* .pfnFinalDestruct = */ NULL,
1874 /* .pfnRequest = */ NULL,
1875 /* .pfnReserved0 = */ NULL,
1876 /* .pfnReserved1 = */ NULL,
1877 /* .pfnReserved2 = */ NULL,
1878 /* .pfnReserved3 = */ NULL,
1879 /* .pfnReserved4 = */ NULL,
1880 /* .pfnReserved5 = */ NULL,
1881 /* .pfnReserved6 = */ NULL,
1882 /* .pfnReserved7 = */ NULL,
1883#elif defined(IN_RC)
1884 /* .pfnConstruct = */ NULL,
1885 /* .pfnReserved0 = */ NULL,
1886 /* .pfnReserved1 = */ NULL,
1887 /* .pfnReserved2 = */ NULL,
1888 /* .pfnReserved3 = */ NULL,
1889 /* .pfnReserved4 = */ NULL,
1890 /* .pfnReserved5 = */ NULL,
1891 /* .pfnReserved6 = */ NULL,
1892 /* .pfnReserved7 = */ NULL,
1893#else
1894# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1895#endif
1896 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1897};
1898
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette