VirtualBox

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

Last change on this file since 6291 was 6291, checked in by vboxsync, 17 years ago

Big virtual disk changeset containing several modifications

  • remove the always buggy translation setting and replace it with two sets of geometries, physical and logical
  • complete vmdk creation (fixed/dynamic variants, both split in 2G chunks and single file)
  • implemented VBoxHDD-new generic snapshot support, i.e. diff image creation and image merging (completely untested, I'm pretty sure there are bugs)
  • assorted changes which generalize the VBoxHDD-new interfaces (both externally and internally)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 53.6 KB
Line 
1/* $Id: DevPcBios.cpp 6291 2008-01-09 10:57:05Z vboxsync $ */
2/** @file
3 * PC BIOS Device.
4 */
5
6/*
7 * Copyright (C) 2006-2008 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DEV_PC_BIOS
22#include <VBox/pdmdev.h>
23#include <VBox/mm.h>
24
25#include <VBox/log.h>
26#include <iprt/assert.h>
27#include <iprt/alloc.h>
28#include <iprt/file.h>
29#include <iprt/string.h>
30#include <VBox/err.h>
31#include <VBox/param.h>
32
33#include "Builtins.h"
34#include "Builtins2.h"
35#include "DevPcBios.h"
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41
42/**
43 * The boot device.
44 */
45typedef enum DEVPCBIOSBOOT
46{
47 DEVPCBIOSBOOT_NONE,
48 DEVPCBIOSBOOT_FLOPPY,
49 DEVPCBIOSBOOT_HD,
50 DEVPCBIOSBOOT_DVD,
51 DEVPCBIOSBOOT_LAN
52} DEVPCBIOSBOOT;
53
54/**
55 * PC Bios instance data structure.
56 */
57typedef struct DEVPCBIOS
58{
59 /** Pointer back to the device instance. */
60 PPDMDEVINS pDevIns;
61
62 /** Boot devices (ordered). */
63 DEVPCBIOSBOOT aenmBootDevice[4];
64 /** Ram Size (in bytes). */
65 uint64_t cbRam;
66 /** Bochs shutdown index. */
67 uint32_t iShutdown;
68 /** Floppy device. */
69 char *pszFDDevice;
70 /** Harddisk device. */
71 char *pszHDDevice;
72 /** Bios message buffer. */
73 char szMsg[256];
74 /** Bios message buffer index. */
75 uint32_t iMsg;
76 /** Current logo data offset. */
77 uint32_t offLogoData;
78 /** Use built-in or loaded logo. */
79 bool fDefaultLogo;
80 /** The size of the BIOS logo data. */
81 uint32_t cbLogo;
82 /** The BIOS logo data. */
83 uint8_t *pu8Logo;
84 /** The name of the logo file. */
85 char *pszLogoFile;
86 /** The LAN boot ROM data. */
87 uint8_t *pu8LanBoot;
88 /** The name of the LAN boot ROM file. */
89 char *pszLanBootFile;
90 /** The DMI tables. */
91 uint8_t au8DMIPage[0x1000];
92 /** The boot countdown (in seconds). */
93 uint8_t uBootDelay;
94 /** I/O-APIC enabled? */
95 uint8_t u8IOAPIC;
96 /** PXE debug logging enabled? */
97 uint8_t u8PXEDebug;
98} DEVPCBIOS, *PDEVPCBIOS;
99
100
101/** @todo The logo stuff shared with the BIOS goes into a header of course. */
102
103/**
104 * PC Bios logo data structure.
105 */
106#pragma pack(2) /* pack(2) is important! (seems that bios compiled with pack(2)...) */
107typedef struct LOGOHDR
108{
109 /** Signature (LOGO_HDR_MAGIC/0x66BB). */
110 uint16_t u16Signature;
111 /** Fade in - boolean. */
112 uint8_t u8FadeIn;
113 /** Fade out - boolean. */
114 uint8_t u8FadeOut;
115 /** Logo time (msec). */
116 uint16_t u16LogoMillies;
117 /** Show setup - boolean. */
118 uint8_t u8ShowBootMenu;
119 /** Logo file size. */
120 uint32_t cbLogo;
121} LOGOHDR, *PLOGOHDR;
122#pragma pack()
123
124/** PC port for Logo I/O */
125#define LOGO_IO_PORT 0x506
126
127/** The value of the LOGOHDR::u16Signature field. */
128#define LOGO_HDR_MAGIC 0x66BB
129
130/** The value which will switch you the default logo. */
131#define LOGO_DEFAULT_LOGO 0xFFFF
132
133/** The maximal logo size in bytes. (640x480x8bpp + header/palette) */
134#define LOGO_MAX_SIZE 640 * 480 + 0x442
135
136#pragma pack(1)
137
138/** DMI header */
139typedef struct DMIHDR
140{
141 uint8_t u8Type;
142 uint8_t u8Length;
143 uint16_t u16Handle;
144} *PDMIHDR;
145AssertCompileSize(DMIHDR, 4);
146
147/** DMI BIOS information */
148typedef struct DMIBIOSINF
149{
150 DMIHDR header;
151 uint8_t u8Vendor;
152 uint8_t u8Version;
153 uint16_t u16Start;
154 uint8_t u8Release;
155 uint8_t u8ROMSize;
156 uint64_t u64Characteristics;
157 uint8_t u8CharacteristicsByte1;
158 uint8_t u8CharacteristicsByte2;
159} *PDMIBIOSINF;
160AssertCompileSize(DMIBIOSINF, 0x14);
161
162/** DMI system information */
163typedef struct DMISYSTEMINF
164{
165 DMIHDR header;
166 uint8_t u8Manufacturer;
167 uint8_t u8ProductName;
168 uint8_t u8Version;
169 uint8_t u8SerialNumber;
170 uint8_t au8Uuid[16];
171 uint8_t u8WakeupType;
172 uint8_t u8SKUNumber;
173 uint8_t u8Family;
174} *PDMISYSTEMINF;
175AssertCompileSize(DMISYSTEMINF, 0x1b);
176
177/** MPS floating pointer structure */
178typedef struct MPSFLOATPTR
179{
180 uint8_t au8Signature[4];
181 uint32_t u32MPSAddr;
182 uint8_t u8Length;
183 uint8_t u8SpecRev;
184 uint8_t u8Checksum;
185 uint8_t au8Feature[5];
186} *PMPSFLOATPTR;
187AssertCompileSize(MPSFLOATPTR, 16);
188
189/** MPS config table header */
190typedef struct MPSCFGTBLHEADER
191{
192 uint8_t au8Signature[4];
193 uint16_t u16Length;
194 uint8_t u8SpecRev;
195 uint8_t u8Checksum;
196 uint8_t au8OemId[8];
197 uint8_t au8ProductId[12];
198 uint32_t u32OemTablePtr;
199 uint16_t u16OemTableSize;
200 uint16_t u16EntryCount;
201 uint32_t u32AddrLocalApic;
202 uint16_t u16ExtTableLength;
203 uint8_t u8ExtTableChecksxum;
204 uint8_t u8Reserved;
205} *PMPSCFGTBLHEADER;
206AssertCompileSize(MPSCFGTBLHEADER, 0x2c);
207
208/** MPS processor entry */
209typedef struct MPSPROCENTRY
210{
211 uint8_t u8EntryType;
212 uint8_t u8LocalApicId;
213 uint8_t u8LocalApicVersion;
214 uint8_t u8CPUFlags;
215 uint32_t u32CPUSignature;
216 uint32_t u32CPUFeatureFlags;
217 uint32_t u32Reserved[2];
218} *PMPSPROCENTRY;
219AssertCompileSize(MPSPROCENTRY, 20);
220
221/** MPS bus entry */
222typedef struct MPSBUSENTRY
223{
224 uint8_t u8EntryType;
225 uint8_t u8BusId;
226 uint8_t au8BusTypeStr[6];
227} *PMPSBUSENTRY;
228AssertCompileSize(MPSBUSENTRY, 8);
229
230/** MPS I/O-APIC entry */
231typedef struct MPSIOAPICENTRY
232{
233 uint8_t u8EntryType;
234 uint8_t u8Id;
235 uint8_t u8Version;
236 uint8_t u8Flags;
237 uint32_t u32Addr;
238} *PMPSIOAPICENTRY;
239AssertCompileSize(MPSIOAPICENTRY, 8);
240
241/** MPS I/O-Interrupt entry */
242typedef struct MPSIOINTERRUPTENTRY
243{
244 uint8_t u8EntryType;
245 uint8_t u8Type;
246 uint16_t u16Flags;
247 uint8_t u8SrcBusId;
248 uint8_t u8SrcBusIrq;
249 uint8_t u8DstIOAPICId;
250 uint8_t u8DstIOAPICInt;
251} *PMPSIOIRQENTRY;
252AssertCompileSize(MPSIOINTERRUPTENTRY, 8);
253
254#pragma pack()
255
256
257/*******************************************************************************
258* Internal Functions *
259*******************************************************************************/
260__BEGIN_DECLS
261
262static DECLCALLBACK(int) logoIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
263static DECLCALLBACK(int) logoIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
264
265__END_DECLS
266
267/* Attempt to guess the LCHS disk geometry from the MS-DOS master boot
268 * record (partition table). */
269static int biosGuessDiskLCHS(PPDMIBLOCK pBlock, PPDMMEDIAGEOMETRY pLCHSGeometry)
270{
271 uint8_t aMBR[512], *p;
272 int rc;
273 uint32_t iEndHead, iEndSector, cLCHSCylinders, cLCHSHeads, cLCHSSectors;
274
275 if (!pBlock)
276 return VERR_INVALID_PARAMETER;
277 rc = pBlock->pfnRead(pBlock, 0, aMBR, sizeof(aMBR));
278 if (VBOX_FAILURE(rc))
279 return rc;
280 /* Test MBR magic number. */
281 if (aMBR[510] != 0x55 || aMBR[511] != 0xaa)
282 return VERR_INVALID_PARAMETER;
283 for (uint32_t i = 0; i < 4; i++)
284 {
285 /* Figure out the start of a partition table entry. */
286 p = &aMBR[0x1be + i * 16];
287 iEndHead = p[5];
288 iEndSector = p[6] & 63;
289 if ((p[12] | p[13] | p[14] | p[15]) && iEndSector & iEndHead)
290 {
291 /* Assumption: partition terminates on a cylinder boundary. */
292 cLCHSHeads = iEndHead + 1;
293 cLCHSSectors = iEndSector;
294 cLCHSCylinders = pBlock->pfnGetSize(pBlock) / (512 * cLCHSHeads * cLCHSSectors);
295 if (cLCHSCylinders >= 1)
296 {
297 pLCHSGeometry->cCylinders = cLCHSCylinders;
298 pLCHSGeometry->cHeads = cLCHSHeads;
299 pLCHSGeometry->cSectors = cLCHSSectors;
300 Log(("%s: LCHS=%d %d %d\n", __FUNCTION__, cLCHSCylinders, cLCHSHeads, cLCHSSectors));
301 return VINF_SUCCESS;
302 }
303 }
304 }
305 return VERR_INVALID_PARAMETER;
306}
307
308
309/**
310 * Write to CMOS memory.
311 * This is used by the init complete code.
312 */
313static void pcbiosCmosWrite(PPDMDEVINS pDevIns, int off, uint32_t u32Val)
314{
315 Assert(off < 128);
316 Assert(u32Val < 256);
317
318#if 1
319 int rc = PDMDevHlpCMOSWrite(pDevIns, off, u32Val);
320 AssertRC(rc);
321#else
322 PVM pVM = PDMDevHlpGetVM(pDevIns);
323 IOMIOPortWrite(pVM, 0x70, off, 1);
324 IOMIOPortWrite(pVM, 0x71, u32Val, 1);
325 IOMIOPortWrite(pVM, 0x70, 0, 1);
326#endif
327}
328
329/* -=-=-=-=-=-=- based on code from pc.c -=-=-=-=-=-=- */
330
331/**
332 * Initializes the CMOS data for one harddisk.
333 */
334static void pcbiosCmosInitHardDisk(PPDMDEVINS pDevIns, int offType, int offInfo, PPDMIBLOCKBIOS pBlockBios)
335{
336 PDMMEDIAGEOMETRY LCHSGeometry;
337 int rc = pBlockBios->pfnGetLCHSGeometry(pBlockBios, &LCHSGeometry);
338 if (VBOX_SUCCESS(rc))
339 {
340 Log2(("%s: offInfo=%#x: LCHS=%d/%d/%d\n", __FUNCTION__, offInfo, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
341 if (offType)
342 pcbiosCmosWrite(pDevIns, offType, 48);
343 /* Cylinders low */
344 pcbiosCmosWrite(pDevIns, offInfo + 0, RT_MIN(LCHSGeometry.cCylinders, 1024) & 0xff);
345 /* Cylinders high */
346 pcbiosCmosWrite(pDevIns, offInfo + 1, RT_MIN(LCHSGeometry.cCylinders, 1024) >> 8);
347 /* Heads */
348 pcbiosCmosWrite(pDevIns, offInfo + 2, LCHSGeometry.cHeads);
349 /* Landing zone low */
350 pcbiosCmosWrite(pDevIns, offInfo + 3, 0xff);
351 /* Landing zone high */
352 pcbiosCmosWrite(pDevIns, offInfo + 4, 0xff);
353 /* Write precomp low */
354 pcbiosCmosWrite(pDevIns, offInfo + 5, 0xff);
355 /* Write precomp high */
356 pcbiosCmosWrite(pDevIns, offInfo + 6, 0xff);
357 /* Sectors */
358 pcbiosCmosWrite(pDevIns, offInfo + 7, LCHSGeometry.cSectors);
359 return;
360 }
361 if (offType)
362 pcbiosCmosWrite(pDevIns, offType, 0);
363}
364
365
366/**
367 * Get BIOS boot code from enmBootDevice in order
368 *
369 * @todo r=bird: This is a rather silly function since the conversion is 1:1.
370 */
371static uint8_t getBiosBootCode(PDEVPCBIOS pData, unsigned iOrder)
372{
373 switch (pData->aenmBootDevice[iOrder])
374 {
375 case DEVPCBIOSBOOT_NONE:
376 return 0;
377 case DEVPCBIOSBOOT_FLOPPY:
378 return 1;
379 case DEVPCBIOSBOOT_HD:
380 return 2;
381 case DEVPCBIOSBOOT_DVD:
382 return 3;
383 case DEVPCBIOSBOOT_LAN:
384 return 4;
385 default:
386 AssertMsgFailed(("aenmBootDevice[%d]=%d\n", iOrder, pData->aenmBootDevice[iOrder]));
387 return 0;
388 }
389}
390
391
392/**
393 * Init complete notification.
394 * This routine will write information needed by the bios to the CMOS.
395 *
396 * @returns VBOX status code.
397 * @param pDevIns The device instance.
398 * @see http://www.brl.ntt.co.jp/people/takehiko/interrupt/CMOS.LST.txt for
399 * a description of standard and non-standard CMOS registers.
400 */
401static DECLCALLBACK(int) pcbiosInitComplete(PPDMDEVINS pDevIns)
402{
403 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
404 uint32_t u32;
405 unsigned i;
406 PVM pVM = PDMDevHlpGetVM(pDevIns);
407 PPDMIBLOCKBIOS apHDs[4] = {0};
408 PPDMIBLOCKBIOS apFDs[2] = {0};
409 AssertRelease(pVM);
410 LogFlow(("pcbiosInitComplete:\n"));
411
412 /*
413 * Memory sizes.
414 */
415 /* base memory. */
416 u32 = pData->cbRam > 640 ? 640 : (uint32_t)pData->cbRam / _1K;
417 pcbiosCmosWrite(pDevIns, 0x15, u32 & 0xff); /* 15h - Base Memory in K, Low Byte */
418 pcbiosCmosWrite(pDevIns, 0x16, u32 >> 8); /* 16h - Base Memory in K, High Byte */
419
420 /* Extended memory, up to 65MB */
421 u32 = pData->cbRam >= 65 * _1M ? 0xffff : ((uint32_t)pData->cbRam - _1M) / _1K;
422 pcbiosCmosWrite(pDevIns, 0x17, u32 & 0xff); /* 17h - Extended Memory in K, Low Byte */
423 pcbiosCmosWrite(pDevIns, 0x18, u32 >> 8); /* 18h - Extended Memory in K, High Byte */
424 pcbiosCmosWrite(pDevIns, 0x30, u32 & 0xff); /* 30h - Extended Memory in K, Low Byte */
425 pcbiosCmosWrite(pDevIns, 0x31, u32 >> 8); /* 31h - Extended Memory in K, High Byte */
426
427 /* Bochs BIOS specific? Anyway, it's the amount of memory above 16MB */
428 if (pData->cbRam > 16 * _1M)
429 {
430 u32 = (uint32_t)( (pData->cbRam - 16 * _1M) / _64K );
431 u32 = RT_MIN(u32, 0xffff);
432 }
433 else
434 u32 = 0;
435 pcbiosCmosWrite(pDevIns, 0x34, u32 & 0xff);
436 pcbiosCmosWrite(pDevIns, 0x35, u32 >> 8);
437
438 /*
439 * Bochs BIOS specifics - boot device.
440 * We do both new and old (ami-style) settings.
441 * See rombios.c line ~7215 (int19_function).
442 */
443
444 uint8_t reg3d = getBiosBootCode(pData, 0) | (getBiosBootCode(pData, 1) << 4);
445 uint8_t reg38 = /* pcbiosCmosRead(pDevIns, 0x38) | */ getBiosBootCode(pData, 2) << 4;
446 /* This is an extension. Bochs BIOS normally supports only 3 boot devices. */
447 uint8_t reg3c = getBiosBootCode(pData, 3) | (pData->uBootDelay << 4);
448 pcbiosCmosWrite(pDevIns, 0x3d, reg3d);
449 pcbiosCmosWrite(pDevIns, 0x38, reg38);
450 pcbiosCmosWrite(pDevIns, 0x3c, reg3c);
451
452 /*
453 * PXE debug option.
454 */
455 pcbiosCmosWrite(pDevIns, 0x3f, pData->u8PXEDebug);
456
457 /*
458 * Floppy drive type.
459 */
460 for (i = 0; i < ELEMENTS(apFDs); i++)
461 {
462 PPDMIBASE pBase;
463 int rc = PDMR3QueryLun(pVM, pData->pszFDDevice, 0, i, &pBase);
464 if (VBOX_SUCCESS(rc))
465 apFDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
466 }
467 u32 = 0;
468 if (apFDs[0])
469 switch (apFDs[0]->pfnGetType(apFDs[0]))
470 {
471 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1 << 4; break;
472 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2 << 4; break;
473 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3 << 4; break;
474 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4 << 4; break;
475 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5 << 4; break;
476 default: AssertFailed(); break;
477 }
478 if (apFDs[1])
479 switch (apFDs[1]->pfnGetType(apFDs[1]))
480 {
481 case PDMBLOCKTYPE_FLOPPY_360: u32 |= 1; break;
482 case PDMBLOCKTYPE_FLOPPY_1_20: u32 |= 2; break;
483 case PDMBLOCKTYPE_FLOPPY_720: u32 |= 3; break;
484 case PDMBLOCKTYPE_FLOPPY_1_44: u32 |= 4; break;
485 case PDMBLOCKTYPE_FLOPPY_2_88: u32 |= 5; break;
486 default: AssertFailed(); break;
487 }
488 pcbiosCmosWrite(pDevIns, 0x10, u32); /* 10h - Floppy Drive Type */
489
490 /*
491 * Equipment byte.
492 */
493 u32 = !!apFDs[0] + !!apFDs[1];
494 switch (u32)
495 {
496 case 1: u32 = 0x01; break; /* floppy installed, 2 drives. */
497 default:u32 = 0; break; /* floppy not installed. */
498 }
499 u32 |= RT_BIT(1); /* math coprocessor installed */
500 u32 |= RT_BIT(2); /* keyboard enabled (or mouse?) */
501 u32 |= RT_BIT(3); /* display enabled (monitory type is 0, i.e. vga) */
502 pcbiosCmosWrite(pDevIns, 0x14, u32); /* 14h - Equipment Byte */
503
504 /*
505 * Harddisks.
506 */
507 for (i = 0; i < ELEMENTS(apHDs); i++)
508 {
509 PPDMIBASE pBase;
510 int rc = PDMR3QueryLun(pVM, pData->pszHDDevice, 0, i, &pBase);
511 if (VBOX_SUCCESS(rc))
512 apHDs[i] = (PPDMIBLOCKBIOS)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK_BIOS);
513 if ( apHDs[i]
514 && ( apHDs[i]->pfnGetType(apHDs[i]) != PDMBLOCKTYPE_HARD_DISK
515 || !apHDs[i]->pfnIsVisible(apHDs[i])))
516 apHDs[i] = NULL;
517 if (apHDs[i])
518 {
519 PDMMEDIAGEOMETRY LCHSGeometry;
520 int rc = apHDs[i]->pfnGetLCHSGeometry(apHDs[i], &LCHSGeometry);
521 if ( rc == VERR_PDM_GEOMETRY_NOT_SET
522 || LCHSGeometry.cCylinders == 0
523 || LCHSGeometry.cCylinders > 1024
524 || LCHSGeometry.cHeads == 0
525 || LCHSGeometry.cHeads > 255
526 || LCHSGeometry.cSectors == 0
527 || LCHSGeometry.cSectors > 63)
528 {
529 PPDMIBLOCK pBlock;
530 pBlock = (PPDMIBLOCK)pBase->pfnQueryInterface(pBase, PDMINTERFACE_BLOCK);
531 /* No LCHS geometry, autodetect and set. */
532 rc = biosGuessDiskLCHS(pBlock, &LCHSGeometry);
533 if (VBOX_FAILURE(rc))
534 {
535 /* Try if PCHS geometry works, otherwise fall back. */
536 rc = apHDs[i]->pfnGetPCHSGeometry(apHDs[i], &LCHSGeometry);
537 }
538 if ( VBOX_FAILURE(rc)
539 || LCHSGeometry.cCylinders == 0
540 || LCHSGeometry.cCylinders > 1024
541 || LCHSGeometry.cHeads == 0
542 || LCHSGeometry.cHeads > 16
543 || LCHSGeometry.cSectors == 0
544 || LCHSGeometry.cSectors > 63)
545 {
546 uint64_t cSectors = pBlock->pfnGetSize(pBlock) / 512;
547 if (cSectors / 16 / 63 <= 1024)
548 {
549 LCHSGeometry.cCylinders = RT_MAX(cSectors / 16 / 63, 1);
550 LCHSGeometry.cHeads = 16;
551 }
552 else if (cSectors / 32 / 63 <= 1024)
553 {
554 LCHSGeometry.cCylinders = RT_MAX(cSectors / 32 / 63, 1);
555 LCHSGeometry.cHeads = 32;
556 }
557 else if (cSectors / 64 / 63 <= 1024)
558 {
559 LCHSGeometry.cCylinders = cSectors / 64 / 63;
560 LCHSGeometry.cHeads = 64;
561 }
562 else if (cSectors / 128 / 63 <= 1024)
563 {
564 LCHSGeometry.cCylinders = cSectors / 128 / 63;
565 LCHSGeometry.cHeads = 128;
566 }
567 else
568 {
569 LCHSGeometry.cCylinders = RT_MIN(cSectors / 255 / 63, 1024);
570 LCHSGeometry.cHeads = 255;
571 }
572 LCHSGeometry.cSectors = 63;
573
574 }
575 rc = apHDs[i]->pfnSetLCHSGeometry(apHDs[i], &LCHSGeometry);
576 AssertRC(rc);
577 }
578 LogRel(("DevPcBios: ATA LUN#%d LCHS=%u/%u/%u\n", i, LCHSGeometry.cCylinders, LCHSGeometry.cHeads, LCHSGeometry.cSectors));
579 pcbiosCmosWrite(pDevIns, 0x40 + i * 4, LCHSGeometry.cCylinders & 0xff);
580 pcbiosCmosWrite(pDevIns, 0x41 + i * 4, LCHSGeometry.cCylinders >> 8);
581 pcbiosCmosWrite(pDevIns, 0x42 + i * 4, LCHSGeometry.cHeads & 0xff);
582 pcbiosCmosWrite(pDevIns, 0x43 + i * 4, LCHSGeometry.cSectors & 0xff);
583 }
584 }
585
586 /* 0Fh means extended and points to 19h, 1Ah */
587 u32 = (apHDs[0] ? 0xf0 : 0) | (apHDs[1] ? 0x0f : 0);
588 pcbiosCmosWrite(pDevIns, 0x12, u32);
589 /* Award BIOS extended drive types for first and second disk, and
590 * extended drive types for third and fourth disk. Used by the BIOS. */
591 if (apHDs[0])
592 pcbiosCmosInitHardDisk(pDevIns, 0x19, 0x1e, apHDs[0]);
593 if (apHDs[1])
594 pcbiosCmosInitHardDisk(pDevIns, 0x1a, 0x26, apHDs[1]);
595 if (apHDs[2])
596 pcbiosCmosInitHardDisk(pDevIns, 0x00, 0x67, apHDs[2]);
597 if (apHDs[3])
598 pcbiosCmosInitHardDisk(pDevIns, 0x00, 0x70, apHDs[3]);
599
600 LogFlow(("%s: returns VINF_SUCCESS\n", __FUNCTION__));
601 return VINF_SUCCESS;
602}
603
604
605/**
606 * Port I/O Handler for IN operations.
607 *
608 * @returns VBox status code.
609 *
610 * @param pDevIns The device instance.
611 * @param pvUser User argument - ignored.
612 * @param Port Port number used for the IN operation.
613 * @param pu32 Where to store the result.
614 * @param cb Number of bytes read.
615 */
616static DECLCALLBACK(int) pcbiosIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
617{
618 NOREF(pDevIns);
619 NOREF(pvUser);
620 NOREF(Port);
621 NOREF(pu32);
622 NOREF(cb);
623 return VERR_IOM_IOPORT_UNUSED;
624}
625
626
627/**
628 * Port I/O Handler for OUT operations.
629 *
630 * @returns VBox status code.
631 *
632 * @param pDevIns The device instance.
633 * @param pvUser User argument - ignored.
634 * @param Port Port number used for the IN operation.
635 * @param u32 The value to output.
636 * @param cb The value size in bytes.
637 */
638static DECLCALLBACK(int) pcbiosIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
639{
640 /*
641 * Bochs BIOS Panic
642 */
643 if ( cb == 2
644 && ( Port == 0x400
645 || Port == 0x401))
646 {
647 Log(("pcbios: PC BIOS panic at rombios.c(%d)\n", u32));
648 AssertReleaseMsgFailed(("PC BIOS panic at rombios.c(%d)\n", u32));
649 return VERR_INTERNAL_ERROR;
650 }
651
652 /*
653 * Bochs BIOS char printing.
654 */
655 if ( cb == 1
656 && ( Port == 0x402
657 || Port == 0x403))
658 {
659 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
660 /* The raw version. */
661 switch (u32)
662 {
663 case '\r': Log2(("pcbios: <return>\n")); break;
664 case '\n': Log2(("pcbios: <newline>\n")); break;
665 case '\t': Log2(("pcbios: <tab>\n")); break;
666 default: Log2(("pcbios: %c (%02x)\n", u32, u32)); break;
667 }
668
669 /* The readable, buffered version. */
670 if (u32 == '\n' || u32 == '\r')
671 {
672 pData->szMsg[pData->iMsg] = '\0';
673 if (pData->iMsg)
674 Log(("pcbios: %s\n", pData->szMsg));
675 pData->iMsg = 0;
676 }
677 else
678 {
679 if (pData->iMsg >= sizeof(pData->szMsg)-1)
680 {
681 pData->szMsg[pData->iMsg] = '\0';
682 Log(("pcbios: %s\n", pData->szMsg));
683 pData->iMsg = 0;
684 }
685 pData->szMsg[pData->iMsg] = (char )u32;
686 pData->szMsg[++pData->iMsg] = '\0';
687 }
688 return VINF_SUCCESS;
689 }
690
691 /*
692 * Bochs BIOS shutdown request.
693 */
694 if (cb == 1 && Port == 0x8900)
695 {
696 static const unsigned char szShutdown[] = "Shutdown";
697 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
698 if (u32 == szShutdown[pData->iShutdown])
699 {
700 pData->iShutdown++;
701 if (pData->iShutdown == 8)
702 {
703 pData->iShutdown = 0;
704 LogRel(("8900h shutdown request.\n"));
705 return PDMDevHlpVMPowerOff(pDevIns);
706 }
707 }
708 else
709 pData->iShutdown = 0;
710 return VINF_SUCCESS;
711 }
712
713 /* not in use. */
714 return VINF_SUCCESS;
715}
716
717
718/**
719 * LOGO port I/O Handler for IN operations.
720 *
721 * @returns VBox status code.
722 *
723 * @param pDevIns The device instance.
724 * @param pvUser User argument - ignored.
725 * @param uPort Port number used for the IN operation.
726 * @param pu32 Where to store the result.
727 * @param cb Number of bytes read.
728 */
729static DECLCALLBACK(int) logoIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
730{
731 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
732 Log(("logoIOPortRead call Port:%x pu32:%x cb:%d (%d)\n", Port, pu32, cb, pData->offLogoData));
733
734 PRTUINT64U p;
735 if (pData->fDefaultLogo)
736 {
737 /*
738 * Default bios logo.
739 */
740 if (pData->offLogoData + cb > g_cbPcDefBiosLogo)
741 {
742 Log(("logoIOPortRead: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
743 pData->offLogoData, pData->offLogoData, g_cbPcDefBiosLogo, g_cbPcDefBiosLogo));
744 return VINF_SUCCESS;
745 }
746 p = (PRTUINT64U)&g_abPcDefBiosLogo[pData->offLogoData];
747 }
748 else
749 {
750 /*
751 * Custom logo.
752 */
753 if (pData->offLogoData + cb > pData->cbLogo)
754 {
755 Log(("logoIOPortRead: Requested address is out of Logo data!!! offLogoData=%#x(%d) cbLogo=%#x(%d)\n",
756 pData->offLogoData, pData->offLogoData, pData->cbLogo, pData->cbLogo));
757 return VINF_SUCCESS;
758 }
759 p = (PRTUINT64U)&pData->pu8Logo[pData->offLogoData];
760 }
761
762 switch (cb)
763 {
764 case 1: *pu32 = p->au8[0]; break;
765 case 2: *pu32 = p->au16[0]; break;
766 case 4: *pu32 = p->au32[0]; break;
767 //case 8: *pu32 = p->au64[0]; break;
768 default: AssertFailed(); break;
769 }
770 Log(("logoIOPortRead: LogoOffset=%#x(%d) cb=%#x %.*Vhxs\n", pData->offLogoData, pData->offLogoData, cb, cb, pu32));
771 pData->offLogoData += cb;
772
773 return VINF_SUCCESS;
774}
775
776
777/**
778 * LOGO port I/O Handler for OUT operations.
779 *
780 * @returns VBox status code.
781 *
782 * @param pDevIns The device instance.
783 * @param pvUser User argument - ignored.
784 * @param uPort Port number used for the IN operation.
785 * @param u32 The value to output.
786 * @param cb The value size in bytes.
787 */
788static DECLCALLBACK(int) logoIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
789{
790 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
791 Log(("logoIOPortWrite: Port=%x cb=%d u32=%#04x (byte)\n", Port, cb, u32));
792
793 /* Switch to default BIOS logo or change logo data offset. */
794 if ( cb == 2
795 && u32 == LOGO_DEFAULT_LOGO)
796 {
797 pData->fDefaultLogo = true;
798 pData->offLogoData = 0;
799 }
800 else
801 pData->offLogoData = u32;
802
803 return VINF_SUCCESS;
804}
805
806
807/**
808 * Construct the DMI table.
809 *
810 * @param table pointer to DMI table.
811 */
812#define STRCPY(p, s) do { memcpy (p, s, sizeof(s)); p += sizeof(s); } while (0)
813static void pcbiosPlantDMITable(uint8_t *pTable, PRTUUID puuid)
814{
815 char *pszStr = (char*)pTable;
816 int iStrNr;
817
818 PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
819 pszStr = (char*)(pBIOSInf+1);
820 iStrNr = 1;
821 pBIOSInf->header.u8Type = 0; /* BIOS Information */
822 pBIOSInf->header.u8Length = sizeof(*pBIOSInf);
823 pBIOSInf->header.u16Handle = 0x0000;
824 pBIOSInf->u8Vendor = iStrNr++;
825 STRCPY(pszStr, "innotek GmbH");
826 pBIOSInf->u8Version = iStrNr++;
827 STRCPY(pszStr, "VirtualBox");
828 pBIOSInf->u16Start = 0xE000;
829 pBIOSInf->u8Release = iStrNr++;
830 STRCPY(pszStr, "12/01/2006");
831 pBIOSInf->u8ROMSize = 1; /* 128K */
832 pBIOSInf->u64Characteristics = RT_BIT(4) /* ISA is supported */
833 | RT_BIT(7) /* PCI is supported */
834 | RT_BIT(15) /* Boot from CD is supported */
835 | RT_BIT(16) /* Selectable Boot is supported */
836 | RT_BIT(27) /* Int 9h, 8042 Keyboard services supported */
837 | RT_BIT(30) /* Int 10h, CGA/Mono Video Services supported */
838 /* any more?? */
839 ;
840 pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0) /* ACPI is supported */
841 /* any more?? */
842 ;
843 pBIOSInf->u8CharacteristicsByte2 = 0
844 /* any more?? */
845 ;
846 *pszStr++ = '\0';
847
848
849 PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
850 pszStr = (char*)(pSystemInf+1);
851 iStrNr = 1;
852 pSystemInf->header.u8Type = 1; /* System Information */
853 pSystemInf->header.u8Length = sizeof(*pSystemInf);
854 pSystemInf->header.u16Handle = 0x0001;
855 pSystemInf->u8Manufacturer = iStrNr++;
856 STRCPY(pszStr, "innotek GmbH");
857 pSystemInf->u8ProductName = iStrNr++;
858 STRCPY(pszStr, "VirtualBox");
859 pSystemInf->u8Version = iStrNr++;
860 STRCPY(pszStr, "1.2");
861 pSystemInf->u8SerialNumber = iStrNr++;
862 STRCPY(pszStr, "0");
863 memcpy(pSystemInf->au8Uuid, puuid, sizeof(RTUUID));
864 pSystemInf->u8WakeupType = 6; /* Power Switch */
865 pSystemInf->u8SKUNumber = 0;
866 pSystemInf->u8Family = iStrNr++;
867 STRCPY(pszStr, "Virtual Machine");
868 *pszStr++ = '\0';
869
870 AssertMsg(pszStr - (char*)pTable == VBOX_DMI_TABLE_SIZE,
871 ("VBOX_DMI_TABLE_SIZE=%d, actual DMI table size is %d",
872 VBOX_DMI_TABLE_SIZE, pszStr - (char*)pTable));
873}
874AssertCompile(VBOX_DMI_TABLE_ENTR == 2);
875
876
877/**
878 * Calculate a simple checksum for the MPS table.
879 *
880 * @param data data
881 * @param len size of data
882 */
883static uint8_t pcbiosChecksum(const uint8_t * const au8Data, uint32_t u32Length)
884{
885 uint8_t u8Sum = 0;
886 for (size_t i = 0; i < u32Length; ++i)
887 u8Sum += au8Data[i];
888 return -u8Sum;
889}
890
891
892/**
893 * Construct the MPS table. Only applicable if IOAPIC is active.
894 *
895 * @param pDevIns The device instance data.
896 * @param addr physical address in guest memory.
897 */
898static void pcbiosPlantMPStable(PPDMDEVINS pDevIns, uint8_t *pTable)
899{
900 /* configuration table */
901 PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
902 memcpy(pCfgTab->au8Signature, "PCMP", 4);
903 pCfgTab->u8SpecRev = 4; /* 1.4 */
904 memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
905 memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
906 pCfgTab->u32OemTablePtr = 0;
907 pCfgTab->u16OemTableSize = 0;
908 pCfgTab->u16EntryCount = 1 /* Processor */
909 + 1 /* ISA Bus */
910 + 1 /* I/O-APIC */
911 + 16 /* Interrupts */;
912 pCfgTab->u32AddrLocalApic = 0xfee00000;
913 pCfgTab->u16ExtTableLength = 0;
914 pCfgTab->u8ExtTableChecksxum = 0;
915 pCfgTab->u8Reserved = 0;
916
917 uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
918 uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
919 uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
920 PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
921 if (u32Eax >= 1)
922 {
923 PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
924 u32CPUSignature = u32Eax & 0xfff;
925 /* Local APIC will be enabled later so override it here. Since we provide
926 * an MP table we have an IOAPIC and therefore a Local APIC. */
927 u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
928 }
929
930 /* one processor so far */
931 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
932 pProcEntry->u8EntryType = 0; /* processor entry */
933 pProcEntry->u8LocalApicId = 0;
934 pProcEntry->u8LocalApicVersion = 0x11;
935 pProcEntry->u8CPUFlags = 2 /* bootstrap processor */ | 1 /* enabled */;
936 pProcEntry->u32CPUSignature = u32CPUSignature;
937 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
938 pProcEntry->u32Reserved[0] =
939 pProcEntry->u32Reserved[1] = 0;
940
941 /* ISA bus */
942 PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)(pProcEntry+1);
943 pBusEntry->u8EntryType = 1; /* bus entry */
944 pBusEntry->u8BusId = 0; /* this ID is referenced by the interrupt entries */
945 memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
946
947 /* PCI bus? */
948
949 /* I/O-APIC.
950 * MP spec: "The configuration table contains one or more entries for I/O APICs.
951 * ... At least one I/O APIC must be enabled." */
952 PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1);
953 pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
954 pIOAPICEntry->u8Id = 1; /* this ID is referenced by the interrupt entries */
955 pIOAPICEntry->u8Version = 0x11;
956 pIOAPICEntry->u8Flags = 1 /* enable */;
957 pIOAPICEntry->u32Addr = 0xfec00000;
958
959 PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
960 for (int i = 0; i < 16; i++, pIrqEntry++)
961 {
962 pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
963 pIrqEntry->u8Type = 0; /* INT, vectored interrupt */
964 pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
965 trigger mode = conforms to bus */
966 pIrqEntry->u8SrcBusId = 0; /* ISA bus */
967 pIrqEntry->u8SrcBusIrq = i;
968 pIrqEntry->u8DstIOAPICId = 1;
969 pIrqEntry->u8DstIOAPICInt = i;
970 }
971
972 pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
973 pCfgTab->u8Checksum = pcbiosChecksum(pTable, pCfgTab->u16Length);
974
975 AssertMsg(pCfgTab->u16Length < 0x1000 - 0x100,
976 ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
977 pCfgTab->u16Length, 0x1000-0x100));
978
979 MPSFLOATPTR floatPtr;
980 floatPtr.au8Signature[0] = '_';
981 floatPtr.au8Signature[1] = 'M';
982 floatPtr.au8Signature[2] = 'P';
983 floatPtr.au8Signature[3] = '_';
984 floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE;
985 floatPtr.u8Length = 1; /* structure size in paragraphs */
986 floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
987 floatPtr.u8Checksum = 0;
988 floatPtr.au8Feature[0] = 0;
989 floatPtr.au8Feature[1] = 0;
990 floatPtr.au8Feature[2] = 0;
991 floatPtr.au8Feature[3] = 0;
992 floatPtr.au8Feature[4] = 0;
993 floatPtr.u8Checksum = pcbiosChecksum((uint8_t*)&floatPtr, 16);
994 PDMDevHlpPhysWrite (pDevIns, 0x9fff0, &floatPtr, 16);
995}
996
997
998/**
999 * Reset notification.
1000 *
1001 * @returns VBox status.
1002 * @param pDevIns The device instance data.
1003 */
1004static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
1005{
1006 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
1007 LogFlow(("pcbiosReset:\n"));
1008
1009 pData->fDefaultLogo = false;
1010 pData->offLogoData = 0;
1011 /** @todo Should we perhaps do pcbiosInitComplete() on reset? */
1012
1013#if 1
1014 /*
1015 * Paranoia: Check that the BIOS ROM hasn't changed.
1016 */
1017 uint8_t abBuf[PAGE_SIZE];
1018
1019 /* the low ROM mapping. */
1020 unsigned cb = RT_MIN(g_cbPcBiosBinary, 128 * _1K);
1021 RTGCPHYS GCPhys = 0x00100000 - cb;
1022 const uint8_t *pbVirgin = &g_abPcBiosBinary[g_cbPcBiosBinary - cb];
1023 while (GCPhys < 0x00100000)
1024 {
1025 PDMDevHlpPhysRead(pDevIns, GCPhys, abBuf, PAGE_SIZE);
1026 if (memcmp(abBuf, pbVirgin, PAGE_SIZE))
1027 {
1028 LogRel(("low ROM mismatch! GCPhys=%VGp - Ignore if you've loaded an old saved state with an different VirtualBox version.\n", GCPhys));
1029 for (unsigned off = 0; off < PAGE_SIZE; off++)
1030 if (abBuf[off] != pbVirgin[off])
1031 LogRel(("%VGp: %02x expected %02x\n", GCPhys + off, abBuf[off], pbVirgin[off]));
1032 AssertFailed();
1033 }
1034
1035 /* next page */
1036 GCPhys += PAGE_SIZE;
1037 pbVirgin += PAGE_SIZE;
1038 }
1039
1040 /* the high ROM mapping. */
1041 GCPhys = UINT32_C(0xffffffff) - (g_cbPcBiosBinary - 1);
1042 pbVirgin = &g_abPcBiosBinary[0];
1043 while (pbVirgin < &g_abPcBiosBinary[g_cbPcBiosBinary])
1044 {
1045 PDMDevHlpPhysRead(pDevIns, GCPhys, abBuf, PAGE_SIZE);
1046 if (memcmp(abBuf, pbVirgin, PAGE_SIZE))
1047 {
1048 LogRel(("high ROM mismatch! GCPhys=%VGp - Ignore if you've loaded an old saved state with an different VirtualBox version.\n", GCPhys));
1049 for (unsigned off = 0; off < PAGE_SIZE; off++)
1050 if (abBuf[off] != pbVirgin[off])
1051 LogRel(("%VGp: %02x expected %02x\n", GCPhys + off, abBuf[off], pbVirgin[off]));
1052 AssertFailed();
1053 }
1054
1055 /* next page */
1056 GCPhys += PAGE_SIZE;
1057 pbVirgin += PAGE_SIZE;
1058 }
1059#endif
1060
1061 if (pData->u8IOAPIC)
1062 pcbiosPlantMPStable(pDevIns, pData->au8DMIPage + 0x100);
1063}
1064
1065
1066/**
1067 * Destruct a device instance.
1068 *
1069 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1070 * resources can be freed correctly.
1071 *
1072 * @param pDevIns The device instance data.
1073 */
1074static DECLCALLBACK(int) pcbiosDestruct(PPDMDEVINS pDevIns)
1075{
1076 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
1077 LogFlow(("pcbiosDestruct:\n"));
1078
1079 /*
1080 * Free MM heap pointers.
1081 */
1082 if (pData->pu8LanBoot)
1083 {
1084 MMR3HeapFree(pData->pu8LanBoot);
1085 pData->pu8LanBoot = NULL;
1086 }
1087
1088 if (pData->pszLanBootFile)
1089 {
1090 MMR3HeapFree(pData->pszLanBootFile);
1091 pData->pszLanBootFile = NULL;
1092 }
1093
1094 if (pData->pu8Logo)
1095 {
1096 MMR3HeapFree(pData->pu8Logo);
1097 pData->pu8Logo = NULL;
1098 }
1099
1100 if (pData->pszLogoFile)
1101 {
1102 MMR3HeapFree(pData->pszLogoFile);
1103 pData->pszLogoFile = NULL;
1104 }
1105
1106 return VINF_SUCCESS;
1107}
1108
1109
1110/**
1111 * Convert config value to DEVPCBIOSBOOT.
1112 *
1113 * @returns VBox status code.
1114 * @param pCfgHandle Configuration handle.
1115 * @param pszParam The name of the value to read.
1116 * @param penmBoot Where to store the boot method.
1117 */
1118static int pcbiosBootFromCfg(PPDMDEVINS pDevIns, PCFGMNODE pCfgHandle, const char *pszParam, DEVPCBIOSBOOT *penmBoot)
1119{
1120 char *psz;
1121 int rc = CFGMR3QueryStringAlloc(pCfgHandle, pszParam, &psz);
1122 if (VBOX_FAILURE(rc))
1123 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1124 N_("Configuration error: Querying \"%s\" as a string failed"),
1125 pszParam);
1126 if (!strcmp(psz, "DVD") || !strcmp(psz, "CDROM"))
1127 *penmBoot = DEVPCBIOSBOOT_DVD;
1128 else if (!strcmp(psz, "IDE"))
1129 *penmBoot = DEVPCBIOSBOOT_HD;
1130 else if (!strcmp(psz, "FLOPPY"))
1131 *penmBoot = DEVPCBIOSBOOT_FLOPPY;
1132 else if (!strcmp(psz, "LAN"))
1133 *penmBoot = DEVPCBIOSBOOT_LAN;
1134 else if (!strcmp(psz, "NONE"))
1135 *penmBoot = DEVPCBIOSBOOT_NONE;
1136 else
1137 {
1138 PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
1139 N_("Configuration error: The \"%s\" value \"%s\" is unknown.\n"),
1140 pszParam, psz);
1141 rc = VERR_INTERNAL_ERROR;
1142 }
1143 MMR3HeapFree(psz);
1144 return rc;
1145}
1146
1147/**
1148 * Construct a device instance for a VM.
1149 *
1150 * @returns VBox status.
1151 * @param pDevIns The device instance data.
1152 * If the registration structure is needed, pDevIns->pDevReg points to it.
1153 * @param iInstance Instance number. Use this to figure out which registers and such to use.
1154 * The device number is also found in pDevIns->iInstance, but since it's
1155 * likely to be freqently used PDM passes it as parameter.
1156 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
1157 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
1158 * iInstance it's expected to be used a bit in this function.
1159 */
1160static DECLCALLBACK(int) pcbiosConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
1161{
1162 unsigned i;
1163 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
1164 int rc;
1165 int cb;
1166
1167 Assert(iInstance == 0);
1168
1169 /*
1170 * Validate configuration.
1171 */
1172 if (!CFGMR3AreValuesValid(pCfgHandle,
1173 "BootDevice0\0"
1174 "BootDevice1\0"
1175 "BootDevice2\0"
1176 "BootDevice3\0"
1177 "RamSize\0"
1178 "HardDiskDevice\0"
1179 "FloppyDevice\0"
1180 "FadeIn\0"
1181 "FadeOut\0"
1182 "LogoTime\0"
1183 "LogoFile\0"
1184 "ShowBootMenu\0"
1185 "DelayBoot\0"
1186 "LanBootRom\0"
1187 "PXEDebug\0"
1188 "UUID\0"
1189 "IOAPIC\0"))
1190 return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
1191 N_("Invalid configuraton for device pcbios device"));
1192
1193 /*
1194 * Init the data.
1195 */
1196 rc = CFGMR3QueryU64(pCfgHandle, "RamSize", &pData->cbRam);
1197 if (VBOX_FAILURE(rc))
1198 return PDMDEV_SET_ERROR(pDevIns, rc,
1199 N_("Configuration error: Querying \"RamSize\" as integer failed"));
1200
1201 rc = CFGMR3QueryU8 (pCfgHandle, "IOAPIC", &pData->u8IOAPIC);
1202 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1203 pData->u8IOAPIC = 1;
1204 else if (VBOX_FAILURE (rc))
1205 return PDMDEV_SET_ERROR(pDevIns, rc,
1206 N_("Configuration error: Failed to read \"IOAPIC\"."));
1207
1208 static const char * const s_apszBootDevices[] = { "BootDevice0", "BootDevice1", "BootDevice2", "BootDevice3" };
1209 Assert(ELEMENTS(s_apszBootDevices) == ELEMENTS(pData->aenmBootDevice));
1210 for (i = 0; i < ELEMENTS(pData->aenmBootDevice); i++)
1211 {
1212 rc = pcbiosBootFromCfg(pDevIns, pCfgHandle, s_apszBootDevices[i], &pData->aenmBootDevice[i]);
1213 if (VBOX_FAILURE(rc))
1214 return rc;
1215 }
1216
1217 rc = CFGMR3QueryStringAlloc(pCfgHandle, "HardDiskDevice", &pData->pszHDDevice);
1218 if (VBOX_FAILURE(rc))
1219 return PDMDEV_SET_ERROR(pDevIns, rc,
1220 N_("Configuration error: Querying \"HardDiskDevice\" as a string failed"));
1221
1222 rc = CFGMR3QueryStringAlloc(pCfgHandle, "FloppyDevice", &pData->pszFDDevice);
1223 if (VBOX_FAILURE(rc))
1224 return PDMDEV_SET_ERROR(pDevIns, rc,
1225 N_("Configuration error: Querying \"FloppyDevice\" as a string failed"));
1226
1227 /*
1228 * Register I/O Ports and PC BIOS.
1229 */
1230 rc = PDMDevHlpIOPortRegister(pDevIns, 0x400, 4, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1231 NULL, NULL, "Bochs PC BIOS - Panic & Debug");
1232 if (VBOX_FAILURE(rc))
1233 return rc;
1234 rc = PDMDevHlpIOPortRegister(pDevIns, 0x8900, 1, NULL, pcbiosIOPortWrite, pcbiosIOPortRead,
1235 NULL, NULL, "Bochs PC BIOS - Shutdown");
1236 if (VBOX_FAILURE(rc))
1237 return rc;
1238
1239 /*
1240 * Query the machine's UUID for SMBIOS/DMI use.
1241 */
1242 RTUUID uuid;
1243 rc = CFGMR3QueryBytes(pCfgHandle, "UUID", &uuid, sizeof(uuid));
1244 if (VBOX_FAILURE(rc))
1245 return PDMDEV_SET_ERROR(pDevIns, rc,
1246 N_("Configuration error: Querying \"UUID\" failed"));
1247
1248
1249 /* Convert the UUID to network byte order. Not entirely straightforward as parts are MSB already... */
1250 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
1251 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
1252 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
1253 pcbiosPlantDMITable(pData->au8DMIPage, &uuid);
1254 if (pData->u8IOAPIC)
1255 pcbiosPlantMPStable(pDevIns, pData->au8DMIPage + 0x100);
1256
1257 rc = PDMDevHlpROMRegister(pDevIns, VBOX_DMI_TABLE_BASE, 0x1000, pData->au8DMIPage, false /* fShadow */, "DMI tables");
1258 if (VBOX_FAILURE(rc))
1259 return rc;
1260
1261 /*
1262 * Read the PXE debug logging option.
1263 */
1264 rc = CFGMR3QueryU8(pCfgHandle, "PXEDebug", &pData->u8PXEDebug);
1265 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1266 pData->u8PXEDebug = 0;
1267 else if (VBOX_FAILURE(rc))
1268 return PDMDEV_SET_ERROR(pDevIns, rc,
1269 N_("Configuration error: Querying \"PXEDebug\" as integer failed"));
1270
1271 /*
1272 * Map the BIOS into memory.
1273 * There are two mappings:
1274 * 1. 0x000e0000 to 0x000fffff contains the last 128 kb of the bios.
1275 * The bios code might be 64 kb in size, and will then start at 0xf0000.
1276 * 2. 0xfffxxxxx to 0xffffffff contains the entire bios.
1277 */
1278 AssertReleaseMsg(g_cbPcBiosBinary >= _64K, ("g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary));
1279 AssertReleaseMsg(RT_ALIGN_Z(g_cbPcBiosBinary, _64K) == g_cbPcBiosBinary,
1280 ("g_cbPcBiosBinary=%#x\n", g_cbPcBiosBinary));
1281 cb = RT_MIN(g_cbPcBiosBinary, 128 * _1K);
1282 rc = PDMDevHlpROMRegister(pDevIns, 0x00100000 - cb, cb, &g_abPcBiosBinary[g_cbPcBiosBinary - cb],
1283 false /* fShadow */, "PC BIOS - 0xfffff");
1284 if (VBOX_FAILURE(rc))
1285 return rc;
1286 rc = PDMDevHlpROMRegister(pDevIns, (uint32_t)-g_cbPcBiosBinary, g_cbPcBiosBinary, &g_abPcBiosBinary[0],
1287 false /* fShadow */, "PC BIOS - 0xffffffff");
1288 if (VBOX_FAILURE(rc))
1289 return rc;
1290
1291 /*
1292 * Register the BIOS Logo port
1293 */
1294 rc = PDMDevHlpIOPortRegister(pDevIns, LOGO_IO_PORT, 1, NULL, logoIOPortWrite, logoIOPortRead, NULL, NULL, "PC BIOS - Logo port");
1295 if (VBOX_FAILURE(rc))
1296 return rc;
1297
1298 /*
1299 * Construct the logo header.
1300 */
1301 LOGOHDR LogoHdr = { LOGO_HDR_MAGIC, 0, 0, 0, 0, 0 };
1302
1303 rc = CFGMR3QueryU8(pCfgHandle, "FadeIn", &LogoHdr.u8FadeIn);
1304 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1305 LogoHdr.u8FadeIn = 1;
1306 else if (VBOX_FAILURE(rc))
1307 return PDMDEV_SET_ERROR(pDevIns, rc,
1308 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
1309
1310 rc = CFGMR3QueryU8(pCfgHandle, "FadeOut", &LogoHdr.u8FadeOut);
1311 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1312 LogoHdr.u8FadeOut = 1;
1313 else if (VBOX_FAILURE(rc))
1314 return PDMDEV_SET_ERROR(pDevIns, rc,
1315 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
1316
1317 rc = CFGMR3QueryU16(pCfgHandle, "LogoTime", &LogoHdr.u16LogoMillies);
1318 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1319 LogoHdr.u16LogoMillies = 1;
1320 else if (VBOX_FAILURE(rc))
1321 return PDMDEV_SET_ERROR(pDevIns, rc,
1322 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
1323
1324 rc = CFGMR3QueryU8(pCfgHandle, "ShowBootMenu", &LogoHdr.u8ShowBootMenu);
1325 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1326 LogoHdr.u8ShowBootMenu = 0;
1327 else if (VBOX_FAILURE(rc))
1328 return PDMDEV_SET_ERROR(pDevIns, rc,
1329 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
1330
1331 /*
1332 * Get the Logo file name.
1333 */
1334 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LogoFile", &pData->pszLogoFile);
1335 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1336 pData->pszLogoFile = NULL;
1337 else if (VBOX_FAILURE(rc))
1338 return PDMDEV_SET_ERROR(pDevIns, rc,
1339 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
1340 else if (!*pData->pszLogoFile)
1341 {
1342 MMR3HeapFree(pData->pszLogoFile);
1343 pData->pszLogoFile = NULL;
1344 }
1345
1346 /*
1347 * Determine the logo size, open any specified logo file in the process.
1348 */
1349 LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1350 RTFILE FileLogo = NIL_RTFILE;
1351 if (pData->pszLogoFile)
1352 {
1353 rc = RTFileOpen(&FileLogo, pData->pszLogoFile,
1354 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1355 if (VBOX_SUCCESS(rc))
1356 {
1357 uint64_t cbFile;
1358 rc = RTFileGetSize(FileLogo, &cbFile);
1359 if (VBOX_SUCCESS(rc))
1360 {
1361 if ( cbFile > 0
1362 && cbFile < LOGO_MAX_SIZE)
1363 LogoHdr.cbLogo = (uint32_t)cbFile;
1364 else
1365 rc = VERR_TOO_MUCH_DATA;
1366 }
1367 }
1368 if (VBOX_FAILURE(rc))
1369 {
1370 /*
1371 * Ignore failure and fall back to the default logo.
1372 */
1373 LogRel(("pcbiosConstruct: Failed to open logo file '%s', rc=%Vrc!\n", pData->pszLogoFile, rc));
1374 RTFileClose(FileLogo);
1375 FileLogo = NIL_RTFILE;
1376 MMR3HeapFree(pData->pszLogoFile);
1377 pData->pszLogoFile = NULL;
1378 }
1379 }
1380
1381 /*
1382 * Allocate buffer for the logo data.
1383 * RT_MAX() is applied to let us fall back to default logo on read failure.
1384 */
1385 pData->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
1386 pData->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pData->cbLogo, g_cbPcDefBiosLogo + sizeof(LogoHdr)));
1387 if (pData->pu8Logo)
1388 {
1389 /*
1390 * Write the logo header.
1391 */
1392 PLOGOHDR pLogoHdr = (PLOGOHDR)pData->pu8Logo;
1393 *pLogoHdr = LogoHdr;
1394
1395 /*
1396 * Write the logo bitmap.
1397 */
1398 if (pData->pszLogoFile)
1399 {
1400 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
1401 if (VBOX_FAILURE(rc))
1402 {
1403 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", LogoHdr.cbLogo, rc));
1404 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1405 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1406 }
1407 }
1408 else
1409 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1410
1411 /*
1412 * Call reset to set values and stuff.
1413 */
1414 pcbiosReset(pDevIns);
1415 rc = VINF_SUCCESS;
1416 }
1417 else
1418 rc = VERR_NO_MEMORY;
1419
1420 /* cleanup */
1421 if (FileLogo != NIL_RTFILE)
1422 RTFileClose(FileLogo);
1423
1424 /*
1425 * Get the LAN boot ROM file name.
1426 */
1427 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LanBootRom", &pData->pszLanBootFile);
1428 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1429 {
1430 pData->pszLanBootFile = NULL;
1431 rc = VINF_SUCCESS;
1432 }
1433 else if (VBOX_FAILURE(rc))
1434 return PDMDEV_SET_ERROR(pDevIns, rc,
1435 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1436 else if (!*pData->pszLanBootFile)
1437 {
1438 MMR3HeapFree(pData->pszLanBootFile);
1439 pData->pszLanBootFile = NULL;
1440 }
1441
1442 const uint8_t *pu8LanBoot = NULL;
1443 uint64_t cbFileLanBoot;
1444#ifdef VBOX_DO_NOT_LINK_LANBOOT
1445 /*
1446 * Determine the LAN boot ROM size, open specified ROM file in the process.
1447 */
1448 RTFILE FileLanBoot = NIL_RTFILE;
1449 if (pData->pszLanBootFile)
1450 {
1451 rc = RTFileOpen(&FileLanBoot, pData->pszLanBootFile,
1452 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1453 if (VBOX_SUCCESS(rc))
1454 {
1455 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1456 if (VBOX_SUCCESS(rc))
1457 {
1458 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1459 || cbFileLanBoot > _32K)
1460 rc = VERR_TOO_MUCH_DATA;
1461 }
1462 }
1463 if (VBOX_FAILURE(rc))
1464 {
1465 /*
1466 * Ignore failure and fall back to no LAN boot ROM.
1467 */
1468 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Vrc!\n", pData->pszLanBootFile, rc));
1469 RTFileClose(FileLanBoot);
1470 FileLanBoot = NIL_RTFILE;
1471 MMR3HeapFree(pData->pszLanBootFile);
1472 pData->pszLanBootFile = NULL;
1473 }
1474 }
1475
1476 /*
1477 * Get the LAN boot ROM data.
1478 */
1479 if (pData->pszLanBootFile)
1480 {
1481 /*
1482 * Allocate buffer for the LAN boot ROM data.
1483 */
1484 pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1485 pData->pu8LanBoot = pu8LanBoot;
1486 if (pu8LanBoot)
1487 {
1488 rc = RTFileRead(FileLanBoot, pData->pu8LanBoot, cbFileLanBoot, NULL);
1489 if (VBOX_FAILURE(rc))
1490 {
1491 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", cbFileLanBoot, rc));
1492 MMR3HeapFree(pu8LanBoot);
1493 pu8LanBoot = NULL;
1494 pData->pu8LanBoot = NULL;
1495 }
1496 rc = VINF_SUCCESS;
1497 }
1498 else
1499 rc = VERR_NO_MEMORY;
1500 }
1501 else
1502 pData->pu8LanBoot = NULL;
1503
1504 /* cleanup */
1505 if (FileLanBoot != NIL_RTFILE)
1506 RTFileClose(FileLanBoot);
1507
1508#else /* !VBOX_DO_NOT_LINK_LANBOOT */
1509 pData->pu8LanBoot = NULL;
1510 pu8LanBoot = g_abNetBiosBinary;
1511 cbFileLanBoot = g_cbNetBiosBinary;
1512#endif /* !VBOX_DO_NOT_LINK_LANBOOT */
1513
1514 /*
1515 * Map the Network Boot ROM into memory.
1516 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1517 * the (up to) 32 kb ROM image.
1518 */
1519 if (pu8LanBoot)
1520 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbFileLanBoot, pu8LanBoot,
1521 true /* fShadow */, "Net Boot ROM");
1522
1523 rc = CFGMR3QueryU8(pCfgHandle, "DelayBoot", &pData->uBootDelay);
1524 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1525 {
1526 pData->uBootDelay = 0;
1527 rc = VINF_SUCCESS;
1528 }
1529 else
1530 {
1531 if (VBOX_FAILURE(rc))
1532 return PDMDEV_SET_ERROR(pDevIns, rc,
1533 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1534 if (pData->uBootDelay > 15)
1535 pData->uBootDelay = 15;
1536 }
1537
1538 return rc;
1539}
1540
1541
1542/**
1543 * The device registration structure.
1544 */
1545const PDMDEVREG g_DevicePcBios =
1546{
1547 /* u32Version */
1548 PDM_DEVREG_VERSION,
1549 /* szDeviceName */
1550 "pcbios",
1551 /* szGCMod */
1552 "",
1553 /* szR0Mod */
1554 "",
1555 /* pszDescription */
1556 "PC BIOS Device",
1557 /* fFlags */
1558 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1559 /* fClass */
1560 PDM_DEVREG_CLASS_ARCH_BIOS,
1561 /* cMaxInstances */
1562 1,
1563 /* cbInstance */
1564 sizeof(DEVPCBIOS),
1565 /* pfnConstruct */
1566 pcbiosConstruct,
1567 /* pfnDestruct */
1568 pcbiosDestruct,
1569 /* pfnRelocate */
1570 NULL,
1571 /* pfnIOCtl */
1572 NULL,
1573 /* pfnPowerOn */
1574 NULL,
1575 /* pfnReset */
1576 pcbiosReset,
1577 /* pfnSuspend */
1578 NULL,
1579 /* pfnResume */
1580 NULL,
1581 /* pfnAttach */
1582 NULL,
1583 /* pfnDetach */
1584 NULL,
1585 /* pfnQueryInterface. */
1586 NULL,
1587 /* pfnInitComplete. */
1588 pcbiosInitComplete
1589};
1590
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