Home | History | Annotate | Line # | Download | only in ofwboot
boot.c revision 1.13
      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