[syslinux:syslinux-4.xx] chain module: backport changes from master to syslinux-4.xx

syslinux-bot for Michal Soltys soltys at ziu.info
Thu Mar 26 13:45:07 PDT 2015


Commit-ID:  542dd3ec274ac0dfeeccb8d030c9bdcfbcc958ea
Gitweb:     http://www.syslinux.org/commit/542dd3ec274ac0dfeeccb8d030c9bdcfbcc958ea
Author:     Michal Soltys <soltys at ziu.info>
AuthorDate: Thu, 14 Feb 2013 16:51:45 +0100
Committer:  Michal Soltys <soltys at ziu.info>
CommitDate: Mon, 1 Dec 2014 00:03:35 +0100

chain module: backport changes from master to syslinux-4.xx

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

---
 com32/chain/Makefile   |   7 +-
 com32/chain/chain.c    | 213 +++++++-------
 com32/chain/chain.h    |  19 +-
 com32/chain/common.h   |   9 -
 com32/chain/mangle.c   | 244 ++++++++++------
 com32/chain/mangle.h   |  34 ++-
 com32/chain/options.c  | 261 ++++++++++-------
 com32/chain/options.h  |  54 +++-
 com32/chain/partiter.c | 743 ++++++++++++++++++++++---------------------------
 com32/chain/partiter.h |  80 +++---
 com32/chain/utility.c  |  93 +++++--
 com32/chain/utility.h  |  79 ++++--
 doc/chain.txt          |  63 ++++-
 13 files changed, 1078 insertions(+), 821 deletions(-)

diff --git a/com32/chain/Makefile b/com32/chain/Makefile
index 9d398a8..4a1d083 100644
--- a/com32/chain/Makefile
+++ b/com32/chain/Makefile
@@ -1,7 +1,9 @@
 ## -----------------------------------------------------------------------
 ##
-##   Copyright 2001-2010 H. Peter Anvin - All Rights Reserved
-##   Copyright 2010 Michal Soltys
+##   Copyright 2001-2009 H. Peter Anvin - All Rights Reserved
+##   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+##   Copyright 2010 Shao Miller
+##   Copyright 2010+ Michal Soltys
 ##
 ##   This program is free software; you can redistribute it and/or modify
 ##   it under the terms of the GNU General Public License as published by
@@ -17,6 +19,7 @@ MAKEDIR = $(topdir)/mk
 include $(MAKEDIR)/com32.mk
 
 OBJS = chain.o partiter.o utility.o options.o mangle.o
+CFLAGS += -fno-strict-aliasing
 
 all: chain.c32
 
diff --git a/com32/chain/chain.c b/com32/chain/chain.c
index 30153c4..f4f45b4 100644
--- a/com32/chain/chain.c
+++ b/com32/chain/chain.c
@@ -3,7 +3,7 @@
  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
  *   Copyright 2010 Shao Miller
- *   Copyright 2010 Michal Soltys
+ *   Copyright 2010+ Michal Soltys
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -34,7 +34,6 @@
 #include <syslinux/config.h>
 #include <syslinux/disk.h>
 #include <syslinux/video.h>
-#include "common.h"
 #include "chain.h"
 #include "utility.h"
 #include "options.h"
@@ -65,27 +64,23 @@ static int is_phys(uint8_t sdifs)
 static int find_by_sig(uint32_t mbr_sig,
 			struct part_iter **_boot_part)
 {
-    struct part_iter *boot_part = NULL;
+    struct part_iter *iter = NULL;
     struct disk_info diskinfo;
     int drive;
 
     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
 	if (disk_get_params(drive, &diskinfo))
 	    continue;		/* Drive doesn't exist */
-	if (!(boot_part = pi_begin(&diskinfo, 0)))
-	    continue;
-	/* Check for a MBR disk */
-	if (boot_part->type != typedos) {
-	    pi_del(&boot_part);
+	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
 	    continue;
-	}
-	if (boot_part->sub.dos.disk_sig == mbr_sig) {
+	/* Check for a matching MBR disk */
+	if (iter->type == typedos && iter->dos.disk_sig == mbr_sig)
 	    goto ok;
-	}
+	pi_del(&iter);
     }
     drive = -1;
 ok:
-    *_boot_part = boot_part;
+    *_boot_part = iter;
     return drive;
 }
 
@@ -96,33 +91,26 @@ ok:
 static int find_by_guid(const struct guid *gpt_guid,
 			struct part_iter **_boot_part)
 {
-    struct part_iter *boot_part = NULL;
+    struct part_iter *iter = NULL;
     struct disk_info diskinfo;
     int drive;
 
     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
 	if (disk_get_params(drive, &diskinfo))
 	    continue;		/* Drive doesn't exist */
-	if (!(boot_part = pi_begin(&diskinfo, 0)))
+	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
 	    continue;
-	/* Check for a GPT disk */
-	if (boot_part->type != typegpt) {
-	    pi_del(&boot_part);
-	    continue;
-	}
-	/* Check for a matching GPT disk guid */
-	if (!memcmp(&boot_part->sub.gpt.disk_guid, gpt_guid, sizeof(*gpt_guid))) {
-	    goto ok;
-	}
-	/* disk guid doesn't match, maybe partition guid will */
-	while (!pi_next(&boot_part)) {
-	    if (!memcmp(&boot_part->sub.gpt.part_guid, gpt_guid, sizeof(*gpt_guid)))
-		goto ok;
-	}
+	/* Check for a matching GPT disk/partition guid */
+	if (iter->type == typegpt)
+	    do {
+		if (!memcmp(&iter->gpt.part_guid, gpt_guid, sizeof *gpt_guid))
+		    goto ok;
+	    } while (!pi_next(iter));
+	pi_del(&iter);
     }
     drive = -1;
 ok:
-    *_boot_part = boot_part;
+    *_boot_part = iter;
     return drive;
 }
 
@@ -132,36 +120,31 @@ ok:
  */
 static int find_by_label(const char *label, struct part_iter **_boot_part)
 {
-    struct part_iter *boot_part = NULL;
+    struct part_iter *iter = NULL;
     struct disk_info diskinfo;
     int drive;
 
     for (drive = 0x80; drive < 0x80 + fixed_cnt; drive++) {
 	if (disk_get_params(drive, &diskinfo))
 	    continue;		/* Drive doesn't exist */
-	if (!(boot_part = pi_begin(&diskinfo, 0)))
-	    continue;
-	/* Check for a GPT disk */
-	if (!(boot_part->type == typegpt)) {
-	    pi_del(&boot_part);
+	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
 	    continue;
-	}
-	/* Check for a matching partition */
-	while (!pi_next(&boot_part)) {
-	    if (!strcmp(label, boot_part->sub.gpt.part_label))
-		goto ok;
-	}
+	/* Check for a matching GPT partition label */
+	if (iter->type == typegpt)
+	    while (!pi_next(iter)) {
+		if (!strcmp(label, iter->gpt.part_label))
+		    goto ok;
+	    }
+	pi_del(&iter);
     }
     drive = -1;
 ok:
-    *_boot_part = boot_part;
+    *_boot_part = iter;
     return drive;
 }
 
 static void do_boot(struct data_area *data, int ndata)
 {
-    uint16_t *const bios_fbm = (uint16_t *) 0x413;
-    addr_t dosmem = (addr_t)(*bios_fbm << 10);	/* Technically a low bound */
     struct syslinux_memmap *mmap;
     struct syslinux_movelist *mlist = NULL;
     addr_t endimage;
@@ -172,7 +155,7 @@ static void do_boot(struct data_area *data, int ndata)
     mmap = syslinux_memory_map();
 
     if (!mmap) {
-	error("Cannot read system memory map\n");
+	error("Cannot read system memory map.");
 	return;
     }
 
@@ -181,7 +164,7 @@ static void do_boot(struct data_area *data, int ndata)
 	if (data[i].base + data[i].size > endimage)
 	    endimage = data[i].base + data[i].size;
     }
-    if (endimage > dosmem)
+    if (endimage > dosmax)
 	goto too_big;
 
     for (i = 0; i < ndata; i++) {
@@ -227,7 +210,7 @@ static void do_boot(struct data_area *data, int ndata)
 	static uint8_t swapstub[1024];
 	uint8_t *p;
 
-	/* Note: we can't rely on either INT 13h nor the dosmem
+	/* Note: we can't rely on either INT 13h nor the dosmax
 	   vector to be correct at this stage, so we have to use an
 	   installer stub to put things in the right place.
 	   Round the installer location to a 1K boundary so the only
@@ -247,14 +230,14 @@ static void do_boot(struct data_area *data, int ndata)
 
 	/* Mapping table; start out with identity mapping everything */
 	for (i = 0; i < 256; i++)
-	    p[i] = (uint8_t)i;
+	    p[i] = i;
 
 	/* And the actual swap */
 	p[driveno] = swapdrive;
 	p[swapdrive] = driveno;
 
 	/* Adjust registers */
-	opt.regs.ds = opt.regs.cs = (uint16_t)(endimage >> 4);
+	opt.regs.ds = opt.regs.cs = endimage >> 4;
 	opt.regs.esi.l = opt.regs.es = 0;
 	opt.regs.ecx.l = sizeof swapstub >> 2;
 	opt.regs.ip = 0x10;	/* Installer offset */
@@ -273,17 +256,17 @@ static void do_boot(struct data_area *data, int ndata)
     /* Force text mode */
     syslinux_force_text_mode();
 
-    fputs("Booting...\n", stdout);
+    puts("Booting...");
     syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, &opt.regs);
-    error("Chainboot failed!\n");
+    error("Chainboot failed !");
     return;
 
 too_big:
-    error("Loader file too large\n");
+    error("Loader file too large.");
     return;
 
 enomem:
-    error("Out of memory\n");
+    error("Out of memory.");
     return;
 }
 
@@ -300,23 +283,23 @@ int find_dp(struct part_iter **_iter)
 
     if (!strncmp(opt.drivename, "mbr", 3)) {
 	if (find_by_sig(strtoul(opt.drivename + 4, NULL, 0), &iter) < 0) {
-	    error("Unable to find requested MBR signature.\n");
+	    error("Unable to find requested MBR signature.");
 	    goto bail;
 	}
     } else if (!strncmp(opt.drivename, "guid", 4)) {
 	if (str_to_guid(opt.drivename + 5, &gpt_guid))
 	    goto bail;
 	if (find_by_guid(&gpt_guid, &iter) < 0) {
-	    error("Unable to find requested GPT disk or partition by guid.\n");
+	    error("Unable to find requested GPT disk or partition by guid.");
 	    goto bail;
 	}
     } else if (!strncmp(opt.drivename, "label", 5)) {
 	if (!opt.drivename[6]) {
-	    error("No label specified.\n");
+	    error("No label specified.");
 	    goto bail;
 	}
 	if (find_by_label(opt.drivename + 6, &iter) < 0) {
-	    error("Unable to find requested GPT partition by label.\n");
+	    error("Unable to find requested GPT partition by label.");
 	    goto bail;
 	}
     } else if ((opt.drivename[0] == 'h' || opt.drivename[0] == 'f') &&
@@ -328,13 +311,13 @@ int find_dp(struct part_iter **_iter)
 	if (disk_get_params(drive, &diskinfo))
 	    goto bail;
 	/* this will start iteration over FDD, possibly raw */
-	if (!(iter = pi_begin(&diskinfo, 0)))
+	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
 	    goto bail;
 
     } else if (!strcmp(opt.drivename, "boot") || !strcmp(opt.drivename, "fs")) {
 	if (!is_phys(sdi->c.filesystem)) {
 	    error("When syslinux is not booted from physical disk (or its emulation),\n"
-		   "'boot' and 'fs' are meaningless.\n");
+		   "'boot' and 'fs' are meaningless.");
 	    goto bail;
 	}
 	/* offsets match, but in case it changes in the future */
@@ -348,23 +331,23 @@ int find_dp(struct part_iter **_iter)
 	if (disk_get_params(drive, &diskinfo))
 	    goto bail;
 	/* this will start iteration over disk emulation, possibly raw */
-	if (!(iter = pi_begin(&diskinfo, 0)))
+	if (!(iter = pi_begin(&diskinfo, opt.piflags)))
 	    goto bail;
 
 	/* 'fs' => we should lookup the syslinux partition number and use it */
 	if (!strcmp(opt.drivename, "fs")) {
-	    while (!pi_next(&iter)) {
-		if (iter->start_lba == fs_lba)
+	    do {
+		if (iter->abs_lba == fs_lba)
 		    break;
-	    }
+	    } while (!pi_next(iter));
 	    /* broken part structure or other problems */
 	    if (iter->status) {
-		error("Can't find myself on the drive I booted from.\n");
+		error("Unable to find partition with syslinux (fs).");
 		goto bail;
 	    }
 	}
     } else {
-	error("Unparsable drive specification.\n");
+	error("Unparsable drive specification.");
 	goto bail;
     }
     /* main options done - only thing left is explicit partition specification,
@@ -377,15 +360,15 @@ int find_dp(struct part_iter **_iter)
 	do {
 	    if (iter->index == partition)
 		break;
-	} while (!pi_next(&iter));
+	} while (!pi_next(iter));
 	if (iter->status) {
-	    error("Requested disk / partition combination not found.\n");
+	    error("Unable to find requested disk / partition combination.");
 	    goto bail;
 	}
     }
 
     if (!(iter->di.disk & 0x80) && iter->index) {
-	error("WARNING: Partitions on floppy devices may not work.\n");
+	warn("Partitions on floppy devices may not work.");
     }
 
     *_iter = iter;
@@ -401,51 +384,53 @@ static int setup_handover(const struct part_iter *iter,
 		   struct data_area *data)
 {
     struct disk_dos_part_entry *ha;
-    uint32_t synth_size;
-    uint32_t *plen;
+    uint32_t synth_size = sizeof *ha;
 
-    if (!iter->index) { /* implies typeraw or non-iterated */
+    /*
+     * we have to cover both non-iterated but otherwise properly detected
+     * gpt/dos schemes as well as raw disks; checking index for 0 covers both
+     */
+    if (iter->index == 0) {
 	uint32_t len;
 	/* RAW handover protocol */
-	synth_size = sizeof(struct disk_dos_part_entry);
 	ha = malloc(synth_size);
 	if (!ha) {
-	    error("Could not build RAW hand-over record!\n");
+	    critm();
 	    goto bail;
 	}
 	len = ~0u;
 	if (iter->length < len)
-	    len = (uint32_t)iter->length;
-	lba2chs(&ha->start, &iter->di, 0, l2c_cadd);
-	lba2chs(&ha->end, &iter->di, len - 1, l2c_cadd);
+	    len = iter->length;
+	lba2chs(&ha->start, &iter->di, 0, L2C_CADD);
+	lba2chs(&ha->end, &iter->di, len - 1, L2C_CADD);
 	ha->active_flag = 0x80;
 	ha->ostype = 0xDA;	/* "Non-FS Data", anything is good here though ... */
 	ha->start_lba = 0;
 	ha->length = len;
     } else if (iter->type == typegpt) {
+	uint32_t *plen;
 	/* GPT handover protocol */
-	synth_size = sizeof(struct disk_dos_part_entry) +
-	    sizeof(uint32_t) + (uint32_t)iter->sub.gpt.pe_size;
+	synth_size += sizeof *plen + iter->gpt.pe_size;
 	ha = malloc(synth_size);
 	if (!ha) {
-	    error("Could not build GPT hand-over record!\n");
+	    critm();
 	    goto bail;
 	}
-	lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
-	lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
+	lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
+	lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
 	ha->active_flag = 0x80;
 	ha->ostype = 0xED;
 	/* All bits set by default */
 	ha->start_lba = ~0u;
 	ha->length = ~0u;
 	/* If these fit the precision, pass them on */
-	if (iter->start_lba < ha->start_lba)
-	    ha->start_lba = (uint32_t)iter->start_lba;
+	if (iter->abs_lba < ha->start_lba)
+	    ha->start_lba = iter->abs_lba;
 	if (iter->length < ha->length)
-	    ha->length = (uint32_t)iter->length;
+	    ha->length = iter->length;
 	/* Next comes the GPT partition record length */
-	plen = (uint32_t *) (ha + 1);
-	plen[0] = (uint32_t)iter->sub.gpt.pe_size;
+	plen = (uint32_t *)(ha + 1);
+	plen[0] = iter->gpt.pe_size;
 	/* Next comes the GPT partition record copy */
 	memcpy(plen + 1, iter->record, plen[0]);
 #ifdef DEBUG
@@ -453,20 +438,20 @@ static int setup_handover(const struct part_iter *iter,
 	disk_dos_part_dump(ha);
 	disk_gpt_part_dump((struct disk_gpt_part_entry *)(plen + 1));
 #endif
+    /* the only possible case left is dos scheme */
     } else if (iter->type == typedos) {
 	/* MBR handover protocol */
-	synth_size = sizeof(struct disk_dos_part_entry);
 	ha = malloc(synth_size);
 	if (!ha) {
-	    error("Could not build MBR hand-over record!\n");
+	    critm();
 	    goto bail;
 	}
 	memcpy(ha, iter->record, synth_size);
 	/* make sure these match bios imaginations and are ebr agnostic */
-	lba2chs(&ha->start, &iter->di, iter->start_lba, l2c_cadd);
-	lba2chs(&ha->end, &iter->di, iter->start_lba + iter->length - 1, l2c_cadd);
-	ha->start_lba = (uint32_t)iter->start_lba;
-	ha->length = (uint32_t)iter->length;
+	lba2chs(&ha->start, &iter->di, iter->abs_lba, L2C_CADD);
+	lba2chs(&ha->end, &iter->di, iter->abs_lba + iter->length - 1, L2C_CADD);
+	ha->start_lba = iter->abs_lba;
+	ha->length = iter->length;
 
 #ifdef DEBUG
 	dprintf("MBR handover:\n");
@@ -495,9 +480,9 @@ int main(int argc, char *argv[])
 
     console_ansi_raw();
 
-    memset(&fdat, 0, sizeof(fdat));
-    memset(&hdat, 0, sizeof(hdat));
-    memset(&sdat, 0, sizeof(sdat));
+    memset(&fdat, 0, sizeof fdat);
+    memset(&hdat, 0, sizeof hdat);
+    memset(&sdat, 0, sizeof sdat);
 
     opt_set_defs();
     if (opt_parse_args(argc, argv))
@@ -530,11 +515,11 @@ int main(int argc, char *argv[])
 	fdat.base = (opt.fseg << 4) + opt.foff;
 
 	if (loadfile(opt.file, &fdat.data, &fdat.size)) {
-	    error("Couldn't read the boot file.\n");
+	    error("Couldn't read the boot file.");
 	    goto bail;
 	}
-	if (fdat.base + fdat.size - 1 > ADDRMAX) {
-	    error("The boot file is too big to load at this address.\n");
+	if (fdat.base + fdat.size > dosmax) {
+	    error("The boot file is too big to load at this address.");
 	    goto bail;
 	}
     }
@@ -544,23 +529,23 @@ int main(int argc, char *argv[])
 	sdat.base = (opt.sseg << 4) + opt.soff;
 	sdat.size = iter->di.bps;
 
-	if (sdat.base + sdat.size - 1 > ADDRMAX) {
-	    error("The sector cannot be loaded at such high address.\n");
+	if (sdat.base + sdat.size > dosmax) {
+	    error("The sector cannot be loaded at such high address.");
 	    goto bail;
 	}
-	if (!(sdat.data = disk_read_sectors(&iter->di, iter->start_lba, 1))) {
-	    error("Couldn't read the sector.\n");
+	if (!(sdat.data = disk_read_sectors(&iter->di, iter->abs_lba, 1))) {
+	    error("Couldn't read the sector.");
 	    goto bail;
 	}
 	if (opt.save) {
 	    if (!(sbck = malloc(sdat.size))) {
-		error("Couldn't allocate cmp-buf for option 'save'.\n");
+		critm();
 		goto bail;
 	    }
 	    memcpy(sbck, sdat.data, sdat.size);
 	}
 	if (opt.file && opt.maps && overlap(&fdat, &sdat)) {
-	    error("WARNING: The sector won't be mmapped, as it would conflict with the boot file.\n");
+	    warn("The sector won't be mmapped, as it would conflict with the boot file.");
 	    opt.maps = false;
 	}
     }
@@ -572,8 +557,8 @@ int main(int argc, char *argv[])
 	/* Verify possible conflicts */
 	if ( ( opt.file && overlap(&fdat, &hdat)) ||
 	     ( opt.maps && overlap(&sdat, &hdat)) ) {
-	    error("WARNING: Handover area won't be prepared,\n"
-		  "as it would conflict with the boot file and/or the sector.\n");
+	    warn("Handover area won't be prepared,\n"
+		  "as it would conflict with the boot file and/or the sector.");
 	    opt.hand = false;
 	}
     }
@@ -617,22 +602,22 @@ int main(int argc, char *argv[])
      */
 
     if (opt.file)
-	memcpy(data + ndata++, &fdat, sizeof(fdat));
+	memcpy(data + ndata++, &fdat, sizeof fdat);
     if (opt.maps)
-	memcpy(data + ndata++, &sdat, sizeof(sdat));
+	memcpy(data + ndata++, &sdat, sizeof sdat);
     if (opt.hand)
-	memcpy(data + ndata++, &hdat, sizeof(hdat));
+	memcpy(data + ndata++, &hdat, sizeof hdat);
 
 #ifdef DEBUG
-    printf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
+    dprintf("iter->di dsk, bps: %X, %u\niter->di lbacnt, C*H*S: %"PRIu64", %u\n"
 	   "iter->di C, H, S: %u, %u, %u\n",
 	iter->di.disk, iter->di.bps,
 	iter->di.lbacnt, iter->di.cyl * iter->di.head * iter->di.spt,
 	iter->di.cyl, iter->di.head, iter->di.spt);
-    printf("iter idx: %d\n", iter->index);
-    printf("iter lba: %"PRIu64"\n", iter->start_lba);
+    dprintf("iter idx: %d\n", iter->index);
+    dprintf("iter lba: %"PRIu64"\n", iter->abs_lba);
     if (opt.hand)
-	printf("hand lba: %u\n",
+	dprintf("hand lba: %u\n",
 		((struct disk_dos_part_entry *)hdat.data)->start_lba);
 #endif
 
@@ -644,7 +629,7 @@ int main(int argc, char *argv[])
     if (ndata && !opt.brkchain) /* boot only if we actually chainload */
 	do_boot(data, ndata);
     else
-	error("Service-only run completed, exiting.\n");
+	puts("Service-only run completed, exiting.");
 bail:
     pi_del(&iter);
     /* Free allocated areas */
diff --git a/com32/chain/chain.h b/com32/chain/chain.h
index fc481bc..be78043 100644
--- a/com32/chain/chain.h
+++ b/com32/chain/chain.h
@@ -1,5 +1,20 @@
-#ifndef _COM32_CHAIN_CHAIN_H
-#define _COM32_CHAIN_CHAIN_H
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_CHAIN_H
+#define COM32_CHAIN_CHAIN_H
 
 #include <syslinux/movebits.h>
 
diff --git a/com32/chain/common.h b/com32/chain/common.h
deleted file mode 100644
index b170a73..0000000
--- a/com32/chain/common.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#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
index 8358106..fa9c61b 100644
--- a/com32/chain/mangle.c
+++ b/com32/chain/mangle.c
@@ -1,3 +1,33 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
 #include <com32.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -5,7 +35,6 @@
 #include <stdint.h>
 #include <dprintf.h>
 #include <syslinux/config.h>
-#include "common.h"
 #include "chain.h"
 #include "options.h"
 #include "utility.h"
@@ -32,7 +61,7 @@ int manglef_isolinux(struct data_area *data)
     sdi = syslinux_derivative_info();
 
     if (sdi->c.filesystem != SYSLINUX_FS_ISOLINUX) {
-	error ("The isolinux= option is only valid when run from ISOLINUX.\n");
+	error("The isolinux= option is only valid when run from ISOLINUX.");
 	goto bail;
     }
 
@@ -58,7 +87,7 @@ int manglef_isolinux(struct data_area *data)
     file_lba = get_file_lba(opt.file);
 
     if (file_lba == 0) {
-	error("Failed to find LBA offset of the boot file\n");
+	error("Failed to find LBA offset of the boot file.");
 	goto bail;
     }
     /* Set it */
@@ -132,8 +161,8 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
     if (!(opt.file && opt.grub))
 	return 0;
 
-    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");
+    if (data->size < sizeof *stage2) {
+	error("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.");
 	goto bail;
     }
     stage2 = data->data;
@@ -144,7 +173,7 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
      */
     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");
+	error("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary.");
 	goto bail;
     }
 
@@ -174,7 +203,7 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
      *   0-3:  primary partitions
      *   4-*:  logical partitions
      */
-    stage2->install_partition.part1 = (uint8_t)(iter->index - 1);
+    stage2->install_partition.part1 = iter->index - 1;
 
     /*
      * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
@@ -182,8 +211,8 @@ int manglef_grub(const struct part_iter *iter, struct data_area *data)
      * 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");
+	if (strlen(opt.grubcfg) >= sizeof stage2->config_file) {
+	    error("The config filename length can't exceed 88 characters.");
 	    goto bail;
 	}
 
@@ -224,19 +253,19 @@ int manglef_drmk(struct data_area *data)
     dprintf("  fs_lba offset is %d\n", fs_lba);
     /* DRMK only uses a DWORD */
     if (fs_lba > 0xffffffff) {
-	error("LBA very large; Only using lower 32 bits; DRMK will probably fail\n");
+	error("LBA very large; Only using lower 32 bits; DRMK will probably fail.");
     }
     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");
+	error("Failed to realloc for DRMK.");
 	goto bail;
     }
     data->size = tsize;
     /* ds:bp is assumed by DRMK to be the boot sector */
     /* offset 28 is the FAT HiddenSectors value */
-    opt.regs.ds = (uint16_t)((tsize >> 4) + (opt.fseg - 2));
+    opt.regs.ds = (tsize >> 4) + (opt.fseg - 2);
     /* "Patch" into tail of the new space */
-    *(uint32_t *)((char*)data->data + tsize - 4) = (uint32_t)fs_lba;
+    *(uint32_t *)((char*)data->data + tsize - 4) = fs_lba;
 
     return 0;
 bail:
@@ -246,29 +275,32 @@ bail:
 /* Adjust BPB common function */
 static int mangle_bpb(const struct part_iter *iter, struct data_area *data, const char *tag)
 {
-    unsigned int off;
     int type = bpb_detect(data->data, tag);
+    int off = drvoff_detect(type);
 
+    /* BPB: hidden sectors 64bit - exFAT only for now */
+    if (type == bpbEXF)
+	    *(uint64_t *) ((char *)data->data + 0x40) = iter->abs_lba;
     /* BPB: hidden sectors 32bit*/
-    if (type >= bpbV34) {
-	if (iter->start_lba < ~0u)
-	    *(uint32_t *) ((char *)data->data + 0x1c) = (uint32_t)iter->start_lba;
+    else if (bpbV34 <= type && type <= bpbV70) {
+	if (iter->abs_lba < ~0u)
+	    *(uint32_t *) ((char *)data->data + 0x1c) = iter->abs_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 if (bpbV30 <= type && type <= bpbV32) {
+	if (iter->abs_lba < 0xFFFF)
+	    *(uint16_t *) ((char *)data->data + 0x1c) = iter->abs_lba;
 	else
 	    /* won't really help much, but ... */
 	    *(uint16_t *) ((char *)data->data + 0x1c) = (uint16_t)~0u;
     }
+
     /* BPB: legacy geometry */
-    if (type >= bpbV30) {
+    if (bpbV30 <= type && type <= bpbV70) {
 	if (iter->di.cbios)
-	    *(uint32_t *)((char *)data->data + 0x18) = (uint32_t)((iter->di.head << 16) | iter->di.spt);
+	    *(uint32_t *)((char *)data->data + 0x18) = (iter->di.head << 16) | iter->di.spt;
 	else {
 	    if (iter->di.disk & 0x80)
 		*(uint32_t *)((char *)data->data + 0x18) = 0x00FF003F;
@@ -277,9 +309,8 @@ static int mangle_bpb(const struct part_iter *iter, struct data_area *data, cons
 	}
     }
     /* BPB: drive */
-    if (drvoff_detect(type, &off)) {
-	*(uint8_t *)((char *)data->data + off) = (uint8_t)
-	    (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
+    if (off >= 0) {
+	*(uint8_t *)((char *)data->data + off) = (opt.swap ? iter->di.disk & 0x80 : iter->di.disk);
     }
 
     return 0;
@@ -314,7 +345,7 @@ int mangles_bpb(const struct part_iter *iter, struct data_area *data)
 int manglesf_bss(struct data_area *sec, struct data_area *fil)
 {
     int type1, type2;
-    unsigned int cnt = 0;
+    size_t cnt = 0;
 
     if (!(opt.sect && opt.file && opt.bss))
 	return 0;
@@ -323,12 +354,12 @@ int manglesf_bss(struct data_area *sec, struct data_area *fil)
     type2 = bpb_detect(sec->data, "bss/sect");
 
     if (!type1 || !type2) {
-	error("Couldn't determine the BPB type for option 'bss'.\n");
+	error("Couldn't determine the BPB type for option 'bss'.");
 	goto bail;
     }
     if (type1 != type2) {
 	error("Option 'bss' can't be used,\n"
-		"when a sector and a file have incompatible BPBs.\n");
+		"when a sector and a file have incompatible BPBs.");
 	goto bail;
     }
 
@@ -348,6 +379,8 @@ int manglesf_bss(struct data_area *sec, struct data_area *fil)
 	cnt = 0x3C;
     } else if (type1 <= bpbV70) {
 	cnt = 0x42;
+    } else if (type1 <= bpbEXF) {
+	cnt = 0x60;
     }
     memcpy((char *)fil->data + 0x18, (char *)sec->data + 0x18, cnt);
 
@@ -365,8 +398,8 @@ int mangles_save(const struct part_iter *iter, const struct data_area *data, voi
 	return 0;
 
     if (memcmp(org, data->data, data->size)) {
-	if (disk_write_sectors(&iter->di, iter->start_lba, data->data, 1)) {
-	    error("Cannot write the updated sector.\n");
+	if (disk_write_sectors(&iter->di, iter->abs_lba, data->data, 1)) {
+	    error("Cannot write the updated sector.");
 	    goto bail;
 	}
 	/* function can be called again */
@@ -388,7 +421,7 @@ int mangles_cmldr(struct data_area *data)
     if (!(opt.sect && opt.cmldr))
 	return 0;
 
-    memcpy((char *)data->data + 3, cmldr_signature, sizeof(cmldr_signature));
+    memcpy((char *)data->data + 3, cmldr_signature, sizeof cmldr_signature);
     return 0;
 }
 
@@ -397,18 +430,18 @@ int mangler_init(const struct part_iter *iter)
 {
     /* 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;
+	opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.fseg;
+	opt.regs.ip = opt.fip;
     } else {
-	opt.regs.cs = opt.regs.ds = opt.regs.ss = (uint16_t)opt.sseg;
-	opt.regs.ip = (uint16_t)opt.sip;
+	opt.regs.cs = opt.regs.ds = opt.regs.ss = opt.sseg;
+	opt.regs.ip = opt.sip;
     }
 
     if (opt.regs.ip == 0x7C00 && !opt.regs.cs)
 	opt.regs.esp.l = 0x7C00;
 
     /* 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;
+    opt.regs.ebx.b[0] = opt.regs.edx.b[0] = iter->di.disk;
 
     return 0;
 }
@@ -418,7 +451,7 @@ int mangler_handover(const struct part_iter *iter, const struct data_area *data)
 {
     if (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.ds = opt.sseg;
 	opt.regs.eax.l = 0;
     } else if (opt.hand) {
 	/* base is really 0x7be */
@@ -442,7 +475,7 @@ int mangler_handover(const struct part_iter *iter, const struct data_area *data)
 int mangler_grldr(const struct part_iter *iter)
 {
     if (opt.grldr)
-	opt.regs.edx.b[1] = (uint8_t)(iter->index - 1);
+	opt.regs.edx.b[1] = iter->index - 1;
 
     return 0;
 }
@@ -450,15 +483,15 @@ int mangler_grldr(const struct part_iter *iter)
 /*
  * try to copy values from temporary iterator, if positions match
  */
-static void push_embr(struct part_iter *diter, struct part_iter *siter)
+static void mbrcpy(struct part_iter *diter, struct part_iter *siter)
 {
-    if (diter->sub.dos.cebr_lba == siter->sub.dos.cebr_lba &&
+    if (diter->dos.cebr_lba == siter->dos.cebr_lba &&
 	    diter->di.disk == siter->di.disk) {
 	memcpy(diter->data, siter->data, sizeof(struct disk_dos_mbr));
     }
 }
 
-static int mpe_sethide(struct part_iter *iter, struct part_iter *miter)
+static int fliphide(struct part_iter *iter, struct part_iter *miter)
 {
     struct disk_dos_part_entry *dp;
     static const uint16_t mask =
@@ -471,8 +504,8 @@ static int mpe_sethide(struct part_iter *iter, struct part_iter *miter)
 
     if ((t <= 0x1f) && ((mask >> (t & ~0x10u)) & 1)) {
 	/* It's a hideable partition type */
-	if (miter->index == iter->index || opt.hide & 4)
-	    t &= (uint8_t)(~0x10u);	/* unhide */
+	if (miter->index == iter->index || opt.hide & HIDE_REV)
+	    t &= ~0x10u;	/* unhide */
 	else
 	    t |= 0x10u;	/* hide */
     }
@@ -494,69 +527,98 @@ int manglepe_hide(struct part_iter *miter)
 {
     int wb = 0, werr = 0;
     struct part_iter *iter = NULL;
-    struct disk_dos_part_entry *dp;
     int ridx;
 
-    if (!opt.hide)
+    if (!(opt.hide & HIDE_ON))
 	return 0;
 
     if (miter->type != typedos) {
-	error("Options '*hide*' is meaningful only for legacy partition scheme.\n");
+	error("Option '[un]hide[all]' works only for legacy (DOS) partition scheme.");
 	return -1;
     }
 
-    if (miter->index < 1)
-	error("WARNING: It's impossible to unhide a disk.\n");
-
-    if (miter->index > 4 && !(opt.hide & 2))
-	error("WARNING: your partition is beyond mbr, so it can't be unhidden without '*hideall'.\n");
+    if (miter->index > 4 && !(opt.hide & HIDE_EXT))
+	warn("Specified partition is logical, so it can't be unhidden without 'unhideall'.");
 
-    if (!(iter = pi_begin(&miter->di, 1)))  /* turn stepall on */
+    if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
 	return -1;
 
-    while (!pi_next(&iter) && !werr) {
-	ridx = iter->rawindex;
-	if (!(opt.hide & 2) && ridx > 4)
+    while (!pi_next(iter) && !werr) {
+	ridx = iter->index0;
+	if (!(opt.hide & HIDE_EXT) && ridx > 3)
 	    break;  /* skip when we're constrained to pri only */
 
-	dp = (struct disk_dos_part_entry *)iter->record;
-	if (dp->ostype)
-	    wb |= mpe_sethide(iter, miter);
+	if (iter->index != -1)
+	    wb |= fliphide(iter, miter);
 
-	if (ridx >= 4 && wb && !werr) {
-	    push_embr(miter, iter);
-	    werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+	/*
+	 * we have to update mbr and each extended partition, but only if
+	 * changes (wb) were detected and there was no prior write error (werr)
+	 */
+	if (ridx >= 3 && wb && !werr) {
+	    mbrcpy(miter, iter);
+	    werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
 	    wb = 0;
 	}
     }
 
-    if (iter->status > PI_DONE)
+    if (iter->status < 0)
 	goto bail;
 
-    /* last write */
+    /* last update */
     if (wb && !werr) {
-	push_embr(miter, iter);
-	werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+	mbrcpy(miter, iter);
+	werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
     }
     if (werr)
-	error("WARNING: failed to write E/MBR during '*hide*'\n");
+	warn("Failed to write E/MBR during '[un]hide[all]'.");
 
 bail:
     pi_del(&iter);
     return 0;
 }
 
-static int mpe_setchs(const struct disk_info *di,
-		     struct disk_dos_part_entry *dp,
-		     uint32_t lba1)
+static int updchs(struct part_iter *iter, int ext)
 {
-    uint32_t ochs1, ochs2;
+    struct disk_dos_part_entry *dp;
+    uint32_t ochs1, ochs2, lba;
 
+    dp = (struct disk_dos_part_entry *)iter->record;
+    if (!ext) {
+	/* primary or logical */
+	lba = (uint32_t)iter->abs_lba;
+    } else {
+	/* extended */
+	dp += 1;
+	lba = iter->dos.nebr_lba;
+    }
     ochs1 = *(uint32_t *)dp->start;
     ochs2 = *(uint32_t *)dp->end;
 
-    lba2chs(&dp->start, di, lba1, l2c_cadd);
-    lba2chs(&dp->end, di, lba1 + dp->length - 1, l2c_cadd);
+    /*
+     * We have to be a bit more careful here in case of 0 start and/or length;
+     * start = 0 would be converted to the beginning of the disk (C/H/S =
+     * 0/0/1) or the [B]EBR, length = 0 would actually set the end CHS to be
+     * lower than the start CHS.
+     *
+     * Both are harmless in case of a hole (and in non-hole case will make
+     * partiter complain about corrupt layout if PIF_STRICT is set), but it
+     * makes everything look silly and not really correct.
+     *
+     * Thus the approach as seen below.
+     */
+
+    if (dp->start_lba || iter->index != -1) {
+	lba2chs(&dp->start, &iter->di, lba, L2C_CADD);
+    } else {
+	memset(&dp->start, 0, sizeof dp->start);
+    }
+
+    if ((dp->start_lba || iter->index != -1) && dp->length) {
+	lba2chs(&dp->end, &iter->di, lba + dp->length - 1, L2C_CADD);
+    } else {
+	memset(&dp->end, 0, sizeof dp->end);
+    }
 
     return
 	*(uint32_t *)dp->start != ochs1 ||
@@ -570,45 +632,47 @@ int manglepe_fixchs(struct part_iter *miter)
 {
     int wb = 0, werr = 0;
     struct part_iter *iter = NULL;
-    struct disk_dos_part_entry *dp;
     int ridx;
 
     if (!opt.fixchs)
 	return 0;
 
     if (miter->type != typedos) {
-	error("Options 'fixchs' is meaningful only for legacy partition scheme.\n");
+	error("Option 'fixchs' works only for legacy (DOS) partition scheme.");
 	return -1;
     }
 
-    if (!(iter = pi_begin(&miter->di, 1)))  /* turn stepall on */
+    if (!(iter = pi_begin(&miter->di, PIF_STEPALL | opt.piflags)))
 	return -1;
 
-    while (!pi_next(&iter) && !werr) {
-	ridx = iter->rawindex;
-	dp = (struct disk_dos_part_entry *)iter->record;
+    while (!pi_next(iter) && !werr) {
+	ridx = iter->index0;
 
-	wb |= mpe_setchs(&iter->di, dp, (uint32_t)iter->start_lba);
-	if (ridx > 4)
-		wb |= mpe_setchs(&iter->di, dp + 1, iter->sub.dos.nebr_lba);
+	wb |= updchs(iter, 0);
+	if (ridx > 3)
+	    wb |= updchs(iter, 1);
 
-	if (ridx >= 4 && wb && !werr) {
-	    push_embr(miter, iter);
-	    werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+	/*
+	 * we have to update mbr and each extended partition, but only if
+	 * changes (wb) were detected and there was no prior write error (werr)
+	 */
+	if (ridx >= 3 && wb && !werr) {
+	    mbrcpy(miter, iter);
+	    werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
 	    wb = 0;
 	}
     }
 
-    if (iter->status > PI_DONE)
+    if (iter->status < 0)
 	goto bail;
 
-    /* last write */
+    /* last update */
     if (wb && !werr) {
-	push_embr(miter, iter);
-	werr |= disk_write_sectors(&iter->di, iter->sub.dos.cebr_lba, iter->data, 1);
+	mbrcpy(miter, iter);
+	werr |= disk_write_sectors(&iter->di, iter->dos.cebr_lba, iter->data, 1);
     }
     if (werr)
-	error("WARNING: failed to write E/MBR during 'fixchs'\n");
+	warn("Failed to write E/MBR during 'fixchs'.");
 
 bail:
     pi_del(&iter);
diff --git a/com32/chain/mangle.h b/com32/chain/mangle.h
index bcefea3..0d08229 100644
--- a/com32/chain/mangle.h
+++ b/com32/chain/mangle.h
@@ -1,5 +1,35 @@
-#ifndef _COM32_CHAIN_MANGLE_H
-#define _COM32_CHAIN_MANGLE_H
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_MANGLE_H
+#define COM32_CHAIN_MANGLE_H
 
 #include "chain.h"
 #include "partiter.h"
diff --git a/com32/chain/options.c b/com32/chain/options.c
index 658a45c..1215630 100644
--- a/com32/chain/options.c
+++ b/com32/chain/options.c
@@ -1,21 +1,55 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <syslinux/movebits.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include "common.h"
 #include "chain.h"
+#include "partiter.h"
 #include "utility.h"
 #include "options.h"
 
 struct options opt;
 
-static int soi_s2n(char *ptr, unsigned int *seg,
-		       unsigned int *off,
-		       unsigned int *ip,
-		       unsigned int def)
+static int soi_s2n(char *ptr,
+			addr_t *seg,
+			addr_t *off,
+			addr_t *ip,
+			addr_t def)
 {
-    unsigned int segval = 0, offval, ipval, val;
+    addr_t segval, offval, ipval, val;
     char *p;
 
+    /* defaults */
+    segval = 0;
     offval = def;
     ipval = def;
 
@@ -25,17 +59,22 @@ static int soi_s2n(char *ptr, unsigned int *seg,
     if (p[0] == ':' && p[1] && p[1] != ':')
 	ipval = strtoul(p+1, NULL, 0);
 
+    /* verify if load address is within [dosmin, dosmax) */
     val = (segval << 4) + offval;
 
-    if (val < ADDRMIN || val > ADDRMAX) {
-	error("Invalid seg:off:* address specified..\n");
+    if (val < dosmin || val >= dosmax) {
+	error("Invalid seg:off:* address specified.");
 	goto bail;
     }
 
+    /*
+     * verify if jump address is within [dosmin, dosmax) and offset is 16bit
+     * sane
+     */
     val = (segval << 4) + ipval;
 
-    if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
-	error("Invalid seg:*:ip address specified.\n");
+    if (ipval > 0xFFFE || val < dosmin || val >= dosmax) {
+	error("Invalid seg:*:ip address specified.");
 	goto bail;
     }
 
@@ -53,79 +92,92 @@ bail:
 
 static void usage(void)
 {
-    unsigned int i;
-    static const char key[] = "Press any key...\n";
+    size_t i;
     static const char *const usage[] = {
-"\
-Usage:\n\
-    chain.c32 [options]\n\
-    chain.c32 {fd|hd}<disk#>{,| }[<part#>] [options]\n\
-    chain.c32 mbr{:|=}<id>{,| }[<part#>] [options]\n\
-    chain.c32 guid{:|=}<guid>{,| }[<part#>] [options]\n\
-    chain.c32 label{:|=}<label> [<part#>] [options]\n\
-    chain.c32 boot{,| }[<part#>] [options]\n\
-    chain.c32 fs [options]\n\
-", "\
-\nOptions ('no' prefix specifies default value):\n\
-    sect[=<s[:o[:i]]>]   Load sector at <s:o>, jump to <s:i>\n\
-                         - defaults to 0:0x7C00:0x7C00\n\
-                         - ommited o/i values default to 0\n\
-    maps                 Map loaded sector into real memory\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\
-    noswap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
-    nohide               Disable all hide variations (also the default)\n\
-    hide                 Hide primary partitions, unhide selected partition\n\
-    hideall              Hide *all* partitions, unhide selected partition\n\
-    unhide               Unhide primary partitions\n\
-    unhideall            Unhide *all* partitions\n\
-    nofixchs             Walk *all* partitions and fix E/MBRs' chs values\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\
-    nobreak              Actually perform the chainloading\n\
-", "\
-\nOptions continued ...\n\
-    file=<file>          Load and execute <file>\n\
-    seg=<s[:o[:i]]>      Load file at <s:o>, jump to <s:i>\n\
-                         - defaults to 0:0x7C00:0x7C00\n\
-                         - ommited o/i values default to 0\n\
-    isolinux=<loader>    Load another version of ISOLINUX\n\
-    ntldr=<loader>       Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
-    reactos=<loader>     Load ReactOS's loader\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\
-    bss=<filename>       Emulate syslinux's BSS\n\
-    bs=<filename>        Emulate syslinux's BS\n\
-\nPlease see doc/chain.txt for the detailed documentation.\n\
-"
-    };
+"Usage:",
+"",
+"  disk + partition selection:",
+"        chain.c32 [options]",
+"        chain.c32 hd#[,#] [options]",
+"        chain.c32 fd#[,#] [options]",
+"        chain.c32 mbr=<id>[,#] [options]",
+"        chain.c32 guid=<guid>[,#] [options]",
+"        chain.c32 boot[,#] [options]",
+"",
+"  direct partition selection:",
+"        chain.c32 guid=<guid> [options]",
+"        chain.c32 label=<label> [options]",
+"        chain.c32 fs [options]",
+"",
+"You can use ':' instead of '=' and ' ' instead of ','.",
+"The default is 'boot,0'.",
+"",
+"Options:",
+"  sect[=<s[:o[:i]]>]   Load sector at <s:o>, jump to <s:i>",
+"                       - defaults to 0:0x7C00:0x7C00",
+"                       - omitted o/i values default to 0",
+"  maps                 Map loaded sector into real memory",
+"  setbpb               Fix BPB fields in loaded sector",
+"  filebpb              Apply 'setbpb' to loaded file",
+"  save                 Write adjusted sector back to disk",
+"  hand                 Prepare handover area",
+"  hptr                 Force ds:si and ds:bp to point to handover area",
+"  swap                 Swap drive numbers, if bootdisk is not fd0/hd0",
+"  nohide               Disable all hide variations (default)",
+"  hide                 Hide primary partitions, unhide selected partition",
+"  hideall              Hide *all* partitions, unhide selected partition",
+"  unhide               Unhide primary partitions",
+"  unhideall            Unhide *all* partitions",
+"  fixchs               Walk *all* partitions and fix E/MBRs' CHS values",
+"  keeppxe              Keep the PXE and UNDI stacks in memory (PXELINUX)",
+"  warn                 Wait for a keypress to continue chainloading",
+"  break                Don't chainload",
+"  gpthcrc              Perform gpt header crc check",
+"  gptlcrc              Perform gpt list crc check",
+"  strict[=<0|1|2>]     Set the level of strictness in sanity checks",
+"                       - strict w/o any value is the same as strict=2",
+"  relax                The same as strict=0",
+"  prefmbr              On hybrid MBR/GPT disks, prefer legacy layout",
+"",
+"  file=<file>          Load and execute <file>",
+"  seg=<s[:o[:i]]>      Load file at <s:o>, jump to <s:i>",
+"                       - defaults to 0:0x7C00:0x7C00",
+"                       - omitted o/i values default to 0",
+"  isolinux=<loader>    Load another version of ISOLINUX",
+"  ntldr=<loader>       Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR",
+"  reactos=<loader>     Load ReactOS's loader",
+"  cmldr=<loader>       Load Recovery Console of Windows NT/2K/XP/2003",
+"  freedos=<loader>     Load FreeDOS KERNEL.SYS",
+"  msdos=<loader>       Load MS-DOS 2.xx - 6.xx IO.SYS",
+"  msdos7=<loader>      Load MS-DOS 7+ IO.SYS",
+"  pcdos=<loader>       Load PC-DOS IBMBIO.COM",
+"  drmk=<loader>        Load DRMK DELLBIO.BIN",
+"  grub=<loader>        Load GRUB Legacy stage2",
+"  grubcfg=<config>     Set alternative config filename for GRUB Legacy",
+"  grldr=<loader>       Load GRUB4DOS grldr",
+"  bss=<sectimage>      Emulate syslinux's BSS",
+"  bs=<sectimage>       Emulate syslinux's BS",
+"",
+"Please see doc/chain.txt for the detailed documentation."
+};
     for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) {
-	if (i) {
-	    error(key);
+	if (i % 20 == 19) {
+	    puts("Press any key...");
 	    wait_key();
 	}
-	error(usage[i]);
+	puts(usage[i]);
     }
 }
 
 void opt_set_defs(void)
 {
-    memset(&opt, 0, sizeof(opt));
+    memset(&opt, 0, sizeof opt);
     opt.sect = true;	    /* by def. load sector */
     opt.maps = true;	    /* by def. map sector */
     opt.hand = true;	    /* by def. prepare handover */
     opt.brkchain = false;   /* by def. do chainload */
+    /* strict but ignore disk size, do all gpt crc checks */
+    opt.piflags = PIF_STRICT | PIF_GPTHCRC | PIF_GPTLCRC;
     opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
     opt.drivename = "boot";
 #ifdef DEBUG
@@ -136,7 +188,7 @@ void opt_set_defs(void)
 int opt_parse_args(int argc, char *argv[])
 {
     int i;
-    unsigned int v;
+    size_t v;
     char *p;
 
     for (i = 1; i < argc; i++) {
@@ -152,7 +204,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.bss = true;
 	    opt.maps = false;
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	} else if (!strncmp(argv[i], "bs=", 3)) {
 	    opt.file = argv[i] + 3;
 	    opt.sect = false;
@@ -168,7 +219,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.fip = 0;
 	    opt.file = argv[i] + 6;
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "reactos=", 8)) {
 	    /*
@@ -182,7 +232,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.fip = 0x8100;
 	    opt.file = argv[i] + 8;
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "cmldr=", 6)) {
 	    opt.fseg = 0x2000;  /* CMLDR wants this address */
@@ -191,7 +240,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.file = argv[i] + 6;
 	    opt.cmldr = true;
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "freedos=", 8)) {
 	    opt.fseg = 0x60;    /* FREEDOS wants this address */
@@ -200,7 +248,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.sseg = 0x1FE0;
 	    opt.file = argv[i] + 8;
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
 		     !strncmp(argv[i], "pcdos=", v)) ||
@@ -211,7 +258,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.sseg = 0x8000;
 	    opt.file = argv[i] + v;
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "drmk=", 5)) {
 	    opt.fseg = 0x70;    /* DRMK wants this address */
@@ -223,7 +269,6 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.file = argv[i] + 5;
 	    /* opt.drmk = true; */
 	    opt.setbpb = true;
-	    /* opt.save = true; */
 	    opt.hand = false;
 	} else if (!strncmp(argv[i], "grub=", 5)) {
 	    opt.fseg = 0x800;	/* stage2 wants this address */
@@ -261,15 +306,19 @@ int opt_parse_args(int argc, char *argv[])
 	} else if (!strcmp(argv[i], "noswap")) {
 	    opt.swap = false;
 	} else if (!strcmp(argv[i], "nohide")) {
-	    opt.hide = 0;
+	    opt.hide = HIDE_OFF;
 	} else if (!strcmp(argv[i], "hide")) {
-	    opt.hide = 1; /* 001b */
+	    opt.hide = HIDE_ON;
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
 	} else if (!strcmp(argv[i], "hideall")) {
-	    opt.hide = 2; /* 010b */
+	    opt.hide = HIDE_ON | HIDE_EXT;
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
 	} else if (!strcmp(argv[i], "unhide")) {
-	    opt.hide = 5; /* 101b */
+	    opt.hide = HIDE_ON | HIDE_REV;
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
 	} else if (!strcmp(argv[i], "unhideall")) {
-	    opt.hide = 6; /* 110b */
+	    opt.hide = HIDE_ON | HIDE_EXT | HIDE_REV;
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
 	} else if (!strcmp(argv[i], "setbpb")) {
 	    opt.setbpb = true;
 	} else if (!strcmp(argv[i], "nosetbpb")) {
@@ -290,16 +339,45 @@ int opt_parse_args(int argc, char *argv[])
 	    opt.maps = false;
 	} else if (!strcmp(argv[i], "save")) {
 	    opt.save = true;
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
 	} else if (!strcmp(argv[i], "nosave")) {
 	    opt.save = false;
 	} else if (!strcmp(argv[i], "fixchs")) {
 	    opt.fixchs = true;
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
 	} else if (!strcmp(argv[i], "nofixchs")) {
 	    opt.fixchs = false;
+	} else if (!strcmp(argv[i], "relax") || !strcmp(argv[i], "nostrict")) {
+	    opt.piflags &= ~(PIF_STRICT | PIF_STRICTER);
+	} else if (!strcmp(argv[i], "norelax") || !strcmp(argv[i], "strict")) {
+	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
+	} else if (!strncmp(argv[i], "strict=", 7)) {
+	    if (argv[i][7] < '0' || argv[i][7] > '2' || !argv[i][8]) {
+		error("Strict level must be 0, 1 or 2.");
+		goto bail;
+	    }
+	    opt.piflags &= ~(PIF_STRICT | PIF_STRICTER);
+	    switch (argv[i][7]) {
+		case '2': opt.piflags |= PIF_STRICTER;
+		case '1': opt.piflags |= PIF_STRICT; break;
+		default:;
+	    }
+	} else if (!strcmp(argv[i], "gpthcrc")) {
+	    opt.piflags |= PIF_GPTHCRC;
+	} else if (!strcmp(argv[i], "nogpthcrc")) {
+	    opt.piflags &= ~PIF_GPTHCRC;
+	} else if (!strcmp(argv[i], "gptlcrc")) {
+	    opt.piflags |= PIF_GPTLCRC;
+	} else if (!strcmp(argv[i], "nogptlcrc")) {
+	    opt.piflags &= ~PIF_GPTLCRC;
 	} else if (!strcmp(argv[i], "warn")) {
 	    opt.warn = true;
 	} else if (!strcmp(argv[i], "nowarn")) {
 	    opt.warn = false;
+	} else if (!strcmp(argv[i], "prefmbr")) {
+	    opt.piflags |= PIF_PREFMBR;
+	} else if (!strcmp(argv[i], "noprefmbr")) {
+	    opt.piflags &= ~PIF_PREFMBR;
 	} else if (!strcmp(argv[i], "nobreak")) {
 	    opt.brkchain = false;
 	} else if (!strcmp(argv[i], "break")) {
@@ -337,34 +415,27 @@ int opt_parse_args(int argc, char *argv[])
     }
 
     if (opt.grubcfg && !opt.grub) {
-	error("grubcfg=<filename> must be used together with grub=<loader>.\n");
+	error("grubcfg=<filename> must be used together with grub=<loader>.");
 	goto bail;
     }
 
-#if 0
-    if ((!opt.maps || !opt.sect) && !opt.file) {
-	error("You have to load something.\n");
-	goto bail;
-    }
-#endif
-
     if (opt.filebpb && !opt.file) {
-	error("Option 'filebpb' requires a file.\n");
+	error("Option 'filebpb' requires a file.");
 	goto bail;
     }
 
     if (opt.save && !opt.sect) {
-	error("Option 'save' requires a sector.\n");
+	error("Option 'save' requires a sector.");
 	goto bail;
     }
 
     if (opt.setbpb && !opt.sect) {
-	error("Option 'setbpb' requires a sector.\n");
+	error("Option 'setbpb' requires a sector.");
 	goto bail;
     }
 
     if (opt.maps && !opt.sect) {
-	error("Option 'maps' requires a sector.\n");
+	error("Option 'maps' requires a sector.");
 	goto bail;
     }
 
diff --git a/com32/chain/options.h b/com32/chain/options.h
index 4493ef1..4e18b88 100644
--- a/com32/chain/options.h
+++ b/com32/chain/options.h
@@ -1,20 +1,56 @@
-#ifndef _COM32_CHAIN_OPTIONS_H
-#define _COM32_CHAIN_OPTIONS_H
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_OPTIONS_H
+#define COM32_CHAIN_OPTIONS_H
 
 #include <stdint.h>
 #include <syslinux/bootrm.h>
+#include <syslinux/movebits.h>
+
+enum {HIDE_OFF = 0, HIDE_ON = 1, HIDE_EXT = 2, HIDE_REV = 4};
 
 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;
+    addr_t fseg;
+    addr_t foff;
+    addr_t fip;
+    addr_t sseg;
+    addr_t soff;
+    addr_t sip;
+    int hide;
+    int piflags;
+    uint16_t keeppxe;
     bool isolinux;
     bool cmldr;
     bool drmk;
@@ -24,7 +60,6 @@ struct options {
     bool hand;
     bool hptr;
     bool swap;
-    int hide;
     bool sect;
     bool save;
     bool bss;
@@ -33,7 +68,6 @@ struct options {
     bool fixchs;
     bool warn;
     bool brkchain;
-    uint16_t keeppxe;
     struct syslinux_rm_regs regs;
 };
 
diff --git a/com32/chain/partiter.c b/com32/chain/partiter.c
index 1acd195..3317b4d 100644
--- a/com32/chain/partiter.c
+++ b/com32/chain/partiter.c
@@ -1,8 +1,9 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
  *   Copyright 2010 Shao Miller
- *   Copyright 2010 Michal Soltys
+ *   Copyright 2010+ Michal Soltys
  *
  *   Permission is hereby granted, free of charge, to any person
  *   obtaining a copy of this software and associated documentation
@@ -39,7 +40,6 @@
 #include <stdarg.h>
 #include <zlib.h>
 #include <syslinux/disk.h>
-#include "common.h"
 #include "partiter.h"
 #include "utility.h"
 
@@ -47,175 +47,110 @@
 #define ost_is_nondata(type) (ost_is_ext(type) || (type) == 0x00)
 #define sane(s,l) ((s)+(l) > (s))
 
-/* forwards */
+/* virtual forwards */
 
-static int iter_ctor(struct part_iter *, va_list *);
-static int iter_dos_ctor(struct part_iter *, va_list *);
-static int iter_gpt_ctor(struct part_iter *, va_list *);
-static void iter_dtor(struct part_iter *);
-static struct part_iter *pi_dos_next(struct part_iter *);
-static struct part_iter *pi_gpt_next(struct part_iter *);
-static struct part_iter *pi_raw_next(struct part_iter *);
+static void pi_dtor_(struct part_iter *);
+static int  pi_next_(struct part_iter *);
+static int  pi_dos_next(struct part_iter *);
+static int  pi_gpt_next(struct part_iter *);
+
+/* vtab and types */
 
 static struct itertype types[] = {
    [0] = {
-	.ctor = &iter_dos_ctor,
-	.dtor = &iter_dtor,
+	.dtor = &pi_dtor_,
 	.next = &pi_dos_next,
 }, [1] = {
-	.ctor = &iter_gpt_ctor,
-	.dtor = &iter_dtor,
+	.dtor = &pi_dtor_,
 	.next = &pi_gpt_next,
 }, [2] = {
-	.ctor = &iter_ctor,
-	.dtor = &iter_dtor,
-	.next = &pi_raw_next,
+	.dtor = &pi_dtor_,
+	.next = &pi_next_,
 }};
 
 const struct itertype * const typedos = types;
 const struct itertype * const typegpt = types+1;
 const struct itertype * const typeraw = types+2;
 
-#ifdef DEBUG
-static int inv_type(const void *type)
+/* pi_dtor_() - common/raw iterator cleanup */
+static void pi_dtor_(struct part_iter *iter)
 {
-    int i, cnt = sizeof(types)/sizeof(types[0]);
-    for (i = 0; i < cnt; i++) {
-	if (type == types + i)
-	    return 0;
-    }
-    return -1;
+    /* syslinux's free is null resilient */
+    free(iter->data);
 }
-#endif
 
-/**
- * iter_ctor() - common iterator initialization
- * @iter:	iterator pointer
- * @args(0):	disk_info structure used for disk functions
- * @args(1):	stepall modifier
- *
- * Second and further arguments are passed as a pointer to va_list
- **/
-static int iter_ctor(struct part_iter *iter, va_list *args)
+/* pi_ctor() - common/raw iterator initialization */
+static int pi_ctor(struct part_iter *iter,
+	const struct disk_info *di, int flags
+)
 {
-    const struct disk_info *di = va_arg(*args, const struct disk_info *);
-    int stepall = va_arg(*args, int);
-
-#ifdef DEBUG
-    if (!di)
-	return -1;
-#endif
-
-    memcpy(&iter->di, di, sizeof(struct disk_info));
-    iter->stepall = stepall;
+    memcpy(&iter->di, di, sizeof *di);
+    iter->flags = flags;
     iter->index0 = -1;
     iter->length = di->lbacnt;
 
+    iter->type = typeraw;
     return 0;
 }
 
-/**
- * iter_dtor() - common iterator cleanup
- * @iter:	iterator pointer
- *
- **/
-static void iter_dtor(struct part_iter *iter)
-{
-    free(iter->data);
-}
-
-/**
- * iter_dos_ctor() - MBR/EBR iterator specific initialization
- * @iter:	iterator pointer
- * @args(0):	disk_info structure used for disk functions
- * @args(1):	pointer to buffer with loaded valid MBR
- *
- * Second and further arguments are passed as a pointer to va_list.
- * This function only makes rudimentary checks. If user uses
- * pi_new(), he/she is responsible for doing proper sanity checks.
- **/
-static int iter_dos_ctor(struct part_iter *iter, va_list *args)
+/* pi_dos_ctor() - MBR/EBR iterator specific initialization */
+static int pi_dos_ctor(struct part_iter *iter,
+	const struct disk_info *di, int flags,
+	const struct disk_dos_mbr *mbr
+)
 {
-    const struct disk_dos_mbr *mbr;
-
-    /* uses args(0) */
-    if (iter_ctor(iter, args))
+    if (pi_ctor(iter, di, flags))
 	return -1;
 
-    mbr = va_arg(*args, const struct disk_dos_mbr *);
-
-#ifdef DEBUG
-    if (!mbr)
-	goto bail;
-#endif
-
-    if (!(iter->data = malloc(sizeof(struct disk_dos_mbr))))
+    if (!(iter->data = malloc(sizeof *mbr))) {
+	critm();
 	goto bail;
+    }
 
-    memcpy(iter->data, mbr, sizeof(struct disk_dos_mbr));
+    memcpy(iter->data, mbr, sizeof *mbr);
 
-    iter->sub.dos.bebr_index0 = -1;
-    iter->sub.dos.disk_sig = mbr->disk_sig;
+    iter->dos.bebr_index0 = -1;
+    iter->dos.disk_sig = mbr->disk_sig;
 
+    iter->type = typedos;
     return 0;
 bail:
-    iter->type->dtor(iter);
+    pi_dtor_(iter);
     return -1;
 }
 
-/**
- * iter_gpt_ctor() - GPT iterator specific initialization
- * @iter:	iterator pointer
- * @args(0):	ptr to disk_info structure
- * @args(1):	ptr to buffer with GPT header
- * @args(2):	ptr to buffer with GPT partition list
- *
- * Second and further arguments are passed as a pointer to va_list.
- * This function only makes rudimentary checks. If user uses
- * pi_new(), he/she is responsible for doing proper sanity checks.
- **/
-static int iter_gpt_ctor(struct part_iter *iter, va_list *args)
+/* pi_gpt_ctor() - GPT iterator specific initialization */
+static int pi_gpt_ctor(struct part_iter *iter,
+	const struct disk_info *di, int flags,
+	const struct disk_gpt_header *gpth, const struct disk_gpt_part_entry *gptl
+)
 {
     uint64_t siz;
-    const struct disk_gpt_header *gpth;
-    const struct disk_gpt_part_entry *gptl;
 
-    /* uses args(0) */
-    if (iter_ctor(iter, args))
+    if (pi_ctor(iter, di, flags))
 	return -1;
 
-    gpth = va_arg(*args, const struct disk_gpt_header *);
-    gptl = va_arg(*args, const struct disk_gpt_part_entry *);
-
-#ifdef DEBUG
-    if (!gpth || !gptl)
-	goto bail;
-#endif
-
     siz = (uint64_t)gpth->part_count * gpth->part_size;
 
-#ifdef DEBUG
-    if (!siz || (siz + iter->di.bps - 1) / iter->di.bps > 255u ||
-	    gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
+    if (!(iter->data = malloc((size_t)siz))) {
+	critm();
 	goto bail;
     }
-#endif
-
-    if (!(iter->data = malloc((size_t)siz)))
-	goto bail;
 
     memcpy(iter->data, gptl, (size_t)siz);
 
-    iter->sub.gpt.pe_count = (int)gpth->part_count;
-    iter->sub.gpt.pe_size = (int)gpth->part_size;
-    iter->sub.gpt.ufirst = gpth->lba_first_usable;
-    iter->sub.gpt.ulast = gpth->lba_last_usable;
+    iter->gpt.pe_count = (int)gpth->part_count;
+    iter->gpt.pe_size = (int)gpth->part_size;
+    iter->gpt.ufirst = gpth->lba_first_usable;
+    iter->gpt.ulast = gpth->lba_last_usable;
 
-    memcpy(&iter->sub.gpt.disk_guid, &gpth->disk_guid, sizeof(struct guid));
+    memcpy(&iter->gpt.disk_guid, &gpth->disk_guid, sizeof gpth->disk_guid);
+    memcpy(&iter->gpt.part_guid, &gpth->disk_guid, sizeof gpth->disk_guid);
 
+    iter->type = typegpt;
     return 0;
 bail:
-    iter->type->dtor(iter);
+    pi_dtor_(iter);
     return -1;
 }
 
@@ -237,18 +172,21 @@ static int notsane_logical(const struct part_iter *iter)
 	return 0;
 
     if (ost_is_ext(dp[0].ostype)) {
-	error("1st EBR entry must be data or empty.\n");
+	error("The 1st EBR entry must be data or empty.");
 	return -1;
     }
 
+    if (!(iter->flags & PIF_STRICT))
+	return 0;
+
     end_log = dp[0].start_lba + dp[0].length;
 
     if (!dp[0].start_lba ||
 	!dp[0].length ||
 	!sane(dp[0].start_lba, dp[0].length) ||
-	end_log > iter->sub.dos.ebr_size) {
+	end_log > iter->dos.nebr_siz) {
 
-	error("Insane logical partition.\n");
+	error("Logical partition (in EBR) with invalid offset and/or length.");
 	return -1;
     }
 
@@ -273,18 +211,21 @@ static int notsane_extended(const struct part_iter *iter)
 	return 0;
 
     if (!ost_is_nondata(dp[1].ostype)) {
-	error("2nd EBR entry must be extended or empty.\n");
+	error("The 2nd EBR entry must be extended or empty.");
 	return -1;
     }
 
+    if (!(iter->flags & PIF_STRICT))
+	return 0;
+
     end_ebr = dp[1].start_lba + dp[1].length;
 
     if (!dp[1].start_lba ||
 	!dp[1].length ||
 	!sane(dp[1].start_lba, dp[1].length) ||
-	end_ebr > iter->sub.dos.bebr_size) {
+	end_ebr > iter->dos.bebr_siz) {
 
-	error("Insane extended partition.\n");
+	error("Extended partition (EBR) with invalid offset and/or length.");
 	return -1;
     }
 
@@ -304,11 +245,14 @@ static int notsane_primary(const struct part_iter *iter)
     if (!dp->ostype)
 	return 0;
 
+    if (!(iter->flags & PIF_STRICT))
+	return 0;
+
     if (!dp->start_lba ||
 	!dp->length ||
 	!sane(dp->start_lba, dp->length) ||
-	dp->start_lba + dp->length > iter->di.lbacnt) {
-	error("Insane primary (MBR) partition.\n");
+	((iter->flags & PIF_STRICTER) && (dp->start_lba + dp->length > iter->di.lbacnt))) {
+	error("Primary partition (in MBR) with invalid offset and/or length.");
 	return -1;
     }
 
@@ -319,21 +263,24 @@ static int notsane_gpt(const struct part_iter *iter)
 {
     const struct disk_gpt_part_entry *gp;
     gp = (const struct disk_gpt_part_entry *)
-	(iter->data + iter->index0 * iter->sub.gpt.pe_size);
+	(iter->data + iter->index0 * iter->gpt.pe_size);
 
     if (guid_is0(&gp->type))
 	return 0;
 
-    if (gp->lba_first < iter->sub.gpt.ufirst ||
-	gp->lba_last > iter->sub.gpt.ulast) {
-	error("Insane GPT partition.\n");
+    if (!(iter->flags & PIF_STRICT))
+	return 0;
+
+    if (gp->lba_first < iter->gpt.ufirst ||
+	gp->lba_last > iter->gpt.ulast) {
+	error("LBA sectors of GPT partition are beyond the range allowed in GPT header.");
 	return -1;
     }
 
     return 0;
 }
 
-static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
+static int dos_next_mbr(struct part_iter *iter, uint32_t *lba,
 			    struct disk_dos_part_entry **_dp)
 {
     struct disk_dos_part_entry *dp;
@@ -343,19 +290,19 @@ static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
 
 	if (notsane_primary(iter)) {
 	    iter->status = PI_INSANE;
-	    goto bail;
+	    return -1;
 	}
 
 	if (ost_is_ext(dp->ostype)) {
-	    if (iter->sub.dos.bebr_index0 >= 0) {
-		error("You have more than 1 extended partition.\n");
+	    if (iter->dos.bebr_index0 >= 0) {
+		error("More than 1 extended partition.");
 		iter->status = PI_INSANE;
-		goto bail;
+		return -1;
 	    }
 	    /* record base EBR index */
-	    iter->sub.dos.bebr_index0 = iter->index0;
+	    iter->dos.bebr_index0 = iter->index0;
 	}
-	if (!ost_is_nondata(dp->ostype) || iter->stepall) {
+	if (!ost_is_nondata(dp->ostype) || (iter->flags & PIF_STEPALL)) {
 	    *lba = dp->start_lba;
 	    *_dp = dp;
 	    break;
@@ -363,52 +310,48 @@ static int pi_dos_next_mbr(struct part_iter *iter, uint32_t *lba,
     }
 
     return 0;
-bail:
-    return -1;
 }
 
 static int prep_base_ebr(struct part_iter *iter)
 {
     struct disk_dos_part_entry *dp;
 
-    if (iter->sub.dos.bebr_index0 < 0)	/* if we don't have base extended partition at all */
+    if (iter->dos.bebr_index0 < 0)	/* if we don't have base extended partition at all */
 	return -1;
-    else if (!iter->sub.dos.bebr_start) { /* if not initialized yet */
-	dp = ((struct disk_dos_mbr *)iter->data)->table + iter->sub.dos.bebr_index0;
+    else if (!iter->dos.bebr_lba) { /* if not initialized yet */
+	dp = ((struct disk_dos_mbr *)iter->data)->table + iter->dos.bebr_index0;
 
-	iter->sub.dos.bebr_start = dp->start_lba;
-	iter->sub.dos.bebr_size = dp->length;
+	iter->dos.bebr_lba = dp->start_lba;
+	iter->dos.bebr_siz = dp->length;
 
-	iter->sub.dos.ebr_start = 0;
-	iter->sub.dos.ebr_size = iter->sub.dos.bebr_size;
-
-	iter->sub.dos.cebr_lba = 0;
-	iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start;
+	iter->dos.nebr_lba = dp->start_lba;
+	iter->dos.nebr_siz = dp->length;
 
 	iter->index0--;
     }
     return 0;
 }
 
-static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
+static int dos_next_ebr(struct part_iter *iter, uint32_t *lba,
 			    struct disk_dos_part_entry **_dp)
 {
     struct disk_dos_part_entry *dp;
 
-    if (prep_base_ebr(iter)) {
+    if (prep_base_ebr(iter) < 0) {
 	iter->status = PI_DONE;
 	return -1;
     }
 
-    while (++iter->index0 < 1024 && iter->sub.dos.nebr_lba) {
+    while (++iter->index0 < 1024 && iter->dos.nebr_lba) {
 	free(iter->data);
 	if (!(iter->data =
-		    disk_read_sectors(&iter->di, iter->sub.dos.nebr_lba, 1))) {
-	    error("Couldn't load EBR.\n");
+		    disk_read_sectors(&iter->di, iter->dos.nebr_lba, 1))) {
+	    error("Couldn't load EBR.");
 	    iter->status = PI_ERRLOAD;
 	    return -1;
 	}
 
+	/* check sanity of loaded data */
 	if (notsane_logical(iter) || notsane_extended(iter)) {
 	    iter->status = PI_INSANE;
 	    return -1;
@@ -416,24 +359,23 @@ static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
 
 	dp = ((struct disk_dos_mbr *)iter->data)->table;
 
-	iter->sub.dos.cebr_lba = iter->sub.dos.nebr_lba;
+	iter->dos.cebr_lba = iter->dos.nebr_lba;
+	iter->dos.cebr_siz = iter->dos.nebr_siz;
 
 	/* setup next frame values */
 	if (dp[1].ostype) {
-	    iter->sub.dos.ebr_start = dp[1].start_lba;
-	    iter->sub.dos.ebr_size = dp[1].length;
-	    iter->sub.dos.nebr_lba = iter->sub.dos.bebr_start + dp[1].start_lba;
+	    iter->dos.nebr_lba = iter->dos.bebr_lba + dp[1].start_lba;
+	    iter->dos.nebr_siz = dp[1].length;
 	} else {
-	    iter->sub.dos.ebr_start = 0;
-	    iter->sub.dos.ebr_size = 0;
-	    iter->sub.dos.nebr_lba = 0;
+	    iter->dos.nebr_lba = 0;
+	    iter->dos.nebr_siz = 0;
 	}
 
 	if (!dp[0].ostype)
-	    iter->sub.dos.skipcnt++;
+	    iter->dos.logskipcnt++;
 
-	if (dp[0].ostype || iter->stepall) {
-	    *lba = iter->sub.dos.cebr_lba + dp[0].start_lba;
+	if (dp[0].ostype || (iter->flags & PIF_STEPALL)) {
+	    *lba = dp[0].start_lba ? iter->dos.cebr_lba + dp[0].start_lba : 0;
 	    *_dp = dp;
 	    return 0;
 	}
@@ -441,44 +383,95 @@ static int pi_dos_next_ebr(struct part_iter *iter, uint32_t *lba,
 	 * This way it's possible to continue, if some crazy soft left a "hole"
 	 * - EBR with a valid extended partition without a logical one. In
 	 * such case, linux will not reserve a number for such hole - so we
-	 * don't increase index0. If stepall flag is set, we will never reach
-	 * this place.
+	 * don't increase index0. If PIF_STEPALL flag is set, we will never
+	 * reach this place.
 	 */
     }
     iter->status = PI_DONE;
     return -1;
 }
 
-static struct part_iter *pi_dos_next(struct part_iter *iter)
+static void gpt_conv_label(struct part_iter *iter)
+{
+    const struct disk_gpt_part_entry *gp;
+    const int16_t *orig_lab;
+
+    gp = (const struct disk_gpt_part_entry *)
+	(iter->data + iter->index0 * iter->gpt.pe_size);
+    orig_lab = (const int16_t *)gp->name;
+
+    /* caveat: this is very crude conversion */
+    for (int i = 0; i < PI_GPTLABSIZE/2; i++) {
+	iter->gpt.part_label[i] = (char)orig_lab[i];
+    }
+    iter->gpt.part_label[PI_GPTLABSIZE/2] = 0;
+}
+
+static inline int valid_crc(uint32_t crc, const uint8_t *buf, unsigned int siz)
+{
+    return crc == crc32(crc32(0, NULL, 0), buf, siz);
+}
+
+static int valid_crc_gpth(struct disk_gpt_header *gh, int flags)
+{
+    uint32_t crc, crcc;
+
+    if (!(flags & PIF_GPTHCRC))
+	return 1;
+
+    crc = gh->chksum;
+    gh->chksum = 0;
+    crcc = crc32(crc32(0, NULL, 0), (const uint8_t *)gh, gh->hdr_size);
+    gh->chksum = crc;
+    return crc == crcc;
+}
+
+static int valid_crc_gptl(const struct disk_gpt_header *gh, const struct disk_gpt_part_entry *gl, int flags)
+{
+    uint32_t crcc;
+
+    if (!(flags & PIF_GPTLCRC))
+	return 1;
+
+    crcc = crc32(crc32(0, NULL, 0), (const uint8_t *)gl, gh->part_size * gh->part_count);
+    return gh->table_chksum == crcc;
+}
+
+static int pi_next_(struct part_iter *iter)
 {
-    uint32_t start_lba = 0;
+    iter->status = PI_DONE;
+    return iter->status;
+}
+
+static int pi_dos_next(struct part_iter *iter)
+{
+    uint32_t abs_lba = 0;
     struct disk_dos_part_entry *dos_part = NULL;
 
     if (iter->status)
-	goto bail;
+	return iter->status;
 
     /* look for primary partitions */
     if (iter->index0 < 4 &&
-	    pi_dos_next_mbr(iter, &start_lba, &dos_part))
-	goto bail;
+	    dos_next_mbr(iter, &abs_lba, &dos_part) < 0)
+	return iter->status;
 
     /* look for logical partitions */
     if (iter->index0 >= 4 &&
-	    pi_dos_next_ebr(iter, &start_lba, &dos_part))
-	goto bail;
+	    dos_next_ebr(iter, &abs_lba, &dos_part) < 0)
+	return iter->status;
 
     /*
-     * note special index handling, if we have stepall set -
-     * this is made to keep index consistent with non-stepall
-     * iterators
+     * note special index handling:
+     * in case PIF_STEPALL is set - this makes the index consistent with
+     * non-PIF_STEPALL iterators
      */
 
-    if (iter->index0 >= 4 && !dos_part->ostype)
+    if (!dos_part->ostype)
 	iter->index = -1;
     else
-	iter->index = iter->index0 - iter->sub.dos.skipcnt + 1;
-    iter->rawindex = iter->index0 + 1;
-    iter->start_lba = start_lba;
+	iter->index = iter->index0 + 1 - iter->dos.logskipcnt;
+    iter->abs_lba = abs_lba;
     iter->length = dos_part->length;
     iter->record = (char *)dos_part;
 
@@ -486,314 +479,242 @@ static struct part_iter *pi_dos_next(struct part_iter *iter)
     disk_dos_part_dump(dos_part);
 #endif
 
-    return iter;
-bail:
-    return NULL;
+    return iter->status;
 }
 
-static void gpt_conv_label(struct part_iter *iter)
-{
-    const struct disk_gpt_part_entry *gp;
-    const int16_t *orig_lab;
-
-    gp = (const struct disk_gpt_part_entry *)
-	(iter->data + iter->index0 * iter->sub.gpt.pe_size);
-    orig_lab = (const int16_t *)gp->name;
-
-    /* caveat: this is very crude conversion */
-    for (int i = 0; i < PI_GPTLABSIZE/2; i++) {
-	iter->sub.gpt.part_label[i] = (char)orig_lab[i];
-    }
-    iter->sub.gpt.part_label[PI_GPTLABSIZE/2] = 0;
-}
-
-static struct part_iter *pi_gpt_next(struct part_iter *iter)
+static int pi_gpt_next(struct part_iter *iter)
 {
     const struct disk_gpt_part_entry *gpt_part = NULL;
 
     if (iter->status)
-	goto bail;
+	return iter->status;
 
-    while (++iter->index0 < iter->sub.gpt.pe_count) {
+    while (++iter->index0 < iter->gpt.pe_count) {
 	gpt_part = (const struct disk_gpt_part_entry *)
-	    (iter->data + iter->index0 * iter->sub.gpt.pe_size);
+	    (iter->data + iter->index0 * iter->gpt.pe_size);
 
 	if (notsane_gpt(iter)) {
 	    iter->status = PI_INSANE;
-	    goto bail;
+	    return iter->status;
 	}
 
-	if (!guid_is0(&gpt_part->type) || iter->stepall)
+	if (!guid_is0(&gpt_part->type) || (iter->flags & PIF_STEPALL))
 	    break;
     }
     /* no more partitions ? */
-    if (iter->index0 == iter->sub.gpt.pe_count) {
+    if (iter->index0 == iter->gpt.pe_count) {
 	iter->status = PI_DONE;
-	goto bail;
+	return iter->status;
     }
     /* gpt_part is guaranteed to be valid here */
     iter->index = iter->index0 + 1;
-    iter->rawindex = iter->index0 + 1;
-    iter->start_lba = gpt_part->lba_first;
+    iter->abs_lba = gpt_part->lba_first;
     iter->length = gpt_part->lba_last - gpt_part->lba_first + 1;
     iter->record = (char *)gpt_part;
-    memcpy(&iter->sub.gpt.part_guid, &gpt_part->uid, sizeof(struct guid));
+    memcpy(&iter->gpt.part_guid, &gpt_part->uid, sizeof(struct guid));
     gpt_conv_label(iter);
 
 #ifdef DEBUG
     disk_gpt_part_dump(gpt_part);
 #endif
 
-    return iter;
-bail:
-    return NULL;
+    return iter->status;
 }
 
-static struct part_iter *pi_raw_next(struct part_iter *iter)
+static struct part_iter *pi_alloc(void)
 {
-    iter->status = PI_DONE;
-    return NULL;
+    struct part_iter *iter;
+    if (!(iter = malloc(sizeof *iter)))
+	critm();
+    else
+	memset(iter, 0, sizeof *iter);
+    return iter;
 }
 
-static int check_crc(uint32_t crc_match, const uint8_t *buf, unsigned int siz)
+/* pi_del() - delete iterator */
+void pi_del(struct part_iter **_iter)
 {
-    uint32_t crc;
-
-    crc = crc32(0, NULL, 0);
-    crc = crc32(crc, buf, siz);
-
-    return crc_match != crc;
+    if(!_iter || !*_iter)
+	return;
+    pi_dtor(*_iter);
+    free(*_iter);
+    *_iter = NULL;
 }
 
-static int gpt_check_hdr_crc(const struct disk_info * const diskinfo, struct disk_gpt_header **_gh)
+static int notsane_gpt_hdr(const struct disk_info *di, const struct disk_gpt_header *gpth, int flags)
 {
-    struct disk_gpt_header *gh = *_gh;
-    uint64_t lba_alt;
-    uint32_t hold_crc32;
+    uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
+    uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
+    uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
+    uint64_t gpt_sec;	    /* secondary gpt header */
 
-    hold_crc32 = gh->chksum;
-    gh->chksum = 0;
-    if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
-	error("WARNING: Primary GPT header checksum invalid.\n");
-	/* retry with backup */
-	lba_alt = gh->lba_alt;
-	free(gh);
-	if (!(gh = *_gh = disk_read_sectors(diskinfo, lba_alt, 1))) {
-	    error("Couldn't read backup GPT header.\n");
-	    return -1;
-	}
-	hold_crc32 = gh->chksum;
-	gh->chksum = 0;
-	if (check_crc(hold_crc32, (const uint8_t *)gh, gh->hdr_size)) {
-	    error("Secondary GPT header checksum invalid.\n");
-	    return -1;
-	}
-    }
-    /* restore old checksum */
-    gh->chksum = hold_crc32;
+    if (!(flags & PIF_STRICT))
+	return 0;
 
-    return 0;
-}
+    if (gpth->lba_alt < gpth->lba_cur)
+	gpt_sec = gpth->lba_cur;
+    else
+	gpt_sec = gpth->lba_alt;
+    gpt_loff = gpth->lba_table;
+    gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
+    gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;
 
-/*
- * ----------------------------------------------------------------------------
- * Following functions are for users to call.
- * ----------------------------------------------------------------------------
- */
+    /*
+     * disk_read_sectors allows reading of max 255 sectors, so we use
+     * it as a sanity check base. EFI doesn't specify max (AFAIK).
+     */
+    if (gpt_loff < 2 || !gpt_lsiz || gpt_lcnt > 255u ||
+	    gpth->lba_first_usable > gpth->lba_last_usable ||
+	    !sane(gpt_loff, gpt_lcnt) ||
+	    (gpt_loff + gpt_lcnt > gpth->lba_first_usable && gpt_loff <= gpth->lba_last_usable) ||
+	     gpt_loff + gpt_lcnt > gpt_sec ||
+	    ((flags & PIF_STRICTER) && (gpt_sec >= di->lbacnt)) ||
+	    gpth->part_size < sizeof(struct disk_gpt_part_entry))
+	return -1;
 
+    return 0;
+}
 
-int pi_next(struct part_iter **_iter)
+static void try_gpt_we(const char *str, int sec)
 {
-    struct part_iter *iter;
-
-    if(!_iter || !*_iter)
-	return 0;
-    iter = *_iter;
-#ifdef DEBUG
-    if (inv_type(iter->type)) {
-	error("This is not a valid iterator.\n");
-	return 0;
-    }
-#endif
-    if ((iter = iter->type->next(iter))) {
-	*_iter = iter;
-    }
-    return (*_iter)->status;
+    if (sec)
+	error(str);
+    else
+	warn(str);
 }
 
-/**
- * pi_new() - get new iterator
- * @itertype:	iterator type
- * @...:	variable arguments passed to ctors
- *
- * Variable arguments depend on the type. Please see functions:
- * iter_gpt_ctor() and iter_dos_ctor() for details.
- **/
-struct part_iter *pi_new(const struct itertype *type, ...)
+static struct disk_gpt_header *try_gpt_hdr(const struct disk_info *di, int sec, int flags)
 {
-    int badctor = 0;
-    struct part_iter *iter = NULL;
-    va_list ap;
-
-    va_start(ap, type);
-
-#ifdef DEBUG
-    if (inv_type(type)) {
-	error("Unknown iterator requested.\n");
-	goto bail;
+    const char *desc = sec ? "backup" : "primary";
+    uint64_t gpt_cur = sec ? di->lbacnt - 1 : 1;
+    struct disk_gpt_header *gpth;
+    char errbuf[96];
+
+    gpth = disk_read_sectors(di, gpt_cur, 1);
+    if (!gpth) {
+	sprintf(errbuf, "Unable to read %s GPT header.", desc);
+	goto out;
     }
-#endif
-
-    if (!(iter = malloc(sizeof(struct part_iter)))) {
-	error("Couldn't allocate memory for the iterator.\n");
-	goto bail;
+    if(!valid_crc_gpth(gpth, flags)) {
+	sprintf(errbuf, "Invalid checksum of %s GPT header.", desc);
+	goto out;
     }
-
-    memset(iter, 0, sizeof(struct part_iter));
-    iter->type = type;
-
-    if (type->ctor(iter, &ap)) {
-	badctor = -1;
-	error("Cannot initialize the iterator.\n");
-	goto bail;
-    }
-
-bail:
-    va_end(ap);
-    if (badctor) {
-	free(iter);
-	iter = NULL;
+    if(notsane_gpt_hdr(di, gpth, flags)) {
+	sprintf(errbuf, "Checksum of %s GPT header is valid, but values fail sanity checks.", desc);
+	goto out;
     }
-    return iter;
+    return gpth;
+out:
+    try_gpt_we(errbuf, sec);
+    free(gpth);
+    return NULL;
 }
 
-/**
- * pi_del() - delete iterator
- * @iter:       iterator double pointer
- *
- **/
-
-void pi_del(struct part_iter **_iter)
+static struct disk_gpt_part_entry *try_gpt_list(const struct disk_info *di, const struct disk_gpt_header *gpth, int alt, int flags)
 {
-    struct part_iter *iter;
-
-    if(!_iter || !*_iter)
-	return;
-    iter = *_iter;
-
-#ifdef DEBUG
-    if (inv_type(iter->type)) {
-	error("This is not a valid iterator.\n");
-	return;
+    int pri = gpth->lba_cur < gpth->lba_alt;
+    const char *desc = alt ? "alternative" : "main";
+    struct disk_gpt_part_entry *gptl;
+    char errbuf[64];
+    uint32_t gpt_lcnt;	    /* size of GPT partition in sectors */
+    uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
+
+    gpt_lcnt = (gpth->part_size * gpth->part_count + di->bps - 1) / di->bps;
+    if (!alt) {
+	/* prefer header value for partition table if not asking for alternative */
+	gpt_loff = gpth->lba_table;
+    } else {
+	/* try to read alternative, we have to calculate its position */
+	if (!pri)
+	    gpt_loff = gpth->lba_alt + 1;
+	else
+	    gpt_loff = gpth->lba_alt - gpt_lcnt;
     }
-#endif
 
-    iter->type->dtor(iter);
-    free(iter);
-    *_iter = NULL;
+    gptl = disk_read_sectors(di, gpt_loff, gpt_lcnt);
+    if (!gptl) {
+	sprintf(errbuf, "Unable to read %s GPT partition list.", desc);
+	goto out;
+    }
+    if (!valid_crc_gptl(gpth, gptl, flags)) {
+	sprintf(errbuf, "Invalid checksum of %s GPT partition list.", desc);
+	goto out;
+    }
+    return gptl;
+out:
+    try_gpt_we(errbuf, alt);
+    free(gptl);
+    return NULL;
 }
 
-/**
- * pi_begin() - check disk, validate, and get proper iterator
- * @di:	    diskinfo struct pointer
- *
- * This function checks the disk for GPT or legacy partition table and allocates
- * an appropriate iterator.
- **/
-struct part_iter *pi_begin(const struct disk_info *di, int stepall)
+/* pi_begin() - validate and and get proper iterator for a disk described by di */
+struct part_iter *pi_begin(const struct disk_info *di, int flags)
 {
-    int setraw = 0;
-    struct part_iter *iter = NULL;
+    int isgpt = 0, ret = -1;
+    struct part_iter *iter;
     struct disk_dos_mbr *mbr = NULL;
     struct disk_gpt_header *gpth = NULL;
     struct disk_gpt_part_entry *gptl = NULL;
 
+    /* Preallocate iterator */
+    if (!(iter = pi_alloc()))
+	goto out;
+
     /* Read MBR */
     if (!(mbr = disk_read_sectors(di, 0, 1))) {
-	error("Couldn't read first disk sector.\n");
-	goto bail;
+	error("Unable to read the first disk sector.");
+	goto out;
     }
 
-    setraw = -1;
-
-    /* Check for MBR magic*/
+    /* Check for MBR magic */
     if (mbr->sig != disk_mbr_sig_magic) {
-	error("No MBR magic.\n");
-	goto bail;
+	warn("No MBR magic, treating disk as raw.");
+	/* looks like RAW */
+	ret = pi_ctor(iter, di, flags);
+	goto out;
     }
 
     /* Check for GPT protective MBR */
-    if (mbr->table[0].ostype == 0xEE) {
-	if (!(gpth = disk_read_sectors(di, 1, 1))) {
-	    error("Couldn't read potential GPT header.\n");
-	    goto bail;
-	}
+    for (size_t i = 0; i < 4; i++)
+	isgpt |= (mbr->table[i].ostype == 0xEE);
+    isgpt = isgpt && !(flags & PIF_PREFMBR);
+
+    /* Try to read GPT header */
+    if (isgpt) {
+	gpth = try_gpt_hdr(di, 0, flags);
+	if (!gpth)
+	    /*
+	     * this read might fail if bios reports different disk size (different vm/pc)
+	     * not much we can do here to avoid it
+	     */
+	    gpth = try_gpt_hdr(di, 1, flags);
+	if (!gpth)
+	    goto out;
     }
 
     if (gpth && gpth->rev.uint32 == 0x00010000 &&
-	    !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof(disk_gpt_sig_magic))) {
+	    !memcmp(gpth->sig, disk_gpt_sig_magic, sizeof gpth->sig)) {
 	/* looks like GPT v1.0 */
-	uint64_t gpt_loff;	    /* offset to GPT partition list in sectors */
-	uint64_t gpt_lsiz;	    /* size of GPT partition list in bytes */
-	uint64_t gpt_lcnt;	    /* size of GPT partition in sectors */
 #ifdef DEBUG
-	puts("Looks like a GPT v1.0 disk.");
+	dprintf("Looks like a GPT v1.0 disk.\n");
 	disk_gpt_header_dump(gpth);
 #endif
-	/* Verify checksum, fallback to backup, then bail if invalid */
-	if (gpt_check_hdr_crc(di, &gpth))
-	    goto bail;
-
-	gpt_loff = gpth->lba_table;
-	gpt_lsiz = (uint64_t)gpth->part_size * gpth->part_count;
-	gpt_lcnt = (gpt_lsiz + di->bps - 1) / di->bps;
-
-	/*
-	 * disk_read_sectors allows reading of max 255 sectors, so we use
-	 * it as a sanity check base. EFI doesn't specify max (AFAIK).
-	 * Apart from that, some extensive sanity checks.
-	 */
-	if (!gpt_loff || !gpt_lsiz || gpt_lcnt > 255u ||
-		gpth->lba_first_usable > gpth->lba_last_usable ||
-		!sane(gpt_loff, gpt_lcnt) ||
-		gpt_loff + gpt_lcnt > gpth->lba_first_usable ||
-		!sane(gpth->lba_last_usable, gpt_lcnt) ||
-		gpth->lba_last_usable + gpt_lcnt >= gpth->lba_alt ||
-		gpth->lba_alt >= di->lbacnt ||
-		gpth->part_size < sizeof(struct disk_gpt_part_entry)) {
-	    error("Invalid GPT header's values.\n");
-	    goto bail;
-	}
-	if (!(gptl = disk_read_sectors(di, gpt_loff, (uint8_t)gpt_lcnt))) {
-	    error("Couldn't read GPT partition list.\n");
-	    goto bail;
-	}
-	/* Check array checksum(s). */
-	if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
-	    error("WARNING: GPT partition list checksum invalid, trying backup.\n");
-	    free(gptl);
-	    /* secondary array directly precedes secondary header */
-	    if (!(gptl = disk_read_sectors(di, gpth->lba_alt - gpt_lcnt, (uint8_t)gpt_lcnt))) {
-		error("Couldn't read backup GPT partition list.\n");
-		goto bail;
-	    }
-	    if (check_crc(gpth->table_chksum, (const uint8_t *)gptl, (unsigned int)gpt_lsiz)) {
-		error("Backup GPT partition list checksum invalid.\n");
-		goto bail;
-	    }
-	}
-	/* allocate iterator and exit */
-	iter = pi_new(typegpt, di, stepall, gpth, gptl);
+	gptl = try_gpt_list(di, gpth, 0, flags);
+	if (!gptl)
+	    gptl = try_gpt_list(di, gpth, 1, flags);
+	if (!gptl)
+	    goto out;
+
+	/* looks like GPT */
+	ret = pi_gpt_ctor(iter, di, flags, gpth, gptl);
     } else {
 	/* looks like MBR */
-	iter = pi_new(typedos, di, stepall, mbr);
+	ret = pi_dos_ctor(iter, di, flags, mbr);
     }
-
-    setraw = 0;
-bail:
-    if (setraw) {
-	error("WARNING: treating disk as raw.\n");
-	iter = pi_new(typeraw, di, stepall);
+out:
+    if (ret < 0) {
+	free(iter);
+	iter = NULL;
     }
     free(mbr);
     free(gpth);
diff --git a/com32/chain/partiter.h b/com32/chain/partiter.h
index 7deeb53..686e23a 100644
--- a/com32/chain/partiter.h
+++ b/com32/chain/partiter.h
@@ -1,8 +1,9 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 2003-2010 H. Peter Anvin - All Rights Reserved
- *   Copyright 2010 Michal Soltys
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
  *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
  *
  *   Permission is hereby granted, free of charge, to any person
  *   obtaining a copy of this software and associated documentation
@@ -33,24 +34,26 @@
  * Provides disk / partition iteration.
  */
 
-#ifndef _COM32_CHAIN_PARTITER_H
-#define _COM32_CHAIN_PARTITER_H
+#ifndef COM32_CHAIN_PARTITER_H
+#define COM32_CHAIN_PARTITER_H
 
 #include <stdint.h>
 #include <syslinux/disk.h>
 
-#define PI_ERRLOAD 3
-#define PI_INSANE 2
-#define PI_DONE 1
-#define PI_OK 0
+/* status */
+
+enum {PI_ERRLOAD = -31, PI_INSANE, PI_OK = 0, PI_DONE};
+
+/* flags */
+
+enum {PIF_STEPALL = 1, PIF_PREFMBR = 2, PIF_STRICT = 4, PIF_STRICTER = 8, PIF_GPTHCRC = 16, PIF_GPTLCRC = 32};
 
 struct itertype;
 struct part_iter;
 
 struct itertype {
-	int (*ctor)(struct part_iter *, va_list *);
 	void (*dtor)(struct part_iter *);
-	struct part_iter *(*next) (struct part_iter *);
+	int  (*next)(struct part_iter *);
 };
 
 #define PI_GPTLABSIZE ((int)sizeof(((struct disk_gpt_part_entry *)0)->name))
@@ -59,29 +62,29 @@ struct part_iter {
     const struct itertype *type;
     char *data;
     char *record;
-    uint64_t start_lba;
+    uint64_t abs_lba;
     uint64_t length;
-    int index;
-    int rawindex;
+    int index0;	    /* including holes, from -1 (disk, then parts from 0) */
+    int index;	    /* excluding holes, from  0 (disk, then parts from 1), -1 means hole, if PIF_STEPALL is set */
+    int flags;	    /* flags, see #defines above */
+    int status;	    /* current status, see enums above */
     struct disk_info di;
-    int stepall;
-    int status;
-    /* internal */
-    int index0;
-    union _sub {
-	struct _dos {
-	    uint32_t disk_sig;
-	    uint32_t nebr_lba;
-	    uint32_t cebr_lba;
-	    /* internal */
-	    uint32_t ebr_start;
-	    uint32_t ebr_size;
-	    uint32_t bebr_start;
-	    uint32_t bebr_size;
-	    int bebr_index0;
-	    int skipcnt;
+    union {
+	struct {
+	    uint32_t disk_sig;	  /* 32bit disk signature as stored in MBR */
+
+	    uint32_t bebr_lba;	  /* absolute lba of base extended partition */
+	    uint32_t bebr_siz;	  /* size of base extended partition */
+
+	    uint32_t cebr_lba;	  /* absolute lba of curr ext. partition */
+	    uint32_t cebr_siz;	  /* size of curr ext. partition */
+	    uint32_t nebr_lba;	  /* absolute lba of next ext. partition */
+	    uint32_t nebr_siz;	  /* size of next ext. partition */
+
+	    int bebr_index0;	  /* index of (0-3) of base ext. part., -1 if not present in MBR */
+	    int logskipcnt;	  /* how many logical holes were skipped */
 	} dos;
-	struct _gpt {
+	struct {
 	    struct guid disk_guid;
 	    struct guid part_guid;
 	    char part_label[PI_GPTLABSIZE/2+1];
@@ -90,17 +93,26 @@ struct part_iter {
 	    uint64_t ufirst;
 	    uint64_t ulast;
 	} gpt;
-    } sub;
+    };
 };
 
 extern const struct itertype * const typedos;
 extern const struct itertype * const typegpt;
 extern const struct itertype * const typeraw;
 
-struct part_iter *pi_begin(const struct disk_info *, int stepall);
-struct part_iter *pi_new(const struct itertype *, ...);
+struct part_iter *pi_begin(const struct disk_info *, int flags);
 void pi_del(struct part_iter **);
-int pi_next(struct part_iter **);
+
+/* inline virtuals */
+static inline int pi_next(struct part_iter *iter)
+{
+    return iter->type->next(iter);
+}
+
+static inline void pi_dtor(struct part_iter *iter)
+{
+    iter->type->dtor(iter);
+}
 
 #endif
 
diff --git a/com32/chain/utility.c b/com32/chain/utility.c
index fb59551..79ecad6 100644
--- a/com32/chain/utility.c
+++ b/com32/chain/utility.c
@@ -1,3 +1,33 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
 #include <com32.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -16,18 +46,9 @@ static const char *bpbtypes[] = {
     [5] =  "4.0",
     [6] =  "8.0 (NT+)",
     [7] =  "7.0",
+    [8] =  "exFAT",
 };
 
-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;
@@ -46,7 +67,29 @@ void wait_key(void)
     } while (!cnt || (cnt < 0 && errno == EAGAIN));
 }
 
-void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode)
+int guid_is0(const struct guid *guid)
+{
+    return
+	!(guid->data1 ||
+	  guid->data2 ||
+	  guid->data3 ||
+	  guid->data4);
+}
+
+/*
+ * mode explanation:
+ *
+ * cnul - "strict" mode, never returning higher value than obtained from cbios
+ * cadd - if the disk is larger than reported geometry /and/ if the geometry has
+ *        less cylinders than 1024 - it means that the total size is somewhere
+ *        between cs and cs+1; in this particular case, we bump the cs to be able
+ *        to return matching chs triplet
+ * cmax - assume we can use any cylinder value
+ *
+ * by default cadd seems most reasonable, giving consistent results with e.g.
+ * sfdisk's behavior
+ */
+void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, int mode)
 {
     uint32_t c, h, s, t;
     uint32_t cs, hs, ss;
@@ -59,9 +102,10 @@ void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t m
 	cs = di->cyl;
 	hs = di->head;
 	ss = di->spt;
-	if (mode == l2c_cadd && cs < 1024 && di->lbacnt > cs*hs*ss)
-	    cs++;
-	else if (mode == l2c_cmax)
+	if (mode == L2C_CADD) {
+	    if (cs < 1024 && di->lbacnt > cs*hs*ss)
+		cs++;
+	} else if (mode == L2C_CMAX)
 	    cs = 1024;
     } else {
 	if (di->disk & 0x80) {
@@ -80,8 +124,8 @@ void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t m
 	h = hs - 1;
 	c = cs - 1;
     } else {
-	s = ((uint32_t)lba % ss) + 1;
-	t = (uint32_t)lba / ss;
+	s = (lba % ss) + 1;
+	t = lba / ss;
 	h = t % hs;
 	c = t / hs;
     }
@@ -133,14 +177,15 @@ uint32_t get_file_lba(const char *filename)
 }
 
 /* drive offset detection */
-int drvoff_detect(int type, unsigned int *off)
+int drvoff_detect(int type)
 {
     if (bpbV40 <= type && type <= bpbVNT) {
-	*off = 0x24;
+	return 0x24;
     } else if (type == bpbV70) {
-	*off = 0x40;
-    } else
-	return 0;
+	return 0x40;
+    } else if (type == bpbEXF) {
+	return 0x6F;
+    }
 
     return -1;
 }
@@ -152,6 +197,12 @@ int bpb_detect(const uint8_t *sec, const char *tag)
 {
     int a, b, c, jmp = -1, rev = 0;
 
+    /* exFAT mess first (media descriptor is 0 here) */
+    if (!memcmp(sec + 0x03, "EXFAT   ", 8)) {
+	rev = bpbEXF;
+	goto out;
+    }
+
     /* media descriptor check */
     if ((sec[0x15] & 0xF0) != 0xF0)
 	goto out;
diff --git a/com32/chain/utility.h b/com32/chain/utility.h
index 8a08be7..4b7853c 100644
--- a/com32/chain/utility.h
+++ b/com32/chain/utility.h
@@ -1,29 +1,74 @@
-#ifndef _COM32_CHAIN_UTILITY_H
-#define _COM32_CHAIN_UTILITY_H
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2010 Shao Miller
+ *   Copyright 2010+ Michal Soltys
+ *
+ *   Permission is hereby granted, free of charge, to any person
+ *   obtaining a copy of this software and associated documentation
+ *   files (the "Software"), to deal in the Software without
+ *   restriction, including without limitation the rights to use,
+ *   copy, modify, merge, publish, distribute, sublicense, and/or
+ *   sell copies of the Software, and to permit persons to whom
+ *   the Software is furnished to do so, subject to the following
+ *   conditions:
+ *
+ *   The above copyright notice and this permission notice shall
+ *   be included in all copies or substantial portions of the Software.
+ *
+ *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *   OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#ifndef COM32_CHAIN_UTILITY_H
+#define COM32_CHAIN_UTILITY_H
 
 #include <stdint.h>
+#include <stdio.h>
 #include <syslinux/disk.h>
+#include <syslinux/movebits.h>
 
-#define bpbUNK	0
-#define bpbV20	1
-#define bpbV30	2
-#define bpbV32	3
-#define bpbV34	4
-#define bpbV40	5
-#define bpbVNT	6
-#define bpbV70	7
+/* most (all ?) bpb "types" known to humankind as of 2012 */
+enum {bpbUNK, bpbV20, bpbV30, bpbV32, bpbV34, bpbV40, bpbVNT, bpbV70, bpbEXF};
 
-#define l2c_cnul 0
-#define l2c_cadd 1
-#define l2c_cmax 2
+/* see utility.c for details */
+enum {L2C_CNUL, L2C_CADD, L2C_CMAX};
+
+/* first usable and first unusable offsets */
+#define dosmin ((addr_t)0x500u)
+#define dosmax ((addr_t)(*(uint16_t *) 0x413 << 10))
 
-void error(const char *msg);
-int guid_is0(const struct guid *guid);
 void wait_key(void);
-void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, uint32_t mode);
+void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, int mode);
 uint32_t get_file_lba(const char *filename);
-int drvoff_detect(int type, unsigned int *off);
+int drvoff_detect(int type);
 int bpb_detect(const uint8_t *bpb, const char *tag);
+int guid_is0(const struct guid *guid);
+
+static inline int warn(const char *x)
+{
+    return fprintf(stderr, "WARN: %s\n", x);
+}
+
+static inline int error(const char *x)
+{
+    return fprintf(stderr, "ERR: %s\n", x);
+}
+
+static inline int crit(const char *x)
+{
+    return fprintf(stderr, "CRIT: %s @%s:%d\n", x, __FILE__, __LINE__);
+}
+
+#define critm()  crit("Malloc failure.")
 
 #endif
 
diff --git a/doc/chain.txt b/doc/chain.txt
index 6dd0632..d22a089 100644
--- a/doc/chain.txt
+++ b/doc/chain.txt
@@ -53,29 +53,27 @@ Module invocation:
 
 chain [drive/partition] [options]
 
+In case of repeated arguments, rightmost ones take precedence.
+
+
 			DRIVE / PARTITION SPECIFICATION
 
 Drive can be specified as 'hd#', 'fd#', 'boot', 'mbr', or 'guid'.
 
-- 'mbr' will select a drive by a signature.
-- 'guid' will select a drive by a guid
+- 'mbr' will select a drive by its signature.
+- 'guid' will select a drive by its guid (GPT only).
 - 'boot' is the drive syslinux was booted from. This is the default value, if
   nothing else is specified.
 - 'hd#' and 'fd#' are standard ways to specify drive number as seen by bios,
   starting from 0.
 
-Option 'guid' is shared with partition selection (see below). If you happened
+Option 'guid' is shared with partition selection (see below). If you happen
 to have non-unique guids, they are searched in disk0, partitions of disk0,
 disk1 ...  order.
 
-The priority of those options are the same as in the above list.
-
-If you specify the same value more than once, the last value will be used.
-
 'mbr' and 'guid' take extra parameter - you should use ':' or '=' as a
 delimiter.
 
-
 Partition can be specified as '#', 'guid', 'label' or 'fs'.
 
 - 'guid' option will select a partition by a guid (not a type guid !)
@@ -85,11 +83,10 @@ Partition can be specified as '#', 'guid', 'label' or 'fs'.
 - '#' is the standard method. Partitions 1-4 are primary, 5+ logical, 0 = boot
   MBR (default).
 
-The priority of those options are the same as in the above list.
-
 If you use a number to select a partition it should be specified after a drive
 using space or comma as delimiters (after 'hd#', 'fd#', 'mbr', 'guid' or 'boot').
 
+
 				    OPTIONS
 	file=<file>
        *nofile
@@ -110,11 +107,11 @@ This triplet lets you alter the addresses a file will use. It's loaded at
 other bootloader or kernel, it's almost always mandatory.
 
 The defaults, if option is not specified, are 0:0x7c00:0x7c00
-If any of the fields are ommited (e.g. 0x2000::), they default to 0.
+If any of the fields are omitted (e.g. 0x2000::), they default to 0.
 
 	sect=<segment>:<offset>:<ip>
-	nosect
 	*sect=0:0x7c00:0x7c00
+	nosect
 	nosect sets: nomaps
 
 This triplet lets you alter the addresses a sector will use. It's loaded at
@@ -126,7 +123,7 @@ expect relocated sector at some particular address (e.g. DRKM).
 is being chainloaded, sector is not necessary.
 
 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 some of the fields are omitted (e.g. 0x2000::), they default to 0.
 
 	*maps
 	nomaps
@@ -163,6 +160,7 @@ useful to also fix its BPB values.
 
 	save
 	*nosave
+	save sets: strict=2
 
 Fixing BPB values only in memory might not be enough. This option allows
 writing of the corrected sector. You will probably want to use this option
@@ -198,6 +196,7 @@ drive we use during chainloading is not fd0 or hd0.
 	hide[all]
 	unhide[all]
 	*nohide
+	[un]hide[all] sets: strict=2
 
 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
@@ -208,6 +207,7 @@ Writing is only performed, if the os type values actually changed.
 
 	fixchs
 	*nofixchs
+	fixchs sets: strict=2
 
 If you want to make a drive you're booting from totally compatible with current
 BIOS, you can use this to fix all partitions' CHS numbers. Good to silence e.g.
@@ -229,8 +229,43 @@ stacks in memory (pxelinux only).
 This option will wait for a keypress right before continuing the chainloading.
 Useful to see warnings emited by the chain module.
 
-	*nobreak
+	prefmbr
+	*noprefmbr
+
+In the case of presence of non-standard hybrid MBR/GPT layout, this flag makes
+chain module prefer MBR layout over GPT.
+
+	*gpthcrc
+	nogpthcrc
+
+GPT header contains its crc32 checksum. By default the partition iterator
+verifies it and aborts in case of mismatch.
+
+	*gptlcrc
+	nogptlcrc
+
+GPT header contains crc32 checksum of GPT partition list. By default the
+partition iterator verifies it and aborts in case of mismatch.
+
+	strict[=<0|1|2>]
+	*strict=1
+	relax
+
+Those options control the level of sanity checks used during the traversal of
+partition table(s). This is useful in buggy corner cases, when the disk size is
+reported differently across different computers or virtual machines (if it
+happens at all, the size usually differs by 1 sector). Normally the partition
+iterator would report an error and abort in such case. Another case scenario is
+disk corruption in some later EMBR partition.
+
+- strict=0 inhibits any checks
+- strict=1 enables checks, but ignores those that involve disk size
+- strict=2 enables all checks
+- relax and nostrict are equivalent to strict=0
+- norelax and strict are equivalent to strict=2
+
 	break
+	*nobreak
 	break sets: nofile nomaps nohand
 
 It is possible to trigger a "service-only" run - The chain module will do


More information about the Syslinux-commits mailing list