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