xref: /netbsd-src/sys/arch/i386/stand/boot/boot2.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /*	$NetBSD: boot2.c,v 1.79 2022/06/08 21:43:45 wiz 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 _STANDALONE
87 #include <bootinfo.h>
88 #endif
89 #ifdef SUPPORT_PS2
90 #include <biosmca.h>
91 #endif
92 
93 extern struct x86_boot_params boot_params;
94 
95 extern	const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
96 
97 int errno;
98 
99 int boot_biosdev;
100 daddr_t boot_biossector;
101 
102 static const char * const names[][2] = {
103 	{ "netbsd", "netbsd.gz" },
104 	{ "onetbsd", "onetbsd.gz" },
105 	{ "netbsd.old", "netbsd.old.gz" },
106 };
107 
108 #define NUMNAMES (sizeof(names)/sizeof(names[0]))
109 #define DEFFILENAME names[0][0]
110 
111 #ifndef NO_GPT
112 #define MAXDEVNAME 39 /* "NAME=" + 34 char part_name */
113 #else
114 #define MAXDEVNAME 16
115 #endif
116 
117 static char *default_devname;
118 static int default_unit, default_partition;
119 static const char *default_filename;
120 static const char *default_part_name;
121 
122 char *sprint_bootsel(const char *);
123 static void bootit(const char *, int);
124 void boot2(int, uint64_t);
125 
126 void	command_help(char *);
127 #if LIBSA_ENABLE_LS_OP
128 void	command_ls(char *);
129 #endif
130 void	command_quit(char *);
131 void	command_boot(char *);
132 void	command_pkboot(char *);
133 void	command_dev(char *);
134 void	command_consdev(char *);
135 void	command_root(char *);
136 #ifndef SMALL
137 void	command_menu(char *);
138 #endif
139 void	command_modules(char *);
140 void	command_multiboot(char *);
141 
142 const struct bootblk_command commands[] = {
143 	{ "help",	command_help },
144 	{ "?",		command_help },
145 #if LIBSA_ENABLE_LS_OP
146 	{ "ls",		command_ls },
147 #endif
148 	{ "quit",	command_quit },
149 	{ "boot",	command_boot },
150 	{ "pkboot",	command_pkboot },
151 	{ "dev",	command_dev },
152 	{ "consdev",	command_consdev },
153 	{ "root",	command_root },
154 #ifndef SMALL
155 	{ "menu",	command_menu },
156 #endif
157 	{ "modules",	command_modules },
158 	{ "load",	module_add },
159 	{ "multiboot",	command_multiboot },
160 	{ "vesa",	command_vesa },
161 	{ "splash",	splash_add },
162 	{ "rndseed",	rnd_add },
163 	{ "fs",		fs_add },
164 	{ "userconf",	userconf_add },
165 	{ NULL,		NULL },
166 };
167 
168 int
169 parsebootfile(const char *fname, char **fsname, char **devname,
170 	      int *unit, int *partition, const char **file)
171 {
172 	const char *col;
173 	static char savedevname[MAXDEVNAME+1];
174 
175 	*fsname = "ufs";
176 	if (default_part_name == NULL) {
177 		*devname = default_devname;
178 	} else {
179 		snprintf(savedevname, sizeof(savedevname),
180 		    "NAME=%s", default_part_name);
181 		*devname = savedevname;
182 	}
183 	*unit = default_unit;
184 	*partition = default_partition;
185 	*file = default_filename;
186 
187 	if (fname == NULL)
188 		return 0;
189 
190 	if ((col = strchr(fname, ':')) != NULL) {	/* device given */
191 		int devlen;
192 		int u = 0, p = 0;
193 		int i = 0;
194 
195 		devlen = col - fname;
196 		if (devlen > MAXDEVNAME)
197 			return EINVAL;
198 
199 #ifndef NO_GPT
200 		if (strstr(fname, "NAME=") == fname) {
201 			strlcpy(savedevname, fname, devlen + 1);
202 			*devname = savedevname;
203 			*unit = -1;
204 			*partition = -1;
205 			fname = col + 1;
206 			goto out;
207 		}
208 #endif
209 
210 #define isvalidname(c) ((c) >= 'a' && (c) <= 'z')
211 		if (!isvalidname(fname[i]))
212 			return EINVAL;
213 		do {
214 			savedevname[i] = fname[i];
215 			i++;
216 		} while (isvalidname(fname[i]));
217 		savedevname[i] = '\0';
218 
219 #define isnum(c) ((c) >= '0' && (c) <= '9')
220 		if (i < devlen) {
221 			if (!isnum(fname[i]))
222 				return EUNIT;
223 			do {
224 				u *= 10;
225 				u += fname[i++] - '0';
226 			} while (isnum(fname[i]));
227 		}
228 
229 #define isvalidpart(c) ((c) >= 'a' && (c) <= 'z')
230 		if (i < devlen) {
231 			if (!isvalidpart(fname[i]))
232 				return EPART;
233 			p = fname[i++] - 'a';
234 		}
235 
236 		if (i != devlen)
237 			return ENXIO;
238 
239 		*devname = savedevname;
240 		*unit = u;
241 		*partition = p;
242 		fname = col + 1;
243 	}
244 
245 out:
246 	if (*fname)
247 		*file = fname;
248 
249 	return 0;
250 }
251 
252 char *
253 sprint_bootsel(const char *filename)
254 {
255 	char *fsname, *devname;
256 	int unit, partition;
257 	const char *file;
258 	static char buf[80];
259 
260 	if (parsebootfile(filename, &fsname, &devname, &unit,
261 			  &partition, &file) == 0) {
262 		if (strstr(devname, "NAME=") == devname)
263 			snprintf(buf, sizeof(buf), "%s:%s", devname, file);
264 		else
265 			snprintf(buf, sizeof(buf), "%s%d%c:%s", devname, unit,
266 			    'a' + partition, file);
267 		return buf;
268 	}
269 	return "(invalid)";
270 }
271 
272 static void
273 clearit(void)
274 {
275 
276 	if (bootcfg_info.clear)
277 		clear_pc_screen();
278 }
279 
280 static void
281 bootit(const char *filename, int howto)
282 {
283 	if (howto & AB_VERBOSE)
284 		printf("booting %s (howto 0x%x)\n", sprint_bootsel(filename),
285 		    howto);
286 #ifdef KERNEL_DIR
287 	char path[512];
288 	strcpy(path, filename);
289 	strcat(path, "/kernel");
290 	(void)exec_netbsd(path, 0, howto, boot_biosdev < 0x80, clearit);
291 #endif
292 
293 	if (exec_netbsd(filename, 0, howto, boot_biosdev < 0x80, clearit) < 0)
294 		printf("boot: %s: %s\n", sprint_bootsel(filename),
295 		       strerror(errno));
296 	else
297 		printf("boot returned\n");
298 }
299 
300 /*
301  * Called from the initial entry point boot_start in biosboot.S
302  *
303  * biosdev: BIOS drive number the system booted from
304  * biossector: Sector number of the NetBSD partition
305  */
306 void
307 boot2(int biosdev, uint64_t biossector)
308 {
309 	extern char twiddle_toggle;
310 	int currname;
311 	char c;
312 
313 	twiddle_toggle = 1;	/* no twiddling until we're ready */
314 
315 	initio(boot_params.bp_consdev);
316 
317 #ifdef SUPPORT_PS2
318 	biosmca();
319 #endif
320 	gateA20();
321 
322 	boot_modules_enabled = !(boot_params.bp_flags
323 				 & X86_BP_FLAGS_NOMODULES);
324 	if (boot_params.bp_flags & X86_BP_FLAGS_RESET_VIDEO)
325 		biosvideomode();
326 
327 	vbe_init();
328 
329 	/* need to remember these */
330 	boot_biosdev = biosdev;
331 	boot_biossector = biossector;
332 
333 	/* try to set default device to what BIOS tells us */
334 	bios2dev(biosdev, biossector, &default_devname, &default_unit,
335 		 &default_partition, &default_part_name);
336 
337 	/* if the user types "boot" without filename */
338 	default_filename = DEFFILENAME;
339 
340 #ifndef SMALL
341 	if (!(boot_params.bp_flags & X86_BP_FLAGS_NOBOOTCONF)) {
342 		parsebootconf(BOOTCFG_FILENAME);
343 	} else {
344 		bootcfg_info.timeout = boot_params.bp_timeout;
345 	}
346 
347 
348 	/*
349 	 * If console set in boot.cfg, switch to it.
350 	 * This will print the banner, so we don't need to explicitly do it
351 	 */
352 	if (bootcfg_info.consdev) {
353 		command_consdev(bootcfg_info.consdev);
354 	} else {
355 		clearit();
356 		print_bootcfg_banner(bootprog_name, bootprog_rev);
357 	}
358 
359 	/* Display the menu, if applicable */
360 	twiddle_toggle = 0;
361 	if (bootcfg_info.nummenu > 0) {
362 		/* Does not return */
363 		doboottypemenu();
364 	}
365 
366 #else
367 	twiddle_toggle = 0;
368 	clearit();
369 	print_bootcfg_banner(bootprog_name, bootprog_rev);
370 #endif
371 
372 	printf("Press return to boot now, any other key for boot menu\n");
373 	for (currname = 0; currname < NUMNAMES; currname++) {
374 		printf("booting %s - starting in ",
375 		       sprint_bootsel(names[currname][0]));
376 
377 #ifdef SMALL
378 		c = awaitkey(boot_params.bp_timeout, 1);
379 #else
380 		c = awaitkey((bootcfg_info.timeout < 0) ? 0
381 		    : bootcfg_info.timeout, 1);
382 #endif
383 		if ((c != '\r') && (c != '\n') && (c != '\0')) {
384 		    if ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0) {
385 			/* do NOT ask for password */
386 			bootmenu(); /* does not return */
387 		    } else {
388 			/* DO ask for password */
389 			if (check_password((char *)boot_params.bp_password)) {
390 			    /* password ok */
391 			    printf("type \"?\" or \"help\" for help.\n");
392 			    bootmenu(); /* does not return */
393 			} else {
394 			    /* bad password */
395 			    printf("Wrong password.\n");
396 			    currname = 0;
397 			    continue;
398 			}
399 		    }
400 		}
401 
402 		/*
403 		 * try pairs of names[] entries, foo and foo.gz
404 		 */
405 		/* don't print "booting..." again */
406 		bootit(names[currname][0], 0);
407 		/* since it failed, try compressed bootfile. */
408 		bootit(names[currname][1], AB_VERBOSE);
409 	}
410 
411 	bootmenu();	/* does not return */
412 }
413 
414 /* ARGSUSED */
415 void
416 command_help(char *arg)
417 {
418 
419 	printf("commands are:\n"
420 	       "boot [dev:][filename] [-12acdqsvxz]\n"
421 #ifndef NO_RAIDFRAME
422 	       "     dev syntax is (hd|fd|cd|raid)[N[x]]\n"
423 #else
424 	       "     dev syntax is (hd|fd|cd)[N[x]]n"
425 #endif
426 #ifndef NO_GPT
427 	       "                or NAME=gpt_label\n"
428 #endif
429 	       "     (ex. \"hd0a:netbsd.old -s\")\n"
430 	       "pkboot [dev:][filename] [-12acdqsvxz]\n"
431 #if LIBSA_ENABLE_LS_OP
432 	       "ls [dev:][path]\n"
433 #endif
434 	       "dev [dev:]\n"
435 	       "consdev {pc|{com[0123]|com[0123]kbd|auto}[,{speed}]}\n"
436 	       "root    {spec}\n"
437 	       "     spec can be disk, e.g. wd0, sd0\n"
438 	       "     or string like wedge:name\n"
439 	       "vesa {modenum|on|off|enabled|disabled|list}\n"
440 #ifndef SMALL
441 	       "menu (reenters boot menu, if defined in boot.cfg)\n"
442 #endif
443 	       "modules {on|off|enabled|disabled}\n"
444 	       "load {path_to_module}\n"
445 	       "multiboot [dev:][filename] [<args>]\n"
446 	       "splash {path_to_image_file}\n"
447 	       "userconf {command}\n"
448 	       "rndseed {path_to_rndseed_file}\n"
449 	       "help|?\n"
450 	       "quit\n");
451 }
452 
453 #if LIBSA_ENABLE_LS_OP
454 void
455 command_ls(char *arg)
456 {
457 	const char *save = default_filename;
458 
459 	default_filename = "/";
460 	ls(arg);
461 	default_filename = save;
462 }
463 #endif
464 
465 /* ARGSUSED */
466 void
467 command_quit(char *arg)
468 {
469 
470 	printf("Exiting...\n");
471 	delay(1000000);
472 	reboot();
473 	/* Note: we shouldn't get to this point! */
474 	panic("Could not reboot!");
475 }
476 
477 void
478 command_boot(char *arg)
479 {
480 	char *filename;
481 	int howto;
482 
483 	if (!parseboot(arg, &filename, &howto))
484 		return;
485 
486 	if (filename != NULL) {
487 		bootit(filename, howto);
488 	} else {
489 		int i;
490 
491 		for (i = 0; i < NUMNAMES; i++) {
492 			bootit(names[i][0], howto);
493 			bootit(names[i][1], howto);
494 		}
495 	}
496 }
497 
498 void
499 command_pkboot(char *arg)
500 {
501 	extern int has_prekern;
502 	has_prekern = 1;
503 	command_boot(arg);
504 	has_prekern = 0;
505 }
506 
507 void
508 command_dev(char *arg)
509 {
510 	static char savedevname[MAXDEVNAME + 1];
511 	char *fsname, *devname;
512 	const char *file; /* dummy */
513 
514 	if (*arg == '\0') {
515 		biosdisk_probe();
516 
517 #ifndef NO_GPT
518 		if (default_part_name)
519 			printf("default NAME=%s on %s%d\n", default_part_name,
520 			       default_devname, default_unit);
521 		else
522 #endif
523 			printf("default %s%d%c\n",
524 			       default_devname, default_unit,
525 			       'a' + default_partition);
526 		return;
527 	}
528 
529 	if (strchr(arg, ':') == NULL ||
530 	    parsebootfile(arg, &fsname, &devname, &default_unit,
531 			  &default_partition, &file)) {
532 		command_help(NULL);
533 		return;
534 	}
535 
536 	/* put to own static storage */
537 	strncpy(savedevname, devname, MAXDEVNAME + 1);
538 	default_devname = savedevname;
539 
540 	/* +5 to skip leading NAME= */
541 	if (strstr(devname, "NAME=") == devname)
542 		default_part_name = default_devname + 5;
543 }
544 
545 static const struct cons_devs {
546 	const char	*name;
547 	u_int		tag;
548 } cons_devs[] = {
549 	{ "pc",		CONSDEV_PC },
550 	{ "com0",	CONSDEV_COM0 },
551 	{ "com1",	CONSDEV_COM1 },
552 	{ "com2",	CONSDEV_COM2 },
553 	{ "com3",	CONSDEV_COM3 },
554 	{ "com0kbd",	CONSDEV_COM0KBD },
555 	{ "com1kbd",	CONSDEV_COM1KBD },
556 	{ "com2kbd",	CONSDEV_COM2KBD },
557 	{ "com3kbd",	CONSDEV_COM3KBD },
558 	{ "auto",	CONSDEV_AUTO },
559 	{ NULL,		0 }
560 };
561 
562 void
563 command_consdev(char *arg)
564 {
565 	const struct cons_devs *cdp;
566 	char *sep;
567 	int speed;
568 
569 	sep = strchr(arg, ',');
570 	if (sep != NULL)
571 		*sep++ = '\0';
572 
573 	for (cdp = cons_devs; cdp->name; cdp++) {
574 		if (strcmp(arg, cdp->name) != 0)
575 			continue;
576 
577 		if (sep != NULL) {
578 			if (cdp->tag == CONSDEV_PC)
579 				goto error;
580 
581 			speed = atoi(sep);
582 			if (speed < 0)
583 				goto error;
584 			boot_params.bp_conspeed = speed;
585 		}
586 
587 		initio(cdp->tag);
588 		clearit();
589 		print_bootcfg_banner(bootprog_name, bootprog_rev);
590 		return;
591 	}
592 error:
593 	printf("invalid console device.\n");
594 }
595 
596 void
597 command_root(char *arg)
598 {
599 	struct btinfo_rootdevice *biv = &bi_root;
600 
601 	strncpy(biv->devname, arg, sizeof(biv->devname));
602 	if (biv->devname[sizeof(biv->devname)-1] != '\0') {
603 		biv->devname[sizeof(biv->devname)-1] = '\0';
604 		printf("truncated to %s\n",biv->devname);
605 	}
606 }
607 
608 #ifndef SMALL
609 /* ARGSUSED */
610 void
611 command_menu(char *arg)
612 {
613 
614 	if (bootcfg_info.nummenu > 0) {
615 		/* Does not return */
616 		doboottypemenu();
617 	} else {
618 		printf("No menu defined in boot.cfg\n");
619 	}
620 }
621 #endif /* !SMALL */
622 
623 void
624 command_modules(char *arg)
625 {
626 
627 	if (strcmp(arg, "enabled") == 0 ||
628 	    strcmp(arg, "on") == 0)
629 		boot_modules_enabled = true;
630 	else if (strcmp(arg, "disabled") == 0 ||
631 	    strcmp(arg, "off") == 0)
632 		boot_modules_enabled = false;
633 	else
634 		printf("invalid flag, must be 'enabled' or 'disabled'.\n");
635 }
636 
637 void
638 command_multiboot(char *arg)
639 {
640 	char *filename;
641 
642 	filename = arg;
643 	if (exec_multiboot(filename, gettrailer(arg)) < 0)
644 		printf("multiboot: %s: %s\n", sprint_bootsel(filename),
645 		       strerror(errno));
646 	else
647 		printf("boot returned\n");
648 }
649 
650