[syslinux] [PATCH 4/4] chain.c: New functionality
Michal Soltys
soltys at ziu.info
Mon Jul 26 15:50:35 PDT 2010
1) Updated seg and segbs options to control load and jump addresses precisely.
2) Carefully check overlapping regions.
3) bsnomap option
4) Update all win/dos related options.
5) setgeometry option
6) setdrive[@<offset>] option
7) setbpb shortcut option
8) writebs, filebpb options
Signed-off-by: Michal Soltys <soltys at ziu.info>
---
com32/modules/chain.c | 498 ++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 389 insertions(+), 109 deletions(-)
diff --git a/com32/modules/chain.c b/com32/modules/chain.c
index 6c3bb64..82654cf 100644
--- a/com32/modules/chain.c
+++ b/com32/modules/chain.c
@@ -45,12 +45,28 @@
*
* Options:
*
+ * segbs=<seg[:off[:ip]]>
+ * main control of where the sector (boot sector or mbr) should be loaded
+ * at, and where to actually jump. Default value is 0:7C00:7C00. Numbers
+ * are parsed as *HEX* values. Of course, if you set custom segbs without
+ * specifying some of the values (or leaving empty field near colon),
+ * unspecified ones are assumed 0. Overall, sector is loaded at seg:off,
+ * and the jump is made to seg:ip.
+ *
+ * seg=<seg[:off[:ip]]>
+ * similary to segbs option, this controls where to load file (if
+ * applicable - see file= option and derivatives). File takes precedence
+ * over boot sector, so in case their areas overlap, only file will be
+ * loaded.
+ *
+ * bsnomap
+ * load sector, but don't map it into real memory before chainloading.
+ * Useful for fixing BPB values in DOS cases.
+ *
* file=<loader>
* loads the file <loader> **from the Syslinux filesystem**
- * instead of loading the boot sector.
- *
- * seg=<segment>
- * loads at and jumps to <seg>:0000 instead of 0000:7C00.
+ * Boot sector will be loaded as well, if possible. Then handover area
+ * will be prepared - again - if possible.
*
* isolinux=<loader>
* chainload another version/build of the ISOLINUX bootloader and patch
@@ -59,26 +75,58 @@
* when you want more than one ISOLINUX per CD/DVD.
*
* ntldr=<loader>
- * equivalent to seg=0x2000 file=<loader> sethidden,
- * used with WinNT's loaders
+ * equivalent of file=<loader> seg=2000 setbpb setdrive writebs,
+ * used with WinNT's loaders. This is a bit interesting, actually
+ * required BPB adjustments are:
+ *
+ * - valid drive unit (which also has to be 0x80)
+ * - sethidden value in memory matching the one written to the disk
+ *
+ * Geometry settings stored in boot sector seems to be ignored, "hidden
+ * sectors" /can/ be junk, as long as on-disk and in-memory values match.
+ * To boot from other disks, you will have to use swap option. If by some
+ * miracle you still have some NTish system on FAT32, add setdrive at 40 to
+ * override default 0x24.
+ *
+ * *NOTE* though - if you plan to boot windows directly through its
+ * bootsector (without loading file), you will need ALL BPB fields valid
+ * and written to the disk (for partitions starting under 8GB, bootsector
+ * and possiby $Boot insist on using CHS functions ...), e.g.
+ * chain.c32 hd2 2 setbpb setdrive swap writebs.
*
* 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
+ * same as ntldr=<loader> with "cmdcons\0" written to
+ * the system name field in the boot sector
*
* freedos=<loader>
- * equivalent to seg=0x60 file=<loader> sethidden,
- * used with FreeDOS' kernel.sys.
+ * equivalent to file=<loader> seg=60 setbpb writebs bsnomap,
+ * used with FreeDOS' kernel.sys. FreeDOS requires proper geometry written
+ * to the disk. BPB's fields "hidden sectors" and "drive unit" seem to be
+ * ignored - FreeDOS rescans all fixed disks and assigns drive letters
+ * accordingly.
+ *
+ * If you want C: to match the disk you're booting from, you will likely
+ * have to use swap option - possibly with hide option, if you have other
+ * primary partitions before the one you're booting from. You can add
+ * appropriate setdrive@ option to keep things nice and tidy, depending on
+ * your filesystem.
*
* msdos=<loader>
* pcdos=<loader>
- * equivalent to seg=0x70 file=<loader> sethidden,
- * used with DOS' io.sys.
+ * equivalent to file=<loader> seg=70 setbpb writebs bsnomap,
+ * used with DOS' io.sys. Comments the same as in freedos= case.
+ * TODO: test with live M$ dos system(s), not clones, to check exact
+ * behaviour.
+ *
+ * msdos7=<loader>
+ * equivalent to file=<loader> seg=70::200 setbpb writebs bsnomap,
+ * DOS 7+ bootsectors jump to 0x200 after loading io.sys, instead of 0 as
+ * it was in pre-win95 age.
*
* grub=<loader>
- * same as seg=0x800 file=<loader> & jumping to seg 0x820,
- * used with GRUB Legacy stage2 files.
+ * same as file=<loader> seg=800::200, used with GRUB Legacy stage2
+ * files.
*
* grubcfg=<filename>
* set an alternative config filename in stage2 of Grub Legacy,
@@ -95,11 +143,38 @@
* 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.
+ * is converted the other way. Currently this works only within
+ * boundaries of primary mbr (partitions 1 - 4).
*
* sethidden
- * update the "hidden sectors" (partition offset) field in a
- * FAT/NTFS boot sector.
+ * update the "hidden sectors" (partition offset) field in a FAT/NTFS boot
+ * sector.
+ *
+ * setgeometry
+ * update the "sectors per track" and "heads" fields in a FAT/NTFS boot
+ * sector.
+ *
+ * setdrive[@<offset>]
+ * update the "drive unit" field in a FAT/NTFS boot sector. Offset should
+ * be either 0x24 (FAT/NTFS excluding FAT32) or 0x40 (FAT32 only) -
+ * chainloader won't accept other values. Offset is parsed as a *HEX*
+ * number. Offsetless value defaults to 0x24.
+ *
+ * setbpb
+ * A shortcut that enables set{hidden,geometry} options;
+ * setdrive is not covered by this shortcut, due to the ambiguity.
+ *
+ * writebs
+ * Write updated boot sector to the disk. This is performed only
+ * if some of the BPB's fields actually changed.
+ *
+ * filebpb
+ * An option that let loaded file be treated as BPB compatible. If any of
+ * the previous set* options is specified and file is being loaded, BPB at
+ * appropriate offsets will be adjusted accordingly. Obviously, writebs
+ * options is ignored for file. It can be used for crude sort-of emulation
+ * of syslinux's native .BSS capability, where BPB patching is limited only
+ * to options specified by set* (but it reflects actual BIOS imaginations).
*
* keeppxe
* keep the PXE and UNDI stacks in memory (PXELINUX only).
@@ -120,11 +195,19 @@
#include <syslinux/video.h>
#define SECTOR 512 /* bytes/sector */
+#define ADDRMAX 0x9F000
+#define ADDRMIN 0x500
static struct options {
const char *loadfile;
+ uint32_t file_lin;
+ uint16_t file_seg;
+ uint16_t file_ip;
+ uint32_t sect_lin;
+ uint16_t sect_seg;
+ uint16_t sect_ip;
+ uint32_t drvoff;
uint16_t keeppxe;
- uint16_t seg;
bool isolinux;
bool cmldr;
bool grub;
@@ -133,6 +216,10 @@ static struct options {
bool swap;
bool hide;
bool sethidden;
+ bool setgeometry;
+ bool writebs;
+ bool filebpb;
+ bool bsnomap;
} opt;
struct data_area {
@@ -173,8 +260,8 @@ struct diskinfo {
int disk;
int ebios; /* EBIOS supported on this disk */
int cbios; /* CHS geometry is valid */
- int head;
- int sect;
+ unsigned int head;
+ unsigned int sect;
} disk_info;
static int get_disk_params(int disk)
@@ -1272,89 +1359,158 @@ Usage: chain.c32 [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\
+ chain.c32 fs [options]\n\n\
+Options:\n\
+ file=<loader> Load and execute file; boot sector is also\n\
+ loaded if possible\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\
+ msdos7=<loader> Load MS-DOS 7+ IO.SYS\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=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\
+ segbs=<s[:o[:i]]> Load boot sector at <s:o>, jump to <s:i>;\n\
+ file takes precedence over boot sector, if loaded\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\
+ setgeometry Set the FAT/NTFS sectors per track and heads fields\n\
+ setdrive@<offset> Set the FAT/NTFS drive unit field at <offset>;\n\
+ setdrive option alone defaults to 0x24\n\
+ setbpb Enable set{hidden,geometry} options\n\
+ writebs Write updated boot sector to the disk\n\
+ filebpb Also mangle file with bpb options\n\
+ bsnomap Don't map boot sector into real memory.\n\
+ keeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\n\
See syslinux/com32/modules/chain.c for more information\n";
error(usage);
}
-int main(int argc, char *argv[])
+/* Adjust BPB options according to chosen options */
+
+static void mangle_bpb(void *bootr, uint32_t sec, uint8_t drv)
{
- struct mbr *mbr = NULL;
+ if(opt.setgeometry && disk_info.cbios)
+ *(uint32_t *)((char *)bootr + 0x18) = (disk_info.head << 16 ) | disk_info.sect;
+ if(opt.sethidden)
+ *(uint32_t *)((char *)bootr + 0x1C) = sec;
+ if(opt.drvoff < 512)
+ *(uint8_t *)((char *)bootr + opt.drvoff) = (opt.swap ? drv & 0x80 : drv);
+}
+
+/* Convert seg:off:ip values into numerical seg:linear_address:ip */
+
+static int segseq2vals(char *ptr, uint16_t *seg, uint32_t *lin, uint16_t *ip)
+{
+ uint32_t segval = 0, offval = 0, ipval = 0, val;
char *p;
- struct disk_part_iter *cur_part = NULL;
- void *sect_area = NULL, *file_area = NULL;
- struct part_entry *hand_area = NULL;
- struct syslinux_rm_regs regs;
- char *drivename, *partition;
- 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];
- int ndata = 0;
- addr_t load_base;
- static const char cmldr_signature[8] = "cmdcons";
+ segval = strtoul(ptr, &p, 16);
+ if(*p == ':')
+ offval = strtoul(p+1, &p, 16);
+ if(*p == ':')
+ ipval = strtoul(p+1, NULL, 16);
- openconsole(&dev_null_r, &dev_stdcon_w);
+ offval = (segval << 4) + offval;
- drivename = "boot";
- partition = NULL;
+ if (offval < ADDRMIN || offval > ADDRMAX) {
+ error("Invalid seg or segbs address.\n");
+ goto bail;
+ }
- /* Prepare the register set */
- memset(®s, 0, sizeof regs);
+ val = (segval << 4) + ipval;
+
+ if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
+ error("Invalid seg or segbs ip value.\n");
+ goto bail;
+ }
+
+ if(seg)
+ *seg = (uint16_t)segval;
+ if(lin)
+ *lin = offval;
+ if(ip)
+ *ip = (uint16_t)ipval;
+
+ return 0;
+
+bail:
+ return -1;
+}
+
+static int parse_args(int argc, char *argv[], char **drivename, char **partition)
+{
+ uint32_t val;
+ int i;
+ char *p;
+
+ *drivename = "boot";
+ *partition = NULL;
for (i = 1; i < argc; i++) {
if (!strncmp(argv[i], "file=", 5)) {
opt.loadfile = argv[i] + 5;
} else if (!strncmp(argv[i], "seg=", 4)) {
- uint32_t segval = strtoul(argv[i] + 4, NULL, 0);
- if (segval < 0x50 || segval > 0x9f000) {
- error("Invalid segment\n");
+ if(segseq2vals(argv[i] + 4, &opt.file_seg, &opt.file_lin, &opt.file_ip))
+ goto bail;
+ } else if (!strncmp(argv[i], "segbs=", 5)) {
+ if(segseq2vals(argv[i] + 6, &opt.sect_seg, &opt.sect_lin, &opt.sect_ip))
+ goto bail;
+ if(opt.sect_lin + SECTOR > ADDRMAX) {
+ error("Sector's address too big.\n");
goto bail;
}
- opt.seg = segval;
} else if (!strncmp(argv[i], "isolinux=", 9)) {
opt.loadfile = argv[i] + 9;
opt.isolinux = true;
} else if (!strncmp(argv[i], "ntldr=", 6)) {
- opt.seg = 0x2000; /* NTLDR wants this address */
+ opt.file_seg = 0x2000; /* NTLDR wants this address */
+ opt.file_lin = 0x20000;
+ opt.file_ip = 0;
opt.loadfile = argv[i] + 6;
opt.sethidden = true;
+ opt.setgeometry = true;
+ opt.drvoff = 0x24;
+ opt.writebs = true;
} else if (!strncmp(argv[i], "cmldr=", 6)) {
- opt.seg = 0x2000; /* CMLDR wants this address */
+ opt.file_seg = 0x2000; /* CMLDR wants this address */
+ opt.file_lin = 0x20000;
+ opt.file_ip = 0;
opt.loadfile = argv[i] + 6;
opt.cmldr = true;
opt.sethidden = true;
+ opt.setgeometry = true;
+ opt.drvoff = 0x24;
+ opt.writebs = true;
} else if (!strncmp(argv[i], "freedos=", 8)) {
- opt.seg = 0x60; /* FREEDOS wants this address */
+ opt.file_seg = 0x60; /* FREEDOS wants this address */
+ opt.file_lin = 0x600;
+ opt.file_ip = 0;
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.setgeometry = true;
+ opt.writebs = true;
+ opt.bsnomap = true;
+ } else if ( (val = 6, !strncmp(argv[i], "msdos=", 6) ||
+ !strncmp(argv[i], "pcdos=", 6)) ||
+ (val = 7, !strncmp(argv[i], "msdos7=", 7)) ) {
+ opt.file_seg = 0x70; /* MS-DOS 2.0+ wants this address */
+ opt.file_lin = 0x700;
+ opt.file_ip = val == 7 ? 0x200 : 0; /* MS-DOS 7.0+ wants this ip */
+ opt.loadfile = argv[i] + val;
opt.sethidden = true;
+ opt.setgeometry = true;
+ opt.writebs = true;
+ opt.bsnomap = true;
} else if (!strncmp(argv[i], "grub=", 5)) {
- opt.seg = 0x800; /* stage2 wants this address */
+ opt.file_seg = 0x800; /* stage2 wants this address */
+ opt.file_lin = 0x8000;
+ opt.file_ip = 0x200;
opt.loadfile = argv[i] + 5;
opt.grub = true;
} else if (!strncmp(argv[i], "grubcfg=", 8)) {
@@ -1376,6 +1532,40 @@ int main(int argc, char *argv[])
opt.sethidden = true;
} else if (!strcmp(argv[i], "nosethidden")) {
opt.sethidden = false;
+ } else if (!strcmp(argv[i], "setgeometry")) {
+ opt.setgeometry = true;
+ } else if (!strcmp(argv[i], "nosetgeometry")) {
+ opt.setgeometry = false;
+ } else if (!strcmp(argv[i], "bsnomap")) {
+ opt.bsnomap = true;
+ } else if (!strcmp(argv[i], "nobsnomap")) {
+ opt.bsnomap = false;
+ } else if (!strncmp(argv[i], "setdrive",8)) {
+ if(!argv[i][8])
+ val = 0x24;
+ else
+ val = strtoul(argv[i]+9, NULL, 16);
+ if (val >= SECTOR || !(val == 0x24 || val == 0x40)) {
+ error("Invalid setdrive offset.\n");
+ goto bail;
+ }
+ opt.drvoff = val;
+ } else if (!strcmp(argv[i], "nosetdrive")) {
+ opt.drvoff = SECTOR;
+ } else if (!strcmp(argv[i], "setbpb")) {
+ opt.sethidden = true;
+ opt.setgeometry = true;
+ } else if (!strcmp(argv[i], "nosetbpb")) {
+ opt.sethidden = false;
+ opt.setgeometry = false;
+ } else if (!strcmp(argv[i], "writebs")) {
+ opt.writebs = true;
+ } else if (!strcmp(argv[i], "nowritebs")) {
+ opt.writebs = false;
+ } else if (!strcmp(argv[i], "filebpb")) {
+ opt.filebpb = true;
+ } else if (!strcmp(argv[i], "nofilebpb")) {
+ opt.filebpb = false;
} else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
&& argv[i][1] == 'd')
|| !strncmp(argv[i], "mbr:", 4)
@@ -1387,14 +1577,14 @@ int main(int argc, char *argv[])
|| !strcmp(argv[i], "boot")
|| !strncmp(argv[i], "boot,", 5)
|| !strcmp(argv[i], "fs")) {
- drivename = argv[i];
- p = strchr(drivename, ',');
+ *drivename = argv[i];
+ p = strchr(*drivename, ',');
if (p) {
*p = '\0';
- partition = p + 1;
+ *partition = p + 1;
} else if (argv[i + 1] && argv[i + 1][0] >= '0'
&& argv[i + 1][0] <= '9') {
- partition = argv[++i];
+ *partition = argv[++i];
}
} else {
usage();
@@ -1407,12 +1597,59 @@ int main(int argc, char *argv[])
goto bail;
}
- if (opt.seg) {
- regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
+ return 0;
+bail:
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct mbr *mbr = NULL;
+ struct disk_part_iter *cur_part = NULL;
+ void *sect_area = NULL, *file_area = NULL;
+ struct part_entry *hand_area = NULL;
+ void *cmp_buf = NULL; /* compare buffer for bpb mangling & writing */
+
+ struct syslinux_rm_regs regs;
+ char *drivename, *partition;
+ int hd, drive, whichpart = 0; /* MBR by default */
+ 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];
+ int ndata = 0, file_didx = -1, sect_didx = -1;
+ static const char cmldr_signature[8] = "cmdcons";
+
+ openconsole(&dev_null_r, &dev_stdcon_w);
+
+ /* Prepare the register set */
+ memset(®s, 0, sizeof regs);
+
+ /* Prepare and set non-0 default values */
+ memset(&opt, 0, sizeof(opt));
+ opt.file_lin = opt.sect_lin = opt.file_ip = opt.sect_ip = 0x7C00;
+ opt.drvoff = SECTOR; /* offset outside sector means turned off */
+
+ if(parse_args(argc, argv, &drivename, &partition))
+ goto bail;
+
+ /* set initial registry values, file takes precedence */
+
+ if(opt.loadfile) {
+ regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.file_seg;
+ regs.ip = opt.file_ip;
} else {
- regs.ip = regs.esp.l = 0x7c00;
+ regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.sect_seg;
+ regs.ip = opt.sect_ip;
}
+ /* if handover address is default, set sp as well */
+
+ if(regs.ip == 0x7C00 && !regs.cs)
+ regs.esp.l = 0x7C00;
+
hd = 0;
if (!strncmp(drivename, "mbr", 3)) {
drive = find_disk(strtoul(drivename + 4, NULL, 0));
@@ -1521,18 +1758,20 @@ int main(int argc, char *argv[])
error("WARNING: failed to write MBR for 'hide'\n");
}
- /* Do the actual chainloading */
- load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
-
+ /* Chainload the file if required */
if (opt.loadfile) {
fputs("Loading the boot file...\n", stdout);
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;
+ if (opt.file_lin + data[ndata].size > ADDRMAX) {
+ error("File too large to load at this address\n");
+ goto bail;
+ }
+ data[ndata].base = opt.file_lin;
file_area = (void *)data[ndata].data;
- load_base = 0x7c00; /* If we also load a boot sector */
+ file_didx = ndata;
/* Create boot info table: needed when you want to chainload
another version of ISOLINUX (or another bootlaoder that needs
@@ -1546,7 +1785,7 @@ int main(int argc, char *argv[])
/* Boot info table info (integers in little endian format)
Offset Name Size Meaning
- 8 bi_pvd 4 bytes LBA of primary volume descriptor
+ 8 bi_pvd 4 bytes LBA of primary volume descriptor
12 bi_file 4 bytes LBA of boot file
16 bi_length 4 bytes Boot file length in bytes
20 bi_csum 4 bytes 32-bit checksum
@@ -1636,7 +1875,7 @@ int main(int argc, char *argv[])
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");
+ ("The file specified by grub=<loader> is too small to be stage2 of GRUB Legacy.\n");
goto bail;
}
@@ -1653,9 +1892,6 @@ int main(int argc, char *argv[])
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.
@@ -1700,11 +1936,18 @@ int main(int argc, char *argv[])
}
}
+ if(opt.filebpb && cur_part)
+ mangle_bpb(data[ndata].data, (uint32_t)cur_part->lba_data, regs.edx.b[0]);
ndata++;
}
- if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
- /* Actually read the boot sector */
+ /* Check if loaded file doesn't overlap with boot sector */
+ if (file_didx < 0 || opt.bsnomap ||
+ data[file_didx].base + data[file_didx].size <= opt.sect_lin ||
+ opt.sect_lin + SECTOR <= data[file_didx].base
+ ) {
+
+ /* Actually read the boot sector (or point to the mbr) */
if (!cur_part) {
data[ndata].data = mbr;
} else if (!(data[ndata].data = read_sectors(cur_part->lba_data, 1))) {
@@ -1714,7 +1957,9 @@ int main(int argc, char *argv[])
sect_area = (void *)data[ndata].data;
data[ndata].size = SECTOR;
- data[ndata].base = load_base;
+ data[ndata].base = opt.sect_lin;
+ if(!opt.bsnomap)
+ sect_didx = ndata;
if (!opt.loadfile) {
const struct mbr *br =
@@ -1726,29 +1971,47 @@ int main(int argc, char *argv[])
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 (cur_part && 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.
+ * Adjust some of the boot sector fields of FAT/NTFS filesystems - if
+ * applicable and requested by set{hidden,geometry,drive} or cmldr=
*/
- if (cur_part && opt.sethidden) {
- *(uint32_t *) ((char *)data[ndata].data + 28) = cur_part->lba_data;
- }
- ndata++;
+ if (cur_part) {
+ if (!(cmp_buf = malloc(SECTOR))) {
+ error("Could not allocate sector-compare buffer.\n");
+ goto bail;
+ }
+ memcpy(cmp_buf, data[ndata].data, SECTOR);
+
+ mangle_bpb((char*)data[ndata].data, (uint32_t)cur_part->lba_data, regs.edx.b[0]);
+
+ /*
+ * Write adjusted boot sector to the disk, but only if at least one
+ * of the fields changed. It must be done before cmldr=.
+ */
+ if (opt.writebs && memcmp(cmp_buf, data[ndata].data, SECTOR)) {
+ if (write_verify_sector(cur_part->lba_data, data[ndata].data)) {
+ error("Cannot write updated boot sector.\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 boot sector of the partition.
+ */
+ if (opt.cmldr)
+ memcpy((char *)data[ndata].data + 3, cmldr_signature, sizeof cmldr_signature);
+
+ }
+ if(!opt.bsnomap)
+ ndata++;
}
if (cur_part) {
+ data[ndata].data = NULL;
if (cur_part->next == next_gpt_part) {
/* Do GPT hand-over, if applicable (as per syslinux/doc/gpt.txt) */
/* Look at the GPT partition */
@@ -1792,7 +2055,6 @@ int main(int argc, char *argv[])
data[ndata].base = 0x7be;
data[ndata].size = synth_size;
data[ndata].data = (void *)hand_area;
- ndata++;
regs.esi.w[0] = 0x7be;
dprintf("GPT handover:\n");
@@ -1814,12 +2076,30 @@ int main(int argc, char *argv[])
data[ndata].base = 0x7be;
data[ndata].size = sizeof(struct part_entry);
data[ndata].data = (void *)hand_area;
- ndata++;
regs.esi.w[0] = 0x7be;
dprintf("MBR handover:\n");
mbr_part_dump(hand_area);
}
+
+ if(data[ndata].data) {
+ /* We have to make sure, that handover data doesn't overlap with the file and/or the boot sector.
+ * For example, part of FreeDOS kernel loaded at 0x600 would conflict with the handover data.
+ * Still, in case of file=, the handover data is generally not needed (not always though - e.g.
+ * recovery console), so we do the following check and increase ndata if there're no conflicts.
+ */
+ if (( file_didx < 0 || /* check against file */
+ data[file_didx].base + data[file_didx].size <= data[ndata].base ||
+ data[ndata].base + data[ndata].size <= data[file_didx].base
+ ) && ( sect_didx < 0 || /* check against sector */
+ data[sect_didx].base + data[sect_didx].size <= data[ndata].base ||
+ data[ndata].base + data[ndata].size <= data[sect_didx].base
+ )) {
+ ndata++;
+ } else {
+ dprintf("Handover ignored due to overlapping regions.\n");
+ }
+ }
}
do_boot(data, ndata, ®s);
--
1.6.3.1
More information about the Syslinux
mailing list