[syslinux:master] chain.c32: add option to set hidden sectors; load boot sector for NTLDR

syslinux-bot for H. Peter Anvin hpa at zytor.com
Sat Feb 13 14:48:11 PST 2010


Commit-ID:  033fb123375ce6af905dcb2408fa0df74960e066
Gitweb:     http://syslinux.zytor.com/commit/033fb123375ce6af905dcb2408fa0df74960e066
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Sat, 13 Feb 2010 14:45:42 -0800
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Sat, 13 Feb 2010 14:45:42 -0800

chain.c32: add option to set hidden sectors; load boot sector for NTLDR

Add an option to update the in-memory version of FAT/NTFS hidden
sectors (== partition offset); load the boot sector for NTLDR.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>


---
 com32/modules/chain.c |  200 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 134 insertions(+), 66 deletions(-)

diff --git a/com32/modules/chain.c b/com32/modules/chain.c
index 458ea89..a559792 100644
--- a/com32/modules/chain.c
+++ b/com32/modules/chain.c
@@ -46,18 +46,22 @@
  *	when you want more than one ISOLINUX per CD/DVD.
  *
  * ntldr=<loader>:
- *	equivalent to -seg 0x2000 -file <loader>, used with WinNT's loaders
+ *	equivalent to seg=0x2000 file=<loader> sethidden,
+ *      used with WinNT's loaders
  *
  * cmldr=<loader>:
  *      used with Recovery Console of Windows NT/2K/XP.
- *      same as ntldr=<loader> & "cmdcons\0" written to memory address 0000:7C03
+ *      same as ntldr=<loader> & "cmdcons\0" written to
+ *      the system name field in the bootsector
  *
  * freedos=<loader>:
- *	equivalent to -seg 0x60 -file <loader>, used with FreeDOS kernel.sys.
+ *	equivalent to seg=0x60 file=<loader> sethidden,
+ *      used with FreeDOS kernel.sys.
  *
  * msdos=<loader>
  * pcdos=<loader>
- *	equivalent to -seg 0x70 -file <loader>, used with DOS' io.sys.
+ *	equivalent to seg=0x70 file=<loader> sethidden,
+ *      used with DOS' io.sys.
  *
  * swap:
  *	if the disk is not fd0/hd0, install a BIOS stub which swaps
@@ -67,6 +71,10 @@
  *	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.
  */
 
 #include <com32.h>
@@ -92,8 +100,15 @@ static struct options {
     bool cmldr;
     bool swap;
     bool hide;
+    bool sethidden;
 } opt;
 
+struct data_area {
+    void *data;
+    addr_t base;
+    addr_t size;
+};
+
 static inline void error(const char *msg)
 {
     fputs(msg, stderr);
@@ -446,7 +461,7 @@ static struct part_entry *find_logical_partition(int whichpart, char *table,
     return NULL;
 }
 
-static void do_boot(void *boot_sector, size_t boot_size,
+static void do_boot(struct data_area *data, int ndata,
 		    struct syslinux_rm_regs *regs)
 {
     uint16_t *const bios_fbm = (uint16_t *) 0x413;
@@ -457,8 +472,6 @@ static void do_boot(void *boot_sector, size_t boot_size,
     uint8_t driveno = regs->edx.b[0];
     uint8_t swapdrive = driveno & 0x80;
     int i;
-    addr_t loadbase = opt.seg ? (opt.seg << 4) : 0x7c00;
-    static const char cmldr_signature[8] = "cmdcons";
 
     mmap = syslinux_memory_map();
 
@@ -467,25 +480,17 @@ static void do_boot(void *boot_sector, size_t boot_size,
 	return;
     }
 
-    /* Nothing below 0x7c00, much simpler... */
-
-    if (boot_size >= dosmem - loadbase)
+    endimage = 0;
+    for (i = 0; i < ndata; i++) {
+	if (data[i].base + data[i].size > endimage)
+	    endimage = data[i].base + data[i].size;
+    }
+    if (endimage > dosmem)
 	goto too_big;
 
-    endimage = loadbase + boot_size;
-
-    if (syslinux_add_movelist(&mlist, loadbase,
-			      (addr_t) boot_sector, boot_size))
-	goto enomem;
-
-    /*
-     * 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 (opt.cmldr) {
-	if (syslinux_add_movelist(&mlist, 0x7c03, (addr_t)cmldr_signature,
-				  sizeof cmldr_signature))
+    for (i = 0; i < ndata; i++) {
+	if (syslinux_add_movelist(&mlist, data[i].base,
+				  (addr_t)data[i].data, data[i].size))
 	    goto enomem;
     }
 
@@ -661,6 +666,27 @@ static uint32_t get_file_lba(const char *filename)
     return lba;
 }
 
+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"
+	);
+}
+
+
 int main(int argc, char *argv[])
 {
     char *mbr, *p;
@@ -673,7 +699,10 @@ int main(int argc, char *argv[])
     uint32_t file_lba = 0;
     unsigned char *isolinux_bin;
     uint32_t *checksum, *chkhead, *chktail;
-    size_t boot_size = SECTOR;
+    struct data_area data[3];
+    int ndata = 0;
+    addr_t load_base;
+    static const char cmldr_signature[8] = "cmdcons";
 
     openconsole(&dev_null_r, &dev_stdcon_w);
 
@@ -699,28 +728,41 @@ int main(int argc, char *argv[])
 	} else if (!strncmp(argv[i], "ntldr=", 6)) {
 	    opt.seg = 0x2000;	/* NTLDR wants this address */
 	    opt.loadfile = argv[i] + 6;
+	    opt.sethidden = true;
 	} else if (!strncmp(argv[i], "cmldr=", 6)) {
 	    opt.seg = 0x2000;    /* CMLDR wants this address */
 	    opt.loadfile = argv[i] + 6;
 	    opt.cmldr = true;
+	    opt.sethidden = true;
 	} else if (!strncmp(argv[i], "freedos=", 8)) {
 	    opt.seg = 0x60;	/* FREEDOS wants this address */
 	    opt.loadfile = argv[i] + 8;
+	    opt.sethidden = true;
 	} else if (!strncmp(argv[i], "msdos=", 6) ||
 		   !strncmp(argv[i], "pcdos=", 6)) {
 	    opt.seg = 0x70;	/* MS-DOS 2.0+ wants this address */
 	    opt.loadfile = argv[i] + 6;
+	    opt.sethidden = true;
 	} else if (!strcmp(argv[i], "swap")) {
 	    opt.swap = true;
+	} else if (!strcmp(argv[i], "noswap")) {
+	    opt.swap = false;
 	} else if (!strcmp(argv[i], "hide")) {
 	    opt.hide = true;
+	} else if (!strcmp(argv[i], "nohide")) {
+	    opt.hide = false;
 	} else if (!strcmp(argv[i], "keeppxe")) {
 	    opt.keeppxe = 3;
-	} else
-	    if (((argv[i][0] == 'h' || argv[i][0] == 'f') && argv[i][1] == 'd')
-		|| !strncmp(argv[i], "mbr:", 4)
-		|| !strncmp(argv[i], "mbr=", 4)
-		|| !strcmp(argv[i], "boot") || !strncmp(argv[i], "boot,", 5)) {
+	} else if (!strcmp(argv[i], "sethidden")) {
+	    opt.sethidden = true;
+	} else if (!strcmp(argv[i], "nosethidden")) {
+	    opt.sethidden = false;
+	} else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
+		    && argv[i][1] == 'd')
+		   || !strncmp(argv[i], "mbr:", 4)
+		   || !strncmp(argv[i], "mbr=", 4)
+		   || !strcmp(argv[i], "boot")
+		   || !strncmp(argv[i], "boot,", 5)) {
 	    drivename = argv[i];
 	    p = strchr(drivename, ',');
 	    if (p) {
@@ -731,21 +773,7 @@ int main(int argc, char *argv[])
 		partition = argv[++i];
 	    }
 	} else {
-	    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");
+	    usage();
 	    goto bail;
 	}
     }
@@ -844,12 +872,16 @@ int main(int argc, char *argv[])
     }
 
     /* Do the actual chainloading */
+    load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
+
     if (opt.loadfile) {
 	fputs("Loading the boot file...\n", stdout);
-	if (loadfile(opt.loadfile, &boot_sector, &boot_size)) {
+	if (loadfile(opt.loadfile, &data[ndata].data, &data[ndata].size)) {
 	    error("Failed to load the boot file\n");
 	    goto bail;
 	}
+	data[ndata].base = load_base;
+	load_base = 0x7c00;	/* If we also load a boot sector */
 
 	/* Create boot info table: needed when you want to chainload
 	   another version of ISOLINUX (or another bootlaoder that needs
@@ -876,7 +908,7 @@ int main(int argc, char *argv[])
 		   LBA of primary volume descriptor should already be set to 16. 
 		 */
 
-		isolinux_bin = (unsigned char *)boot_sector;
+		isolinux_bin = (unsigned char *)data[ndata].data;
 
 		/* Get LBA address of bootfile */
 		file_lba = get_file_lba(opt.loadfile);
@@ -886,20 +918,27 @@ 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]) = boot_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[boot_size - 1];
-		/* Fresh checksum and clear possibly fractional uint32_t at the end */
-		*checksum = *((uint32_t *) & isolinux_bin[boot_size]) = 0;
-
-		while (chkhead <= chktail) {
+		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++;
+
+		/*
+		 * Deal with possible fractional dword at the end;
+		 * this *should* never happen...
+		 */
+		if (data[ndata].size & 3) {
+		    uint32_t xword = 0;
+		    memcpy(&xword, chkhead, data[ndata].size & 3);
+		    *checksum += xword;
 		}
 	    } else {
 		error
@@ -908,30 +947,59 @@ int main(int argc, char *argv[])
 	    }
 	}
 
-    } else if (partinfo) {
+	ndata++;
+    }
+
+    if (partinfo && (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR)) {
 	/* Actually read the boot sector */
 	/* Pick the first buffer that isn't already in use */
-	if (!(boot_sector = read_sector(partinfo->start_lba))) {
+	if (!(data[ndata].data = read_sector(partinfo->start_lba))) {
 	    error("Cannot read boot sector\n");
 	    goto bail;
 	}
-    }
-
-    if (!opt.loadfile) {
-	if (*(uint16_t *) ((char *)boot_sector + boot_size - 2) != 0xaa55) {
-	    error
-		("Boot sector signature not found (unbootable disk/partition?)\n");
+	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;
 	}
+
+	/*
+	 * 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 (opt.cmldr) {
+	    memcpy((char *)data[ndata].data+3, cmldr_signature,
+		   sizeof cmldr_signature);
+	}
+
+	/*
+	 * Modify the hidden sectors (partition offset) copy in memory;
+	 * 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) =
+		part_info->start_lba;
+	}
+
+	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;
-	memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
     }
 
-    do_boot(boot_sector, boot_size, &regs);
+    do_boot(data, ndata, &regs);
 
   bail:
     return 255;



More information about the Syslinux-commits mailing list