xref: /netbsd-src/sys/arch/zaurus/stand/zboot/boot.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: boot.c,v 1.2 2011/01/22 19:19:25 joerg 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 
57 static void bootcmd_help(char *);
58 static void bootcmd_ls(char *);
59 static void bootcmd_quit(char *);
60 static void bootcmd_boot(char *);
61 static void bootcmd_disk(char *);
62 #ifdef SUPPORT_CONSDEV
63 static void bootcmd_consdev(char *);
64 #endif
65 
66 static const struct bootblk_command {
67 	const char *c_name;
68 	void (*c_fn)(char *arg);
69 } bootcmds[] = {
70 	{ "help",	bootcmd_help },
71 	{ "?",		bootcmd_help },
72 	{ "quit",	bootcmd_quit },
73 	{ "ls",		bootcmd_ls },
74 	{ "boot",	bootcmd_boot },
75 	{ "disk",	bootcmd_disk },
76 #ifdef SUPPORT_CONSDEV
77 	{ "consdev",	bootcmd_consdev },
78 #endif
79 	{ NULL,		NULL },
80 };
81 
82 static struct btinfo_howto bi_howto;
83 
84 static void print_banner(void);
85 static int exec_netbsd(const char *file, int howto);
86 
87 int
88 parsebootfile(const char *fname, char **fsname, char **devname,
89 	uint *unit, uint *partition, const char **file)
90 {
91 	const char *col;
92 
93 	*fsname = "ufs";
94 	*devname = default_devname;
95 	*unit = default_unit;
96 	*partition = default_partition;
97 	*file = default_filename;
98 
99 	if (fname == NULL)
100 		return 0;
101 
102 	if ((col = strchr(fname, ':'))) {	/* device given */
103 		static char savedevname[MAXDEVNAME+1];
104 		int devlen;
105 		unsigned int u = 0, p = 0;
106 		int i = 0;
107 
108 		devlen = col - fname;
109 		if (devlen > MAXDEVNAME)
110 			return EINVAL;
111 
112 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
113 		if (!isvalidname(fname[i]))
114 			return EINVAL;
115 		do {
116 			savedevname[i] = fname[i];
117 			i++;
118 		} while (isvalidname(fname[i]));
119 		savedevname[i] = '\0';
120 
121 #define isnum(c) ((c) >= '0' && (c) <= '9')
122 		if (i < devlen) {
123 			if (!isnum(fname[i]))
124 				return (EUNIT);
125 			do {
126 				u *= 10;
127 				u += fname[i++] - '0';
128 			} while (isnum(fname[i]));
129 		}
130 
131 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'a' + MAXPARTITIONS)
132 		if (i < devlen) {
133 			if (!isvalidpart(fname[i]))
134 				return (EPART);
135 			p = fname[i++] - 'a';
136 		}
137 
138 		if (i != devlen)
139 			return ENXIO;
140 
141 		*devname = savedevname;
142 		*unit = u;
143 		*partition = p;
144 		fname = col + 1;
145 	}
146 
147 	if (*fname)
148 		*file = fname;
149 
150 	return 0;
151 }
152 
153 char *
154 sprint_bootsel(const char *filename)
155 {
156 	static char buf[80];
157 	char *fsname, *devname;
158 	uint unit, partition;
159 	const char *file;
160 
161 	if (parsebootfile(filename, &fsname, &devname, &unit, &partition,
162 	    &file) == 0) {
163 		snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
164 		    'a' + partition, file);
165 		return buf;
166 	}
167 	return "(invalid)";
168 }
169 
170 static void
171 print_banner(void)
172 {
173 	extern const char bootprog_name[];
174 	extern const char bootprog_rev[];
175 
176 	printf("\n");
177 	printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
178 }
179 
180 void
181 boot(dev_t bootdev)
182 {
183 	extern char twiddle_toggle;
184 	int currname;
185 	int c;
186 
187 	consinit(CONSDEV_GLASS, -1);
188 
189 	twiddle_toggle = 1;	/* no twiddling until we're ready */
190 
191 	/* set default value: hd0a:netbsd */
192 	default_devname = "hd";
193 	default_unit = 0;
194 	default_partition = 0;
195 	default_filename = names[0][0];
196 
197 	diskprobe(probed_disks, sizeof(probed_disks));
198 
199 	parsebootconf(_PATH_BOOTCONF);
200 
201 #ifdef SUPPORT_CONSDEV
202 	/*
203 	 * If console set in boot.cfg, switch to it.
204 	 * This will print the banner, so we don't need to explicitly do it
205 	 */
206 	if (bootconf.consdev)
207 		bootcmd_consdev(bootconf.consdev);
208 	else
209 #endif
210 		print_banner();
211 
212 	printf("\ndisks: %s\n", probed_disks);
213 
214 	/* Display the menu, if applicable */
215 	twiddle_toggle = 0;
216 	if (bootconf.nummenu > 0) {
217 		/* Does not return */
218 		doboottypemenu();
219 	}
220 
221 	printf("Press return to boot now, any other key for boot menu\n");
222 	currname = 0;
223 	for (currname = 0; currname < __arraycount(names); currname++) {
224 		printf("booting %s - starting in ",
225 		    sprint_bootsel(names[currname][0]));
226 
227 		c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1);
228 		if ((c != '\r') && (c != '\n') && (c != '\0')) {
229 			printf("type \"?\" or \"help\" for help.\n");
230 			bootmenu(); /* does not return */
231 		}
232 
233 		/*
234 		 * try pairs of names[] entries, foo and foo.gz
235 		 */
236 		/* don't print "booting..." again */
237 		bootit(names[currname][0], 0, 0);
238 		/* since it failed, try compressed bootfile. */
239 		bootit(names[currname][1], 0, 1);
240 	}
241 
242 	bootmenu(); /* does not return */
243 }
244 
245 void
246 bootit(const char *filename, int howto, int tell)
247 {
248 
249 	if (tell) {
250 		printf("booting %s", sprint_bootsel(filename));
251 		if (howto)
252 			printf(" (howto 0x%x)", howto);
253 		printf("\n");
254 	}
255 
256 	if (exec_netbsd(filename, howto) < 0) {
257 		printf("boot: %s: %s\n", sprint_bootsel(filename),
258 		       strerror(errno));
259 	} else {
260 		printf("boot returned\n");
261 	}
262 }
263 
264 static int
265 exec_netbsd(const char *file, int howto)
266 {
267 	u_long marks[MARK_MAX];
268 
269 	BI_ALLOC(BTINFO_MAX);
270 
271 	bi_howto.howto = howto;
272 	BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto));
273 
274 	if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1)
275 		goto out;
276 
277 	/*NOTREACHED*/
278 	return 0;
279 
280 out:
281 	BI_FREE();
282 	bootinfo = 0;
283 	return -1;
284 }
285 
286 /*
287  * bootmenu
288  */
289 static char *gettrailer(char *arg);
290 static int parseopts(const char *opts, int *howto);
291 static int parseboot(char *arg, char **filename, int *howto);
292 
293 /* ARGSUSED */
294 static void
295 bootcmd_help(char *arg)
296 {
297 
298 	printf("commands are:\n"
299 	    "boot [xdNx:][filename] [-acdqsv]\n"
300 	    "     (ex. \"hd0a:netbsd.old -s\"\n"
301 	    "ls [path]\n"
302 #ifdef SUPPORT_CONSDEV
303 	    "consdev {glass|com [speed]}\n"
304 #endif
305 	    "disk\n"
306 	    "help|?\n"
307 	    "quit\n");
308 }
309 
310 /* ARGSUSED */
311 static void
312 bootcmd_quit(char *arg)
313 {
314 
315 	printf("Exiting...\n");
316 	exit(0);
317 	/*NOTREACHED*/
318 }
319 
320 static void
321 bootcmd_ls(char *arg)
322 {
323 	const char *save = default_filename;
324 
325 	default_filename = "/";
326 	ufs_ls(arg);
327 	default_filename = save;
328 }
329 
330 static void
331 bootcmd_boot(char *arg)
332 {
333 	char *filename;
334 	int howto;
335 
336 	if (parseboot(arg, &filename, &howto)) {
337 		bootit(filename, howto, 1);
338 	}
339 }
340 
341 /* ARGSUSED */
342 static void
343 bootcmd_disk(char *arg)
344 {
345 
346 	printf("disks: %s\n", probed_disks);
347 }
348 
349 #ifdef SUPPORT_CONSDEV
350 static const struct cons_devs {
351 	const char	*name;
352 	int		tag;
353 } cons_devs[] = {
354 	{ "glass",	CONSDEV_GLASS },
355 	{ "com",	CONSDEV_COM0 },
356 	{ "com0",	CONSDEV_COM0 },
357 	{ NULL,		0 }
358 };
359 
360 static void
361 bootcmd_consdev(char *arg)
362 {
363 	const struct cons_devs *cdp;
364 	char *p;
365 	int speed = 9600;
366 
367 	p = strchr(arg, ' ');
368 	if (p != NULL) {
369 		*p++ = '\0';
370 		speed = atoi(p);
371 	}
372 	for (cdp = cons_devs; cdp->name != NULL; cdp++) {
373 		if (strcmp(arg, cdp->name) == 0) {
374 			consinit(cdp->tag, speed);
375 			print_banner();
376 			return;
377 		}
378 	}
379 	printf("invalid console device.\n");
380 }
381 #endif
382 
383 void
384 docommand(char *arg)
385 {
386 	char *options;
387 	int i;
388 
389 	options = gettrailer(arg);
390 
391 	for (i = 0; bootcmds[i].c_name != NULL; i++) {
392 		if (strcmp(arg, bootcmds[i].c_name) == 0) {
393 			(*bootcmds[i].c_fn)(options);
394 			return;
395 		}
396 	}
397 
398 	printf("unknown command\n");
399 	bootcmd_help(NULL);
400 }
401 
402 void
403 bootmenu(void)
404 {
405 	char input[256];
406 	char *c;
407 
408 	for (;;) {
409 		c = input;
410 
411 		input[0] = '\0';
412 		printf("> ");
413 		gets(input);
414 
415 		/*
416 		 * Skip leading whitespace.
417 		 */
418 		while (*c == ' ') {
419 			c++;
420 		}
421 		if (*c != '\0') {
422 			docommand(c);
423 		}
424 	}
425 }
426 
427 /*
428  * chops the head from the arguments and returns the arguments if any,
429  * or possibly an empty string.
430  */
431 static char *
432 gettrailer(char *arg)
433 {
434 	char *options;
435 
436 	if ((options = strchr(arg, ' ')) == NULL)
437 		return ("");
438 	else
439 		*options++ = '\0';
440 
441 	/* trim leading blanks */
442 	while (*options && *options == ' ')
443 		options++;
444 
445 	return options;
446 }
447 
448 static int
449 parseopts(const char *opts, int *howto)
450 {
451 	int r, tmpopt = 0;
452 
453 	opts++; 	/* skip - */
454 	while (*opts && *opts != ' ') {
455 		r = 0;
456 		BOOT_FLAG(*opts, r);
457 		if (r == 0) {
458 			printf("-%c: unknown flag\n", *opts);
459 			bootcmd_help(NULL);
460 			return 0;
461 		}
462 		tmpopt |= r;
463 		opts++;
464 	}
465 
466 	*howto = tmpopt;
467 	return 1;
468 }
469 
470 static int
471 parseboot(char *arg, char **filename, int *howto)
472 {
473 	char *opts = NULL;
474 
475 	*filename = 0;
476 	*howto = 0;
477 
478 	/* if there were no arguments */
479 	if (arg == NULL || *arg == '\0')
480 		return 1;
481 
482 	/* format is... */
483 	/* [[xxNx:]filename] [-adqsv] */
484 
485 	/* check for just args */
486 	if (arg[0] == '-') {
487 		opts = arg;
488 	} else {
489 		/* there's a file name */
490 		*filename = arg;
491 
492 		opts = gettrailer(arg);
493 		if (opts == NULL || *opts == '\0') {
494 			opts = NULL;
495 		} else if (*opts != '-') {
496 			printf("invalid arguments\n");
497 			bootcmd_help(NULL);
498 			return 0;
499 		}
500 	}
501 
502 	/* at this point, we have dealt with filenames. */
503 
504 	/* now, deal with options */
505 	if (opts) {
506 		if (parseopts(opts, howto) == 0) {
507 			return 0;
508 		}
509 	}
510 	return 1;
511 }
512