VirtualBox

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

Last change on this file since 53148 was 53148, checked in by vboxsync, 10 years ago

Port a part of r95116 (disk encryption changes in 4.3) which was forgotten to trunk. Fixes starting a VM with an encrypted VHD image

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