diff options
author | H. Peter Anvin <hpa@zytor.com> | 2010-02-13 14:45:42 -0800 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2010-02-13 14:45:42 -0800 |
commit | 033fb123375ce6af905dcb2408fa0df74960e066 (patch) | |
tree | dba52fcad2d80a00b406bb00aaac2f46c29c3ff9 | |
parent | f38420bf678cfa556a6a5c5591668d30063d4c15 (diff) | |
download | syslinux-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.c | 200 |
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, ®s); + do_boot(data, ndata, ®s); bail: return 255; |