Home | History | Annotate | Line # | Download | only in common
      1 /*	$NetBSD: boot.c,v 1.9 2020/08/19 02:19:07 msaitoh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998 Michael Smith <msmith (at) freebsd.org>
      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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 /* __FBSDID("$FreeBSD: src/sys/boot/common/boot.c,v 1.29 2003/08/25 23:30:41 obrien Exp $"); */
     31 
     32 
     33 /*
     34  * Loading modules, booting the system
     35  */
     36 
     37 #include <lib/libsa/stand.h>
     38 #include <lib/libsa/loadfile.h>
     39 #include <lib/libkern/libkern.h>
     40 
     41 #include "bootstrap.h"
     42 
     43 static char	*getbootfile(int try);
     44 static int	loadakernel(int try, int argc, char* argv[]);
     45 
     46 /* List of kernel names to try */
     47 static const char *default_bootfiles = "netbsd";
     48 
     49 static int autoboot_tried;
     50 
     51 /*
     52  * The user wants us to boot.
     53  */
     54 
     55 int
     56 command_boot(int argc, char *argv[])
     57 {
     58     struct preloaded_file	*fp;
     59 
     60     /*
     61      * See if the user has specified an explicit kernel to boot.
     62      */
     63     if ((argc > 1) && (argv[1][0] != '-')) {
     64 
     65 	/* XXX maybe we should discard everything and start again? */
     66 	if (file_findfile(NULL, NULL) != NULL) {
     67 	    command_seterr("can't boot '%s', kernel module already loaded",
     68 		argv[1]);
     69 	    return(CMD_ERROR);
     70 	}
     71 
     72 	/* find/load the kernel module */
     73 	if (file_loadkernel(argv[1], argc - 2, argv + 2) != 0)
     74 	    return(CMD_ERROR);
     75 
     76 	/* we have consumed all arguments */
     77 	argc = 1;
     78     }
     79 
     80     /*
     81      * See if there is a kernel module already loaded
     82      */
     83     if (file_findfile(NULL, NULL) == NULL)
     84 	if (loadakernel(0, argc - 1, argv + 1))
     85 	    /* we have consumed all arguments */
     86 	    argc = 1;
     87 
     88     /*
     89      * Loaded anything yet?
     90      */
     91     if ((fp = file_findfile(NULL, NULL)) == NULL) {
     92 	command_seterr("no bootable kernel");
     93 	return(CMD_ERROR);
     94     }
     95 
     96     /*
     97      * If we were given arguments, discard any previous.
     98      * XXX should we merge arguments?  Hard to DWIM.
     99      */
    100     if (argc > 1) {
    101 	if (fp->f_args != NULL)
    102 	    free(fp->f_args);
    103 	fp->f_args = unargv(argc - 1, argv + 1);
    104     }
    105 
    106     /* Hook for platform-specific autoloading of modules */
    107     if (archsw.arch_autoload() != 0)
    108 	return(CMD_ERROR);
    109 
    110     /* Call the exec handler from the loader matching the kernel */
    111     command_seterr("%s", strerror(file_formats[fp->f_loader]->l_exec(fp)));
    112     return(CMD_ERROR);
    113 }
    114 
    115 
    116 /*
    117  * Autoboot after a delay
    118  */
    119 
    120 int
    121 command_autoboot(int argc, char *argv[])
    122 {
    123     int		howlong;
    124     char	*cp, *prompt;
    125 
    126     prompt = NULL;
    127     howlong = -1;
    128     switch(argc) {
    129     case 3:
    130 	prompt = argv[2];
    131 	/* FALLTHROUGH */
    132     case 2:
    133 	howlong = strtol(argv[1], &cp, 0);
    134 	if (*cp != 0) {
    135 	    command_seterr("bad delay '%s'", argv[1]);
    136 	    return(CMD_ERROR);
    137 	}
    138 	/* FALLTHROUGH */
    139     case 1:
    140 	return(autoboot(howlong, prompt));
    141     }
    142 
    143     command_seterr("too many arguments");
    144     return(CMD_ERROR);
    145 }
    146 
    147 /*
    148  * Called before we go interactive.  If we think we can autoboot, and
    149  * we haven't tried already, try now.
    150  */
    151 void
    152 autoboot_maybe(void)
    153 {
    154     char	*cp;
    155 
    156     cp = getenv("autoboot_delay");
    157     if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO")))
    158 	autoboot(-1, NULL);		/* try to boot automatically */
    159 }
    160 
    161 int
    162 autoboot(int timeout, char *prompt)
    163 {
    164     time_t	when, otime, ntime;
    165     int		c, yes;
    166     char	*argv[2], *cp, *ep;
    167     char	*kernelname;
    168 
    169     autoboot_tried = 1;
    170 
    171     if (timeout == -1) {
    172 	/* try to get a delay from the environment */
    173 	if ((cp = getenv("autoboot_delay"))) {
    174 	    timeout = strtol(cp, &ep, 0);
    175 	    if (cp == ep)
    176 		timeout = -1;
    177 	}
    178     }
    179     if (timeout == -1)		/* all else fails */
    180 	timeout = 10;
    181 
    182     kernelname = getenv("kernelname");
    183     if (kernelname == NULL) {
    184 	argv[0] = NULL;
    185 	loadakernel(0, 0, argv);
    186 	kernelname = getenv("kernelname");
    187 	if (kernelname == NULL) {
    188 	    command_seterr("no valid kernel found");
    189 	    return(CMD_ERROR);
    190 	}
    191     }
    192 
    193     otime = time(NULL);
    194     when = otime + timeout;	/* when to boot */
    195     yes = 0;
    196 
    197     printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt);
    198 
    199     for (;;) {
    200 	if (ischar()) {
    201 	    c = getchar();
    202 	    if ((c == '\r') || (c == '\n'))
    203 		yes = 1;
    204 	    break;
    205 	}
    206 	ntime = time(NULL);
    207 	if (ntime >= when) {
    208 	    yes = 1;
    209 	    break;
    210 	}
    211 
    212 	if (ntime != otime) {
    213 	    printf("\rBooting [%s] in %d second%s... ",
    214 	    		kernelname, (int)(when - ntime),
    215 			(when-ntime)==1?"":"s");
    216 	    otime = ntime;
    217 	}
    218     }
    219     if (yes)
    220 	printf("\rBooting [%s]...               ", kernelname);
    221     putchar('\n');
    222     if (yes) {
    223 	argv[0] = "boot";
    224 	argv[1] = NULL;
    225 	return(command_boot(1, argv));
    226     }
    227     return(CMD_OK);
    228 }
    229 
    230 /*
    231  * Scrounge for the name of the (try)'th file we will try to boot.
    232  */
    233 static char *
    234 getbootfile(int try)
    235 {
    236     static char *name = NULL;
    237     const char	*spec, *ep;
    238     size_t	len;
    239 
    240     /* we use dynamic storage */
    241     if (name != NULL) {
    242 	free(name);
    243 	name = NULL;
    244     }
    245 
    246     /*
    247      * Try $bootfile, then try our builtin default
    248      */
    249     if ((spec = getenv("bootfile")) == NULL)
    250 	spec = default_bootfiles;
    251 
    252     while ((try > 0) && (spec != NULL)) {
    253 	spec = strchr(spec, ';');
    254 	if (spec)
    255 	    spec++;	/* skip over the leading ';' */
    256 	try--;
    257     }
    258     if (spec != NULL) {
    259 	if ((ep = strchr(spec, ';')) != NULL) {
    260 	    len = ep - spec;
    261 	} else {
    262 	    len = strlen(spec);
    263 	}
    264 	name = alloc(len + 1);
    265 	strncpy(name, spec, len);
    266 	name[len] = 0;
    267     }
    268     if (name && name[0] == 0) {
    269 	free(name);
    270 	name = NULL;
    271     }
    272     else {
    273       setenv("kernelname", name, 1);
    274     }
    275 
    276     return(name);
    277 }
    278 
    279 /*
    280  * Try to find the /etc/fstab file on the filesystem (rootdev),
    281  * which should be the root filesystem, and parse it to find
    282  * out what the kernel ought to think the root filesystem is.
    283  *
    284  * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
    285  * so that the kernel can tell both which VFS and which node to use
    286  * to mount the device.  If this variable's already set, don't
    287  * overwrite it.
    288  */
    289 int
    290 getrootmount(char *rootdev)
    291 {
    292     char	lbuf[128], *cp, *ep, *dev, *fstyp;
    293     int		fd, error;
    294 
    295     if (getenv("vfs.root.mountfrom") != NULL)
    296 	return(0);
    297 
    298     snprintf(lbuf, sizeof(lbuf), "%s/etc/fstab", rootdev);
    299     if ((fd = open(lbuf, O_RDONLY)) < 0)
    300 	return(1);
    301 
    302     /* loop reading lines from /etc/fstab    What was that about sscanf again? */
    303     error = 1;
    304     while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) {
    305 	if ((lbuf[0] == 0) || (lbuf[0] == '#'))
    306 	    continue;
    307 
    308 	/* skip device name */
    309 	for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++)
    310 	    ;
    311 	if (*cp == 0)		/* misformatted */
    312 	    continue;
    313 	/* delimit and save */
    314 	*cp++ = 0;
    315 	dev = strdup(lbuf);
    316 
    317 	/* skip whitespace up to mountpoint */
    318 	while ((*cp != 0) && isspace(*cp))
    319 	    cp++;
    320 	/* must have /<space> to be root */
    321 	if ((*cp != '/') || !isspace(*(cp + 1)))
    322 	    continue;
    323 	/* skip whitespace up to fstype */
    324 	cp += 2;
    325 	while ((*cp != 0) && isspace(*cp))
    326 	    cp++;
    327 	if (*cp == 0)		/* misformatted */
    328 	    continue;
    329 	/* skip text to end of fstype and delimit */
    330 	ep = cp;
    331 	while ((*cp != 0) && !isspace(*cp))
    332 	    cp++;
    333 	*cp = 0;
    334 	fstyp = strdup(ep);
    335 
    336 	/* build the final result and save it */
    337 	snprintf(lbuf, sizeof(lbuf), "%s:%s", fstyp, dev);
    338 	free(dev);
    339 	free(fstyp);
    340 	setenv("vfs.root.mountfrom", lbuf, 0);
    341 	error = 0;
    342 	break;
    343     }
    344     close(fd);
    345     return(error);
    346 }
    347 
    348 static int
    349 loadakernel(int try, int argc, char* argv[])
    350 {
    351 	char *cp;
    352 
    353 	for (try = 0; (cp = getbootfile(try)) != NULL; try++)
    354 		if (file_loadkernel(cp, argc - 1, argv + 1) != 0)
    355 			printf("can't load '%s'\n", cp);
    356 		else
    357 			return 1;
    358 	return 0;
    359 }
    360 
    361