summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2010-02-13 14:45:42 -0800
committerH. Peter Anvin <hpa@zytor.com>2010-02-13 14:45:42 -0800
commit033fb123375ce6af905dcb2408fa0df74960e066 (patch)
treedba52fcad2d80a00b406bb00aaac2f46c29c3ff9
parentf38420bf678cfa556a6a5c5591668d30063d4c15 (diff)
downloadsyslinux-033fb123375ce6af905dcb2408fa0df74960e066.tar.gz
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@zytor.com>
-rw-r--r--com32/modules/chain.c200
1 files changed, 134 insertions, 66 deletions
diff --git a/com32/modules/chain.c b/com32/modules/chain.c
index 458ea890..a5597921 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;