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
40struct options opt;
41
42static 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;
89bail:
90    return -1;
91}
92
93static 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
170void 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
185int 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;
432bail:
433    return -1;
434}
435
436/* vim: set ts=8 sts=4 sw=4 noet: */
437