xref: /netbsd-src/sys/arch/sparc/stand/ofwboot/boot.c (revision ce2c90c7c172d95d2402a5b3d96d8f8e6d138a21)
1 /*	$NetBSD: boot.c,v 1.13 2006/07/13 20:03:34 uwe 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 
93 int debug  = 0;
94 int compatmode = 0;
95 
96 #if 0
97 static void
98 prom2boot(char *dev)
99 {
100 	char *cp, *lp = 0;
101 	int handle;
102 	char devtype[16];
103 
104 	for (cp = dev; *cp; cp++)
105 		if (*cp == ':')
106 			lp = cp;
107 	if (!lp)
108 		lp = cp;
109 	*lp = 0;
110 }
111 #endif
112 
113 static int
114 bootoptions(const char *ap, char *kernel, char *options)
115 {
116 	int v = 0;
117 	const char *cp;
118 
119 	*kernel  = '\0';
120 	*options = '\0';
121 
122 	if (ap == NULL) {
123 		return (0);
124 	}
125 
126 	while (*ap == ' ') {
127 		ap++;
128 	}
129 
130 	cp = ap;
131 	if (*ap != '-') {
132 		while (*ap != '\0' && *ap != ' ') {
133 			ap++;
134 		}
135 
136 		memcpy(kernel, cp, (ap - cp));
137 		kernel[ap - cp] = '\0';
138 
139 		while (*ap != '\0' && *ap == ' ') {
140 			ap++;
141 		}
142 	}
143 
144 	strcpy(options, ap);
145 	while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') {
146 		BOOT_FLAG(*ap, v);
147 		switch(*ap++) {
148 		case 'D':
149 			debug = 2;
150 			break;
151 		case 'C':
152 			compatmode = 1;
153 			break;
154 		default:
155 			break;
156 		}
157 	}
158 
159 	if (((v & RB_KDB) != 0) && (debug == 0)) {
160 		debug = 1;
161 	}
162 
163 	DPRINTF(("bootoptions: kernel='%s', options='%s'\n", kernel, options));
164 	return (v);
165 }
166 
167 /*
168  * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle
169  * ksyms information unless it resides in a dedicated memory allocated from
170  * PROM and aligned on NBPG boundary. This is because the kernels calculate
171  * their ends on their own, they use address of 'end[]' reference which follows
172  * text segment. Ok, allocate some memory from PROM and copy symbol information
173  * over there.
174  */
175 static void
176 ksyms_copyout(void **ssym, void **esym)
177 {
178 	void *addr;
179 	int kssize = (int)(long)(*esym - *ssym + 1);
180 
181 	DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n",
182 				*ssym, *esym, kssize));
183 
184 	if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) {
185 		panic("ksyms_copyout(): no space for symbol table");
186 	}
187 
188 	memcpy(addr, *ssym, kssize);
189 	*ssym = addr;
190 	*esym = addr + kssize - 1;
191 
192 	DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym));
193 }
194 
195 /*
196  * Prepare boot information and jump directly to the kernel.
197  */
198 static void
199 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw)
200 {
201 	extern char end[];
202 	int l, machine_tag;
203 	long newargs[4];
204 	void *ssym, *esym;
205 	vaddr_t bootinfo;
206 	struct btinfo_symtab bi_sym;
207 	struct btinfo_kernend bi_kend;
208 	char *cp;
209 	char bootline[PROM_MAX_PATH * 2];
210 
211 	/* Compose kernel boot line. */
212 	strncpy(bootline, kernel, sizeof(bootline));
213 	cp = bootline + strlen(bootline);
214 	if (*args) {
215 		*cp++ = ' ';
216 		strncpy(bootline, args, sizeof(bootline) - (cp - bootline));
217 	}
218 	*cp = 0; args = bootline;
219 
220 	/* Record symbol information in the bootinfo. */
221 	bootinfo = bi_init(marks[MARK_END]);
222 	bi_sym.nsym = marks[MARK_NSYM];
223 	bi_sym.ssym = marks[MARK_SYM];
224 	bi_sym.esym = marks[MARK_END];
225 	bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym));
226 	bi_kend.addr= bootinfo + BOOTINFO_SIZE;
227 	bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend));
228 	sparc64_bi_add();
229 
230 	ssym  = (void*)(long)marks[MARK_SYM];
231 	esym  = (void*)(long)marks[MARK_END];
232 
233 	DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
234 
235 	/* Adjust ksyms pointers, if needed. */
236 	if (COMPAT_BOOT(marks) || compatmode) {
237 		ksyms_copyout(&ssym, &esym);
238 	}
239 
240 	freeall();
241 	/*
242 	 * When we come in args consists of a pointer to the boot
243 	 * string.  We need to fix it so it takes into account
244 	 * other params such as romp.
245 	 */
246 
247 	/*
248 	 * Stash pointer to end of symbol table after the argument
249 	 * strings.
250 	 */
251 	l = strlen(args) + 1;
252 	bcopy(&esym, args + l, sizeof(esym));
253 	l += sizeof(esym);
254 
255 	/*
256 	 * Tell the kernel we're an OpenFirmware system.
257 	 */
258 	machine_tag = SPARC_MACHINE_OPENFIRMWARE;
259 	bcopy(&machine_tag, args + l, sizeof(machine_tag));
260 	l += sizeof(machine_tag);
261 
262 	/*
263 	 * Since we don't need the boot string (we can get it from /chosen)
264 	 * we won't pass it in.  Just pass in esym and magic #
265 	 */
266 	newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
267 	newargs[1] = (long)esym;
268 	newargs[2] = (long)ssym;
269 	newargs[3] = (long)(void*)bootinfo;
270 	args = (char *)newargs;
271 	l = sizeof(newargs);
272 
273 	/* if -D is set then pause in the PROM. */
274 	if (debug > 1) callrom();
275 
276 	/*
277 	 * Jump directly to the kernel. Solaris kernel and Sun PROM
278 	 * flash updates expect ROMP vector in %o0, so we do. Format
279 	 * of other parameters and their order reflect OF_chain()
280 	 * symantics since this is what older NetBSD kernels rely on.
281 	 * (see sparc64/include/bootinfo.h for specification).
282 	 */
283 	DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
284 				(long)args, (long)l, (long)ofw, (long)ofw,
285 				(void*)marks[MARK_ENTRY]));
286 	(*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
287 				      (long)ofw);
288 	printf("Returned from kernel entry point!\n");
289 }
290 
291 static void
292 start_kernel(char *kernel, char *bootline, void *ofw)
293 {
294 	int fd;
295 	u_long marks[MARK_MAX];
296 
297 	/*
298 	 * First, load headers using default allocator and check whether kernel
299 	 * entry address matches kernel text load address. If yes, this is the
300 	 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
301 	 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
302 	 */
303 	loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
304 	if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
305 		if (COMPAT_BOOT(marks) || compatmode) {
306 			(void)printf("[c] ");
307 			loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
308 		} else {
309 			loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
310 		}
311 		(void)printf("Loading %s: ", kernel);
312 
313 		if (fdloadfile(fd, marks, LOAD_ALL) != -1) {
314 			jump_to_kernel(marks, kernel, bootline, ofw);
315 		}
316 	}
317 	(void)printf("Failed to load '%s'.\n", kernel);
318 }
319 
320 void
321 main(void *ofw)
322 {
323 	int boothowto, i = 0;
324 
325 	char kernel[PROM_MAX_PATH];
326 	char bootline[PROM_MAX_PATH];
327 
328 	/* Initialize OpenFirmware */
329 	romp = ofw;
330 	prom_init();
331 
332 	printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
333 	printf(">> (%s, %s)\n", bootprog_maker, bootprog_date);
334 
335 	/* Figure boot arguments */
336 	boothowto = bootoptions(prom_getbootargs(), kernel, bootline);
337 	strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
338 	strncpy(bootline, prom_getbootargs(), sizeof(bootline) - 1);
339 
340 	for (;; *kernel = '\0') {
341 		if (boothowto & RB_ASKNAME) {
342 			char *cp, cmdline[PROM_MAX_PATH];
343 
344 			printf("Boot: ");
345 			gets(cmdline);
346 
347 			boothowto  = bootoptions(cmdline, kernel, bootline);
348 			boothowto |= RB_ASKNAME;
349 
350 			if (!strcmp(kernel,"exit") || !strcmp(kernel,"halt")) {
351 				prom_halt();
352 			}
353 		}
354 
355 		if (*kernel == '\0') {
356 			if (kernelnames[i] == NULL) {
357 				boothowto |= RB_ASKNAME;
358 				continue;
359 			}
360 			strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
361 		} else if (i == 0) {
362 			/*
363 			 * Kernel name was passed via command line -- ask user
364 			 * again if requested image fails to boot.
365 			 */
366 			boothowto |= RB_ASKNAME;
367 		}
368 
369 		start_kernel(kernel, bootline, ofw);
370 
371 		/*
372 		 * Try next name from kernel name list if not in askname mode,
373 		 * enter askname on reaching list's end.
374 		 */
375 		if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
376 			printf(": trying %s...\n", kernelnames[i]);
377 		} else {
378 			printf("\n");
379 			boothowto |= RB_ASKNAME;
380 		}
381 	}
382 
383 	(void)printf("Boot failed! Exiting to the Firmware.\n");
384 	prom_halt();
385 }
386