1/* 2 * Load and boot NetBSD kernel on Human68k 3 * 4 * written by 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.13 2009/03/18 10:22:38 cegger Exp $ 23 */ 24 25#include <sys/cdefs.h> 26 27__RCSID("$NetBSD: loadbsd.c,v 1.13 2009/03/18 10:22:38 cegger Exp $"); 28#define VERSION "$Revision: 1.14 $ $Date: 2011/02/21 02:31:59 $" 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 50static const char *lookupif(const char *name, 51 unsigned *pif, unsigned *punit); 52static void get_current_scsi_interface(unsigned *pif, unsigned *punit); 53static int bootdev(const char *devstr); 54static struct tramparg *read_kernel(const char *fn); 55static int chkmpu(void); 56static __dead void usage(int status, const char *msg) 57 __attribute__((noreturn)); 58 59int main(int argc, char *argv[]); 60 61int opt_v; 62int opt_N; 63const char *kernel_fn; 64 65const 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 */ 76static const char * 77lookupif(const char *name, unsigned *pif, unsigned *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 93found: 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 */ 113static void 114get_current_scsi_interface(unsigned *pif, unsigned *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 157const struct devtbl { 158 char name[3]; 159 u_char major; 160} devtable[] = { 161 X68K_BOOT_DEV_LIST, 162 X68K_BOOT_NETIF_LIST 163}; 164 165static int 166bootdev(const char *devstr) 167{ 168 unsigned u; 169 unsigned major, unit, lun, partition; 170 int dev; 171 const char *s = devstr; 172 unsigned interface = 0, unit_if = 0; 173 174 if (*s == '/') { 175 /* 176 * /<interface>/<device>" 177 * "/spc@1/sd@2,3:e" 178 */ 179 while (*++s == '/') /* skip slashes */ 180 ; 181 if (!strchr(s, '/')) 182 xerrx(1, "%s: bad format", devstr); 183 184 if (!(s = lookupif(s, &interface, &unit_if))) 185 xerrx(1, "%s: unknown interface", devstr); 186 187 while (*s == '/') /* skip slashes */ 188 s++; 189 } else { 190 /* make lint happy */ 191 interface = 0; 192 unit_if = 0; 193 } 194 195 /* allow r at the top */ 196 if (*s == 'r') 197 s++; 198 199 for (u = 0; u < sizeof devtable / sizeof devtable[0]; u++) 200 if (s[0] == devtable[u].name[0] && s[1] == devtable[u].name[1]) 201 goto found; 202 203 /* not found */ 204 xerrx(1, "%s: unknown device", devstr); 205 206found: major = devtable[u].major; 207 208 /* 209 * <type>@unit[,lun][:part] 210 * "sd@1,3:a" 211 */ 212 213 /* get device unit # */ 214 s += 2; 215 if (*s == '@') 216 s++; 217 if (!*s) 218 xerrx(1, "%s: missing unit number", devstr); 219 if (!ISDIGIT(*s)) 220 xerrx(1, "%s: wrong device", devstr); 221 222 unit = 0; 223 GETDECIMAL(unit, s); 224 225 lun = 0; 226 if (*s == ',') { 227 s++; 228 if (!ISDIGIT(*s)) 229 xerrx(1, "%s: wrong device", devstr); 230 GETDECIMAL(lun, s); 231 } 232 233 /* get device partition */ 234 if (*s == ':') 235 s++; 236 if (!*s) 237 partition = 0; /* no partition letter -- assuming 'a' */ 238 else if (!s[1]) 239 partition = *s - 'a'; 240 else 241 xerrx(1, "%s: wrong partition letter", devstr); 242 243 /* 244 * sanity check 245 */ 246 if (unit_if >= 16) 247 xerrx(1, "%s: interface unit # too large", devstr); 248 if (unit >= 16) 249 xerrx(1, "%s: device unit # too large", devstr); 250 if (lun >= 8) 251 xerrx(1, "%s: SCSI LUN >= 8 is not supported yet", devstr); 252 if (partition >= 16) 253 xerrx(1, "%s: unsupported partition", devstr); 254 255 /* 256 * encode device to be passed to kernel 257 */ 258 if (X68K_BOOT_DEV_IS_SCSI(major)) { 259 /* 260 * encode SCSI device 261 */ 262 if (interface == 0) 263 get_current_scsi_interface(&interface, &unit_if); 264 265 dev = X68K_MAKESCSIBOOTDEV(major, interface, unit_if, 266 unit, lun, partition); 267 } else { 268 /* encode non-SCSI device */ 269 dev = X68K_MAKEBOOTDEV(major, unit, partition); 270 } 271 272 if (opt_v) 273 xwarnx("%s: major %u, if %u, un_if %u, unit %u, lun %u, partition %u; bootdev 0x%x", 274 devstr, major, interface, unit_if, unit, lun, partition, dev); 275 276 return dev; 277} 278 279#define LOADBSD 280#include "../common/exec_sub.c" 281 282/* 283 * read kernel and create trampoline 284 * 285 * |----------------------| <- allocated buf addr 286 * | kernel image | 287 * ~ (exec file contents) ~ 288 * | | 289 * |----------------------| <- return value (entry addr of trampoline) 290 * | struct tramparg | 291 * | (trampoline args) | 292 * |----------------------| 293 * | trampoline code | 294 * | (in assembly) | 295 * |----------------------| 296 */ 297static struct tramparg * 298read_kernel(const char *fn) 299{ 300 int fd; 301 union dos_fcb *fcb; 302 size_t filesize, nread; 303 void *buf; 304 struct tramparg *arg; 305 size_t size_tramp = end_trampoline - trampoline; 306 307 kernel_fn = fn; 308 309 if ((fd = DOS_OPEN(fn, 0x00)) < 0) /* read only */ 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 if (filesize < sizeof(Elf32_Ehdr)) 326 xerrx(1, "%s: Unknown format", fn); 327 328 /* 329 * read entire file 330 */ 331 if ((int)(buf = DOS_MALLOC(filesize + ALIGNBYTES 332 + sizeof(struct tramparg) 333 + size_tramp + SIZE_TMPSTACK)) < 0) 334 xerr(1, "read_kernel"); 335 336 if ((nread = DOS_READ(fd, buf, filesize)) != filesize) { 337 if ((int)nread < 0) 338 xerr(1, "%s: read", fn); 339 else 340 xerrx(1, "%s: short read", fn); 341 } 342 343 if (DOS_CLOSE(fd) < 0) 344 xerr(1, "%s: close", fn); 345 346 /* 347 * address for argument for trampoline code 348 */ 349 arg = (struct tramparg *) ALIGN((char *) buf + nread); 350 351 if (opt_v) 352 xwarnx("trampoline arg at %p", arg); 353 354 xk_load(&arg->xk, buf, 0 /* XXX load addr should not be fixed */); 355 356 /* 357 * create argument for trampoline code 358 */ 359 arg->bsr_inst = TRAMP_BSR + sizeof(struct tramparg) - 2; 360 arg->tmp_stack = (char *) arg + sizeof(struct tramparg) 361 + size_tramp + SIZE_TMPSTACK; 362 arg->mpu_type = IOCS_MPU_STAT() & 0xff; 363 364 arg->xk.d5 = IOCS_BOOTINF(); /* unused for now */ 365#if 0 366 /* filled afterwards */ 367 arg->xk.rootdev = 368 arg->xk.boothowto = 369#endif 370 371 if (opt_v) 372 xwarnx("args: mpu %d, image %p, load 0x%x, entry 0x%x", 373 arg->mpu_type, arg->xk.sec[0].sec_image, 374 arg->xk.load_addr, arg->xk.entry_addr); 375 376 /* 377 * copy trampoline code 378 */ 379 if (opt_v) 380 xwarnx("trampoline code at %p (%u bytes)", 381 (char *) arg + sizeof(struct tramparg), size_tramp); 382 383 memcpy((char *) arg + sizeof(struct tramparg), trampoline, size_tramp); 384 385 return arg; 386} 387 388/* 389 * MC68000/010 -> return zero 390 * MC68020 and later -> return nonzero 391 */ 392static int 393chkmpu(void) 394{ 395 register int ret __asm("%d0"); 396 397 __asm("| %0 <- this must be %%d0\n\ 398 moveq #1,%%d0\n\ 399 .long 0x103B02FF | foo: moveb %%pc@((foo+1)-foo-2:B,d0:W:2),%%d0\n\ 400 | ^ ^\n\ 401 | %%d0.b = 0x02 (68000/010)\n\ 402 | = 0xff (68020 and later)\n\ 403 bmis 1f\n\ 404 moveq #0,%%d0 | 68000/010\n\ 4051:" : "=d" (ret)); 406 407 return ret; 408} 409 410static __dead void 411usage(int status, const char *msg) 412{ 413 extern const char *const __progname; 414 415 if (msg) 416 xwarnx("%s", msg); 417 418 xerrprintf("\ 419%s [-hvV] [-abDs] [-r root_device] netbsd\n\ 420\n\ 421loadbsd options:\n\ 422\t-h help\n\ 423\t-v verbose\n\ 424\t-V print version and exit\n\ 425\n\ 426kernel options:\n\ 427\t-a auto boot, opposite of -s\n\ 428\t-s single user boot (default)\n\ 429\t-D enter kernel debugger\n\ 430\t-b ask root device\n\ 431\t-r specify root device (default %s)\n\ 432\t format: [/interface/]device@unit[,lun][:partition]\n\ 433\t interface: one of spc@0, spc@1, mha@0\n\ 434\t (current boot interface if omitted)\n\ 435\t device: one of fd, sd, cd, md, ne\n\ 436\t unit: device unit number (SCSI ID for SCSI device)\n\ 437\t lun: SCSI LUN # (0 if omitted)\n\ 438\t partition: partition letter ('a' if omitted)\n\ 439", __progname, DEFAULT_ROOTDEVNAME); 440 441 DOS_EXIT2(status); 442} 443 444int 445main(int argc, char *argv[]) 446{ 447 char *rootdevname = 0; 448 int rootdev; 449 u_long boothowto = RB_SINGLE; 450 const char *kernel; 451 char *p, **flg, **arg; 452 struct tramparg *tramp; 453 struct dos_dregs regs; /* unused... */ 454 int i; 455 456 /* parse options */ 457 for (arg = flg = argv + 1; (p = *flg) && *p == '-'; ) { 458 int c; 459 460 while ((c = *++p)) 461 switch (c) { 462 case 'h': 463 usage(0, (char *) 0); 464 /* NOTREACHED */ 465 break; 466 case 'N': /* don't actually execute kernel */ 467 opt_N = 1; 468 break; 469 case 'v': 470 opt_v = 1; 471 boothowto |= AB_VERBOSE; /* XXX */ 472 break; 473 case 'V': 474 xprintf("loadbsd %s\n", VERSION); 475 return 0; 476 477 /* 478 * kernel boot flags 479 */ 480 case 'r': 481 if (rootdevname) 482 usage(1, "multiple -r flags"); 483 else if (!*++arg) 484 usage(1, "-r requires device name"); 485 else 486 rootdevname = *arg; 487 break; 488 case 'b': 489 boothowto |= RB_ASKNAME; 490 break; 491 case 'a': 492 boothowto &= ~RB_SINGLE; 493 break; 494 case 's': 495 boothowto |= RB_SINGLE; 496 break; 497 case 'D': 498 boothowto |= RB_KDB; 499 break; 500 case 'q': 501 boothowto |= AB_QUIET; 502 break; 503 504 default: 505 usage(1, (char *) 0); 506 /* NOTREACHED */ 507 break; 508 } 509 flg = ++arg; 510 } 511 512 /* check MPU */ 513 if (chkmpu() == 0) 514 xerrx(1, "Can't boot NetBSD on 68000/010"); 515 516 argc -= arg - argv; 517 argv = arg; 518 519 if (argc != 1) 520 usage(1, (char *) 0); 521 522 kernel = *argv; 523 524 rootdev = bootdev(rootdevname ? rootdevname : DEFAULT_ROOTDEVNAME); 525 526 if (opt_v) 527 xwarnx("boothowto 0x%x", boothowto); 528 529 tramp = read_kernel(kernel); 530 531 tramp->xk.rootdev = rootdev; 532 tramp->xk.boothowto = boothowto; 533 534 /* 535 * we never return, and make sure the disk cache 536 * be flushed (if write-back cache is enabled) 537 */ 538 if (opt_v) 539 xwarnx("flush disk cache..."); 540 541 i = DOS_FFLUSH_SET(1); /* enable fflush */ 542 DOS_FFLUSH(); /* then, issue fflush */ 543 (void) DOS_FFLUSH_SET(i); /* restore old mode just in case */ 544 545 /* 546 * the program assumes the MPU caches off 547 */ 548 if (opt_v) 549 xwarnx("flush and disable MPU caches..."); 550 551 IOCS_CACHE_MD(-1); /* flush */ 552 if (!opt_N) 553 IOCS_CACHE_MD(0); /* disable both caches */ 554 555 if (opt_v) 556 xwarnx("Jumping to the kernel. Good Luck!"); 557 558 if (opt_N) 559 xerrx(0, "But don't actually do it."); 560 561 DOS_SUPER_JSR((void (*)(void)) tramp, ®s, ®s); 562 563 /* NOTREACHED */ 564 565 xwarnx("??? return from kernel"); 566 567 return 1; 568} 569