blob: 658a45cae8ceb0f45f1a8619f866e0fcdbe0809a [file] [log] [blame]
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "chain.h"
#include "utility.h"
#include "options.h"
struct options opt;
static int soi_s2n(char *ptr, unsigned int *seg,
unsigned int *off,
unsigned int *ip,
unsigned int def)
{
unsigned int segval = 0, offval, ipval, val;
char *p;
offval = def;
ipval = def;
segval = strtoul(ptr, &p, 0);
if (p[0] == ':' && p[1] && p[1] != ':')
offval = strtoul(p+1, &p, 0);
if (p[0] == ':' && p[1] && p[1] != ':')
ipval = strtoul(p+1, NULL, 0);
val = (segval << 4) + offval;
if (val < ADDRMIN || val > ADDRMAX) {
error("Invalid seg:off:* address specified..\n");
goto bail;
}
val = (segval << 4) + ipval;
if (ipval > 0xFFFE || val < ADDRMIN || val > ADDRMAX) {
error("Invalid seg:*:ip address specified.\n");
goto bail;
}
if (seg)
*seg = segval;
if (off)
*off = offval;
if (ip)
*ip = ipval;
return 0;
bail:
return -1;
}
static void usage(void)
{
unsigned int i;
static const char key[] = "Press any key...\n";
static const char *const usage[] = {
"\
Usage:\n\
chain.c32 [options]\n\
chain.c32 {fd|hd}<disk#>{,| }[<part#>] [options]\n\
chain.c32 mbr{:|=}<id>{,| }[<part#>] [options]\n\
chain.c32 guid{:|=}<guid>{,| }[<part#>] [options]\n\
chain.c32 label{:|=}<label> [<part#>] [options]\n\
chain.c32 boot{,| }[<part#>] [options]\n\
chain.c32 fs [options]\n\
", "\
\nOptions ('no' prefix specifies default value):\n\
sect[=<s[:o[:i]]>] Load sector at <s:o>, jump to <s:i>\n\
- defaults to 0:0x7C00:0x7C00\n\
- ommited o/i values default to 0\n\
maps Map loaded sector into real memory\n\
nosetbpb Fix BPB fields in loaded sector\n\
nofilebpb Apply 'setbpb' to loaded file\n\
nosave Write adjusted sector back to disk\n\
hand Prepare handover area\n\
nohptr Force ds:si and ds:bp to point to handover area\n\
noswap Swap drive numbers, if bootdisk is not fd0/hd0\n\
nohide Disable all hide variations (also the default)\n\
hide Hide primary partitions, unhide selected partition\n\
hideall Hide *all* partitions, unhide selected partition\n\
unhide Unhide primary partitions\n\
unhideall Unhide *all* partitions\n\
nofixchs Walk *all* partitions and fix E/MBRs' chs values\n\
nokeeppxe Keep the PXE and UNDI stacks in memory (PXELINUX)\n\
nowarn Wait for a keypress to continue chainloading\n\
- useful to see emited warnings\n\
nobreak Actually perform the chainloading\n\
", "\
\nOptions continued ...\n\
file=<file> Load and execute <file>\n\
seg=<s[:o[:i]]> Load file at <s:o>, jump to <s:i>\n\
- defaults to 0:0x7C00:0x7C00\n\
- ommited o/i values default to 0\n\
isolinux=<loader> Load another version of ISOLINUX\n\
ntldr=<loader> Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n\
reactos=<loader> Load ReactOS's loader\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 2.xx - 6.xx IO.SYS\n\
msdos7=<loader> Load MS-DOS 7+ IO.SYS\n\
pcdos=<loader> Load PC-DOS IBMBIO.COM\n\
drmk=<loader> Load DRMK DELLBIO.BIN\n\
grub=<loader> Load GRUB Legacy stage2\n\
grubcfg=<filename> Set alternative config filename for GRUB Legacy\n\
grldr=<loader> Load GRUB4DOS grldr\n\
bss=<filename> Emulate syslinux's BSS\n\
bs=<filename> Emulate syslinux's BS\n\
\nPlease see doc/chain.txt for the detailed documentation.\n\
"
};
for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) {
if (i) {
error(key);
wait_key();
}
error(usage[i]);
}
}
void opt_set_defs(void)
{
memset(&opt, 0, sizeof(opt));
opt.sect = true; /* by def. load sector */
opt.maps = true; /* by def. map sector */
opt.hand = true; /* by def. prepare handover */
opt.brkchain = false; /* by def. do chainload */
opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
opt.drivename = "boot";
#ifdef DEBUG
opt.warn = true;
#endif
}
int opt_parse_args(int argc, char *argv[])
{
int i;
unsigned int v;
char *p;
for (i = 1; i < argc; i++) {
if (!strncmp(argv[i], "file=", 5)) {
opt.file = argv[i] + 5;
} else if (!strcmp(argv[i], "nofile")) {
opt.file = NULL;
} else if (!strncmp(argv[i], "seg=", 4)) {
if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip, 0))
goto bail;
} else if (!strncmp(argv[i], "bss=", 4)) {
opt.file = argv[i] + 4;
opt.bss = true;
opt.maps = false;
opt.setbpb = true;
/* opt.save = true; */
} else if (!strncmp(argv[i], "bs=", 3)) {
opt.file = argv[i] + 3;
opt.sect = false;
opt.filebpb = true;
} else if (!strncmp(argv[i], "isolinux=", 9)) {
opt.file = argv[i] + 9;
opt.isolinux = true;
opt.hand = false;
opt.sect = false;
} else if (!strncmp(argv[i], "ntldr=", 6)) {
opt.fseg = 0x2000; /* NTLDR wants this address */
opt.foff = 0;
opt.fip = 0;
opt.file = argv[i] + 6;
opt.setbpb = true;
/* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "reactos=", 8)) {
/*
* settings based on commit
* ad4cf1470977f648ee1dd45e97939589ccb0393c
* note, conflicts with:
* http://reactos.freedoors.org/Reactos%200.3.13/ReactOS-0.3.13-REL-src/boot/freeldr/notes.txt
*/
opt.fseg = 0;
opt.foff = 0x8000;
opt.fip = 0x8100;
opt.file = argv[i] + 8;
opt.setbpb = true;
/* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "cmldr=", 6)) {
opt.fseg = 0x2000; /* CMLDR wants this address */
opt.foff = 0;
opt.fip = 0;
opt.file = argv[i] + 6;
opt.cmldr = true;
opt.setbpb = true;
/* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "freedos=", 8)) {
opt.fseg = 0x60; /* FREEDOS wants this address */
opt.foff = 0;
opt.fip = 0;
opt.sseg = 0x1FE0;
opt.file = argv[i] + 8;
opt.setbpb = true;
/* opt.save = true; */
opt.hand = false;
} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
!strncmp(argv[i], "pcdos=", v)) ||
(v = 7, !strncmp(argv[i], "msdos7=", v)) ) {
opt.fseg = 0x70; /* MS-DOS 2.00 .. 6.xx wants this address */
opt.foff = 0;
opt.fip = v == 7 ? 0x200 : 0; /* MS-DOS 7.0+ wants this ip */
opt.sseg = 0x8000;
opt.file = argv[i] + v;
opt.setbpb = true;
/* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "drmk=", 5)) {
opt.fseg = 0x70; /* DRMK wants this address */
opt.foff = 0;
opt.fip = 0;
opt.sseg = 0x2000;
opt.soff = 0;
opt.sip = 0;
opt.file = argv[i] + 5;
/* opt.drmk = true; */
opt.setbpb = true;
/* opt.save = true; */
opt.hand = false;
} else if (!strncmp(argv[i], "grub=", 5)) {
opt.fseg = 0x800; /* stage2 wants this address */
opt.foff = 0;
opt.fip = 0x200;
opt.file = argv[i] + 5;
opt.grub = true;
opt.hand = false;
opt.sect = false;
} else if (!strncmp(argv[i], "grubcfg=", 8)) {
opt.grubcfg = argv[i] + 8;
} else if (!strncmp(argv[i], "grldr=", 6)) {
opt.file = argv[i] + 6;
opt.grldr = true;
opt.hand = false;
opt.sect = false;
} else if (!strcmp(argv[i], "keeppxe")) {
opt.keeppxe = 3;
} else if (!strcmp(argv[i], "nokeeppxe")) {
opt.keeppxe = 0;
} else if (!strcmp(argv[i], "maps")) {
opt.maps = true;
} else if (!strcmp(argv[i], "nomaps")) {
opt.maps = false;
} else if (!strcmp(argv[i], "hand")) {
opt.hand = true;
} else if (!strcmp(argv[i], "nohand")) {
opt.hand = false;
} else if (!strcmp(argv[i], "hptr")) {
opt.hptr = true;
} else if (!strcmp(argv[i], "nohptr")) {
opt.hptr = false;
} else if (!strcmp(argv[i], "swap")) {
opt.swap = true;
} else if (!strcmp(argv[i], "noswap")) {
opt.swap = false;
} else if (!strcmp(argv[i], "nohide")) {
opt.hide = 0;
} else if (!strcmp(argv[i], "hide")) {
opt.hide = 1; /* 001b */
} else if (!strcmp(argv[i], "hideall")) {
opt.hide = 2; /* 010b */
} else if (!strcmp(argv[i], "unhide")) {
opt.hide = 5; /* 101b */
} else if (!strcmp(argv[i], "unhideall")) {
opt.hide = 6; /* 110b */
} else if (!strcmp(argv[i], "setbpb")) {
opt.setbpb = true;
} else if (!strcmp(argv[i], "nosetbpb")) {
opt.setbpb = false;
} else if (!strcmp(argv[i], "filebpb")) {
opt.filebpb = true;
} else if (!strcmp(argv[i], "nofilebpb")) {
opt.filebpb = false;
} else if (!strncmp(argv[i], "sect=", 5) ||
!strcmp(argv[i], "sect")) {
if (argv[i][4]) {
if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip, 0))
goto bail;
}
opt.sect = true;
} else if (!strcmp(argv[i], "nosect")) {
opt.sect = false;
opt.maps = false;
} else if (!strcmp(argv[i], "save")) {
opt.save = true;
} else if (!strcmp(argv[i], "nosave")) {
opt.save = false;
} else if (!strcmp(argv[i], "fixchs")) {
opt.fixchs = true;
} else if (!strcmp(argv[i], "nofixchs")) {
opt.fixchs = false;
} else if (!strcmp(argv[i], "warn")) {
opt.warn = true;
} else if (!strcmp(argv[i], "nowarn")) {
opt.warn = false;
} else if (!strcmp(argv[i], "nobreak")) {
opt.brkchain = false;
} else if (!strcmp(argv[i], "break")) {
opt.brkchain = true;
opt.file = NULL;
opt.maps = false;
opt.hand = 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)
|| !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)
|| !strcmp(argv[i], "fs")) {
opt.drivename = argv[i];
if (strncmp(argv[i], "label", 5))
p = strchr(opt.drivename, ',');
else
p = NULL;
if (p) {
*p = '\0';
opt.partition = p + 1;
} else if (argv[i + 1] && argv[i + 1][0] >= '0'
&& argv[i + 1][0] <= '9') {
opt.partition = argv[++i];
}
} else {
usage();
goto bail;
}
}
if (opt.grubcfg && !opt.grub) {
error("grubcfg=<filename> must be used together with grub=<loader>.\n");
goto bail;
}
#if 0
if ((!opt.maps || !opt.sect) && !opt.file) {
error("You have to load something.\n");
goto bail;
}
#endif
if (opt.filebpb && !opt.file) {
error("Option 'filebpb' requires a file.\n");
goto bail;
}
if (opt.save && !opt.sect) {
error("Option 'save' requires a sector.\n");
goto bail;
}
if (opt.setbpb && !opt.sect) {
error("Option 'setbpb' requires a sector.\n");
goto bail;
}
if (opt.maps && !opt.sect) {
error("Option 'maps' requires a sector.\n");
goto bail;
}
return 0;
bail:
return -1;
}
/* vim: set ts=8 sts=4 sw=4 noet: */