1 /* 2 * Load and boot NetBSD kernel on Human68k 3 * 4 * written by ITOH Yasufumi 5 * public domain 6 * 7 * loadbsd [-hvV] [-abDNqs] [-r root_device] netbsd 8 * 9 * loadbsd options: 10 * -h help 11 * -N do not actually execute kernel 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 * -q quiet boot 21 * -v verbose boot (also turn on verbosity of loadbsd) 22 * 23 * $NetBSD: loadbsd.c,v 1.16 2024/01/07 07:58:34 isaki Exp $ 24 */ 25 26 #include <sys/cdefs.h> 27 28 __RCSID("$NetBSD: loadbsd.c,v 1.16 2024/01/07 07:58:34 isaki Exp $"); 29 #define VERSION "$Revision: 1.16 $ $Date: 2024/01/07 07:58:34 $" 30 31 #include <sys/types.h> /* ntohl */ 32 #include <sys/reboot.h> 33 #include <sys/param.h> /* ALIGN, ALIGNBYTES */ 34 #include <a.out.h> 35 #include <sys/exec_elf.h> 36 #include <string.h> 37 #include <machine/bootinfo.h> 38 39 #include <dos.h> 40 #include <iocs.h> 41 #include "../common/xprintf.h" 42 #include "trampoline.h" 43 44 #define DEFAULT_ROOTDEVNAME "sd@0,0:a" 45 46 #define ISDIGIT(c) ((c) >= '0' && (c) <= '9') 47 48 #define GETDECIMAL(var, str) \ 49 do { var *= 10; var += *str++ - '0'; } while (ISDIGIT(*str)) 50 51 static const char *lookupif(const char *name, 52 unsigned *pif, unsigned *punit); 53 static void get_current_scsi_interface(unsigned *pif, unsigned *punit); 54 static int bootdev(const char *devstr); 55 static struct tramparg *read_kernel(const char *fn); 56 static int chkmpu(void); 57 static __dead void usage(int status, const char *msg) 58 __attribute__((noreturn)); 59 60 int main(int argc, char *argv[]); 61 62 int opt_v; 63 int opt_N; 64 const char *kernel_fn; 65 66 const struct hatbl { 67 char name[4]; 68 unsigned short id; 69 } hatable[] = { 70 X68K_BOOT_SCSIIF_LIST 71 }; 72 73 /* 74 * parse interface name 75 * return the next position 76 */ 77 static const char * 78 lookupif(const char *name, unsigned *pif, unsigned *punit) 79 { 80 unsigned u, unit; 81 const char *p; 82 83 for (u = 0; u < sizeof hatable / sizeof hatable[0]; u++) { 84 const char *n; 85 86 for (n = hatable[u].name, p = name; *n && *n == *p; n++, p++) 87 ; 88 if (!*n) 89 goto found; 90 } 91 /* not found */ 92 return (char *) 0; 93 94 found: 95 if (*p == '@') 96 p++; 97 98 /* get unit # */ 99 if (!ISDIGIT(*p)) 100 return (char *) 0; 101 102 unit = 0; 103 GETDECIMAL(unit, p); 104 105 *pif = hatable[u].id; 106 *punit = unit; 107 108 return p; 109 } 110 111 /* 112 * if the SCSI interface is not specified, use the current one 113 */ 114 static void 115 get_current_scsi_interface(unsigned *pif, unsigned *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(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 #define LOADBSD 281 #include "../common/exec_sub.c" 282 283 /* 284 * read kernel and create trampoline 285 * 286 * |----------------------| <- allocated buf addr 287 * | kernel image | 288 * ~ (exec file contents) ~ 289 * | | 290 * |----------------------| <- return value (entry addr of trampoline) 291 * | struct tramparg | 292 * | (trampoline args) | 293 * |----------------------| 294 * | trampoline code | 295 * | (in assembly) | 296 * |----------------------| 297 */ 298 static struct tramparg * 299 read_kernel(const char *fn) 300 { 301 int fd; 302 union dos_fcb *fcb; 303 size_t filesize, nread; 304 void *buf; 305 struct tramparg *arg; 306 size_t size_tramp = end_trampoline - trampoline; 307 308 kernel_fn = fn; 309 310 if ((fd = DOS_OPEN(fn, 0x00)) < 0) /* read only */ 311 xerr(1, "%s: open", fn); 312 313 if ((int)(fcb = DOS_GET_FCB_ADR(fd)) < 0) 314 xerr(1, "%s: get_fcb_adr", fn); 315 316 /* 317 * XXX FCB is in supervisor area 318 */ 319 /*if (fcb->blk.mode != 0)*/ 320 if (IOCS_B_BPEEK((char *)fcb + 1) & 0x80) 321 xerrx(1, "%s: Not a regular file", fn); 322 323 /*filesize = fcb->blk.size;*/ 324 filesize = IOCS_B_LPEEK(&fcb->blk.size); 325 326 if (filesize < sizeof(Elf32_Ehdr)) 327 xerrx(1, "%s: Unknown format", fn); 328 329 /* 330 * read entire file 331 */ 332 if ((int)(buf = DOS_MALLOC(filesize + ALIGNBYTES 333 + sizeof(struct tramparg) 334 + size_tramp + SIZE_TMPSTACK)) < 0) 335 xerr(1, "read_kernel"); 336 337 if ((nread = DOS_READ(fd, buf, filesize)) != filesize) { 338 if ((int)nread < 0) 339 xerr(1, "%s: read", fn); 340 else 341 xerrx(1, "%s: short read", fn); 342 } 343 344 if (DOS_CLOSE(fd) < 0) 345 xerr(1, "%s: close", fn); 346 347 /* 348 * address for argument for trampoline code 349 */ 350 arg = (struct tramparg *) ALIGN((char *) buf + nread); 351 352 if (opt_v) 353 xwarnx("trampoline arg at %p", arg); 354 355 xk_load(&arg->xk, buf, 0 /* XXX load addr should not be fixed */); 356 357 /* 358 * create argument for trampoline code 359 */ 360 arg->bsr_inst = TRAMP_BSR + sizeof(struct tramparg) - 2; 361 arg->tmp_stack = (char *) arg + sizeof(struct tramparg) 362 + size_tramp + SIZE_TMPSTACK; 363 arg->mpu_type = IOCS_MPU_STAT() & 0xff; 364 365 arg->xk.d5 = IOCS_BOOTINF(); /* unused for now */ 366 #if 0 367 /* filled afterwards */ 368 arg->xk.rootdev = 369 arg->xk.boothowto = 370 #endif 371 372 if (opt_v) 373 xwarnx("args: mpu %d, image %p, load 0x%x, entry 0x%x", 374 arg->mpu_type, arg->xk.sec[0].sec_image, 375 arg->xk.load_addr, arg->xk.entry_addr); 376 377 /* 378 * copy trampoline code 379 */ 380 if (opt_v) 381 xwarnx("trampoline code at %p (%u bytes)", 382 (char *) arg + sizeof(struct tramparg), size_tramp); 383 384 memcpy((char *) arg + sizeof(struct tramparg), trampoline, size_tramp); 385 386 return arg; 387 } 388 389 /* 390 * MC68000/010 -> return zero 391 * MC68020 and later -> return nonzero 392 */ 393 static int 394 chkmpu(void) 395 { 396 register int ret __asm("%d0"); 397 398 __asm("| %0 <- this must be %%d0\n\ 399 moveq #1,%%d0\n\ 400 .long 0x103B02FF | foo: moveb %%pc@((foo+1)-foo-2:B,d0:W:2),%%d0\n\ 401 | ^ ^\n\ 402 | %%d0.b = 0x02 (68000/010)\n\ 403 | = 0xff (68020 and later)\n\ 404 bmis 1f\n\ 405 moveq #0,%%d0 | 68000/010\n\ 406 1:" : "=d" (ret)); 407 408 return ret; 409 } 410 411 static __dead void 412 usage(int status, const char *msg) 413 { 414 extern const char *const __progname; 415 416 if (msg) 417 xwarnx("%s", msg); 418 419 xerrprintf("\ 420 %s [-hvV] [-abDNqs] [-r root_device] netbsd\n\ 421 \n\ 422 loadbsd options:\n\ 423 \t-h help\n\ 424 \t-N do not execute kernel\n\ 425 \t-v verbose\n\ 426 \t-V print version and exit\n\ 427 \n\ 428 kernel options:\n\ 429 \t-a auto boot, opposite of -s\n\ 430 \t-s single user boot (default)\n\ 431 \t-D enter kernel debugger\n\ 432 \t-q quiet boot\n\ 433 \t-b ask root device\n\ 434 \t-r specify root device (default %s)\n\ 435 \t format: [/interface/]device@unit[,lun][:partition]\n\ 436 \t interface: one of spc@0, spc@1, mha@0\n\ 437 \t (current boot interface if omitted)\n\ 438 \t device: one of fd, sd, cd, md, ne\n\ 439 \t unit: device unit number (SCSI ID for SCSI device)\n\ 440 \t lun: SCSI LUN # (0 if omitted)\n\ 441 \t partition: partition letter ('a' if omitted)\n\ 442 ", __progname, DEFAULT_ROOTDEVNAME); 443 444 DOS_EXIT2(status); 445 } 446 447 int 448 main(int argc, char *argv[]) 449 { 450 char *rootdevname = 0; 451 int rootdev; 452 u_long boothowto = RB_SINGLE; 453 const char *kernel; 454 char *p, **flg, **arg; 455 struct tramparg *tramp; 456 struct dos_dregs regs; /* unused... */ 457 int i; 458 459 /* parse options */ 460 for (arg = flg = argv + 1; (p = *flg) && *p == '-'; ) { 461 int c; 462 463 while ((c = *++p)) 464 switch (c) { 465 case 'h': 466 usage(0, (char *) 0); 467 /* NOTREACHED */ 468 break; 469 case 'N': /* don't actually execute kernel */ 470 opt_N = 1; 471 break; 472 case 'v': 473 opt_v = 1; 474 boothowto |= AB_VERBOSE; /* XXX */ 475 break; 476 case 'V': 477 xprintf("loadbsd %s\n", VERSION); 478 return 0; 479 480 /* 481 * kernel boot flags 482 */ 483 case 'r': 484 if (rootdevname) 485 usage(1, "multiple -r flags"); 486 else if (!*++arg) 487 usage(1, "-r requires device name"); 488 else 489 rootdevname = *arg; 490 break; 491 case 'b': 492 boothowto |= RB_ASKNAME; 493 break; 494 case 'a': 495 boothowto &= ~RB_SINGLE; 496 break; 497 case 's': 498 boothowto |= RB_SINGLE; 499 break; 500 case 'D': 501 boothowto |= RB_KDB; 502 break; 503 case 'q': 504 boothowto |= AB_QUIET; 505 break; 506 507 default: 508 usage(1, (char *) 0); 509 /* NOTREACHED */ 510 break; 511 } 512 flg = ++arg; 513 } 514 515 /* check MPU */ 516 if (chkmpu() == 0) 517 xerrx(1, "Can't boot NetBSD on 68000/010"); 518 519 argc -= arg - argv; 520 argv = arg; 521 522 if (argc != 1) 523 usage(1, (char *) 0); 524 525 kernel = *argv; 526 527 rootdev = bootdev(rootdevname ? rootdevname : DEFAULT_ROOTDEVNAME); 528 529 if (opt_v) 530 xwarnx("boothowto 0x%x", boothowto); 531 532 tramp = read_kernel(kernel); 533 534 tramp->xk.rootdev = rootdev; 535 tramp->xk.boothowto = boothowto; 536 537 /* 538 * we never return, and make sure the disk cache 539 * be flushed (if write-back cache is enabled) 540 */ 541 if (opt_v) 542 xwarnx("flush disk cache..."); 543 544 i = DOS_FFLUSH_SET(1); /* enable fflush */ 545 DOS_FFLUSH(); /* then, issue fflush */ 546 (void) DOS_FFLUSH_SET(i); /* restore old mode just in case */ 547 548 /* 549 * the program assumes the MPU caches off 550 */ 551 if (opt_v) 552 xwarnx("flush and disable MPU caches..."); 553 554 IOCS_CACHE_MD(-1); /* flush */ 555 if (!opt_N) 556 IOCS_CACHE_MD(0); /* disable both caches */ 557 558 if (opt_v) 559 xwarnx("Jumping to the kernel. Good Luck!"); 560 561 if (opt_N) 562 xerrx(0, "But don't actually do it."); 563 564 DOS_SUPER_JSR((void (*)(void)) tramp, ®s, ®s); 565 566 /* NOTREACHED */ 567 568 xwarnx("??? return from kernel"); 569 570 return 1; 571 } 572