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