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