From b515977955303666fc4a3afd0ffeb2eefb65600e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 18 Nov 2009 02:37:15 +0000 Subject: [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. --- src/arch/i386/interface/pcbios/int13.c | 54 ++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) (limited to 'src/arch/i386') 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 @@ -51,6 +51,48 @@ extern void int13_wrapper ( void ); /** List of registered emulated drives */ 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 * @@ -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(); } /** -- cgit v1.2.3-55-g7522