1 /* $OpenBSD: boot.c,v 1.44 2024/03/26 14:46:48 claudio Exp $ */
2 /* $NetBSD: boot.c,v 1.3 2001/05/31 08:55:19 mrg Exp $ */
3 /*
4 * Copyright (c) 1997, 1999 Eduardo E. Horvath. All rights reserved.
5 * Copyright (c) 1997 Jason R. Thorpe. All rights reserved.
6 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
7 * Copyright (C) 1995, 1996 TooLs GmbH.
8 * All rights reserved.
9 *
10 * ELF support derived from NetBSD/alpha's boot loader, written
11 * by Christopher G. Demetriou.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by TooLs GmbH.
24 * 4. The name of TooLs GmbH may not be used to endorse or promote products
25 * derived from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * First try for the boot code
41 *
42 * Input syntax is:
43 * [promdev[{:|,}partition]]/[filename] [flags]
44 */
45
46 #define ELFSIZE 64
47
48 #include <lib/libsa/stand.h>
49 #include <lib/libkern/funcs.h>
50
51 #include <sys/param.h>
52 #include <sys/exec.h>
53 #include <sys/exec_elf.h>
54 #include <sys/reboot.h>
55 #include <sys/disklabel.h>
56
57 #include <machine/cpu.h>
58 #include <lib/libsa/arc4.h>
59
60 #ifdef SOFTRAID
61 #include <sys/queue.h>
62 #include <dev/biovar.h>
63 #include <dev/softraidvar.h>
64 #include <lib/libsa/softraid.h>
65
66 #include "disk.h"
67 #include "softraid_sparc64.h"
68 #endif
69
70 #include "ofdev.h"
71 #include "openfirm.h"
72
73 #ifdef BOOT_DEBUG
74 uint32_t boot_debug = 0
75 /* | BOOT_D_OFDEV */
76 /* | BOOT_D_OFNET */
77 ;
78 #endif
79
80 #define MEG (1024*1024)
81
82 /*
83 * Boot device is derived from ROM provided information, or if there is none,
84 * this list is used in sequence, to find a kernel.
85 */
86 char *kernels[] = {
87 "bsd",
88 NULL
89 };
90
91 char bootdev[128];
92 extern char bootfile[128];
93 int boothowto;
94 int debug;
95
96 char rnddata[BOOTRANDOM_MAX];
97 struct rc4_ctx randomctx;
98
99 int elf64_exec(int, Elf64_Ehdr *, u_int64_t *, void **, void **);
100
101 /*
102 * parse:
103 * [kernel-name] [-options]
104 * leave kernel-name in passed-in string
105 * put options into *howtop
106 * return -1 iff syntax error (no - before options)
107 */
108
109 static int
parseargs(char * str,int * howtop)110 parseargs(char *str, int *howtop)
111 {
112 char *cp;
113
114 *howtop = 0;
115 cp = str;
116 while (*cp == ' ')
117 ++cp;
118 if (*cp != '-') {
119 while (*cp && *cp != ' ')
120 *str++ = *cp++;
121 while (*cp == ' ')
122 ++cp;
123 }
124 /*
125 * Note that, if only options have been passed, without a kernel
126 * name, str == cp and options will be ignored at the boot blocks
127 * level.
128 * This a feature intended to make `boot -a' behave as intended.
129 * If you want the bootblocks to handle arguments explicitly, a
130 * kernel filename needs to be provided (as in `boot bsd -a').
131 */
132 *str = 0;
133 switch (*cp) {
134 default:
135 printf("boot options string <%s> must start with -\n", cp);
136 return -1;
137 case 0:
138 return 0;
139 case '-':
140 break;
141 }
142
143 ++cp;
144 while (*cp) {
145 switch (*cp++) {
146 case 'a':
147 *howtop |= RB_ASKNAME;
148 break;
149 case 'd':
150 if (!debug) debug = 1;
151 break;
152 case 'D':
153 debug = 2;
154 break;
155 }
156 }
157 return 0;
158 }
159
160
161 static void
chain(u_int64_t pentry,char * args,void * ssym,void * esym)162 chain(u_int64_t pentry, char *args, void *ssym, void *esym)
163 {
164 extern char end[];
165 void (*entry)();
166 int l, machine_tag;
167 long newargs[3];
168
169 entry = (void *)(long)pentry;
170
171 /*
172 * When we come in args consists of a pointer to the boot
173 * string. We need to fix it so it takes into account
174 * other params such as romp.
175 */
176
177 /*
178 * Stash pointer to end of symbol table after the argument
179 * strings.
180 */
181 l = strlen(args) + 1;
182 bcopy(&esym, args + l, sizeof(esym));
183 l += sizeof(esym);
184
185 /*
186 * Tell the kernel we're an OpenFirmware system.
187 */
188 #define SPARC_MACHINE_OPENFIRMWARE 0x44444230
189 machine_tag = SPARC_MACHINE_OPENFIRMWARE;
190 bcopy(&machine_tag, args + l, sizeof(machine_tag));
191 l += sizeof(machine_tag);
192
193 /*
194 * Since we don't need the boot string (we can get it from /chosen)
195 * we won't pass it in. Just pass in esym and magic #
196 */
197 newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
198 newargs[1] = (long)esym;
199 newargs[2] = (long)ssym;
200 args = (char *)newargs;
201 l = sizeof(newargs);
202
203 #ifdef DEBUG
204 printf("chain: calling OF_chain(%x, %x, %x, %x, %x)\n",
205 (void *)RELOC, end - (char *)RELOC, entry, args, l);
206 #endif
207 /* if -D is set then pause in the PROM. */
208 if (debug > 1) OF_enter();
209 OF_chain((void *)RELOC, ((end - (char *)RELOC)+PAGE_SIZE)%PAGE_SIZE,
210 entry, args, l);
211 panic("chain");
212 }
213
214 int
loadfile(int fd,char * args,int isupgrade)215 loadfile(int fd, char *args, int isupgrade)
216 {
217 union {
218 Elf64_Ehdr elf64;
219 } hdr;
220 int rval;
221 u_int64_t entry = 0;
222 void *ssym;
223 void *esym;
224
225 ssym = NULL;
226 esym = NULL;
227
228 /* Load the header. */
229 #ifdef DEBUG
230 printf("loadfile: reading header\n");
231 #endif
232 if ((rval = read(fd, &hdr, sizeof(hdr))) != sizeof(hdr)) {
233 if (rval == -1)
234 printf("read header: %s\n", strerror(errno));
235 else
236 printf("read header: short read (only %d of %d)\n",
237 rval, sizeof(hdr));
238 rval = 1;
239 goto err;
240 }
241
242 /* Determine file type, load kernel. */
243 if (bcmp(hdr.elf64.e_ident, ELFMAG, SELFMAG) == 0 &&
244 hdr.elf64.e_ident[EI_CLASS] == ELFCLASS64) {
245 printf("Booting %s\n", opened_name);
246 rval = elf64_exec(fd, &hdr.elf64, &entry, &ssym, &esym);
247 } else {
248 rval = 1;
249 printf("unknown executable format\n");
250 }
251
252 if (rval)
253 goto err;
254
255 printf(" start=0x%lx\n", (unsigned long)entry);
256
257 /* Prevent re-upgrade: chmod a-x bsd.upgrade */
258 if (isupgrade) {
259 struct stat st;
260
261 if (fstat(fd, &st) == 0) {
262 st.st_mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
263 if (fchmod(fd, st.st_mode) == -1)
264 printf("fchmod a-x %s: failed\n", opened_name);
265 }
266 }
267 close(fd);
268
269 #ifdef SOFTRAID
270 if (bootdev_dip)
271 OF_close(bootdev_dip->sr_handle);
272 sr_clear_keys();
273 #endif
274 chain(entry, args, ssym, esym);
275 /* NOTREACHED */
276
277 err:
278 close(fd);
279 return (rval);
280 }
281
282 static int
upgrade(void)283 upgrade(void)
284 {
285 struct stat sb;
286
287 if (stat("/bsd.upgrade", &sb) < 0)
288 return 0;
289 if ((sb.st_mode & S_IXUSR) == 0) {
290 printf("/bsd.upgrade is not u+x\n");
291 return 0;
292 }
293 return 1;
294 }
295
296 int
loadrandom(char * path,char * buf,size_t buflen)297 loadrandom(char *path, char *buf, size_t buflen)
298 {
299 struct stat sb;
300 int fd, error = 0;
301
302 fd = open(path, O_RDONLY);
303 if (fd == -1)
304 return -1;
305 if (fstat(fd, &sb) == -1) {
306 error = -1;
307 goto done;
308 }
309 if (read(fd, buf, buflen) != buflen) {
310 error = -1;
311 goto done;
312 }
313 if (sb.st_mode & S_ISTXT) {
314 printf("NOTE: random seed is being reused.\n");
315 error = -1;
316 goto done;
317 }
318 fchmod(fd, sb.st_mode | S_ISTXT);
319 done:
320 close(fd);
321 return (error);
322 }
323
324 #ifdef SOFTRAID
325 /*
326 * Set bootdev_dip to the softraid boot volume, if specified.
327 * Otherwise default to the softraid volume on the boot device, if any.
328 */
329 static int
srbootdev(const char * bootline)330 srbootdev(const char *bootline)
331 {
332 struct sr_boot_volume *bv;
333 int unit;
334
335 bootdev_dip = NULL;
336
337 /*
338 * Look for softraid disks in bootline.
339 * E.g. 'sr0', 'sr0:bsd', or 'sr0a:/bsd'
340 */
341 if (bootline[0] == 's' && bootline[1] == 'r' &&
342 '0' <= bootline[2] && bootline[2] <= '9') {
343 unit = bootline[2] - '0';
344
345 /* Create a fake diskinfo for this softraid volume. */
346 SLIST_FOREACH(bv, &sr_volumes, sbv_link)
347 if (bv->sbv_unit == unit)
348 break;
349 if (bv == NULL) {
350 printf("Unknown device: sr%d\n", unit);
351 return ENODEV;
352 }
353 } else {
354 struct sr_boot_chunk *bc;
355
356 /*
357 * Check if the boot device is a member of any of the assembled
358 * softraid volumes.
359 */
360 SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
361 if ((bv->sbv_flags & BIOC_SCBOOTABLE) == 0)
362 continue;
363
364 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
365 struct diskinfo *dip = bc->sbc_diskinfo;
366
367 if (!strcmp(dip->path, bootdev))
368 break;
369 }
370 if (bc != NULL)
371 break;
372 }
373 }
374
375 if (bv != NULL) {
376 if ((bv->sbv_flags & BIOC_SCBOOTABLE) == 0) {
377 printf("device sr%d is not bootable\n", unit);
378 return ENODEV;
379 }
380
381 if ((bv->sbv_level == 'C' || bv->sbv_level == 0x1C) &&
382 bv->sbv_keys == NULL)
383 if (sr_crypto_unlock_volume(bv) != 0)
384 return EPERM;
385
386 if (bv->sbv_diskinfo == NULL) {
387 struct sr_boot_chunk *bc;
388 struct diskinfo *dip, *bc_dip;
389 int sr_handle;
390
391 /* All reads will come from the boot chunk. */
392 bc = sr_vol_boot_chunk(bv);
393 if (bc == NULL)
394 return ENXIO;
395 bc_dip = (struct diskinfo *)bc->sbc_diskinfo;
396 sr_handle = OF_open(bc_dip->path);
397 if (sr_handle == -1)
398 return EIO;
399
400 dip = alloc(sizeof(struct diskinfo));
401 bzero(dip, sizeof(*dip));
402 dip->sr_vol = bv;
403 dip->sr_handle = sr_handle;
404 bv->sbv_diskinfo = dip;
405 }
406
407 /* strategy() and devopen() will use bootdev_dip */
408 bootdev_dip = bv->sbv_diskinfo;
409
410 /* Attempt to read disklabel. */
411 bv->sbv_part = 'c';
412 if (sr_getdisklabel(bv, &bootdev_dip->disklabel)) {
413 OF_close(bootdev_dip->sr_handle);
414 free(bv->sbv_diskinfo, sizeof(struct diskinfo));
415 bv->sbv_diskinfo = NULL;
416 bootdev_dip = NULL;
417 return ERDLAB;
418 }
419 }
420
421 return 0;
422 }
423 #endif
424
425 int
main(void)426 main(void)
427 {
428 extern char version[];
429 int chosen;
430 int isupgrade = 0;
431 char bootline[512]; /* Should check size? */
432 char *cp;
433 int fd;
434 #ifdef SOFTRAID
435 int err;
436 #endif
437 char **bootlp;
438 char *just_bootline[2];
439
440 printf(">> OpenBSD BOOT %s\n", version);
441
442 /*
443 * Get the boot arguments from Openfirmware
444 */
445 if ((chosen = OF_finddevice("/chosen")) == -1 ||
446 OF_getprop(chosen, "bootpath", bootdev, sizeof bootdev) < 0 ||
447 OF_getprop(chosen, "bootargs", bootline, sizeof bootline) < 0) {
448 printf("Invalid Openfirmware environment\n");
449 exit();
450 }
451
452 #ifdef SOFTRAID
453 diskprobe();
454 srprobe();
455 err = srbootdev(bootline);
456 if (err) {
457 printf("Cannot boot from softraid: %s\n", strerror(err));
458 _rtt();
459 }
460 #endif
461
462 /*
463 * case 1: boot net -a
464 * -> getln loop
465 * case 2: boot net kernel [options]
466 * -> boot kernel, getln loop
467 * case 3: boot net [options]
468 * -> iterate boot list, getln loop
469 */
470
471 bootlp = kernels;
472 if (parseargs(bootline, &boothowto) == -1 ||
473 (boothowto & RB_ASKNAME)) {
474 bootlp = 0;
475 } else if (*bootline) {
476 just_bootline[0] = bootline;
477 just_bootline[1] = 0;
478 bootlp = just_bootline;
479 } else if (upgrade()) {
480 just_bootline[0] = "/bsd.upgrade";
481 just_bootline[1] = 0;
482 isupgrade = 1;
483 bootlp = just_bootline;
484 printf("upgrade detected: switching to %s\n", *bootlp);
485 }
486 for (;;) {
487 if (bootlp) {
488 cp = *bootlp++;
489 if (!cp) {
490 printf("\n");
491 bootlp = 0;
492 kernels[0] = 0; /* no more iteration */
493 } else if (cp != bootline) {
494 printf("Trying %s...\n", cp);
495 if (strlcpy(bootline, cp, sizeof bootline)
496 >= sizeof bootline) {
497 printf("bootargs too long: %s\n",
498 bootline);
499 _rtt();
500 }
501 }
502 }
503 if (!bootlp) {
504 printf("Boot: ");
505 getln(bootline, sizeof bootline);
506 if (parseargs(bootline, &boothowto) == -1)
507 continue;
508 if (!*bootline) {
509 bootlp = kernels;
510 continue;
511 }
512 if (strcmp(bootline, "exit") == 0 ||
513 strcmp(bootline, "halt") == 0) {
514 _rtt();
515 }
516 }
517 if (loadrandom(BOOTRANDOM, rnddata, sizeof(rnddata)) == 0)
518 boothowto |= RB_GOODRANDOM;
519
520 rc4_keysetup(&randomctx, rnddata, sizeof rnddata);
521 rc4_skip(&randomctx, 1536);
522
523 if ((fd = open(bootline, O_RDONLY)) < 0) {
524 printf("open %s: %s\n", opened_name, strerror(errno));
525 continue;
526 }
527 /* XXX void, for now */
528 #ifdef DEBUG
529 if (debug)
530 printf("main: Calling loadfile(fd, %s)\n", opened_name);
531 #endif
532 (void)loadfile(fd, opened_name, isupgrade);
533 }
534 return 0;
535 }
536