xref: /netbsd-src/sys/arch/zaurus/stand/zboot/boot.c (revision bf8938557c3419b143f0c430a95bf7d172d67553)
1 /*	$NetBSD: boot.c,v 1.8 2016/06/11 06:58:42 dholland 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
parsebootfile(const char * fname,char ** fsname,char ** devname,uint * unit,uint * partition,const char ** file)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 *
sprint_bootsel(const char * filename)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
print_banner(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
boot(dev_t bootdev)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 	    BOOTCFG_FILENAME);
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 (bootcfg_info.consdev)
211 		bootcmd_consdev(bootcfg_info.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 (bootcfg_info.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((bootcfg_info.timeout < 0) ? 0
232 		    : bootcfg_info.timeout, 1);
233 		if ((c != '\r') && (c != '\n') && (c != '\0')) {
234 			printf("type \"?\" or \"help\" for help.\n");
235 			bootmenu(); /* does not return */
236 		}
237 
238 		/*
239 		 * try pairs of names[] entries, foo and foo.gz
240 		 */
241 		/* don't print "booting..." again */
242 		bootit(names[currname][0], 0, 0);
243 		/* since it failed, try compressed bootfile. */
244 		bootit(names[currname][1], 0, 1);
245 	}
246 
247 	bootmenu(); /* does not return */
248 }
249 
250 void
bootit(const char * filename,int howto,int tell)251 bootit(const char *filename, int howto, int tell)
252 {
253 
254 	if (tell) {
255 		printf("booting %s", sprint_bootsel(filename));
256 		if (howto)
257 			printf(" (howto 0x%x)", howto);
258 		printf("\n");
259 	}
260 
261 	if (exec_netbsd(filename, howto) < 0) {
262 		printf("boot: %s: %s\n", sprint_bootsel(filename),
263 		       strerror(errno));
264 	} else {
265 		printf("boot returned\n");
266 	}
267 }
268 
269 static int
exec_netbsd(const char * file,int howto)270 exec_netbsd(const char *file, int howto)
271 {
272 	u_long marks[MARK_MAX];
273 
274 	BI_ALLOC(BTINFO_MAX);
275 
276 	bi_howto.howto = howto;
277 	BI_ADD(&bi_howto, BTINFO_HOWTO, sizeof(bi_howto));
278 
279 	if (loadfile_zboot(file, marks, LOAD_KERNEL) == -1)
280 		goto out;
281 
282 	/*NOTREACHED*/
283 	return 0;
284 
285 out:
286 	BI_FREE();
287 	bootinfo = 0;
288 	return -1;
289 }
290 
291 /*
292  * bootmenu
293  */
294 static char *gettrailer(char *arg);
295 static int parseopts(const char *opts, int *howto);
296 static int parseboot(char *arg, char **filename, int *howto);
297 
298 /* ARGSUSED */
299 static void
bootcmd_help(char * arg)300 bootcmd_help(char *arg)
301 {
302 
303 	printf("commands are:\n"
304 	    "boot [xdNx:][filename] [-1acdqsv]\n"
305 	    "     (ex. \"boot hd0a:netbsd.old -s\")\n"
306 	    "     (ex. \"boot path:/mnt/card/netbsd -1\")\n"
307 	    "ls [path]\n"
308 #ifdef SUPPORT_CONSDEV
309 	    "consdev {glass|com [speed]}\n"
310 #endif
311 	    "disk\n"
312 	    "help|?\n"
313 	    "quit\n");
314 }
315 
316 /* ARGSUSED */
317 static void
bootcmd_quit(char * arg)318 bootcmd_quit(char *arg)
319 {
320 
321 	printf("Exiting...\n");
322 	exit(0);
323 	/*NOTREACHED*/
324 }
325 
326 static void
bootcmd_ls(char * arg)327 bootcmd_ls(char *arg)
328 {
329 	const char *save = default_filename;
330 
331 	default_filename = "/";
332 	ls(arg);
333 	default_filename = save;
334 }
335 
336 static void
bootcmd_boot(char * arg)337 bootcmd_boot(char *arg)
338 {
339 	char *filename;
340 	int howto;
341 
342 	if (parseboot(arg, &filename, &howto)) {
343 		bootit(filename, howto, 1);
344 	}
345 }
346 
347 /* ARGSUSED */
348 static void
bootcmd_disk(char * arg)349 bootcmd_disk(char *arg)
350 {
351 
352 	printf("disks: %s\n", probed_disks);
353 }
354 
355 #ifdef SUPPORT_CONSDEV
356 static const struct cons_devs {
357 	const char	*name;
358 	int		tag;
359 } cons_devs[] = {
360 	{ "glass",	CONSDEV_GLASS },
361 	{ "com",	CONSDEV_COM0 },
362 	{ "com0",	CONSDEV_COM0 },
363 	{ NULL,		0 }
364 };
365 
366 static void
bootcmd_consdev(char * arg)367 bootcmd_consdev(char *arg)
368 {
369 	const struct cons_devs *cdp;
370 	char *p;
371 	int speed = 9600;
372 
373 	p = strchr(arg, ' ');
374 	if (p != NULL) {
375 		*p++ = '\0';
376 		speed = atoi(p);
377 	}
378 	for (cdp = cons_devs; cdp->name != NULL; cdp++) {
379 		if (strcmp(arg, cdp->name) == 0) {
380 			consinit(cdp->tag, speed);
381 			print_banner();
382 			return;
383 		}
384 	}
385 	printf("invalid console device.\n");
386 }
387 #endif
388 
389 void
docommand(char * arg)390 docommand(char *arg)
391 {
392 	char *options;
393 	int i;
394 
395 	options = gettrailer(arg);
396 
397 	for (i = 0; bootcmds[i].c_name != NULL; i++) {
398 		if (strcmp(arg, bootcmds[i].c_name) == 0) {
399 			(*bootcmds[i].c_fn)(options);
400 			return;
401 		}
402 	}
403 
404 	printf("unknown command\n");
405 	bootcmd_help(NULL);
406 }
407 
408 void
bootmenu(void)409 bootmenu(void)
410 {
411 	char input[256];
412 	char *c;
413 
414 	for (;;) {
415 		c = input;
416 
417 		input[0] = '\0';
418 		printf("> ");
419 		kgets(input, sizeof(input));
420 
421 		/*
422 		 * Skip leading whitespace.
423 		 */
424 		while (*c == ' ') {
425 			c++;
426 		}
427 		if (*c != '\0') {
428 			docommand(c);
429 		}
430 	}
431 }
432 
433 /*
434  * chops the head from the arguments and returns the arguments if any,
435  * or possibly an empty string.
436  */
437 static char *
gettrailer(char * arg)438 gettrailer(char *arg)
439 {
440 	char *options;
441 
442 	if ((options = strchr(arg, ' ')) == NULL)
443 		return ("");
444 	else
445 		*options++ = '\0';
446 
447 	/* trim leading blanks */
448 	while (*options == ' ')
449 		options++;
450 
451 	return options;
452 }
453 
454 static int
parseopts(const char * opts,int * howto)455 parseopts(const char *opts, int *howto)
456 {
457 	int r, tmpopt = 0;
458 
459 	opts++; 	/* skip - */
460 	while (*opts && *opts != ' ') {
461 		r = 0;
462 		BOOT_FLAG(*opts, r);
463 		if (r == 0) {
464 			printf("-%c: unknown flag\n", *opts);
465 			bootcmd_help(NULL);
466 			return 0;
467 		}
468 		tmpopt |= r;
469 		opts++;
470 	}
471 
472 	*howto = tmpopt;
473 	return 1;
474 }
475 
476 static int
parseboot(char * arg,char ** filename,int * howto)477 parseboot(char *arg, char **filename, int *howto)
478 {
479 	char *opts = NULL;
480 
481 	*filename = 0;
482 	*howto = 0;
483 
484 	/* if there were no arguments */
485 	if (arg == NULL || *arg == '\0')
486 		return 1;
487 
488 	/* format is... */
489 	/* [[xxNx:]filename] [-adqsv] */
490 
491 	/* check for just args */
492 	if (arg[0] == '-') {
493 		opts = arg;
494 	} else {
495 		/* there's a file name */
496 		*filename = arg;
497 
498 		opts = gettrailer(arg);
499 		if (opts == NULL || *opts == '\0') {
500 			opts = NULL;
501 		} else if (*opts != '-') {
502 			printf("invalid arguments\n");
503 			bootcmd_help(NULL);
504 			return 0;
505 		}
506 	}
507 
508 	/* at this point, we have dealt with filenames. */
509 
510 	/* now, deal with options */
511 	if (opts) {
512 		if (parseopts(opts, howto) == 0) {
513 			return 0;
514 		}
515 	}
516 	return 1;
517 }
518