summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2009-11-18 03:37:15 +0100
committerMichael Brown2009-11-18 03:44:49 +0100
commitb515977955303666fc4a3afd0ffeb2eefb65600e (patch)
treecf84ff5930ec13c40911e97d2e3249dd9b9f56f1
parent[int13] Fix number of sectors returned by INT 13,15 (diff)
downloadipxe-b515977955303666fc4a3afd0ffeb2eefb65600e.tar.gz
ipxe-b515977955303666fc4a3afd0ffeb2eefb65600e.tar.xz
ipxe-b515977955303666fc4a3afd0ffeb2eefb65600e.zip
[int13] Guard against BIOSes that "fix" the drive count
Some BIOSes (observed with an AMI BIOS on a SunFire X2200) seem to reset the BIOS drive counter at 40:75 after a failed boot attempt. This causes problems when attempting a Windows direct-to-iSCSI installation: bootmgr.exe calls INT 13,0800 and gets told that there are no hard disks, so never bothers to read the MBR in order to obtain the boot disk signature. The Windows iSCSI initiator will detect the iBFT and connect to the target, and everything will appear to work except for the error message "This computer's hardware may not support booting to this disk. Ensure that the disk's controller is enabled in the computer's BIOS menu." Fix by checking the BIOS drive counter on every INT 13 call, and updating it whenever necessary.
-rw-r--r--src/arch/i386/interface/pcbios/int13.c54
1 files changed, 48 insertions, 6 deletions
diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c
index 07439b93..87b613a8 100644
--- a/src/arch/i386/interface/pcbios/int13.c
+++ b/src/arch/i386/interface/pcbios/int13.c
@@ -52,6 +52,48 @@ extern void int13_wrapper ( void );
static LIST_HEAD ( drives );
/**
+ * Number of BIOS drives
+ *
+ * Note that this is the number of drives in the system as a whole
+ * (i.e. a mirror of the counter at 40:75), rather than a count of the
+ * number of emulated drives.
+ */
+static uint8_t num_drives;
+
+/**
+ * Update BIOS drive count
+ */
+static void int13_set_num_drives ( void ) {
+ struct int13_drive *drive;
+
+ /* Get current drive count */
+ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+
+ /* Ensure count is large enough to cover all of our emulated drives */
+ list_for_each_entry ( drive, &drives, list ) {
+ if ( num_drives <= ( drive->drive & 0x7f ) )
+ num_drives = ( ( drive->drive & 0x7f ) + 1 );
+ }
+
+ /* Update current drive count */
+ put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+}
+
+/**
+ * Check number of drives
+ */
+static void int13_check_num_drives ( void ) {
+ uint8_t check_num_drives;
+
+ get_real ( check_num_drives, BDA_SEG, BDA_NUM_DRIVES );
+ if ( check_num_drives != num_drives ) {
+ int13_set_num_drives();
+ DBG ( "INT13 fixing up number of drives from %d to %d\n",
+ check_num_drives, num_drives );
+ }
+}
+
+/**
* INT 13, 00 - Reset disk system
*
* @v drive Emulated drive
@@ -340,6 +382,9 @@ static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
struct int13_drive *drive;
int status;
+ /* Check BIOS hasn't killed off our drive */
+ int13_check_num_drives();
+
list_for_each_entry ( drive, &drives, list ) {
if ( bios_drive != drive->drive ) {
@@ -555,7 +600,6 @@ void register_int13_drive ( struct int13_drive *drive ) {
/* Assign natural drive number */
get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
drive->natural_drive = ( num_drives | 0x80 );
- num_drives++;
/* Assign drive number */
if ( ( drive->drive & 0xff ) == 0xff ) {
@@ -564,13 +608,8 @@ void register_int13_drive ( struct int13_drive *drive ) {
} else {
/* Use specified drive number (+0x80 if necessary) */
drive->drive |= 0x80;
- if ( num_drives <= ( drive->drive & 0x7f ) )
- num_drives = ( ( drive->drive & 0x7f ) + 1 );
}
- /* Update BIOS drive count */
- put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
-
DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S "
"geometry %d/%d/%d\n", drive->drive, drive->natural_drive,
drive->cylinders, drive->heads, drive->sectors_per_track );
@@ -581,6 +620,9 @@ void register_int13_drive ( struct int13_drive *drive ) {
/* Add to list of emulated drives */
list_add ( &drive->list, &drives );
+
+ /* Update BIOS drive count */
+ int13_set_num_drives();
}
/**