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 /* 29 * Loading modules, booting the system 30 */ 31 32 #include <stand.h> 33 #include <sys/boot.h> 34 #include <sys/kenv.h> 35 #include <sys/reboot.h> 36 #include <string.h> 37 38 #include "bootstrap.h" 39 40 static int autoboot(int timeout, char *prompt); 41 static char *getbootfile(int try); 42 static int loadakernel(int try, int argc, char* argv[]); 43 44 /* List of kernel names to try (may be overwritten by boot.config) XXX should move from here? */ 45 static const char *default_bootfiles = "kernel"; 46 47 static int autoboot_tried; 48 49 /* 50 * The user wants us to boot. 51 */ 52 COMMAND_SET(boot, "boot", "boot a file or loaded kernel", command_boot); 53 54 static 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 snprintf(command_errbuf, sizeof(command_errbuf), 67 "can't boot '%s', kernel module already loaded", argv[1]); 68 return(CMD_ERROR); 69 } 70 71 /* find/load the kernel module */ 72 if (mod_loadkld(argv[1], argc - 2, argv + 2) != 0) 73 return(CMD_ERROR); 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 #ifdef LOADER_VERIEXEC 109 verify_pcr_export(); /* for measured boot */ 110 #ifdef LOADER_VERIEXEC_PASS_MANIFEST 111 pass_manifest_export_envs(); 112 #endif 113 #endif 114 115 /* Pass the tslog buffer to the kernel as a preloaded module. */ 116 tslog_publish(); 117 118 /* Call the exec handler from the loader matching the kernel */ 119 file_formats[fp->f_loader]->l_exec(fp); 120 return(CMD_ERROR); 121 } 122 123 124 /* 125 * Autoboot after a delay 126 */ 127 128 COMMAND_SET(autoboot, "autoboot", "boot automatically after a delay", command_autoboot); 129 130 static int 131 command_autoboot(int argc, char *argv[]) 132 { 133 int howlong; 134 char *cp, *prompt; 135 136 prompt = NULL; 137 howlong = -1; 138 switch(argc) { 139 case 3: 140 prompt = argv[2]; 141 /* FALLTHROUGH */ 142 case 2: 143 howlong = strtol(argv[1], &cp, 0); 144 if (*cp != 0) { 145 snprintf(command_errbuf, sizeof(command_errbuf), 146 "bad delay '%s'", argv[1]); 147 return(CMD_ERROR); 148 } 149 /* FALLTHROUGH */ 150 case 1: 151 return(autoboot(howlong, prompt)); 152 } 153 154 command_errmsg = "too many arguments"; 155 return(CMD_ERROR); 156 } 157 158 /* 159 * Called before we go interactive. If we think we can autoboot, and 160 * we haven't tried already, try now. 161 */ 162 void 163 autoboot_maybe(void) 164 { 165 char *cp; 166 167 cp = getenv("autoboot_delay"); 168 if ((autoboot_tried == 0) && ((cp == NULL) || strcasecmp(cp, "NO"))) 169 autoboot(-1, NULL); /* try to boot automatically */ 170 } 171 172 static int 173 autoboot(int timeout, char *prompt) 174 { 175 time_t when, otime, ntime; 176 int c, yes; 177 char *argv[2], *cp, *ep; 178 char *kernelname; 179 #ifdef BOOT_PROMPT_123 180 const char *seq = "123", *p = seq; 181 #endif 182 183 autoboot_tried = 1; 184 185 if (timeout == -1) { 186 timeout = 10; 187 /* try to get a delay from the environment */ 188 if ((cp = getenv("autoboot_delay"))) { 189 timeout = strtol(cp, &ep, 0); 190 if (cp == ep) 191 timeout = 10; /* Unparseable? Set default! */ 192 } 193 } 194 195 kernelname = getenv("kernelname"); 196 if (kernelname == NULL) { 197 argv[0] = NULL; 198 loadakernel(0, 0, argv); 199 kernelname = getenv("kernelname"); 200 if (kernelname == NULL) { 201 command_errmsg = "no valid kernel found"; 202 return(CMD_ERROR); 203 } 204 } 205 206 if (timeout >= 0) { 207 otime = -1; 208 ntime = time(NULL); 209 when = ntime + timeout; /* when to boot */ 210 211 yes = 0; 212 213 #ifdef BOOT_PROMPT_123 214 printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or " 215 "1 2 3 sequence for command prompt." : prompt); 216 #else 217 printf("%s\n", (prompt == NULL) ? "Hit [Enter] to boot immediately, or any other key for command prompt." : prompt); 218 #endif 219 220 for (;;) { 221 if (ischar()) { 222 c = getchar(); 223 #ifdef BOOT_PROMPT_123 224 if ((c == '\r') || (c == '\n')) { 225 yes = 1; 226 break; 227 } else if (c != *p++) 228 p = seq; 229 if (*p == 0) 230 break; 231 #else 232 if ((c == '\r') || (c == '\n')) 233 yes = 1; 234 break; 235 #endif 236 } 237 ntime = time(NULL); 238 if (ntime >= when) { 239 yes = 1; 240 break; 241 } 242 243 if (ntime != otime) { 244 printf("\rBooting [%s] in %d second%s... ", 245 kernelname, (int)(when - ntime), 246 (when-ntime)==1?"":"s"); 247 otime = ntime; 248 } 249 } 250 } else { 251 yes = 1; 252 } 253 254 if (yes) 255 printf("\rBooting [%s]... ", kernelname); 256 putchar('\n'); 257 if (yes) { 258 argv[0] = "boot"; 259 argv[1] = NULL; 260 return(command_boot(1, argv)); 261 } 262 return(CMD_OK); 263 } 264 265 /* 266 * Scrounge for the name of the (try)'th file we will try to boot. 267 */ 268 static char * 269 getbootfile(int try) 270 { 271 static char *name = NULL; 272 const char *spec, *ep; 273 size_t len; 274 275 /* we use dynamic storage */ 276 if (name != NULL) { 277 free(name); 278 name = NULL; 279 } 280 281 /* 282 * Try $bootfile, then try our builtin default 283 */ 284 if ((spec = getenv("bootfile")) == NULL) 285 spec = default_bootfiles; 286 287 while ((try > 0) && (spec != NULL)) { 288 spec = strchr(spec, ';'); 289 if (spec) 290 spec++; /* skip over the leading ';' */ 291 try--; 292 } 293 if (spec != NULL) { 294 if ((ep = strchr(spec, ';')) != NULL) { 295 len = ep - spec; 296 } else { 297 len = strlen(spec); 298 } 299 name = malloc(len + 1); 300 strncpy(name, spec, len); 301 name[len] = 0; 302 } 303 if (name && name[0] == 0) { 304 free(name); 305 name = NULL; 306 } 307 return(name); 308 } 309 310 /* 311 * Try to find the /etc/fstab file on the filesystem (rootdev), 312 * which should be the root filesystem, and parse it to find 313 * out what the kernel ought to think the root filesystem is. 314 * 315 * If we're successful, set vfs.root.mountfrom to <vfstype>:<path> 316 * so that the kernel can tell both which VFS and which node to use 317 * to mount the device. If this variable's already set, don't 318 * overwrite it. 319 */ 320 int 321 getrootmount(char *rootdev) 322 { 323 char lbuf[KENV_MVALLEN], *cp, *ep, *dev, *fstyp, *options; 324 int fd, error; 325 326 if (getenv("vfs.root.mountfrom") != NULL) 327 return(0); 328 329 error = 1; 330 snprintf(lbuf, sizeof(lbuf), "%s/etc/fstab", rootdev); 331 if ((fd = open(lbuf, O_RDONLY)) < 0) 332 goto notfound; 333 334 /* loop reading lines from /etc/fstab What was that about sscanf again? */ 335 fstyp = NULL; 336 dev = NULL; 337 while (fgetstr(lbuf, sizeof(lbuf), fd) >= 0) { 338 if ((lbuf[0] == 0) || (lbuf[0] == '#')) 339 continue; 340 341 /* skip device name */ 342 for (cp = lbuf; (*cp != 0) && !isspace(*cp); cp++) 343 ; 344 if (*cp == 0) /* misformatted */ 345 continue; 346 /* delimit and save */ 347 *cp++ = 0; 348 free(dev); 349 dev = strdup(lbuf); 350 351 /* skip whitespace up to mountpoint */ 352 while ((*cp != 0) && isspace(*cp)) 353 cp++; 354 /* must have /<space> to be root */ 355 if ((*cp == 0) || (*cp != '/') || !isspace(*(cp + 1))) 356 continue; 357 /* skip whitespace up to fstype */ 358 cp += 2; 359 while ((*cp != 0) && isspace(*cp)) 360 cp++; 361 if (*cp == 0) /* misformatted */ 362 continue; 363 /* skip text to end of fstype and delimit */ 364 ep = cp; 365 while ((*cp != 0) && !isspace(*cp)) 366 cp++; 367 *cp = 0; 368 free(fstyp); 369 fstyp = strdup(ep); 370 371 /* skip whitespace up to mount options */ 372 cp += 1; 373 while ((*cp != 0) && isspace(*cp)) 374 cp++; 375 if (*cp == 0) /* misformatted */ 376 continue; 377 /* skip text to end of mount options and delimit */ 378 ep = cp; 379 while ((*cp != 0) && !isspace(*cp)) 380 cp++; 381 *cp = 0; 382 options = strdup(ep); 383 /* Build the <fstype>:<device> and save it in vfs.root.mountfrom */ 384 snprintf(lbuf, sizeof(lbuf), "%s:%s", fstyp, dev); 385 setenv("vfs.root.mountfrom", lbuf, 0); 386 387 /* Don't override vfs.root.mountfrom.options if it is already set */ 388 if (getenv("vfs.root.mountfrom.options") == NULL) { 389 /* save mount options */ 390 setenv("vfs.root.mountfrom.options", options, 0); 391 } 392 free(options); 393 error = 0; 394 break; 395 } 396 close(fd); 397 free(dev); 398 free(fstyp); 399 400 notfound: 401 if (error) { 402 const char *currdev; 403 404 currdev = getenv("currdev"); 405 if (currdev != NULL && strncmp("zfs:", currdev, 4) == 0) { 406 cp = strdup(currdev); 407 cp[strlen(cp) - 1] = '\0'; 408 setenv("vfs.root.mountfrom", cp, 0); 409 error = 0; 410 free(cp); 411 } 412 } 413 414 return(error); 415 } 416 417 static int 418 loadakernel(int try, int argc, char* argv[]) 419 { 420 char *cp; 421 422 for (try = 0; (cp = getbootfile(try)) != NULL; try++) 423 if (mod_loadkld(cp, argc - 1, argv + 1) != 0) 424 printf("can't load '%s'\n", cp); 425 else 426 return 1; 427 return 0; 428 } 429