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