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