xref: /netbsd-src/sys/arch/i386/stand/boot/boot2.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: boot2.c,v 1.17 2007/12/13 11:52:17 sborrill Exp $	*/
2 
3 /*
4  * Copyright (c) 2003
5  *	David Laight.  All rights reserved
6  * Copyright (c) 1996, 1997, 1999
7  * 	Matthias Drochner.  All rights reserved.
8  * Copyright (c) 1996, 1997
9  * 	Perry E. Metzger.  All rights reserved.
10  * Copyright (c) 1997
11  *	Jason R. Thorpe.  All rights reserved
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgements:
23  *	This product includes software developed for the NetBSD Project
24  *	by Matthias Drochner.
25  *	This product includes software developed for the NetBSD Project
26  *	by Perry E. Metzger.
27  * 4. The names of the authors may not be used to endorse or promote products
28  *    derived from this software without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
31  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
33  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
34  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
39  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  */
41 
42 /* Based on stand/biosboot/main.c */
43 
44 #include <sys/types.h>
45 #include <sys/reboot.h>
46 #include <sys/bootblock.h>
47 
48 #include <lib/libsa/stand.h>
49 #include <lib/libsa/ufs.h>
50 #include <lib/libkern/libkern.h>
51 
52 #include <libi386.h>
53 #include "devopen.h"
54 
55 #ifdef SUPPORT_USTARFS
56 #include "ustarfs.h"
57 #endif
58 #ifdef SUPPORT_PS2
59 #include <biosmca.h>
60 #endif
61 
62 extern struct x86_boot_params boot_params;
63 
64 extern	const char bootprog_name[], bootprog_rev[], bootprog_date[],
65 	bootprog_maker[];
66 
67 int errno;
68 
69 int boot_biosdev;
70 u_int boot_biossector;
71 
72 static const char * const names[][2] = {
73 	{ "netbsd", "netbsd.gz" },
74 	{ "onetbsd", "onetbsd.gz" },
75 	{ "netbsd.old", "netbsd.old.gz" },
76 };
77 
78 #define NUMNAMES (sizeof(names)/sizeof(names[0]))
79 #define DEFFILENAME names[0][0]
80 
81 #define MAXDEVNAME 16
82 
83 #ifndef SMALL
84 #define BOOTCONF "boot.cfg"
85 #define MAXMENU 10
86 #define MAXBANNER 10
87 #endif /* !SMALL */
88 
89 static char *default_devname;
90 static int default_unit, default_partition;
91 static const char *default_filename;
92 
93 char *sprint_bootsel(const char *);
94 void bootit(const char *, int, int);
95 void print_banner(void);
96 void boot2(int, u_int);
97 
98 #ifndef SMALL
99 void parsebootconf(const char *);
100 void doboottypemenu(void);
101 int atoi(const char *);
102 #endif /* !SMALL */
103 
104 void	command_help(char *);
105 void	command_ls(char *);
106 void	command_quit(char *);
107 void	command_boot(char *);
108 void	command_dev(char *);
109 void	command_consdev(char *);
110 
111 const struct bootblk_command commands[] = {
112 	{ "help",	command_help },
113 	{ "?",		command_help },
114 	{ "ls",		command_ls },
115 	{ "quit",	command_quit },
116 	{ "boot",	command_boot },
117 	{ "dev",	command_dev },
118 	{ "consdev",	command_consdev },
119 	{ NULL,		NULL },
120 };
121 
122 #ifndef SMALL
123 struct bootconf_def {
124 	char *banner[MAXBANNER];	/* Banner text */
125 	char *command[MAXMENU];		/* Menu commands per entry*/
126 	char *consdev;			/* Console device */
127 	int def;			/* Default menu option */
128 	char *desc[MAXMENU];		/* Menu text per entry */
129 	int nummenu;			/* Number of menu items */
130 	int timeout;		 	/* Timeout in seconds */
131 } bootconf;
132 #endif /* !SMALL */
133 
134 int
135 parsebootfile(const char *fname, char **fsname, char **devname,
136 	      int *unit, int *partition, const char **file)
137 {
138 	const char *col;
139 
140 	*fsname = "ufs";
141 	*devname = default_devname;
142 	*unit = default_unit;
143 	*partition = default_partition;
144 	*file = default_filename;
145 
146 	if (fname == NULL)
147 		return 0;
148 
149 	if ((col = strchr(fname, ':')) != NULL) {	/* device given */
150 		static char savedevname[MAXDEVNAME+1];
151 		int devlen;
152 		int u = 0, p = 0;
153 		int i = 0;
154 
155 		devlen = col - fname;
156 		if (devlen > MAXDEVNAME)
157 			return EINVAL;
158 
159 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
160 		if (!isvalidname(fname[i]))
161 			return EINVAL;
162 		do {
163 			savedevname[i] = fname[i];
164 			i++;
165 		} while (isvalidname(fname[i]));
166 		savedevname[i] = '\0';
167 
168 #define isnum(c) ((c) >= '0' && (c) <= '9')
169 		if (i < devlen) {
170 			if (!isnum(fname[i]))
171 				return EUNIT;
172 			do {
173 				u *= 10;
174 				u += fname[i++] - '0';
175 			} while (isnum(fname[i]));
176 		}
177 
178 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
179 		if (i < devlen) {
180 			if (!isvalidpart(fname[i]))
181 				return EPART;
182 			p = fname[i++] - 'a';
183 		}
184 
185 		if (i != devlen)
186 			return ENXIO;
187 
188 		*devname = savedevname;
189 		*unit = u;
190 		*partition = p;
191 		fname = col + 1;
192 	}
193 
194 	if (*fname)
195 		*file = fname;
196 
197 	return 0;
198 }
199 
200 char *
201 sprint_bootsel(const char *filename)
202 {
203 	char *fsname, *devname;
204 	int unit, partition;
205 	const char *file;
206 	static char buf[80];
207 
208 	if (parsebootfile(filename, &fsname, &devname, &unit,
209 			  &partition, &file) == 0) {
210 		sprintf(buf, "%s%d%c:%s", devname, unit, 'a' + partition, file);
211 		return buf;
212 	}
213 	return "(invalid)";
214 }
215 
216 void
217 bootit(const char *filename, int howto, int tell)
218 {
219 
220 	if (tell) {
221 		printf("booting %s", sprint_bootsel(filename));
222 		if (howto)
223 			printf(" (howto 0x%x)", howto);
224 		printf("\n");
225 	}
226 
227 	if (exec_netbsd(filename, 0, howto) < 0)
228 		printf("boot: %s: %s\n", sprint_bootsel(filename),
229 		       strerror(errno));
230 	else
231 		printf("boot returned\n");
232 }
233 
234 void
235 print_banner(void)
236 {
237 #ifndef SMALL
238 	int n;
239 	if (bootconf.banner[0]) {
240 		for (n = 0; bootconf.banner[n]; n++)
241 			printf("%s\n", bootconf.banner[n]);
242 	} else {
243 #endif /* !SMALL */
244 		printf("\n");
245 		printf(">> %s, Revision %s\n", bootprog_name, bootprog_rev);
246 		printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
247 		printf(">> Memory: %d/%d k\n", getbasemem(), getextmem());
248 
249 #ifndef SMALL
250 	}
251 #endif /* !SMALL */
252 }
253 
254 #ifndef SMALL
255 int
256 atoi(const char *in)
257 {
258 	char *c;
259 	int ret;
260 
261 	ret = 0;
262 	c = (char *)in;
263 	if (*c == '-')
264 		c++;
265 	for (; isnum(*c); c++)
266 		ret = (ret * 10) + (*c - '0');
267 
268 	return (*in == '-') ? -ret : ret;
269 }
270 
271 /*
272  * This function parses a boot.cnf file in the root of the filesystem
273  * (if present) and populates the global boot configuration.
274  *
275  * The file consists of a number of lines each terminated by \n
276  * The lines are in the format keyword=value. There should be spaces
277  * around the = sign.
278  *
279  * The recognised keywords are:
280  * banner: text displayed instead of the normal welcome text
281  * menu: Descriptive text:command to use
282  * timeout: Timeout in seconds (overrides that set by installboot)
283  * default: the default menu option to use if Return is pressed
284  * consdev: the console device to use
285  *
286  * Example boot.cnf file:
287  * banner=Welcome to NetBSD
288  * banner=Please choose the boot type from the following menu
289  * menu=Boot NetBSD:boot netbsd
290  * menu=Boot into single user mode:boot netbsd -s
291  * menu=Goto boot comand line:prompt
292  * timeout=10
293  * consdev=com0
294  * default=1
295 */
296 void
297 parsebootconf(const char *conf)
298 {
299 	char *bc, *c;
300 	int cmenu, cbanner, len;
301 	int fd, err, off;
302 	struct stat st;
303 	char *value, *key;
304 #ifdef SUPPORT_USTARFS
305 	void *op_open;
306 #endif
307 
308 	/* Clear bootconf structure */
309 	bzero((void *)&bootconf, sizeof(bootconf));
310 
311 	/* Set timeout to configured */
312 	bootconf.timeout = boot_params.bp_timeout;
313 
314 	/* don't try to open BOOTCONF if the target fs is ustarfs */
315 #ifdef SUPPORT_USTARFS
316 #if !defined(LIBSA_SINGLE_FILESYSTEM)
317 	fd = open("boot", 0);	/* assume we are loaded as "boot" from here */
318 	if (fd < 0)
319 		op_open = NULL;	/* XXX */
320 	else {
321 		op_open = files[fd].f_ops->open;
322 		close(fd);
323 	}
324 #else
325 	op_open = file_system[0].open;
326 #endif	/* !LIBSA_SINGLE_FILESYSTEM */
327 	if (op_open == ustarfs_open)
328 		return;
329 #endif	/* SUPPORT_USTARFS */
330 
331 	fd = open(BOOTCONF, 0);
332 	if (fd < 0)
333 		return;
334 
335 	err = fstat(fd, &st);
336 	if (err == -1) {
337 		close(fd);
338 		return;
339 	}
340 
341 	bc = alloc(st.st_size + 1);
342 	if (bc == NULL) {
343 		printf("Could not allocate memory for boot configuration\n");
344 		return;
345 	}
346 
347 	off = 0;
348 	do {
349 		len = read(fd, bc + off, 1024);
350 		if (len <= 0)
351 			break;
352 		off += len;
353 	} while (len > 0);
354 	bc[off] = '\0';
355 
356 	close(fd);
357 	/* bc now contains the whole boot.cnf file */
358 
359 	cmenu = 0;
360 	cbanner = 0;
361 	for(c = bc; *c; c++) {
362 		key = c;
363 		/* Look for = separator between key and value */
364 		for (; *c && *c != '='; c++)
365 			continue;
366 		if (*c == '\0')
367 			break; /* break if at end of data */
368 
369 		/* zero terminate key which points to keyword */
370 		*c++ = 0;
371 		value = c;
372 		/* Look for end of line (or file) and zero terminate value */
373 		for (; *c && *c != '\n'; c++)
374 			continue;
375 		*c = 0;
376 
377 		if (!strncmp(key, "menu", 4)) {
378 			if (cmenu >= MAXMENU)
379 				continue;
380 			bootconf.desc[cmenu] = value;
381 			/* Look for : between description and command */
382 			for (; *value && *value != ':'; value++)
383 				continue;
384 			if(*value) {
385 				*value++ = 0;
386 				bootconf.command[cmenu] = value;
387 				cmenu++;
388 			} else {
389 				/* No delimiter means invalid line */
390 				bootconf.desc[cmenu] = NULL;
391 			}
392 		} else if (!strncmp(key, "banner", 6)) {
393 			if (cbanner < MAXBANNER)
394 				bootconf.banner[cbanner++] = value;
395 		} else if (!strncmp(key, "timeout", 7)) {
396 			if (!isnum(*value))
397 				bootconf.timeout = -1;
398 			else
399 				bootconf.timeout = atoi(value);
400 		} else if (!strncmp(key, "default", 7)) {
401 			bootconf.def = atoi(value) - 1;
402 		} else if (!strncmp(key, "consdev", 7)) {
403 			bootconf.consdev = value;
404 		}
405 	}
406 	bootconf.nummenu = cmenu;
407 	if (bootconf.def < 0)
408 		bootconf.def = 0;
409 	if (bootconf.def >= cmenu)
410 		bootconf.def = cmenu - 1;
411 }
412 
413 /*
414  * doboottypemenu will render the menu and parse any user input
415  */
416 
417 void
418 doboottypemenu(void)
419 {
420 	int choice;
421 	char input[80], c;
422 
423 	printf("\n");
424 	/* Display menu */
425 	for (choice = 0; bootconf.desc[choice]; choice++)
426 		printf("    %d. %s\n", choice+1, bootconf.desc[choice]);
427 
428 	choice = -1;
429 	for(;;) {
430 		input[0] = '\0';
431 
432 		if (bootconf.timeout < 0) {
433 			printf("\nOption: [%d]:", bootconf.def + 1);
434 			gets(input);
435 			if (input[0] == '\0') choice = bootconf.def;
436 			if (input[0] >= '1' &&
437 				input[0] <= bootconf.nummenu + '0')
438 				    choice = input[0] - '1';
439 		} else if (bootconf.timeout == 0)
440 			choice = bootconf.def;
441 		else  {
442 			printf("\nPress the key for your chosen option or ");
443 			printf("Return to choose the default (%d)\n",
444 			      bootconf.def + 1);
445 			printf("Option %d will be chosen in ",
446 			      bootconf.def + 1);
447 			c = awaitkey(bootconf.timeout, 1);
448 			if (c >= '1' && c <= bootconf.nummenu + '0')
449 				choice = c - '1';
450 			else if (c ==  '\r' || c == '\n' || c == '\0')
451 				/* default if timed out or Return pressed */
452 				choice = bootconf.def;
453 			else {
454 				/* If any other key pressed, drop to menu */
455 				bootconf.timeout = -1;
456 				choice = -1;
457 			}
458 		}
459 		if (choice < 0)
460 			continue;
461 		if (!strcmp(bootconf.command[choice], "prompt") &&
462 		    ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
463 		    check_password(boot_params.bp_password))) {
464 			printf("type \"?\" or \"help\" for help.\n");
465 			bootmenu(); /* does not return */
466 		} else
467 			docommand(bootconf.command[choice]);
468 
469 	}
470 }
471 #endif /* !SMALL */
472 
473 /*
474  * Called from the initial entry point boot_start in biosboot.S
475  *
476  * biosdev: BIOS drive number the system booted from
477  * biossector: Sector number of the NetBSD partition
478  */
479 void
480 boot2(int biosdev, u_int biossector)
481 {
482 	int currname;
483 	char c;
484 
485 	initio(boot_params.bp_consdev);
486 
487 #ifdef SUPPORT_PS2
488 	biosmca();
489 #endif
490 	gateA20();
491 
492 	if (boot_params.bp_flags & X86_BP_FLAGS_RESET_VIDEO)
493 		biosvideomode();
494 
495 	/* need to remember these */
496 	boot_biosdev = biosdev;
497 	boot_biossector = biossector;
498 
499 	/* try to set default device to what BIOS tells us */
500 	bios2dev(biosdev, biossector, &default_devname, &default_unit,
501 		 &default_partition);
502 
503 	/* if the user types "boot" without filename */
504 	default_filename = DEFFILENAME;
505 
506 #ifndef SMALL
507 	parsebootconf(BOOTCONF);
508 
509 	/*
510 	 * If console set in boot.cnf, switch to it.
511 	 * This will print the banner, so we don't need to explicitly do it
512 	 */
513 	if (bootconf.consdev)
514 		command_consdev(bootconf.consdev);
515 	else
516 		print_banner();
517 
518 	/* Display the menu, if applicable */
519 	if (bootconf.nummenu > 0) {
520 		/* Does not return */
521 		doboottypemenu();
522 	}
523 #else
524 		print_banner();
525 #endif
526 
527 	printf("Press return to boot now, any other key for boot menu\n");
528 	for (currname = 0; currname < NUMNAMES; currname++) {
529 		printf("booting %s - starting in ",
530 		       sprint_bootsel(names[currname][0]));
531 
532 #ifdef SMALL
533 		c = awaitkey(boot_params.bp_timeout, 1);
534 #else
535 		c = awaitkey((bootconf.timeout < 0) ? 0 : bootconf.timeout, 1);
536 #endif
537 		if ((c != '\r') && (c != '\n') && (c != '\0') &&
538 		    ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0
539 		     || check_password(boot_params.bp_password))) {
540 			printf("type \"?\" or \"help\" for help.\n");
541 			bootmenu(); /* does not return */
542 		}
543 
544 		/*
545 		 * try pairs of names[] entries, foo and foo.gz
546 		 */
547 		/* don't print "booting..." again */
548 		bootit(names[currname][0], 0, 0);
549 		/* since it failed, try compressed bootfile. */
550 		bootit(names[currname][1], 0, 1);
551 	}
552 
553 	bootmenu();	/* does not return */
554 }
555 
556 /* ARGSUSED */
557 void
558 command_help(char *arg)
559 {
560 
561 	printf("commands are:\n"
562 	       "boot [xdNx:][filename] [-acdqsv]\n"
563 	       "     (ex. \"hd0a:netbsd.old -s\"\n"
564 	       "ls [path]\n"
565 	       "dev xd[N[x]]:\n"
566 	       "consdev {pc|com[0123]|com[0123]kbd|auto}\n"
567 	       "help|?\n"
568 	       "quit\n");
569 }
570 
571 void
572 command_ls(char *arg)
573 {
574 	const char *save = default_filename;
575 
576 	default_filename = "/";
577 	ufs_ls(arg);
578 	default_filename = save;
579 }
580 
581 /* ARGSUSED */
582 void
583 command_quit(char *arg)
584 {
585 
586 	printf("Exiting...\n");
587 	delay(1000000);
588 	reboot();
589 	/* Note: we shouldn't get to this point! */
590 	panic("Could not reboot!");
591 	exit(0);
592 }
593 
594 void
595 command_boot(char *arg)
596 {
597 	char *filename;
598 	int howto;
599 
600 	if (parseboot(arg, &filename, &howto))
601 		bootit(filename, howto, 1);
602 }
603 
604 void
605 command_dev(char *arg)
606 {
607 	static char savedevname[MAXDEVNAME + 1];
608 	char *fsname, *devname;
609 	const char *file; /* dummy */
610 
611 	if (*arg == '\0') {
612 		printf("%s%d%c:\n", default_devname, default_unit,
613 		       'a' + default_partition);
614 		return;
615 	}
616 
617 	if (strchr(arg, ':') == NULL ||
618 	    parsebootfile(arg, &fsname, &devname, &default_unit,
619 			  &default_partition, &file)) {
620 		command_help(NULL);
621 		return;
622 	}
623 
624 	/* put to own static storage */
625 	strncpy(savedevname, devname, MAXDEVNAME + 1);
626 	default_devname = savedevname;
627 }
628 
629 static const struct cons_devs {
630 	const char	*name;
631 	u_int		tag;
632 } cons_devs[] = {
633 	{ "pc",		CONSDEV_PC },
634 	{ "com0",	CONSDEV_COM0 },
635 	{ "com1",	CONSDEV_COM1 },
636 	{ "com2",	CONSDEV_COM2 },
637 	{ "com3",	CONSDEV_COM3 },
638 	{ "com0kbd",	CONSDEV_COM0KBD },
639 	{ "com1kbd",	CONSDEV_COM1KBD },
640 	{ "com2kbd",	CONSDEV_COM2KBD },
641 	{ "com3kbd",	CONSDEV_COM3KBD },
642 	{ "auto",	CONSDEV_AUTO },
643 	{ NULL,		0 }
644 };
645 
646 void
647 command_consdev(char *arg)
648 {
649 	const struct cons_devs *cdp;
650 
651 	for (cdp = cons_devs; cdp->name; cdp++) {
652 		if (strcmp(arg, cdp->name) == 0) {
653 			initio(cdp->tag);
654 			print_banner();
655 			return;
656 		}
657 	}
658 	printf("invalid console device.\n");
659 }
660