Home | History | Annotate | Line # | Download | only in ofwboot
boot.c revision 1.24
      1 /*	$NetBSD: boot.c,v 1.24 2010/08/25 16:38:04 christos 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, int isfloppy)
    354 {
    355 	int fd;
    356 	u_long marks[MARK_MAX];
    357 	int flags = LOAD_ALL;
    358 	if (isfloppy)
    359 		flags &= ~LOAD_BACKWARDS;
    360 
    361 	/*
    362 	 * First, load headers using default allocator and check whether kernel
    363 	 * entry address matches kernel text load address. If yes, this is the
    364 	 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
    365 	 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
    366 	 */
    367 	loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
    368 	if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
    369 		if (COMPAT_BOOT(marks) || compatmode) {
    370 			(void)printf("[c] ");
    371 			loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
    372 		} else {
    373 			loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
    374 		}
    375 		(void)printf("Loading %s: ", kernel);
    376 
    377 		if (fdloadfile(fd, marks, flags) != -1) {
    378 			close(fd);
    379 			jump_to_kernel(marks, kernel, bootline, ofw);
    380 		}
    381 	}
    382 	(void)printf("Failed to load '%s'.\n", kernel);
    383 }
    384 
    385 static void
    386 help(void)
    387 {
    388 	printf( "enter a special command\n"
    389 		"  halt\n"
    390 		"  exit\n"
    391 		"    to return to OpenFirmware\n"
    392 		"  ?\n"
    393 		"  help\n"
    394 		"    to display this message\n"
    395 		"or a boot specification:\n"
    396 		"  [device] [kernel] [options]\n"
    397 		"\n"
    398 		"for example:\n"
    399 		"  disk:a netbsd -s\n");
    400 }
    401 
    402 static void
    403 do_config_command(const char *cmd, const char *arg)
    404 {
    405 	DPRINTF(("do_config_command: %s\n", cmd));
    406 	if (strcmp(cmd, "bootpartition") == 0) {
    407 		char *c;
    408 
    409 		DPRINTF(("switching boot partition to %s from %s\n",
    410 		    arg, bootdev));
    411 		c = strrchr(bootdev, ':');
    412 		if (!c) return;
    413 		if (c[1] == 0) return;
    414 		if (strlen(arg) > strlen(c)) return;
    415 		strcpy(c, arg);
    416 		DPRINTF(("new boot device: %s\n", bootdev));
    417 		bootinfo_pass_bootdev = true;
    418 	}
    419 }
    420 
    421 static void
    422 parse_boot_config(char *cfg, size_t len)
    423 {
    424 	const char *cmd = NULL, *arg = NULL;
    425 
    426 	while (len) {
    427 		if (isspace(*cfg)) {
    428 			cfg++; len--; continue;
    429 		}
    430 		if (*cfg == ';' || *cfg == '#') {
    431 			while (len && *cfg != '\r' && *cfg != '\n') {
    432 				cfg++; len--;
    433 			}
    434 			continue;
    435 		}
    436 		cmd = cfg;
    437 		while (len && !isspace(*cfg)) {
    438 			cfg++; len--;
    439 		}
    440 		*cfg = 0;
    441 		if (len > 0) {
    442 			cfg++; len--;
    443 			while (isspace(*cfg) && len) {
    444 				cfg++; len--;
    445 			}
    446 			if (len > 0 ) {
    447 				arg = cfg;
    448 				while (len && !isspace(*cfg)) {
    449 					cfg++; len--;
    450 				}
    451 				*cfg = 0;
    452 			}
    453 		}
    454 		do_config_command(cmd, arg);
    455 		if (len > 0) {
    456 			cfg++; len--;
    457 		}
    458 	}
    459 }
    460 
    461 static void
    462 check_boot_config(void)
    463 {
    464 	int fd, err, off, len;
    465 	struct stat st;
    466 	char *bc;
    467 
    468 	if (!root_fs_quickseekable) return;
    469 	DPRINTF(("checking for /boot.cfg...\n"));
    470 	fd = open("/boot.cfg", 0);
    471 	if (fd < 0) return;
    472 	DPRINTF(("found /boot.cfg\n"));
    473 	if (fstat(fd, &st) == -1 || st.st_size > 32*1024) {
    474 		close(fd);
    475 		return;
    476 	}
    477 	bc = alloc(st.st_size+1);
    478 	off = 0;
    479 	do {
    480 		len = read(fd, bc+off, 1024);
    481 		if (len <= 0)
    482 			break;
    483 		off += len;
    484 	} while (len > 0);
    485 	bc[off] = 0;
    486 	close(fd);
    487 
    488 	parse_boot_config(bc, off);
    489 }
    490 
    491 void
    492 main(void *ofw)
    493 {
    494 	int boothowto, i = 0, isfloppy;
    495 
    496 	char kernel[PROM_MAX_PATH];
    497 	char bootline[PROM_MAX_PATH];
    498 
    499 	/* Initialize OpenFirmware */
    500 	romp = ofw;
    501 	prom_init();
    502 
    503 	printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
    504 	DPRINTF((">> (%s, %s)\n", bootprog_maker, bootprog_date));
    505 
    506 	/* Figure boot arguments */
    507 	strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
    508 	boothowto = bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
    509 	isfloppy = strstr(bootdev, "fd") || strstr(bootdev, "floppy");
    510 
    511 	for (;; *kernel = '\0') {
    512 		if (boothowto & RB_ASKNAME) {
    513 			char *cp, cmdline[PROM_MAX_PATH];
    514 
    515 			printf("Boot: ");
    516 			gets(cmdline);
    517 
    518 			if (!strcmp(cmdline,"exit") ||
    519 			    !strcmp(cmdline,"halt")) {
    520 				prom_halt();
    521 			} else if (!strcmp(cmdline, "?") ||
    522 				   !strcmp(cmdline, "help")) {
    523 				help();
    524 				continue;
    525 			}
    526 
    527 			boothowto  = bootoptions(cmdline, bootdev, kernel,
    528 			    bootline);
    529 			boothowto |= RB_ASKNAME;
    530 			i = 0;
    531 		}
    532 
    533 		if (*kernel == '\0') {
    534 			if (kernelnames[i] == NULL) {
    535 				boothowto |= RB_ASKNAME;
    536 				continue;
    537 			}
    538 			strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
    539 		} else if (i == 0) {
    540 			/*
    541 			 * Kernel name was passed via command line -- ask user
    542 			 * again if requested image fails to boot.
    543 			 */
    544 			boothowto |= RB_ASKNAME;
    545 		}
    546 
    547 		check_boot_config();
    548 		start_kernel(kernel, bootline, ofw, isfloppy);
    549 
    550 		/*
    551 		 * Try next name from kernel name list if not in askname mode,
    552 		 * enter askname on reaching list's end.
    553 		 */
    554 		if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
    555 			printf(": trying %s...\n", kernelnames[i]);
    556 		} else {
    557 			printf("\n");
    558 			boothowto |= RB_ASKNAME;
    559 		}
    560 	}
    561 
    562 	(void)printf("Boot failed! Exiting to the Firmware.\n");
    563 	prom_halt();
    564 }
    565