From 0566ab2a2f814ae486032d603588933cbea8a387 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 8 Sep 2006 22:22:03 +0000 Subject: [PATCH] Added geometry-guessing code based on the partition table --- src/arch/i386/include/int13.h | 39 +++++++++++++++ src/arch/i386/interface/pcbios/int13.c | 69 ++++++++++++++++++++------ 2 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/arch/i386/include/int13.h b/src/arch/i386/include/int13.h index 0525229d..16802a01 100644 --- a/src/arch/i386/include/int13.h +++ b/src/arch/i386/include/int13.h @@ -201,6 +201,45 @@ struct int13_disk_parameters { /** @} */ +/** A C/H/S address within a partition table entry */ +struct partition_chs { + /** Head number */ + uint8_t head; + /** Sector number, plus high 2 bits of cylinder number */ + uint8_t cyl_sector; + /** Low 8 bits of cylinder number */ + uint8_t cyl; +} __attribute__ (( packed )); + +#define PART_HEAD(chs) ( (chs).head ) +#define PART_SECTOR(chs) ( (chs).cyl_sector & 0x3f ) +#define PART_CYLINDER(chs) ( (chs).cyl | ( ( (chs).cyl_sector & 0xc0 ) << 2 ) ) + +/** A partition table entry within the MBR */ +struct partition_table_entry { + /** Bootable flag */ + uint8_t bootable; + /** C/H/S start address */ + struct partition_chs chs_start; + /** System indicator (partition type) */ + uint8_t type; + /** C/H/S end address */ + struct partition_chs chs_end; + /** Linear start address */ + uint32_t start; + /** Linear length */ + uint32_t length; +} __attribute__ (( packed )); + +/** A Master Boot Record */ +struct master_boot_record { + uint8_t pad[446]; + /** Partition table */ + struct partition_table_entry partitions[4]; + /** 0x55aa MBR signature */ + uint16_t signature; +} __attribute__ (( packed )); + extern void register_int13_drive ( struct int13_drive *drive ); extern void unregister_int13_drive ( struct int13_drive *drive ); extern int int13_boot ( unsigned int drive ); diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index 7204ce74..798fb6df 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -437,6 +437,60 @@ static void unhook_int13 ( void ) { &int13_vector ); } +/** + * Guess INT 13 drive geometry + * + * @v drive Emulated drive + * + * Guesses the drive geometry by inspecting the partition table. + */ +static void guess_int13_geometry ( struct int13_drive *drive ) { + struct master_boot_record mbr; + struct partition_table_entry *partition; + unsigned int guessed_heads = 255; + unsigned int guessed_sectors_per_track = 63; + unsigned long blocks; + unsigned long blocks_per_cyl; + unsigned int i; + + /* Scan through partition table and modify guesses for heads + * and sectors_per_track if we find any used partitions. + */ + if ( drive->blockdev->read ( drive->blockdev, 0, 1, + virt_to_user ( &mbr ) ) == 0 ) { + for ( i = 0 ; i < 4 ; i++ ) { + partition = &mbr.partitions[i]; + if ( ! partition->type ) + continue; + guessed_heads = + ( PART_HEAD ( partition->chs_end ) + 1 ); + guessed_sectors_per_track = + PART_SECTOR ( partition->chs_end ); + DBG ( "Guessing C/H/S xx/%d/%d based on partition " + "%d\n", guessed_heads, + guessed_sectors_per_track, ( i + 1 ) ); + } + } else { + DBG ( "Could not read partition table to guess geometry\n" ); + } + + /* Apply guesses if no geometry already specified */ + if ( ! drive->heads ) + drive->heads = guessed_heads; + if ( ! drive->sectors_per_track ) + drive->sectors_per_track = guessed_sectors_per_track; + if ( ! drive->cylinders ) { + /* Avoid attempting a 64-bit divide on a 32-bit system */ + blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ? + drive->blockdev->blocks : ULONG_MAX ); + blocks_per_cyl = ( drive->heads * drive->sectors_per_track ); + assert ( blocks_per_cyl != 0 ); + drive->cylinders = ( blocks / blocks_per_cyl ); + if ( drive->cylinders > 1024 ) + drive->cylinders = 1024; + } +} + /** * Register INT 13 emulated drive * @@ -450,22 +504,9 @@ static void unhook_int13 ( void ) { */ void register_int13_drive ( struct int13_drive *drive ) { uint8_t num_drives; - unsigned long blocks; - unsigned long blocks_per_cyl; /* Give drive a default geometry if none specified */ - if ( ! drive->heads ) - drive->heads = 255; - if ( ! drive->sectors_per_track ) - drive->sectors_per_track = 63; - if ( ! drive->cylinders ) { - /* Avoid attempting a 64-bit divide on a 32-bit system */ - blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ? - drive->blockdev->blocks : ULONG_MAX ); - blocks_per_cyl = ( drive->heads * drive->sectors_per_track ); - assert ( blocks_per_cyl != 0 ); - drive->cylinders = ( blocks / blocks_per_cyl ); - } + guess_int13_geometry ( drive ); /* Assign drive number if none specified, update BIOS drive count */ get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );