xref: /netbsd-src/sys/arch/zaurus/stand/zboot/boot.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /*	$NetBSD: boot.c,v 1.5 2012/01/18 23:12:21 nonaka Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 NONAKA Kimihiro <nonaka@netbsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/bootblock.h>
30 #include <sys/boot_flag.h>
31 
32 #include "boot.h"
33 #include "bootinfo.h"
34 #include "bootmenu.h"
35 #include "disk.h"
36 #include "unixdev.h"
37 #include "pathnames.h"
38 
39 #include <lib/libsa/loadfile.h>
40 #include <lib/libsa/ufs.h>
41 
42 #include "compat_linux.h"
43 
44 static const char * const names[][2] = {
45 	{ "netbsd", "netbsd.gz" },
46 	{ "netbsd.old", "netbsd.old.gz", },
47 	{ "onetbsd", "onetbsd.gz" },
48 };
49 
50 char *default_devname;
51 uint default_unit, default_partition;
52 const char *default_filename;
53 int default_timeout = 5;
54 
55 static char probed_disks[256];
56 static char bootconfpath[1024];
57 
58 static void bootcmd_help(char *);
59 static void bootcmd_ls(char *);
60 static void bootcmd_quit(char *);
61 static void bootcmd_boot(char *);
62 static void bootcmd_disk(char *);
63 #ifdef SUPPORT_CONSDEV
64 static void bootcmd_consdev(char *);
65 #endif
66 
67 static const struct bootblk_command {
68 	const char *c_name;
69 	void (*c_fn)(char *arg);
70 } bootcmds[] = {
71 	{ "help",	bootcmd_help },
72 	{ "?",		bootcmd_help },
73 	{ "quit",	bootcmd_quit },
74 	{ "ls",		bootcmd_ls },
75 	{ "boot",	bootcmd_boot },
76 	{ "disk",	bootcmd_disk },
77 #ifdef SUPPORT_CONSDEV
78 	{ "consdev",	bootcmd_consdev },
79 #endif
80 	{ NULL,		NULL },
81 };
82 
83 static struct btinfo_howto bi_howto;
84 
85 static void print_banner(void);
86 static int exec_netbsd(const char *file, int howto);
87 
88 int
89 parsebootfile(const char *fname, char **fsname, char **devname,
90 	uint *unit, uint *partition, const char **file)
91 {
92 	const char *col;
93 
94 	*fsname = "ufs";
95 	*devname = default_devname;
96 	*unit = default_unit;
97 	*partition = default_partition;
98 	*file = default_filename;
99 
100 	if (fname == NULL)
101 		return 0;
102 
103 	if ((col = strchr(fname, ':'))) {	/* device given */
104 		static char savedevname[MAXDEVNAME+1];
105 		int devlen;
106 		unsigned int u = 0, p = 0;
107 		int i = 0;
108 
109 		devlen = col - fname;
110 		if (devlen > MAXDEVNAME)
111 			return EINVAL;
112 
113 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
114 		if (!isvalidname(fname[i]))
115 			return EINVAL;
116 		do {
117 			savedevname[i] = fname[i];
118 			i++;
119 		} while (isvalidname(fname[i]));
120 		savedevname[i] = '\0';
121 
122 #define isnum(c) ((c) >= '0' && (c) <= '9')
123 		if (i < devlen) {
124 			if (!isnum(fname[i]))
125 				return (EUNIT);
126 			do {
127 				u *= 10;
128 				u += fname[i++] - '0';
129 			} while (isnum(fname[i]));
130 		}
131 
132 #define isvalidpart(c) ((c) >= 'a' && (c) < 'a' + MAXPARTITIONS)
133 		if (i < devlen) {
134 			if (!isvalidpart(fname[i]))
135 				return (EPART);
136 			p = fname[i++] - 'a';
137 		}
138 
139 		if (i != devlen)
140 			return ENXIO;
141 
142 		*devname = savedevname;
143 		*unit = u;
144 		*partition = p;
145 		fname = col + 1;
146 	}
147 
148 	if (*fname)
149 		*file = fname;
150 
151 	return 0;
152 }
153 
154 char *
155 sprint_bootsel(const char *filename)
156 {
157 	static char buf[80];
158 	char *fsname, *devname;
159 	uint unit, partition;
160 	const char *file;
161 
162 	if (parsebootfile(filename, &fsname, &devname, &unit, &partition,
163 	    &file) == 0) {
164 		snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
165 		    'a' + partition, file);
166 		return buf;
167 	}
168 	return "(invalid)";
169 }
170 
171 static void
172 print_banner(void)
173 {
174 	extern const char bootprog_name[];
175 	extern const char bootprog_rev[];
176 
177 	printf("\n");
178 	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
179 }
180 
181 void
182 boot(dev_t bootdev)
183 {
184 	extern char twiddle_toggle;
185 	int currname;
186 	int c;
187 
188 	consinit(CONSDEV_GLASS, -1);
189 
190 	twiddle_toggle = 1;	/* no twiddling until we're ready */
191 
192 	/* set default value: hd0a:netbsd */
193 	default_devname = "hd";
194 	default_unit = 0;
195 	default_partition = 0;
196 	default_filename = names[0][0];
197 
198 	diskprobe(probed_disks, sizeof(probed_disks));
199 
200 	snprintf(bootconfpath, sizeof(bootconfpath), "%s%d%c:%s",
201 	    default_devname, default_unit, 'a' + default_partition,
202 	    _PATH_BOOTCONF);
203 	parsebootconf(bootconfpath);
204 
205 #ifdef SUPPORT_CONSDEV
206 	/*
207 	 * If console set in boot.cfg, switch to it.
208 	 * This will print the banner, so we don't need to explicitly do it
209 	 */
210 	if (bootconf.consdev)
211 		bootcmd_consdev(bootconf.consdev);
212 	else
213 #endif
214 		print_banner();
215 
216 	printf("\ndisks: %s\n", probed_disks);
217 
218 	/* Display the menu, if applicable */
219 	twiddle_toggle = 0;
220 	if (bootconf.nummenu > 0) {
221 		/* Does not return */
222 		doboottypemenu();
223 	}
224 
225 	printf("Press return to boot now, any other key for boot menu\n");
226 	currname = 0;
227 	for (currname = 0; currname < __arraycount(names); currname++) {
228 		printf("booting %s - starting in ",
229 		    sprint_bootsel(names[currname][0]));
230 
231 		c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1);
232 		if ((c != '\r') && (c != '\n') && (c != '\0')) {
233 			printf("type \"?\" or \"help\" for help.\n");
234 			bootmenu(); /* does not return */
235 		}
236 
237 		/*
238 		 * try pairs of names[] entries, foo and foo.gz
239 		 */
240 		/* don't print "booting..." again */
241 		bootit(names[currname][0], 0, 0);
242 		/* since it failed, try compressed bootfile. */
243 		bootit(names[currname][1], 0, 1);
244 	}
245 
246 	bootmenu(); /* does not return */
247 }
248 
249 void
250 bootit(const char *filename, int howto, int tell)
251 {
252 
253 	if (tell) {
254 		printf("booting %s", sprint_bootsel(filename));
255 		if (howto)
256 			printf(" (howto 0x%x)", howto);
257 		printf("\n");
258 	}
259 
260 	if (exec_netbsd(filename, howto) < 0) {
261 		printf("boot: %s: %s\n", sprint_bootsel(filename),
262 		       strerror(errno));
263 	} else {
264 		printf("boot returned\n");
265 	}
266 }
267 
268 static int
269 exec_netbsd(const char *file, int howto)
270 {
271 	u_long marks[MARK_MAX];
272 
273 	BI_ALLOC(BTINFO_MAX);
274 
275 	bi_howto.howto = howto;
276 	BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto));
277 
278 	if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1)
279 		goto out;
280 
281 	/*NOTREACHED*/
282 	return 0;
283 
284 out:
285 	BI_FREE();
286 	bootinfo = 0;
287 	return -1;
288 }
289 
290 /*
291  * bootmenu
292  */
293 static char *gettrailer(char *arg);
294 static int parseopts(const char *opts, int *howto);
295 static int parseboot(char *arg, char **filename, int *howto);
296 
297 /* ARGSUSED */
298 static void
299 bootcmd_help(char *arg)
300 {
301 
302 	printf("commands are:\n"
303 	    "boot [xdNx:][filename] [-1acdqsv]\n"
304 	    "     (ex. \"boot hd0a:netbsd.old -s\")\n"
305 	    "     (ex. \"boot path:/mnt/card/netbsd -1\")\n"
306 	    "ls [path]\n"
307 #ifdef SUPPORT_CONSDEV
308 	    "consdev {glass|com [speed]}\n"
309 #endif
310 	    "disk\n"
311 	    "help|?\n"
312 	    "quit\n");
313 }
314 
315 /* ARGSUSED */
316 static void
317 bootcmd_quit(char *arg)
318 {
319 
320 	printf("Exiting...\n");
321 	exit(0);
322 	/*NOTREACHED*/
323 }
324 
325 static void
326 bootcmd_ls(char *arg)
327 {
328 	const char *save = default_filename;
329 
330 	default_filename = "/";
331 	ls(arg);
332 	default_filename = save;
333 }
334 
335 static void
336 bootcmd_boot(char *arg)
337 {
338 	char *filename;
339 	int howto;
340 
341 	if (parseboot(arg, &filename, &howto)) {
342 		bootit(filename, howto, 1);
343 	}
344 }
345 
346 /* ARGSUSED */
347 static void
348 bootcmd_disk(char *arg)
349 {
350 
351 	printf("disks: %s\n", probed_disks);
352 }
353 
354 #ifdef SUPPORT_CONSDEV
355 static const struct cons_devs {
356 	const char	*name;
357 	int		tag;
358 } cons_devs[] = {
359 	{ "glass",	CONSDEV_GLASS },
360 	{ "com",	CONSDEV_COM0 },
361 	{ "com0",	CONSDEV_COM0 },
362 	{ NULL,		0 }
363 };
364 
365 static void
366 bootcmd_consdev(char *arg)
367 {
368 	const struct cons_devs *cdp;
369 	char *p;
370 	int speed = 9600;
371 
372 	p = strchr(arg, ' ');
373 	if (p != NULL) {
374 		*p++ = '\0';
375 		speed = atoi(p);
376 	}
377 	for (cdp = cons_devs; cdp->name != NULL; cdp++) {
378 		if (strcmp(arg, cdp->name) == 0) {
379 			consinit(cdp->tag, speed);
380 			print_banner();
381 			return;
382 		}
383 	}
384 	printf("invalid console device.\n");
385 }
386 #endif
387 
388 void
389 docommand(char *arg)
390 {
391 	char *options;
392 	int i;
393 
394 	options = gettrailer(arg);
395 
396 	for (i = 0; bootcmds[i].c_name != NULL; i++) {
397 		if (strcmp(arg, bootcmds[i].c_name) == 0) {
398 			(*bootcmds[i].c_fn)(options);
399 			return;
400 		}
401 	}
402 
403 	printf("unknown command\n");
404 	bootcmd_help(NULL);
405 }
406 
407 void
408 bootmenu(void)
409 {
410 	char input[256];
411 	char *c;
412 
413 	for (;;) {
414 		c = input;
415 
416 		input[0] = '\0';
417 		printf("> ");
418 		gets(input);
419 
420 		/*
421 		 * Skip leading whitespace.
422 		 */
423 		while (*c == ' ') {
424 			c++;
425 		}
426 		if (*c != '\0') {
427 			docommand(c);
428 		}
429 	}
430 }
431 
432 /*
433  * chops the head from the arguments and returns the arguments if any,
434  * or possibly an empty string.
435  */
436 static char *
437 gettrailer(char *arg)
438 {
439 	char *options;
440 
441 	if ((options = strchr(arg, ' ')) == NULL)
442 		return ("");
443 	else
444 		*options++ = '\0';
445 
446 	/* trim leading blanks */
447 	while (*options && *options == ' ')
448 		options++;
449 
450 	return options;
451 }
452 
453 static int
454 parseopts(const char *opts, int *howto)
455 {
456 	int r, tmpopt = 0;
457 
458 	opts++; 	/* skip - */
459 	while (*opts && *opts != ' ') {
460 		r = 0;
461 		BOOT_FLAG(*opts, r);
462 		if (r == 0) {
463 			printf("-%c: unknown flag\n", *opts);
464 			bootcmd_help(NULL);
465 			return 0;
466 		}
467 		tmpopt |= r;
468 		opts++;
469 	}
470 
471 	*howto = tmpopt;
472 	return 1;
473 }
474 
475 static int
476 parseboot(char *arg, char **filename, int *howto)
477 {
478 	char *opts = NULL;
479 
480 	*filename = 0;
481 	*howto = 0;
482 
483 	/* if there were no arguments */
484 	if (arg == NULL || *arg == '\0')
485 		return 1;
486 
487 	/* format is... */
488 	/* [[xxNx:]filename] [-adqsv] */
489 
490 	/* check for just args */
491 	if (arg[0] == '-') {
492 		opts = arg;
493 	} else {
494 		/* there's a file name */
495 		*filename = arg;
496 
497 		opts = gettrailer(arg);
498 		if (opts == NULL || *opts == '\0') {
499 			opts = NULL;
500 		} else if (*opts != '-') {
501 			printf("invalid arguments\n");
502 			bootcmd_help(NULL);
503 			return 0;
504 		}
505 	}
506 
507 	/* at this point, we have dealt with filenames. */
508 
509 	/* now, deal with options */
510 	if (opts) {
511 		if (parseopts(opts, howto) == 0) {
512 			return 0;
513 		}
514 	}
515 	return 1;
516 }
517