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
soi_s2n(char * ptr,addr_t * seg,addr_t * off,addr_t * ip,addr_t def)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
usage(void)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
opt_set_defs(void)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
opt_parse_args(int argc,char * argv[])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