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
93extern struct x86_boot_params boot_params;
94
95extern	const char bootprog_name[], bootprog_rev[], bootprog_kernrev[];
96
97int errno;
98
99int boot_biosdev;
100daddr_t boot_biossector;
101
102static 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
117static char *default_devname;
118static int default_unit, default_partition;
119static const char *default_filename;
120static const char *default_part_name;
121
122char *sprint_bootsel(const char *);
123static void bootit(const char *, int);
124void boot2(int, uint64_t);
125
126void	command_help(char *);
127#if LIBSA_ENABLE_LS_OP
128void	command_ls(char *);
129#endif
130void	command_quit(char *);
131void	command_boot(char *);
132void	command_pkboot(char *);
133void	command_dev(char *);
134void	command_consdev(char *);
135void	command_root(char *);
136#ifndef SMALL
137void	command_menu(char *);
138#endif
139void	command_modules(char *);
140void	command_multiboot(char *);
141
142const 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
168int
169parsebootfile(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
245out:
246	if (*fname)
247		*file = fname;
248
249	return 0;
250}
251
252char *
253sprint_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
272static void
273clearit(void)
274{
275
276	if (bootcfg_info.clear)
277		clear_pc_screen();
278}
279
280static void
281bootit(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 */
306void
307boot2(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 */
415void
416command_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
454void
455command_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 */
466void
467command_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
477void
478command_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
498void
499command_pkboot(char *arg)
500{
501	extern int has_prekern;
502	has_prekern = 1;
503	command_boot(arg);
504	has_prekern = 0;
505}
506
507void
508command_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
545static 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
562void
563command_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	}
592error:
593	printf("invalid console device.\n");
594}
595
596void
597command_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 */
610void
611command_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
623void
624command_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
637void
638command_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