VirtualBox

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

Last change on this file since 41580 was 41580, checked in by vboxsync, 13 years ago

PC/DevPcBios: Addition of info cmos and info cmos2 debugging commands

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 52.7 KB
Line 
1/* $Id: DevPcBios.cpp 41580 2012-06-05 14:26:59Z vboxsync $ */
2/** @file
3 * PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2008 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* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DEV_PC_BIOS
22#include <VBox/vmm/pdmdev.h>
23#include <VBox/vmm/mm.h>
24#include <VBox/vmm/pgm.h>
25
26#include <VBox/log.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/buildconfig.h>
30#include <iprt/file.h>
31#include <iprt/mem.h>
32#include <iprt/string.h>
33#include <iprt/uuid.h>
34#include <VBox/err.h>
35#include <VBox/param.h>
36
37#include "VBoxDD.h"
38#include "VBoxDD2.h"
39#include "DevPcBios.h"
40#include "DevFwCommon.h"
41
42#define NET_BOOT_DEVS 4
43
44#define CMOS_BANK_LOWER_LIMIT 0x0E
45#define CMOS_BANK_UPPER_LIMIT 0x7F
46#define CMOS_BANK2_LOWER_LIMIT 0x80
47#define CMOS_BANK2_UPPER_LIMIT 0xFF
48
49/** @page pg_devbios_cmos_assign CMOS Assignments (BIOS)
50 *
51 * The BIOS uses a CMOS to store configuration data.
52 * It is currently used as follows:
53 *
54 * @verbatim
55 First CMOS bank (offsets 0x00 to 0x7f):
56 Floppy drive type:
57 0x10
58 Hard disk type (old):
59 0x12
60 Equipment byte:
61 0x14
62 Base memory:
63 0x15
64 0x16
65 Extended memory:
66 0x17
67 0x18
68 0x30
69 0x31
70 First IDE HDD:
71 0x19
72 0x1e - 0x25
73 Second IDE HDD:
74 0x1a
75 0x26 - 0x2d
76 Checksum of 0x10-0x2d:
77 0x2e
78 0x2f
79 Amount of memory above 16M and below 4GB in 64KB units:
80 0x34
81 0x35
82 Boot device (BOCHS BIOS specific):
83 0x38
84 0x3c
85 0x3d
86 PXE debug:
87 0x3f
88 First SATA HDD:
89 0x40 - 0x47
90 Second SATA HDD:
91 0x48 - 0x4f
92 Third SATA HDD:
93 0x50 - 0x57
94 Fourth SATA HDD:
95 0x58 - 0x5f
96 Number of CPUs:
97 0x60
98 RAM above 4G in 64KB units:
99 0x61 - 0x65
100 Third IDE HDD:
101 0x67 - 0x6e
102 Fourth IDE HDD:
103 0x70 - 0x77
104
105 Second CMOS bank (offsets 0x80 to 0xff):
106 Reserved for internal use by PXE ROM:
107 0x80 - 0x81
108 First net boot device PCI bus/dev/fn:
109 0x82 - 0x83
110 Second to third net boot devices:
111 0x84 - 0x89
112@endverbatim
113 *
114 * @todo Mark which bits are compatible with which BIOSes and
115 * which are our own definitions.
116 */
117
118
119/*******************************************************************************
120* Structures and Typedefs *
121*******************************************************************************/
122
123/**
124 * The boot device.
125 */
126typedef enum DEVPCBIOSBOOT
127{
128 DEVPCBIOSBOOT_NONE,
129 DEVPCBIOSBOOT_FLOPPY,
130 DEVPCBIOSBOOT_HD,
131 DEVPCBIOSBOOT_DVD,
132 DEVPCBIOSBOOT_LAN
133} DEVPCBIOSBOOT;
134
135/**
136 * PC Bios instance data structure.
137 */
138typedef struct DEVPCBIOS
139{
140 /** Pointer back to the device instance. */
141 PPDMDEVINS pDevIns;
142
143 /** Boot devices (ordered). */
144 DEVPCBIOSBOOT aenmBootDevice[4];
145 /** RAM size (in bytes). */
146 uint64_t cbRam;
147 /** RAM hole size (in bytes). */
148 uint32_t cbRamHole;
149 /** Bochs shutdown index. */
150 uint32_t iShutdown;
151 /** Floppy device. */
152 char *pszFDDevice;
153 /** Harddisk device. */
154 char *pszHDDevice;
155 /** Sata harddisk device. */
156 char *pszSataDevice;
157 /** LUN of the four harddisks which are emulated as IDE. */
158 uint32_t iSataHDLUN[4];
159 /** Bios message buffer. */
160 char szMsg[256];
161 /** Bios message buffer index. */
162 uint32_t iMsg;
163 /** The system BIOS ROM data. */
164 uint8_t *pu8PcBios;
165 /** The size of the system BIOS ROM. */
166 uint32_t cbPcBios;
167 /** The name of the BIOS ROM file. */
168 char *pszPcBiosFile;
169 /** The LAN boot ROM data. */
170 uint8_t *pu8LanBoot;
171 /** The name of the LAN boot ROM file. */
172 char *pszLanBootFile;
173 /** The size of the LAN boot ROM. */
174 uint64_t cbLanBoot;
175 /** The DMI tables. */
176 uint8_t au8DMIPage[0x1000];
177 /** The boot countdown (in seconds). */
178 uint8_t uBootDelay;
179 /** I/O-APIC enabled? */
180 uint8_t u8IOAPIC;
181 /** PXE debug logging enabled? */
182 uint8_t u8PXEDebug;
183 /** PXE boot PCI bus/dev/fn list. */
184 uint16_t au16NetBootDev[NET_BOOT_DEVS];
185 /** Number of logical CPUs in guest */
186 uint16_t cCpus;
187 uint32_t u32McfgBase;
188 uint32_t cbMcfgLength;
189} DEVPCBIOS, *PDEVPCBIOS;
190
191
192/* Attempt to guess the LCHS disk geometry from the MS-DOS master boot
193 * record (partition table). */
194static int biosGuessDiskLCHS(PPDMIBLOCK pBlock, PPDMMEDIAGEOMETRY pLCHSGeometry)
195{
196 uint8_t aMBR[512], *p;
197 int rc;
198 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
199
200 if (!pBlock)
201 return VERR_INVALID_PARAMETER;
202 rc = pBlock->pfnRead(pBlock, 0, aMBR, sizeof(aMBR));
203 if (RT_FAILURE(rc))
204 return rc;
205 /* Test MBR magic number. */
206 if (aMBR[510] != 0x55 || aMBR[511] != 0xaa)
207 return VERR_INVALID_PARAMETER;
208 for (uint32_t i = 0; i < 4; i++)
209 {
210 /* Figure out the start of a partition table entry. */
211 p = &aMBR[0x1be + i * 16];
212 iEndHead = p[5];
213 iEndSector = p[6] & 63;
214 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector & iEndHead)
215 {
216 /* Assumption: partition terminates on a cylinder boundary. */
217 cLCHSHeads = iEndHead + 1;
218 cLCHSSectors = iEndSector;
219 cLCHSCylinders = RT_MIN(1024, pBlock->pfnGetSize(pBlock) / (512 * cLCHSHeads * cLCHSSectors));
220 if (cLCHSCylinders >= 1)
221 {
222 pLCHSGeometry->cCylinders = cLCHSCylinders;
223 pLCHSGeometry->cHeads = cLCHSHeads;
224 pLCHSGeometry->cSectors = cLCHSSectors;
225 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
226 return VINF_SUCCESS;
227 }
228 }
229 }
230 return VERR_INVALID_PARAMETER;
231}
232
233
234/**
235 * Write to CMOS memory.
236 * This is used by the init complete code.
237 */
238static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
239{
240 Assert(off < 256);
241 Assert(u32Val < 256);
242
243 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
244 AssertRC(rc);
245}
246
247/**
248 * Read from CMOS memory.
249 * This is used by the init complete code.
250 */
251static uint8_t pcbiosCmosRead(PPDMDEVINS pDevIns, int off)
252{
253 uint8_t u8val;
254
255 Assert(off < 256);
256
257 int rc = PDMDevHlpCMOSRead(pDevIns, off, &u8val);
258 AssertRC(rc);
259
260 return u8val;
261}
262
263/**
264 * @callback_method_impl{FNDBGFHANDLERDEV,
265 * Dumps the cmos Bank Info.}
266 */
267static DECLCALLBACK(void) CMOSBankInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
268{
269 const char *PChCMOSBank = "CMOS Bank Info 0x0E - 0x7F";
270 uint16_t u16ByteCount = 0;
271 uint8_t u8CMOSByte;
272 pHlp->pfnPrintf(pHlp, "%s\n" ,PChCMOSBank);
273 for (u16ByteCount = CMOS_BANK_LOWER_LIMIT; u16ByteCount < CMOS_BANK_UPPER_LIMIT; u16ByteCount++)
274 {
275 u8CMOSByte = pcbiosCmosRead(pDevIns, u16ByteCount);
276 pHlp->pfnPrintf(pHlp, "Off: 0x%02x Val: 0x%02x\n",u16ByteCount, u8CMOSByte);
277 }
278}
279
280/**
281 * @callback_method_impl{FNDBGFHANDLERDEV,
282 * Dumps the cmos Bank2 Info.}
283 */
284static DECLCALLBACK(void) CMOSBank2Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
285{
286 const char *PChCMOSBank = "CMOS Bank2 Info 0x80 - 0xFF";
287 uint16_t u16ByteCount = 0;
288 uint8_t u8CMOSByte;
289 pHlp->pfnPrintf(pHlp, "%s\n" ,PChCMOSBank);
290 for (u16ByteCount = CMOS_BANK2_LOWER_LIMIT; u16ByteCount < CMOS_BANK2_UPPER_LIMIT; u16ByteCount++)
291 {
292 u8CMOSByte = pcbiosCmosRead(pDevIns, u16ByteCount);
293 pHlp->pfnPrintf(pHlp, "Off: 0x%02x Val: 0x%02x\n",u16ByteCount, u8CMOSByte);
294 }
295
296}
297/* -=-=-=-=-=-=- based on code from pc.c -=-=-=-=-=-=- */
298
299/**
300 * Initializes the CMOS data for one harddisk.
301 */
302static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PCPDMMEDIAGEOMETRY pLCHSGeometry)
303{
304 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
305 if (offType)
306 pcbiosCmosWrite(pDevIns, offType, 47);
307 /* Cylinders low */
308 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(pLCHSGeometry->cCylinders, 1024) & 0xff);
309 /* Cylinders high */
310 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(pLCHSGeometry->cCylinders, 1024) >> 8);
311 /* Heads */
312 pcbiosCmosWrite(pDevIns, offInfo + 2, pLCHSGeometry->cHeads);
313 /* Landing zone low */
314 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
315 /* Landing zone high */
316 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
317 /* Write precomp low */
318 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
319 /* Write precomp high */
320 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
321 /* Sectors */
322 pcbiosCmosWrite(pDevIns, offInfo + 7, pLCHSGeometry->cSectors);
323}
324
325/**
326 * Set logical CHS geometry for a hard disk
327 *
328 * @returns VBox status code.
329 * @param pBase Base interface for the device.
330 * @param pHardDisk The hard disk.
331 * @param pLCHSGeometry Where to store the geometry settings.
332 */
333static int setLogicalDiskGeometry(PPDMIBASE pBase, PPDMIBLOCKBIOS pHardDisk, PPDMMEDIAGEOMETRY pLCHSGeometry)
334{
335 PDMMEDIAGEOMETRY LCHSGeometry;
336 int rc = VINF_SUCCESS;
337
338 rc = pHardDisk->pfnGetLCHSGeometry(pHardDisk, &LCHSGeometry);
339 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
340 || LCHSGeometry.cCylinders == 0
341 || LCHSGeometry.cHeads == 0
342 || LCHSGeometry.cHeads > 255
343 || LCHSGeometry.cSectors == 0
344 || LCHSGeometry.cSectors > 63)
345 {
346 PPDMIBLOCK pBlock;
347 pBlock = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCK);
348 /* No LCHS geometry, autodetect and set. */
349 rc = biosGuessDiskLCHS(pBlock, &LCHSGeometry);
350 if (RT_FAILURE(rc))
351 {
352 /* Try if PCHS geometry works, otherwise fall back. */
353 rc = pHardDisk->pfnGetPCHSGeometry(pHardDisk, &LCHSGeometry);
354 }
355 if ( RT_FAILURE(rc)
356 || LCHSGeometry.cCylinders == 0
357 || LCHSGeometry.cCylinders > 1024
358 || LCHSGeometry.cHeads == 0
359 || LCHSGeometry.cHeads > 16
360 || LCHSGeometry.cSectors == 0
361 || LCHSGeometry.cSectors > 63)
362 {
363 uint64_t cSectors = pBlock->pfnGetSize(pBlock) / 512;
364 if (cSectors / 16 / 63 <= 1024)
365 {
366 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
367 LCHSGeometry.cHeads = 16;
368 }
369 else if (cSectors / 32 / 63 <= 1024)
370 {
371 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
372 LCHSGeometry.cHeads = 32;
373 }
374 else if (cSectors / 64 / 63 <= 1024)
375 {
376 LCHSGeometry.cCylinders = cSectors / 64 / 63;
377 LCHSGeometry.cHeads = 64;
378 }
379 else if (cSectors / 128 / 63 <= 1024)
380 {
381 LCHSGeometry.cCylinders = cSectors / 128 / 63;
382 LCHSGeometry.cHeads = 128;
383 }
384 else
385 {
386 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
387 LCHSGeometry.cHeads = 255;
388 }
389 LCHSGeometry.cSectors = 63;
390
391 }
392 rc = pHardDisk->pfnSetLCHSGeometry(pHardDisk, &LCHSGeometry);
393 if (rc == VERR_VD_IMAGE_READ_ONLY)
394 {
395 LogRel(("DevPcBios: ATA failed to update LCHS geometry, read only\n"));
396 rc = VINF_SUCCESS;
397 }
398 else if (rc == VERR_PDM_GEOMETRY_NOT_SET)
399 {
400 LogRel(("DevPcBios: ATA failed to update LCHS geometry, backend refused\n"));
401 rc = VINF_SUCCESS;
402 }
403 }
404
405 *pLCHSGeometry = LCHSGeometry;
406
407 return rc;
408}
409
410/**
411 * Get BIOS boot code from enmBootDevice in order
412 *
413 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
414 */
415static uint8_t getBiosBootCode(PDEVPCBIOS pThis, unsigned iOrder)
416{
417 switch (pThis->aenmBootDevice[iOrder])
418 {
419 case DEVPCBIOSBOOT_NONE:
420 return 0;
421 case DEVPCBIOSBOOT_FLOPPY:
422 return 1;
423 case DEVPCBIOSBOOT_HD:
424 return 2;
425 case DEVPCBIOSBOOT_DVD:
426 return 3;
427 case DEVPCBIOSBOOT_LAN:
428 return 4;
429 default:
430 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pThis->aenmBootDevice[iOrder]));
431 return 0;
432 }
433}
434
435
436/**
437 * Init complete notification.
438 * This routine will write information needed by the bios to the CMOS.
439 *
440 * @returns VBOX status code.
441 * @param pDevIns The device instance.
442 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
443 * a description of standard and non-standard CMOS registers.
444 */
445static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
446{
447 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
448 uint32_t u32;
449 unsigned i;
450 PVM pVM = PDMDevHlpGetVM(pDevIns);
451 PPDMIBLOCKBIOS apHDs[4] = {0};
452 PPDMIBLOCKBIOS apFDs[2] = {0};
453 AssertRelease(pVM);
454 LogFlow(("pcbiosInitComplete:\n"));
455
456 /*
457 * Memory sizes.
458 */
459 /* base memory. */
460 u32 = pThis->cbRam > 640 ? 640 : (uint32_t)pThis->cbRam / _1K; /* <-- this test is wrong, but it doesn't matter since we never assign less than 1MB */
461 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
462 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
463
464 /* Extended memory, up to 65MB */
465 u32 = pThis->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pThis->cbRam - _1M) / _1K;
466 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
467 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
468 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
469 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
470
471 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB
472 and below 4GB (as it can only hold 4GB+16M). We have to chop off the
473 top 2MB or it conflict with what the ACPI tables return. (Should these
474 be adjusted, we still have to chop it at 0xfffc0000 or it'll conflict
475 with the high BIOS mapping.) */
476 uint64_t const offRamHole = _4G - pThis->cbRamHole;
477 if (pThis->cbRam > 16 * _1M)
478 u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
479 else
480 u32 = 0;
481 pcbiosCmosWrite(pDevIns, 0x34, u32 & 0xff);
482 pcbiosCmosWrite(pDevIns, 0x35, u32 >> 8);
483
484 /* Bochs/VBox BIOS specific way of specifying memory above 4GB in 64KB units.
485 Bochs got these in a different location which we've already used for SATA,
486 it also lacks the last two. */
487 uint64_t c64KBAbove4GB;
488 if (pThis->cbRam <= offRamHole)
489 c64KBAbove4GB = 0;
490 else
491 {
492 c64KBAbove4GB = (pThis->cbRam - offRamHole) / _64K;
493 /* Make sure it doesn't hit the limits of the current BIOS code. */
494 AssertLogRelMsgReturn((c64KBAbove4GB >> (3 * 8)) < 255, ("%#RX64\n", c64KBAbove4GB), VERR_OUT_OF_RANGE);
495 }
496 pcbiosCmosWrite(pDevIns, 0x61, c64KBAbove4GB & 0xff);
497 pcbiosCmosWrite(pDevIns, 0x62, (c64KBAbove4GB >> 8) & 0xff);
498 pcbiosCmosWrite(pDevIns, 0x63, (c64KBAbove4GB >> 16) & 0xff);
499 pcbiosCmosWrite(pDevIns, 0x64, (c64KBAbove4GB >> 24) & 0xff);
500 pcbiosCmosWrite(pDevIns, 0x65, (c64KBAbove4GB >> 32) & 0xff);
501
502 /*
503 * Number of CPUs.
504 */
505 pcbiosCmosWrite(pDevIns, 0x60, pThis->cCpus & 0xff);
506
507 /*
508 * Bochs BIOS specifics - boot device.
509 * We do both new and old (ami-style) settings.
510 * See rombios.c line ~7215 (int19_function).
511 */
512
513 uint8_t reg3d = getBiosBootCode(pThis, 0) | (getBiosBootCode(pThis, 1) << 4);
514 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pThis, 2) << 4;
515 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
516 uint8_t reg3c = getBiosBootCode(pThis, 3) | (pThis->uBootDelay << 4);
517 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
518 pcbiosCmosWrite(pDevIns, 0x38, reg38);
519 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
520
521 /*
522 * PXE debug option.
523 */
524 pcbiosCmosWrite(pDevIns, 0x3f, pThis->u8PXEDebug);
525
526 /*
527 * Network boot device list.
528 */
529 for (i = 0; i < NET_BOOT_DEVS; ++i)
530 {
531 pcbiosCmosWrite(pDevIns, 0x82 + i * 2, pThis->au16NetBootDev[i] & 0xff);
532 pcbiosCmosWrite(pDevIns, 0x83 + i * 2, pThis->au16NetBootDev[i] >> 8);
533 }
534
535 /*
536 * Floppy drive type.
537 */
538 for (i = 0; i < RT_ELEMENTS(apFDs); i++)
539 {
540 PPDMIBASE pBase;
541 int rc = PDMR3QueryLun(pVM, pThis->pszFDDevice, 0, i, &pBase);
542 if (RT_SUCCESS(rc))
543 apFDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
544 }
545 u32 = 0;
546 if (apFDs[0])
547 switch (apFDs[0]->pfnGetType(apFDs[0]))
548 {
549 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << 4; break;
550 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << 4; break;
551 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << 4; break;
552 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << 4; break;
553 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << 4; break;
554 default: AssertFailed(); break;
555 }
556 if (apFDs[1])
557 switch (apFDs[1]->pfnGetType(apFDs[1]))
558 {
559 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1; break;
560 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2; break;
561 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3; break;
562 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4; break;
563 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5; break;
564 default: AssertFailed(); break;
565 }
566 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
567
568 /*
569 * Equipment byte.
570 */
571 u32 = !!apFDs[0] + !!apFDs[1];
572 switch (u32)
573 {
574 case 1: u32 = 0x01; break; /* floppy installed, 2 drives. */
575 default:u32 = 0; break; /* floppy not installed. */
576 }
577 u32 |= RT_BIT(1); /* math coprocessor installed */
578 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
579 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
580 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
581
582 /*
583 * Harddisks.
584 */
585 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
586 {
587 PPDMIBASE pBase;
588 int rc = PDMR3QueryLun(pVM, pThis->pszHDDevice, 0, i, &pBase);
589 if (RT_SUCCESS(rc))
590 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
591 if ( apHDs[i]
592 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
593 || !apHDs[i]->pfnIsVisible(apHDs[i])))
594 apHDs[i] = NULL;
595 if (apHDs[i])
596 {
597 PDMMEDIAGEOMETRY LCHSGeometry;
598 int rc2 = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
599 AssertRC(rc2);
600
601 if (i < 4)
602 {
603 /* Award BIOS extended drive types for first to fourth disk.
604 * Used by the BIOS for setting the logical geometry. */
605 int offType, offInfo;
606 switch (i)
607 {
608 case 0:
609 offType = 0x19;
610 offInfo = 0x1e;
611 break;
612 case 1:
613 offType = 0x1a;
614 offInfo = 0x26;
615 break;
616 case 2:
617 offType = 0x00;
618 offInfo = 0x67;
619 break;
620 case 3:
621 default:
622 offType = 0x00;
623 offInfo = 0x70;
624 break;
625 }
626 pcbiosCmosInitHardDisk(pDevIns, offType, offInfo,
627 &LCHSGeometry);
628 }
629 LogRel(("DevPcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
630 }
631 }
632
633 /* 0Fh means extended and points to 19h, 1Ah */
634 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
635 pcbiosCmosWrite(pDevIns, 0x12, u32);
636
637 /*
638 * Sata Harddisks.
639 */
640 if (pThis->pszSataDevice)
641 {
642 /* Clear pointers to IDE controller. */
643 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
644 apHDs[i] = NULL;
645
646 for (i = 0; i < RT_ELEMENTS(apHDs); i++)
647 {
648 PPDMIBASE pBase;
649 int rc = PDMR3QueryLun(pVM, pThis->pszSataDevice, 0, pThis->iSataHDLUN[i], &pBase);
650 if (RT_SUCCESS(rc))
651 apHDs[i] = PDMIBASE_QUERY_INTERFACE(pBase, PDMIBLOCKBIOS);
652 if ( apHDs[i]
653 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
654 || !apHDs[i]->pfnIsVisible(apHDs[i])))
655 apHDs[i] = NULL;
656 if (apHDs[i])
657 {
658 PDMMEDIAGEOMETRY LCHSGeometry;
659 rc = setLogicalDiskGeometry(pBase, apHDs[i], &LCHSGeometry);
660 AssertRC(rc);
661
662 if (i < 4)
663 {
664 /* Award BIOS extended drive types for first to fourth disk.
665 * Used by the BIOS for setting the logical geometry. */
666 int offInfo;
667 switch (i)
668 {
669 case 0:
670 offInfo = 0x40;
671 break;
672 case 1:
673 offInfo = 0x48;
674 break;
675 case 2:
676 offInfo = 0x50;
677 break;
678 case 3:
679 default:
680 offInfo = 0x58;
681 break;
682 }
683 pcbiosCmosInitHardDisk(pDevIns, 0x00, offInfo,
684 &LCHSGeometry);
685 }
686 LogRel(("DevPcBios: SATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
687 }
688 }
689 }
690
691 /* Calculate and store AT-style CMOS checksum. */
692 uint16_t cksum = 0;
693 for (i = 0x10; i < 0x2e; ++i)
694 cksum += pcbiosCmosRead(pDevIns, i);
695 pcbiosCmosWrite(pDevIns, 0x2e, cksum >> 8);
696 pcbiosCmosWrite(pDevIns, 0x2f, cksum & 0xff);
697
698 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
699 return VINF_SUCCESS;
700}
701
702/**
703 * Port I/O Handler for IN operations.
704 *
705 * @returns VBox status code.
706 *
707 * @param pDevIns The device instance.
708 * @param pvUser User argument - ignored.
709 * @param Port Port number used for the IN operation.
710 * @param pu32 Where to store the result.
711 * @param cb Number of bytes read.
712 */
713static DECLCALLBACK(int) pcbiosIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
714{
715 return VERR_IOM_IOPORT_UNUSED;
716}
717
718
719/**
720 * Port I/O Handler for OUT operations.
721 *
722 * @returns VBox status code.
723 *
724 * @param pDevIns The device instance.
725 * @param pvUser User argument - ignored.
726 * @param Port Port number used for the IN operation.
727 * @param u32 The value to output.
728 * @param cb The value size in bytes.
729 */
730static DECLCALLBACK(int) pcbiosIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
731{
732 /*
733 * Bochs BIOS Panic
734 */
735 if ( cb == 2
736 && ( Port == 0x400
737 || Port == 0x401))
738 {
739 Log(("pcbios: PC BIOS panic at rombios.c(%d)\n", u32));
740 AssertReleaseMsgFailed(("PC BIOS panic at rombios.c(%d)\n", u32));
741 return VERR_INTERNAL_ERROR;
742 }
743
744 /*
745 * Bochs BIOS char printing.
746 */
747 if ( cb == 1
748 && ( Port == 0x402
749 || Port == 0x403))
750 {
751 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
752 /* The raw version. */
753 switch (u32)
754 {
755 case '\r': Log2(("pcbios: <return>\n")); break;
756 case '\n': Log2(("pcbios: <newline>\n")); break;
757 case '\t': Log2(("pcbios: <tab>\n")); break;
758 default: Log2(("pcbios: %c (%02x)\n", u32, u32)); break;
759 }
760
761 /* The readable, buffered version. */
762 if (u32 == '\n' || u32 == '\r')
763 {
764 pThis->szMsg[pThis->iMsg] = '\0';
765 if (pThis->iMsg)
766 Log(("pcbios: %s\n", pThis->szMsg));
767 pThis->iMsg = 0;
768 }
769 else
770 {
771 if (pThis->iMsg >= sizeof(pThis->szMsg)-1)
772 {
773 pThis->szMsg[pThis->iMsg] = '\0';
774 Log(("pcbios: %s\n", pThis->szMsg));
775 pThis->iMsg = 0;
776 }
777 pThis->szMsg[pThis->iMsg] = (char )u32;
778 pThis->szMsg[++pThis->iMsg] = '\0';
779 }
780 return VINF_SUCCESS;
781 }
782
783 /*
784 * Bochs BIOS shutdown request.
785 */
786 if (cb == 1 && Port == 0x8900)
787 {
788 static const unsigned char szShutdown[] = "Shutdown";
789 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
790 if (u32 == szShutdown[pThis->iShutdown])
791 {
792 pThis->iShutdown++;
793 if (pThis->iShutdown == 8)
794 {
795 pThis->iShutdown = 0;
796 LogRel(("DevPcBios: 8900h shutdown request.\n"));
797 return PDMDevHlpVMPowerOff(pDevIns);
798 }
799 }
800 else
801 pThis->iShutdown = 0;
802 return VINF_SUCCESS;
803 }
804
805 /* not in use. */
806 return VINF_SUCCESS;
807}
808
809/**
810 * Reset notification.
811 *
812 * @returns VBox status.
813 * @param pDevIns The device instance data.
814 */
815static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
816{
817 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
818 LogFlow(("pcbiosReset:\n"));
819
820 if (pThis->u8IOAPIC)
821 FwCommonPlantMpsFloatPtr(pDevIns);
822
823 /*
824 * Re-shadow the LAN ROM image and make it RAM/RAM.
825 *
826 * This is normally done by the BIOS code, but since we're currently lacking
827 * the chipset support for this we do it here (and in the constructor).
828 */
829 uint32_t cPages = RT_ALIGN_64(pThis->cbLanBoot, PAGE_SIZE) >> PAGE_SHIFT;
830 RTGCPHYS GCPhys = VBOX_LANBOOT_SEG << 4;
831 while (cPages > 0)
832 {
833 uint8_t abPage[PAGE_SIZE];
834 int rc;
835
836 /* Read the (original) ROM page and write it back to the RAM page. */
837 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_ROM_WRITE_RAM);
838 AssertLogRelRC(rc);
839
840 rc = PDMDevHlpPhysRead(pDevIns, GCPhys, abPage, PAGE_SIZE);
841 AssertLogRelRC(rc);
842 if (RT_FAILURE(rc))
843 memset(abPage, 0xcc, sizeof(abPage));
844
845 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys, abPage, PAGE_SIZE);
846 AssertLogRelRC(rc);
847
848 /* Switch to the RAM/RAM mode. */
849 rc = PDMDevHlpROMProtectShadow(pDevIns, GCPhys, PAGE_SIZE, PGMROMPROT_READ_RAM_WRITE_RAM);
850 AssertLogRelRC(rc);
851
852 /* Advance */
853 GCPhys += PAGE_SIZE;
854 cPages--;
855 }
856}
857
858
859/**
860 * Destruct a device instance.
861 *
862 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
863 * resources can be freed correctly.
864 *
865 * @param pDevIns The device instance data.
866 */
867static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
868{
869 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
870 LogFlow(("pcbiosDestruct:\n"));
871 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
872
873 /*
874 * Free MM heap pointers.
875 */
876 if (pThis->pu8PcBios)
877 {
878 MMR3HeapFree(pThis->pu8PcBios);
879 pThis->pu8PcBios = NULL;
880 }
881
882 if (pThis->pszPcBiosFile)
883 {
884 MMR3HeapFree(pThis->pszPcBiosFile);
885 pThis->pszPcBiosFile = NULL;
886 }
887
888 if (pThis->pu8LanBoot)
889 {
890 MMR3HeapFree(pThis->pu8LanBoot);
891 pThis->pu8LanBoot = NULL;
892 }
893
894 if (pThis->pszLanBootFile)
895 {
896 MMR3HeapFree(pThis->pszLanBootFile);
897 pThis->pszLanBootFile = NULL;
898 }
899
900 if (pThis->pszHDDevice)
901 {
902 MMR3HeapFree(pThis->pszHDDevice);
903 pThis->pszHDDevice = NULL;
904 }
905
906 if (pThis->pszFDDevice)
907 {
908 MMR3HeapFree(pThis->pszFDDevice);
909 pThis->pszFDDevice = NULL;
910 }
911
912 if (pThis->pszSataDevice)
913 {
914 MMR3HeapFree(pThis->pszSataDevice);
915 pThis->pszSataDevice = NULL;
916 }
917
918 return VINF_SUCCESS;
919}
920
921
922/**
923 * Convert config value to DEVPCBIOSBOOT.
924 *
925 * @returns VBox status code.
926 * @param pCfg Configuration handle.
927 * @param pszParam The name of the value to read.
928 * @param penmBoot Where to store the boot method.
929 */
930static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfg, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
931{
932 char *psz;
933 int rc = CFGMR3QueryStringAlloc(pCfg, pszParam, &psz);
934 if (RT_FAILURE(rc))
935 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
936 N_("Configuration error: Querying \"%s\" as a string failed"),
937 pszParam);
938 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
939 *penmBoot = DEVPCBIOSBOOT_DVD;
940 else if (!strcmp(psz, "IDE"))
941 *penmBoot = DEVPCBIOSBOOT_HD;
942 else if (!strcmp(psz, "FLOPPY"))
943 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
944 else if (!strcmp(psz, "LAN"))
945 *penmBoot = DEVPCBIOSBOOT_LAN;
946 else if (!strcmp(psz, "NONE"))
947 *penmBoot = DEVPCBIOSBOOT_NONE;
948 else
949 {
950 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
951 N_("Configuration error: The \"%s\" value \"%s\" is unknown"),
952 pszParam, psz);
953 rc = VERR_INTERNAL_ERROR;
954 }
955 MMR3HeapFree(psz);
956 return rc;
957}
958
959/**
960 * @interface_method_impl{PDMDEVREG,pfnConstruct}
961 */
962static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
963{
964 PDEVPCBIOS pThis = PDMINS_2_DATA(pDevIns, PDEVPCBIOS);
965 int rc;
966 int cb;
967
968 Assert(iInstance == 0);
969 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
970
971 /*
972 * Validate configuration.
973 */
974 if (!CFGMR3AreValuesValid(pCfg,
975 "BootDevice0\0"
976 "BootDevice1\0"
977 "BootDevice2\0"
978 "BootDevice3\0"
979 "RamSize\0"
980 "RamHoleSize\0"
981 "HardDiskDevice\0"
982 "SataHardDiskDevice\0"
983 "SataPrimaryMasterLUN\0"
984 "SataPrimarySlaveLUN\0"
985 "SataSecondaryMasterLUN\0"
986 "SataSecondarySlaveLUN\0"
987 "FloppyDevice\0"
988 "DelayBoot\0"
989 "BiosRom\0"
990 "LanBootRom\0"
991 "PXEDebug\0"
992 "UUID\0"
993 "IOAPIC\0"
994 "NumCPUs\0"
995 "McfgBase\0"
996 "McfgLength\0"
997 "DmiBIOSFirmwareMajor\0"
998 "DmiBIOSFirmwareMinor\0"
999 "DmiBIOSReleaseDate\0"
1000 "DmiBIOSReleaseMajor\0"
1001 "DmiBIOSReleaseMinor\0"
1002 "DmiBIOSVendor\0"
1003 "DmiBIOSVersion\0"
1004 "DmiSystemFamily\0"
1005 "DmiSystemProduct\0"
1006 "DmiSystemSerial\0"
1007 "DmiSystemSKU\0"
1008 "DmiSystemUuid\0"
1009 "DmiSystemVendor\0"
1010 "DmiSystemVersion\0"
1011 "DmiBoardAssetTag\0"
1012 "DmiBoardBoardType\0"
1013 "DmiBoardLocInChass\0"
1014 "DmiBoardProduct\0"
1015 "DmiBoardSerial\0"
1016 "DmiBoardVendor\0"
1017 "DmiBoardVersion\0"
1018 "DmiChassisAssetTag\0"
1019 "DmiChassisSerial\0"
1020 "DmiChassisVendor\0"
1021 "DmiChassisVersion\0"
1022 "DmiProcManufacturer\0"
1023 "DmiProcVersion\0"
1024 "DmiOEMVBoxVer\0"
1025 "DmiOEMVBoxRev\0"
1026 "DmiUseHostInfo\0"
1027 "DmiExposeMemoryTable\0"
1028 "DmiExposeProcInf\0"
1029 ))
1030 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1031 N_("Invalid configuration for device pcbios device"));
1032
1033 /*
1034 * Init the data.
1035 */
1036 rc = CFGMR3QueryU64(pCfg, "RamSize", &pThis->cbRam);
1037 if (RT_FAILURE(rc))
1038 return PDMDEV_SET_ERROR(pDevIns, rc,
1039 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1040
1041 rc = CFGMR3QueryU32Def(pCfg, "RamHoleSize", &pThis->cbRamHole, MM_RAM_HOLE_SIZE_DEFAULT);
1042 if (RT_FAILURE(rc))
1043 return PDMDEV_SET_ERROR(pDevIns, rc,
1044 N_("Configuration error: Querying \"RamHoleSize\" as integer failed"));
1045
1046 rc = CFGMR3QueryU16Def(pCfg, "NumCPUs", &pThis->cCpus, 1);
1047 if (RT_FAILURE(rc))
1048 return PDMDEV_SET_ERROR(pDevIns, rc,
1049 N_("Configuration error: Querying \"NumCPUs\" as integer failed"));
1050
1051 rc = CFGMR3QueryU32Def(pCfg, "McfgBase", &pThis->u32McfgBase, 0);
1052 if (RT_FAILURE(rc))
1053 return PDMDEV_SET_ERROR(pDevIns, rc,
1054 N_("Configuration error: Querying \"\" as integer failed"));
1055 rc = CFGMR3QueryU32Def(pCfg, "McfgLength", &pThis->cbMcfgLength, 0);
1056 if (RT_FAILURE(rc))
1057 return PDMDEV_SET_ERROR(pDevIns, rc,
1058 N_("Configuration error: Querying \"McfgLength\" as integer failed"));
1059
1060
1061 LogRel(("[SMP] BIOS with %u CPUs\n", pThis->cCpus));
1062
1063 rc = CFGMR3QueryU8Def(pCfg, "IOAPIC", &pThis->u8IOAPIC, 1);
1064 if (RT_FAILURE (rc))
1065 return PDMDEV_SET_ERROR(pDevIns, rc,
1066 N_("Configuration error: Failed to read \"IOAPIC\""));
1067
1068 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1069 Assert(RT_ELEMENTS(s_apszBootDevices) == RT_ELEMENTS(pThis->aenmBootDevice));
1070 for (unsigned i = 0; i < RT_ELEMENTS(pThis->aenmBootDevice); i++)
1071 {
1072 rc = pcbiosBootFromCfg(pDevIns, pCfg, s_apszBootDevices[i], &pThis->aenmBootDevice[i]);
1073 if (RT_FAILURE(rc))
1074 return rc;
1075 }
1076
1077 rc = CFGMR3QueryStringAlloc(pCfg, "HardDiskDevice", &pThis->pszHDDevice);
1078 if (RT_FAILURE(rc))
1079 return PDMDEV_SET_ERROR(pDevIns, rc,
1080 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1081
1082 rc = CFGMR3QueryStringAlloc(pCfg, "FloppyDevice", &pThis->pszFDDevice);
1083 if (RT_FAILURE(rc))
1084 return PDMDEV_SET_ERROR(pDevIns, rc,
1085 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1086
1087 rc = CFGMR3QueryStringAlloc(pCfg, "SataHardDiskDevice", &pThis->pszSataDevice);
1088 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1089 pThis->pszSataDevice = NULL;
1090 else if (RT_FAILURE(rc))
1091 return PDMDEV_SET_ERROR(pDevIns, rc,
1092 N_("Configuration error: Querying \"SataHardDiskDevice\" as a string failed"));
1093
1094 if (pThis->pszSataDevice)
1095 {
1096 static const char * const s_apszSataDisks[] =
1097 { "SataPrimaryMasterLUN", "SataPrimarySlaveLUN", "SataSecondaryMasterLUN", "SataSecondarySlaveLUN" };
1098 Assert(RT_ELEMENTS(s_apszSataDisks) == RT_ELEMENTS(pThis->iSataHDLUN));
1099 for (unsigned i = 0; i < RT_ELEMENTS(pThis->iSataHDLUN); i++)
1100 {
1101 rc = CFGMR3QueryU32(pCfg, s_apszSataDisks[i], &pThis->iSataHDLUN[i]);
1102 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1103 pThis->iSataHDLUN[i] = i;
1104 else if (RT_FAILURE(rc))
1105 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1106 N_("Configuration error: Querying \"%s\" as a string failed"), s_apszSataDisks);
1107 }
1108 }
1109 /*
1110 * Register I/O Ports and PC BIOS.
1111 */
1112 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1113 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1114 if (RT_FAILURE(rc))
1115 return rc;
1116 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1117 NULL, NULL, "Bochs PC BIOS - Shutdown");
1118 if (RT_FAILURE(rc))
1119 return rc;
1120
1121 /*
1122 * Read the PXE debug logging option.
1123 */
1124 rc = CFGMR3QueryU8Def(pCfg, "PXEDebug", &pThis->u8PXEDebug, false);
1125 if (RT_FAILURE(rc))
1126 return PDMDEV_SET_ERROR(pDevIns, rc,
1127 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1128
1129 /* Clear the net boot device list. All bits set invokes old behavior,
1130 * as if no second CMOS bank was present.
1131 */
1132 memset(&pThis->au16NetBootDev, 0xff, sizeof(pThis->au16NetBootDev));
1133
1134 /*
1135 * Determine the network boot order.
1136 */
1137 PCFGMNODE pCfgNetBoot = CFGMR3GetChild(pCfg, "NetBoot");
1138 if (pCfgNetBoot == NULL)
1139 {
1140 /* Do nothing. */
1141 rc = VINF_SUCCESS;
1142 }
1143 else
1144 {
1145 PCFGMNODE pCfgNetBootDevice;
1146 uint8_t u8PciBus;
1147 uint8_t u8PciDev;
1148 uint8_t u8PciFn;
1149 uint16_t u16BusDevFn;
1150 char szIndex[] = "?";
1151
1152 Assert(pCfgNetBoot);
1153 for (unsigned i = 0; i < NET_BOOT_DEVS; ++i)
1154 {
1155 szIndex[0] = '0' + i;
1156 pCfgNetBootDevice = CFGMR3GetChild(pCfgNetBoot, szIndex);
1157
1158 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIBusNo", &u8PciBus);
1159 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1160 {
1161 /* Do nothing and stop iterating. */
1162 rc = VINF_SUCCESS;
1163 break;
1164 }
1165 else if (RT_FAILURE(rc))
1166 return PDMDEV_SET_ERROR(pDevIns, rc,
1167 N_("Configuration error: Querying \"Netboot/x/PCIBusNo\" as integer failed"));
1168 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIDeviceNo", &u8PciDev);
1169 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1170 {
1171 /* Do nothing and stop iterating. */
1172 rc = VINF_SUCCESS;
1173 break;
1174 }
1175 else if (RT_FAILURE(rc))
1176 return PDMDEV_SET_ERROR(pDevIns, rc,
1177 N_("Configuration error: Querying \"Netboot/x/PCIDeviceNo\" as integer failed"));
1178 rc = CFGMR3QueryU8(pCfgNetBootDevice, "PCIFunctionNo", &u8PciFn);
1179 if (rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT)
1180 {
1181 /* Do nothing and stop iterating. */
1182 rc = VINF_SUCCESS;
1183 break;
1184 }
1185 else if (RT_FAILURE(rc))
1186 return PDMDEV_SET_ERROR(pDevIns, rc,
1187 N_("Configuration error: Querying \"Netboot/x/PCIFunctionNo\" as integer failed"));
1188 u16BusDevFn = (((uint16_t)u8PciBus) << 8) | ((u8PciDev & 0x1F) << 3) | (u8PciFn & 0x7);
1189 pThis->au16NetBootDev[i] = u16BusDevFn;
1190 }
1191 }
1192
1193 /*
1194 * Get the system BIOS ROM file name.
1195 */
1196 rc = CFGMR3QueryStringAlloc(pCfg, "BiosRom", &pThis->pszPcBiosFile);
1197 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1198 {
1199 pThis->pszPcBiosFile = NULL;
1200 rc = VINF_SUCCESS;
1201 }
1202 else if (RT_FAILURE(rc))
1203 return PDMDEV_SET_ERROR(pDevIns, rc,
1204 N_("Configuration error: Querying \"BiosRom\" as a string failed"));
1205 else if (!*pThis->pszPcBiosFile)
1206 {
1207 MMR3HeapFree(pThis->pszPcBiosFile);
1208 pThis->pszPcBiosFile = NULL;
1209 }
1210
1211 const uint8_t *pu8PcBiosBinary;
1212 uint32_t cbPcBiosBinary;
1213 if (pThis->pszPcBiosFile)
1214 {
1215 /*
1216 * Load the BIOS ROM.
1217 */
1218 RTFILE hFilePcBios;
1219 rc = RTFileOpen(&hFilePcBios, pThis->pszPcBiosFile,
1220 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1221 if (RT_SUCCESS(rc))
1222 {
1223 /* Figure the size and check restrictions. */
1224 uint64_t cbPcBios;
1225 rc = RTFileGetSize(hFilePcBios, &cbPcBios);
1226 if (RT_SUCCESS(rc))
1227 {
1228 pThis->cbPcBios = (uint32_t)cbPcBios;
1229 if ( RT_ALIGN(pThis->cbPcBios, _64K) == pThis->cbPcBios
1230 && pThis->cbPcBios == cbPcBios
1231 && pThis->cbPcBios <= 32 * _64K
1232 && pThis->cbPcBios >= _64K)
1233 {
1234 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, pThis->cbPcBios);
1235 if (pThis->pu8PcBios)
1236 {
1237 rc = RTFileRead(hFilePcBios, pThis->pu8PcBios, pThis->cbPcBios, NULL);
1238 if (RT_FAILURE(rc))
1239 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1240 N_("Error reading the BIOS image ('%s)"), pThis->pszPcBiosFile);
1241 }
1242 else
1243 rc = PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1244 N_("Failed to allocate %#x bytes for loading the BIOS image"),
1245 pThis->cbPcBios);
1246 }
1247 else
1248 rc = PDMDevHlpVMSetError(pDevIns, VERR_OUT_OF_RANGE, RT_SRC_POS,
1249 N_("Invalid system BIOS file size ('%s'): %#llx (%llu)"),
1250 pThis->pszPcBiosFile, cbPcBios, cbPcBios);
1251 }
1252 else
1253 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1254 N_("Failed to query the system BIOS file size ('%s')"),
1255 pThis->pszPcBiosFile);
1256 RTFileClose(hFilePcBios);
1257 }
1258 else
1259 rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1260 N_("Failed to open system BIOS file '%s'"), pThis->pszPcBiosFile);
1261 if (RT_FAILURE(rc))
1262 return rc;
1263
1264 LogRel(("DevPcBios: Using BIOS ROM '%s' with a size of %#x bytes\n", pThis->pszPcBiosFile, pThis->cbPcBios));
1265 }
1266 else
1267 {
1268 /*
1269 * Use the embedded BIOS ROM image.
1270 */
1271 pThis->pu8PcBios = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, g_cbPcBiosBinary);
1272 if (pThis->pu8PcBios)
1273 {
1274 pThis->cbPcBios = g_cbPcBiosBinary;
1275 memcpy(pThis->pu8PcBios, g_abPcBiosBinary, pThis->cbPcBios);
1276 }
1277 else
1278 return PDMDevHlpVMSetError(pDevIns, VERR_NO_MEMORY, RT_SRC_POS,
1279 N_("Failed to allocate %#x bytes for loading the embedded BIOS image"),
1280 g_cbPcBiosBinary);
1281 }
1282 pu8PcBiosBinary = pThis->pu8PcBios;
1283 cbPcBiosBinary = pThis->cbPcBios;
1284
1285 /*
1286 * Query the machine's UUID for SMBIOS/DMI use.
1287 */
1288 RTUUID uuid;
1289 rc = CFGMR3QueryBytes(pCfg, "UUID", &uuid, sizeof(uuid));
1290 if (RT_FAILURE(rc))
1291 return PDMDEV_SET_ERROR(pDevIns, rc,
1292 N_("Configuration error: Querying \"UUID\" failed"));
1293
1294 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1295 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1296 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1297 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1298 uint16_t cbDmiTables = 0;
1299 rc = FwCommonPlantDMITable(pDevIns, pThis->au8DMIPage, VBOX_DMI_TABLE_SIZE,
1300 &uuid, pCfg, pThis->cCpus, &cbDmiTables);
1301 if (RT_FAILURE(rc))
1302 return rc;
1303
1304 /* If the DMI table is located at the expected place, patch the DMI table length and the checksum. */
1305 if ( pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x00] == '_'
1306 && pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x01] == 'D'
1307 && pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x02] == 'M'
1308 && pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x03] == 'I'
1309 && pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x04] == '_')
1310 {
1311 *(uint16_t*)&pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x06] = cbDmiTables;
1312 uint8_t u8Sum = 0;
1313 for (unsigned i = 0; i < pThis->cbPcBios; i++)
1314 if (i != VBOX_DMI_TABLE_OFFSET + 0x05)
1315 u8Sum += pThis->pu8PcBios[i];
1316 pThis->pu8PcBios[VBOX_DMI_TABLE_OFFSET + 0x05] = -u8Sum;
1317 }
1318
1319 if (pThis->u8IOAPIC)
1320 FwCommonPlantMpsTable(pDevIns, pThis->au8DMIPage + VBOX_DMI_TABLE_SIZE,
1321 _4K - VBOX_DMI_TABLE_SIZE, pThis->cCpus);
1322
1323 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, _4K, pThis->au8DMIPage, _4K,
1324 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
1325 if (RT_FAILURE(rc))
1326 return rc;
1327
1328 /*
1329 * Map the BIOS into memory.
1330 * There are two mappings:
1331 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1332 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1333 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1334 */
1335 AssertReleaseMsg(cbPcBiosBinary >= _64K, ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1336 AssertReleaseMsg(RT_ALIGN_Z(cbPcBiosBinary, _64K) == cbPcBiosBinary,
1337 ("cbPcBiosBinary=%#x\n", cbPcBiosBinary));
1338 cb = RT_MIN(cbPcBiosBinary, 128 * _1K); /* Effectively either 64 or 128K. */
1339 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &pu8PcBiosBinary[cbPcBiosBinary - cb], cb,
1340 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xfffff");
1341 if (RT_FAILURE(rc))
1342 return rc;
1343 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-(int32_t)cbPcBiosBinary, cbPcBiosBinary, pu8PcBiosBinary, cbPcBiosBinary,
1344 PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "PC BIOS - 0xffffffff");
1345 if (RT_FAILURE(rc))
1346 return rc;
1347
1348 /*
1349 * Get the LAN boot ROM file name.
1350 */
1351 rc = CFGMR3QueryStringAlloc(pCfg, "LanBootRom", &pThis->pszLanBootFile);
1352 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1353 {
1354 pThis->pszLanBootFile = NULL;
1355 rc = VINF_SUCCESS;
1356 }
1357 else if (RT_FAILURE(rc))
1358 return PDMDEV_SET_ERROR(pDevIns, rc,
1359 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1360 else if (!*pThis->pszLanBootFile)
1361 {
1362 MMR3HeapFree(pThis->pszLanBootFile);
1363 pThis->pszLanBootFile = NULL;
1364 }
1365
1366 uint64_t cbFileLanBoot;
1367 const uint8_t *pu8LanBootBinary = NULL;
1368 uint64_t cbLanBootBinary;
1369
1370 /*
1371 * Determine the LAN boot ROM size, open specified ROM file in the process.
1372 */
1373 RTFILE FileLanBoot = NIL_RTFILE;
1374 if (pThis->pszLanBootFile)
1375 {
1376 rc = RTFileOpen(&FileLanBoot, pThis->pszLanBootFile,
1377 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1378 if (RT_SUCCESS(rc))
1379 {
1380 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1381 if (RT_SUCCESS(rc))
1382 {
1383 if (cbFileLanBoot > _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff))
1384 rc = VERR_TOO_MUCH_DATA;
1385 }
1386 }
1387 if (RT_FAILURE(rc))
1388 {
1389 /*
1390 * Ignore failure and fall back to the built-in LAN boot ROM.
1391 */
1392 LogRel(("DevPcBios: Failed to open LAN boot ROM file '%s', rc=%Rrc!\n", pThis->pszLanBootFile, rc));
1393 RTFileClose(FileLanBoot);
1394 FileLanBoot = NIL_RTFILE;
1395 MMR3HeapFree(pThis->pszLanBootFile);
1396 pThis->pszLanBootFile = NULL;
1397 }
1398 }
1399
1400 /*
1401 * Get the LAN boot ROM data.
1402 */
1403 if (pThis->pszLanBootFile)
1404 {
1405 LogRel(("DevPcBios: Using LAN ROM '%s' with a size of %#x bytes\n", pThis->pszLanBootFile, cbFileLanBoot));
1406 /*
1407 * Allocate buffer for the LAN boot ROM data.
1408 */
1409 pThis->pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAllocZ(pDevIns, cbFileLanBoot);
1410 if (pThis->pu8LanBoot)
1411 {
1412 rc = RTFileRead(FileLanBoot, pThis->pu8LanBoot, cbFileLanBoot, NULL);
1413 if (RT_FAILURE(rc))
1414 {
1415 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Rrc\n", cbFileLanBoot, rc));
1416 MMR3HeapFree(pThis->pu8LanBoot);
1417 pThis->pu8LanBoot = NULL;
1418 }
1419 rc = VINF_SUCCESS;
1420 }
1421 else
1422 rc = VERR_NO_MEMORY;
1423 }
1424 else
1425 pThis->pu8LanBoot = NULL;
1426
1427 /* cleanup */
1428 if (FileLanBoot != NIL_RTFILE)
1429 RTFileClose(FileLanBoot);
1430
1431 /* If we were unable to get the data from file for whatever reason, fall
1432 * back to the built-in LAN boot ROM image.
1433 */
1434 if (pThis->pu8LanBoot == NULL)
1435 {
1436#ifdef VBOX_WITH_PXE_ROM
1437 pu8LanBootBinary = g_abNetBiosBinary;
1438 cbLanBootBinary = g_cbNetBiosBinary;
1439#endif
1440 }
1441 else
1442 {
1443 pu8LanBootBinary = pThis->pu8LanBoot;
1444 cbLanBootBinary = cbFileLanBoot;
1445 }
1446
1447 /*
1448 * Map the Network Boot ROM into memory.
1449 * Currently there is a fixed mapping: 0x000e2000 to 0x000effff contains
1450 * the (up to) 56 kb ROM image. The mapping size is fixed to trouble with
1451 * the saved state (in PGM).
1452 */
1453 if (pu8LanBootBinary)
1454 {
1455 pThis->cbLanBoot = cbLanBootBinary;
1456
1457 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4,
1458 RT_MAX(cbLanBootBinary, _64K - (VBOX_LANBOOT_SEG << 4 & 0xffff)),
1459 pu8LanBootBinary, cbLanBootBinary,
1460 PGMPHYS_ROM_FLAGS_SHADOWED, "Net Boot ROM");
1461 AssertRCReturn(rc, rc);
1462 }
1463
1464 rc = CFGMR3QueryU8Def(pCfg, "DelayBoot", &pThis->uBootDelay, 0);
1465 if (RT_FAILURE(rc))
1466 return PDMDEV_SET_ERROR(pDevIns, rc,
1467 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1468 if (pThis->uBootDelay > 15)
1469 pThis->uBootDelay = 15;
1470
1471 /*
1472 * Register debugger info callback.
1473 */
1474 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos", "Display CMOS Bank 1 Info.. "
1475 "'cmos'. No argument.", CMOSBankInfo);
1476 PDMDevHlpDBGFInfoRegister(pDevIns, "cmos2", "Display CMOS Bank 2 Info.. "
1477 "'cmos2'. No argument", CMOSBank2Info);
1478
1479 /*
1480 * Call reset plant tables and shadow the PXE ROM.
1481 */
1482 pcbiosReset(pDevIns);
1483
1484 return VINF_SUCCESS;
1485}
1486
1487
1488/**
1489 * The device registration structure.
1490 */
1491const PDMDEVREG g_DevicePcBios =
1492{
1493 /* u32Version */
1494 PDM_DEVREG_VERSION,
1495 /* szName */
1496 "pcbios",
1497 /* szRCMod */
1498 "",
1499 /* szR0Mod */
1500 "",
1501 /* pszDescription */
1502 "PC BIOS Device",
1503 /* fFlags */
1504 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1505 /* fClass */
1506 PDM_DEVREG_CLASS_ARCH_BIOS,
1507 /* cMaxInstances */
1508 1,
1509 /* cbInstance */
1510 sizeof(DEVPCBIOS),
1511 /* pfnConstruct */
1512 pcbiosConstruct,
1513 /* pfnDestruct */
1514 pcbiosDestruct,
1515 /* pfnRelocate */
1516 NULL,
1517 /* pfnIOCtl */
1518 NULL,
1519 /* pfnPowerOn */
1520 NULL,
1521 /* pfnReset */
1522 pcbiosReset,
1523 /* pfnSuspend */
1524 NULL,
1525 /* pfnResume */
1526 NULL,
1527 /* pfnAttach */
1528 NULL,
1529 /* pfnDetach */
1530 NULL,
1531 /* pfnQueryInterface. */
1532 NULL,
1533 /* pfnInitComplete. */
1534 pcbiosInitComplete,
1535 /* pfnPowerOff */
1536 NULL,
1537 /* pfnSoftReset */
1538 NULL,
1539 /* u32VersionEnd */
1540 PDM_DEVREG_VERSION
1541};
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