From 3ec9e0ec1012ff5699d0dd68f4926e36e62cfe3a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Mar 2009 13:18:49 -0700 Subject: [PATCH] memdisk: auto-detect large floppy geometry if it is FAT If a "large floppy" image is formatted with a FAT filesystem, we can use the headers in the FAT image to derive the geometry. This is nice and user-friendly, so do it that way. --- NEWS | 3 ++ memdisk/setup.c | 153 +++++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 116 insertions(+), 40 deletions(-) diff --git a/NEWS b/NEWS index de62659..1fcb3ab 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ Changes in 3.74: * gPXE updated to version 0.9.7. * hdt.c32: Hardware Detection Tool, an interactive hardware analyzer module by Erwan Velu. + * MEMDISK: enable automatic determination of the disk geometry + for a large floppy disk image if (and only if) it is + formatted with a FAT filesystem. Changes in 3.73: * Upgrade gPXE to release version 0.9.5. diff --git a/memdisk/setup.c b/memdisk/setup.c index 7b1f456..c30a11f 100644 --- a/memdisk/setup.c +++ b/memdisk/setup.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2009 H. Peter Anvin - All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -384,7 +384,49 @@ struct ptab_entry { uint8_t end_h, end_s, end_c; uint32_t start; uint32_t size; -}; +} __attribute__((packed)); + +/* Format of a FAT filesystem superblock */ +struct fat_extra { + uint8_t bs_drvnum; + uint8_t bs_resv1; + uint8_t bs_bootsig; + uint32_t bs_volid; + char bs_vollab[11]; + char bs_filsystype[8]; +} __attribute__((packed)); +struct fat_super { + uint8_t bs_jmpboot[3]; + char bs_oemname[8]; + uint16_t bpb_bytspersec; + uint8_t bpb_secperclus; + uint16_t bpb_rsvdseccnt; + uint8_t bpb_numfats; + uint16_t bpb_rootentcnt; + uint16_t bpb_totsec16; + uint8_t bpb_media; + uint16_t bpb_fatsz16; + uint16_t bpb_secpertrk; + uint16_t bpb_numheads; + uint32_t bpb_hiddsec; + uint32_t bpb_totsec32; + union { + struct { + struct fat_extra extra; + } fat16; + struct { + uint32_t bpb_fatsz32; + uint16_t bpb_extflags; + uint16_t bpb_fsver; + uint32_t bpb_rootclus; + uint16_t bpb_fsinfo; + uint16_t bpb_bkbootsec; + char bpb_reserved[12]; + /* Clever, eh? Same fields, different offset... */ + struct fat_extra extra; + } fat32 __attribute__((packed)); + } x; +} __attribute__((packed)); /* Format of a DOSEMU header */ struct dosemu_header { @@ -401,13 +443,10 @@ struct dosemu_header { const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) { static struct geometry hd_geometry; - struct ptab_entry ptab[4]; /* Partition table buffer */ struct dosemu_header dosemu; unsigned int sectors, v; - unsigned int max_c, max_h, max_s; - unsigned int c, h, s, offset; + unsigned int offset; int i; - int drive_specified; const char *p; printf("command line: %s\n", shdr->cmdline); @@ -425,6 +464,7 @@ const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) while (!ok) { /* Assume it's a floppy drive, guess a geometry */ unsigned int type, track; + int c, h, s; if (xsectors < 320*2) { c = 40; h = 1; type = 1; @@ -488,44 +528,64 @@ const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) if ( CMD_HASDATA(p = getcmditem("s")) && (v = atou(p)) ) hd_geometry.s = v; - if ( (p = getcmditem("floppy")) != CMD_NOTFOUND ) { - hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0; - if ( hd_geometry.type == 0 ) - hd_geometry.type = 0x10; /* ATAPI floppy, e.g. LS-120 */ - drive_specified = 1; - } else if ( (p = getcmditem("harddisk")) != CMD_NOTFOUND ) { - hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80; - hd_geometry.type = 0; - drive_specified = 1; - } + if ( !hd_geometry.c || !hd_geometry.h || !hd_geometry.s ) { + int h, s, max_h, max_s; + + max_h = max_s = 0; + + if ( hd_geometry.driveno & 0x80 ) { + /* Hard disk image, need to examine the partition table for geometry */ + const struct ptab_entry *ptab = (const struct ptab_entry *) + ((char *)where+hd_geometry.offset+(512-2-4*16)); + + for ( i = 0 ; i < 4 ; i++ ) { + if ( ptab[i].type ) { + s = (ptab[i].start_s & 0x3f); + h = ptab[i].start_h + 1; + + if ( max_h < h ) max_h = h; + if ( max_s < s ) max_s = s; + + s = (ptab[i].end_s & 0x3f); + h = ptab[i].end_h + 1; + + if ( max_h < h ) max_h = h; + if ( max_s < s ) max_s = s; + } + } + } - if ( (hd_geometry.c == 0) || (hd_geometry.h == 0) || - (hd_geometry.s == 0) ) { - /* Hard disk image, need to examine the partition table for geometry */ - memcpy(&ptab, (char *)where+hd_geometry.offset+(512-2-4*16), sizeof ptab); - - max_c = max_h = 0; max_s = 1; - for ( i = 0 ; i < 4 ; i++ ) { - if ( ptab[i].type ) { - c = ptab[i].start_c + (ptab[i].start_s >> 6); - s = (ptab[i].start_s & 0x3f); - h = ptab[i].start_h; - - if ( max_c < c ) max_c = c; - if ( max_h < h ) max_h = h; - if ( max_s < s ) max_s = s; - - c = ptab[i].end_c + (ptab[i].end_s >> 6); - s = (ptab[i].end_s & 0x3f); - h = ptab[i].end_h; - - if ( max_c < c ) max_c = c; - if ( max_h < h ) max_h = h; - if ( max_s < s ) max_s = s; + if (!max_h && !max_s) { + /* Floppy image, or unpartitioned hard disk... look for a FAT + superblock and if we find something that looks enough like one, + use geometry from that. */ + const struct fat_extra *extra = NULL; + const struct fat_super *fs = (const struct fat_super *) + ((char *)where+hd_geometry.offset); + + if ((fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8) && + (fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb) && + fs->bpb_bytspersec == 512 && + fs->bpb_numheads >= 1 && fs->bpb_numheads <= 256 && + fs->bpb_secpertrk >= 1 && fs->bpb_secpertrk <= 63) { + extra = fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra; + if (!(extra->bs_bootsig == 0x29 && + extra->bs_filsystype[0] == 'F' && + extra->bs_filsystype[1] == 'A' && + extra->bs_filsystype[2] == 'T')) + extra = NULL; + } + if (extra) { + hd_geometry.driveno = extra->bs_drvnum & 0x80; + max_h = fs->bpb_numheads; + max_s = fs->bpb_secpertrk; } } - max_c++; max_h++; /* Convert to count (1-based) */ + if (!max_h) + max_h = sectors > 2097152 ? 255 : 64; + if (!max_s) + max_s = sectors > 2097152 ? 63 : 32; if ( !hd_geometry.h ) hd_geometry.h = max_h; @@ -535,6 +595,19 @@ const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) hd_geometry.c = sectors/(hd_geometry.h*hd_geometry.s); } + if ( (p = getcmditem("floppy")) != CMD_NOTFOUND ) { + hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0; + } else if ( (p = getcmditem("harddisk")) != CMD_NOTFOUND ) { + hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80; + } + + if (hd_geometry.driveno & 0x80) { + hd_geometry.type = 0; /* Type = hard disk */ + } else { + if (hd_geometry.type == 0) + hd_geometry.type = 0x10; /* ATAPI floppy, e.g. LS-120 */ + } + if ( (size-hd_geometry.offset) & 0x1ff ) { puts("MEMDISK: Image has fractional end sector\n"); } -- 2.7.4