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