Home | History | Annotate | Download | only in chain
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
      4  *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
      5  *   Copyright 2010 Shao Miller
      6  *   Copyright 2010-2012 Michal Soltys
      7  *
      8  *   Permission is hereby granted, free of charge, to any person
      9  *   obtaining a copy of this software and associated documentation
     10  *   files (the "Software"), to deal in the Software without
     11  *   restriction, including without limitation the rights to use,
     12  *   copy, modify, merge, publish, distribute, sublicense, and/or
     13  *   sell copies of the Software, and to permit persons to whom
     14  *   the Software is furnished to do so, subject to the following
     15  *   conditions:
     16  *
     17  *   The above copyright notice and this permission notice shall
     18  *   be included in all copies or substantial portions of the Software.
     19  *
     20  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     21  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     22  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     23  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     24  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     25  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     26  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     27  *   OTHER DEALINGS IN THE SOFTWARE.
     28  *
     29  * ----------------------------------------------------------------------- */
     30 
     31 #include <syslinux/movebits.h>
     32 #include <stdint.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include "chain.h"
     36 #include "partiter.h"
     37 #include "utility.h"
     38 #include "options.h"
     39 
     40 struct options opt;
     41 
     42 static int soi_s2n(char *ptr,
     43 			addr_t *seg,
     44 			addr_t *off,
     45 			addr_t *ip,
     46 			addr_t def)
     47 {
     48     addr_t segval, offval, ipval, val;
     49     char *p;
     50 
     51     /* defaults */
     52     segval = 0;
     53     offval = def;
     54     ipval = def;
     55 
     56     segval = strtoul(ptr, &p, 0);
     57     if (p[0] == ':' && p[1] && p[1] != ':')
     58 	offval = strtoul(p+1, &p, 0);
     59     if (p[0] == ':' && p[1] && p[1] != ':')
     60 	ipval = strtoul(p+1, NULL, 0);
     61 
     62     /* verify if load address is within [dosmin, dosmax) */
     63     val = (segval << 4) + offval;
     64 
     65     if (val < dosmin || val >= dosmax) {
     66 	error("Invalid seg:off:* address specified.");
     67 	goto bail;
     68     }
     69 
     70     /*
     71      * verify if jump address is within [dosmin, dosmax) and offset is 16bit
     72      * sane
     73      */
     74     val = (segval << 4) + ipval;
     75 
     76     if (ipval > 0xFFFE || val < dosmin || val >= dosmax) {
     77 	error("Invalid seg:*:ip address specified.");
     78 	goto bail;
     79     }
     80 
     81     if (seg)
     82 	*seg = segval;
     83     if (off)
     84 	*off = offval;
     85     if (ip)
     86 	*ip  = ipval;
     87 
     88     return 0;
     89 bail:
     90     return -1;
     91 }
     92 
     93 static void usage(void)
     94 {
     95     size_t i;
     96     static const char *const usage[] = {
     97 "Usage:",
     98 "",
     99 "  disk + partition selection:",
    100 "        chain.c32 [options]",
    101 "        chain.c32 hd#[,#] [options]",
    102 "        chain.c32 fd#[,#] [options]",
    103 "        chain.c32 mbr=<id>[,#] [options]",
    104 "        chain.c32 guid=<guid>[,#] [options]",
    105 "        chain.c32 boot[,#] [options]",
    106 "",
    107 "  direct partition selection:",
    108 "        chain.c32 guid=<guid> [options]",
    109 "        chain.c32 label=<label> [options]",
    110 "        chain.c32 fs [options]",
    111 "",
    112 "You can use ':' instead of '=' and ' ' instead of ','.",
    113 "The default is 'boot,0'.",
    114 "",
    115 "Options:",
    116 "  sect[=<s[:o[:i]]>]   Load sector at <s:o>, jump to <s:i>",
    117 "                       - defaults to 0:0x7C00:0x7C00",
    118 "                       - omitted o/i values default to 0",
    119 "  maps                 Map loaded sector into real memory",
    120 "  setbpb               Fix BPB fields in loaded sector",
    121 "  filebpb              Apply 'setbpb' to loaded file",
    122 "  save                 Write adjusted sector back to disk",
    123 "  hand                 Prepare handover area",
    124 "  hptr                 Force ds:si and ds:bp to point to handover area",
    125 "  swap                 Swap drive numbers, if bootdisk is not fd0/hd0",
    126 "  nohide               Disable all hide variations (default)",
    127 "  hide                 Hide primary partitions, unhide selected partition",
    128 "  hideall              Hide *all* partitions, unhide selected partition",
    129 "  unhide               Unhide primary partitions",
    130 "  unhideall            Unhide *all* partitions",
    131 "  fixchs               Walk *all* partitions and fix E/MBRs' CHS values",
    132 "  keeppxe              Keep the PXE and UNDI stacks in memory (PXELINUX)",
    133 "  warn                 Wait for a keypress to continue chainloading",
    134 "  break                Don't chainload",
    135 "  strict[=<0|1|2>]     Set the level of strictness in sanity checks",
    136 "                       - strict w/o any value is the same as strict=2",
    137 "  relax                The same as strict=0",
    138 "  prefmbr              On hybrid MBR/GPT disks, prefer legacy layout",
    139 "",
    140 "  file=<file>          Load and execute <file>",
    141 "  seg=<s[:o[:i]]>      Load file at <s:o>, jump to <s:i>",
    142 "                       - defaults to 0:0x7C00:0x7C00",
    143 "                       - omitted o/i values default to 0",
    144 "  isolinux=<loader>    Load another version of ISOLINUX",
    145 "  ntldr=<loader>       Load Windows NTLDR, SETUPLDR.BIN or BOOTMGR",
    146 "  reactos=<loader>     Load ReactOS's loader",
    147 "  cmldr=<loader>       Load Recovery Console of Windows NT/2K/XP/2003",
    148 "  freedos=<loader>     Load FreeDOS KERNEL.SYS",
    149 "  msdos=<loader>       Load MS-DOS 2.xx - 6.xx IO.SYS",
    150 "  msdos7=<loader>      Load MS-DOS 7+ IO.SYS",
    151 "  pcdos=<loader>       Load PC-DOS IBMBIO.COM",
    152 "  drmk=<loader>        Load DRMK DELLBIO.BIN",
    153 "  grub=<loader>        Load GRUB Legacy stage2",
    154 "  grubcfg=<config>     Set alternative config filename for GRUB Legacy",
    155 "  grldr=<loader>       Load GRUB4DOS grldr",
    156 "  bss=<sectimage>      Emulate syslinux's BSS",
    157 "  bs=<sectimage>       Emulate syslinux's BS",
    158 "",
    159 "Please see doc/chain.txt for the detailed documentation."
    160 };
    161     for (i = 0; i < sizeof(usage)/sizeof(usage[0]); i++) {
    162 	if (i % 20 == 19) {
    163 	    puts("Press any key...");
    164 	    wait_key();
    165 	}
    166 	puts(usage[i]);
    167     }
    168 }
    169 
    170 void opt_set_defs(void)
    171 {
    172     memset(&opt, 0, sizeof opt);
    173     opt.sect = true;	    /* by def. load sector */
    174     opt.maps = true;	    /* by def. map sector */
    175     opt.hand = true;	    /* by def. prepare handover */
    176     opt.brkchain = false;   /* by def. do chainload */
    177     opt.piflags = PIF_STRICT;	/* by def. be strict, but ignore disk sizes */
    178     opt.foff = opt.soff = opt.fip = opt.sip = 0x7C00;
    179     opt.drivename = "boot";
    180 #ifdef DEBUG
    181     opt.warn = true;
    182 #endif
    183 }
    184 
    185 int opt_parse_args(int argc, char *argv[])
    186 {
    187     int i;
    188     size_t v;
    189     char *p;
    190 
    191     for (i = 1; i < argc; i++) {
    192 	if (!strncmp(argv[i], "file=", 5)) {
    193 	    opt.file = argv[i] + 5;
    194 	} else if (!strcmp(argv[i], "nofile")) {
    195 	    opt.file = NULL;
    196 	} else if (!strncmp(argv[i], "seg=", 4)) {
    197 	    if (soi_s2n(argv[i] + 4, &opt.fseg, &opt.foff, &opt.fip, 0))
    198 		goto bail;
    199 	} else if (!strncmp(argv[i], "bss=", 4)) {
    200 	    opt.file = argv[i] + 4;
    201 	    opt.bss = true;
    202 	    opt.maps = false;
    203 	    opt.setbpb = true;
    204 	} else if (!strncmp(argv[i], "bs=", 3)) {
    205 	    opt.file = argv[i] + 3;
    206 	    opt.sect = false;
    207 	    opt.filebpb = true;
    208 	} else if (!strncmp(argv[i], "isolinux=", 9)) {
    209 	    opt.file = argv[i] + 9;
    210 	    opt.isolinux = true;
    211 	    opt.hand = false;
    212 	    opt.sect = false;
    213 	} else if (!strncmp(argv[i], "ntldr=", 6)) {
    214 	    opt.fseg = 0x2000;  /* NTLDR wants this address */
    215 	    opt.foff = 0;
    216 	    opt.fip = 0;
    217 	    opt.file = argv[i] + 6;
    218 	    opt.setbpb = true;
    219 	    opt.hand = false;
    220 	} else if (!strncmp(argv[i], "reactos=", 8)) {
    221 	    /*
    222 	     * settings based on commit
    223 	     *   ad4cf1470977f648ee1dd45e97939589ccb0393c
    224 	     * note, conflicts with:
    225 	     *   http://reactos.freedoors.org/Reactos%200.3.13/ReactOS-0.3.13-REL-src/boot/freeldr/notes.txt
    226 	     */
    227 	    opt.fseg = 0;
    228 	    opt.foff = 0x8000;
    229 	    opt.fip = 0x8100;
    230 	    opt.file = argv[i] + 8;
    231 	    opt.setbpb = true;
    232 	    opt.hand = false;
    233 	} else if (!strncmp(argv[i], "cmldr=", 6)) {
    234 	    opt.fseg = 0x2000;  /* CMLDR wants this address */
    235 	    opt.foff = 0;
    236 	    opt.fip = 0;
    237 	    opt.file = argv[i] + 6;
    238 	    opt.cmldr = true;
    239 	    opt.setbpb = true;
    240 	    opt.hand = false;
    241 	} else if (!strncmp(argv[i], "freedos=", 8)) {
    242 	    opt.fseg = 0x60;    /* FREEDOS wants this address */
    243 	    opt.foff = 0;
    244 	    opt.fip = 0;
    245 	    opt.sseg = 0x1FE0;
    246 	    opt.file = argv[i] + 8;
    247 	    opt.setbpb = true;
    248 	    opt.hand = false;
    249 	} else if ( (v = 6, !strncmp(argv[i], "msdos=", v) ||
    250 		     !strncmp(argv[i], "pcdos=", v)) ||
    251 		    (v = 7, !strncmp(argv[i], "msdos7=", v)) ) {
    252 	    opt.fseg = 0x70;    /* MS-DOS 2.00 .. 6.xx wants this address */
    253 	    opt.foff = 0;
    254 	    opt.fip = v == 7 ? 0x200 : 0;  /* MS-DOS 7.0+ wants this ip */
    255 	    opt.sseg = 0x8000;
    256 	    opt.file = argv[i] + v;
    257 	    opt.setbpb = true;
    258 	    opt.hand = false;
    259 	} else if (!strncmp(argv[i], "drmk=", 5)) {
    260 	    opt.fseg = 0x70;    /* DRMK wants this address */
    261 	    opt.foff = 0;
    262 	    opt.fip = 0;
    263 	    opt.sseg = 0x2000;
    264 	    opt.soff = 0;
    265 	    opt.sip = 0;
    266 	    opt.file = argv[i] + 5;
    267 	    /* opt.drmk = true; */
    268 	    opt.setbpb = true;
    269 	    opt.hand = false;
    270 	} else if (!strncmp(argv[i], "grub=", 5)) {
    271 	    opt.fseg = 0x800;	/* stage2 wants this address */
    272 	    opt.foff = 0;
    273 	    opt.fip = 0x200;
    274 	    opt.file = argv[i] + 5;
    275 	    opt.grub = true;
    276 	    opt.hand = false;
    277 	    opt.sect = false;
    278 	} else if (!strncmp(argv[i], "grubcfg=", 8)) {
    279 	    opt.grubcfg = argv[i] + 8;
    280 	} else if (!strncmp(argv[i], "grldr=", 6)) {
    281 	    opt.file = argv[i] + 6;
    282 	    opt.grldr = true;
    283 	    opt.hand = false;
    284 	    opt.sect = false;
    285 	} else if (!strcmp(argv[i], "keeppxe")) {
    286 	    opt.keeppxe = 3;
    287 	} else if (!strcmp(argv[i], "nokeeppxe")) {
    288 	    opt.keeppxe = 0;
    289 	} else if (!strcmp(argv[i], "maps")) {
    290 	    opt.maps = true;
    291 	} else if (!strcmp(argv[i], "nomaps")) {
    292 	    opt.maps = false;
    293 	} else if (!strcmp(argv[i], "hand")) {
    294 	    opt.hand = true;
    295 	} else if (!strcmp(argv[i], "nohand")) {
    296 	    opt.hand = false;
    297 	} else if (!strcmp(argv[i], "hptr")) {
    298 	    opt.hptr = true;
    299 	} else if (!strcmp(argv[i], "nohptr")) {
    300 	    opt.hptr = false;
    301 	} else if (!strcmp(argv[i], "swap")) {
    302 	    opt.swap = true;
    303 	} else if (!strcmp(argv[i], "noswap")) {
    304 	    opt.swap = false;
    305 	} else if (!strcmp(argv[i], "nohide")) {
    306 	    opt.hide = HIDE_OFF;
    307 	} else if (!strcmp(argv[i], "hide")) {
    308 	    opt.hide = HIDE_ON;
    309 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    310 	} else if (!strcmp(argv[i], "hideall")) {
    311 	    opt.hide = HIDE_ON | HIDE_EXT;
    312 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    313 	} else if (!strcmp(argv[i], "unhide")) {
    314 	    opt.hide = HIDE_ON | HIDE_REV;
    315 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    316 	} else if (!strcmp(argv[i], "unhideall")) {
    317 	    opt.hide = HIDE_ON | HIDE_EXT | HIDE_REV;
    318 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    319 	} else if (!strcmp(argv[i], "setbpb")) {
    320 	    opt.setbpb = true;
    321 	} else if (!strcmp(argv[i], "nosetbpb")) {
    322 	    opt.setbpb = false;
    323 	} else if (!strcmp(argv[i], "filebpb")) {
    324 	    opt.filebpb = true;
    325 	} else if (!strcmp(argv[i], "nofilebpb")) {
    326 	    opt.filebpb = false;
    327 	} else if (!strncmp(argv[i], "sect=", 5) ||
    328 		   !strcmp(argv[i], "sect")) {
    329 	    if (argv[i][4]) {
    330 		if (soi_s2n(argv[i] + 5, &opt.sseg, &opt.soff, &opt.sip, 0))
    331 		    goto bail;
    332 	    }
    333 	    opt.sect = true;
    334 	} else if (!strcmp(argv[i], "nosect")) {
    335 	    opt.sect = false;
    336 	    opt.maps = false;
    337 	} else if (!strcmp(argv[i], "save")) {
    338 	    opt.save = true;
    339 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    340 	} else if (!strcmp(argv[i], "nosave")) {
    341 	    opt.save = false;
    342 	} else if (!strcmp(argv[i], "fixchs")) {
    343 	    opt.fixchs = true;
    344 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    345 	} else if (!strcmp(argv[i], "nofixchs")) {
    346 	    opt.fixchs = false;
    347 	} else if (!strcmp(argv[i], "relax") || !strcmp(argv[i], "nostrict")) {
    348 	    opt.piflags &= ~(PIF_STRICT | PIF_STRICTER);
    349 	} else if (!strcmp(argv[i], "norelax") || !strcmp(argv[i], "strict")) {
    350 	    opt.piflags |= PIF_STRICT | PIF_STRICTER;
    351 	} else if (!strncmp(argv[i], "strict=", 7)) {
    352 	    if (argv[i][7] < '0' || argv[i][7] > '2' || !argv[i][8]) {
    353 		error("Strict level must be 0, 1 or 2.");
    354 		goto bail;
    355 	    }
    356 	    opt.piflags &= ~(PIF_STRICT | PIF_STRICTER);
    357 	    switch (argv[i][7]) {
    358 		case '2': opt.piflags |= PIF_STRICTER;
    359 		case '1': opt.piflags |= PIF_STRICT; break;
    360 		default:;
    361 	    }
    362 	} else if (!strcmp(argv[i], "warn")) {
    363 	    opt.warn = true;
    364 	} else if (!strcmp(argv[i], "nowarn")) {
    365 	    opt.warn = false;
    366 	} else if (!strcmp(argv[i], "prefmbr")) {
    367 	    opt.piflags |= PIF_PREFMBR;
    368 	} else if (!strcmp(argv[i], "noprefmbr")) {
    369 	    opt.piflags &= ~PIF_PREFMBR;
    370 	} else if (!strcmp(argv[i], "nobreak")) {
    371 	    opt.brkchain = false;
    372 	} else if (!strcmp(argv[i], "break")) {
    373 	    opt.brkchain = true;
    374 	    opt.file = NULL;
    375 	    opt.maps = false;
    376 	    opt.hand = false;
    377 	} else if (((argv[i][0] == 'h' || argv[i][0] == 'f')
    378 		    && argv[i][1] == 'd')
    379 		   || !strncmp(argv[i], "mbr:", 4)
    380 		   || !strncmp(argv[i], "mbr=", 4)
    381 		   || !strncmp(argv[i], "guid:", 5)
    382 		   || !strncmp(argv[i], "guid=", 5)
    383 		   || !strncmp(argv[i], "label:", 6)
    384 		   || !strncmp(argv[i], "label=", 6)
    385 		   || !strcmp(argv[i], "boot")
    386 		   || !strncmp(argv[i], "boot,", 5)
    387 		   || !strcmp(argv[i], "fs")) {
    388 	    opt.drivename = argv[i];
    389 	    if (strncmp(argv[i], "label", 5))
    390 		p = strchr(opt.drivename, ',');
    391 	    else
    392 		p = NULL;
    393 	    if (p) {
    394 		*p = '\0';
    395 		opt.partition = p + 1;
    396 	    } else if (argv[i + 1] && argv[i + 1][0] >= '0'
    397 		    && argv[i + 1][0] <= '9') {
    398 		opt.partition = argv[++i];
    399 	    }
    400 	} else {
    401 	    usage();
    402 	    goto bail;
    403 	}
    404     }
    405 
    406     if (opt.grubcfg && !opt.grub) {
    407 	error("grubcfg=<filename> must be used together with grub=<loader>.");
    408 	goto bail;
    409     }
    410 
    411     if (opt.filebpb && !opt.file) {
    412 	error("Option 'filebpb' requires a file.");
    413 	goto bail;
    414     }
    415 
    416     if (opt.save && !opt.sect) {
    417 	error("Option 'save' requires a sector.");
    418 	goto bail;
    419     }
    420 
    421     if (opt.setbpb && !opt.sect) {
    422 	error("Option 'setbpb' requires a sector.");
    423 	goto bail;
    424     }
    425 
    426     if (opt.maps && !opt.sect) {
    427 	error("Option 'maps' requires a sector.");
    428 	goto bail;
    429     }
    430 
    431     return 0;
    432 bail:
    433     return -1;
    434 }
    435 
    436 /* vim: set ts=8 sts=4 sw=4 noet: */
    437