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.8 2002/05/18 13:54:39 isaki Exp $ 23 */ 24 25 #include <sys/cdefs.h> 26 27 __RCSID("$NetBSD: loadbsd.c,v 1.8 2002/05/18 13:54:39 isaki Exp $"); 28 #define VERSION "$Revision: 1.8 $ $Date: 2002/05/18 13:54:39 $" 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, ®s, ®s); 571 572 /* NOTREACHED */ 573 574 xwarnx("??? return from kernel"); 575 576 return 1; 577 } 578