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$");
29119483Sobrien
3038465Smsmith/*
3138465Smsmith * Loading modules, booting the system
3238465Smsmith */
3338465Smsmith
3438465Smsmith#include <stand.h>
3538465Smsmith#include <string.h>
3638465Smsmith
3738465Smsmith#include "bootstrap.h"
3838465Smsmith
3938465Smsmithstatic char	*getbootfile(int try);
4065613Sdcsstatic int	loadakernel(int try, int argc, char* argv[]);
4138465Smsmith
4238465Smsmith/* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */
4368851Smsmithstatic const char *default_bootfiles = "kernel";
4438465Smsmith
4540015Smsmithstatic int autoboot_tried;
4640015Smsmith
4738465Smsmith/*
4838465Smsmith * The user wants us to boot.
4938465Smsmith */
5038465SmsmithCOMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot);
5138465Smsmith
5238465Smsmithstatic int
5338465Smsmithcommand_boot(int argc, char *argv[])
5438465Smsmith{
5559854Sbp    struct preloaded_file	*fp;
56146365Ssobomax
5738465Smsmith    /*
5838465Smsmith     * See if the user has specified an explicit kernel to boot.
5938465Smsmith     */
6038465Smsmith    if ((argc > 1) && (argv[1][0] != '-')) {
61146365Ssobomax
6238465Smsmith	/* XXX maybe we should discard everything and start again? */
6359854Sbp	if (file_findfile(NULL, NULL) != NULL) {
6447609Sdfr	    sprintf(command_errbuf, "can't boot '%s', kernel module already loaded", argv[1]);
6538465Smsmith	    return(CMD_ERROR);
6638465Smsmith	}
67146365Ssobomax
6838465Smsmith	/* find/load the kernel module */
6983321Speter	if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0)
7038465Smsmith	    return(CMD_ERROR);
7138465Smsmith	/* we have consumed all arguments */
7238465Smsmith	argc = 1;
7338465Smsmith    }
7438465Smsmith
7538465Smsmith    /*
7638465Smsmith     * See if there is a kernel module already loaded
7738465Smsmith     */
7865613Sdcs    if (file_findfile(NULL, NULL) == NULL)
7965613Sdcs	if (loadakernel(0, argc - 1, argv + 1))
8065613Sdcs	    /* we have consumed all arguments */
8165613Sdcs	    argc = 1;
8238465Smsmith
8338465Smsmith    /*
8438465Smsmith     * Loaded anything yet?
8538465Smsmith     */
8659854Sbp    if ((fp = file_findfile(NULL, NULL)) == NULL) {
8738465Smsmith	command_errmsg = "no bootable kernel";
8838465Smsmith	return(CMD_ERROR);
8938465Smsmith    }
9038465Smsmith
9138465Smsmith    /*
9238465Smsmith     * If we were given arguments, discard any previous.
9338465Smsmith     * XXX should we merge arguments?  Hard to DWIM.
9438465Smsmith     */
9538465Smsmith    if (argc > 1) {
96146365Ssobomax	if (fp->f_args != NULL)
9759854Sbp	    free(fp->f_args);
9859854Sbp	fp->f_args = unargv(argc - 1, argv + 1);
9938465Smsmith    }
10038465Smsmith
10138465Smsmith    /* Hook for platform-specific autoloading of modules */
10238465Smsmith    if (archsw.arch_autoload() != 0)
10338465Smsmith	return(CMD_ERROR);
10438712Smsmith
10538712Smsmith    /* Call the exec handler from the loader matching the kernel */
10659854Sbp    file_formats[fp->f_loader]->l_exec(fp);
10738465Smsmith    return(CMD_ERROR);
10838465Smsmith}
10938465Smsmith
11038465Smsmith
11138465Smsmith/*
11238465Smsmith * Autoboot after a delay
11338465Smsmith */
11438465Smsmith
11538465SmsmithCOMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot);
11638465Smsmith
11738465Smsmithstatic int
11838465Smsmithcommand_autoboot(int argc, char *argv[])
11938465Smsmith{
12038465Smsmith    int		howlong;
12138465Smsmith    char	*cp, *prompt;
12238465Smsmith
12338465Smsmith    prompt = NULL;
12438465Smsmith    howlong = -1;
12538465Smsmith    switch(argc) {
12638465Smsmith    case 3:
12738465Smsmith	prompt = argv[2];
12838465Smsmith	/* FALLTHROUGH */
12938465Smsmith    case 2:
13038465Smsmith	howlong = strtol(argv[1], &cp, 0);
13138465Smsmith	if (*cp != 0) {
13238465Smsmith	    sprintf(command_errbuf, "bad delay '%s'", argv[1]);
13338465Smsmith	    return(CMD_ERROR);
13438465Smsmith	}
13538465Smsmith	/* FALLTHROUGH */
13638465Smsmith    case 1:
13738465Smsmith	return(autoboot(howlong, prompt));
13838465Smsmith    }
139146365Ssobomax
14038465Smsmith    command_errmsg = "too many arguments";
14138465Smsmith    return(CMD_ERROR);
14238465Smsmith}
14338465Smsmith
14440015Smsmith/*
14540015Smsmith * Called before we go interactive.  If we think we can autoboot, and
14640015Smsmith * we haven't tried already, try now.
14740015Smsmith */
14840015Smsmithvoid
14940015Smsmithautoboot_maybe()
15040015Smsmith{
15140015Smsmith    char	*cp;
152146365Ssobomax
15340015Smsmith    cp = getenv("autoboot_delay");
15440015Smsmith    if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO")))
15540015Smsmith	autoboot(-1, NULL);		/* try to boot automatically */
15640015Smsmith}
15740015Smsmith
15838465Smsmithint
15964187Sjhbautoboot(int timeout, char *prompt)
16038465Smsmith{
16138465Smsmith    time_t	when, otime, ntime;
16247576Sbrian    int		c, yes;
16340015Smsmith    char	*argv[2], *cp, *ep;
16465613Sdcs    char	*kernelname;
165199210Sattilio#ifdef BOOT_PROMPT_123
166199210Sattilio    const char	*seq = "123", *p = seq;
167199210Sattilio#endif
16838465Smsmith
16940015Smsmith    autoboot_tried = 1;
17038465Smsmith
17164187Sjhb    if (timeout == -1) {
172146421Ssobomax        timeout = 10;
17340015Smsmith	/* try to get a delay from the environment */
17440015Smsmith	if ((cp = getenv("autoboot_delay"))) {
17564187Sjhb	    timeout = strtol(cp, &ep, 0);
17640015Smsmith	    if (cp == ep)
177146421Ssobomax		timeout = 10;		/* Unparseable? Set default! */
17840015Smsmith	}
17940015Smsmith    }
18040015Smsmith
18165613Sdcs    kernelname = getenv("kernelname");
18265613Sdcs    if (kernelname == NULL) {
18365613Sdcs	argv[0] = NULL;
18465613Sdcs	loadakernel(0, 0, argv);
18565613Sdcs	kernelname = getenv("kernelname");
18665613Sdcs	if (kernelname == NULL) {
18765613Sdcs	    command_errmsg = "no valid kernel found";
18865613Sdcs	    return(CMD_ERROR);
18965613Sdcs	}
19065613Sdcs    }
19165613Sdcs
192146421Ssobomax    if (timeout >= 0) {
193146421Ssobomax        otime = time(NULL);
194146421Ssobomax        when = otime + timeout;	/* when to boot */
19565881Sdcs
196146421Ssobomax        yes = 0;
19738465Smsmith
198199210Sattilio#ifdef BOOT_PROMPT_123
199199210Sattilio        printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or "
200199210Sattilio	    "1 2 3 sequence for command prompt." : prompt);
201199210Sattilio#else
202146421Ssobomax        printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt);
203199210Sattilio#endif
204146365Ssobomax
205146421Ssobomax        for (;;) {
206146421Ssobomax	    if (ischar()) {
207146421Ssobomax	        c = getchar();
208199210Sattilio#ifdef BOOT_PROMPT_123
209199210Sattilio		if ((c == '\r') || (c == '\n')) {
210199210Sattilio			yes = 1;
211199210Sattilio			break;
212199210Sattilio		} else if (c != *p++)
213199210Sattilio			p = seq;
214199210Sattilio		if (*p == 0)
215199210Sattilio			break;
216199210Sattilio#else
217146421Ssobomax	        if ((c == '\r') || (c == '\n'))
218146421Ssobomax		    yes = 1;
219146421Ssobomax	        break;
220199210Sattilio#endif
221146421Ssobomax	    }
222146421Ssobomax	    ntime = time(NULL);
223146421Ssobomax	    if (ntime >= when) {
224146421Ssobomax	        yes = 1;
225146421Ssobomax	        break;
226146421Ssobomax	    }
227146421Ssobomax
228146421Ssobomax	    if (ntime != otime) {
229146421Ssobomax	        printf("\rBooting [%s] in %d second%s... ",
230146421Ssobomax	    		    kernelname, (int)(when - ntime),
231146421Ssobomax			    (when-ntime)==1?"":"s");
232146421Ssobomax	        otime = ntime;
233146421Ssobomax	    }
234146421Ssobomax        }
235146421Ssobomax    } else {
236146421Ssobomax        yes = 1;
23738465Smsmith    }
238146421Ssobomax
23940775Smsmith    if (yes)
24065613Sdcs	printf("\rBooting [%s]...               ", kernelname);
24147576Sbrian    putchar('\n');
24238465Smsmith    if (yes) {
24338465Smsmith	argv[0] = "boot";
24438465Smsmith	argv[1] = NULL;
24538465Smsmith	return(command_boot(1, argv));
24638465Smsmith    }
24738465Smsmith    return(CMD_OK);
24838465Smsmith}
24938465Smsmith
25038465Smsmith/*
25138465Smsmith * Scrounge for the name of the (try)'th file we will try to boot.
25238465Smsmith */
25338465Smsmithstatic char *
254146365Ssobomaxgetbootfile(int try)
25538465Smsmith{
25638465Smsmith    static char *name = NULL;
25765613Sdcs    const char	*spec, *ep;
25864187Sjhb    size_t	len;
259146365Ssobomax
26038465Smsmith    /* we use dynamic storage */
26138465Smsmith    if (name != NULL) {
26238465Smsmith	free(name);
26338465Smsmith	name = NULL;
26438465Smsmith    }
265146365Ssobomax
266146365Ssobomax    /*
26738465Smsmith     * Try $bootfile, then try our builtin default
26838465Smsmith     */
26938465Smsmith    if ((spec = getenv("bootfile")) == NULL)
27038465Smsmith	spec = default_bootfiles;
27138465Smsmith
27239894Smsmith    while ((try > 0) && (spec != NULL)) {
27340832Smsmith	spec = strchr(spec, ';');
27460490Sobrien	if (spec)
27560490Sobrien	    spec++;	/* skip over the leading ';' */
27639894Smsmith	try--;
27739894Smsmith    }
27838465Smsmith    if (spec != NULL) {
27940832Smsmith	if ((ep = strchr(spec, ';')) != NULL) {
28038465Smsmith	    len = ep - spec;
28138465Smsmith	} else {
28238465Smsmith	    len = strlen(spec);
28338465Smsmith	}
28438465Smsmith	name = malloc(len + 1);
28538465Smsmith	strncpy(name, spec, len);
28638465Smsmith	name[len] = 0;
28738465Smsmith    }
28867591Sdfr    if (name && name[0] == 0) {
28939894Smsmith	free(name);
29039894Smsmith	name = NULL;
29139894Smsmith    }
29238465Smsmith    return(name);
29338465Smsmith}
29438465Smsmith
29548952Smsmith/*
29648952Smsmith * Try to find the /etc/fstab file on the filesystem (rootdev),
297146365Ssobomax * which should be be the root filesystem, and parse it to find
29848952Smsmith * out what the kernel ought to think the root filesystem is.
29948952Smsmith *
30048952Smsmith * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
30148952Smsmith * so that the kernel can tell both which VFS and which node to use
30248952Smsmith * to mount the device.  If this variable's already set, don't
30348952Smsmith * overwrite it.
30448952Smsmith */
30548952Smsmithint
30648952Smsmithgetrootmount(char *rootdev)
30748952Smsmith{
308193192Srodrigc    char	lbuf[128], *cp, *ep, *dev, *fstyp, *options;
30948952Smsmith    int		fd, error;
31048952Smsmith
31148952Smsmith    if (getenv("vfs.root.mountfrom") != NULL)
31248952Smsmith	return(0);
31348952Smsmith
314235330Savg    error = 1;
31548952Smsmith    sprintf(lbuf, "%s/etc/fstab", rootdev);
31648952Smsmith    if ((fd = open(lbuf, O_RDONLY)) < 0)
317235330Savg	goto notfound;
31848952Smsmith
31948952Smsmith    /* loop reading lines from /etc/fstab    What was that about sscanf again? */
32048952Smsmith    while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) {
32148952Smsmith	if ((lbuf[0] == 0) || (lbuf[0] == '#'))
32248952Smsmith	    continue;
323146365Ssobomax
32448952Smsmith	/* skip device name */
32548952Smsmith	for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
32648952Smsmith	    ;
32748952Smsmith	if (*cp == 0)		/* misformatted */
32848952Smsmith	    continue;
32948952Smsmith	/* delimit and save */
33048952Smsmith	*cp++ = 0;
33148952Smsmith	dev = strdup(lbuf);
332146365Ssobomax
33348952Smsmith	/* skip whitespace up to mountpoint */
33448952Smsmith	while ((*cp != 0) && isspace(*cp))
33548952Smsmith	    cp++;
33648952Smsmith	/* must have /<space> to be root */
33748952Smsmith	if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1)))
33848952Smsmith	    continue;
33948952Smsmith	/* skip whitespace up to fstype */
34048952Smsmith	cp += 2;
34148952Smsmith	while ((*cp != 0) && isspace(*cp))
34248952Smsmith	    cp++;
34348952Smsmith	if (*cp == 0)		/* misformatted */
34448952Smsmith	    continue;
34548952Smsmith	/* skip text to end of fstype and delimit */
34648952Smsmith	ep = cp;
34748952Smsmith	while ((*cp != 0) && !isspace(*cp))
34848952Smsmith	    cp++;
34948952Smsmith	*cp = 0;
35048952Smsmith	fstyp = strdup(ep);
35148952Smsmith
352193192Srodrigc	/* skip whitespace up to mount options */
353193192Srodrigc	cp += 1;
354193192Srodrigc	while ((*cp != 0) && isspace(*cp))
355193192Srodrigc		cp++;
356193192Srodrigc	if (*cp == 0)           /* misformatted */
357193192Srodrigc		continue;
358193192Srodrigc	/* skip text to end of mount options and delimit */
359193192Srodrigc	ep = cp;
360193192Srodrigc	while ((*cp != 0) && !isspace(*cp))
361193192Srodrigc		cp++;
362193192Srodrigc	*cp = 0;
363193192Srodrigc	options = strdup(ep);
364193192Srodrigc	/* Build the <fstype>:<device> and save it in vfs.root.mountfrom */
36548952Smsmith	sprintf(lbuf, "%s:%s", fstyp, dev);
36648952Smsmith	free(dev);
36748952Smsmith	free(fstyp);
36848952Smsmith	setenv("vfs.root.mountfrom", lbuf, 0);
369193192Srodrigc
370193192Srodrigc	/* Don't override vfs.root.mountfrom.options if it is already set */
371193192Srodrigc	if (getenv("vfs.root.mountfrom.options") == NULL) {
372193192Srodrigc		/* save mount options */
373193192Srodrigc		setenv("vfs.root.mountfrom.options", options, 0);
374193192Srodrigc	}
375193192Srodrigc	free(options);
37648952Smsmith	error = 0;
37748952Smsmith	break;
37848952Smsmith    }
37948952Smsmith    close(fd);
380235330Savg
381235330Savgnotfound:
382235330Savg    if (error) {
383235330Savg	const char *currdev;
384235330Savg
385235330Savg	currdev = getenv("currdev");
386235330Savg	if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) {
387235330Savg	    cp = strdup(currdev);
388235330Savg	    cp[strlen(cp) - 1] = '\0';
389235330Savg	    setenv("vfs.root.mountfrom", cp, 0);
390235330Savg	    error = 0;
391235330Savg	}
392235330Savg    }
393235330Savg
39448952Smsmith    return(error);
39548952Smsmith}
39665613Sdcs
39765613Sdcsstatic int
39865613Sdcsloadakernel(int try, int argc, char* argv[])
39965613Sdcs{
40065613Sdcs    char *cp;
40165613Sdcs
40265613Sdcs	for (try = 0; (cp = getbootfile(try)) != NULL; try++)
40383321Speter	    if (mod_loadkld(cp, argc - 1, argv + 1) != 0)
40465613Sdcs		printf("can't load '%s'\n", cp);
40565613Sdcs	    else
40665613Sdcs		return 1;
40765613Sdcs	return 0;
40865613Sdcs}
409