VirtualBox

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

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

*: scm cleanup run.

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