Home | History | Annotate | Line # | Download | only in loadbsd
loadbsd.c revision 1.7.16.1
      1 /*
      2  *	Load and boot NetBSD kernel on Human68k
      3  *
      4  *	written by Yasha (ITOH Yasufumi)
      5  *	public domain
      6  *
      7  *	loadbsd [-hvV] [-abDs] [-r root_device] netbsd
      8  *
      9  *	loadbsd options:
     10  *		-h	help
     11  *		-V	print version and exit
     12  *
     13  *	kernel options:
     14  *		-a	auto boot, opposite of -s
     15  *		-s	single user boot (default)
     16  *		-D	enter kernel debugger
     17  *		-b	ask root device
     18  *		-r	specify root device
     19  *		-q	quiet boot
     20  *		-v	verbose boot (also turn on verbosity of loadbsd)
     21  *
     22  *	$NetBSD: loadbsd.c,v 1.7.16.1 2002/05/30 15:36:42 gehenna Exp $
     23  */
     24 
     25 #include <sys/cdefs.h>
     26 
     27 __RCSID("$NetBSD: loadbsd.c,v 1.7.16.1 2002/05/30 15:36:42 gehenna Exp $");
     28 #define VERSION	"$Revision: 1.7.16.1 $ $Date: 2002/05/30 15:36:42 $"
     29 
     30 #include <sys/types.h>		/* ntohl */
     31 #include <sys/reboot.h>
     32 #include <sys/param.h>		/* ALIGN, ALIGNBYTES */
     33 #include <a.out.h>
     34 #include <sys/exec_elf.h>
     35 #include <string.h>
     36 #include <machine/bootinfo.h>
     37 
     38 #include <dos.h>
     39 #include <iocs.h>
     40 #include "../common/xprintf.h"
     41 #include "trampoline.h"
     42 
     43 #define DEFAULT_ROOTDEVNAME	"sd@0,0:a"
     44 
     45 #define ISDIGIT(c)	((c) >= '0' && (c) <= '9')
     46 
     47 #define GETDECIMAL(var, str)	\
     48 	do {	var *= 10; var += *str++ - '0'; } while (ISDIGIT(*str))
     49 
     50 static const char *lookupif __P((const char *name,
     51 				 unsigned *pif, unsigned *punit));
     52 static void get_current_scsi_interface __P((unsigned *pif, unsigned *punit));
     53 static int bootdev __P((const char *devstr));
     54 static struct tramparg *read_kernel __P((const char *fn));
     55 static int chkmpu __P((void));
     56 static __dead void usage __P((int status, const char *msg))
     57 					__attribute__((noreturn));
     58 
     59 int main __P((int argc, char *argv[]));
     60 
     61 int opt_v;
     62 int opt_N;
     63 const char *kernel_fn;
     64 
     65 const struct hatbl {
     66 	char name[4];
     67 	unsigned short id;
     68 } hatable[] = {
     69 	X68K_BOOT_SCSIIF_LIST
     70 };
     71 
     72 /*
     73  * parse interface name
     74  * return the next position
     75  */
     76 static const char *
     77 lookupif(name, pif, punit)
     78 	const char *name;
     79 	unsigned *pif, *punit;
     80 {
     81 	unsigned u, unit;
     82 	const char *p;
     83 
     84 	for (u = 0; u < sizeof hatable / sizeof hatable[0]; u++) {
     85 		const char *n;
     86 
     87 		for (n = hatable[u].name, p = name; *n && *n == *p; n++, p++)
     88 			;
     89 		if (!*n)
     90 			goto found;
     91 	}
     92 	/* not found */
     93 	return (char *) 0;
     94 
     95 found:
     96 	if (*p == '@')
     97 		p++;
     98 
     99 	/* get unit # */
    100 	if (!ISDIGIT(*p))
    101 		return (char *) 0;
    102 
    103 	unit = 0;
    104 	GETDECIMAL(unit, p);
    105 
    106 	*pif = hatable[u].id;
    107 	*punit = unit;
    108 
    109 	return p;
    110 }
    111 
    112 /*
    113  * if the SCSI interface is not specified, use the current one
    114  */
    115 static void
    116 get_current_scsi_interface(pif, punit)
    117 	unsigned *pif, *punit;
    118 {
    119 	unsigned binf;
    120 	char *bootrom;
    121 	int bus_err_buf;
    122 
    123 	binf = (unsigned) IOCS_BOOTINF();
    124 	if (binf < 0x00fc0000)
    125 		return;			/* not booted from SCSI */
    126 
    127 	bootrom = (char *) (binf & 0x00ffffe0);
    128 	if (IOCS_B_LPEEK(bootrom + 0x24) == 0x53435349 &&	/* 'SCSI' */
    129 	    IOCS_B_WPEEK(bootrom + 0x28) == 0x494E) {		/* 'IN' */
    130 		/* spc0 */
    131 		*pif = X68K_BOOT_SCSIIF_SPC;
    132 		*punit = 0;
    133 	} else if (DOS_BUS_ERR(&bus_err_buf, (void *)EXSPC_BDID, 1)) {
    134 		/* mha0 */
    135 		*pif = X68K_BOOT_SCSIIF_MHA;
    136 		*punit = 0;
    137 	} else {
    138 		/* spc1 */
    139 		*pif = X68K_BOOT_SCSIIF_SPC;
    140 		*punit = 1;
    141 	}
    142 }
    143 
    144 /*
    145  * parse device name
    146  *
    147  * [/<controller>@<unit>/]<device>@<unit>[,<lun>][:<partition>]
    148  *
    149  * <unit> must be target SCSI ID if <device> is a SCSI device
    150  *
    151  *	full form:
    152  *		/spc@0/sd@1,2:e
    153  *
    154  *	partial form:
    155  *		/mha@0/sd@1	= /mha@0/sd@1,0:a
    156  *		sd@1:e		= /current_device/sd@1,0e
    157  *		sd@1,2:e	= /current_device/sd@1,2:e
    158  */
    159 
    160 const struct devtbl {
    161 	char name[3];
    162 	u_char major;
    163 } devtable[] = {
    164 	X68K_BOOT_DEV_LIST,
    165 	X68K_BOOT_NETIF_LIST
    166 };
    167 
    168 static int
    169 bootdev(devstr)
    170 	const char *devstr;
    171 {
    172 	unsigned u;
    173 	unsigned major, unit, lun, partition;
    174 	int dev;
    175 	const char *s = devstr;
    176 	unsigned interface = 0, unit_if = 0;
    177 
    178 	if (*s == '/') {
    179 		/*
    180 		 * /<interface>/<device>"
    181 		 * "/spc@1/sd@2,3:e"
    182 		 */
    183 		while (*++s == '/')	/* skip slashes */
    184 			;
    185 		if (!strchr(s, '/'))
    186 			xerrx(1, "%s: bad format", devstr);
    187 
    188 		if (!(s = lookupif(s, &interface, &unit_if)))
    189 			xerrx(1, "%s: unknown interface", devstr);
    190 
    191 		while (*s == '/')	/* skip slashes */
    192 			s++;
    193 	} else {
    194 		/* make lint happy */
    195 		interface = 0;
    196 		unit_if = 0;
    197 	}
    198 
    199 	/* allow r at the top */
    200 	if (*s == 'r')
    201 		s++;
    202 
    203 	for (u = 0; u < sizeof devtable / sizeof devtable[0]; u++)
    204 		if (s[0] == devtable[u].name[0] && s[1] == devtable[u].name[1])
    205 			goto found;
    206 
    207 	/* not found */
    208 	xerrx(1, "%s: unknown device", devstr);
    209 
    210 found:	major = devtable[u].major;
    211 
    212 	/*
    213 	 * <type>@unit[,lun][:part]
    214 	 * "sd@1,3:a"
    215 	 */
    216 
    217 	/* get device unit # */
    218 	s += 2;
    219 	if (*s == '@')
    220 		s++;
    221 	if (!*s)
    222 		xerrx(1, "%s: missing unit number", devstr);
    223 	if (!ISDIGIT(*s))
    224 		xerrx(1, "%s: wrong device", devstr);
    225 
    226 	unit = 0;
    227 	GETDECIMAL(unit, s);
    228 
    229 	lun = 0;
    230 	if (*s == ',') {
    231 		s++;
    232 		if (!ISDIGIT(*s))
    233 			xerrx(1, "%s: wrong device", devstr);
    234 		GETDECIMAL(lun, s);
    235 	}
    236 
    237 	/* get device partition */
    238 	if (*s == ':')
    239 		s++;
    240 	if (!*s)
    241 		partition = 0;	/* no partition letter -- assuming 'a' */
    242 	else if (!s[1])
    243 		partition = *s - 'a';
    244 	else
    245 		xerrx(1, "%s: wrong partition letter", devstr);
    246 
    247 	/*
    248 	 * sanity check
    249 	 */
    250 	if (unit_if >= 16)
    251 		xerrx(1, "%s: interface unit # too large", devstr);
    252 	if (unit >= 16)
    253 		xerrx(1, "%s: device unit # too large", devstr);
    254 	if (lun >= 8)
    255 		xerrx(1, "%s: SCSI LUN >= 8 is not supported yet", devstr);
    256 	if (partition >= 16)
    257 		xerrx(1, "%s: unsupported partition", devstr);
    258 
    259 	/*
    260 	 * encode device to be passed to kernel
    261 	 */
    262 	if (X68K_BOOT_DEV_IS_SCSI(major)) {
    263 		/*
    264 		 * encode SCSI device
    265 		 */
    266 		if (interface == 0)
    267 			get_current_scsi_interface(&interface, &unit_if);
    268 
    269 		dev = X68K_MAKESCSIBOOTDEV(major, interface, unit_if,
    270 						unit, lun, partition);
    271 	} else {
    272 		/* encode non-SCSI device */
    273 		dev = X68K_MAKEBOOTDEV(major, unit, partition);
    274 	}
    275 
    276 	if (opt_v)
    277 		xwarnx("%s: major %u, if %u, un_if %u, unit %u, lun %u, partition %u; bootdev 0x%x",
    278 			devstr, major, interface, unit_if, unit, lun, partition, dev);
    279 
    280 	return dev;
    281 }
    282 
    283 #define LOADBSD
    284 #include "../common/exec_sub.c"
    285 
    286 /*
    287  * read kernel and create trampoline
    288  *
    289  *	|----------------------| <- allocated buf addr
    290  *	| kernel image         |
    291  *	~ (exec file contents) ~
    292  *	|                      |
    293  *	|----------------------| <- return value (entry addr of trampoline)
    294  *	| struct tramparg      |
    295  *	| (trampoline args)    |
    296  *	|----------------------|
    297  *	| trampoline code      |
    298  *	| (in assembly)        |
    299  *	|----------------------|
    300  */
    301 static struct tramparg *
    302 read_kernel(fn)
    303 	const char *fn;
    304 {
    305 	int fd;
    306 	union dos_fcb *fcb;
    307 	size_t filesize, nread;
    308 	void *buf;
    309 	struct tramparg *arg;
    310 	size_t size_tramp = end_trampoline - trampoline;
    311 
    312 	kernel_fn = fn;
    313 
    314 	if ((fd = DOS_OPEN(fn, 0x00)) < 0)	/* read only */
    315 		xerr(1, "%s: open", fn);
    316 
    317 	if ((int)(fcb = DOS_GET_FCB_ADR(fd)) < 0)
    318 		xerr(1, "%s: get_fcb_adr", fn);
    319 
    320 	/*
    321 	 * XXX FCB is in supervisor area
    322 	 */
    323 	/*if (fcb->blk.mode != 0)*/
    324 	if (IOCS_B_BPEEK((char *)fcb + 1) & 0x80)
    325 		xerrx(1, "%s: Not a regular file", fn);
    326 
    327 	/*filesize = fcb->blk.size;*/
    328 	filesize = IOCS_B_LPEEK(&fcb->blk.size);
    329 
    330 	if (filesize < sizeof(Elf32_Ehdr))
    331 		xerrx(1, "%s: Unknown format", fn);
    332 
    333 	/*
    334 	 * read entire file
    335 	 */
    336 	if ((int)(buf = DOS_MALLOC(filesize + ALIGNBYTES
    337 				   + sizeof(struct tramparg)
    338 				   + size_tramp + SIZE_TMPSTACK)) < 0)
    339 		xerr(1, "read_kernel");
    340 
    341 	if ((nread = DOS_READ(fd, buf, filesize)) != filesize) {
    342 		if ((int)nread < 0)
    343 			xerr(1, "%s: read", fn);
    344 		else
    345 			xerrx(1, "%s: short read", fn);
    346 	}
    347 
    348 	if (DOS_CLOSE(fd) < 0)
    349 		xerr(1, "%s: close", fn);
    350 
    351 	/*
    352 	 * address for argument for trampoline code
    353 	 */
    354 	arg = (struct tramparg *) ALIGN((char *) buf + nread);
    355 
    356 	if (opt_v)
    357 		xwarnx("trampoline arg at %p", arg);
    358 
    359 	xk_load(&arg->xk, buf, 0 /* XXX load addr should not be fixed */);
    360 
    361 	/*
    362 	 * create argument for trampoline code
    363 	 */
    364 	arg->bsr_inst = TRAMP_BSR + sizeof(struct tramparg) - 2;
    365 	arg->tmp_stack = (char *) arg + sizeof(struct tramparg)
    366 				+ size_tramp + SIZE_TMPSTACK;
    367 	arg->mpu_type = IOCS_MPU_STAT() & 0xff;
    368 
    369 	arg->xk.d5 = IOCS_BOOTINF();	/* unused for now */
    370 #if 0
    371 	/* filled afterwards */
    372 	arg->xk.rootdev =
    373 	arg->xk.boothowto =
    374 #endif
    375 
    376 	if (opt_v)
    377 		xwarnx("args: mpu %d, image %p, load 0x%x, entry 0x%x",
    378 			arg->mpu_type, arg->xk.sec[0].sec_image,
    379 			arg->xk.load_addr, arg->xk.entry_addr);
    380 
    381 	/*
    382 	 * copy trampoline code
    383 	 */
    384 	if (opt_v)
    385 		xwarnx("trampoline code at %p (%u bytes)",
    386 			(char *) arg + sizeof(struct tramparg), size_tramp);
    387 
    388 	memcpy((char *) arg + sizeof(struct tramparg), trampoline, size_tramp);
    389 
    390 	return arg;
    391 }
    392 
    393 /*
    394  * MC68000/010		-> return zero
    395  * MC68020 and later	-> return nonzero
    396  */
    397 static int
    398 chkmpu()
    399 {
    400 	register int ret asm("%d0");
    401 
    402 	asm("| %0 <- this must be %%d0\n\
    403 	moveq	#1,%%d0\n\
    404 	.long	0x103B02FF	| foo: moveb %%pc@((foo+1)-foo-2:B,d0:W:2),%%d0\n\
    405 	|	      ^ ^\n\
    406 	| %%d0.b	= 0x02	(68000/010)\n\
    407 	|	= 0xff	(68020 and later)\n\
    408 	bmis	1f\n\
    409 	moveq	#0,%%d0		| 68000/010\n\
    410 1:"	: "=d" (ret));
    411 
    412 	return ret;
    413 }
    414 
    415 static __dead void
    416 usage(status, msg)
    417 	int status;
    418 	const char *msg;
    419 {
    420 	extern const char *const __progname;
    421 
    422 	if (msg)
    423 		xwarnx("%s", msg);
    424 
    425 	xerrprintf("\
    426 %s [-hvV] [-abDs] [-r root_device] netbsd\n\
    427 \n\
    428 loadbsd options:\n\
    429 \t-h	help\n\
    430 \t-v	verbose\n\
    431 \t-V	print version and exit\n\
    432 \n\
    433 kernel options:\n\
    434 \t-a	auto boot, opposite of -s\n\
    435 \t-s	single user boot (default)\n\
    436 \t-D	enter kernel debugger\n\
    437 \t-b	ask root device\n\
    438 \t-r	specify root device (default %s)\n\
    439 \t	format:  [/interface/]device@unit[,lun][:partition]\n\
    440 \t	    interface: one of  spc@0, spc@1, mha@0\n\
    441 \t		       (current boot interface if omitted)\n\
    442 \t	    device:    one of  fd, sd, cd, md, ne\n\
    443 \t	    unit:      device unit number (SCSI ID for SCSI device)\n\
    444 \t	    lun:       SCSI LUN # (0 if omitted)\n\
    445 \t	    partition: partition letter ('a' if omitted)\n\
    446 ", __progname, DEFAULT_ROOTDEVNAME);
    447 
    448 	DOS_EXIT2(status);
    449 }
    450 
    451 int
    452 main(argc, argv)
    453 	int argc;
    454 	char *argv[];
    455 {
    456 	char *rootdevname = 0;
    457 	int rootdev;
    458 	u_long boothowto = RB_SINGLE;
    459 	const char *kernel;
    460 	char *p, **flg, **arg;
    461 	struct tramparg *tramp;
    462 	struct dos_dregs regs;	/* unused... */
    463 	int i;
    464 
    465 	/* parse options */
    466 	for (arg = flg = argv + 1; (p = *flg) && *p == '-'; ) {
    467 		int c;
    468 
    469 		while ((c = *++p))
    470 			switch (c) {
    471 			case 'h':
    472 				usage(0, (char *) 0);
    473 				/* NOTREACHED */
    474 				break;
    475 			case 'N':	/* don't actually execute kernel */
    476 				opt_N = 1;
    477 				break;
    478 			case 'v':
    479 				opt_v = 1;
    480 				boothowto |= AB_VERBOSE; /* XXX */
    481 				break;
    482 			case 'V':
    483 				xprintf("loadbsd %s\n", VERSION);
    484 				return 0;
    485 
    486 			/*
    487 			 * kernel boot flags
    488 			 */
    489 			case 'r':
    490 				if (rootdevname)
    491 					usage(1, "multiple -r flags");
    492 				else if (!*++arg)
    493 					usage(1, "-r requires device name");
    494 				else
    495 					rootdevname = *arg;
    496 				break;
    497 			case 'b':
    498 				boothowto |= RB_ASKNAME;
    499 				break;
    500 			case 'a':
    501 				boothowto &= ~RB_SINGLE;
    502 				break;
    503 			case 's':
    504 				boothowto |= RB_SINGLE;
    505 				break;
    506 			case 'D':
    507 				boothowto |= RB_KDB;
    508 				break;
    509 			case 'q':
    510 				boothowto |= AB_QUIET;
    511 				break;
    512 
    513 			default:
    514 				usage(1, (char *) 0);
    515 				/* NOTREACHED */
    516 				break;
    517 			}
    518 		flg = ++arg;
    519 	}
    520 
    521 	/* check MPU */
    522 	if (chkmpu() == 0)
    523 		xerrx(1, "Can't boot NetBSD on 68000/010");
    524 
    525 	argc -= arg - argv;
    526 	argv = arg;
    527 
    528 	if (argc != 1)
    529 		usage(1, (char *) 0);
    530 
    531 	kernel = *argv;
    532 
    533 	rootdev = bootdev(rootdevname ? rootdevname : DEFAULT_ROOTDEVNAME);
    534 
    535 	if (opt_v)
    536 		xwarnx("boothowto 0x%x", boothowto);
    537 
    538 	tramp = read_kernel(kernel);
    539 
    540 	tramp->xk.rootdev = rootdev;
    541 	tramp->xk.boothowto = boothowto;
    542 
    543 	/*
    544 	 * we never return, and make sure the disk cache
    545 	 * be flushed (if write-back cache is enabled)
    546 	 */
    547 	if (opt_v)
    548 		xwarnx("flush disk cache...");
    549 
    550 	i = DOS_FFLUSH_SET(1);		/* enable fflush */
    551 	DOS_FFLUSH();			/* then, issue fflush */
    552 	(void) DOS_FFLUSH_SET(i);	/* restore old mode just in case */
    553 
    554 	/*
    555 	 * the program assumes the MPU caches off
    556 	 */
    557 	if (opt_v)
    558 		xwarnx("flush and disable MPU caches...");
    559 
    560 	IOCS_CACHE_MD(-1);		/* flush */
    561 	if (!opt_N)
    562 		IOCS_CACHE_MD(0);	/* disable both caches */
    563 
    564 	if (opt_v)
    565 		xwarnx("Jumping to the kernel.  Good Luck!");
    566 
    567 	if (opt_N)
    568 		xerrx(0, "But don't actually do it.");
    569 
    570 	DOS_SUPER_JSR((void (*) __P((void))) tramp, &regs, &regs);
    571 
    572 	/* NOTREACHED */
    573 
    574 	xwarnx("??? return from kernel");
    575 
    576 	return 1;
    577 }
    578