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