VirtualBox

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

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

QueryCPUId -> GetCpuId (it's a wrapper for CPUMGetGuestCpuId).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.4 KB
Line 
1/** @file
2 *
3 * VBox basic PC devices:
4 * PC BIOS device
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEV_PC_BIOS
27#include <VBox/pdm.h>
28#include <VBox/mm.h>
29
30#include <VBox/log.h>
31#include <iprt/assert.h>
32#include <iprt/alloc.h>
33#include <iprt/file.h>
34#include <iprt/string.h>
35#include <VBox/err.h>
36
37#include "vl_vbox.h"
38#include "Builtins.h"
39#include "Builtins2.h"
40#include "DevPcBios.h"
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46
47/**
48 * The boot device.
49 */
50typedef enum DEVPCBIOSBOOT
51{
52 DEVPCBIOSBOOT_NONE,
53 DEVPCBIOSBOOT_FLOPPY,
54 DEVPCBIOSBOOT_HD,
55 DEVPCBIOSBOOT_DVD,
56 DEVPCBIOSBOOT_LAN
57} DEVPCBIOSBOOT;
58
59/**
60 * PC Bios instance data structure.
61 */
62typedef struct DEVPCBIOS
63{
64 /** Pointer back to the device instance. */
65 PPDMDEVINS pDevIns;
66
67 /** Boot devices (ordered). */
68 DEVPCBIOSBOOT aenmBootDevice[4];
69 /** Ram Size (in bytes). */
70 uint64_t cbRam;
71 /** Bochs shutdown index. */
72 uint32_t iShutdown;
73 /** Floppy device. */
74 char *pszFDDevice;
75 /** Harddisk device. */
76 char *pszHDDevice;
77 /** Bios message buffer. */
78 char szMsg[256];
79 /** Bios message buffer index. */
80 uint32_t iMsg;
81 /** Current logo data memory bank. */
82 uint8_t u8LogoBank;
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 (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/** The value of the LOGOHDR::u16Signature field. */
126#define LOGOHDR_MAGIC 0x66BB
127
128/** Size of a logo bank. */
129#define LOGO_BANK_SIZE 0xffff
130/** Logo offset mask. */
131#define LOGO_BANK_OFFSET 0xffff
132/** The last bank for custom logo data. */
133#define LOGO_BANK_LAST 254
134/** The bank which will give you the default logo. */
135#define LOGO_BANK_DEFAULT_LOGO 255
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) logoMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb);
264static DECLCALLBACK(int) logoMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, 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 * Legacy LOGO memory (0xd0000 - 0xdffff) read hook, to be called from IOM.
647 *
648 * @returns VBox status code.
649 * @param pDevIns Pointer device instance.
650 * @param pvUser User argument - ignored.
651 * @param GCPhysAddr Physical address of memory to read.
652 * @param pv Where to store readed data.
653 * @param cb Bytes to read.
654 */
655static DECLCALLBACK(int) logoMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
656{
657 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
658 Log(("logoMMIORead call GCPhysAddr:%x pv:%x cb:%d (%d)\n", GCPhysAddr, pv, cb, pData->u8LogoBank));
659
660 uint32_t offLogo = GCPhysAddr & LOGO_BANK_OFFSET;
661 if (pData->u8LogoBank != LOGO_BANK_DEFAULT_LOGO)
662 {
663 /*
664 * Banked logo.
665 */
666 offLogo += pData->u8LogoBank * LOGO_BANK_SIZE;
667 if ( offLogo > pData->cbLogo
668 || offLogo + cb > pData->cbLogo)
669 {
670 Log(("logoMMIORead: Requested address is out of Logo data!!! offLogo=%#x(%d) cbLogo=%#x(%d)\n",
671 offLogo, offLogo, pData->cbLogo, pData->cbLogo));
672 return VINF_SUCCESS;
673 }
674
675 memcpy(pv, &pData->pu8Logo[offLogo], cb);
676 Log(("logoMMIORead: offLogo=%#x(%d) cb=%#x %.*Vhxs\n", offLogo, offLogo, cb, cb, &pData->pu8Logo[offLogo]));
677 }
678 else
679 {
680 /*
681 * Default bios logo.
682 */
683 if (offLogo > g_cbPcDefBiosLogo || offLogo + cb > g_cbPcDefBiosLogo)
684 {
685 Log(("logoMMIORead: Requested address is out of Logo data!!! offLogo=%#x(%d) max:%#x(%d)\n",
686 offLogo, offLogo, g_cbPcDefBiosLogo, g_cbPcDefBiosLogo));
687 return VINF_SUCCESS;
688 }
689
690 memcpy(pv, &g_abPcDefBiosLogo[offLogo], cb);
691 Log(("logoMMIORead: offLogo=%#x(%d) cb=%#x %.*Vhxs\n", offLogo, offLogo, cb, cb, &g_abPcDefBiosLogo[offLogo]));
692 }
693
694 return VINF_SUCCESS;
695}
696
697
698/**
699 * Legacy LOGO memory (0xd0000 - 0xdffff) write hook, to be called from IOM.
700 *
701 * @returns VBox status code.
702 * @param pDevIns Pointer device instance.
703 * @param pvUser User argument - ignored.
704 * @param GCPhysAddr Physical address of memory to write.
705 * @param pv Pointer to data.
706 * @param cb Bytes to write.
707 */
708static DECLCALLBACK(int) logoMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
709{
710 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
711 Log(("logoMMIOWrite: GCPhysAddr=%x cb=%d pv[0]=%#04x (byte)\n", GCPhysAddr, pv, cb, *(uint8_t *)pv));
712
713 /*
714 * Byte write to off 0: Switch the logo bank.
715 */
716 if ((GCPhysAddr & LOGO_BANK_OFFSET) == 0 && cb == 1)
717 {
718 uint8_t u8Bank = *(uint8_t *)pv;
719 uint32_t off = u8Bank * LOGO_BANK_SIZE;
720
721 if ( u8Bank != LOGO_BANK_DEFAULT_LOGO
722 && off > pData->cbLogo)
723 {
724 Log(("logoMMIOWrite: The requested bank is outside the logo image! (cbLogo=%d off=%d)\n", pData->cbLogo, off));
725 return VINF_SUCCESS;
726 }
727
728 pData->u8LogoBank = u8Bank;
729 }
730
731 return VINF_SUCCESS;
732}
733
734
735/**
736 * Construct the DMI table.
737 *
738 * @param table pointer to DMI table.
739 */
740#define STRCPY(p, s) do { memcpy (p, s, sizeof(s)); p += sizeof(s); } while (0)
741static void pcbiosPlantDMITable(uint8_t *pTable)
742{
743 char *pszStr = (char*)pTable;
744 int iStrNr;
745
746 PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
747 pszStr = (char*)(pBIOSInf+1);
748 iStrNr = 1;
749 pBIOSInf->header.u8Type = 0; /* BIOS Information */
750 pBIOSInf->header.u8Length = sizeof(*pBIOSInf);
751 pBIOSInf->header.u16Handle = 0x0000;
752 pBIOSInf->u8Vendor = iStrNr++;
753 STRCPY(pszStr, "InnoTek Systemberatung GmbH");
754 pBIOSInf->u8Version = iStrNr++;
755 STRCPY(pszStr, "VirtualBox");
756 pBIOSInf->u16Start = 0xE000;
757 pBIOSInf->u8Release = iStrNr++;
758 STRCPY(pszStr, "12/01/2006");
759 pBIOSInf->u8ROMSize = 1; /* 128K */
760 pBIOSInf->u64Characteristics = BIT(4) /* ISA is supported */
761 | BIT(7) /* PCI is supported */
762 | BIT(15) /* Boot from CD is supported */
763 | BIT(16) /* Selectable Boot is supported */
764 | BIT(27) /* Int 9h, 8042 Keyboard services supported */
765 | BIT(30) /* Int 10h, CGA/Mono Video Services supported */
766 /* any more?? */
767 ;
768 pBIOSInf->u8CharacteristicsByte1 = BIT(0) /* ACPI is supported */
769 /* any more?? */
770 ;
771 pBIOSInf->u8CharacteristicsByte2 = 0
772 /* any more?? */
773 ;
774 *pszStr++ = '\0';
775
776
777 PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
778 pszStr = (char*)(pSystemInf+1);
779 iStrNr = 1;
780 pSystemInf->header.u8Type = 1; /* System Information */
781 pSystemInf->header.u8Length = sizeof(*pSystemInf);
782 pSystemInf->header.u16Handle = 0x0001;
783 pSystemInf->u8Manufacturer = iStrNr++;
784 STRCPY(pszStr, "InnoTek Systemberatung GmbH");
785 pSystemInf->u8ProductName = iStrNr++;
786 STRCPY(pszStr, "VirtualBox");
787 pSystemInf->u8Version = iStrNr++;
788 STRCPY(pszStr, "1.2");
789 pSystemInf->u8SerialNumber = iStrNr++;
790 STRCPY(pszStr, "0");
791 pSystemInf->u8WakeupType = 6; /* Power Switch */
792 pSystemInf->u8SKUNumber = 0;
793 pSystemInf->u8Family = iStrNr++;
794 STRCPY(pszStr, "Virtual Machine");
795 *pszStr++ = '\0';
796
797 AssertMsg(pszStr - (char*)pTable == VBOX_DMI_TABLE_SIZE,
798 ("VBOX_DMI_TABLE_SIZE=%d, actual DMI table size is %d",
799 VBOX_DMI_TABLE_SIZE, pszStr - (char*)pTable));
800}
801AssertCompile(VBOX_DMI_TABLE_ENTR == 2);
802
803
804/**
805 * Calculate a simple checksum for the MPS table.
806 *
807 * @param data data
808 * @param len size of data
809 */
810static uint8_t pcbiosChecksum(const uint8_t * const au8Data, uint32_t u32Length)
811{
812 uint8_t u8Sum = 0;
813 for (size_t i = 0; i < u32Length; ++i)
814 u8Sum += au8Data[i];
815 return -u8Sum;
816}
817
818
819/**
820 * Construct the MPS table. Only applicable if IOAPIC is active.
821 *
822 * @param pDevIns The device instance data.
823 * @param addr physical address in guest memory.
824 */
825static void pcbiosPlantMPStable(PPDMDEVINS pDevIns, uint8_t *pTable)
826{
827 /* configuration table */
828 PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
829 memcpy(pCfgTab->au8Signature, "PCMP", 4);
830 pCfgTab->u8SpecRev = 4; /* 1.4 */
831 memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
832 memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
833 pCfgTab->u32OemTablePtr = 0;
834 pCfgTab->u16OemTableSize = 0;
835 pCfgTab->u16EntryCount = 1 /* Processor */
836 + 1 /* ISA Bus */
837 + 1 /* I/O-APIC */
838 + 16 /* Interrupts */;
839 pCfgTab->u32AddrLocalApic = 0xfee00000;
840 pCfgTab->u16ExtTableLength = 0;
841 pCfgTab->u8ExtTableChecksxum = 0;
842 pCfgTab->u8Reserved = 0;
843
844 uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
845 uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
846 uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
847 PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
848 if (u32Eax >= 1)
849 {
850 PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
851 u32CPUSignature = u32Eax & 0xfff;
852 /* Local APIC will be enabled later so override it here. Since we provide
853 * an MP table we have an IOAPIC and therefore a Local APIC. */
854 u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
855 }
856
857 /* one processor so far */
858 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
859 pProcEntry->u8EntryType = 0; /* processor entry */
860 pProcEntry->u8LocalApicId = 0;
861 pProcEntry->u8LocalApicVersion = 0x11;
862 pProcEntry->u8CPUFlags = 2 /* bootstrap processor */ | 1 /* enabled */;
863 pProcEntry->u32CPUSignature = u32CPUSignature;
864 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
865 pProcEntry->u32Reserved[0] =
866 pProcEntry->u32Reserved[1] = 0;
867
868 /* ISA bus */
869 PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)(pProcEntry+1);
870 pBusEntry->u8EntryType = 1; /* bus entry */
871 pBusEntry->u8BusId = 0; /* this ID is referenced by the interrupt entries */
872 memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
873
874 /* PCI bus? */
875
876 /* I/O-APIC.
877 * MP spec: "The configuration table contains one or more entries for I/O APICs.
878 * ... At least one I/O APIC must be enabled." */
879 PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1);
880 pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
881 pIOAPICEntry->u8Id = 1; /* this ID is referenced by the interrupt entries */
882 pIOAPICEntry->u8Version = 0x11;
883 pIOAPICEntry->u8Flags = 1 /* enable */;
884 pIOAPICEntry->u32Addr = 0xfec00000;
885
886 PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
887 for (int i = 0; i < 16; i++, pIrqEntry++)
888 {
889 pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
890 pIrqEntry->u8Type = 0; /* INT, vectored interrupt */
891 pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
892 trigger mode = conforms to bus */
893 pIrqEntry->u8SrcBusId = 0; /* ISA bus */
894 pIrqEntry->u8SrcBusIrq = i;
895 pIrqEntry->u8DstIOAPICId = 1;
896 pIrqEntry->u8DstIOAPICInt = i;
897 }
898
899 pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
900 pCfgTab->u8Checksum = pcbiosChecksum(pTable, pCfgTab->u16Length);
901
902 AssertMsg(pCfgTab->u16Length < 0x1000 - 0x100,
903 ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
904 pCfgTab->u16Length, 0x1000-0x100));
905
906 MPSFLOATPTR floatPtr;
907 floatPtr.au8Signature[0] = '_';
908 floatPtr.au8Signature[1] = 'M';
909 floatPtr.au8Signature[2] = 'P';
910 floatPtr.au8Signature[3] = '_';
911 floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE;
912 floatPtr.u8Length = 1; /* structure size in paragraphs */
913 floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
914 floatPtr.u8Checksum = 0;
915 floatPtr.au8Feature[0] = 0;
916 floatPtr.au8Feature[1] = 0;
917 floatPtr.au8Feature[2] = 0;
918 floatPtr.au8Feature[3] = 0;
919 floatPtr.au8Feature[4] = 0;
920 floatPtr.u8Checksum = pcbiosChecksum((uint8_t*)&floatPtr, 16);
921 PDMDevHlpPhysWrite (pDevIns, 0x9fff0, &floatPtr, 16);
922}
923
924
925/**
926 * Reset notification.
927 *
928 * @returns VBox status.
929 * @param pDevIns The device instance data.
930 */
931static DECLCALLBACK(void) pcbiosReset(PPDMDEVINS pDevIns)
932{
933 PDEVPCBIOS pData = PDMINS2DATA(pDevIns, PDEVPCBIOS);
934 LogFlow(("pcbiosReset:\n"));
935
936 pData->u8LogoBank = 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 MMIO region for the BIOS Logo: 0x000d0000 to 0x000dffff (64k)
1178 */
1179 rc = PDMDevHlpMMIORegister(pDevIns, 0x000d0000, 0x00010000, 0,
1180 logoMMIOWrite, logoMMIORead, NULL, "PC BIOS - Logo Buffer");
1181 if (VBOX_FAILURE(rc))
1182 return rc;
1183
1184 /*
1185 * Construct the logo header.
1186 */
1187 LOGOHDR LogoHdr = { LOGOHDR_MAGIC, 0, 0, 0, 0, 0 };
1188
1189 rc = CFGMR3QueryU8(pCfgHandle, "FadeIn", &LogoHdr.u8FadeIn);
1190 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1191 LogoHdr.u8FadeIn = 1;
1192 else if (VBOX_FAILURE(rc))
1193 return PDMDEV_SET_ERROR(pDevIns, rc,
1194 N_("Configuration error: Querying \"FadeIn\" as integer failed"));
1195
1196 rc = CFGMR3QueryU8(pCfgHandle, "FadeOut", &LogoHdr.u8FadeOut);
1197 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1198 LogoHdr.u8FadeOut = 1;
1199 else if (VBOX_FAILURE(rc))
1200 return PDMDEV_SET_ERROR(pDevIns, rc,
1201 N_("Configuration error: Querying \"FadeOut\" as integer failed"));
1202
1203 rc = CFGMR3QueryU16(pCfgHandle, "LogoTime", &LogoHdr.u16LogoMillies);
1204 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1205 LogoHdr.u16LogoMillies = 1;
1206 else if (VBOX_FAILURE(rc))
1207 return PDMDEV_SET_ERROR(pDevIns, rc,
1208 N_("Configuration error: Querying \"LogoTime\" as integer failed"));
1209
1210 rc = CFGMR3QueryU8(pCfgHandle, "ShowBootMenu", &LogoHdr.u8ShowBootMenu);
1211 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1212 LogoHdr.u8ShowBootMenu = 0;
1213 else if (VBOX_FAILURE(rc))
1214 return PDMDEV_SET_ERROR(pDevIns, rc,
1215 N_("Configuration error: Querying \"ShowBootMenu\" as integer failed"));
1216
1217 /*
1218 * Get the Logo file name.
1219 */
1220 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LogoFile", &pData->pszLogoFile);
1221 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1222 pData->pszLogoFile = NULL;
1223 else if (VBOX_FAILURE(rc))
1224 return PDMDEV_SET_ERROR(pDevIns, rc,
1225 N_("Configuration error: Querying \"LogoFile\" as a string failed"));
1226 else if (!*pData->pszLogoFile)
1227 {
1228 MMR3HeapFree(pData->pszLogoFile);
1229 pData->pszLogoFile = NULL;
1230 }
1231
1232 /*
1233 * Determine the logo size, open any specified logo file in the process.
1234 */
1235 LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1236 RTFILE FileLogo = NIL_RTFILE;
1237 if (pData->pszLogoFile)
1238 {
1239 rc = RTFileOpen(&FileLogo, pData->pszLogoFile,
1240 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1241 if (VBOX_SUCCESS(rc))
1242 {
1243 uint64_t cbFile;
1244 rc = RTFileGetSize(FileLogo, &cbFile);
1245 if (VBOX_SUCCESS(rc))
1246 {
1247 if ( cbFile > 0
1248 && cbFile < ((LOGO_BANK_LAST + 1) * LOGO_BANK_SIZE) - sizeof(LogoHdr))
1249 LogoHdr.cbLogo = (uint32_t)cbFile;
1250 else
1251 rc = VERR_TOO_MUCH_DATA;
1252 }
1253 }
1254 if (VBOX_FAILURE(rc))
1255 {
1256 /*
1257 * Ignore failure and fall back to the default logo.
1258 */
1259 LogRel(("pcbiosConstruct: Failed to open logo file '%s', rc=%Vrc!\n", pData->pszLogoFile, rc));
1260 RTFileClose(FileLogo);
1261 FileLogo = NIL_RTFILE;
1262 MMR3HeapFree(pData->pszLogoFile);
1263 pData->pszLogoFile = NULL;
1264 }
1265 }
1266
1267 /*
1268 * Allocate buffer for the logo data.
1269 * RT_MAX() is applied to let us fall back to default logo on read failure.
1270 */
1271 pData->cbLogo = sizeof(LogoHdr) + LogoHdr.cbLogo;
1272 pData->pu8Logo = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, RT_MAX(pData->cbLogo, g_cbPcDefBiosLogo + sizeof(LogoHdr)));
1273 if (pData->pu8Logo)
1274 {
1275 /*
1276 * Write the logo header.
1277 */
1278 PLOGOHDR pLogoHdr = (PLOGOHDR)pData->pu8Logo;
1279 *pLogoHdr = LogoHdr;
1280
1281 /*
1282 * Write the logo bitmap.
1283 */
1284 if (pData->pszLogoFile)
1285 {
1286 rc = RTFileRead(FileLogo, pLogoHdr + 1, LogoHdr.cbLogo, NULL);
1287 if (VBOX_FAILURE(rc))
1288 {
1289 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", LogoHdr.cbLogo, rc));
1290 pLogoHdr->cbLogo = LogoHdr.cbLogo = g_cbPcDefBiosLogo;
1291 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1292 }
1293 }
1294 else
1295 memcpy(pLogoHdr + 1, g_abPcDefBiosLogo, LogoHdr.cbLogo);
1296
1297 /*
1298 * Call reset to set values and stuff.
1299 */
1300 pcbiosReset(pDevIns);
1301 rc = VINF_SUCCESS;
1302 }
1303 else
1304 rc = VERR_NO_MEMORY;
1305
1306 /* cleanup */
1307 if (FileLogo != NIL_RTFILE)
1308 RTFileClose(FileLogo);
1309
1310 /*
1311 * Get the LAN boot ROM file name.
1312 */
1313 rc = CFGMR3QueryStringAlloc(pCfgHandle, "LanBootRom", &pData->pszLanBootFile);
1314 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1315 {
1316 pData->pszLanBootFile = NULL;
1317 rc = VINF_SUCCESS;
1318 }
1319 else if (VBOX_FAILURE(rc))
1320 return PDMDEV_SET_ERROR(pDevIns, rc,
1321 N_("Configuration error: Querying \"LanBootRom\" as a string failed"));
1322 else if (!*pData->pszLanBootFile)
1323 {
1324 MMR3HeapFree(pData->pszLanBootFile);
1325 pData->pszLanBootFile = NULL;
1326 }
1327
1328 const uint8_t *pu8LanBoot = NULL;
1329 uint64_t cbFileLanBoot;
1330#ifdef VBOX_DO_NOT_LINK_LANBOOT
1331 /*
1332 * Determine the LAN boot ROM size, open specified ROM file in the process.
1333 */
1334 RTFILE FileLanBoot = NIL_RTFILE;
1335 if (pData->pszLanBootFile)
1336 {
1337 rc = RTFileOpen(&FileLanBoot, pData->pszLanBootFile,
1338 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1339 if (VBOX_SUCCESS(rc))
1340 {
1341 rc = RTFileGetSize(FileLanBoot, &cbFileLanBoot);
1342 if (VBOX_SUCCESS(rc))
1343 {
1344 if ( RT_ALIGN(cbFileLanBoot, _4K) != cbFileLanBoot
1345 || cbFileLanBoot > _32K)
1346 rc = VERR_TOO_MUCH_DATA;
1347 }
1348 }
1349 if (VBOX_FAILURE(rc))
1350 {
1351 /*
1352 * Ignore failure and fall back to no LAN boot ROM.
1353 */
1354 Log(("pcbiosConstruct: Failed to open LAN boot ROM file '%s', rc=%Vrc!\n", pData->pszLanBootFile, rc));
1355 RTFileClose(FileLanBoot);
1356 FileLanBoot = NIL_RTFILE;
1357 MMR3HeapFree(pData->pszLanBootFile);
1358 pData->pszLanBootFile = NULL;
1359 }
1360 }
1361
1362 /*
1363 * Get the LAN boot ROM data.
1364 */
1365 if (pData->pszLanBootFile)
1366 {
1367 /*
1368 * Allocate buffer for the LAN boot ROM data.
1369 */
1370 pu8LanBoot = (uint8_t *)PDMDevHlpMMHeapAlloc(pDevIns, cbFileLanBoot);
1371 pData->pu8LanBoot = pu8LanBoot;
1372 if (pu8LanBoot)
1373 {
1374 rc = RTFileRead(FileLanBoot, pData->pu8LanBoot, cbFileLanBoot, NULL);
1375 if (VBOX_FAILURE(rc))
1376 {
1377 AssertMsgFailed(("RTFileRead(,,%d,NULL) -> %Vrc\n", cbFileLanBoot, rc));
1378 MMR3HeapFree(pu8LanBoot);
1379 pu8LanBoot = NULL;
1380 pData->pu8LanBoot = NULL;
1381 }
1382 rc = VINF_SUCCESS;
1383 }
1384 else
1385 rc = VERR_NO_MEMORY;
1386 }
1387 else
1388 pData->pu8LanBoot = NULL;
1389
1390 /* cleanup */
1391 if (FileLanBoot != NIL_RTFILE)
1392 RTFileClose(FileLanBoot);
1393
1394#else /* !VBOX_DO_NOT_LINK_LANBOOT */
1395 pData->pu8LanBoot = NULL;
1396 pu8LanBoot = g_abNetBiosBinary;
1397 cbFileLanBoot = g_cbNetBiosBinary;
1398#endif /* !VBOX_DO_NOT_LINK_LANBOOT */
1399
1400 /*
1401 * Map the Network Boot ROM into memory.
1402 * Currently there is a fixed mapping: 0x000c8000 to 0x000cffff contains
1403 * the (up to) 32 kb ROM image.
1404 */
1405 if (pu8LanBoot)
1406 rc = PDMDevHlpROMRegister(pDevIns, VBOX_LANBOOT_SEG << 4, cbFileLanBoot, pu8LanBoot, "Net Boot ROM");
1407
1408 rc = CFGMR3QueryU8(pCfgHandle, "DelayBoot", &pData->uBootDelay);
1409 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1410 {
1411 pData->uBootDelay = 0;
1412 rc = VINF_SUCCESS;
1413 }
1414 else
1415 {
1416 if (VBOX_FAILURE(rc))
1417 return PDMDEV_SET_ERROR(pDevIns, rc,
1418 N_("Configuration error: Querying \"DelayBoot\" as integer failed"));
1419 if (pData->uBootDelay > 15)
1420 pData->uBootDelay = 15;
1421 }
1422
1423 return rc;
1424}
1425
1426
1427/**
1428 * The device registration structure.
1429 */
1430const PDMDEVREG g_DevicePcBios =
1431{
1432 /* u32Version */
1433 PDM_DEVREG_VERSION,
1434 /* szDeviceName */
1435 "pcbios",
1436 /* szGCMod */
1437 "",
1438 /* szR0Mod */
1439 "",
1440 /* pszDescription */
1441 "Bochs PC BIOS",
1442 /* fFlags */
1443 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64,
1444 /* fClass */
1445 PDM_DEVREG_CLASS_ARCH_BIOS,
1446 /* cMaxInstances */
1447 1,
1448 /* cbInstance */
1449 sizeof(DEVPCBIOS),
1450 /* pfnConstruct */
1451 pcbiosConstruct,
1452 /* pfnDestruct */
1453 pcbiosDestruct,
1454 /* pfnRelocate */
1455 NULL,
1456 /* pfnIOCtl */
1457 NULL,
1458 /* pfnPowerOn */
1459 NULL,
1460 /* pfnReset */
1461 pcbiosReset,
1462 /* pfnSuspend */
1463 NULL,
1464 /* pfnResume */
1465 NULL,
1466 /* pfnAttach */
1467 NULL,
1468 /* pfnDetach */
1469 NULL,
1470 /* pfnQueryInterface. */
1471 NULL,
1472 /* pfnInitComplete. */
1473 pcbiosInitComplete
1474};
1475
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