Added geometry-guessing code based on the partition table
authorMichael Brown <mcb30@etherboot.org>
Fri, 8 Sep 2006 22:22:03 +0000 (22:22 +0000)
committerMichael Brown <mcb30@etherboot.org>
Fri, 8 Sep 2006 22:22:03 +0000 (22:22 +0000)
src/arch/i386/include/int13.h
src/arch/i386/interface/pcbios/int13.c

index 0525229..16802a0 100644 (file)
@@ -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 );
index 7204ce7..798fb6d 100644 (file)
@@ -438,26 +438,47 @@ static void unhook_int13 ( void ) {
 }
 
 /**
- * Register INT 13 emulated drive
+ * Guess INT 13 drive geometry
  *
  * @v drive            Emulated drive
  *
- * Registers the drive with the INT 13 emulation subsystem, and hooks
- * the INT 13 interrupt vector (if not already hooked).
- *
- * The underlying block device must be valid.  A drive number and
- * geometry will be assigned if left blank.
+ * Guesses the drive geometry by inspecting the partition table.
  */
-void register_int13_drive ( struct int13_drive *drive ) {
-       uint8_t num_drives;
+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;
 
-       /* Give drive a default geometry if none specified */
+       /* 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 = 255;
+               drive->heads = guessed_heads;
        if ( ! drive->sectors_per_track )
-               drive->sectors_per_track = 63;
+               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 ) ?
@@ -465,7 +486,27 @@ void register_int13_drive ( struct int13_drive *drive ) {
                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
+ *
+ * @v drive            Emulated drive
+ *
+ * Registers the drive with the INT 13 emulation subsystem, and hooks
+ * the INT 13 interrupt vector (if not already hooked).
+ *
+ * The underlying block device must be valid.  A drive number and
+ * geometry will be assigned if left blank.
+ */
+void register_int13_drive ( struct int13_drive *drive ) {
+       uint8_t num_drives;
+
+       /* Give drive a default geometry if none specified */
+       guess_int13_geometry ( drive );
 
        /* Assign drive number if none specified, update BIOS drive count */
        get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );