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