138465Smsmith/*-
238465Smsmith * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
338465Smsmith * All rights reserved.
438465Smsmith *
538465Smsmith * Redistribution and use in source and binary forms, with or without
638465Smsmith * modification, are permitted provided that the following conditions
738465Smsmith * are met:
838465Smsmith * 1. Redistributions of source code must retain the above copyright
938465Smsmith *    notice, this list of conditions and the following disclaimer.
1038465Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1138465Smsmith *    notice, this list of conditions and the following disclaimer in the
1238465Smsmith *    documentation and/or other materials provided with the distribution.
1338465Smsmith *
1438465Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538465Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638465Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738465Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838465Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938465Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038465Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138465Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238465Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338465Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438465Smsmith * SUCH DAMAGE.
2538465Smsmith */
2638465Smsmith
27119483Sobrien#include <sys/cdefs.h>
28119483Sobrien__FBSDID("$FreeBSD: stable/11/stand/common/boot.c 344378 2019-02-20 19:19:24Z kevans $");
29119483Sobrien
3038465Smsmith/*
3138465Smsmith * Loading modules, booting the system
3238465Smsmith */
3338465Smsmith
3438465Smsmith#include <stand.h>
35335755Skevans#include <sys/reboot.h>
36335755Skevans#include <sys/boot.h>
3738465Smsmith#include <string.h>
3838465Smsmith
3938465Smsmith#include "bootstrap.h"
4038465Smsmith
41334572Sdimstatic int	autoboot(int timeout, char *prompt);
4238465Smsmithstatic char	*getbootfile(int try);
4365613Sdcsstatic int	loadakernel(int try, int argc, char* argv[]);
4438465Smsmith
4538465Smsmith/* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */
4668851Smsmithstatic const char *default_bootfiles = "kernel";
4738465Smsmith
4840015Smsmithstatic int autoboot_tried;
4940015Smsmith
5038465Smsmith/*
5138465Smsmith * The user wants us to boot.
5238465Smsmith */
5338465SmsmithCOMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot);
5438465Smsmith
5538465Smsmithstatic int
5638465Smsmithcommand_boot(int argc, char *argv[])
5738465Smsmith{
58329183Skevans	struct preloaded_file	*fp;
59146365Ssobomax
60329183Skevans	/*
61329183Skevans	 * See if the user has specified an explicit kernel to boot.
62329183Skevans	 */
63329183Skevans	if ((argc > 1) && (argv[1][0] != '-')) {
64146365Ssobomax
65329183Skevans		/* XXX maybe we should discard everything and start again? */
66329183Skevans		if (file_findfile(NULL, NULL) != NULL) {
67329183Skevans			snprintf(command_errbuf, sizeof(command_errbuf),
68329183Skevans			    "can't boot '%s', kernel module already loaded", argv[1]);
69329183Skevans			return(CMD_ERROR);
70329183Skevans		}
71329183Skevans
72329183Skevans		/* find/load the kernel module */
73329183Skevans		if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0)
74329183Skevans			return(CMD_ERROR);
75329183Skevans		/* we have consumed all arguments */
76329183Skevans		argc = 1;
7738465Smsmith	}
78146365Ssobomax
79329183Skevans	/*
80329183Skevans	 * See if there is a kernel module already loaded
81329183Skevans	 */
82329183Skevans	if (file_findfile(NULL, NULL) == NULL)
83329183Skevans		if (loadakernel(0, argc - 1, argv + 1))
84329183Skevans			/* we have consumed all arguments */
85329183Skevans			argc = 1;
8638465Smsmith
87329183Skevans	/*
88329183Skevans	 * Loaded anything yet?
89329183Skevans	 */
90329183Skevans	if ((fp = file_findfile(NULL, NULL)) == NULL) {
91329183Skevans		command_errmsg = "no bootable kernel";
92329183Skevans		return(CMD_ERROR);
93329183Skevans	}
9438465Smsmith
95329183Skevans	/*
96329183Skevans	 * If we were given arguments, discard any previous.
97329183Skevans	 * XXX should we merge arguments?  Hard to DWIM.
98329183Skevans	 */
99329183Skevans	if (argc > 1) {
100329183Skevans		if (fp->f_args != NULL)
101329183Skevans			free(fp->f_args);
102329183Skevans		fp->f_args = unargv(argc - 1, argv + 1);
103329183Skevans	}
10438465Smsmith
105329183Skevans	/* Hook for platform-specific autoloading of modules */
106329183Skevans	if (archsw.arch_autoload() != 0)
107329183Skevans		return(CMD_ERROR);
10838465Smsmith
109329183Skevans	/* Call the exec handler from the loader matching the kernel */
110329183Skevans	file_formats[fp->f_loader]->l_exec(fp);
11138465Smsmith	return(CMD_ERROR);
11238465Smsmith}
11338465Smsmith
11438465Smsmith
11538465Smsmith/*
11638465Smsmith * Autoboot after a delay
11738465Smsmith */
11838465Smsmith
11938465SmsmithCOMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot);
12038465Smsmith
12138465Smsmithstatic int
12238465Smsmithcommand_autoboot(int argc, char *argv[])
12338465Smsmith{
124329183Skevans	int		howlong;
125329183Skevans	char	*cp, *prompt;
12638465Smsmith
127329183Skevans	prompt = NULL;
128329183Skevans	howlong = -1;
129329183Skevans	switch(argc) {
130329183Skevans	case 3:
131329183Skevans		prompt = argv[2];
132329183Skevans		/* FALLTHROUGH */
133329183Skevans	case 2:
134329183Skevans		howlong = strtol(argv[1], &cp, 0);
135329183Skevans		if (*cp != 0) {
136329183Skevans			snprintf(command_errbuf, sizeof(command_errbuf),
137329183Skevans			    "bad delay '%s'", argv[1]);
138329183Skevans			return(CMD_ERROR);
139329183Skevans		}
140329183Skevans		/* FALLTHROUGH */
141329183Skevans	case 1:
142329183Skevans		return(autoboot(howlong, prompt));
14338465Smsmith	}
144146365Ssobomax
145329183Skevans	command_errmsg = "too many arguments";
146329183Skevans	return(CMD_ERROR);
14738465Smsmith}
14838465Smsmith
14940015Smsmith/*
15040015Smsmith * Called before we go interactive.  If we think we can autoboot, and
15140015Smsmith * we haven't tried already, try now.
15240015Smsmith */
15340015Smsmithvoid
15440015Smsmithautoboot_maybe()
15540015Smsmith{
156329183Skevans	char	*cp;
157146365Ssobomax
158329183Skevans	cp = getenv("autoboot_delay");
159329183Skevans	if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO")))
160329183Skevans		autoboot(-1, NULL);		/* try to boot automatically */
16140015Smsmith}
16240015Smsmith
163334572Sdimstatic int
16464187Sjhbautoboot(int timeout, char *prompt)
16538465Smsmith{
166329183Skevans	time_t	when, otime, ntime;
167329183Skevans	int		c, yes;
168329183Skevans	char	*argv[2], *cp, *ep;
169329183Skevans	char	*kernelname;
170199210Sattilio#ifdef BOOT_PROMPT_123
171329183Skevans	const char	*seq = "123", *p = seq;
172199210Sattilio#endif
17338465Smsmith
174329183Skevans	autoboot_tried = 1;
17538465Smsmith
176329183Skevans	if (timeout == -1) {
177329183Skevans		timeout = 10;
178329183Skevans		/* try to get a delay from the environment */
179329183Skevans		if ((cp = getenv("autoboot_delay"))) {
180329183Skevans			timeout = strtol(cp, &ep, 0);
181329183Skevans			if (cp == ep)
182329183Skevans				timeout = 10;		/* Unparseable? Set default! */
183329183Skevans		}
18440015Smsmith	}
18540015Smsmith
18665613Sdcs	kernelname = getenv("kernelname");
18765613Sdcs	if (kernelname == NULL) {
188329183Skevans		argv[0] = NULL;
189329183Skevans		loadakernel(0, 0, argv);
190329183Skevans		kernelname = getenv("kernelname");
191329183Skevans		if (kernelname == NULL) {
192329183Skevans			command_errmsg = "no valid kernel found";
193329183Skevans			return(CMD_ERROR);
194329183Skevans		}
19565613Sdcs	}
19665613Sdcs
197329183Skevans	if (timeout >= 0) {
198329183Skevans		otime = time(NULL);
199329183Skevans		when = otime + timeout;	/* when to boot */
20065881Sdcs
201329183Skevans		yes = 0;
20238465Smsmith
203199210Sattilio#ifdef BOOT_PROMPT_123
204329183Skevans		printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or "
205329183Skevans		    "1 2 3 sequence for command prompt." : prompt);
206199210Sattilio#else
207329183Skevans		printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt);
208199210Sattilio#endif
209146365Ssobomax
210329183Skevans		for (;;) {
211329183Skevans			if (ischar()) {
212329183Skevans				c = getchar();
213199210Sattilio#ifdef BOOT_PROMPT_123
214329183Skevans				if ((c == '\r') || (c == '\n')) {
215329183Skevans					yes = 1;
216329183Skevans					break;
217329183Skevans				} else if (c != *p++)
218329183Skevans					p = seq;
219329183Skevans				if (*p == 0)
220329183Skevans					break;
221199210Sattilio#else
222329183Skevans				if ((c == '\r') || (c == '\n'))
223329183Skevans					yes = 1;
224329183Skevans				break;
225199210Sattilio#endif
226329183Skevans			}
227329183Skevans			ntime = time(NULL);
228329183Skevans			if (ntime >= when) {
229329183Skevans				yes = 1;
230329183Skevans				break;
231329183Skevans			}
232146421Ssobomax
233329183Skevans			if (ntime != otime) {
234329183Skevans				printf("\rBooting [%s] in %d second%s... ",
235329183Skevans				    kernelname, (int)(when - ntime),
236329183Skevans				    (when-ntime)==1?"":"s");
237329183Skevans				otime = ntime;
238329183Skevans			}
239329183Skevans		}
240329183Skevans	} else {
241329183Skevans		yes = 1;
242329183Skevans	}
243146421Ssobomax
244329183Skevans	if (yes)
245329183Skevans		printf("\rBooting [%s]...               ", kernelname);
246329183Skevans	putchar('\n');
247329183Skevans	if (yes) {
248329183Skevans		argv[0] = "boot";
249329183Skevans		argv[1] = NULL;
250329183Skevans		return(command_boot(1, argv));
251329183Skevans	}
252329183Skevans	return(CMD_OK);
25338465Smsmith}
25438465Smsmith
25538465Smsmith/*
25638465Smsmith * Scrounge for the name of the (try)'th file we will try to boot.
25738465Smsmith */
25838465Smsmithstatic char *
259146365Ssobomaxgetbootfile(int try)
26038465Smsmith{
261329183Skevans	static char *name = NULL;
262329183Skevans	const char	*spec, *ep;
263329183Skevans	size_t	len;
264146365Ssobomax
265329183Skevans	/* we use dynamic storage */
266329183Skevans	if (name != NULL) {
267329183Skevans		free(name);
268329183Skevans		name = NULL;
269329183Skevans	}
270146365Ssobomax
271329183Skevans	/*
272329183Skevans	 * Try $bootfile, then try our builtin default
273329183Skevans	 */
274329183Skevans	if ((spec = getenv("bootfile")) == NULL)
275329183Skevans		spec = default_bootfiles;
27638465Smsmith
277329183Skevans	while ((try > 0) && (spec != NULL)) {
278329183Skevans		spec = strchr(spec, ';');
279329183Skevans		if (spec)
280329183Skevans			spec++;	/* skip over the leading ';' */
281329183Skevans		try--;
28238465Smsmith	}
283329183Skevans	if (spec != NULL) {
284329183Skevans		if ((ep = strchr(spec, ';')) != NULL) {
285329183Skevans			len = ep - spec;
286329183Skevans		} else {
287329183Skevans			len = strlen(spec);
288329183Skevans		}
289329183Skevans		name = malloc(len + 1);
290329183Skevans		strncpy(name, spec, len);
291329183Skevans		name[len] = 0;
292329183Skevans	}
293329183Skevans	if (name && name[0] == 0) {
294329183Skevans		free(name);
295329183Skevans		name = NULL;
296329183Skevans	}
297329183Skevans	return(name);
29838465Smsmith}
29938465Smsmith
30048952Smsmith/*
30148952Smsmith * Try to find the /etc/fstab file on the filesystem (rootdev),
302146365Ssobomax * which should be be the root filesystem, and parse it to find
30348952Smsmith * out what the kernel ought to think the root filesystem is.
30448952Smsmith *
30548952Smsmith * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
30648952Smsmith * so that the kernel can tell both which VFS and which node to use
30748952Smsmith * to mount the device.  If this variable's already set, don't
30848952Smsmith * overwrite it.
30948952Smsmith */
31048952Smsmithint
31148952Smsmithgetrootmount(char *rootdev)
31248952Smsmith{
313329183Skevans	char	lbuf[128], *cp, *ep, *dev, *fstyp, *options;
314329183Skevans	int		fd, error;
31548952Smsmith
316329183Skevans	if (getenv("vfs.root.mountfrom") != NULL)
317329183Skevans		return(0);
31848952Smsmith
319329183Skevans	error = 1;
320329183Skevans	sprintf(lbuf, "%s/etc/fstab", rootdev);
321329183Skevans	if ((fd = open(lbuf, O_RDONLY)) < 0)
322329183Skevans		goto notfound;
32348952Smsmith
324329183Skevans	/* loop reading lines from /etc/fstab    What was that about sscanf again? */
325329183Skevans	fstyp = NULL;
326329183Skevans	dev = NULL;
327329183Skevans	while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) {
328329183Skevans		if ((lbuf[0] == 0) || (lbuf[0] == '#'))
329329183Skevans			continue;
330146365Ssobomax
331329183Skevans		/* skip device name */
332329183Skevans		for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
333329183Skevans			;
334329183Skevans		if (*cp == 0)		/* misformatted */
335329183Skevans			continue;
336329183Skevans		/* delimit and save */
337329183Skevans		*cp++ = 0;
338329183Skevans		free(dev);
339329183Skevans		dev = strdup(lbuf);
340146365Ssobomax
341329183Skevans		/* skip whitespace up to mountpoint */
342329183Skevans		while ((*cp != 0) && isspace(*cp))
343329183Skevans			cp++;
344329183Skevans		/* must have /<space> to be root */
345329183Skevans		if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1)))
346329183Skevans			continue;
347329183Skevans		/* skip whitespace up to fstype */
348329183Skevans		cp += 2;
349329183Skevans		while ((*cp != 0) && isspace(*cp))
350329183Skevans			cp++;
351329183Skevans		if (*cp == 0)		/* misformatted */
352329183Skevans			continue;
353329183Skevans		/* skip text to end of fstype and delimit */
354329183Skevans		ep = cp;
355329183Skevans		while ((*cp != 0) && !isspace(*cp))
356329183Skevans			cp++;
357329183Skevans		*cp = 0;
358329183Skevans		free(fstyp);
359329183Skevans		fstyp = strdup(ep);
36048952Smsmith
361329183Skevans		/* skip whitespace up to mount options */
362329183Skevans		cp += 1;
363329183Skevans		while ((*cp != 0) && isspace(*cp))
364329183Skevans			cp++;
365329183Skevans		if (*cp == 0)           /* misformatted */
366329183Skevans			continue;
367329183Skevans		/* skip text to end of mount options and delimit */
368329183Skevans		ep = cp;
369329183Skevans		while ((*cp != 0) && !isspace(*cp))
370329183Skevans			cp++;
371329183Skevans		*cp = 0;
372329183Skevans		options = strdup(ep);
373329183Skevans		/* Build the <fstype>:<device> and save it in vfs.root.mountfrom */
374329183Skevans		sprintf(lbuf, "%s:%s", fstyp, dev);
375329183Skevans		setenv("vfs.root.mountfrom", lbuf, 0);
376329183Skevans
377329183Skevans		/* Don't override vfs.root.mountfrom.options if it is already set */
378329183Skevans		if (getenv("vfs.root.mountfrom.options") == NULL) {
379329183Skevans			/* save mount options */
380329183Skevans			setenv("vfs.root.mountfrom.options", options, 0);
381329183Skevans		}
382329183Skevans		free(options);
383329183Skevans		error = 0;
384329183Skevans		break;
385329183Skevans	}
386329183Skevans	close(fd);
38748952Smsmith	free(dev);
38848952Smsmith	free(fstyp);
389193192Srodrigc
390235330Savgnotfound:
391329183Skevans	if (error) {
392329183Skevans		const char *currdev;
393235330Savg
394329183Skevans		currdev = getenv("currdev");
395329183Skevans		if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) {
396329183Skevans			cp = strdup(currdev);
397329183Skevans			cp[strlen(cp) - 1] = '\0';
398329183Skevans			setenv("vfs.root.mountfrom", cp, 0);
399329183Skevans			error = 0;
400329183Skevans			free(cp);
401329183Skevans		}
402235330Savg	}
403235330Savg
404329183Skevans	return(error);
40548952Smsmith}
40665613Sdcs
40765613Sdcsstatic int
40865613Sdcsloadakernel(int try, int argc, char* argv[])
40965613Sdcs{
410329183Skevans	char *cp;
41165613Sdcs
41265613Sdcs	for (try = 0; (cp = getbootfile(try)) != NULL; try++)
413329183Skevans		if (mod_loadkld(cp, argc - 1, argv + 1) != 0)
414329183Skevans			printf("can't load '%s'\n", cp);
415329183Skevans		else
416329183Skevans			return 1;
41765613Sdcs	return 0;
41865613Sdcs}
419