Home | History | Annotate | Line # | Download | only in ofwboot
      1 /*	$NetBSD: boot.c,v 1.35 2017/09/15 13:25:34 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/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 == ' ') {
    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_bootunit)
    288 		bi_add(&bi_unit, BTINFO_BOOTDEV_UNIT,
    289 		    sizeof(bi_unit));
    290 	if (bootinfo_pass_bootdev) {
    291 		struct {
    292 			struct btinfo_common common;
    293 			char name[256];
    294 		} info;
    295 
    296 		strcpy(info.name, bootdev);
    297 		bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev)
    298 			+sizeof(struct btinfo_bootdev));
    299 	}
    300 
    301 	sparc64_finalize_tlb(marks[MARK_DATA]);
    302 	sparc64_bi_add();
    303 
    304 	ssym  = (void*)(long)marks[MARK_SYM];
    305 	esym  = (void*)(long)marks[MARK_END];
    306 
    307 	DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym));
    308 
    309 	/* Adjust ksyms pointers, if needed. */
    310 	if (COMPAT_BOOT(marks) || compatmode) {
    311 		ksyms_copyout(&ssym, &esym);
    312 	}
    313 
    314 	freeall();
    315 	/*
    316 	 * When we come in args consists of a pointer to the boot
    317 	 * string.  We need to fix it so it takes into account
    318 	 * other params such as romp.
    319 	 */
    320 
    321 	/*
    322 	 * Stash pointer to end of symbol table after the argument
    323 	 * strings.
    324 	 */
    325 	l = strlen(args) + 1;
    326 	memcpy(args + l, &esym, sizeof(esym));
    327 	l += sizeof(esym);
    328 
    329 	/*
    330 	 * Tell the kernel we're an OpenFirmware system.
    331 	 */
    332 	machine_tag = SPARC_MACHINE_OPENFIRMWARE;
    333 	memcpy(args + l, &machine_tag, sizeof(machine_tag));
    334 	l += sizeof(machine_tag);
    335 
    336 	/*
    337 	 * Since we don't need the boot string (we can get it from /chosen)
    338 	 * we won't pass it in.  Just pass in esym and magic #
    339 	 */
    340 	newargs[0] = SPARC_MACHINE_OPENFIRMWARE;
    341 	newargs[1] = (long)esym;
    342 	newargs[2] = (long)ssym;
    343 	newargs[3] = (long)(void*)bootinfo;
    344 	args = (char *)newargs;
    345 	l = sizeof(newargs);
    346 
    347 	/* if -D is set then pause in the PROM. */
    348 	if (debug > 1) callrom();
    349 
    350 	/*
    351 	 * Jump directly to the kernel. Solaris kernel and Sun PROM
    352 	 * flash updates expect ROMP vector in %o0, so we do. Format
    353 	 * of other parameters and their order reflect OF_chain()
    354 	 * symantics since this is what older NetBSD kernels rely on.
    355 	 * (see sparc64/include/bootinfo.h for specification).
    356 	 */
    357 	DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw,
    358 				(long)args, (long)l, (long)ofw, (long)ofw,
    359 				(void*)marks[MARK_ENTRY]));
    360 	(*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw,
    361 				      (long)ofw);
    362 	printf("Returned from kernel entry point!\n");
    363 }
    364 
    365 static void
    366 start_kernel(char *kernel, char *bootline, void *ofw, int isfloppy,
    367 	int boothowto)
    368 {
    369 	int fd;
    370 	u_long marks[MARK_MAX] = {0};
    371 	int flags = LOAD_ALL;
    372 
    373 	if (isfloppy)
    374 		flags &= ~LOAD_BACKWARDS;
    375 
    376 	/*
    377 	 * First, load headers using default allocator and check whether kernel
    378 	 * entry address matches kernel text load address. If yes, this is the
    379 	 * old kernel designed for ofwboot v1.8 and therefore it must be mapped
    380 	 * by PROM. Otherwise, map the kernel with 4MB permanent pages.
    381 	 */
    382 	loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR);
    383 	if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) {
    384 		if (COMPAT_BOOT(marks) || compatmode) {
    385 			(void)printf("[c] ");
    386 			loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR);
    387 		} else {
    388 			loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR);
    389 		}
    390 		(void)printf("Loading %s: ", kernel);
    391 
    392 		if (fdloadfile(fd, marks, flags) != -1) {
    393 			close(fd);
    394 			jump_to_kernel(marks, kernel, bootline, ofw, boothowto);
    395 		}
    396 	}
    397 	(void)printf("Failed to load '%s'.\n", kernel);
    398 }
    399 
    400 static void
    401 help(void)
    402 {
    403 	printf( "enter a special command\n"
    404 		"  halt\n"
    405 		"  exit\n"
    406 		"    to return to OpenFirmware\n"
    407 		"  ?\n"
    408 		"  help\n"
    409 		"    to display this message\n"
    410 		"or a boot specification:\n"
    411 		"  [device] [kernel] [options]\n"
    412 		"\n"
    413 		"for example:\n"
    414 		"  disk:a netbsd -s\n");
    415 }
    416 
    417 static void
    418 do_config_command(const char *cmd, char *arg)
    419 {
    420 	DPRINTF(("do_config_command: %s\n", cmd));
    421 	if (strcmp(cmd, "bootpartition") == 0) {
    422 		char *c;
    423 
    424 		DPRINTF(("switching boot partition to %s from %s\n",
    425 		    arg, bootdev));
    426 		c = strrchr(bootdev, ':');
    427 		if (!c) return;
    428 		if (c[1] == 0) return;
    429 		if (strlen(arg) > strlen(c)) return;
    430 		strcpy(c, arg);
    431 		DPRINTF(("new boot device: %s\n", bootdev));
    432 		bootinfo_pass_bootdev = true;
    433 	}
    434 }
    435 
    436 static void
    437 check_boot_config(void)
    438 {
    439 	if (!root_fs_quickseekable)
    440 		return;
    441 
    442 	perform_bootcfg(BOOTCFG_FILENAME, &do_config_command, 32768);
    443 }
    444 
    445 void
    446 main(void *ofw)
    447 {
    448 	int boothowto, i = 0, isfloppy, kboothowto;
    449 
    450 	char kernel[PROM_MAX_PATH];
    451 	char bootline[PROM_MAX_PATH];
    452 
    453 	/* Initialize OpenFirmware */
    454 	romp = ofw;
    455 	prom_init();
    456 
    457 	printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev);
    458 
    459 	/* Figure boot arguments */
    460 	strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1);
    461 	kboothowto = boothowto =
    462 	    bootoptions(prom_getbootargs(), bootdev, kernel, bootline);
    463 	isfloppy = bootdev_isfloppy(bootdev);
    464 
    465 	for (;; *kernel = '\0') {
    466 		if (boothowto & RB_ASKNAME) {
    467 			char cmdline[PROM_MAX_PATH];
    468 
    469 			printf("Boot: ");
    470 			kgets(cmdline, sizeof(cmdline));
    471 
    472 			if (!strcmp(cmdline,"exit") ||
    473 			    !strcmp(cmdline,"halt")) {
    474 				prom_halt();
    475 			} else if (!strcmp(cmdline, "?") ||
    476 				   !strcmp(cmdline, "help")) {
    477 				help();
    478 				continue;
    479 			}
    480 
    481 			boothowto  = bootoptions(cmdline, bootdev, kernel,
    482 			    bootline);
    483 			boothowto |= RB_ASKNAME;
    484 			i = 0;
    485 		}
    486 
    487 		if (*kernel == '\0') {
    488 			if (kernelnames[i] == NULL) {
    489 				boothowto |= RB_ASKNAME;
    490 				continue;
    491 			}
    492 			strncpy(kernel, kernelnames[i++], PROM_MAX_PATH);
    493 		} else if (i == 0) {
    494 			/*
    495 			 * Kernel name was passed via command line -- ask user
    496 			 * again if requested image fails to boot.
    497 			 */
    498 			boothowto |= RB_ASKNAME;
    499 		}
    500 
    501 		check_boot_config();
    502 		start_kernel(kernel, bootline, ofw, isfloppy, kboothowto);
    503 
    504 		/*
    505 		 * Try next name from kernel name list if not in askname mode,
    506 		 * enter askname on reaching list's end.
    507 		 */
    508 		if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) {
    509 			printf(": trying %s...\n", kernelnames[i]);
    510 		} else {
    511 			printf("\n");
    512 			boothowto |= RB_ASKNAME;
    513 		}
    514 	}
    515 
    516 	(void)printf("Boot failed! Exiting to the Firmware.\n");
    517 	prom_halt();
    518 }
    519