[syslinux:elflink] elflink: some change in com32/

syslinux-bot for Feng Tang feng.tang at intel.com
Thu Aug 12 21:04:00 PDT 2010


Commit-ID:  180fe1a0801a1fe9efa5c6bb0c6b7ac6b9ee98db
Gitweb:     http://syslinux.zytor.com/commit/180fe1a0801a1fe9efa5c6bb0c6b7ac6b9ee98db
Author:     Feng Tang <feng.tang at intel.com>
AuthorDate: Wed, 14 Jul 2010 11:16:22 +0800
Committer:  Feng Tang <feng.tang at intel.com>
CommitDate: Tue, 20 Jul 2010 11:10:04 +0800

elflink: some change in com32/

modified:   com32/MCONFIG
modified:   com32/Makefile
modified:   com32/mboot/map.c
modified:   com32/mboot/mboot.h
modified:   com32/menu/menu.h
modified:   com32/menu/menumain.c
modified:   com32/menu/readconfig.c
modified:   com32/modules/chain.c
modified:   com32/modules/config.c
modified:   com32/modules/elf.c
modified:   com32/modules/ifcpu.c
modified:   com32/modules/ifcpu64.c
new file:   com32/modules/ifplop.c
new file:   com32/modules/ls.c
modified:   com32/modules/pmload.c
new file:   com32/modules/pwd.c
modified:   com32/modules/sdi.c
new file:   com32/modules/whichsys.c


---
 com32/MCONFIG                 |    3 +-
 com32/Makefile                |    2 +-
 com32/mboot/map.c             |   33 +-
 com32/mboot/mboot.h           |    8 +-
 com32/menu/menu.h             |    2 +
 com32/menu/menumain.c         |    9 +-
 com32/menu/readconfig.c       |   26 +-
 com32/modules/chain.c         | 1239 ++++++++++++++++++++++++++++++++++-------
 com32/modules/config.c        |    7 +-
 com32/modules/elf.c           |    8 +-
 com32/modules/ifcpu.c         |   98 ++--
 com32/modules/ifcpu64.c       |    9 +-
 com32/modules/ifplop.c        |  165 ++++++
 com32/modules/{dir.c => ls.c} |    2 +-
 com32/modules/pmload.c        |    8 +-
 com32/modules/pwd.c           |   51 ++
 com32/modules/sdi.c           |    8 +-
 com32/modules/whichsys.c      |  128 +++++
 18 files changed, 1500 insertions(+), 306 deletions(-)

diff --git a/com32/MCONFIG b/com32/MCONFIG
index a2b5d0a..6216f0d 100644
--- a/com32/MCONFIG
+++ b/com32/MCONFIG
@@ -58,7 +58,8 @@ COM32LD	   = $(com32)/lib/elf32.ld
 LDFLAGS    = -m elf_i386 -shared --hash-style=gnu -T $(COM32LD)
 LIBGCC    := $(shell $(CC) $(GCCOPT) --print-libgcc)
 
-LNXCFLAGS  = -I$(com32)/libutil/include $(GCCWARN) -O -g -D_GNU_SOURCE
+LNXCFLAGS  = -I$(com32)/libutil/include $(GCCWARN) -O -g \
+	     -D_GNU_SOURCE -D_FORTIFY_SOURCE=0 -Wno-error
 LNXSFLAGS  = -g
 LNXLDFLAGS = -g
 
diff --git a/com32/Makefile b/com32/Makefile
index b247bce..de9ad28 100644
--- a/com32/Makefile
+++ b/com32/Makefile
@@ -1,4 +1,4 @@
 SUBDIRS = tools lib gpllib libutil modules mboot menu samples elflink elflink/modules rosh cmenu \
-	  hdt gfxboot sysdump
+	  hdt gfxboot sysdump lua/src
 all tidy dist clean spotless install:
 	set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
diff --git a/com32/mboot/map.c b/com32/mboot/map.c
index 267e50c..0a71d4c 100644
--- a/com32/mboot/map.c
+++ b/com32/mboot/map.c
@@ -1,7 +1,7 @@
 /* ----------------------------------------------------------------------- *
  *
  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
- *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
  *
  *   Permission is hereby granted, free of charge, to any person
  *   obtaining a copy of this software and associated documentation
@@ -107,15 +107,15 @@ struct multiboot_header *map_image(void *ptr, size_t len)
     Elf32_Ehdr *eh = ptr;
     Elf32_Phdr *ph;
     Elf32_Shdr *sh;
-    unsigned int i;
+    unsigned int i, mbh_offset;
     uint32_t bad_flags;
 
     /*
      * Search for the multiboot header...
      */
     mbh_len = 0;
-    for (i = 0; i < MULTIBOOT_SEARCH; i += 4) {
-	mbh = (struct multiboot_header *)((char *)ptr + i);
+    for (mbh_offset = 0; mbh_offset < MULTIBOOT_SEARCH; mbh_offset += 4) {
+	mbh = (struct multiboot_header *)((char *)ptr + mbh_offset);
 	if (mbh->magic != MULTIBOOT_MAGIC)
 	    continue;
 	if (mbh->magic + mbh->flags + mbh->checksum)
@@ -127,7 +127,7 @@ struct multiboot_header *map_image(void *ptr, size_t len)
 	else
 	    mbh_len = 12;
 
-	if (i + mbh_len > len)
+	if (mbh_offset + mbh_len > len)
 	    mbh_len = 0;	/* Invalid... */
 	else
 	    break;		/* Found something... */
@@ -169,7 +169,7 @@ struct multiboot_header *map_image(void *ptr, size_t len)
 
 	for (i = 0; i < eh->e_phnum; i++) {
 	    if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
-		/* 
+		/*
 		 * This loads at p_paddr, which matches Grub.  However, if
 		 * e_entry falls within the p_vaddr range of this PHDR, then
 		 * adjust it to match the p_paddr range... this is how Grub
@@ -272,12 +272,23 @@ struct multiboot_header *map_image(void *ptr, size_t len)
 	 */
 	char *data_ptr;
 	addr_t data_len, bss_len;
+	addr_t bss_addr;
 
 	regs.eip = mbh->entry_addr;
 
 	data_ptr = (char *)mbh - (mbh->header_addr - mbh->load_addr);
-	data_len = mbh->load_end_addr - mbh->load_addr;
-	bss_len = mbh->bss_end_addr - mbh->load_end_addr;
+
+	if (mbh->load_end_addr)
+	    data_len = mbh->load_end_addr - mbh->load_addr;
+	else
+	    data_len = len - mbh_offset + (mbh->header_addr - mbh->load_addr);
+
+	bss_addr = mbh->load_addr + data_len;
+
+	if (mbh->bss_end_addr)
+	    bss_len = mbh->bss_end_addr - mbh->load_end_addr;
+	else
+	    bss_len = 0;
 
 	if (syslinux_memmap_type(amap, mbh->load_addr, data_len + bss_len)
 	    != SMT_FREE) {
@@ -298,12 +309,12 @@ struct multiboot_header *map_image(void *ptr, size_t len)
 	    }
 	if (bss_len)
 	    if (syslinux_add_memmap
-		(&mmap, mbh->load_end_addr, bss_len, SMT_ZERO)) {
+		(&mmap, bss_addr, bss_len, SMT_ZERO)) {
 		error("Failed to map a.out bss\n");
 		return NULL;
 	    }
-	if (mbh->bss_end_addr > mboot_high_water_mark)
-	    mboot_high_water_mark = mbh->bss_end_addr;
+	if (bss_addr + bss_len > mboot_high_water_mark)
+	    mboot_high_water_mark = bss_addr + bss_len;
     } else {
 	error
 	    ("Invalid Multiboot image: neither ELF header nor a.out kludge found\n");
diff --git a/com32/mboot/mboot.h b/com32/mboot/mboot.h
index b646cd3..da6ca2f 100644
--- a/com32/mboot/mboot.h
+++ b/com32/mboot/mboot.h
@@ -34,6 +34,7 @@
 
 #ifndef MBOOT_H
 
+#include <dprintf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <inttypes.h>
@@ -55,13 +56,6 @@
 #include "mb_header.h"
 #include "mb_info.h"
 
-#define DEBUG 0
-#if DEBUG
-# define dprintf printf
-#else
-# define dprintf(f, ...) ((void)0)
-#endif
-
 static inline void error(const char *msg)
 {
     fputs(msg, stderr);
diff --git a/com32/menu/menu.h b/com32/menu/menu.h
index 63e1859..36c5669 100644
--- a/com32/menu/menu.h
+++ b/com32/menu/menu.h
@@ -49,6 +49,7 @@ enum menu_action {
     MA_QUIT,			/* Quit to CLI */
     MA_EXIT,			/* Exit to higher-level menu */
     MA_EXIT_UNRES,		/* Unresolved exit */
+    MA_HELP,			/* Show help text */
 };
 
 struct menu_entry {
@@ -58,6 +59,7 @@ struct menu_entry {
     const char *passwd;
     char *helptext;
     const char *cmdline;
+    const char *background;
     struct menu *submenu;
     struct menu_entry *next;	/* Linked list of all labels across menus */
     int entry;			/* Entry number inside menu */
diff --git a/com32/menu/menumain.c b/com32/menu/menumain.c
index b04c70b..06725f3 100644
--- a/com32/menu/menumain.c
+++ b/com32/menu/menumain.c
@@ -446,7 +446,7 @@ static const char *edit_cmdline(const char *input, int top)
     int key, len, prev_len, cursor;
     int redraw = 1;		/* We enter with the menu already drawn */
 
-    strncpy(cmdline, input, MAX_CMDLINE_LEN);
+    strlcpy(cmdline, input, MAX_CMDLINE_LEN);
     cmdline[MAX_CMDLINE_LEN - 1] = '\0';
 
     len = cursor = strlen(cmdline);
@@ -921,6 +921,13 @@ static const char *run_menu(void)
 		    clear = 1;
 		    draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
 		    break;
+		case MA_HELP:
+		    key = show_message_file(me->cmdline, me->background);
+		    /* If the exit was an F-key, display that help screen */
+		    show_fkey(key);
+		    done = 0;
+		    clear = 1;
+		    break;
 		default:
 		    done = 0;
 		    break;
diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c
index 5685e6f..8c16fda 100644
--- a/com32/menu/readconfig.c
+++ b/com32/menu/readconfig.c
@@ -343,7 +343,8 @@ static void record(struct menu *m, struct labeldata *ld, const char *append)
 	    if (ld->ipappend) {
 		ipappend = syslinux_ipappend_strings();
 		for (i = 0; i < ipappend->count; i++) {
-		    if ((ld->ipappend & (1U << i)) && ipappend->ptr[i])
+		    if ((ld->ipappend & (1U << i)) && ipappend->ptr[i] &&
+			ipappend->ptr[i][0])
 			ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
 		}
 	    }
@@ -372,6 +373,11 @@ static void record(struct menu *m, struct labeldata *ld, const char *append)
 	    me->submenu = ld->submenu;
 	    break;
 
+	case MA_HELP:
+	    me->cmdline = refstr_get(ld->kernel);
+	    me->background = refstr_get(ld->append);
+	    break;
+
 	default:
 	    break;
 	}
@@ -834,6 +840,24 @@ static void parse_config_file(FILE * f)
 		}
 	    } else if (looking_at(p, "start")) {
 		start_menu = m;
+	    } else if (looking_at(p, "help")) {
+		if (ld.label) {
+		    ld.action = MA_HELP;
+		    p = skipspace(p + 4);
+
+		    refstr_put(ld.kernel);
+		    ld.kernel = refdup_word(&p);
+
+		    if (ld.append) {
+			refstr_put(ld.append);
+			ld.append = NULL;
+		    }
+
+		    if (*p) {
+			p = skipspace(p);
+			ld.append = refdup_word(&p); /* Background */
+		    }
+		}
 	    } else if ((ep = looking_at(p, "resolution"))) {
 		int x, y;
 		x = strtoul(ep, &ep, 0);
diff --git a/com32/modules/chain.c b/com32/modules/chain.c
index ad746ee..48a83d2 100644
--- a/com32/modules/chain.c
+++ b/com32/modules/chain.c
@@ -2,6 +2,8 @@
  *
  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *   Significant portions copyright (C) 2010 Shao Miller
+ *					[partition iteration, GPT, "fs"]
  *
  *   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
@@ -16,65 +18,91 @@
  *
  * Chainload a hard disk (currently rather braindead.)
  *
- * Usage: chain hd<disk#> [<partition>] [options]
- *        chain fd<disk#> [options]
+ * Usage: chain [options]
+ *	  chain hd<disk#> [<partition>] [options]
+ *	  chain fd<disk#> [options]
  *	  chain mbr:<id> [<partition>] [options]
+ *	  chain guid:<guid> [<partition>] [options]
+ *	  chain label:<label> [<partition>] [options]
  *	  chain boot [<partition>] [options]
  *
- * ... e.g. "chain hd0 1" will boot the first partition on the first hard
+ * For example, "chain msdos=io.sys" will load DOS from the current Syslinux
+ * filesystem.  "chain hd0 1" will boot the first partition on the first hard
  * disk.
  *
+ * When none of the "hdX", "fdX", "mbr:", "guid:", "label:", "boot" or "fs"
+ * options are specified, the default behaviour is equivalent to "boot".
+ * "boot" means to use the current Syslinux drive, and you can also specify
+ * a partition.
  *
  * The mbr: syntax means search all the hard disks until one with a
  * specific MBR serial number (bytes 440-443) is found.
  *
  * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
  *
+ * "fs" will use the current Syslinux filesystem as the boot drive/partition.
+ * When booting from PXELINUX, you will most likely wish to specify a disk.
+ *
  * Options:
  *
- * file=<loader>:
- *	loads the file <loader> **from the SYSLINUX filesystem**
+ * file=<loader>
+ *	loads the file <loader> **from the Syslinux filesystem**
  *	instead of loading the boot sector.
  *
- * seg=<segment>:
+ * seg=<segment>
  *	loads at and jumps to <seg>:0000 instead of 0000:7C00.
  *
- * isolinux=<loader>:
+ * isolinux=<loader>
  *	chainload another version/build of the ISOLINUX bootloader and patch
  *	the loader with appropriate parameters in memory.
  *	This avoids the need for the -eltorito-alt-boot parameter of mkisofs,
  *	when you want more than one ISOLINUX per CD/DVD.
  *
- * ntldr=<loader>:
+ * ntldr=<loader>
  *	equivalent to seg=0x2000 file=<loader> sethidden,
- *      used with WinNT's loaders
+ *	used with WinNT's loaders
  *
- * cmldr=<loader>:
- *      used with Recovery Console of Windows NT/2K/XP.
- *      same as ntldr=<loader> & "cmdcons\0" written to
- *      the system name field in the bootsector
+ * cmldr=<loader>
+ *	used with Recovery Console of Windows NT/2K/XP.
+ *	same as ntldr=<loader> & "cmdcons\0" written to
+ *	the system name field in the bootsector
  *
- * freedos=<loader>:
+ * freedos=<loader>
  *	equivalent to seg=0x60 file=<loader> sethidden,
- *      used with FreeDOS kernel.sys.
+ *	used with FreeDOS' kernel.sys.
  *
  * msdos=<loader>
  * pcdos=<loader>
  *	equivalent to seg=0x70 file=<loader> sethidden,
- *      used with DOS' io.sys.
+ *	used with DOS' io.sys.
+ *
+ * grub=<loader>
+ *	same as seg=0x800 file=<loader> & jumping to seg 0x820,
+ *	used with GRUB Legacy stage2 files.
+ *
+ * grubcfg=<filename>
+ *	set an alternative config filename in stage2 of Grub Legacy,
+ *	only applicable in combination with "grub=<loader>".
+ *
+ * grldr=<loader>
+ *	pass the partition number to GRUB4DOS,
+ *	used with GRUB4DOS' grldr.
  *
- * swap:
+ * swap
  *	if the disk is not fd0/hd0, install a BIOS stub which swaps
  *	the drive numbers.
  *
- * hide:
+ * hide
  *	change type of primary partitions with IDs 01, 04, 06, 07,
  *	0b, 0c, or 0e to 1x, except for the selected partition, which
  *	is converted the other way.
  *
- * sethidden:
- *      update the "hidden sectors" (partition offset) field in a
- *      FAT/NTFS boot sector.
+ * sethidden
+ *	update the "hidden sectors" (partition offset) field in a
+ *	FAT/NTFS boot sector.
+ *
+ * keeppxe
+ *	keep the PXE and UNDI stacks in memory (PXELINUX only).
  */
 
 #include <com32.h>
@@ -85,6 +113,7 @@
 #include <console.h>
 #include <minmax.h>
 #include <stdbool.h>
+#include <dprintf.h>
 #include <syslinux/loadfile.h>
 #include <syslinux/bootrm.h>
 #include <syslinux/config.h>
@@ -98,6 +127,9 @@ static struct options {
     uint16_t seg;
     bool isolinux;
     bool cmldr;
+    bool grub;
+    bool grldr;
+    const char *grubcfg;
     bool swap;
     bool hide;
     bool sethidden;
@@ -199,18 +231,23 @@ struct ebios_dapa {
     uint64_t lba;
 };
 
-static void *read_sector(unsigned int lba)
+/* Read count sectors from drive, starting at lba.  Return a new buffer */
+static void *read_sectors(uint64_t lba, uint8_t count)
 {
     com32sys_t inreg;
     struct ebios_dapa *dapa = __com32.cs_bounce;
     void *buf = (char *)__com32.cs_bounce + SECTOR;
     void *data;
 
+    if (!count)
+	/* Silly */
+	return NULL;
+
     memset(&inreg, 0, sizeof inreg);
 
     if (disk_info.ebios) {
 	dapa->len = sizeof(*dapa);
-	dapa->count = 1;	/* 1 sector */
+	dapa->count = count;
 	dapa->off = OFFS(buf);
 	dapa->seg = SEG(buf);
 	dapa->lba = lba;
@@ -241,7 +278,8 @@ static void *read_sector(unsigned int lba)
 	if (s > 63 || h > 256 || c > 1023)
 	    return NULL;
 
-	inreg.eax.w[0] = 0x0201;	/* Read one sector */
+	inreg.eax.b[0] = count;
+	inreg.eax.b[1] = 0x02;	/* Read */
 	inreg.ecx.b[1] = c & 0xff;
 	inreg.ecx.b[0] = s + (c >> 6);
 	inreg.edx.b[1] = h;
@@ -253,9 +291,9 @@ static void *read_sector(unsigned int lba)
     if (int13_retry(&inreg, NULL))
 	return NULL;
 
-    data = malloc(SECTOR);
+    data = malloc(count * SECTOR);
     if (data)
-	memcpy(data, buf, SECTOR);
+	memcpy(data, buf, count * SECTOR);
     return data;
 }
 
@@ -324,7 +362,7 @@ static int write_verify_sector(unsigned int lba, const void *buf)
     rv = write_sector(lba, buf);
     if (rv)
 	return rv;		/* Write failure */
-    rb = read_sector(lba);
+    rb = read_sectors(lba, 1);
     if (!rb)
 	return -1;		/* Readback failure */
     rv = memcmp(buf, rb, SECTOR);
@@ -332,135 +370,694 @@ static int write_verify_sector(unsigned int lba, const void *buf)
     return rv ? -1 : 0;
 }
 
-/* Search for a specific drive, based on the MBR signature; bytes
-   440-443. */
+/*
+ * CHS (cylinder, head, sector) value extraction macros.
+ * Taken from WinVBlock.  Does not expand to an lvalue
+*/
+#define     chs_head(chs) chs[0]
+#define   chs_sector(chs) (chs[1] & 0x3F)
+#define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
+#define  chs_cyl_low(chs) ((uint16_t)chs[2])
+#define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
+typedef uint8_t chs[3];
+
+/* A DOS partition table entry */
+struct part_entry {
+    uint8_t active_flag;	/* 0x80 if "active" */
+    chs start;
+    uint8_t ostype;
+    chs end;
+    uint32_t start_lba;
+    uint32_t length;
+} __attribute__ ((packed));
+
+static void mbr_part_dump(const struct part_entry *part)
+{
+    (void)part;
+    dprintf("Partition status _____ : 0x%.2x\n"
+	    "Partition CHS start\n"
+	    "  Cylinder ___________ : 0x%.4x (%u)\n"
+	    "  Head _______________ : 0x%.2x (%u)\n"
+	    "  Sector _____________ : 0x%.2x (%u)\n"
+	    "Partition type _______ : 0x%.2x\n"
+	    "Partition CHS end\n"
+	    "  Cylinder ___________ : 0x%.4x (%u)\n"
+	    "  Head _______________ : 0x%.2x (%u)\n"
+	    "  Sector _____________ : 0x%.2x (%u)\n"
+	    "Partition LBA start __ : 0x%.8x (%u)\n"
+	    "Partition LBA count __ : 0x%.8x (%u)\n"
+	    "-------------------------------\n",
+	    part->active_flag,
+	    chs_cylinder(part->start),
+	    chs_cylinder(part->start),
+	    chs_head(part->start),
+	    chs_head(part->start),
+	    chs_sector(part->start),
+	    chs_sector(part->start),
+	    part->ostype,
+	    chs_cylinder(part->end),
+	    chs_cylinder(part->end),
+	    chs_head(part->end),
+	    chs_head(part->end),
+	    chs_sector(part->end),
+	    chs_sector(part->end),
+	    part->start_lba,
+	    part->start_lba,
+	    part->length,
+	    part->length);
+}
+
+/* A DOS MBR */
+struct mbr {
+    char code[440];
+    uint32_t disk_sig;
+    char pad[2];
+    struct part_entry table[4];
+    uint16_t sig;
+} __attribute__ ((packed));
+static const uint16_t mbr_sig_magic = 0xAA55;
+
+/* Search for a specific drive, based on the MBR signature; bytes 440-443 */
 static int find_disk(uint32_t mbr_sig)
 {
     int drive;
     bool is_me;
-    char *buf;
+    struct mbr *mbr;
 
     for (drive = 0x80; drive <= 0xff; drive++) {
 	if (get_disk_params(drive))
 	    continue;		/* Drive doesn't exist */
-	if (!(buf = read_sector(0)))
+	if (!(mbr = read_sectors(0, 1)))
 	    continue;		/* Cannot read sector */
-	is_me = (*(uint32_t *) ((char *)buf + 440) == mbr_sig);
-	free(buf);
+	is_me = (mbr->disk_sig == mbr_sig);
+	free(mbr);
 	if (is_me)
 	    return drive;
     }
     return -1;
 }
 
-/* A DOS partition table entry */
-struct part_entry {
-    uint8_t active_flag;	/* 0x80 if "active" */
-    uint8_t start_head;
-    uint8_t start_sect;
-    uint8_t start_cyl;
-    uint8_t ostype;
-    uint8_t end_head;
-    uint8_t end_sect;
-    uint8_t end_cyl;
-    uint32_t start_lba;
-    uint32_t length;
-} __attribute__ ((packed));
-
-/* Search for a logical partition.  Logical partitions are actually implemented
-   as recursive partition tables; theoretically they're supposed to form a
-   linked list, but other structures have been seen.
-
-   To make things extra confusing: data partition offsets are relative to where
-   the data partition record is stored, whereas extended partition offsets
-   are relative to the beginning of the extended partition all the way back
-   at the MBR... but still not absolute! */
+/* Forward declaration */
+struct disk_part_iter;
+
+/* Partition-/scheme-specific routine returning the next partition */
+typedef struct disk_part_iter *(*disk_part_iter_func) (struct disk_part_iter *
+						       part);
+
+/* Contains details for a partition under examination */
+struct disk_part_iter {
+    /* The block holding the table we are part of */
+    char *block;
+    /* The LBA for the beginning of data */
+    uint64_t lba_data;
+    /* The partition number, as determined by our heuristic */
+    int index;
+    /* The DOS partition record to pass, if applicable */
+    const struct part_entry *record;
+    /* Function returning the next available partition */
+    disk_part_iter_func next;
+    /* Partition-/scheme-specific details */
+    union {
+	/* MBR specifics */
+	int mbr_index;
+	/* EBR specifics */
+	struct {
+	    /* The first extended partition's start LBA */
+	    uint64_t lba_extended;
+	    /* Any applicable parent, or NULL */
+	    struct disk_part_iter *parent;
+	    /* The parent extended partition index */
+	    int parent_index;
+	} ebr;
+	/* GPT specifics */
+	struct {
+	    /* Real (not effective) index in the partition table */
+	    int index;
+	    /* Current partition GUID */
+	    const struct guid *part_guid;
+	    /* Current partition label */
+	    const char *part_label;
+	    /* Count of entries in GPT */
+	    int parts;
+	    /* Partition record size */
+	    uint32_t size;
+	} gpt;
+    } private;
+};
 
-int nextpart;			/* Number of the next logical partition */
+static struct disk_part_iter *next_ebr_part(struct disk_part_iter *part)
+{
+    const struct part_entry *ebr_table;
+    const struct part_entry *parent_table =
+	((const struct mbr *)part->private.ebr.parent->block)->table;
+    static const struct part_entry phony = {.start_lba = 0 };
+    uint64_t ebr_lba;
+
+    /* Don't look for a "next EBR" the first time around */
+    if (part->private.ebr.parent_index >= 0)
+	/* Look at the linked list */
+	ebr_table = ((const struct mbr *)part->block)->table + 1;
+    /* Do we need to look for an extended partition? */
+    if (part->private.ebr.parent_index < 0 || !ebr_table->start_lba) {
+	/* Start looking for an extended partition in the MBR */
+	while (++part->private.ebr.parent_index < 4) {
+	    uint8_t type = parent_table[part->private.ebr.parent_index].ostype;
+
+	    if ((type == 0x05) || (type == 0x0F) || (type == 0x85))
+		break;
+	}
+	if (part->private.ebr.parent_index == 4)
+	    /* No extended partitions found */
+	    goto out_finished;
+	part->private.ebr.lba_extended =
+	    parent_table[part->private.ebr.parent_index].start_lba;
+	ebr_table = &phony;
+    }
+    /* Load next EBR */
+    ebr_lba = ebr_table->start_lba + part->private.ebr.lba_extended;
+    free(part->block);
+    part->block = read_sectors(ebr_lba, 1);
+    if (!part->block) {
+	error("Could not load EBR!\n");
+	goto err_ebr;
+    }
+    ebr_table = ((const struct mbr *)part->block)->table;
+    dprintf("next_ebr_part:\n");
+    mbr_part_dump(ebr_table);
+
+    /*
+     * Sanity check entry: must not extend outside the
+     * extended partition.  This is necessary since some OSes
+     * put crap in some entries.
+     */
+    {
+	const struct mbr *mbr =
+	    (const struct mbr *)part->private.ebr.parent->block;
+	const struct part_entry *extended =
+	    mbr->table + part->private.ebr.parent_index;
+
+	if (ebr_table[0].start_lba >= extended->start_lba + extended->length) {
+	    dprintf("Insane logical partition!\n");
+	    goto err_insane;
+	}
+    }
+    /* Success */
+    part->lba_data = ebr_table[0].start_lba + ebr_lba;
+    dprintf("Partition %d logical lba %u\n", part->index, part->lba_data);
+    part->index++;
+    part->record = ebr_table;
+    return part;
+
+err_insane:
+
+    free(part->block);
+    part->block = NULL;
+err_ebr:
+
+out_finished:
+    free(part->private.ebr.parent->block);
+    free(part->private.ebr.parent);
+    free(part->block);
+    free(part);
+    return NULL;
+}
 
-static struct part_entry *find_logical_partition(int whichpart, char *table,
-						 struct part_entry *self,
-						 struct part_entry *root)
+static struct disk_part_iter *next_mbr_part(struct disk_part_iter *part)
 {
-    static struct part_entry ltab_entry;
-    struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
-    struct part_entry *found;
-    char *sector;
+    struct disk_part_iter *ebr_part;
+    /* Look at the partition table */
+    struct part_entry *table = ((struct mbr *)part->block)->table;
 
-    int i;
+    /* Look for data partitions */
+    while (++part->private.mbr_index < 4) {
+	uint8_t type = table[part->private.mbr_index].ostype;
 
-    if (*(uint16_t *) (table + 0x1fe) != 0xaa55)
-	return NULL;		/* Signature missing */
+	if (type == 0x00 || type == 0x05 || type == 0x0F || type == 0x85)
+	    /* Skip empty or extended partitions */
+	    continue;
+	if (!table[part->private.mbr_index].length)
+	    /* Empty */
+	    continue;
+	break;
+    }
+    /* If we're currently the last partition, it's time for EBR processing */
+    if (part->private.mbr_index == 4) {
+	/* Allocate another iterator for extended partitions */
+	ebr_part = malloc(sizeof(*ebr_part));
+	if (!ebr_part) {
+	    error("Could not allocate extended partition iterator!\n");
+	    goto err_alloc;
+	}
+	/* Setup EBR iterator parameters */
+	ebr_part->block = NULL;
+	ebr_part->index = 4;
+	ebr_part->record = NULL;
+	ebr_part->next = next_ebr_part;
+	ebr_part->private.ebr.parent = part;
+	/* Trigger an initial EBR load */
+	ebr_part->private.ebr.parent_index = -1;
+	/* The EBR iterator is responsible for freeing us */
+	return next_ebr_part(ebr_part);
+    }
+    dprintf("next_mbr_part:\n");
+    mbr_part_dump(table + part->private.mbr_index);
 
-    /* We are assumed to already having enumerated all the data partitions
-       in this table if this is the MBR.  For MBR, self == NULL. */
+    /* Update parameters to reflect this new partition.  Re-use iterator */
+    part->lba_data = table[part->private.mbr_index].start_lba;
+    dprintf("Partition %d primary lba %u\n", part->index, part->lba_data);
+    part->index++;
+    part->record = table + part->private.mbr_index;
+    return part;
 
-    if (self) {
-	/* Scan the data partitions. */
+    free(ebr_part);
+err_alloc:
 
-	for (i = 0; i < 4; i++) {
-	    if (ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
-		ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85)
-		continue;	/* Skip empty or extended partitions */
+    free(part->block);
+    free(part);
+    return NULL;
+}
 
-	    if (!ptab[i].length)
-		continue;
+/*
+ * GUID
+ * Be careful with endianness, you must adjust it yourself
+ * iff you are directly using the fourth data chunk
+ */
+struct guid {
+    uint32_t data1;
+    uint16_t data2;
+    uint16_t data3;
+    uint64_t data4;
+} __attribute__ ((packed));
 
-	    /* Adjust the offset to account for the extended partition itself */
-	    ptab[i].start_lba += self->start_lba;
+    /*
+     * This walk-map effectively reverses the little-endian
+     * portions of the GUID in the output text
+     */
+static const char guid_le_walk_map[] = {
+    3, -1, -1, -1, 0,
+    5, -1, 0,
+    3, -1, 0,
+    2, 1, 0,
+    1, 1, 1, 1, 1, 1
+};
 
-	    /*
-	     * Sanity check entry: must not extend outside the
-	     * extended partition.  This is necessary since some OSes
-	     * put crap in some entries.  Note that root is non-NULL here.
-	     */
-	    if (ptab[i].start_lba + ptab[i].length <= root->start_lba ||
-		ptab[i].start_lba >= root->start_lba + root->length)
-		continue;
-
-	    /* OK, it's a data partition.  Is it the one we're looking for? */
-	    if (nextpart++ == whichpart) {
-		memcpy(&ltab_entry, &ptab[i], sizeof ltab_entry);
-		return &ltab_entry;
+#if DEBUG
+/*
+ * Fill a buffer with a textual GUID representation.
+ * The buffer must be >= char[37] and will be populated
+ * with an ASCII NUL C string terminator.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian:  LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+static void guid_to_str(char *buf, const struct guid *id)
+{
+    unsigned int i = 0;
+    const char *walker = (const char *)id;
+
+    while (i < sizeof(guid_le_walk_map)) {
+	walker += guid_le_walk_map[i];
+	if (!guid_le_walk_map[i])
+	    *buf = '-';
+	else {
+	    *buf = ((*walker & 0xF0) >> 4) + '0';
+	    if (*buf > '9')
+		*buf += 'A' - '9' - 1;
+	    buf++;
+	    *buf = (*walker & 0x0F) + '0';
+	    if (*buf > '9')
+		*buf += 'A' - '9' - 1;
+	}
+	buf++;
+	i++;
+    }
+    *buf = 0;
+}
+#endif
+
+/*
+ * Create a GUID structure from a textual GUID representation.
+ * The input buffer must be >= 32 hexadecimal chars and be
+ * terminated with an ASCII NUL.  Returns non-zero on failure.
+ * Example: 11111111-2222-3333-4444-444444444444
+ * Endian:  LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB
+ */
+static int str_to_guid(const char *buf, struct guid *id)
+{
+    char guid_seq[sizeof(struct guid) * 2];
+    unsigned int i = 0;
+    char *walker = (char *)id;
+
+    while (*buf && i < sizeof(guid_seq)) {
+	switch (*buf) {
+	    /* Skip these three characters */
+	case '{':
+	case '}':
+	case '-':
+	    break;
+	default:
+	    /* Copy something useful to the temp. sequence */
+	    if ((*buf >= '0') && (*buf <= '9'))
+		guid_seq[i] = *buf - '0';
+	    else if ((*buf >= 'A') && (*buf <= 'F'))
+		guid_seq[i] = *buf - 'A' + 10;
+	    else if ((*buf >= 'a') && (*buf <= 'f'))
+		guid_seq[i] = *buf - 'a' + 10;
+	    else {
+		/* Or not */
+		error("Illegal character in GUID!\n");
+		return -1;
 	    }
+	    i++;
 	}
+	buf++;
+    }
+    /* Check for insufficient valid characters */
+    if (i < sizeof(guid_seq)) {
+	error("Too few GUID characters!\n");
+	return -1;
+    }
+    buf = guid_seq;
+    i = 0;
+    while (i < sizeof(guid_le_walk_map)) {
+	if (!guid_le_walk_map[i])
+	    i++;
+	walker += guid_le_walk_map[i];
+	*walker = *buf << 4;
+	buf++;
+	*walker |= *buf;
+	buf++;
+	i++;
     }
+    return 0;
+}
+
+/* A GPT partition */
+struct gpt_part {
+    struct guid type;
+    struct guid uid;
+    uint64_t lba_first;
+    uint64_t lba_last;
+    uint64_t attribs;
+    char name[72];
+} __attribute__ ((packed));
+
+static void gpt_part_dump(const struct gpt_part *gpt_part)
+{
+#ifdef DEBUG
+    unsigned int i;
+    char guid_text[37];
+
+    dprintf("----------------------------------\n"
+	    "GPT part. LBA first __ : 0x%.16llx\n"
+	    "GPT part. LBA last ___ : 0x%.16llx\n"
+	    "GPT part. attribs ____ : 0x%.16llx\n"
+	    "GPT part. name _______ : '",
+	    gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs);
+    for (i = 0; i < sizeof(gpt_part->name); i++) {
+	if (gpt_part->name[i])
+	    dprintf("%c", gpt_part->name[i]);
+    }
+    dprintf("'");
+    guid_to_str(guid_text, &gpt_part->type);
+    dprintf("GPT part. type GUID __ : {%s}\n", guid_text);
+    guid_to_str(guid_text, &gpt_part->uid);
+    dprintf("GPT part. unique ID __ : {%s}\n", guid_text);
+#endif
+    (void)gpt_part;
+}
 
-    /* Scan the extended partitions. */
-    for (i = 0; i < 4; i++) {
-	if (ptab[i].ostype != 0x05 &&
-	    ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85)
-	    continue;		/* Skip empty or data partitions */
+/* A GPT header */
+struct gpt {
+    char sig[8];
+    union {
+	struct {
+	    uint16_t minor;
+	    uint16_t major;
+	} fields __attribute__ ((packed));
+	uint32_t uint32;
+	char raw[4];
+    } rev __attribute__ ((packed));
+    uint32_t hdr_size;
+    uint32_t chksum;
+    char reserved1[4];
+    uint64_t lba_cur;
+    uint64_t lba_alt;
+    uint64_t lba_first_usable;
+    uint64_t lba_last_usable;
+    struct guid disk_guid;
+    uint64_t lba_table;
+    uint32_t part_count;
+    uint32_t part_size;
+    uint32_t table_chksum;
+    char reserved2[1];
+} __attribute__ ((packed));
+static const char gpt_sig_magic[] = "EFI PART";
 
-	if (!ptab[i].length)
+#if DEBUG
+static void gpt_dump(const struct gpt *gpt)
+{
+    char guid_text[37];
+
+    printf("GPT sig ______________ : '%8.8s'\n"
+	   "GPT major revision ___ : 0x%.4x\n"
+	   "GPT minor revision ___ : 0x%.4x\n"
+	   "GPT header size ______ : 0x%.8x\n"
+	   "GPT header checksum __ : 0x%.8x\n"
+	   "GPT reserved _________ : '%4.4s'\n"
+	   "GPT LBA current ______ : 0x%.16llx\n"
+	   "GPT LBA alternative __ : 0x%.16llx\n"
+	   "GPT LBA first usable _ : 0x%.16llx\n"
+	   "GPT LBA last usable __ : 0x%.16llx\n"
+	   "GPT LBA part. table __ : 0x%.16llx\n"
+	   "GPT partition count __ : 0x%.8x\n"
+	   "GPT partition size ___ : 0x%.8x\n"
+	   "GPT part. table chksum : 0x%.8x\n",
+	   gpt->sig,
+	   gpt->rev.fields.major,
+	   gpt->rev.fields.minor,
+	   gpt->hdr_size,
+	   gpt->chksum,
+	   gpt->reserved1,
+	   gpt->lba_cur,
+	   gpt->lba_alt,
+	   gpt->lba_first_usable,
+	   gpt->lba_last_usable,
+	   gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum);
+    guid_to_str(guid_text, &gpt->disk_guid);
+    printf("GPT disk GUID ________ : {%s}\n", guid_text);
+}
+#endif
+
+static struct disk_part_iter *next_gpt_part(struct disk_part_iter *part)
+{
+    const struct gpt_part *gpt_part = NULL;
+
+    while (++part->private.gpt.index < part->private.gpt.parts) {
+	gpt_part =
+	    (const struct gpt_part *)(part->block +
+				      (part->private.gpt.index *
+				       part->private.gpt.size));
+	if (!gpt_part->lba_first)
 	    continue;
+	break;
+    }
+    /* Were we the last partition? */
+    if (part->private.gpt.index == part->private.gpt.parts) {
+	goto err_last;
+    }
+    part->lba_data = gpt_part->lba_first;
+    part->private.gpt.part_guid = &gpt_part->uid;
+    part->private.gpt.part_label = gpt_part->name;
+    /* Update our index */
+    part->index++;
+    gpt_part_dump(gpt_part);
 
-	/* Adjust the offset to account for the extended partition itself */
-	if (root)
-	    ptab[i].start_lba += root->start_lba;
+    /* In a GPT scheme, we re-use the iterator */
+    return part;
 
-	/* Sanity check entry: must not extend outside the extended partition.
-	   This is necessary since some OSes put crap in some entries. */
-	if (root)
-	    if (ptab[i].start_lba + ptab[i].length <= root->start_lba ||
-		ptab[i].start_lba >= root->start_lba + root->length)
-		continue;
+err_last:
+    free(part->block);
+    free(part);
 
-	/* Process this partition */
-	if (!(sector = read_sector(ptab[i].start_lba)))
-	    continue;		/* Read error, must be invalid */
+    return NULL;
+}
 
-	found = find_logical_partition(whichpart, sector, &ptab[i],
-				       root ? root : &ptab[i]);
-	free(sector);
-	if (found)
-	    return found;
+static struct disk_part_iter *get_first_partition(struct disk_part_iter *part)
+{
+    const struct gpt *gpt_candidate;
+
+    /*
+     * Ignore any passed partition iterator.  The caller should
+     * have passed NULL.  Allocate a new partition iterator
+     */
+    part = malloc(sizeof(*part));
+    if (!part) {
+	error("Count not allocate partition iterator!\n");
+	goto err_alloc_iter;
+    }
+    /* Read MBR */
+    part->block = read_sectors(0, 2);
+    if (!part->block) {
+	error("Could not read two sectors!\n");
+	goto err_read_mbr;
     }
+    /* Check for an MBR */
+    if (((struct mbr *)part->block)->sig != mbr_sig_magic) {
+	error("No MBR magic!\n");
+	goto err_mbr;
+    }
+    /* Establish a pseudo-partition for the MBR (index 0) */
+    part->index = 0;
+    part->record = NULL;
+    part->private.mbr_index = -1;
+    part->next = next_mbr_part;
+    /* Check for a GPT disk */
+    gpt_candidate = (const struct gpt *)(part->block + SECTOR);
+    if (!memcmp(gpt_candidate->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
+	/* LBA for partition table */
+	uint64_t lba_table;
+
+	/* It looks like one */
+	/* TODO: Check checksum.  Possibly try alternative GPT */
+#if DEBUG
+	puts("Looks like a GPT disk.");
+	gpt_dump(gpt_candidate);
+#endif
+	/* TODO: Check table checksum (maybe) */
+	/* Note relevant GPT details */
+	part->next = next_gpt_part;
+	part->private.gpt.index = -1;
+	part->private.gpt.parts = gpt_candidate->part_count;
+	part->private.gpt.size = gpt_candidate->part_size;
+	lba_table = gpt_candidate->lba_table;
+	gpt_candidate = NULL;
+	/* Load the partition table */
+	free(part->block);
+	part->block =
+	    read_sectors(lba_table,
+			 ((part->private.gpt.size * part->private.gpt.parts) +
+			  SECTOR - 1) / SECTOR);
+	if (!part->block) {
+	    error("Could not read GPT partition list!\n");
+	    goto err_gpt_table;
+	}
+    }
+    /* Return the pseudo-partition's next partition, which is real */
+    return part->next(part);
+
+err_gpt_table:
+
+err_mbr:
+
+    free(part->block);
+    part->block = NULL;
+err_read_mbr:
+
+    free(part);
+err_alloc_iter:
 
-    /* If we get here, there ain't nothing... */
     return NULL;
 }
 
+/*
+ * Search for a specific drive/partition, based on the GPT GUID.
+ * We return the disk drive number if found, as well as populating the
+ * boot_part pointer with the matching partition, if applicable.
+ * If no matching partition is found or the GUID is a disk GUID,
+ * boot_part will be populated with NULL.  If not matching disk is
+ * found, we return -1.
+ */
+static int find_by_guid(const struct guid *gpt_guid,
+			struct disk_part_iter **boot_part)
+{
+    int drive;
+    bool is_me;
+    struct gpt *header;
+
+    for (drive = 0x80; drive <= 0xff; drive++) {
+	if (get_disk_params(drive))
+	    continue;		/* Drive doesn't exist */
+	if (!(header = read_sectors(1, 1)))
+	    continue;		/* Cannot read sector */
+	if (memcmp(&header->sig, gpt_sig_magic, sizeof(gpt_sig_magic))) {
+	    /* Not a GPT disk */
+	    free(header);
+	    continue;
+	}
+#if DEBUG
+	gpt_dump(header);
+#endif
+	is_me = !memcmp(&header->disk_guid, &gpt_guid, sizeof(*gpt_guid));
+	free(header);
+	if (!is_me) {
+	    /* Check for a matching partition */
+	    boot_part[0] = get_first_partition(NULL);
+	    while (boot_part[0]) {
+		is_me =
+		    !memcmp(boot_part[0]->private.gpt.part_guid, gpt_guid,
+			    sizeof(*gpt_guid));
+		if (is_me)
+		    break;
+		boot_part[0] = boot_part[0]->next(boot_part[0]);
+	    }
+	} else
+	    boot_part[0] = NULL;
+	if (is_me)
+	    return drive;
+    }
+    return -1;
+}
+
+/*
+ * Search for a specific partition, based on the GPT label.
+ * We return the disk drive number if found, as well as populating the
+ * boot_part pointer with the matching partition, if applicable.
+ * If no matching partition is found, boot_part will be populated with
+ * NULL and we return -1.
+ */
+static int find_by_label(const char *label, struct disk_part_iter **boot_part)
+{
+    int drive;
+    bool is_me;
+
+    for (drive = 0x80; drive <= 0xff; drive++) {
+	if (get_disk_params(drive))
+	    continue;		/* Drive doesn't exist */
+	/* Check for a GPT disk */
+	boot_part[0] = get_first_partition(NULL);
+	if (!(boot_part[0]->next == next_gpt_part)) {
+	    /* Not a GPT disk */
+	    while (boot_part[0]) {
+		/* Run through until the end */
+		boot_part[0] = boot_part[0]->next(boot_part[0]);
+	    }
+	    continue;
+	}
+	/* Check for a matching partition */
+	while (boot_part[0]) {
+	    char gpt_label[sizeof(((struct gpt_part *) NULL)->name)];
+	    const char *gpt_label_scanner =
+		boot_part[0]->private.gpt.part_label;
+	    int j = 0;
+
+	    /* Re-write the GPT partition label as ASCII */
+	    while (gpt_label_scanner <
+		   boot_part[0]->private.gpt.part_label + sizeof(gpt_label)) {
+		if ((gpt_label[j] = *gpt_label_scanner))
+		    j++;
+		gpt_label_scanner++;
+	    }
+	    if ((is_me = !strcmp(label, gpt_label)))
+		break;
+	    boot_part[0] = boot_part[0]->next(boot_part[0]);
+	}
+	if (is_me)
+	    return drive;
+    }
+
+    return -1;
+}
+
 static void do_boot(struct data_area *data, int ndata,
 		    struct syslinux_rm_regs *regs)
 {
@@ -490,7 +1087,7 @@ static void do_boot(struct data_area *data, int ndata,
 
     for (i = 0; i < ndata; i++) {
 	if (syslinux_add_movelist(&mlist, data[i].base,
-				  (addr_t)data[i].data, data[i].size))
+				  (addr_t) data[i].data, data[i].size))
 	    goto enomem;
     }
 
@@ -582,16 +1179,16 @@ static void do_boot(struct data_area *data, int ndata,
     error("Chainboot failed!\n");
     return;
 
-  too_big:
+too_big:
     error("Loader file too large\n");
     return;
 
-  enomem:
+enomem:
     error("Out of memory\n");
     return;
 }
 
-static int hide_unhide(char *mbr, int part)
+static int hide_unhide(struct mbr *mbr, int part)
 {
     int i;
     struct part_entry *pt;
@@ -604,7 +1201,7 @@ static int hide_unhide(char *mbr, int part)
     bool write_back = false;
 
     for (i = 1; i <= 4; i++) {
-	pt = (struct part_entry *)&mbr[0x1be + 16 * (i - 1)];
+	pt = mbr->table + i - 1;
 	t = pt->ostype;
 	if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
 	    /* It's a hideable partition type */
@@ -668,34 +1265,46 @@ static uint32_t get_file_lba(const char *filename)
 
 static void usage(void)
 {
-    error("Usage:   chain.c32 hd<disk#> [<partition>] [options]\n"
-	  "         chain.c32 fd<disk#> [options]\n"
-	  "         chain.c32 mbr:<id> [<partition>] [options]\n"
-	  "         chain.c32 boot [<partition>] [options]\n"
-	  "Options: file=<loader>      load file, instead of boot sector\n"
-	  "         isolinux=<loader>  load another version of ISOLINUX\n"
-	  "         ntldr=<loader>     load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n"
-	  "         cmldr=<loader>     load Recovery Console of Windows NT/2K/XP\n"
-	  "         freedos=<loader>   load FreeDOS kernel.sys\n"
-	  "         msdos=<loader>     load MS-DOS io.sys\n"
-	  "         pcdos=<loader>     load PC-DOS ibmbio.com\n"
-	  "         seg=<segment>      jump to <seg>:0000 instead of 0000:7C00\n"
-	  "         swap               swap drive numbers, if bootdisk is not fd0/hd0\n"
-	  "         hide               hide primary partitions, except selected partition\n"
-	  "         sethidden          set the FAT/NTFS hidden sectors field\n"
-	);
+    static const char usage[] = "\
+Usage:   chain.c32 [options]\n\
+         chain.c32 hd<disk#> [<partition>] [options]\n\
+         chain.c32 fd<disk#> [options]\n\
+         chain.c32 mbr:<id> [<partition>] [options]\n\
+         chain.c32 guid:<guid> [<partition>] [options]\n\
+         chain.c32 label:<label> [<partition>] [options]\n\
+         chain.c32 boot [<partition>] [options]\n\
+         chain.c32 fs [options]\n\
+Options: file=<loader>      Load and execute file, instead of boot sector\n\
+         isolinux=<loader>  Load another version of ISOLINUX\n\
+         ntldr=<loader>     Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
+         cmldr=<loader>     Load Recovery Console of Windows NT/2K/XP/2003\n\
+         freedos=<loader>   Load FreeDOS KERNEL.SYS\n\
+         msdos=<loader>     Load MS-DOS IO.SYS\n\
+         pcdos=<loader>     Load PC-DOS IBMBIO.COM\n\
+         grub=<loader>      Load GRUB Legacy stage2\n\
+         grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
+         grldr=<loader>     Load GRUB4DOS grldr\n\
+         seg=<segment>      Jump to <seg>:0000, instead of 0000:7C00\n\
+         swap               Swap drive numbers, if bootdisk is not fd0/hd0\n\
+         hide               Hide primary partitions, except selected partition\n\
+         sethidden          Set the FAT/NTFS hidden sectors field\n\
+         keeppxe            Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
+See syslinux/com32/modules/chain.c for more information\n";
+    error(usage);
 }
 
-
 int main(int argc, char *argv[])
 {
-    char *mbr, *p;
-    struct part_entry *partinfo;
+    struct mbr *mbr = NULL;
+    char *p;
+    struct disk_part_iter *cur_part = NULL;
     struct syslinux_rm_regs regs;
     char *drivename, *partition;
-    int hd, drive, whichpart;
+    int hd, drive, whichpart = 0;	/* MBR by default */
     int i;
+    uint64_t fs_lba = 0;	/* Syslinux partition */
     uint32_t file_lba = 0;
+    struct guid gpt_guid;
     unsigned char *isolinux_bin;
     uint32_t *checksum, *chkhead, *chktail;
     struct data_area data[3];
@@ -729,7 +1338,7 @@ int main(int argc, char *argv[])
 	    opt.loadfile = argv[i] + 6;
 	    opt.sethidden = true;
 	} else if (!strncmp(argv[i], "cmldr=", 6)) {
-	    opt.seg = 0x2000;    /* CMLDR wants this address */
+	    opt.seg = 0x2000;	/* CMLDR wants this address */
 	    opt.loadfile = argv[i] + 6;
 	    opt.cmldr = true;
 	    opt.sethidden = true;
@@ -742,6 +1351,15 @@ int main(int argc, char *argv[])
 	    opt.seg = 0x70;	/* MS-DOS 2.0+ wants this address */
 	    opt.loadfile = argv[i] + 6;
 	    opt.sethidden = true;
+	} else if (!strncmp(argv[i], "grub=", 5)) {
+	    opt.seg = 0x800;	/* stage2 wants this address */
+	    opt.loadfile = argv[i] + 5;
+	    opt.grub = true;
+	} else if (!strncmp(argv[i], "grubcfg=", 8)) {
+	    opt.grubcfg = argv[i] + 8;
+	} else if (!strncmp(argv[i], "grldr=", 6)) {
+	    opt.loadfile = argv[i] + 6;
+	    opt.grldr = true;
 	} else if (!strcmp(argv[i], "swap")) {
 	    opt.swap = true;
 	} else if (!strcmp(argv[i], "noswap")) {
@@ -760,8 +1378,13 @@ int main(int argc, char *argv[])
 		    && argv[i][1] == 'd')
 		   || !strncmp(argv[i], "mbr:", 4)
 		   || !strncmp(argv[i], "mbr=", 4)
+		   || !strncmp(argv[i], "guid:", 5)
+		   || !strncmp(argv[i], "guid=", 5)
+		   || !strncmp(argv[i], "label:", 6)
+		   || !strncmp(argv[i], "label=", 6)
 		   || !strcmp(argv[i], "boot")
-		   || !strncmp(argv[i], "boot,", 5)) {
+		   || !strncmp(argv[i], "boot,", 5)
+		   || !strcmp(argv[i], "fs")) {
 	    drivename = argv[i];
 	    p = strchr(drivename, ',');
 	    if (p) {
@@ -777,6 +1400,11 @@ int main(int argc, char *argv[])
 	}
     }
 
+    if (opt.grubcfg && !opt.grub) {
+	error("grubcfg=<filename> must be used together with grub=<loader>.\n");
+	goto bail;
+    }
+
     if (opt.seg) {
 	regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
     } else {
@@ -790,18 +1418,43 @@ int main(int argc, char *argv[])
 	    error("Unable to find requested MBR signature\n");
 	    goto bail;
 	}
+    } else if (!strncmp(drivename, "guid", 4)) {
+	if (str_to_guid(drivename + 5, &gpt_guid))
+	    goto bail;
+	drive = find_by_guid(&gpt_guid, &cur_part);
+	if (drive == -1) {
+	    error("Unable to find requested GPT disk/partition\n");
+	    goto bail;
+	}
+    } else if (!strncmp(drivename, "label", 5)) {
+	if (!drivename[6]) {
+	    error("No label specified.\n");
+	    goto bail;
+	}
+	drive = find_by_label(drivename + 6, &cur_part);
+	if (drive == -1) {
+	    error("Unable to find requested partition by label\n");
+	    goto bail;
+	}
     } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
 	       drivename[1] == 'd') {
 	hd = drivename[0] == 'h';
 	drivename += 2;
 	drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
-    } else if (!strcmp(drivename, "boot")) {
+    } else if (!strcmp(drivename, "boot") || !strcmp(drivename, "fs")) {
 	const union syslinux_derivative_info *sdi;
+
 	sdi = syslinux_derivative_info();
 	if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX)
 	    drive = 0x80;	/* Boot drive not available */
 	else
 	    drive = sdi->disk.drive_number;
+	if (!strcmp(drivename, "fs")
+	    && (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX
+		|| sdi->c.filesystem == SYSLINUX_FS_EXTLINUX
+		|| sdi->c.filesystem == SYSLINUX_FS_ISOLINUX))
+	    /* We should lookup the Syslinux partition number and use it */
+	    fs_lba = *sdi->disk.partoffset;
     } else {
 	error("Unparsable drive specification\n");
 	goto bail;
@@ -810,33 +1463,54 @@ int main(int argc, char *argv[])
     /* DOS kernels want the drive number in BL instead of DL.  Indulge them. */
     regs.ebx.b[0] = regs.edx.b[0] = drive;
 
-    whichpart = 0;		/* Default */
+    /* Get the disk geometry and disk access setup */
+    if (get_disk_params(drive)) {
+	error("Cannot get disk parameters\n");
+	goto bail;
+    }
+
+    /* Get MBR */
+    if (!(mbr = read_sectors(0, 1))) {
+	error("Cannot read Master Boot Record or sector 0\n");
+	goto bail;
+    }
+
     if (partition)
 	whichpart = strtoul(partition, NULL, 0);
+    /* "guid:" or "label:" might have specified a partition */
+    if (cur_part)
+	whichpart = cur_part->index;
+
+    /* Boot the MBR by default */
+    if (!cur_part && (whichpart || fs_lba)) {
+	/* Boot a partition, possibly the Syslinux partition itself */
+	cur_part = get_first_partition(NULL);
+	while (cur_part) {
+	    if ((cur_part->index == whichpart)
+		|| (cur_part->lba_data == fs_lba))
+		/* Found the partition to boot */
+		break;
+	    cur_part = cur_part->next(cur_part);
+	}
+	if (!cur_part) {
+	    error("Requested partition not found!\n");
+	    goto bail;
+	}
+	whichpart = cur_part->index;
+    }
 
     if (!(drive & 0x80) && whichpart) {
 	error("Warning: Partitions of floppy devices may not work\n");
     }
 
     /* 
-     * grldr of Grub4dos wants the partition number in DH:
+     * GRLDR of GRUB4DOS wants the partition number in DH:
      * -1:   whole drive (default)
      * 0-3:  primary partitions
      * 4-*:  logical partitions
      */
-    regs.edx.b[1] = whichpart-1;
-
-    /* Get the disk geometry and disk access setup */
-    if (get_disk_params(drive)) {
-	error("Cannot get disk parameters\n");
-	goto bail;
-    }
-
-    /* Get MBR */
-    if (!(mbr = read_sector(0))) {
-	error("Cannot read Master Boot Record\n");
-	goto bail;
-    }
+    if (opt.grldr)
+	regs.edx.b[1] = whichpart - 1;
 
     if (opt.hide) {
 	if (whichpart < 1 || whichpart > 4)
@@ -845,30 +1519,6 @@ int main(int argc, char *argv[])
 	    error("WARNING: failed to write MBR for 'hide'\n");
     }
 
-    if (whichpart == 0) {
-	/* Boot the MBR */
-
-	partinfo = NULL;
-    } else if (whichpart <= 4) {
-	/* Boot a primary partition */
-
-	partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart - 1];
-	if (partinfo->ostype == 0) {
-	    error("Invalid primary partition\n");
-	    goto bail;
-	}
-    } else {
-	/* Boot a logical partition */
-
-	nextpart = 5;
-	partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
-
-	if (!partinfo || partinfo->ostype == 0) {
-	    error("Requested logical partition not found\n");
-	    goto bail;
-	}
-    }
-
     /* Do the actual chainloading */
     load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
 
@@ -916,15 +1566,15 @@ int main(int argc, char *argv[])
 		    goto bail;
 		}
 		/* Set it */
-		*((uint32_t *) &isolinux_bin[12]) = file_lba;
+		*((uint32_t *) & isolinux_bin[12]) = file_lba;
 
 		/* Set boot file length */
-		*((uint32_t *) &isolinux_bin[16]) = data[ndata].size;
+		*((uint32_t *) & isolinux_bin[16]) = data[ndata].size;
 
 		/* Calculate checksum */
-		checksum = (uint32_t *) &isolinux_bin[20];
-		chkhead = (uint32_t *) &isolinux_bin[64];
-		chktail = (uint32_t *) &isolinux_bin[data[ndata].size & ~3];
+		checksum = (uint32_t *) & isolinux_bin[20];
+		chkhead = (uint32_t *) & isolinux_bin[64];
+		chktail = (uint32_t *) & isolinux_bin[data[ndata].size & ~3];
 		*checksum = 0;
 		while (chkhead < chktail)
 		    *checksum += *chkhead++;
@@ -945,34 +1595,139 @@ int main(int argc, char *argv[])
 	    }
 	}
 
+	if (opt.grub) {
+	    /* Layout of stage2 file (from byte 0x0 to 0x270) */
+	    struct grub_stage2_patch_area {
+		/* 0x0 to 0x205 */
+		char unknown[0x206];
+		/* 0x206: compatibility version number major */
+		uint8_t compat_version_major;
+		/* 0x207: compatibility version number minor */
+		uint8_t compat_version_minor;
+
+		/* 0x208: install_partition variable */
+		struct {
+		    /* 0x208: sub-partition in sub-partition part2 */
+		    uint8_t part3;
+		    /* 0x209: sub-partition in top-level partition */
+		    uint8_t part2;
+		    /* 0x20a: top-level partiton number */
+		    uint8_t part1;
+		    /* 0x20b: BIOS drive number (must be 0) */
+		    uint8_t drive;
+		} __attribute__ ((packed)) install_partition;
+
+		/* 0x20c: deprecated (historical reason only) */
+		uint32_t saved_entryno;
+		/* 0x210: stage2_ID: will always be STAGE2_ID_STAGE2 = 0 in stage2 */
+		uint8_t stage2_id;
+		/* 0x211: force LBA */
+		uint8_t force_lba;
+		/* 0x212: version string (will probably be 0.97) */
+		char version_string[5];
+		/* 0x217: config filename */
+		char config_file[89];
+		/* 0x270: start of code (after jump from 0x200) */
+		char codestart[1];
+	    } __attribute__ ((packed)) *stage2;
+
+	    if (data[ndata].size < sizeof(struct grub_stage2_patch_area)) {
+		error
+		    ("The file specified by grub=<loader> is to small to be stage2 of GRUB Legacy.\n");
+		goto bail;
+	    }
+
+	    stage2 = data[ndata].data;
+
+	    /*
+	     * Check the compatibility version number to see if we loaded a real
+	     * stage2 file or a stage2 file that we support.
+	     */
+	    if (stage2->compat_version_major != 3
+		|| stage2->compat_version_minor != 2) {
+		error
+		    ("The file specified by grub=<loader> is not a supported stage2 GRUB Legacy binary\n");
+		goto bail;
+	    }
+
+	    /* jump 0x200 bytes into the loadfile */
+	    regs.ip = 0x200;
+
+	    /*
+	     * GRUB Legacy wants the partition number in the install_partition
+	     * variable, located at offset 0x208 of stage2.
+	     * When GRUB Legacy is loaded, it is located at memory address 0x8208.
+	     *
+	     * It looks very similar to the "boot information format" of the
+	     * Multiboot specification:
+	     *   http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Boot-information-format
+	     *
+	     *   0x208 = part3: sub-partition in sub-partition part2
+	     *   0x209 = part2: sub-partition in top-level partition
+	     *   0x20a = part1: top-level partition number
+	     *   0x20b = drive: BIOS drive number (must be 0)
+	     *
+	     * GRUB Legacy doesn't store the BIOS drive number at 0x20b, but at
+	     * another location.
+	     *
+	     * Partition numbers always start from zero.
+	     * Unused partition bytes must be set to 0xFF. 
+	     *
+	     * We only care about top-level partition, so we only need to change
+	     * "part1" to the appropriate value:
+	     *   -1:   whole drive (default) (-1 = 0xFF)
+	     *   0-3:  primary partitions
+	     *   4-*:  logical partitions
+	     */
+	    stage2->install_partition.part1 = whichpart - 1;
+
+	    /*
+	     * Grub Legacy reserves 89 bytes (from 0x8217 to 0x826f) for the
+	     * config filename. The filename passed via grubcfg= will overwrite
+	     * the default config filename "/boot/grub/menu.lst".
+	     */
+	    if (opt.grubcfg) {
+		if (strlen(opt.grubcfg) > sizeof(stage2->config_file) - 1) {
+		    error
+			("The config filename length can't exceed 88 characters.\n");
+		    goto bail;
+		}
+
+		strcpy((char *)stage2->config_file, opt.grubcfg);
+	    }
+	}
+
 	ndata++;
     }
 
     if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
 	/* Actually read the boot sector */
-	if (!partinfo) {
+	if (!cur_part) {
 	    data[ndata].data = mbr;
-	} else if (!(data[ndata].data = read_sector(partinfo->start_lba))) {
+	} else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
 	    error("Cannot read boot sector\n");
 	    goto bail;
 	}
 	data[ndata].size = SECTOR;
 	data[ndata].base = load_base;
-	
-	if (!opt.loadfile &&
-	    *(uint16_t *)((char *)data[ndata].data +
-			  data[ndata].size - 2) != 0xaa55) {
-	    error("Boot sector signature not found (unbootable disk/partition?)\n");
-	    goto bail;
-	}
 
+	if (!opt.loadfile) {
+	    const struct mbr *br =
+		(const struct mbr *)((char *)data[ndata].data +
+				     data[ndata].size - sizeof(struct mbr));
+	    if (br->sig != mbr_sig_magic) {
+		error
+		    ("Boot sector signature not found (unbootable disk/partition?)\n");
+		goto bail;
+	    }
+	}
 	/*
 	 * To boot the Recovery Console of Windows NT/2K/XP we need to write
 	 * the string "cmdcons\0" to memory location 0000:7C03.
 	 * Memory location 0000:7C00 contains the bootsector of the partition.
 	 */
-	if (partinfo && opt.cmldr) {
-	    memcpy((char *)data[ndata].data+3, cmldr_signature,
+	if (cur_part && opt.cmldr) {
+	    memcpy((char *)data[ndata].data + 3, cmldr_signature,
 		   sizeof cmldr_signature);
 	}
 
@@ -981,25 +1736,91 @@ int main(int argc, char *argv[])
 	 * this modifies the field used by FAT and NTFS filesystems, and
 	 * possibly other boot loaders which use the same format.
 	 */
-	if (partinfo && opt.sethidden) {
-	    *(uint32_t *)((char *)data[ndata].data + 28) =
-		partinfo->start_lba;
+	if (cur_part && opt.sethidden) {
+	    *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
 	}
 
 	ndata++;
     }
 
-    if (partinfo) {
-	/* 0x7BE is the canonical place for the first partition entry. */
-	data[ndata].data = partinfo;
-	data[ndata].base = 0x7be;
-	data[ndata].size = sizeof *partinfo;
-	ndata++;
-	regs.esi.w[0] = 0x7be;
+    if (cur_part) {
+	if (cur_part->next == next_gpt_part) {
+	    /* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
+	    struct part_entry *record;
+	    /* Look at the GPT partition */
+	    const struct gpt_part *gp = (const struct gpt_part *)
+		(cur_part->block +
+		 (cur_part->private.gpt.size * cur_part->private.gpt.index));
+	    /* Note the partition length */
+	    uint64_t lba_count = gp->lba_last - gp->lba_first + 1;
+	    /* The length of the hand-over */
+	    int synth_size =
+		sizeof(struct part_entry) + sizeof(uint32_t) +
+		cur_part->private.gpt.size;
+	    /* Will point to the partition record length in the hand-over */
+	    uint32_t *plen;
+
+	    /* Allocate the hand-over record */
+	    record = malloc(synth_size);
+	    if (!record) {
+		error("Could not build GPT hand-over record!\n");
+		goto bail;
+	    }
+	    /* Synthesize the record */
+	    memset(record, 0, synth_size);
+	    record->active_flag = 0x80;
+	    record->ostype = 0xED;
+	    /* All bits set by default */
+	    record->start_lba = ~(uint32_t) 0;
+	    record->length = ~(uint32_t) 0;
+	    /* If these fit the precision, pass them on */
+	    if (cur_part->lba_data < record->start_lba)
+		record->start_lba = cur_part->lba_data;
+	    if (lba_count < record->length)
+		record->length = lba_count;
+	    /* Next comes the GPT partition record length */
+	    plen = (uint32_t *) (record + 1);
+	    plen[0] = cur_part->private.gpt.size;
+	    /* Next comes the GPT partition record copy */
+	    memcpy(plen + 1, gp, plen[0]);
+	    cur_part->record = record;
+
+	    regs.eax.l = 0x54504721;	/* '!GPT' */
+	    data[ndata].base = 0x7be;
+	    data[ndata].size = synth_size;
+	    data[ndata].data = (void *)record;
+	    ndata++;
+	    regs.esi.w[0] = 0x7be;
+
+	    dprintf("GPT handover:\n");
+	    mbr_part_dump(record);
+	    gpt_part_dump((struct gpt_part *)(plen + 1));
+	} else if (cur_part->record) {
+	    /* MBR handover protocol */
+	    static struct part_entry handover_record;
+
+	    handover_record = *cur_part->record;
+	    handover_record.start_lba = cur_part->lba_data;
+
+	    data[ndata].base = 0x7be;
+	    data[ndata].size = sizeof handover_record;
+	    data[ndata].data = &handover_record;
+	    ndata++;
+	    regs.esi.w[0] = 0x7be;
+
+	    dprintf("MBR handover:\n");
+	    mbr_part_dump(&handover_record);
+	}
     }
 
     do_boot(data, ndata, &regs);
 
-  bail:
+bail:
+    if (cur_part) {
+	free(cur_part->block);
+	free((void *)cur_part->record);
+    }
+    free(cur_part);
+    free(mbr);
     return 255;
 }
diff --git a/com32/modules/config.c b/com32/modules/config.c
index fb6cb86..334a635 100644
--- a/com32/modules/config.c
+++ b/com32/modules/config.c
@@ -26,12 +26,13 @@ int main(int argc, char *argv[])
 {
     openconsole(&dev_null_r, &dev_stdcon_w);
 
-    if (argc != 2) {
-	fprintf(stderr, "Usage: config <filename>\n");
+    if (argc < 2 || argc > 3) {
+	fprintf(stderr, "Usage: config <filename> [<directory>]\n");
 	return 1;
     }
 
-    syslinux_run_kernel_image(argv[1], "", 0, IMAGE_TYPE_CONFIG);
+    syslinux_run_kernel_image(argv[1], argv[2] ? argv[2] : "",
+			      0, IMAGE_TYPE_CONFIG);
 
     fprintf(stderr, "config: %s: failed to load (missing file?)\n", argv[1]);
     return 1;
diff --git a/com32/modules/elf.c b/com32/modules/elf.c
index d53d936..182afa6 100644
--- a/com32/modules/elf.c
+++ b/com32/modules/elf.c
@@ -42,6 +42,7 @@
 #include <sys/stat.h>
 #include <elf.h>
 #include <console.h>
+#include <dprintf.h>
 
 #include <syslinux/loadfile.h>
 #include <syslinux/movebits.h>
@@ -50,13 +51,6 @@
 /* If we don't have this much memory for the stack, signal failure */
 #define MIN_STACK	512
 
-#define DEBUG 0
-#if DEBUG
-# define dprintf printf
-#else
-# define dprintf(f, ...) ((void)0)
-#endif
-
 static inline void error(const char *msg)
 {
     fputs(msg, stderr);
diff --git a/com32/modules/ifcpu.c b/com32/modules/ifcpu.c
index 8a9a5f4..a28acc4 100644
--- a/com32/modules/ifcpu.c
+++ b/com32/modules/ifcpu.c
@@ -13,34 +13,6 @@
 /*
  * ifcpu.c
  *
- * Run one command (boot_entry_1) if the booted system match some CPU features
- * and another (boot_entry_2) if it doesn't.
- * Eventually this and other features should get folded into some kind
- * of scripting engine.
- *
- * Usage:
- *
- *    label test
- *        com32 ifcpu.c32
- *        append <option> <cpu_features> -- boot_entry_1 -- boot_entry_2
- *    label boot_entry_1
- *    	  kernel vmlinuz
- *    	  append ...
- *    label boot_entry_2
- *        kernel vmlinuz_64
- *        append ...
- *
- * options could be :
- *    debug     : display some debugging messages
- *    dry-run   : just do the detection, don't boot
- *
- * cpu_features could be:
- *    64        : CPU have to be x86_64 compatible
- *    hvm       : Processor must have hardware virtualization (hvm or svm)
- *    multicore : Processor must be multi-core
- *    smp       : System have to be SMP
- *
- * if you want to match many cpu features, just separate them with a single space
  */
 
 #include <alloca.h>
@@ -48,43 +20,64 @@
 #include <stdio.h>
 #include <string.h>
 #include <cpuid.h>
+#include <unistd.h>
 #include <syslinux/boot.h>
 #include <com32.h>
 #include <consoles.h>
 
-#define REG_AH(x) ((x).eax.b[1])
-#define REG_CX(x) ((x).ecx.w[0])
-#define REG_DX(x) ((x).edx.w[0])
-
-static unsigned char sleep(unsigned int msec)
+static inline void error(const char *msg)
 {
-    unsigned long micro = 1000 * msec;
-    com32sys_t inreg, outreg;
+    fputs(msg, stderr);
+}
 
-    REG_AH(inreg) = 0x86;
-    REG_CX(inreg) = (micro >> 16);
-    REG_DX(inreg) = (micro & 0xFFFF);
-    __intcall(0x15, &inreg, &outreg);
-    return REG_AH(outreg);
+static void usage(void) 
+{
+ error("Run one command if system match some CPU features, another if it doesn't. \n"
+ "Usage: \n"
+ "   label ifcpu \n"
+ "       com32 ifcpu.c32 \n"
+ "       append <option> <cpu_features> -- boot_entry_1 -- boot_entry_2 \n"
+ "   label boot_entry_1 \n"
+ "   	  kernel vmlinuz_entry1 \n"
+ "	  append ... \n"
+ "   label boot_entry_2 \n"
+ "       kernel vmlinuz_entry2 \n"
+ "       append ... \n"
+ "\n"
+ "options could be :\n"
+ "   debug     : display some debugging messages \n"
+ "   dry-run   : just do the detection, don't boot \n"
+ "\n"
+ "cpu_features could be:\n"
+ "   64        : Processor is x86_64 compatible (lm cpu flag)\n"
+ "   hvm       : Processor features hardware virtualization (hvm or svm cpu flag)\n"
+ "   multicore : Processor must be multi-core \n"
+ "   smp       : System must be multi-processor \n"
+ "   pae       : Processor features Physical Address Extension (PAE)\n"
+ "\n"
+ "if you want to match many cpu features, just separate them with a single space.\n");
 }
 
 /* XXX: this really should be librarized */
 static void boot_args(char **args)
 {
-    int len = 0;
+    int len = 0, a = 0;
     char **pp;
     const char *p;
     char c, *q, *str;
 
     for (pp = args; *pp; pp++)
-	len += strlen(*pp);
+	len += strlen(*pp) + 1;
 
-    q = str = alloca(len + 1);
+    q = str = alloca(len);
     for (pp = args; *pp; pp++) {
 	p = *pp;
 	while ((c = *p++))
 	    *q++ = c;
+	*q++ = ' ';
+	a = 1;
     }
+    q -= a;
     *q = '\0';
 
     if (!str[0])
@@ -98,8 +91,8 @@ static void boot_args(char **args)
 int main(int argc, char *argv[])
 {
     char **args[3];
-    int i;
-    int n;
+    int i=0;
+    int n=0;
     bool hardware_matches = true;
     bool multicore = false;
     bool dryrun = false;
@@ -108,7 +101,13 @@ int main(int argc, char *argv[])
     s_cpu cpu;
     console_ansi_raw();
     detect_cpu(&cpu);
-    n = 0;
+
+    /* If no argument got passed, let's show the usage */
+    if (argc == 1) {
+	    usage();
+	    return -1;
+    }
+
     for (i = 1; i < argc; i++) {
 	if (!strcmp(argv[i], "--")) {
 	    argv[i] = NULL;
@@ -118,6 +117,11 @@ int main(int argc, char *argv[])
 		printf(" 64bit     : %s on this system\n",
 		       show_bool(cpu.flags.lm));
 	    hardware_matches = cpu.flags.lm && hardware_matches;
+	} else if (!strcmp(argv[i], "pae")) {
+	    if (debug)
+		printf(" pae       : %s on this system\n",
+		       show_bool(cpu.flags.pae));
+	    hardware_matches = cpu.flags.pae && hardware_matches;
 	} else if (!strcmp(argv[i], "hvm")) {
 	    if (debug)
 		printf(" hvm       : %s on this system\n",
@@ -153,7 +157,7 @@ int main(int argc, char *argv[])
 	       hardware_matches ? *args[0] : *args[1]);
 	printf("Sleeping 5sec before booting\n");
 	if (!dryrun)
-	    sleep(5000);
+	    sleep(5);
     }
 
     if (!dryrun)
diff --git a/com32/modules/ifcpu64.c b/com32/modules/ifcpu64.c
index 6d566a2..e123922 100644
--- a/com32/modules/ifcpu64.c
+++ b/com32/modules/ifcpu64.c
@@ -73,20 +73,23 @@ static bool __constfunc cpu_has_feature(int x)
 /* XXX: this really should be librarized */
 static void boot_args(char **args)
 {
-    int len = 0;
+    int len = 0, a = 0;
     char **pp;
     const char *p;
     char c, *q, *str;
 
     for (pp = args; *pp; pp++)
-	len += strlen(*pp);
+	len += strlen(*pp) + 1;
 
-    q = str = alloca(len + 1);
+    q = str = alloca(len);
     for (pp = args; *pp; pp++) {
 	p = *pp;
 	while ((c = *p++))
 	    *q++ = c;
+	*q++ = ' ';
+	a = 1;
     }
+    q -= a;
     *q = '\0';
 
     if (!str[0])
diff --git a/com32/modules/ifplop.c b/com32/modules/ifplop.c
new file mode 100644
index 0000000..c6286d9
--- /dev/null
+++ b/com32/modules/ifplop.c
@@ -0,0 +1,165 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2010 Gert Hulselmans - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * ifplop.c
+ *
+ * This COM32 module detects if the PLoP Boot Manager was used to boot a CDROM
+ * drive or USB drive, by checking for the presence of the PLoP INT13h hook.
+ *
+ * Usage:   ifplop.c32 [<plop_detected>] -- [<plop_not_detected>]
+ * Example: ifplop.c32 menu.c32 another.cfg -- plpbt.bin hiddenusb usb1=2
+ *
+ * A possible config file could be:
+ *
+ * ===========================================================================
+ *  DEFAULT plopcheck
+ *
+ *  # Check for the presence of PLoP (run by default)
+ *  #   When PLoP INT13h hook is found, run the first command (plop_detected)
+ *  #   When PLoP INT13h hook isn't found, run the second command (plop_not_detected)
+ *  LABEL plopcheck
+ *      COM32 ifplop.c32
+ *      APPEND plop_detected -- plop_not_detected
+ *
+ *  # When PLoP INT13h hook was found, boot the menu system.
+ *  # PLoP can have added USB 2.0 speed, so the entries we want to boot
+ *  # will be read from disk much faster (supposing that we have a BIOS
+ *  # that only supports USB 1.1 speed, but a mobo with USB 2.0 controllers).
+ *  LABEL plop_detected
+ *      COM32 menu.c32
+ *      APPEND another.cfg
+ *
+ *  # PLoP INT13h hook wasn't found, so we boot PLoP, so it can add USB 2.0 support
+ *  LABEL plop_not_detected
+ *      LINUX plpbt.bin hiddenusb usb1=2
+ *
+ * ===========================================================================
+ *
+ * Why is/can this module be useful?
+ *
+ * You may want to boot PLoP by default from Syslinux when you boot from your
+ * USB stick/drive:
+ *   1. PLoP can upgrade USB 1.1 speed offered by the BIOS to USB 2.0 speed
+ *      if you have USB 2.0 controllers on your mobo.
+ *   2. Some BIOSes only can acees the first 128GiB (137GB) on USB drives, while
+ *      internal hard drives don't neseccarily suffer from this 128GiB problem.
+ *      Using PLoPs USB capabilities, you can access the whole drive.
+ *
+ * When you select the "USB" entry in PLoP, it will boot your USB stick/drive
+ * again and it will boot PLoP again when you have set booting PLoP as DEFAULT
+ * boot option in your Syslinux configuration file.
+ *
+ * By using ifplop.c32 you can specify which action you want to do the second
+ * time your USB stick/drive is booted. So you can load another config file or
+ * boot a large hard disk image or whathever you want.
+ *
+ * PLoP Boot Manager website: http://www.plop.at/en/bootmanager.html
+ */
+
+#include <com32.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <alloca.h>
+#include <console.h>
+#include <syslinux/boot.h>
+
+static bool plop_INT13h_check(void)
+{
+    com32sys_t inregs, outregs;
+
+    /* Prepare the register set */
+    memset(&inregs, 0, sizeof inregs);
+
+    /*
+     * Check if PLoP already has booted a CDROM or USB drive by checking
+     * for the presence of the PLoP INT13h hook.
+     *
+     * The following assembly code (NASM) can detect the PLoP INT13h hook:
+     *
+     *   mov eax,'PoLP'  ; Reverse of 'PLoP'
+     *   mov ebp,'DKHC'  ; Reverse of 'CHKD'
+     *   int 13h
+     *   cmp eax,' sey'  ; Reverse of 'yes '
+     *   jz plop_INT13h_active
+     */
+
+    inregs.eax.l = 0x504c6f50;	/* "PLoP" */
+    inregs.ebp.l = 0x43484b44;	/* "CHKD" */
+
+    __intcall(0x13, &inregs, &outregs);
+
+    /* eax will contain "yes " if PLoP INT13h hook is available */
+    if (outregs.eax.l == 0x79657320)
+	return true;
+
+    return false;
+}
+
+/* XXX: this really should be librarized */
+static void boot_args(char **args)
+{
+    int len = 0, a = 0;
+    char **pp;
+    const char *p;
+    char c, *q, *str;
+
+    for (pp = args; *pp; pp++)
+	len += strlen(*pp) + 1;
+
+    q = str = alloca(len);
+    for (pp = args; *pp; pp++) {
+	p = *pp;
+	while ((c = *p++))
+	    *q++ = c;
+	*q++ = ' ';
+	a = 1;
+    }
+    q -= a;
+    *q = '\0';
+
+    if (!str[0])
+	syslinux_run_default();
+    else
+	syslinux_run_command(str);
+}
+
+int main(int argc, char *argv[])
+{
+    char **args[2];
+    int arg = 0;
+
+    openconsole(&dev_null_r, &dev_stdcon_w);
+
+    if (argc)
+	arg++;
+    args[0] = &argv[arg];
+    args[1] = NULL;
+    while (arg < argc) {
+	if (!strcmp(argv[arg], "--")) {
+	    argv[arg] = NULL;
+	    args[1] = &argv[arg + 1];
+	    break;
+	}
+	arg++;
+    }
+    if (args[1] != NULL) {
+	boot_args(plop_INT13h_check()? args[0] : args[1]);
+    } else {
+	fprintf(stderr,
+		"Usage:   ifplop.c32 [<plop_detected>] -- [<plop_not_detected>]\n"
+		"Example: ifplop.c32 menu.c32 another.cfg -- plpbt.bin hiddenusb usb1=2\n");
+    }
+
+    return 0;
+}
diff --git a/com32/modules/dir.c b/com32/modules/ls.c
similarity index 98%
copy from com32/modules/dir.c
copy to com32/modules/ls.c
index c9d9370..c311621 100644
--- a/com32/modules/dir.c
+++ b/com32/modules/ls.c
@@ -168,7 +168,7 @@ int main(int argc, char *argv[])
     else if (argc == 2)
 	rv = display_directory(argv[1]);
     else {
-	printf("Usage: dir directory\n");
+	printf("Usage: %s directory\n", argv[0]);
 	rv = 1;
     }
 
diff --git a/com32/modules/pmload.c b/com32/modules/pmload.c
index 7a0e193..3064a94 100644
--- a/com32/modules/pmload.c
+++ b/com32/modules/pmload.c
@@ -44,6 +44,7 @@
 #include <sys/stat.h>
 #include <elf.h>
 #include <console.h>
+#include <dprintf.h>
 
 #include <syslinux/loadfile.h>
 #include <syslinux/movebits.h>
@@ -52,13 +53,6 @@
 /* If we don't have this much memory for the stack, signal failure */
 #define MIN_STACK	512
 
-#define DEBUG 0
-#if DEBUG
-# define dprintf printf
-#else
-# define dprintf(f, ...) ((void)0)
-#endif
-
 static inline void error(const char *msg)
 {
     fputs(msg, stderr);
diff --git a/com32/modules/pwd.c b/com32/modules/pwd.c
new file mode 100644
index 0000000..880327d
--- /dev/null
+++ b/com32/modules/pwd.c
@@ -0,0 +1,51 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2010 Gene Cumm - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Display present (current) working directory
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <console.h>
+#include <unistd.h>
+#include <dirent.h>
+
+/* Size of path buffer string */
+#ifndef PATH_MAX
+#  ifdef NAME_MAX
+#    define PATH_MAX   NAME_MAX
+#  elif FILENAME_MAX
+#    define PATH_MAX   FILENAME_MAX
+#  else
+#    define PATH_MAX   256
+#  endif       /* NAME_MAX */
+#endif /* PATH_MAX */
+
+int main(void)
+{
+    int rv = 0;
+    char pwd[PATH_MAX], *pwdptr;
+
+    openconsole(&dev_rawcon_r, &dev_stdcon_w);
+    pwdptr = getcwd(pwd, PATH_MAX);
+    if (pwdptr) {
+       if (pwd[0] != 0)
+           puts(pwd);
+       else
+           puts(".");
+    } else {
+       rv = errno;
+       puts("ERROR: getcwd() returned NULL");
+    }
+    return rv;
+}
diff --git a/com32/modules/sdi.c b/com32/modules/sdi.c
index fbca6a5..69841d2 100644
--- a/com32/modules/sdi.c
+++ b/com32/modules/sdi.c
@@ -28,18 +28,12 @@
 #include <minmax.h>
 #include <sys/stat.h>
 #include <console.h>
+#include <dprintf.h>
 
 #include <syslinux/loadfile.h>
 #include <syslinux/movebits.h>
 #include <syslinux/bootrm.h>
 
-#define DEBUG 0
-#if DEBUG
-# define dprintf printf
-#else
-# define dprintf(f, ...) ((void)0)
-#endif
-
 typedef uint8_t guid_t[16];
 
 struct SDIHeader {
diff --git a/com32/modules/whichsys.c b/com32/modules/whichsys.c
new file mode 100644
index 0000000..c186722
--- /dev/null
+++ b/com32/modules/whichsys.c
@@ -0,0 +1,128 @@
+/* ----------------------------------------------------------------------- *
+ *
+ *   Copyright 2010 Gert Hulselmans - All Rights Reserved
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *   Boston MA 02110-1301, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * whichsys.c
+ *
+ * Detemine which command to execute, based on the Syslinux bootloader variant
+ * from which you run it.
+ *
+ * Usage:    whichsys.c32 [-iso- command] [-pxe- command] [-sys- command]
+ * Examples: whichsys.c32 -iso- chain.c32 hd0 -sys- chain.c32 hd1 swap
+ *           whichsys.c32 -iso- config iso.cfg -sys- sys.cfg -pxe- pxe.cfg
+ *
+ */
+
+#include <stdio.h>
+#include <alloca.h>
+#include <console.h>
+#include <string.h>
+#include <syslinux/boot.h>
+#include "syslinux/config.h"
+
+
+static struct syslinux_parameter {
+    char **arg[1];
+    bool option;
+} isolinux, pxelinux, syslinux;
+
+/* XXX: this really should be librarized */
+static void boot_args(char **args)
+{
+    int len = 0, a = 0;
+    char **pp;
+    const char *p;
+    char c, *q, *str;
+
+    for (pp = args; *pp; pp++)
+	len += strlen(*pp) + 1;
+
+    q = str = alloca(len);
+    for (pp = args; *pp; pp++) {
+	p = *pp;
+	while ((c = *p++))
+	    *q++ = c;
+	*q++ = ' ';
+	a = 1;
+    }
+    q -= a;
+    *q = '\0';
+
+    if (!str[0])
+	syslinux_run_default();
+    else
+	syslinux_run_command(str);
+}
+
+static void usage(void)
+{
+    static const char usage[] = "\
+Usage:    whichsys.c32 [-iso- command] [-pxe- command] [-sys- command]\n\
+Examples: whichsys.c32 -iso- chain.c32 hd0 -sys- chain.c32 hd1 swap\n\
+          whichsys.c32 -iso- config iso.cfg -sys- sys.cfg -pxe- pxe.cfg\n";
+    fprintf(stderr, usage);
+}
+
+int main(int argc, char *argv[])
+{
+    const union syslinux_derivative_info *sdi;
+
+    int arg = 0;
+
+    openconsole(&dev_null_r, &dev_stdcon_w);
+
+    /* If no argument got passed, let's show the usage */
+    if (argc == 1) {
+	    usage();
+	    return 0;
+    }
+
+    arg++;
+
+    while (arg < argc) {
+	if (!strcmp(argv[arg], "-iso-")) {
+	    argv[arg] = NULL;
+	    isolinux.arg[0] = &argv[arg + 1];
+	    isolinux.option = true;
+	}
+	if (!strcmp(argv[arg], "-pxe-")) {
+	    argv[arg] = NULL;
+	    pxelinux.arg[0] = &argv[arg + 1];
+	    pxelinux.option = true;
+	}
+	if (!strcmp(argv[arg], "-sys-")) {
+	    argv[arg] = NULL;
+	    syslinux.arg[0] = &argv[arg + 1];
+	    syslinux.option = true;
+	}
+	arg++;
+    }
+
+    sdi = syslinux_derivative_info();
+
+    switch (sdi->c.filesystem) {
+	case SYSLINUX_FS_ISOLINUX:
+	    isolinux.option ? boot_args(isolinux.arg[0]) : fprintf(stderr, "No command specified for ISOLINUX.\n\n"); usage();
+	    break;
+	case SYSLINUX_FS_PXELINUX:
+	    pxelinux.option ? boot_args(pxelinux.arg[0]) : fprintf(stderr, "No command specified for PXELINUX.\n\n"); usage();
+	    break;
+	case SYSLINUX_FS_SYSLINUX:
+	    syslinux.option ? boot_args(syslinux.arg[0]) : fprintf(stderr, "No command specified for SYSLINUX.\n\n"); usage();
+	    break;
+	case SYSLINUX_FS_UNKNOWN:
+	default:
+	    fprintf(stderr, "Unknown Syslinux filesystem\n\n");
+    }
+
+    return -1;
+}



More information about the Syslinux-commits mailing list