[syslinux:master] chain module: setbpb changes, bss & bs options, bugfixes

syslinux-bot for Michal Soltys soltys at ziu.info
Mon Mar 26 15:03:17 PDT 2012


Commit-ID:  0d591b9348e43cf59cd4857dcc0e9029566d96e5
Gitweb:     http://www.syslinux.org/commit/0d591b9348e43cf59cd4857dcc0e9029566d96e5
Author:     Michal Soltys <soltys at ziu.info>
AuthorDate: Sat, 28 Aug 2010 01:06:13 +0200
Committer:  Michal Soltys <soltys at ziu.info>
CommitDate: Tue, 28 Sep 2010 09:32:52 +0200

chain module: setbpb changes, bss & bs options, bugfixes

Generic function detecting BPB type (7 versions) have been added.
set{hid,geo,drv} have been replaced by single setbpb option, using
mentioned function to make more precise decisions what to update where.

Full BSS and BS emulation has been added, also employing BPB detection.

Some logic/flow changes in chain's main(). There was also a bug, in
which backup sector was populated with wrong data.

Appropriate documentation updates.

Signed-off-by: Michal Soltys <soltys at ziu.info>

---
 com32/chain/chain.c   |  196 +++++++++++++-----------------------------
 com32/chain/chain.h   |   35 +--------
 com32/chain/mangle.c  |  226 +++++++++++++++++++++++++++++++++++++------------
 com32/chain/mangle.h  |   12 ++-
 com32/chain/options.c |  108 +++++++-----------------
 com32/chain/options.h |   32 +++++++
 com32/chain/utility.c |   68 +++++++++++++++
 com32/chain/utility.h |   10 ++
 doc/chain.txt         |   93 +++++++++------------
 9 files changed, 421 insertions(+), 359 deletions(-)

diff --git a/com32/chain/chain.c b/com32/chain/chain.c
index 3bd2a31..cd2a812 100644
--- a/com32/chain/chain.c
+++ b/com32/chain/chain.c
@@ -629,49 +629,21 @@ bail:
     return -1;
 }
 
-int setdrv_auto(const struct part_iter *iter)
-{
-    int a, b;
-    char *buf;
-
-    if (!(buf = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
-	error("Couldn't read a sector to detect 'setdrv' offset.\n");
-	return -1;
-    }
-
-    a = strncmp(buf + 0x36, "FAT", 3);
-    b = strncmp(buf + 0x52, "FAT", 3);
-
-    if ((!a && b && (buf[0x26] & 0xFE) == 0x28) || *((uint8_t*)buf + 0x26) == 0x80) {
-	opt.drvoff = 0x24;
-    } else if (a && !b && (buf[0x42] & 0xFE) == 0x28) {
-	opt.drvoff = 0x40;
-    } else {
-	error("WARNING: Couldn't autodetect 'setdrv' offset - turning option off.\n");
-	opt.setdrv = false;
-    }
-
-    free(buf);
-    return 0;
-
-}
-
 int main(int argc, char *argv[])
 {
     struct part_iter *iter = NULL;
 
-    void *file_area = NULL;
-    void *sect_area = NULL;
-    void *sbck_area = NULL;
-    struct disk_dos_part_entry *hand_area = NULL;
-
-    struct data_area data[3], bdata[3];
-    int ndata = 0, fidx = -1, sidx = -1, hidx = -1;
+    void *sbck = NULL;
+    struct data_area fdat, hdat, sdat, data[3];
+    int ndata = 0;
 
     console_ansi_raw();
 /*    openconsole(&dev_null_r, &dev_stdcon_w);*/
 
     /* Prepare and set defaults */
+    memset(&fdat, 0, sizeof(fdat));
+    memset(&hdat, 0, sizeof(hdat));
+    memset(&sdat, 0, sizeof(sdat));
     memset(&opt, 0, sizeof(opt));
     opt.sect = true;	/* by def load sector */
     opt.maps = true;	/* by def map sector */
@@ -686,18 +658,6 @@ int main(int argc, char *argv[])
     if (parse_args(argc, argv))
 	goto bail;
 
-    /* Set initial registry values */
-    if (opt.file) {
-	opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
-	opt.regs.ip = (uint16_t)opt.fip;
-    } else {
-	opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
-	opt.regs.ip = (uint16_t)opt.sip;
-    }
-
-    if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
-	opt.regs.esp.l = 0x7C00;
-
     /* Get max fixed disk number */
     fixed_cnt = *(uint8_t *)(0x475);
 
@@ -705,13 +665,6 @@ int main(int argc, char *argv[])
     if (find_dp(&iter))
 	goto bail;
 
-    /* Try to autodetect setdrv offest */
-    if (opt.setdrv && opt.drvoff == ~0u && setdrv_auto(iter))
-	goto bail;
-
-    /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
-    opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
-
     /* Perform initial partition entry mangling */
     if (opt.hide || opt.mbrchs)
 	pentry_mangle(iter);
@@ -719,135 +672,106 @@ int main(int argc, char *argv[])
 
     /* Load the boot file */
     if (opt.file) {
-	data[ndata].base = (opt.fseg << 4) + opt.foff;
+	fdat.base = (opt.fseg << 4) + opt.foff;
 
-	if (loadfile(opt.file, &data[ndata].data, &data[ndata].size)) {
+	if (loadfile(opt.file, &fdat.data, &fdat.size)) {
 	    error("Couldn't read the boot file.\n");
 	    goto bail;
 	}
-	file_area = (void *)data[ndata].data;
-
-	if (data[ndata].base + data[ndata].size - 1 > ADDRMAX) {
+	if (fdat.base + fdat.size - 1 > ADDRMAX) {
 	    error("The boot file is too big to load at this address.\n");
 	    goto bail;
 	}
-
-	fidx = ndata;
-	ndata++;
     }
 
     /* Load the sector */
     if (opt.sect) {
-	data[ndata].size = SECTOR;
-	data[ndata].base = (opt.sseg << 4) + opt.soff;
+	sdat.size = SECTOR;
+	sdat.base = (opt.sseg << 4) + opt.soff;
 
-	if (opt.file && opt.maps && overlap(data + fidx, data + ndata)) {
+	if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
 	    error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
+	    opt.sect = false;
 	} else {
-	    if (!(data[ndata].data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
+	    if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
 		error("Couldn't read the sector.\n");
 		goto bail;
 	    }
-	    sect_area = (void *)data[ndata].data;
-
 	    if (opt.save) {
-		if (!(sbck_area = malloc(SECTOR))) {
+		if (!(sbck = malloc(SECTOR))) {
 		    error("Couldn't allocate cmp-buf for option 'save'.\n");
 		    goto bail;
 		}
-		memcpy(sbck_area, data->data, data->size);
+		memcpy(sbck, sdat.data, sdat.size);
 	    }
-
-	    sidx = ndata;
-	    ndata++;
 	}
     }
 
     /* Prep the handover */
-    if (opt.hand && iter->index) {
-	if (setup_handover(iter, data + ndata))
+    if (!iter->index) {
+	opt.hand = false;
+    } else if (opt.hand) {
+	if (setup_handover(iter, &hdat))
 	    goto bail;
-	hand_area = (void *)data[ndata].data;
-
 	/* Verify possible conflicts */
-	if ( ( fidx >= 0 && overlap(data + fidx, data + ndata)) ||
-	     ( sidx >= 0 && opt.maps && overlap(data + sidx, data + ndata)) ) {
+	if ( ( opt.file && overlap(&fdat, &hdat)) ||
+	     ( opt.sect && overlap(&sdat, &hdat) && opt.maps) ) {
 	    error("WARNING: Handover area won't be prepared,\n"
 		  "as it would conflict with the boot file and/or the sector.\n");
-	} else {
-	    hidx = ndata;
-	    ndata++;
+	    opt.hand = false;
 	}
     }
 
+    /* Adjust registers */
+    mangler_common(iter);
+    mangler_handover(iter, &hdat);
+    mangler_grldr(iter);
+
     /*
-     *  Adjust registers - ds:si & ds:bp
-     *  We do it here, as they might get further
-     *  overriden during mangling.
+     * Patching functions
+     * opt.* are tested inside
      */
 
-    if (sidx >= 0 && fidx >= 0 && opt.maps && !opt.hptr) {
-	opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
-	opt.regs.ds = (uint16_t)opt.sseg;
-	opt.regs.eax.l = 0;
-    } else if (hidx >= 0) {
-	opt.regs.esi.l = opt.regs.ebp.l = data[hidx].base;
-	opt.regs.ds = 0;
-	if (iter->type == typegpt)
-	    opt.regs.eax.l = 0x54504721;	/* '!GPT' */
-	else
-	    opt.regs.eax.l = 0;
-    }
-
-    /* Do file related stuff */
-
-    if (fidx >= 0) {
-	if (manglef_isolinux(data + fidx))
-	    goto bail;
-
-	if (manglef_grldr(iter))
-	    goto bail;
+    if (manglef_isolinux(&fdat))
+	goto bail;
 
-	if (manglef_grub(iter, data + fidx))
-	    goto bail;
+    if (manglef_grub(iter, &fdat))
+	goto bail;
 #if 0
-	if (manglef_drmk(data + fidx))
-	    goto bail;
+    if (manglef_drmk(&fdat))
+	goto bail;
 #endif
-	if (manglef_bpb(iter, data + fidx))
-	    goto bail;
-    }
+    if (manglef_bpb(iter, &fdat))
+	goto bail;
 
-    /* Do sector related stuff */
+    if (mangles_bpb(iter, &sdat))
+	goto bail;
 
-    if (sidx >= 0) {
-	if (mangles_bpb(iter, data + sidx))
-	    goto bail;
+    if (mangles_save(iter, &sdat, sbck))
+	goto bail;
 
-	if (mangles_save(iter, data + sidx, sbck_area))
-	    goto bail;
+    if (manglesf_bss(&sdat, &fdat))
+	goto bail;
 
-	/* This *must* be after last BPB saving */
-	if (mangles_cmldr(data + sidx))
-	    goto bail;
-    }
+    /* This *must* be after BPB saving or copying */
+    if (mangles_cmldr(&sdat))
+	goto bail;
 
     /* Prepare boot-time mmap data */
 
-    ndata = 0;
-    if (sidx >= 0)
-	memcpy(bdata + ndata++, data + sidx, sizeof(struct data_area));
-    if (fidx >= 0)
-	memcpy(bdata + ndata++, data + fidx, sizeof(struct data_area));
-    if (hidx >= 0)
-	memcpy(bdata + ndata++, data + hidx, sizeof(struct data_area));
+    if (opt.file)
+	memcpy(data + ndata++, &fdat, sizeof(fdat));
+    if (opt.sect && opt.maps)
+	memcpy(data + ndata++, &sdat, sizeof(sdat));
+    if (opt.hand)
+	memcpy(data + ndata++, &hdat, sizeof(hdat));
 
 #ifdef DEBUG
     printf("iter dsk: %d\n", iter->di.disk);
     printf("iter idx: %d\n", iter->index);
     printf("iter lba: %llu\n", iter->start_lba);
-    if (hidx >= 0)
-	printf("hand lba: %u\n", hand_area->start_lba);
+    if (opt.hand)
+	printf("hand lba: %u\n", ((disk_dos_part_entry *)hdat.data)->start_lba);
 #endif
 
     if (opt.warn) {
@@ -855,14 +779,14 @@ int main(int argc, char *argv[])
 	wait_key();
     }
 
-    do_boot(bdata, ndata);
+    do_boot(data, ndata);
 bail:
     pi_del(&iter);
     /* Free allocated areas */
-    free(file_area);
-    free(sect_area);
-    free(sbck_area);
-    free(hand_area);
+    free(fdat.data);
+    free(sdat.data);
+    free(hdat.data);
+    free(sbck);
     return 255;
 }
 
diff --git a/com32/chain/chain.h b/com32/chain/chain.h
index 7e303b8..6714475 100644
--- a/com32/chain/chain.h
+++ b/com32/chain/chain.h
@@ -3,40 +3,7 @@
 
 #include <stdint.h>
 #include <syslinux/bootrm.h>
-
-struct options {
-    unsigned int fseg;
-    unsigned int foff;
-    unsigned int fip;
-    unsigned int sseg;
-    unsigned int soff;
-    unsigned int sip;
-    unsigned int drvoff;
-    const char *drivename;
-    const char *partition;
-    const char *file;
-    const char *grubcfg;
-    bool isolinux;
-    bool cmldr;
-    bool drmk;
-    bool grub;
-    bool grldr;
-    bool maps;
-    bool hand;
-    bool hptr;
-    bool swap;
-    int hide;
-    bool sethid;
-    bool setgeo;
-    bool setdrv;
-    bool sect;
-    bool save;
-    bool filebpb;
-    bool mbrchs;
-    bool warn;
-    uint16_t keeppxe;
-    struct syslinux_rm_regs regs;
-};
+#include "options.h"
 
 struct data_area {
     void *data;
diff --git a/com32/chain/mangle.c b/com32/chain/mangle.c
index 3ddff71..34b033e 100644
--- a/com32/chain/mangle.c
+++ b/com32/chain/mangle.c
@@ -6,6 +6,7 @@
 #include <syslinux/config.h>
 #include "common.h"
 #include "chain.h"
+#include "options.h"
 #include "utility.h"
 #include "partiter.h"
 #include "mangle.h"
@@ -24,7 +25,7 @@ int manglef_isolinux(struct data_area *data)
     uint32_t *checksum, *chkhead, *chktail;
     uint32_t file_lba = 0;
 
-    if (!opt.isolinux)
+    if (!(opt.file && opt.isolinux))
 	return 0;
 
     sdi = syslinux_derivative_info();
@@ -88,21 +89,6 @@ bail:
 }
 
 /*
- * GRLDR of GRUB4DOS wants the partition number in DH:
- * -1:   whole drive (default)
- * 0-3:  primary partitions
- * 4-*:  logical partitions
- */
-int manglef_grldr(const struct part_iter *iter)
-{
-    if (!opt.grldr)
-	return 0;
-
-    opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
-    return 0;
-}
-
-/*
  * Legacy grub's stage2 chainloading
  */
 int manglef_grub(const struct part_iter *iter, struct data_area *data)
@@ -142,7 +128,7 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
 	char codestart[1];
     } __attribute__ ((packed)) *stage2;
 
-    if (!opt.grub)
+    if (!(opt.file && opt.grub))
 	return 0;
 
     if (data->size < sizeof(struct grub_stage2_patch_area)) {
@@ -207,22 +193,62 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
 bail:
     return -1;
 }
-
+#if 0
 /*
- * Adjust BPB of a BPB-compatible file
+ * Dell's DRMK chainloading.
  */
-int mangles_bpb(const struct part_iter *iter, struct data_area *data)
+int manglef_drmk(struct data_area *data)
+{
+    /*
+     * DRMK entry is different than MS-DOS/PC-DOS
+     * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
+     * We only really need 4 new, usable bytes at the end.
+     */
+
+    if (!(opt.file && opt.drmk))
+	return 0;
+
+    uint32_t tsize = (data->size + 19) & 0xfffffff0;
+    opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;	/* Used before initialized */
+    if (!realloc(data->data, tsize)) {
+	error("Failed to realloc for DRMK.\n");
+	goto bail;
+    }
+    data->size = tsize;
+    /* ds:[bp+28] must be 0x0000003f */
+    opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
+    /* "Patch" into tail of the new space */
+    *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
+
+    return 0;
+bail:
+    return -1;
+}
+#endif
+/* Adjust BPB common function */
+static int mangle_bpb(const struct part_iter *iter, struct data_area *data)
 {
-    /* BPB: hidden sectors */
-    if (opt.sethid) {
+    unsigned int off;
+    int type = bpb_detect(data->data);
+
+    /* BPB: hidden sectors 32bit*/
+    if (type >= bpbV34) {
 	if (iter->start_lba < ~0u)
 	    *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
 	else
 	    /* won't really help much, but ... */
 	    *(uint32_t *) ((char *)data->data + 0x1c) = ~0u;
     }
+    /* BPB: hidden sectors 16bit*/
+    if (bpbV30 <= type && type <= bpbV32) {
+	if (iter->start_lba < 0xFFFF)
+	    *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)iter->start_lba;
+	else
+	    /* won't really help much, but ... */
+	    *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
+    }
     /* BPB: legacy geometry */
-    if (opt.setgeo) {
+    if (type >= bpbV30) {
 	if (iter->di.cbios)
 	    *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
 	else {
@@ -233,24 +259,91 @@ int mangles_bpb(const struct part_iter *iter, struct data_area *data)
 	}
     }
     /* BPB: drive */
-    if (opt.setdrv)
-	*(uint8_t *)((char *)data->data + opt.drvoff) = (uint8_t)
+    if (drvoff_detect(type, &off)) {
+	*(uint8_t *)((char *)data->data + off) = (uint8_t)
 	    (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
+    }
 
     return 0;
 }
 
+/*
+ * Adjust BPB of a BPB-compatible file
+ */
 int manglef_bpb(const struct part_iter *iter, struct data_area *data)
 {
-    if (!opt.filebpb)
+    if (!(opt.file && opt.filebpb))
+	return 0;
+
+    return mangle_bpb(iter, data);
+}
+
+/*
+ * Adjust BPB of a sector
+ */
+int mangles_bpb(const struct part_iter *iter, struct data_area *data)
+{
+    if (!(opt.sect && opt.setbpb))
+	return 0;
+
+    return mangle_bpb(iter, data);
+}
+
+/*
+ * This function performs full BPB patching, analogously to syslinux's
+ * native BSS. opt.drv is prereq
+ */
+int manglesf_bss(struct data_area *sec, struct data_area *fil)
+{
+    int type1, type2;
+    unsigned int cnt = 0;
+
+    if (!(opt.sect && opt.file && opt.bss))
 	return 0;
 
-    return mangles_bpb(iter, data);
+    type1 = bpb_detect(fil->data);
+    type2 = bpb_detect(sec->data);
+
+    if (type1 < 0 || type2 < 0) {
+	error("Option 'bss' can't determine BPB type.\n");
+	goto bail;
+    }
+    if (type1 != type2) {
+	error("Option 'bss' can't be used,\n"
+		"when a sector and a file have incompatible BPBs.\n");
+	goto bail;
+    }
+
+    /* Copy common 2.0 data */
+    memcpy((char *)fil->data + 0x0B, (char *)sec->data + 0x0B, 0x0D);
+
+    /* Copy 3.0+ data */
+    if (type1 <= bpbV30) {
+	cnt = 0x06;
+    } else if (type1 <= bpbV32) {
+	cnt = 0x08;
+    } else if (type1 <= bpbV34) {
+	cnt = 0x0C;
+    } else if (type1 <= bpbV40) {
+	cnt = 0x2E;
+    } else if (type1 <= bpbVNT) {
+	cnt = 0x3C;
+    } else if (type1 <= bpbV70) {
+	cnt = 0x42;
+    }
+    memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
+
+    return 0;
+bail:
+    return -1;
 }
 
+/*
+ * Save sector.
+ */
 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org)
 {
-    if (!opt.save)
+    if (!(opt.sect && opt.save))
 	return 0;
 
     if (memcmp(org, data->data, data->size)) {
@@ -258,12 +351,11 @@ int mangles_save(const struct part_iter *iter, const struct data_area *data, voi
 	    error("Cannot write the updated sector.\n");
 	    goto bail;
 	}
-	/* function is ready do be called again */
+	/* function can be called again */
 	memcpy(org, data->data, data->size);
     }
 
     return 0;
-
 bail:
     return -1;
 }
@@ -275,44 +367,66 @@ bail:
  */
 int mangles_cmldr(struct data_area *data)
 {
-    if (!opt.cmldr)
+    if (!(opt.sect && opt.cmldr))
 	return 0;
 
     memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
     return 0;
 }
 
-#if 0
-/*
- * Dell's DRMK chainloading.
- */
-int manglef_drmk(struct data_area *data)
+/* Set common registers */
+int mangler_common(const struct part_iter *iter)
 {
-    /*
-     * DRMK entry is different than MS-DOS/PC-DOS
-     * A new size, aligned to 16 bytes to ease use of ds:[bp+28].
-     * We only really need 4 new, usable bytes at the end.
-     */
+    /* Set initial registry values */
+    if (opt.file) {
+	opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.fseg;
+	opt.regs.ip = (uint16_t)opt.fip;
+    } else {
+	opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
+	opt.regs.ip = (uint16_t)opt.sip;
+    }
 
-    if (!opt.drmk)
-	return 0;
+    if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
+	opt.regs.esp.l = 0x7C00;
 
-    uint32_t tsize = (data->size + 19) & 0xfffffff0;
-    opt.regs.ss = opt.regs.fs = opt.regs.gs = 0;	/* Used before initialized */
-    if (!realloc(data->data, tsize)) {
-	error("Failed to realloc for DRMK.\n");
-	goto bail;
+    /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
+    opt.regs.ebx.b[0] = opt.regs.edx.b[0] = (uint8_t)iter->di.disk;
+
+    return 0;
+}
+
+/* ds:si & ds:bp */
+int mangler_handover(const struct part_iter *iter, const struct data_area *data)
+{
+    if (opt.sect && opt.file && opt.maps && !opt.hptr) {
+	opt.regs.esi.l = opt.regs.ebp.l = opt.soff;
+	opt.regs.ds = (uint16_t)opt.sseg;
+	opt.regs.eax.l = 0;
+    } else if (opt.hand) {
+	/* base is really 0x7be */
+	opt.regs.esi.l = opt.regs.ebp.l = data->base;
+	opt.regs.ds = 0;
+	if (iter->type == typegpt)
+	    opt.regs.eax.l = 0x54504721;	/* '!GPT' */
+	else
+	    opt.regs.eax.l = 0;
     }
-    data->size = tsize;
-    /* ds:[bp+28] must be 0x0000003f */
-    opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
-    /* "Patch" into tail of the new space */
-    *(uint32_t *)((char*)data->data + tsize - 4) = 0x0000003f;
 
     return 0;
-bail:
-    return -1;
 }
-#endif
+
+/*
+ * GRLDR of GRUB4DOS wants the partition number in DH:
+ * -1:   whole drive (default)
+ * 0-3:  primary partitions
+ * 4-*:  logical partitions
+ */
+int mangler_grldr(const struct part_iter *iter)
+{
+    if (opt.grldr)
+	opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
+
+    return 0;
+}
 
 /* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h
index 006fb3b..76bab4b 100644
--- a/com32/chain/mangle.h
+++ b/com32/chain/mangle.h
@@ -5,15 +5,19 @@
 #include "partiter.h"
 
 int manglef_isolinux(struct data_area *data);
-int manglef_grldr(const struct part_iter *iter);
 int manglef_grub(const struct part_iter *iter, struct data_area *data);
 int manglef_bpb(const struct part_iter *iter, struct data_area *data);
+/* int manglef_drmk(struct data_area *data);*/
+
 int mangles_bpb(const struct part_iter *iter, struct data_area *data);
 int mangles_save(const struct part_iter *iter, const struct data_area *data, void *org);
 int mangles_cmldr(struct data_area *data);
-#if 0
-int manglef_drmk(struct data_area *data);
-#endif
+
+int manglesf_bss(struct data_area *sec, struct data_area *fil);
+
+int mangler_common(const struct part_iter *iter);
+int mangler_handover(const struct part_iter *iter, const struct data_area *data);
+int mangler_grldr(const struct part_iter *iter);
 
 #endif
 
diff --git a/com32/chain/options.c b/com32/chain/options.c
index 6fb07fd..975224b 100644
--- a/com32/chain/options.c
+++ b/com32/chain/options.c
@@ -73,18 +73,13 @@ Usage:\n\
                          - defaults to 0:0x7C00:0x7C00\n\
                          - ommited o/i values default to 0x7C00\n\
     maps                 Map loaded sector into real memory\n\
-    nosethid[den]        Set BPB's hidden sectors field\n\
-    nosetgeo             Set BPB's sectors per track and heads fields\n\
-    nosetdrv[@<off>]     Set BPB's drive unit field at <off>\n\
-                         - <off> defaults to autodetection\n\
-                         - only 0x24 and 0x40 are accepted\n\
-    nosetbpb             Enable set{hid,geo,drv}\n\
-    nofilebpb            Treat file in memory as BPB compatible\n\
-", "\
-\nOptions #2 ('no' prefix specifies default value):\n\
+    nosetbpb             Fix BPB fields in loaded sector\n\
+    nofilebpb            Apply 'setbpb' to loaded file\n\
     nosave               Write adjusted sector back to disk\n\
     hand                 Prepare handover area\n\
     nohptr               Force ds:si and ds:bp to point to handover area\n\
+    bss=<filename>       Emulate syslinux's BSS\n\
+    bs=<filename>        Emulate syslinux's BS\n\
     noswap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
     nohide               Hide primary partitions, unhide selected partition\n\
     nohideall            Hide *all* partitions, unhide selected partition\n\
@@ -105,7 +100,6 @@ Usage:\n\
     grub=<loader>        Load GRUB Legacy stage2\n\
     grubcfg=<filename>   Set alternative config filename for GRUB Legacy\n\
     grldr=<loader>       Load GRUB4DOS grldr\n\
-    bss=<filename>       Emulate BSS (see doc/chain.txt for differences)\n\
 \nPlease see doc/chain.txt for the detailed documentation.\n\
 "
     };
@@ -134,13 +128,14 @@ int parse_args(int argc, char *argv[])
 		goto bail;
 	} else if (!strncmp(argv[i], "bss=", 4)) {
 	    opt.file = argv[i] + 4;
+	    opt.bss = true;
 	    opt.maps = false;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
-	    opt.filebpb = true;
+	    opt.setbpb = true;
 	    /* opt.save = true; */
+	} else if (!strncmp(argv[i], "bs=", 3)) {
+	    opt.file = argv[i] + 3;
+	    opt.sect = false;
+	    opt.filebpb = true;
 	} else if (!strncmp(argv[i], "isolinux=", 9)) {
 	    opt.file = argv[i] + 9;
 	    opt.isolinux = true;
@@ -151,10 +146,7 @@ int parse_args(int argc, char *argv[])
 	    opt.foff = 0;
 	    opt.fip = 0;
 	    opt.file = argv[i] + 6;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = 0x24;
+	    opt.setbpb = true;
 	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "cmldr=", 6)) {
@@ -163,10 +155,7 @@ int parse_args(int argc, char *argv[])
 	    opt.fip = 0;
 	    opt.file = argv[i] + 6;
 	    opt.cmldr = true;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = 0x24;
+	    opt.setbpb = true;
 	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "freedos=", 8)) {
@@ -175,10 +164,7 @@ int parse_args(int argc, char *argv[])
 	    opt.fip = 0;
 	    opt.sseg = 0x1FE0;
 	    opt.file = argv[i] + 8;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
+	    opt.setbpb = true;
 	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
@@ -189,10 +175,7 @@ int parse_args(int argc, char *argv[])
 	    opt.fip = v == 7 ? 0x200 : 0;  /* MS-DOS 7.0+ wants this ip */
 	    opt.sseg = 0x8000;
 	    opt.file = argv[i] + v;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
+	    opt.setbpb = true;
 	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "drmk=", 5)) {
@@ -204,10 +187,7 @@ int parse_args(int argc, char *argv[])
 	    opt.sip = 0;
 	    opt.file = argv[i] + 5;
 	    /* opt.drmk = true; */
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
+	    opt.setbpb = true;
 	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "grub=", 5)) {
@@ -252,44 +232,14 @@ int parse_args(int argc, char *argv[])
 	    opt.hide = 1;
 	} else if (!strcmp(argv[i], "hideall")) {
 	    opt.hide = 2;
-	} else if (!strcmp(argv[i], "sethid") ||
-		   !strcmp(argv[i], "sethidden")) {
-	    opt.sethid = true;
-	} else if (!strcmp(argv[i], "nosethid") ||
-		   !strcmp(argv[i], "nosethidden")) {
-	    opt.sethid = false;
-	} else if (!strcmp(argv[i], "setgeo")) {
-	    opt.setgeo = true;
-	} else if (!strcmp(argv[i], "nosetgeo")) {
-	    opt.setgeo = false;
-	} else if (!strncmp(argv[i], "setdrv",6)) {
-	    if (!argv[i][6])
-		v = ~0u;    /* autodetect */
-	    else if (argv[i][6] == '@' ||
-		    argv[i][6] == '=' ||
-		    argv[i][6] == ':') {
-		v = strtoul(argv[i] + 7, NULL, 0);
-		if (!(v == 0x24 || v == 0x40)) {
-		    error("Invalid 'setdrv' offset.\n");
-		    goto bail;
-		}
-	    } else {
-		    error("Invalid 'setdrv' specification.\n");
-		    goto bail;
-		}
-	    opt.setdrv = true;
-	    opt.drvoff = v;
-	} else if (!strcmp(argv[i], "nosetdrv")) {
-	    opt.setdrv = false;
 	} else if (!strcmp(argv[i], "setbpb")) {
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
-	    opt.setgeo = true;
-	    opt.sethid = true;
+	    opt.setbpb = true;
 	} else if (!strcmp(argv[i], "nosetbpb")) {
-	    opt.setdrv = false;
-	    opt.setgeo = false;
-	    opt.sethid = false;
+	    opt.setbpb = false;
+	} else if (!strcmp(argv[i], "filebpb")) {
+	    opt.filebpb = true;
+	} else if (!strcmp(argv[i], "nofilebpb")) {
+	    opt.filebpb = false;
 	} else if (!strncmp(argv[i], "sect=", 5) ||
 		   !strcmp(argv[i], "sect")) {
 	    if (argv[i][4]) {
@@ -307,10 +257,6 @@ int parse_args(int argc, char *argv[])
 	    opt.save = true;
 	} else if (!strcmp(argv[i], "nosave")) {
 	    opt.save = false;
-	} else if (!strcmp(argv[i], "filebpb")) {
-	    opt.filebpb = true;
-	} else if (!strcmp(argv[i], "nofilebpb")) {
-	    opt.filebpb = false;
 	} else if (!strcmp(argv[i], "mbrchs")) {
 	    opt.mbrchs = true;
 	} else if (!strcmp(argv[i], "nombrchs")) {
@@ -358,7 +304,17 @@ int parse_args(int argc, char *argv[])
     }
 
     if (opt.filebpb && !opt.file) {
-	error("Option 'filebpb' requires file.\n");
+	error("Option 'filebpb' requires a file.\n");
+	goto bail;
+    }
+
+    if (opt.save && !opt.sect) {
+	error("Option 'save' requires a sector.\n");
+	goto bail;
+    }
+
+    if (opt.setbpb && !opt.sect) {
+	error("Option 'setbpb' requires a sector.\n");
 	goto bail;
     }
 
diff --git a/com32/chain/options.h b/com32/chain/options.h
index acd50e6..ee15768 100644
--- a/com32/chain/options.h
+++ b/com32/chain/options.h
@@ -1,6 +1,38 @@
 #ifndef _COM32_CHAIN_OPTIONS_H
 #define _COM32_CHAIN_OPTIONS_H
 
+struct options {
+    unsigned int fseg;
+    unsigned int foff;
+    unsigned int fip;
+    unsigned int sseg;
+    unsigned int soff;
+    unsigned int sip;
+    const char *drivename;
+    const char *partition;
+    const char *file;
+    const char *grubcfg;
+    bool isolinux;
+    bool cmldr;
+    bool drmk;
+    bool grub;
+    bool grldr;
+    bool maps;
+    bool hand;
+    bool hptr;
+    bool swap;
+    int hide;
+    bool sect;
+    bool save;
+    bool bss;
+    bool setbpb;
+    bool filebpb;
+    bool mbrchs;
+    bool warn;
+    uint16_t keeppxe;
+    struct syslinux_rm_regs regs;
+};
+
 int soi_s2n(char *ptr, unsigned int *seg, unsigned int *off,
 	unsigned int *ip, unsigned int def);
 void usage(void);
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
index 5aa196d..6894305 100644
--- a/com32/chain/utility.c
+++ b/com32/chain/utility.c
@@ -112,4 +112,72 @@ uint32_t get_file_lba(const char *filename)
     return lba;
 }
 
+/* drive offset detection */
+int drvoff_detect(int type, unsigned int *off)
+{
+    if (bpbV40 <= type && type <= bpbVNT) {
+	*off = 0x24;
+    } else if (type == bpbV70) {
+	*off = 0x40;
+    } else
+	return 0;
+
+    return -1;
+}
+
+/*
+ * heuristics could certainly be improved
+ */
+int bpb_detect(const uint8_t *sec)
+{
+    int a, b, c, jmp = -1, rev = -1;
+
+    /* media descriptor check */
+    if ((sec[0x15] & 0xF0) != 0xF0)
+	return -1;
+
+    if (sec[0] == 0xEB)	/* jump short */
+	jmp = 2 + *(int8_t *)(sec + 1);
+    else if (sec[0] == 0xE9) /* jump near */
+	jmp = 3 + *(int16_t *)(sec + 1);
+
+    if (jmp < 0)    /* no boot code at all ? */
+	goto nocode;
+
+    /* sanity */
+    if (jmp < 0x18 || jmp > 0x1F0)
+	return -1;
+
+    /* detect by jump */
+    if (jmp >= 0x18 && jmp < 0x1E)
+	rev = bpbV20;
+    else if (jmp >= 0x1E && jmp < 0x20)
+	rev = bpbV30;
+    else if (jmp >= 0x20 && jmp < 0x24)
+	rev = bpbV32;
+    else if (jmp >= 0x24 && jmp < 0x46)
+	rev = bpbV34;
+
+    /* TODO: some better V2 - V3.4 checks ? */
+
+    if (rev >= 0)
+	return rev;
+
+nocode:
+    a = memcmp(sec + 0x03, "NTFS", 4);
+    b = memcmp(sec + 0x36, "FAT", 3);
+    c = memcmp(sec + 0x52, "FAT", 3);	/* ext. DOS 7+ bs */
+
+    if ((sec[0x26] & 0xFE) == 0x28 && !b) {
+	rev = bpbV40;
+    } else if (sec[0x26] == 0x80 && !a) {
+	rev = bpbVNT;
+    } else if ((sec[0x42] & 0xFE) == 0x28 && !c) {
+	rev = bpbV70;
+    }
+
+    return rev;
+}
+
+
 /* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/utility.h b/com32/chain/utility.h
index b77f633..0cdb36f 100644
--- a/com32/chain/utility.h
+++ b/com32/chain/utility.h
@@ -4,11 +4,21 @@
 #include <stdint.h>
 #include <syslinux/disk.h>
 
+#define bpbV20	1
+#define bpbV30	2
+#define bpbV32	3
+#define bpbV34	4
+#define bpbV40	5
+#define bpbVNT	6
+#define bpbV70	7
+
 void error(const char *msg);
 int guid_is0(const struct guid *guid);
 void wait_key(void);
 uint32_t lba2chs(const struct disk_info *di, uint64_t lba);
 uint32_t get_file_lba(const char *filename);
+int drvoff_detect(int type, unsigned int *off);
+int bpb_detect(const uint8_t *bpb);
 
 #endif
 
diff --git a/doc/chain.txt b/doc/chain.txt
index 5f28fbe..1443c24 100644
--- a/doc/chain.txt
+++ b/doc/chain.txt
@@ -19,18 +19,15 @@ In more details, the flow of code is as follows:
 1.  Parse arguments.
 2.  Find drive and/or partition to boot from.
 3.  Hide / unhide systems and/or fix chs values in partition entries on the
-    drive syslinux is booting from (options: 'hide', 'hideall', 'mbrchs').
-4.  Load file to boot from (options: 'file', 'seg').
-5.  Load sector to boot from (options: 'sect', 'maps'), if it doesn't conflict
-    with #5.
-6.  Prepare handover area (options: 'hand', 'maps'), if it doesn't conflict
-    with #5 & #6, and syslinux is booting a partition.
-7.  Adjust ds:si and ds:bp to point to either a handover, a sector (options:
-    'hptr', 'maps', 'sect', 'file') or nowhere.
-8.  Patch loaded file if necessary ('isolinux', 'grub', 'grldr', 'filebpb')
-9.  Patch loaded sector if necessary ('setdrv', 'sethid', 'setgeo', 'setbpb',
-    'save', 'cmldr')
-10. Chainload (options: 'swap').
+    drive syslinux is booting from.
+4.  Load a file to boot from.
+5.  Load a sector to boot from, if it doesn't conflict with #5.
+6.  Prepare handover area, if it doesn't conflict with #5 & #6, and syslinux is
+    booting a partition.
+7.  Prepare registers.
+8.  Patch loaded file if necessary.
+9.  Patch loaded sector if necessary.
+10. Chainload.
 
 In most basic form, syslinux loads specified boot sector (or mbr, if not
 specified) at 0:0x7c00, prepares handover area as a standard mbr would do, and
@@ -98,7 +95,7 @@ This triplet lets you alter the addresses a file will use. Loading is done to
 bootloader or kernel, it's almost always mandatory.
 
 The defaults, if option is not specified, are 0:0x7c00:0x7c00
-If some of the fields are ommited (e.g. 0x2000::), they default to 0.
+If any of the fields are ommited (e.g. 0x2000::), they default to 0.
 
 	sect=<segment>:<offset>:<ip>
 	nosect
@@ -125,14 +122,8 @@ continue booting. 'nomaps' allows that - a sector will be loaded, but won't be
 mmapped into real memory. Any overlap tests (vs. handover or file areas) are
 not performed, being meaningless in such case.
 
-	sethid[den]
-	*nosethid[den]
-
-	setgeo
-	*nosetgeo
-
-	setdrv[@<addr>]
-	*nodrv
+	setbpb
+	*nosetbpb
 
 Microsoft side of the world is paritculary bitchy about certain BPB values.
 Depending on the system and chainloading method (sector or file), some or all
@@ -145,19 +136,9 @@ The "reality" means:
 "geometry" - valid disk geometry as reported by BIOS
 "drive" - valid drive number
 
-'sethidden'
-  updates the partition offset to match the current disk position
-'setgeo'
-  updates "heads" and "sectors per track" values as reported by BIOS
-'setdrv'
-  will update the drive value at proper offset. Only 0x24 and 0x40 are
-  accepted as valid values. You can use '@', '=' and ':' as delimiters.
-  If the address is not specified, it's autodetected.
-
-	setbpb
-	*nosetbpb
-
-Handy shortcut for setdrv, setgeo and sethid.
+This option will automatically determine the type of BPB and fix what is possible
+to fix, relatively to detected BPB. If it's impossible to detect BPB, function
+will do nothing.
 
 	filebpb
 	*nofilebpb
@@ -174,10 +155,10 @@ together with 'setbpb' or other ones using that implicitly.
 
 - this option never applies to a loaded file
 - chain module will never save anything to disk by default
-- writing is only performed, if the values actually changed
+- writing is only performed, if the values actually got changed
 
-	hand
 	*hand
+	nohand
 
 By default, a handover area is always prepared if possible and potentially
 useful - meaning it doesn't overlap with other areas, and syslinux chainloads a
@@ -206,7 +187,7 @@ In certain situations it's useful to hide partitions - for example to make sure
 DOS gets C:. 'hide' will hide hidable primary partitions, except the one we're
 booting from. Similary, 'hideall' will hide all hidable partitions, except the
 one we're booting from. Hiding is performed only on the boot drive.
-Writing is only performed, if the values actually changed.
+Writing is only performed, if the values actually got changed.
 
 	mbrchs
 	*nombrchs
@@ -217,13 +198,13 @@ FreeDOS complainig about 'logical CHS differs from physcial' of sfdisk about
 'found (...) expected (...).  Functionally seems to be mostly cosmetic, as
 Microsoft world - in cases it cares about geometry - generally sticks to values
 written in bootsectors. And the rest of the world generally doesn't care about
-them at all. Writing is only performed, if the values actually changed.
+them at all. Writing is only performed, if the values actually got changed.
 
 	keepexe
 	*nokeepexe
 
 If you're booting over a network using pxelinux - this lets you keep UNDI
-stacks in memory.
+stacks in memory (pxelinux only).
 
 	warn
 	*nowarn
@@ -232,7 +213,7 @@ This option will wait for a keypress right before continuing the chainloading.
 Useful to see warnings emited by the chain module.
 
 	isolinux=<file>
-	sets: file=<file> nohand nosect
+	sets: file=<file> nohand nosect isolinux
 
 Chainload another version/build of the ISOLINUX bootloader and patch the loader
 with appropriate parameters in memory. This avoids the need for the
@@ -246,7 +227,7 @@ Prepares to load ntldr directly. You might want to add 'save' option to store
 corrected BPB values.
 
 	cmldr=<file>
-	sets: file=<file> seg=0x2000 setbpb nohand
+	sets: file=<file> seg=0x2000 setbpb nohand cmldr
 
 Prepares to load recovery console directly. In-memory copy of bootsector is
 patched with "cmdcons\0". Remarks the same as in 'ntldr='.
@@ -257,7 +238,8 @@ patched with "cmdcons\0". Remarks the same as in 'ntldr='.
 Prepares to load freedos kernel directly. You will likely want to add 'save'
 option, as those kernels seem to require proper geometry written back to disk.
 Sector address is chosen based on where freedos' bootsectors relocate themselves,
-although it seems the kernel doesn't rely on one.
+although it seems the kernel doesn't rely on it.
+
 You might also want to employ 'hide' option, if you have problems with properly
 assigned C: drive.
 
@@ -271,37 +253,42 @@ Sector address is chosen arbitrarily. Otherwise comments as above.
 	msdos7=<file>
 	sets: file=<file> seg=0x70::0x200 sect=0x8000 setbpb nohand
 
-Only for MSDOS 7+ versions (98se ~ 7.xx, millenium ~ 8.xx). Comments as above.
+Only for MSDOS 7+ versions (98se ~ 7.xx, Me ~ 8.xx). Comments as above.
 TODO/TEST
 
 	drmk=<file>
 	sets: file=<file> seg=0x70 sect=0x2000:0:0 setbpb nohand
 
 This is used for loading of *only* Dell's DOS derivatives. It does require boot
-sector at 0x2000 and overall valid BPB values. As with other DOSish cases,
+sector at 0x2000 and overall valid BPB values. As in other DOS-ish cases,
 likely candidates for use are 'save' and 'hide'.
 
 	grub=<file>
 	grubcfg=<config>
-	sets: file=<file> seg=0x800::0x200 nohand nosect
+	sets: file=<file> seg=0x800::0x200 nohand nosect grub
 
 Chainloads grub legacy's stage2, performing additional corrections on the file
 in memory. Additionally, alternate config file can be specified through
 'grubcfg=' option
 
 	grldr=<file>
-	sets: file=<file> nohand nosect
+	sets: file=<file> nohand nosect grldr
 
 Chainloads GRUB4DOS grldr, performing additional corrections on the file
 in memory.
 
 	bss=<file>
-	sets: bss=<file> nomaps setbpb filebpb
+	sets: bss=<file> nomaps setbpb bss
+
+This emulates syslinux's native BSS option. This loads both the file and the
+sector, adjusts BPB values in the loaded sector, then copies all possible BPB
+fields to the loaded file. Everything is made with reference to selected
+disk/partition.
+
+	bs=<file>
+	sets: bs=<file> nosect filebpb
 
-This attempts to emulate syslinux's native BSS option to certain extent. This
-loads both the file and the sector and adjusts BPB values in both. As only basic BPB
-fields are corrected, both the file and the sector should be identical enough
-(except boot code, drive number, geometry, hidden sectors). Don't forget about
-'save' and 'swap' options.
+This emulates syslinux's native BS option. This loads the file and if possible
+- adjusts its BPB values. Everything is made with reference to selected
+disk/partition.
 
-This will likely get expanded in future.


More information about the Syslinux-commits mailing list