VirtualBox

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

Last change on this file since 26028 was 26001, checked in by vboxsync, 15 years ago

PDM,*: Redid the PDM structure versions. Check the instance and helper versions in every device and driver constructor.

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