VirtualBox

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

Last change on this file since 4859 was 4670, checked in by vboxsync, 18 years ago

Eliminated one MMPhysGCPhys2HCVirt user.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette