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