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