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