xref: /openbsd-src/sys/arch/sparc64/stand/ofwboot/boot.c (revision 21c3ae38122e1d434e1fd93d65303cc3c0785470)
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