VirtualBox

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

Last change on this file since 3867 was 3721, checked in by vboxsync, 18 years ago

Applied patch moving the logo from MMIO to port I/O space. 0xd000..0xdfff is now free again.

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