--- grub-0.94/lib/device.c.26geom 2004-04-14 14:05:24.786938853 -0400 +++ grub-0.94/lib/device.c 2004-04-14 14:32:50.768600176 -0400 @@ -88,6 +88,122 @@ #include #include +#if defined(__linux__) +/* The 2.6 kernel has removed all of the geometry handling for IDE drives + * that did fixups for LBA, etc. This means that the geometry we get + * with the ioctl has a good chance of being wrong. So, we get to + * also know about partition tables and try to read what the geometry + * is there. *grumble* Very closely based on code from cfdisk + */ +static void get_kernel_geometry(int fd, int *cyl, int *heads, int *sectors) { + struct hd_geometry hdg; + + if (ioctl (fd, HDIO_GETGEO, &hdg)) + return; + + *cyl = hdg.cylinders; + *heads = hdg.heads; + *sectors = hdg.sectors; +} + +struct partition { + unsigned char boot_ind; /* 0x80 - active */ + unsigned char head; /* starting head */ + unsigned char sector; /* starting sector */ + unsigned char cyl; /* starting cylinder */ + unsigned char sys_ind; /* What partition type */ + unsigned char end_head; /* end head */ + unsigned char end_sector; /* end sector */ + unsigned char end_cyl; /* end cylinder */ + unsigned char start4[4]; /* starting sector counting from 0 */ + unsigned char size4[4]; /* nr of sectors in partition */ +}; + +#define ALIGNMENT 2 +typedef union { + struct { + unsigned char align[ALIGNMENT]; + unsigned char b[SECTOR_SIZE]; + } c; + struct { + unsigned char align[ALIGNMENT]; + unsigned char buffer[0x1BE]; + struct partition part[4]; + unsigned char magicflag[2]; + } p; +} partition_table; + +#define PART_TABLE_FLAG0 0x55 +#define PART_TABLE_FLAG1 0xAA + +static void +get_partition_table_geometry(partition_table *bufp, int *cyl, int *heads, + int *sectors) { + struct partition *p; + int i,h,s,hh,ss; + int first = 1; + int bad = 0; + + if (bufp->p.magicflag[0] != PART_TABLE_FLAG0 || + bufp->p.magicflag[1] != PART_TABLE_FLAG1) { + /* Matthew Wilcox: slightly friendlier version of + fatal(_("Bad signature on partition table"), 3); + */ + fprintf(stderr, "Unknown partition table signature\n"); + return; + } + + hh = ss = 0; + for (i=0; i<4; i++) { + p = &(bufp->p.part[i]); + if (p->sys_ind != 0) { + h = p->end_head + 1; + s = (p->end_sector & 077); + if (first) { + hh = h; + ss = s; + first = 0; + } else if (hh != h || ss != s) + bad = 1; + } + } + + if (!first && !bad) { + *heads = hh; + *sectors = ss; + } +} + +static void get_linux_geometry (int fd, struct geometry *geom) { + int kern_cyl = 0, kern_head = 0, kern_sectors = 0; + int pt_cyl = 0, pt_head = 0, pt_sectors = 0; + partition_table bufp; + + get_kernel_geometry(fd, &kern_cyl, &kern_head, &kern_sectors); + + if (read(fd, bufp.c.b, SECTOR_SIZE) == SECTOR_SIZE) { + get_partition_table_geometry(&bufp, &pt_cyl, &pt_head, &pt_sectors); + } else { + fprintf(stderr, "Unable to read partition table: %s\n", strerror(errno)); + } + + if (pt_head && pt_sectors) { + int cyl_size; + + geom->heads = pt_head; + geom->sectors = pt_sectors; + cyl_size = pt_head * pt_sectors; + geom->cylinders = geom->total_sectors/cyl_size; + } else { + geom->heads = kern_head; + geom->sectors = kern_sectors; + geom->cylinders = kern_cyl; + } + + return; +} +#endif + /* Get the geometry of a drive DRIVE. */ void get_drive_geometry (struct geometry *geom, char **map, int drive) @@ -100,20 +216,16 @@ #if defined(__linux__) /* Linux */ { - struct hd_geometry hdg; unsigned long nr; - if (ioctl (fd, HDIO_GETGEO, &hdg)) - goto fail; - if (ioctl (fd, BLKGETSIZE, &nr)) goto fail; - - /* Got the geometry, so save it. */ - geom->cylinders = hdg.cylinders; - geom->heads = hdg.heads; - geom->sectors = hdg.sectors; + geom->total_sectors = nr; + get_linux_geometry(fd, geom); + + if (!geom->heads && !geom->cylinders && !geom->sectors) + goto fail; close (fd); return;