[syslinux:master] chain.c: Split chain into smaller files

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


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

chain.c: Split chain into smaller files

new file:   chain.h
new file:   common.h
new file:   mangle.c
new file:   mangle.h
new file:   options.c
new file:   options.h
new file:   utility.c
new file:   utility.h

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

---
 com32/chain/Makefile   |    2 +-
 com32/chain/chain.c    |  788 +-----------------------------------------------
 com32/chain/chain.h    |   50 +++
 com32/chain/common.h   |    9 +
 com32/chain/mangle.c   |  318 +++++++++++++++++++
 com32/chain/mangle.h   |   19 ++
 com32/chain/options.c  |  336 +++++++++++++++++++++
 com32/chain/options.h  |   10 +
 com32/chain/partiter.c |   12 +-
 com32/chain/partiter.h |    6 +-
 com32/chain/utility.c  |  114 +++++++
 com32/chain/utility.h  |   15 +
 12 files changed, 891 insertions(+), 788 deletions(-)

diff --git a/com32/chain/Makefile b/com32/chain/Makefile
index 337775c..054c768 100644
--- a/com32/chain/Makefile
+++ b/com32/chain/Makefile
@@ -15,7 +15,7 @@
 topdir = ../..
 include ../MCONFIG
 
-OBJS = chain.o partiter.o
+OBJS = chain.o partiter.o utility.o options.o mangle.o
 GCCEXTRA = -Wextra -Wconversion -pedantic -Wno-error -DDEBUG
 
 all: chain.c32
diff --git a/com32/chain/chain.c b/com32/chain/chain.c
index 5af6c15..d1939ec 100644
--- a/com32/chain/chain.c
+++ b/com32/chain/chain.c
@@ -34,83 +34,22 @@
 #include <syslinux/config.h>
 #include <syslinux/disk.h>
 #include <syslinux/video.h>
+#include "common.h"
+#include "chain.h"
+#include "utility.h"
+#include "options.h"
 #include "partiter.h"
+#include "mangle.h"
 
-/* used in checks, whenever addresses supplied by user are sane */
+struct options opt;
 
-#define ADDRMAX 0x9EFFFu
-#define ADDRMIN 0x500u
-
-static const char cmldr_signature[8] = "cmdcons";
 static int fixed_cnt;
 
-static 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;
-    bool hide;
-    bool sethid;
-    bool setgeo;
-    bool setdrv;
-    bool sect;
-    bool save;
-    bool filebpb;
-    bool warn;
-    uint16_t keeppxe;
-    struct syslinux_rm_regs regs;
-} opt;
-
-struct data_area {
-    void *data;
-    addr_t base;
-    addr_t size;
-};
-
-static void wait_key(void)
-{
-    int cnt;
-    char junk;
-
-    /* drain */
-    do {
-	errno = 0;
-	cnt = read(0, &junk, 1);
-    } while (cnt > 0 || (cnt < 0 && errno == EAGAIN));
-
-    /* wait */
-    do {
-	errno = 0;
-	cnt = read(0, &junk, 1);
-    } while (!cnt || (cnt < 0 && errno == EAGAIN));
-}
-
-static void error(const char *msg)
-{
-    fputs(msg, stderr);
-}
-
-static int no_ov(const struct data_area *a, const struct data_area *b)
+static int overlap(const struct data_area *a, const struct data_area *b)
 {
     return
-	a->base + a->size <= b->base ||
-	b->base + b->size <= a->base;
+	a->base + a->size > b->base &&
+	b->base + b->size > a->base;
 }
 
 static int is_phys(uint8_t sdifs)
@@ -400,377 +339,6 @@ out:
     free(mbr);
 }
 
-static uint32_t get_file_lba(const char *filename)
-{
-    com32sys_t inregs;
-    uint32_t lba;
-
-    /* Start with clean registers */
-    memset(&inregs, 0, sizeof(com32sys_t));
-
-    /* Put the filename in the bounce buffer */
-    strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
-
-    /* Call comapi_open() which returns a structure pointer in SI
-     * to a structure whose first member happens to be the LBA.
-     */
-    inregs.eax.w[0] = 0x0006;
-    inregs.esi.w[0] = OFFS(__com32.cs_bounce);
-    inregs.es = SEG(__com32.cs_bounce);
-    __com32.cs_intcall(0x22, &inregs, &inregs);
-
-    if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
-	return 0;		/* Filename not found */
-    }
-
-    /* Since the first member is the LBA, we simply cast */
-    lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
-
-    /* Clean the registers for the next call */
-    memset(&inregs, 0, sizeof(com32sys_t));
-
-    /* Put the filename in the bounce buffer */
-    strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
-
-    /* Call comapi_close() to free the structure */
-    inregs.eax.w[0] = 0x0008;
-    inregs.esi.w[0] = OFFS(__com32.cs_bounce);
-    inregs.es = SEG(__com32.cs_bounce);
-    __com32.cs_intcall(0x22, &inregs, &inregs);
-
-    return lba;
-}
-
-/* Convert string seg:off:ip values into numerical seg:off:ip ones */
-
-static int soi_s2n(char *ptr, unsigned int *seg,
-			      unsigned int *off,
-			      unsigned int *ip)
-{
-    unsigned int segval = 0, offval = 0, ipval = 0, val;
-    char *p;
-
-    segval = strtoul(ptr, &p, 0);
-    if (*p == ':')
-	offval = strtoul(p+1, &p, 0);
-    if (*p == ':')
-	ipval = strtoul(p+1, NULL, 0);
-
-    val = (segval << 4) + offval;
-
-    if (val < ADDRMIN || val > ADDRMAX) {
-	error("Invalid seg:off:* address specified..\n");
-	goto bail;
-    }
-
-    val = (segval << 4) + ipval;
-
-    if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
-	error("Invalid seg:*:ip address specified.\n");
-	goto bail;
-    }
-
-    if (seg)
-	*seg = segval;
-    if (off)
-	*off = offval;
-    if (ip)
-	*ip  = ipval;
-
-    return 0;
-
-bail:
-    return -1;
-}
-
-static void usage(void)
-{
-    static const char *const usage[] = { "\
-Usage:\n\
-    chain.c32 [options]\n\
-    chain.c32 {fd|hd}<disk> [<partition>] [options]\n\
-    chain.c32 mbr{:|=}<id> [<partition>] [options]\n\
-    chain.c32 guid{:|=}<guid> [<partition>] [options]\n\
-    chain.c32 label{:|=}<label> [<partition>] [options]\n\
-    chain.c32 boot{,| }[<partition>] [options]\n\
-    chain.c32 fs [options]\n\
-\nOptions ('no' prefix specify defaulti value):\n\
-    file=<loader>        Load and execute file\n\
-    seg=<s[:o[:i]]>      Load file at <s:o>, jump to <s:i>\n\
-    nofilebpb            Treat file in memory as BPB compatible\n\
-    sect[=<s[:o[:i]]>]   Load sector at <s:o>, jump to <s:i>\n\
-                         - defaults to 0:0x7C00: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 <o>\n\
-                         - <off> defaults to autodetection\n\
-                         - only 0x24 and 0x40 are accepted\n\
-    nosetbpb             Enable set{hid,geo,drv}\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\
-    noswap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
-    nohide               Hide primary partitions, unhide selected partition\n\
-    nokeeppxe            Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
-    nowarn               Wait for a keypress to continue chainloading\n\
-                         - useful to see emited warnings\n\
-", "\
-\nComposite options:\n\
-    isolinux=<loader>    Load another version of ISOLINUX\n\
-    ntldr=<loader>       Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
-    cmldr=<loader>       Load Recovery Console of Windows NT/2K/XP/2003\n\
-    freedos=<loader>     Load FreeDOS KERNEL.SYS\n\
-    msdos=<loader>       Load MS-DOS 2.xx - 6.xx IO.SYS\n\
-    msdos7=<loader>      Load MS-DOS 7+ IO.SYS\n\
-    pcdos=<loader>       Load PC-DOS IBMBIO.COM\n\
-    drmk=<loader>        Load DRMK DELLBIO.BIN\n\
-    grub=<loader>        Load GRUB Legacy stage2\n\
-    grubcfg=<filename>   Set alternative config filename for GRUB Legacy\n\
-    grldr=<loader>       Load GRUB4DOS grldr\n\
-\nPlease see doc/chain.txt for the detailed documentation.\n"
-    };
-    error(usage[0]);
-    error("Press any key...\n");
-    wait_key();
-    error(usage[1]);
-}
-
-static int parse_args(int argc, char *argv[])
-{
-    int i;
-    unsigned int v;
-    char *p;
-
-    for (i = 1; i < argc; i++) {
-	if (!strncmp(argv[i], "file=", 5)) {
-	    opt.file = argv[i] + 5;
-	} else if (!strcmp(argv[i], "nofile")) {
-	    opt.file = NULL;
-	} else if (!strncmp(argv[i], "seg=", 4)) {
-	    if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip))
-		goto bail;
-	} else if (!strncmp(argv[i], "isolinux=", 9)) {
-	    opt.file = argv[i] + 9;
-	    opt.isolinux = true;
-	    opt.hand = false;
-	    opt.sect = false;
-	} else if (!strncmp(argv[i], "ntldr=", 6)) {
-	    opt.fseg = 0x2000;  /* NTLDR wants this address */
-	    opt.foff = 0;
-	    opt.fip = 0;
-	    opt.file = argv[i] + 6;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = 0x24;
-	    /* opt.save = true; */
-	    opt.hand = false;
-	} else if (!strncmp(argv[i], "cmldr=", 6)) {
-	    opt.fseg = 0x2000;  /* CMLDR wants this address */
-	    opt.foff = 0;
-	    opt.fip = 0;
-	    opt.file = argv[i] + 6;
-	    opt.cmldr = true;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = 0x24;
-	    /* opt.save = true; */
-	    opt.hand = false;
-	} else if (!strncmp(argv[i], "freedos=", 8)) {
-	    opt.fseg = 0x60;    /* FREEDOS wants this address */
-	    opt.foff = 0;
-	    opt.fip = 0;
-	    opt.sseg = 0x9000;
-	    opt.soff = 0;
-	    opt.sip = 0;
-	    opt.file = argv[i] + 8;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
-	    /* opt.save = true; */
-	    opt.hand = false;
-	} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
-		     !strncmp(argv[i], "pcdos=", v)) ||
-		    (v = 7, !strncmp(argv[i], "msdos7=", v)) ) {
-	    opt.fseg = 0x70;    /* MS-DOS 2.00 .. 6.xx wants this address */
-	    opt.foff = 0;
-	    opt.fip = v == 7 ? 0x200 : 0;  /* MS-DOS 7.0+ wants this ip */
-	    opt.sseg = 0x9000;
-	    opt.soff = 0;
-	    opt.sip = 0;
-	    opt.file = argv[i] + v;
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
-	    /* opt.save = true; */
-	    opt.hand = false;
-	} else if (!strncmp(argv[i], "drmk=", 5)) {
-	    opt.fseg = 0x70;    /* DRMK wants this address */
-	    opt.foff = 0;
-	    opt.fip = 0;
-	    opt.sseg = 0x2000;
-	    opt.soff = 0;
-	    opt.sip = 0;
-	    opt.file = argv[i] + 5;
-	    /* opt.drmk = true; */
-	    opt.sethid = true;
-	    opt.setgeo = true;
-	    opt.setdrv = true;
-	    opt.drvoff = ~0u;
-	    /* opt.save = true; */
-	    opt.hand = false;
-	} else if (!strncmp(argv[i], "grub=", 5)) {
-	    opt.fseg = 0x800;	/* stage2 wants this address */
-	    opt.foff = 0;
-	    opt.fip = 0x200;
-	    opt.file = argv[i] + 5;
-	    opt.grub = true;
-	    opt.hand = false;
-	    opt.sect = false;
-	} else if (!strncmp(argv[i], "grubcfg=", 8)) {
-	    opt.grubcfg = argv[i] + 8;
-	} else if (!strncmp(argv[i], "grldr=", 6)) {
-	    opt.file = argv[i] + 6;
-	    opt.grldr = true;
-	    opt.hand = false;
-	    opt.sect = false;
-	} else if (!strcmp(argv[i], "keeppxe")) {
-	    opt.keeppxe = 3;
-	} else if (!strcmp(argv[i], "nokeeppxe")) {
-	    opt.keeppxe = 0;
-	} else if (!strcmp(argv[i], "maps")) {
-	    opt.maps = true;
-	} else if (!strcmp(argv[i], "nomaps")) {
-	    opt.maps = false;
-	} else if (!strcmp(argv[i], "hand")) {
-	    opt.hand = true;
-	} else if (!strcmp(argv[i], "nohand")) {
-	    opt.hand = false;
-	} else if (!strcmp(argv[i], "hptr")) {
-	    opt.hptr = true;
-	} else if (!strcmp(argv[i], "nohptr")) {
-	    opt.hptr = false;
-	} else if (!strcmp(argv[i], "swap")) {
-	    opt.swap = true;
-	} else if (!strcmp(argv[i], "noswap")) {
-	    opt.swap = false;
-	} else if (!strcmp(argv[i], "hide")) {
-	    opt.hide = true;
-	} else if (!strcmp(argv[i], "nohide")) {
-	    opt.hide = false;
-	} 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;
-	} else if (!strcmp(argv[i], "nosetbpb")) {
-	    opt.setdrv = false;
-	    opt.setgeo = false;
-	    opt.sethid = false;
-	} else if (!strncmp(argv[i], "sect=", 5) ||
-		   !strcmp(argv[i], "sect")) {
-	    if (argv[i][4]) {
-		if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip))
-		    goto bail;
-		if ((opt.sseg << 4) + opt.soff + SECTOR - 1 > ADDRMAX) {
-		    error("Arguments of 'sect=' are invalid - resulting address too big.\n");
-		    goto bail;
-		}
-	    }
-	    opt.sect = true;
-	} else if (!strcmp(argv[i], "nosect")) {
-	    opt.sect = false;
-	} else if (!strcmp(argv[i], "save")) {
-	    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], "warn")) {
-	    opt.warn = true;
-	} else if (!strcmp(argv[i], "nowarn")) {
-	    opt.warn = false;
-	} else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
-		    && argv[i][1] == 'd')
-		   || !strncmp(argv[i], "mbr:", 4)
-		   || !strncmp(argv[i], "mbr=", 4)
-		   || !strncmp(argv[i], "guid:", 5)
-		   || !strncmp(argv[i], "guid=", 5)
-		   || !strncmp(argv[i], "label:", 6)
-		   || !strncmp(argv[i], "label=", 6)
-		   || !strcmp(argv[i], "boot")
-		   || !strncmp(argv[i], "boot,", 5)
-		   || !strcmp(argv[i], "fs")) {
-	    opt.drivename = argv[i];
-	    p = strchr(opt.drivename, ',');
-	    if (p) {
-		*p = '\0';
-		opt.partition = p + 1;
-	    } else if (argv[i + 1] && argv[i + 1][0] >= '0'
-		       && argv[i + 1][0] <= '9') {
-		opt.partition = argv[++i];
-	    }
-	} else {
-	    usage();
-	    goto bail;
-	}
-    }
-
-    if (opt.grubcfg && !opt.grub) {
-	error("grubcfg=<filename> must be used together with grub=<loader>.\n");
-	goto bail;
-    }
-
-    if ((!opt.maps || !opt.sect) && !opt.file) {
-	error("You have to load something.\n");
-	goto bail;
-    }
-
-    if (opt.filebpb && !opt.file) {
-	error("Option 'filebpb' requires file.\n");
-	goto bail;
-    }
-
-    return 0;
-bail:
-    return -1;
-}
-
 int find_dp(struct part_iter **_iter)
 {
     struct part_iter *iter;
@@ -888,256 +456,6 @@ bail:
     return -1;
 }
 
-/* Create boot info table: needed when you want to chainload
- * another version of ISOLINUX (or another bootlaoder that needs
- * the -boot-info-table switch of mkisofs)
- * (will only work when run from ISOLINUX)
- */
-static int manglef_isolinux(struct data_area *data)
-{
-    const union syslinux_derivative_info *sdi;
-    unsigned char *isolinux_bin;
-    uint32_t *checksum, *chkhead, *chktail;
-    uint32_t file_lba = 0;
-
-    sdi = syslinux_derivative_info();
-
-    if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
-	error ("The isolinux= option is only valid when run from ISOLINUX.\n");
-	goto bail;
-    }
-
-    /* Boot info table info (integers in little endian format)
-
-       Offset Name         Size      Meaning
-       8      bi_pvd       4 bytes   LBA of primary volume descriptor
-       12     bi_file      4 bytes   LBA of boot file
-       16     bi_length    4 bytes   Boot file length in bytes
-       20     bi_csum      4 bytes   32-bit checksum
-       24     bi_reserved  40 bytes  Reserved
-
-       The 32-bit checksum is the sum of all the 32-bit words in the
-       boot file starting at byte offset 64. All linear block
-       addresses (LBAs) are given in CD sectors (normally 2048 bytes).
-
-       LBA of primary volume descriptor should already be set to 16.
-       */
-
-    isolinux_bin = (unsigned char *)data->data;
-
-    /* Get LBA address of bootfile */
-    file_lba = get_file_lba(opt.file);
-
-    if (file_lba == 0) {
-	error("Failed to find LBA offset of the boot file\n");
-	goto bail;
-    }
-    /* Set it */
-    *((uint32_t *) & isolinux_bin[12]) = file_lba;
-
-    /* Set boot file length */
-    *((uint32_t *) & isolinux_bin[16]) = data->size;
-
-    /* Calculate checksum */
-    checksum = (uint32_t *) & isolinux_bin[20];
-    chkhead = (uint32_t *) & isolinux_bin[64];
-    chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
-    *checksum = 0;
-    while (chkhead < chktail)
-	*checksum += *chkhead++;
-
-    /*
-     * Deal with possible fractional dword at the end;
-     * this *should* never happen...
-     */
-    if (data->size & 3) {
-	uint32_t xword = 0;
-	memcpy(&xword, chkhead, data->size & 3);
-	*checksum += xword;
-    }
-    return 0;
-bail:
-    return -1;
-}
-
-/*
- * GRLDR of GRUB4DOS wants the partition number in DH:
- * -1:   whole drive (default)
- * 0-3:  primary partitions
- * 4-*:  logical partitions
- */
-static int manglef_grldr(const struct part_iter *iter)
-{
-    opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
-    return 0;
-}
-
-/*
- * Legacy grub's stage2 chainloading
- */
-static int manglef_grub(const struct part_iter *iter, struct data_area *data)
-{
-    /* Layout of stage2 file (from byte 0x0 to 0x270) */
-    struct grub_stage2_patch_area {
-	/* 0x0 to 0x205 */
-	char unknown[0x206];
-	/* 0x206: compatibility version number major */
-	uint8_t compat_version_major;
-	/* 0x207: compatibility version number minor */
-	uint8_t compat_version_minor;
-
-	/* 0x208: install_partition variable */
-	struct {
-	    /* 0x208: sub-partition in sub-partition part2 */
-	    uint8_t part3;
-	    /* 0x209: sub-partition in top-level partition */
-	    uint8_t part2;
-	    /* 0x20a: top-level partiton number */
-	    uint8_t part1;
-	    /* 0x20b: BIOS drive number (must be 0) */
-	    uint8_t drive;
-	} __attribute__ ((packed)) install_partition;
-
-	/* 0x20c: deprecated (historical reason only) */
-	uint32_t saved_entryno;
-	/* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
-	uint8_t stage2_id;
-	/* 0x211: force LBA */
-	uint8_t force_lba;
-	/* 0x212: version string (will probably be 0.97) */
-	char version_string[5];
-	/* 0x217: config filename */
-	char config_file[89];
-	/* 0x270: start of code (after jump from 0x200) */
-	char codestart[1];
-    } __attribute__ ((packed)) *stage2;
-
-    if (data->size < sizeof(struct grub_stage2_patch_area)) {
-	error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
-	goto bail;
-    }
-    stage2 = data->data;
-
-    /*
-     * Check the compatibility version number to see if we loaded a real
-     * stage2 file or a stage2 file that we support.
-     */
-    if (stage2->compat_version_major != 3
-	    || stage2->compat_version_minor != 2) {
-	error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
-	goto bail;
-    }
-
-    /*
-     * GRUB Legacy wants the partition number in the install_partition
-     * variable, located at offset 0x208 of stage2.
-     * When GRUB Legacy is loaded, it is located at memory address 0x8208.
-     *
-     * It looks very similar to the "boot information format" of the
-     * Multiboot specification:
-     *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
-     *
-     *   0x208 = part3: sub-partition in sub-partition part2
-     *   0x209 = part2: sub-partition in top-level partition
-     *   0x20a = part1: top-level partition number
-     *   0x20b = drive: BIOS drive number (must be 0)
-     *
-     * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
-     * another location.
-     *
-     * Partition numbers always start from zero.
-     * Unused partition bytes must be set to 0xFF.
-     *
-     * We only care about top-level partition, so we only need to change
-     * "part1" to the appropriate value:
-     *   -1:   whole drive (default) (-1 = 0xFF)
-     *   0-3:  primary partitions
-     *   4-*:  logical partitions
-     */
-    stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
-
-    /*
-     * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
-     * config filename. The filename passed via grubcfg= will overwrite
-     * the default config filename "/boot/grub/menu.lst".
-     */
-    if (opt.grubcfg) {
-	if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
-	    error ("The config filename length can't exceed 88 characters.\n");
-	    goto bail;
-	}
-
-	strcpy((char *)stage2->config_file, opt.grubcfg);
-    }
-
-    return 0;
-bail:
-    return -1;
-}
-
-/*
- * Dell's DRMK chainloading.
- */
-static 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.
-     */
-
-    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;
-}
-
-static uint32_t lba2chs(const struct disk_info *di, uint64_t lba)
-{
-    uint32_t c, h, s, t;
-
-    if (di->cbios) {
-	if (lba >= di->cyl * di->head * di->sect) {
-	    s = di->sect;
-	    h = di->head - 1;
-	    c = di->cyl - 1;
-	    goto out;
-	}
-	s = ((uint32_t)lba % di->sect) + 1;
-	t = (uint32_t)lba / di->sect;
-	h = t % di->head;
-	c = t / di->head;
-    } else
-	goto fallback;
-
-out:
-    return h | (s << 8) | ((c & 0x300) << 6) | ((c & 0xFF) << 16);
-
-fallback:
-    if (di->disk & 0x80)
-	return 0x00FFFFFE; /* 1023/63/254 */
-    else
-	/* FIXME ?
-	 * this is mostly "useful" with partitioned floppy,
-	 * maybe stick to 2.88mb ?
-	 */
-	return 0x004F1201; /* 79/18/1 */
-#if 0
-	return 0x004F2401; /* 79/36/1 */
-#endif
-}
-
 static int setup_handover(const struct part_iter *iter,
 		   struct data_area *data)
 {
@@ -1218,86 +536,6 @@ bail:
     return -1;
 }
 
-static int manglef_bpb(const struct part_iter *iter, struct data_area *data)
-{
-    /* BPB: hidden sectors */
-    if (opt.sethid) {
-	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: legacy geometry */
-    if (opt.setgeo) {
-	if (iter->di.cbios)
-	    *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
-	else {
-	    if (iter->di.disk & 0x80)
-		*(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
-	    else
-		*(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
-	}
-    }
-
-    /* BPB: drive */
-    if (opt.setdrv)
-	*(uint8_t *)((char *)data->data + opt.drvoff) = (uint8_t)
-	    (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
-
-    return 0;
-}
-
-static int try_mangles_bpb(const struct part_iter *iter, struct data_area *data)
-{
-    void *cmp_buf = NULL;
-
-    if (!(opt.setdrv || opt.setgeo || opt.sethid))
-	return 0;
-
-#if 0
-    /* Turn this off for now. It's hard to find a reason to
-     * BPB-mangle sector 0 of whatever there is, but it's
-     * "potentially" useful (fixing fdd's sector ?).
-     */
-    if (!iter->index)
-	return 0;
-#endif
-
-    if (!(cmp_buf = malloc(data->size))) {
-	error("Could not allocate sector-compare buffer.\n");
-	goto bail;
-    }
-
-    memcpy(cmp_buf, data->data, data->size);
-
-    manglef_bpb(iter, data);
-
-    if (opt.save && memcmp(cmp_buf, data->data, data->size)) {
-	if (disk_write_verify_sector(&iter->di, iter->start_lba, data->data)) {
-	    error("Cannot write updated boot sector.\n");
-	    goto bail;
-	}
-    }
-
-    free(cmp_buf);
-    return 0;
-
-bail:
-    return -1;
-}
-
-/*
- * To boot the Recovery Console of Windows NT/2K/XP we need to write
- * the string "cmdcons\0" to memory location 0000:7C03.
- * Memory location 0000:7C00 contains the bootsector of the partition.
- */
-static int mangles_cmldr(struct data_area *data)
-{
-    memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
-    return 0;
-}
-
 int setdrv_auto(const struct part_iter *iter)
 {
     int a, b;
@@ -1408,7 +646,7 @@ int main(int argc, char *argv[])
 	data[ndata].size = SECTOR;
 	data[ndata].base = (opt.sseg << 4) + opt.soff;
 
-	if (opt.file && opt.maps && !no_ov(data + fidx, data + ndata)) {
+	if (opt.file && opt.maps && overlap(data + fidx, data + ndata)) {
 	    error("WARNING: The sector won't be loaded, as it would conflict with the boot file.\n");
 	} else {
 	    if (!(data[ndata].data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
@@ -1429,8 +667,8 @@ int main(int argc, char *argv[])
 	hand_area = (void *)data[ndata].data;
 
 	/* Verify possible conflicts */
-	if ( ( fidx >= 0 && !no_ov(data + fidx, data + ndata)) ||
-	     ( sidx >= 0 && opt.maps && !no_ov(data + sidx, data + ndata)) ) {
+	if ( ( fidx >= 0 && overlap(data + fidx, data + ndata)) ||
+	     ( sidx >= 0 && opt.maps && overlap(data + sidx, data + ndata)) ) {
 	    error("WARNING: Handover area won't be prepared,\n"
 		  "as it would conflict with the boot file and/or the sector.\n");
 	} else {
@@ -1470,8 +708,10 @@ int main(int argc, char *argv[])
 	if (opt.grub && manglef_grub(iter, data + fidx))
 	    goto bail;
 
+#if 0
 	if (opt.drmk && manglef_drmk(data + fidx))
 	    goto bail;
+#endif
 
 	if (opt.filebpb && manglef_bpb(iter, data + fidx))
 	    goto bail;
diff --git a/com32/chain/chain.h b/com32/chain/chain.h
new file mode 100644
index 0000000..7a63022
--- /dev/null
+++ b/com32/chain/chain.h
@@ -0,0 +1,50 @@
+#ifndef _COM32_CHAIN_CHAIN_H
+#define _COM32_CHAIN_CHAIN_H
+
+#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;
+    bool hide;
+    bool sethid;
+    bool setgeo;
+    bool setdrv;
+    bool sect;
+    bool save;
+    bool filebpb;
+    bool warn;
+    uint16_t keeppxe;
+    struct syslinux_rm_regs regs;
+};
+
+struct data_area {
+    void *data;
+    addr_t base;
+    addr_t size;
+};
+
+extern struct options opt;
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/common.h b/com32/chain/common.h
new file mode 100644
index 0000000..b170a73
--- /dev/null
+++ b/com32/chain/common.h
@@ -0,0 +1,9 @@
+#ifndef _COM32_CHAIN_COMMON_H
+#define _COM32_CHAIN_COMMON_H
+
+#define ADDRMAX 0x9EFFFu
+#define ADDRMIN 0x500u
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/mangle.c b/com32/chain/mangle.c
new file mode 100644
index 0000000..5aea2f6
--- /dev/null
+++ b/com32/chain/mangle.c
@@ -0,0 +1,318 @@
+#include <com32.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <syslinux/config.h>
+#include "common.h"
+#include "chain.h"
+#include "utility.h"
+#include "partiter.h"
+#include "mangle.h"
+
+static const char cmldr_signature[8] = "cmdcons";
+
+/* Create boot info table: needed when you want to chainload
+ * another version of ISOLINUX (or another bootlaoder that needs
+ * the -boot-info-table switch of mkisofs)
+ * (will only work when run from ISOLINUX)
+ */
+int manglef_isolinux(struct data_area *data)
+{
+    const union syslinux_derivative_info *sdi;
+    unsigned char *isolinux_bin;
+    uint32_t *checksum, *chkhead, *chktail;
+    uint32_t file_lba = 0;
+
+    sdi = syslinux_derivative_info();
+
+    if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
+	error ("The isolinux= option is only valid when run from ISOLINUX.\n");
+	goto bail;
+    }
+
+    /* Boot info table info (integers in little endian format)
+
+       Offset Name         Size      Meaning
+       8      bi_pvd       4 bytes   LBA of primary volume descriptor
+       12     bi_file      4 bytes   LBA of boot file
+       16     bi_length    4 bytes   Boot file length in bytes
+       20     bi_csum      4 bytes   32-bit checksum
+       24     bi_reserved  40 bytes  Reserved
+
+       The 32-bit checksum is the sum of all the 32-bit words in the
+       boot file starting at byte offset 64. All linear block
+       addresses (LBAs) are given in CD sectors (normally 2048 bytes).
+
+       LBA of primary volume descriptor should already be set to 16.
+       */
+
+    isolinux_bin = (unsigned char *)data->data;
+
+    /* Get LBA address of bootfile */
+    file_lba = get_file_lba(opt.file);
+
+    if (file_lba == 0) {
+	error("Failed to find LBA offset of the boot file\n");
+	goto bail;
+    }
+    /* Set it */
+    *((uint32_t *) & isolinux_bin[12]) = file_lba;
+
+    /* Set boot file length */
+    *((uint32_t *) & isolinux_bin[16]) = data->size;
+
+    /* Calculate checksum */
+    checksum = (uint32_t *) & isolinux_bin[20];
+    chkhead = (uint32_t *) & isolinux_bin[64];
+    chktail = (uint32_t *) & isolinux_bin[data->size & ~3u];
+    *checksum = 0;
+    while (chkhead < chktail)
+	*checksum += *chkhead++;
+
+    /*
+     * Deal with possible fractional dword at the end;
+     * this *should* never happen...
+     */
+    if (data->size & 3) {
+	uint32_t xword = 0;
+	memcpy(&xword, chkhead, data->size & 3);
+	*checksum += xword;
+    }
+    return 0;
+bail:
+    return -1;
+}
+
+/*
+ * 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)
+{
+    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)
+{
+    /* Layout of stage2 file (from byte 0x0 to 0x270) */
+    struct grub_stage2_patch_area {
+	/* 0x0 to 0x205 */
+	char unknown[0x206];
+	/* 0x206: compatibility version number major */
+	uint8_t compat_version_major;
+	/* 0x207: compatibility version number minor */
+	uint8_t compat_version_minor;
+
+	/* 0x208: install_partition variable */
+	struct {
+	    /* 0x208: sub-partition in sub-partition part2 */
+	    uint8_t part3;
+	    /* 0x209: sub-partition in top-level partition */
+	    uint8_t part2;
+	    /* 0x20a: top-level partiton number */
+	    uint8_t part1;
+	    /* 0x20b: BIOS drive number (must be 0) */
+	    uint8_t drive;
+	} __attribute__ ((packed)) install_partition;
+
+	/* 0x20c: deprecated (historical reason only) */
+	uint32_t saved_entryno;
+	/* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
+	uint8_t stage2_id;
+	/* 0x211: force LBA */
+	uint8_t force_lba;
+	/* 0x212: version string (will probably be 0.97) */
+	char version_string[5];
+	/* 0x217: config filename */
+	char config_file[89];
+	/* 0x270: start of code (after jump from 0x200) */
+	char codestart[1];
+    } __attribute__ ((packed)) *stage2;
+
+    if (data->size < sizeof(struct grub_stage2_patch_area)) {
+	error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
+	goto bail;
+    }
+    stage2 = data->data;
+
+    /*
+     * Check the compatibility version number to see if we loaded a real
+     * stage2 file or a stage2 file that we support.
+     */
+    if (stage2->compat_version_major != 3
+	    || stage2->compat_version_minor != 2) {
+	error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.\n");
+	goto bail;
+    }
+
+    /*
+     * GRUB Legacy wants the partition number in the install_partition
+     * variable, located at offset 0x208 of stage2.
+     * When GRUB Legacy is loaded, it is located at memory address 0x8208.
+     *
+     * It looks very similar to the "boot information format" of the
+     * Multiboot specification:
+     *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
+     *
+     *   0x208 = part3: sub-partition in sub-partition part2
+     *   0x209 = part2: sub-partition in top-level partition
+     *   0x20a = part1: top-level partition number
+     *   0x20b = drive: BIOS drive number (must be 0)
+     *
+     * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
+     * another location.
+     *
+     * Partition numbers always start from zero.
+     * Unused partition bytes must be set to 0xFF.
+     *
+     * We only care about top-level partition, so we only need to change
+     * "part1" to the appropriate value:
+     *   -1:   whole drive (default) (-1 = 0xFF)
+     *   0-3:  primary partitions
+     *   4-*:  logical partitions
+     */
+    stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
+
+    /*
+     * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
+     * config filename. The filename passed via grubcfg= will overwrite
+     * the default config filename "/boot/grub/menu.lst".
+     */
+    if (opt.grubcfg) {
+	if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
+	    error ("The config filename length can't exceed 88 characters.\n");
+	    goto bail;
+	}
+
+	strcpy((char *)stage2->config_file, opt.grubcfg);
+    }
+
+    return 0;
+bail:
+    return -1;
+}
+
+/*
+ * Adjust BPB of a BPB-compatible file
+ */
+int manglef_bpb(const struct part_iter *iter, struct data_area *data)
+{
+    /* BPB: hidden sectors */
+    if (opt.sethid) {
+	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: legacy geometry */
+    if (opt.setgeo) {
+	if (iter->di.cbios)
+	    *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.sect);
+	else {
+	    if (iter->di.disk & 0x80)
+		*(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
+	    else
+		*(uint32_t *)((char *)data->data + 0x18) = 0x00020012;
+	}
+    }
+
+    /* BPB: drive */
+    if (opt.setdrv)
+	*(uint8_t *)((char *)data->data + opt.drvoff) = (uint8_t)
+	    (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
+
+    return 0;
+}
+
+/*
+ * Adjust BPB of a sector
+ */
+int try_mangles_bpb(const struct part_iter *iter, struct data_area *data)
+{
+    void *cmp_buf = NULL;
+
+    if (!(opt.setdrv || opt.setgeo || opt.sethid))
+	return 0;
+
+#if 0
+    /* Turn this off for now. It's hard to find a reason to
+     * BPB-mangle sector 0 of whatever there is, but it's
+     * "potentially" useful (fixing fdd's sector ?).
+     */
+    if (!iter->index)
+	return 0;
+#endif
+
+    if (!(cmp_buf = malloc(data->size))) {
+	error("Could not allocate sector-compare buffer.\n");
+	goto bail;
+    }
+
+    memcpy(cmp_buf, data->data, data->size);
+
+    manglef_bpb(iter, data);
+
+    if (opt.save && memcmp(cmp_buf, data->data, data->size)) {
+	if (disk_write_verify_sector(&iter->di, iter->start_lba, data->data)) {
+	    error("Cannot write updated boot sector.\n");
+	    goto bail;
+	}
+    }
+
+    free(cmp_buf);
+    return 0;
+
+bail:
+    return -1;
+}
+
+/*
+ * To boot the Recovery Console of Windows NT/2K/XP we need to write
+ * the string "cmdcons\0" to memory location 0000:7C03.
+ * Memory location 0000:7C00 contains the bootsector of the partition.
+ */
+int mangles_cmldr(struct data_area *data)
+{
+    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)
+{
+    /*
+     * 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.
+     */
+
+    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
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h
new file mode 100644
index 0000000..71a347a
--- /dev/null
+++ b/com32/chain/mangle.h
@@ -0,0 +1,19 @@
+#ifndef _COM32_CHAIN_MANGLE_H
+#define _COM32_CHAIN_MANGLE_H
+
+#include "chain.h"
+#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 try_mangles_bpb(const struct part_iter *iter, struct data_area *data);
+int mangles_cmldr(struct data_area *data);
+#if 0
+int manglef_drmk(struct data_area *data);
+#endif
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/options.c b/com32/chain/options.c
new file mode 100644
index 0000000..efb563d
--- /dev/null
+++ b/com32/chain/options.c
@@ -0,0 +1,336 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common.h"
+#include "chain.h"
+#include "utility.h"
+#include "options.h"
+
+int soi_s2n(char *ptr, unsigned int *seg,
+		       unsigned int *off,
+		       unsigned int *ip)
+{
+    unsigned int segval = 0, offval = 0, ipval = 0, val;
+    char *p;
+
+    segval = strtoul(ptr, &p, 0);
+    if (*p == ':')
+	offval = strtoul(p+1, &p, 0);
+    if (*p == ':')
+	ipval = strtoul(p+1, NULL, 0);
+
+    val = (segval << 4) + offval;
+
+    if (val < ADDRMIN || val > ADDRMAX) {
+	error("Invalid seg:off:* address specified..\n");
+	goto bail;
+    }
+
+    val = (segval << 4) + ipval;
+
+    if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
+	error("Invalid seg:*:ip address specified.\n");
+	goto bail;
+    }
+
+    if (seg)
+	*seg = segval;
+    if (off)
+	*off = offval;
+    if (ip)
+	*ip  = ipval;
+
+    return 0;
+bail:
+    return -1;
+}
+
+void usage(void)
+{
+    static const char *const usage[] = { "\
+Usage:\n\
+    chain.c32 [options]\n\
+    chain.c32 {fd|hd}<disk> [<partition>] [options]\n\
+    chain.c32 mbr{:|=}<id> [<partition>] [options]\n\
+    chain.c32 guid{:|=}<guid> [<partition>] [options]\n\
+    chain.c32 label{:|=}<label> [<partition>] [options]\n\
+    chain.c32 boot{,| }[<partition>] [options]\n\
+    chain.c32 fs [options]\n\
+\nOptions ('no' prefix specify defaulti value):\n\
+    file=<loader>        Load and execute file\n\
+    seg=<s[:o[:i]]>      Load file at <s:o>, jump to <s:i>\n\
+    nofilebpb            Treat file in memory as BPB compatible\n\
+    sect[=<s[:o[:i]]>]   Load sector at <s:o>, jump to <s:i>\n\
+                         - defaults to 0:0x7C00: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 <o>\n\
+                         - <off> defaults to autodetection\n\
+                         - only 0x24 and 0x40 are accepted\n\
+    nosetbpb             Enable set{hid,geo,drv}\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\
+    noswap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
+    nohide               Hide primary partitions, unhide selected partition\n\
+    nokeeppxe            Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
+    nowarn               Wait for a keypress to continue chainloading\n\
+                         - useful to see emited warnings\n\
+", "\
+\nComposite options:\n\
+    isolinux=<loader>    Load another version of ISOLINUX\n\
+    ntldr=<loader>       Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
+    cmldr=<loader>       Load Recovery Console of Windows NT/2K/XP/2003\n\
+    freedos=<loader>     Load FreeDOS KERNEL.SYS\n\
+    msdos=<loader>       Load MS-DOS 2.xx - 6.xx IO.SYS\n\
+    msdos7=<loader>      Load MS-DOS 7+ IO.SYS\n\
+    pcdos=<loader>       Load PC-DOS IBMBIO.COM\n\
+    drmk=<loader>        Load DRMK DELLBIO.BIN\n\
+    grub=<loader>        Load GRUB Legacy stage2\n\
+    grubcfg=<filename>   Set alternative config filename for GRUB Legacy\n\
+    grldr=<loader>       Load GRUB4DOS grldr\n\
+\nPlease see doc/chain.txt for the detailed documentation.\n"
+    };
+    error(usage[0]);
+    error("Press any key...\n");
+    wait_key();
+    error(usage[1]);
+}
+
+int parse_args(int argc, char *argv[])
+{
+    int i;
+    unsigned int v;
+    char *p;
+
+    for (i = 1; i < argc; i++) {
+	if (!strncmp(argv[i], "file=", 5)) {
+	    opt.file = argv[i] + 5;
+	} else if (!strcmp(argv[i], "nofile")) {
+	    opt.file = NULL;
+	} else if (!strncmp(argv[i], "seg=", 4)) {
+	    if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip))
+		goto bail;
+	} else if (!strncmp(argv[i], "isolinux=", 9)) {
+	    opt.file = argv[i] + 9;
+	    opt.isolinux = true;
+	    opt.hand = false;
+	    opt.sect = false;
+	} else if (!strncmp(argv[i], "ntldr=", 6)) {
+	    opt.fseg = 0x2000;  /* NTLDR wants this address */
+	    opt.foff = 0;
+	    opt.fip = 0;
+	    opt.file = argv[i] + 6;
+	    opt.sethid = true;
+	    opt.setgeo = true;
+	    opt.setdrv = true;
+	    opt.drvoff = 0x24;
+	    /* opt.save = true; */
+	    opt.hand = false;
+	} else if (!strncmp(argv[i], "cmldr=", 6)) {
+	    opt.fseg = 0x2000;  /* CMLDR wants this address */
+	    opt.foff = 0;
+	    opt.fip = 0;
+	    opt.file = argv[i] + 6;
+	    opt.cmldr = true;
+	    opt.sethid = true;
+	    opt.setgeo = true;
+	    opt.setdrv = true;
+	    opt.drvoff = 0x24;
+	    /* opt.save = true; */
+	    opt.hand = false;
+	} else if (!strncmp(argv[i], "freedos=", 8)) {
+	    opt.fseg = 0x60;    /* FREEDOS wants this address */
+	    opt.foff = 0;
+	    opt.fip = 0;
+	    opt.sseg = 0x9000;
+	    opt.soff = 0;
+	    opt.sip = 0;
+	    opt.file = argv[i] + 8;
+	    opt.sethid = true;
+	    opt.setgeo = true;
+	    opt.setdrv = true;
+	    opt.drvoff = ~0u;
+	    /* opt.save = true; */
+	    opt.hand = false;
+	} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
+		     !strncmp(argv[i], "pcdos=", v)) ||
+		    (v = 7, !strncmp(argv[i], "msdos7=", v)) ) {
+	    opt.fseg = 0x70;    /* MS-DOS 2.00 .. 6.xx wants this address */
+	    opt.foff = 0;
+	    opt.fip = v == 7 ? 0x200 : 0;  /* MS-DOS 7.0+ wants this ip */
+	    opt.sseg = 0x9000;
+	    opt.soff = 0;
+	    opt.sip = 0;
+	    opt.file = argv[i] + v;
+	    opt.sethid = true;
+	    opt.setgeo = true;
+	    opt.setdrv = true;
+	    opt.drvoff = ~0u;
+	    /* opt.save = true; */
+	    opt.hand = false;
+	} else if (!strncmp(argv[i], "drmk=", 5)) {
+	    opt.fseg = 0x70;    /* DRMK wants this address */
+	    opt.foff = 0;
+	    opt.fip = 0;
+	    opt.sseg = 0x2000;
+	    opt.soff = 0;
+	    opt.sip = 0;
+	    opt.file = argv[i] + 5;
+	    /* opt.drmk = true; */
+	    opt.sethid = true;
+	    opt.setgeo = true;
+	    opt.setdrv = true;
+	    opt.drvoff = ~0u;
+	    /* opt.save = true; */
+	    opt.hand = false;
+	} else if (!strncmp(argv[i], "grub=", 5)) {
+	    opt.fseg = 0x800;	/* stage2 wants this address */
+	    opt.foff = 0;
+	    opt.fip = 0x200;
+	    opt.file = argv[i] + 5;
+	    opt.grub = true;
+	    opt.hand = false;
+	    opt.sect = false;
+	} else if (!strncmp(argv[i], "grubcfg=", 8)) {
+	    opt.grubcfg = argv[i] + 8;
+	} else if (!strncmp(argv[i], "grldr=", 6)) {
+	    opt.file = argv[i] + 6;
+	    opt.grldr = true;
+	    opt.hand = false;
+	    opt.sect = false;
+	} else if (!strcmp(argv[i], "keeppxe")) {
+	    opt.keeppxe = 3;
+	} else if (!strcmp(argv[i], "nokeeppxe")) {
+	    opt.keeppxe = 0;
+	} else if (!strcmp(argv[i], "maps")) {
+	    opt.maps = true;
+	} else if (!strcmp(argv[i], "nomaps")) {
+	    opt.maps = false;
+	} else if (!strcmp(argv[i], "hand")) {
+	    opt.hand = true;
+	} else if (!strcmp(argv[i], "nohand")) {
+	    opt.hand = false;
+	} else if (!strcmp(argv[i], "hptr")) {
+	    opt.hptr = true;
+	} else if (!strcmp(argv[i], "nohptr")) {
+	    opt.hptr = false;
+	} else if (!strcmp(argv[i], "swap")) {
+	    opt.swap = true;
+	} else if (!strcmp(argv[i], "noswap")) {
+	    opt.swap = false;
+	} else if (!strcmp(argv[i], "hide")) {
+	    opt.hide = true;
+	} else if (!strcmp(argv[i], "nohide")) {
+	    opt.hide = false;
+	} 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;
+	} else if (!strcmp(argv[i], "nosetbpb")) {
+	    opt.setdrv = false;
+	    opt.setgeo = false;
+	    opt.sethid = false;
+	} else if (!strncmp(argv[i], "sect=", 5) ||
+		   !strcmp(argv[i], "sect")) {
+	    if (argv[i][4]) {
+		if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip))
+		    goto bail;
+		if ((opt.sseg << 4) + opt.soff + SECTOR - 1 > ADDRMAX) {
+		    error("Arguments of 'sect=' are invalid - resulting address too big.\n");
+		    goto bail;
+		}
+	    }
+	    opt.sect = true;
+	} else if (!strcmp(argv[i], "nosect")) {
+	    opt.sect = false;
+	} else if (!strcmp(argv[i], "save")) {
+	    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], "warn")) {
+	    opt.warn = true;
+	} else if (!strcmp(argv[i], "nowarn")) {
+	    opt.warn = false;
+	} else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
+		    && argv[i][1] == 'd')
+		   || !strncmp(argv[i], "mbr:", 4)
+		   || !strncmp(argv[i], "mbr=", 4)
+		   || !strncmp(argv[i], "guid:", 5)
+		   || !strncmp(argv[i], "guid=", 5)
+		   || !strncmp(argv[i], "label:", 6)
+		   || !strncmp(argv[i], "label=", 6)
+		   || !strcmp(argv[i], "boot")
+		   || !strncmp(argv[i], "boot,", 5)
+		   || !strcmp(argv[i], "fs")) {
+	    opt.drivename = argv[i];
+	    p = strchr(opt.drivename, ',');
+	    if (p) {
+		*p = '\0';
+		opt.partition = p + 1;
+	    } else if (argv[i + 1] && argv[i + 1][0] >= '0'
+		       && argv[i + 1][0] <= '9') {
+		opt.partition = argv[++i];
+	    }
+	} else {
+	    usage();
+	    goto bail;
+	}
+    }
+
+    if (opt.grubcfg && !opt.grub) {
+	error("grubcfg=<filename> must be used together with grub=<loader>.\n");
+	goto bail;
+    }
+
+    if ((!opt.maps || !opt.sect) && !opt.file) {
+	error("You have to load something.\n");
+	goto bail;
+    }
+
+    if (opt.filebpb && !opt.file) {
+	error("Option 'filebpb' requires file.\n");
+	goto bail;
+    }
+
+    return 0;
+bail:
+    return -1;
+}
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/options.h b/com32/chain/options.h
new file mode 100644
index 0000000..e400eca
--- /dev/null
+++ b/com32/chain/options.h
@@ -0,0 +1,10 @@
+#ifndef _COM32_CHAIN_OPTIONS_H
+#define _COM32_CHAIN_OPTIONS_H
+
+int soi_s2n(char *ptr, unsigned int *seg, unsigned int *off, unsigned int *ip);
+void usage(void);
+int parse_args(int argc, char *argv[]);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */
diff --git a/com32/chain/partiter.c b/com32/chain/partiter.c
index 6934e8d..2859694 100644
--- a/com32/chain/partiter.c
+++ b/com32/chain/partiter.c
@@ -39,7 +39,9 @@
 #include <stdarg.h>
 #include <zlib.h>
 #include <syslinux/disk.h>
+#include "common.h"
 #include "partiter.h"
+#include "utility.h"
 
 #define ost_is_ext(type) ((type) == 0x05 || (type) == 0x0F || (type) == 0x85)
 #define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00)
@@ -48,11 +50,6 @@
 /* this is chosen to follow how many sectors disklib can read at once */
 #define MAXGPTPTSIZE (255u*SECTOR)
 
-static void error(const char *msg)
-{
-    fputs(msg, stderr);
-}
-
 /* forwards */
 
 static int iter_ctor(struct part_iter *, va_list *);
@@ -94,11 +91,6 @@ static int inv_type(const void *type)
 }
 #endif
 
-static int guid_is0(const struct guid *guid)
-{
-    return !*(const uint64_t *)guid && !*((const uint64_t *)guid+1);
-}
-
 /**
  * iter_ctor() - common iterator initialization
  * @iter:	iterator pointer
diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h
index f32543e..d00d609 100644
--- a/com32/chain/partiter.h
+++ b/com32/chain/partiter.h
@@ -1,8 +1,8 @@
 /* ----------------------------------------------------------------------- *
  *
  *   Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
- *   Copyright 2010 Shao Miller
  *   Copyright 2010 Michal Soltys
+ *   Copyright 2010 Shao Miller
  *
  *   Permission is hereby granted, free of charge, to any person
  *   obtaining a copy of this software and associated documentation
@@ -33,8 +33,8 @@
  * Provides disk / partition iteration.
  */
 
-#ifndef _SYSLINUX_PARTITER_H
-#define _SYSLINUX_PARTITER_H
+#ifndef _COM32_CHAIN_PARTITER_H
+#define _COM32_CHAIN_PARTITER_H
 
 #include <stdint.h>
 #include <syslinux/disk.h>
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
new file mode 100644
index 0000000..9b978a6
--- /dev/null
+++ b/com32/chain/utility.c
@@ -0,0 +1,114 @@
+#include <com32.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslinux/disk.h>
+#include "utility.h"
+
+void error(const char *msg)
+{
+    fputs(msg, stderr);
+}
+
+int guid_is0(const struct guid *guid)
+{
+    return !*(const uint64_t *)guid && !*((const uint64_t *)guid+1);
+}
+
+void wait_key(void)
+{
+    int cnt;
+    char junk;
+
+    /* drain */
+    do {
+	errno = 0;
+	cnt = read(0, &junk, 1);
+    } while (cnt > 0 || (cnt < 0 && errno == EAGAIN));
+
+    /* wait */
+    do {
+	errno = 0;
+	cnt = read(0, &junk, 1);
+    } while (!cnt || (cnt < 0 && errno == EAGAIN));
+}
+
+uint32_t lba2chs(const struct disk_info *di, uint64_t lba)
+{
+    uint32_t c, h, s, t;
+
+    if (di->cbios) {
+	if (lba >= di->cyl * di->head * di->sect) {
+	    s = di->sect;
+	    h = di->head - 1;
+	    c = di->cyl - 1;
+	    goto out;
+	}
+	s = ((uint32_t)lba % di->sect) + 1;
+	t = (uint32_t)lba / di->sect;
+	h = t % di->head;
+	c = t / di->head;
+    } else
+	goto fallback;
+
+out:
+    return h | (s << 8) | ((c & 0x300) << 6) | ((c & 0xFF) << 16);
+
+fallback:
+    if (di->disk & 0x80)
+	return 0x00FFFFFE; /* 1023/63/254 */
+    else
+	/*
+	 * adjust ?
+	 * this is somewhat "useful" with partitioned floppy,
+	 * maybe stick to 2.88mb ?
+	 */
+	return 0x004F1201; /* 79/18/1 */
+#if 0
+	return 0x004F2401; /* 79/36/1 */
+#endif
+}
+
+uint32_t get_file_lba(const char *filename)
+{
+    com32sys_t inregs;
+    uint32_t lba;
+
+    /* Start with clean registers */
+    memset(&inregs, 0, sizeof(com32sys_t));
+
+    /* Put the filename in the bounce buffer */
+    strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
+
+    /* Call comapi_open() which returns a structure pointer in SI
+     * to a structure whose first member happens to be the LBA.
+     */
+    inregs.eax.w[0] = 0x0006;
+    inregs.esi.w[0] = OFFS(__com32.cs_bounce);
+    inregs.es = SEG(__com32.cs_bounce);
+    __com32.cs_intcall(0x22, &inregs, &inregs);
+
+    if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
+	return 0;		/* Filename not found */
+    }
+
+    /* Since the first member is the LBA, we simply cast */
+    lba = *((uint32_t *) MK_PTR(inregs.ds, inregs.esi.w[0]));
+
+    /* Clean the registers for the next call */
+    memset(&inregs, 0, sizeof(com32sys_t));
+
+    /* Put the filename in the bounce buffer */
+    strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
+
+    /* Call comapi_close() to free the structure */
+    inregs.eax.w[0] = 0x0008;
+    inregs.esi.w[0] = OFFS(__com32.cs_bounce);
+    inregs.es = SEG(__com32.cs_bounce);
+    __com32.cs_intcall(0x22, &inregs, &inregs);
+
+    return lba;
+}
+
diff --git a/com32/chain/utility.h b/com32/chain/utility.h
new file mode 100644
index 0000000..b77f633
--- /dev/null
+++ b/com32/chain/utility.h
@@ -0,0 +1,15 @@
+#ifndef _COM32_CHAIN_UTILITY_H
+#define _COM32_CHAIN_UTILITY_H
+
+#include <stdint.h>
+#include <syslinux/disk.h>
+
+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);
+
+#endif
+
+/* vim: set ts=8 sts=4 sw=4 noet: */


More information about the Syslinux-commits mailing list