boot.c revision 334572
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/11/stand/common/boot.c 334572 2018-06-03 17:17:45Z dim $"); 29 30/* 31 * Loading modules, booting the system 32 */ 33 34#include <stand.h> 35#include <string.h> 36 37#include "bootstrap.h" 38 39static int autoboot(int timeout, char *prompt); 40static char *getbootfile(int try); 41static int loadakernel(int try, int argc, char* argv[]); 42 43/* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */ 44static const char *default_bootfiles = "kernel"; 45 46static int autoboot_tried; 47 48/* 49 * The user wants us to boot. 50 */ 51COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot); 52 53static int 54command_boot(int argc, char *argv[]) 55{ 56 struct preloaded_file *fp; 57 58 /* 59 * See if the user has specified an explicit kernel to boot. 60 */ 61 if ((argc > 1) && (argv[1][0] != '-')) { 62 63 /* XXX maybe we should discard everything and start again? */ 64 if (file_findfile(NULL, NULL) != NULL) { 65 snprintf(command_errbuf, sizeof(command_errbuf), 66 "can't boot '%s', kernel module already loaded", argv[1]); 67 return(CMD_ERROR); 68 } 69 70 /* find/load the kernel module */ 71 if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0) 72 return(CMD_ERROR); 73 /* we have consumed all arguments */ 74 argc = 1; 75 } 76 77 /* 78 * See if there is a kernel module already loaded 79 */ 80 if (file_findfile(NULL, NULL) == NULL) 81 if (loadakernel(0, argc - 1, argv + 1)) 82 /* we have consumed all arguments */ 83 argc = 1; 84 85 /* 86 * Loaded anything yet? 87 */ 88 if ((fp = file_findfile(NULL, NULL)) == NULL) { 89 command_errmsg = "no bootable kernel"; 90 return(CMD_ERROR); 91 } 92 93 /* 94 * If we were given arguments, discard any previous. 95 * XXX should we merge arguments? Hard to DWIM. 96 */ 97 if (argc > 1) { 98 if (fp->f_args != NULL) 99 free(fp->f_args); 100 fp->f_args = unargv(argc - 1, argv + 1); 101 } 102 103 /* Hook for platform-specific autoloading of modules */ 104 if (archsw.arch_autoload() != 0) 105 return(CMD_ERROR); 106 107 /* Call the exec handler from the loader matching the kernel */ 108 file_formats[fp->f_loader]->l_exec(fp); 109 return(CMD_ERROR); 110} 111 112 113/* 114 * Autoboot after a delay 115 */ 116 117COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot); 118 119static int 120command_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 snprintf(command_errbuf, sizeof(command_errbuf), 135 "bad delay '%s'", argv[1]); 136 return(CMD_ERROR); 137 } 138 /* FALLTHROUGH */ 139 case 1: 140 return(autoboot(howlong, prompt)); 141 } 142 143 command_errmsg = "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 */ 151void 152autoboot_maybe() 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 161static int 162autoboot(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#ifdef BOOT_PROMPT_123 169 const char *seq = "123", *p = seq; 170#endif 171 172 autoboot_tried = 1; 173 174 if (timeout == -1) { 175 timeout = 10; 176 /* try to get a delay from the environment */ 177 if ((cp = getenv("autoboot_delay"))) { 178 timeout = strtol(cp, &ep, 0); 179 if (cp == ep) 180 timeout = 10; /* Unparseable? Set default! */ 181 } 182 } 183 184 kernelname = getenv("kernelname"); 185 if (kernelname == NULL) { 186 argv[0] = NULL; 187 loadakernel(0, 0, argv); 188 kernelname = getenv("kernelname"); 189 if (kernelname == NULL) { 190 command_errmsg = "no valid kernel found"; 191 return(CMD_ERROR); 192 } 193 } 194 195 if (timeout >= 0) { 196 otime = time(NULL); 197 when = otime + timeout; /* when to boot */ 198 199 yes = 0; 200 201#ifdef BOOT_PROMPT_123 202 printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or " 203 "1 2 3 sequence for command prompt." : prompt); 204#else 205 printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt); 206#endif 207 208 for (;;) { 209 if (ischar()) { 210 c = getchar(); 211#ifdef BOOT_PROMPT_123 212 if ((c == '\r') || (c == '\n')) { 213 yes = 1; 214 break; 215 } else if (c != *p++) 216 p = seq; 217 if (*p == 0) 218 break; 219#else 220 if ((c == '\r') || (c == '\n')) 221 yes = 1; 222 break; 223#endif 224 } 225 ntime = time(NULL); 226 if (ntime >= when) { 227 yes = 1; 228 break; 229 } 230 231 if (ntime != otime) { 232 printf("\rBooting [%s] in %d second%s... ", 233 kernelname, (int)(when - ntime), 234 (when-ntime)==1?"":"s"); 235 otime = ntime; 236 } 237 } 238 } else { 239 yes = 1; 240 } 241 242 if (yes) 243 printf("\rBooting [%s]... ", kernelname); 244 putchar('\n'); 245 if (yes) { 246 argv[0] = "boot"; 247 argv[1] = NULL; 248 return(command_boot(1, argv)); 249 } 250 return(CMD_OK); 251} 252 253/* 254 * Scrounge for the name of the (try)'th file we will try to boot. 255 */ 256static char * 257getbootfile(int try) 258{ 259 static char *name = NULL; 260 const char *spec, *ep; 261 size_t len; 262 263 /* we use dynamic storage */ 264 if (name != NULL) { 265 free(name); 266 name = NULL; 267 } 268 269 /* 270 * Try $bootfile, then try our builtin default 271 */ 272 if ((spec = getenv("bootfile")) == NULL) 273 spec = default_bootfiles; 274 275 while ((try > 0) && (spec != NULL)) { 276 spec = strchr(spec, ';'); 277 if (spec) 278 spec++; /* skip over the leading ';' */ 279 try--; 280 } 281 if (spec != NULL) { 282 if ((ep = strchr(spec, ';')) != NULL) { 283 len = ep - spec; 284 } else { 285 len = strlen(spec); 286 } 287 name = malloc(len + 1); 288 strncpy(name, spec, len); 289 name[len] = 0; 290 } 291 if (name && name[0] == 0) { 292 free(name); 293 name = NULL; 294 } 295 return(name); 296} 297 298/* 299 * Try to find the /etc/fstab file on the filesystem (rootdev), 300 * which should be be the root filesystem, and parse it to find 301 * out what the kernel ought to think the root filesystem is. 302 * 303 * If we're successful, set vfs.root.mountfrom to <vfstype>:<path> 304 * so that the kernel can tell both which VFS and which node to use 305 * to mount the device. If this variable's already set, don't 306 * overwrite it. 307 */ 308int 309getrootmount(char *rootdev) 310{ 311 char lbuf[128], *cp, *ep, *dev, *fstyp, *options; 312 int fd, error; 313 314 if (getenv("vfs.root.mountfrom") != NULL) 315 return(0); 316 317 error = 1; 318 sprintf(lbuf, "%s/etc/fstab", rootdev); 319 if ((fd = open(lbuf, O_RDONLY)) < 0) 320 goto notfound; 321 322 /* loop reading lines from /etc/fstab What was that about sscanf again? */ 323 fstyp = NULL; 324 dev = NULL; 325 while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) { 326 if ((lbuf[0] == 0) || (lbuf[0] == '#')) 327 continue; 328 329 /* skip device name */ 330 for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++) 331 ; 332 if (*cp == 0) /* misformatted */ 333 continue; 334 /* delimit and save */ 335 *cp++ = 0; 336 free(dev); 337 dev = strdup(lbuf); 338 339 /* skip whitespace up to mountpoint */ 340 while ((*cp != 0) && isspace(*cp)) 341 cp++; 342 /* must have /<space> to be root */ 343 if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1))) 344 continue; 345 /* skip whitespace up to fstype */ 346 cp += 2; 347 while ((*cp != 0) && isspace(*cp)) 348 cp++; 349 if (*cp == 0) /* misformatted */ 350 continue; 351 /* skip text to end of fstype and delimit */ 352 ep = cp; 353 while ((*cp != 0) && !isspace(*cp)) 354 cp++; 355 *cp = 0; 356 free(fstyp); 357 fstyp = strdup(ep); 358 359 /* skip whitespace up to mount options */ 360 cp += 1; 361 while ((*cp != 0) && isspace(*cp)) 362 cp++; 363 if (*cp == 0) /* misformatted */ 364 continue; 365 /* skip text to end of mount options and delimit */ 366 ep = cp; 367 while ((*cp != 0) && !isspace(*cp)) 368 cp++; 369 *cp = 0; 370 options = strdup(ep); 371 /* Build the <fstype>:<device> and save it in vfs.root.mountfrom */ 372 sprintf(lbuf, "%s:%s", fstyp, dev); 373 setenv("vfs.root.mountfrom", lbuf, 0); 374 375 /* Don't override vfs.root.mountfrom.options if it is already set */ 376 if (getenv("vfs.root.mountfrom.options") == NULL) { 377 /* save mount options */ 378 setenv("vfs.root.mountfrom.options", options, 0); 379 } 380 free(options); 381 error = 0; 382 break; 383 } 384 close(fd); 385 free(dev); 386 free(fstyp); 387 388notfound: 389 if (error) { 390 const char *currdev; 391 392 currdev = getenv("currdev"); 393 if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) { 394 cp = strdup(currdev); 395 cp[strlen(cp) - 1] = '\0'; 396 setenv("vfs.root.mountfrom", cp, 0); 397 error = 0; 398 free(cp); 399 } 400 } 401 402 return(error); 403} 404 405static int 406loadakernel(int try, int argc, char* argv[]) 407{ 408 char *cp; 409 410 for (try = 0; (cp = getbootfile(try)) != NULL; try++) 411 if (mod_loadkld(cp, argc - 1, argv + 1) != 0) 412 printf("can't load '%s'\n", cp); 413 else 414 return 1; 415 return 0; 416} 417