VirtualBox

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

Last change on this file since 66146 was 66133, checked in by vboxsync, 8 years ago

Devices/PC/DevPcBios: fix copy/paste bug

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