VirtualBox

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

Last change on this file since 59248 was 59248, checked in by vboxsync, 9 years ago

Storage: Get rid of the block driver and merge the the little extra functionality it had into the VD driver. Enables us to get rid of PDMIBLOCK which is basically a subset of PDMIMEDIA and makes changes to the latter interface tedious because it had to be replicated in the former. (bugref:4114)

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