VirtualBox

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

Last change on this file since 67075 was 66153, checked in by vboxsync, 8 years ago

DevPcBios: doxygen fix

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

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