diff --git a/src/arch/i386/include/int13.h b/src/arch/i386/include/int13.h index 2a193831..72ca97d7 100644 --- a/src/arch/i386/include/int13.h +++ b/src/arch/i386/include/int13.h @@ -69,8 +69,20 @@ struct int13_drive { /** Underlying block device */ struct block_device *blockdev; - /** BIOS drive number (0x80-0xff) */ + /** BIOS in-use drive number (0x80-0xff) */ unsigned int drive; + /** BIOS natural drive number (0x80-0xff) + * + * This is the drive number that would have been assigned by + * 'naturally' appending the drive to the end of the BIOS + * drive list. + * + * If the emulated drive replaces a preexisting drive, this is + * the drive number that the preexisting drive gets remapped + * to. + */ + unsigned int natural_drive; + /** Number of cylinders * * The cylinder number field in an INT 13 call is ten bits diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index 7e09fb5f..6f61e4a1 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -325,15 +325,20 @@ static int int13_get_extended_parameters ( struct int13_drive *drive, static __cdecl void int13 ( struct i386_all_regs *ix86 ) { int command = ix86->regs.ah; unsigned int bios_drive = ix86->regs.dl; - unsigned int original_bios_drive = bios_drive; struct int13_drive *drive; int status; list_for_each_entry ( drive, &drives, list ) { - if ( drive->drive > bios_drive ) - continue; - if ( drive->drive < bios_drive ) { - original_bios_drive--; + + if ( bios_drive != drive->drive ) { + /* Remap any accesses to this drive's natural number */ + if ( bios_drive == drive->natural_drive ) { + DBG ( "INT 13,%04x (%02x) remapped to " + "(%02x)\n", ix86->regs.ax, + bios_drive, drive->drive ); + ix86->regs.dl = drive->drive; + return; + } continue; } @@ -393,13 +398,6 @@ static __cdecl void int13 ( struct i386_all_regs *ix86 ) { return; } - - /* Remap BIOS drive */ - if ( bios_drive != original_bios_drive ) { - DBG ( "INT 13,%04x (%02x) remapped to (%02x)\n", - ix86->regs.ax, bios_drive, original_bios_drive ); - } - ix86->regs.dl = original_bios_drive; } /** @@ -542,19 +540,28 @@ void register_int13_drive ( struct int13_drive *drive ) { /* Give drive a default geometry if none specified */ guess_int13_geometry ( drive ); - /* Assign drive number if none specified, update BIOS drive count */ + /* Assign natural drive number */ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); - if ( ( drive->drive & 0xff ) == 0xff ) - drive->drive = num_drives; - drive->drive |= 0x80; + drive->natural_drive = ( num_drives | 0x80 ); num_drives++; - if ( num_drives <= ( drive->drive & 0x7f ) ) - num_drives = ( ( drive->drive & 0x7f ) + 1 ); + + /* Assign drive number */ + if ( ( drive->drive & 0xff ) == 0xff ) { + /* Drive number == -1 => use natural drive number */ + drive->drive = drive->natural_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 with C/H/S geometry %d/%d/%d\n", - drive->drive, drive->cylinders, drive->heads, - drive->sectors_per_track ); + 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 ); /* Hook INT 13 vector if not already hooked */ if ( list_empty ( &drives ) )