xref: /netbsd-src/sys/arch/sparc/stand/ofwboot/boot.c (revision bbde328be4e75ea9ad02e9715ea13ca54b797ada)
1 /*	$NetBSD: boot.c,v 1.22 2010/04/02 18:39:44 martin Exp $	*/
2 
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 #include <lib/libsa/stand.h>
47 #include <lib/libsa/loadfile.h>
48 #include <lib/libkern/libkern.h>
49 
50 #include <sys/param.h>
51 #include <sys/reboot.h>
52 #include <sys/disklabel.h>
53 #include <sys/boot_flag.h>
54 
55 #include <machine/cpu.h>
56 #include <machine/promlib.h>
57 #include <machine/bootinfo.h>
58 
59 #include "boot.h"
60 #include "ofdev.h"
61 #include "openfirm.h"
62 
63 
64 #define COMPAT_BOOT(marks)	(marks[MARK_START] == marks[MARK_ENTRY])
65 
66 
67 typedef void (*entry_t)(long o0, long bootargs, long bootsize, long o3,
68                         long ofw);
69 
70 /*
71  * Boot device is derived from ROM provided information, or if there is none,
72  * this list is used in sequence, to find a kernel.
73  */
74 const char *kernelnames[] = {
75 	"netbsd",
76 	"netbsd.gz",
77 	"netbsd.old",
78 	"netbsd.old.gz",
79 	"onetbsd",
80 	"onetbsd.gz",
81 	"vmunix ",
82 #ifdef notyet
83 	"netbsd.pl ",
84 	"netbsd.pl.gz ",
85 	"netbsd.el ",
86 	"netbsd.el.gz ",
87 #endif
88 	NULL
89 };
90 
91 char bootdev[PROM_MAX_PATH];
92 bool root_fs_quickseekable = true;	/* unset for tftp boots */
93 static bool bootinfo_pass_bootdev = false;
94 
95 int debug  = 0;
96 int compatmode = 0;
97 
98 #if 0
99 static void
100 prom2boot(char *dev)
101 {
102 	char *cp, *lp = 0;
103 	int handle;
104 	char devtype[16];
105 
106 	for (cp = dev; *cp; cp++)
107 		if (*cp == ':')
108 			lp = cp;
109 	if (!lp)
110 		lp = cp;
111 	*lp = 0;
112 }
113 #endif
114 
115 static int
116 bootoptions(const char *ap, char *loaddev, char *kernel, char *options)
117 {
118 	int v = 0;
119 	const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL;
120 	const char *path;
121 	char partition, *pp;
122 
123 	*kernel  = '\0';
124 	*options = '\0';
125 
126 	if (ap == NULL) {
127 		return (0);
128 	}
129 
130 	while (*ap == ' ') {
131 		ap++;
132 	}
133 
134 	if (*ap != '-') {
135 		start1 = ap;
136 		while (*ap != '\0' && *ap != ' ') {
137 			ap++;
138 		}
139 		end1 = ap;
140 
141 		while (*ap != '\0' && *ap == ' ') {
142 			ap++;
143 		}
144 
145 		if (*ap != '-') {
146 			start2 = ap;
147 			while (*ap != '\0' && *ap != ' ') {
148 				ap++;
149 			}
150 			end2 = ap;
151 			while (*ap != '\0' && *ap == ' ') {
152 				ap++;
153 			}
154 		}
155 	}
156 	if (end2 == start2) {
157 		start2 = end2 = NULL;
158 	}
159 	if (end1 == start1) {
160 		start1 = end1 = NULL;
161 	}
162 
163 	if (start1 == NULL) {
164 		/* only options */
165 	} else if (start2 == NULL) {
166 		memcpy(kernel, start1, (end1 - start1));
167 		kernel[end1 - start1] = '\0';
168 		path = filename(kernel, &partition);
169 		if (path == NULL) {
170 			strcpy(loaddev, kernel);
171 			kernel[0] = '\0';
172 		} else if (path != kernel) {
173 			/* copy device part */
174 			memcpy(loaddev, kernel, path-kernel);
175 			loaddev[path-kernel] = '\0';
176 			if (partition) {
177 				pp = loaddev + strlen(loaddev);
178 				pp[0] = ':';
179 				pp[1] = partition;
180 				pp[2] = '\0';
181 			}
182 			/* and kernel path */
183 			strcpy(kernel, path);
184 		}
185 	} else {
186 		memcpy(loaddev, start1, (end1-start1));
187 		loaddev[end1-start1] = '\0';
188 		memcpy(kernel, start2, (end2 - start2));
189 		kernel[end2 - start2] = '\0';
190 	}
191 
192 	strcpy(options, ap);
193 	while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') {
194 		BOOT_FLAG(*ap, v);
195 		switch(*ap++) {
196 		case 'D':
197 			debug = 2;
198 			break;
199 		case 'C':
200 			compatmode = 1;
201 			break;
202 		default:
203 			break;
204 		}
205 	}
206 
207 	if (((v & RB_KDB) != 0) && (debug == 0)) {
208 		debug = 1;
209 	}
210 
211 	DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n",
212 	    loaddev, kernel, options));
213 	return (v);
214 }
215 
216 /*
217  * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle
218  * ksyms information unless it resides in a dedicated memory allocated from
219  * PROM and aligned on NBPG boundary. This is because the kernels calculate
220  * their ends on their own, they use address of 'end[]' reference which follows
221  * text segment. Ok, allocate some memory from PROM and copy symbol information
222  * over there.
223  */
224 static void
225 ksyms_copyout(void **ssym, void **esym)
226 {
227 	void *addr;
228 	int kssize = (int)(long)(*esym - *ssym + 1);
229 
230 	DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n",
231 				*ssym, *esym, kssize));
232 
233 	if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) {
234 		panic("ksyms_copyout(): no space for symbol table");
235 	}
236 
237 	memcpy(addr, *ssym, kssize);
238 	*ssym = addr;
239 	*esym = addr + kssize - 1;
240 
241 	DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym));
242 }
243 
244 /*
245  * Prepare boot information and jump directly to the kernel.
246  */
247 static void
248 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw)
249 {
250 	extern char end[];
251 	int l, machine_tag;
252 	long newargs[4];
253 	void *ssym, *esym;
254 	vaddr_t bootinfo;
255 	struct btinfo_symtab bi_sym;
256 	struct btinfo_kernend bi_kend;
257 	char *cp;
258 	char bootline[PROM_MAX_PATH * 2];
259 
260 	/* Compose kernel boot line. */
261 	strncpy(bootline, kernel, sizeof(bootline));
262 	cp = bootline + strlen(bootline);
263 	if (*args) {
264 		*cp++ = ' ';
265 		strncpy(bootline, args, sizeof(bootline) - (cp - bootline));
266 	}
267 	*cp = 0; args = bootline;
268 
269 	/* Record symbol information in the bootinfo. */
270 	bootinfo = bi_init(marks[MARK_END]);
271 	bi_sym.nsym = marks[MARK_NSYM];
272 	bi_sym.ssym = marks[MARK_SYM];
273 	bi_sym.esym = marks[MARK_END];
274 	bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym));
275 	bi_kend.addr= bootinfo + BOOTINFO_SIZE;
276 	bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend));
277 	if (bootinfo_pass_bootdev) {
278 		struct {
279 			struct btinfo_common common;
280 			char name[256];
281 		} info;
282 
283 		strcpy(info.name, bootdev);
284 		bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev)
285 			+sizeof(struct btinfo_bootdev));
286 	}
287 
288 	sparc64_finalize_tlb(marks[MARK_DATA]);
289 	sparc64_bi_add();
290 
291 	ssym  = (void*)(long)marks[MARK_SYM];
292 	esym  = (void*)(long)marks[MARK_END];
293 
294 	DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
295 
296 	/* Adjust ksyms pointers, if needed. */
297 	if (COMPAT_BOOT(marks) || compatmode) {
298 		ksyms_copyout(&ssym, &esym);
299 	}
300 
301 	freeall();
302 	/*
303 	 * When we come in args consists of a pointer to the boot
304 	 * string.  We need to fix it so it takes into account
305 	 * other params such as romp.
306 	 */
307 
308 	/*
309 	 * Stash pointer to end of symbol table after the argument
310 	 * strings.
311 	 */
312 	l = strlen(args) + 1;
313 	memcpy(args + l, &esym, sizeof(esym));
314 	l += sizeof(esym);
315 
316 	/*
317 	 * Tell the kernel we're an OpenFirmware system.
318 	 */
319 	machine_tag = SPARC_MACHINE_OPENFIRMWARE;
320 	memcpy(args + l, &machine_tag, sizeof(machine_tag));
321 	l += sizeof(machine_tag);
322 
323 	/*
324 	 * Since we don't need the boot string (we can get it from /chosen)
325 	 * we won't pass it in.  Just pass in esym and magic #
326 	 */
327 	newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
328 	newargs[1] = (long)esym;
329 	newargs[2] = (long)ssym;
330 	newargs[3] = (long)(void*)bootinfo;
331 	args = (char *)newargs;
332 	l = sizeof(newargs);
333 
334 	/* if -D is set then pause in the PROM. */
335 	if (debug > 1) callrom();
336 
337 	/*
338 	 * Jump directly to the kernel. Solaris kernel and Sun PROM
339 	 * flash updates expect ROMP vector in %o0, so we do. Format
340 	 * of other parameters and their order reflect OF_chain()
341 	 * symantics since this is what older NetBSD kernels rely on.
342 	 * (see sparc64/include/bootinfo.h for specification).
343 	 */
344 	DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
345 				(long)args, (long)l, (long)ofw, (long)ofw,
346 				(void*)marks[MARK_ENTRY]));
347 	(*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
348 				      (long)ofw);
349 	printf("Returned from kernel entry point!\n");
350 }
351 
352 static void
353 start_kernel(char *kernel, char *bootline, void *ofw)
354 {
355 	int fd;
356 	u_long marks[MARK_MAX];
357 
358 	/*
359 	 * First, load headers using default allocator and check whether kernel
360 	 * entry address matches kernel text load address. If yes, this is the
361 	 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
362 	 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
363 	 */
364 	loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
365 	if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
366 		if (COMPAT_BOOT(marks) || compatmode) {
367 			(void)printf("[c] ");
368 			loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
369 		} else {
370 			loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
371 		}
372 		(void)printf("Loading %s: ", kernel);
373 
374 		if (fdloadfile(fd, marks, LOAD_ALL) != -1) {
375 			close(fd);
376 			jump_to_kernel(marks, kernel, bootline, ofw);
377 		}
378 	}
379 	(void)printf("Failed to load '%s'.\n", kernel);
380 }
381 
382 static void
383 help(void)
384 {
385 	printf( "enter a special command\n"
386 		"  halt\n"
387 		"  exit\n"
388 		"    to return to OpenFirmware\n"
389 		"  ?\n"
390 		"  help\n"
391 		"    to display this message\n"
392 		"or a boot specification:\n"
393 		"  [device] [kernel] [options]\n"
394 		"\n"
395 		"for example:\n"
396 		"  disk:a netbsd -s\n");
397 }
398 
399 static void
400 do_config_command(const char *cmd, const char *arg)
401 {
402 	DPRINTF(("do_config_command: %s\n", cmd));
403 	if (strcmp(cmd, "bootpartition") == 0) {
404 		char *c;
405 
406 		DPRINTF(("switching boot partition to %s from %s\n",
407 		    arg, bootdev));
408 		c = strrchr(bootdev, ':');
409 		if (!c) return;
410 		if (c[1] == 0) return;
411 		if (strlen(arg) > strlen(c)) return;
412 		strcpy(c, arg);
413 		DPRINTF(("new boot device: %s\n", bootdev));
414 		bootinfo_pass_bootdev = true;
415 	}
416 }
417 
418 static void
419 parse_boot_config(char *cfg, size_t len)
420 {
421 	const char *cmd = NULL, *arg = NULL;
422 
423 	while (len) {
424 		if (isspace(*cfg)) {
425 			cfg++; len--; continue;
426 		}
427 		if (*cfg == ';' || *cfg == '#') {
428 			while (len && *cfg != '\r' && *cfg != '\n') {
429 				cfg++; len--;
430 			}
431 			continue;
432 		}
433 		cmd = cfg;
434 		while (len && !isspace(*cfg)) {
435 			cfg++; len--;
436 		}
437 		*cfg = 0;
438 		if (len > 0) {
439 			cfg++; len--;
440 			while (isspace(*cfg) && len) {
441 				cfg++; len--;
442 			}
443 			if (len > 0 ) {
444 				arg = cfg;
445 				while (len && !isspace(*cfg)) {
446 					cfg++; len--;
447 				}
448 				*cfg = 0;
449 			}
450 		}
451 		do_config_command(cmd, arg);
452 		if (len > 0) {
453 			cfg++; len--;
454 		}
455 	}
456 }
457 
458 static void
459 check_boot_config(void)
460 {
461 	int fd, err, off, len;
462 	struct stat st;
463 	char *bc;
464 
465 	if (!root_fs_quickseekable) return;
466 	DPRINTF(("checking for /boot.cfg...\n"));
467 	fd = open("/boot.cfg", 0);
468 	if (fd < 0) return;
469 	DPRINTF(("found /boot.cfg\n"));
470 	if (fstat(fd, &st) == -1 || st.st_size > 32*1024) {
471 		close(fd);
472 		return;
473 	}
474 	bc = alloc(st.st_size+1);
475 	off = 0;
476 	do {
477 		len = read(fd, bc+off, 1024);
478 		if (len <= 0)
479 			break;
480 		off += len;
481 	} while (len > 0);
482 	bc[off] = 0;
483 	close(fd);
484 
485 	parse_boot_config(bc, off);
486 }
487 
488 void
489 main(void *ofw)
490 {
491 	int boothowto, i = 0;
492 
493 	char kernel[PROM_MAX_PATH];
494 	char bootline[PROM_MAX_PATH];
495 
496 	/* Initialize OpenFirmware */
497 	romp = ofw;
498 	prom_init();
499 
500 	printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
501 	DPRINTF((">> (%s, %s)\n", bootprog_maker, bootprog_date));
502 
503 	/* Figure boot arguments */
504 	strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
505 	boothowto = bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
506 
507 	for (;; *kernel = '\0') {
508 		if (boothowto & RB_ASKNAME) {
509 			char *cp, cmdline[PROM_MAX_PATH];
510 
511 			printf("Boot: ");
512 			gets(cmdline);
513 
514 			if (!strcmp(cmdline,"exit") ||
515 			    !strcmp(cmdline,"halt")) {
516 				prom_halt();
517 			} else if (!strcmp(cmdline, "?") ||
518 				   !strcmp(cmdline, "help")) {
519 				help();
520 				continue;
521 			}
522 
523 			boothowto  = bootoptions(cmdline, bootdev, kernel,
524 			    bootline);
525 			boothowto |= RB_ASKNAME;
526 			i = 0;
527 		}
528 
529 		if (*kernel == '\0') {
530 			if (kernelnames[i] == NULL) {
531 				boothowto |= RB_ASKNAME;
532 				continue;
533 			}
534 			strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
535 		} else if (i == 0) {
536 			/*
537 			 * Kernel name was passed via command line -- ask user
538 			 * again if requested image fails to boot.
539 			 */
540 			boothowto |= RB_ASKNAME;
541 		}
542 
543 		check_boot_config();
544 		start_kernel(kernel, bootline, ofw);
545 
546 		/*
547 		 * Try next name from kernel name list if not in askname mode,
548 		 * enter askname on reaching list's end.
549 		 */
550 		if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
551 			printf(": trying %s...\n", kernelnames[i]);
552 		} else {
553 			printf("\n");
554 			boothowto |= RB_ASKNAME;
555 		}
556 	}
557 
558 	(void)printf("Boot failed! Exiting to the Firmware.\n");
559 	prom_halt();
560 }
561