1 /* $NetBSD: boot.c,v 1.29 2012/05/28 19:24:30 martin Exp $ */ 2 3 /* 4 * Copyright (c) 1997, 1999 Eduardo E. Horvath. All rights reserved. 5 * Copyright (c) 1997 Jason R. Thorpe. All rights reserved. 6 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 7 * Copyright (C) 1995, 1996 TooLs GmbH. 8 * All rights reserved. 9 * 10 * ELF support derived from NetBSD/alpha's boot loader, written 11 * by Christopher G. Demetriou. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgement: 23 * This product includes software developed by TooLs GmbH. 24 * 4. The name of TooLs GmbH may not be used to endorse or promote products 25 * derived from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 28 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 33 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 34 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * First try for the boot code 41 * 42 * Input syntax is: 43 * [promdev[{:|,}partition]]/[filename] [flags] 44 */ 45 46 #include <lib/libsa/stand.h> 47 #include <lib/libsa/loadfile.h> 48 #include <lib/libkern/libkern.h> 49 50 #include <sys/param.h> 51 #include <sys/reboot.h> 52 #include <sys/disklabel.h> 53 #include <sys/boot_flag.h> 54 55 #include <machine/cpu.h> 56 #include <machine/promlib.h> 57 #include <machine/bootinfo.h> 58 #include <sparc/stand/common/isfloppy.h> 59 60 #include "boot.h" 61 #include "ofdev.h" 62 #include "openfirm.h" 63 64 65 #define COMPAT_BOOT(marks) (marks[MARK_START] == marks[MARK_ENTRY]) 66 67 68 typedef void (*entry_t)(long o0, long bootargs, long bootsize, long o3, 69 long ofw); 70 71 /* 72 * Boot device is derived from ROM provided information, or if there is none, 73 * this list is used in sequence, to find a kernel. 74 */ 75 const char *kernelnames[] = { 76 "netbsd", 77 "netbsd.gz", 78 "netbsd.old", 79 "netbsd.old.gz", 80 "onetbsd", 81 "onetbsd.gz", 82 "vmunix ", 83 #ifdef notyet 84 "netbsd.pl ", 85 "netbsd.pl.gz ", 86 "netbsd.el ", 87 "netbsd.el.gz ", 88 #endif 89 NULL 90 }; 91 92 char bootdev[PROM_MAX_PATH]; 93 bool root_fs_quickseekable = true; /* unset for tftp boots */ 94 static bool bootinfo_pass_bootdev = false; 95 96 int debug = 0; 97 int compatmode = 0; 98 extern char twiddle_toggle; 99 100 #if 0 101 static void 102 prom2boot(char *dev) 103 { 104 char *cp, *lp = 0; 105 int handle; 106 char devtype[16]; 107 108 for (cp = dev; *cp; cp++) 109 if (*cp == ':') 110 lp = cp; 111 if (!lp) 112 lp = cp; 113 *lp = 0; 114 } 115 #endif 116 117 static int 118 bootoptions(const char *ap, char *loaddev, char *kernel, char *options) 119 { 120 int v = 0; 121 const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL; 122 const char *path; 123 char partition, *pp; 124 125 *kernel = '\0'; 126 *options = '\0'; 127 128 if (ap == NULL) { 129 return (0); 130 } 131 132 while (*ap == ' ') { 133 ap++; 134 } 135 136 if (*ap != '-') { 137 start1 = ap; 138 while (*ap != '\0' && *ap != ' ') { 139 ap++; 140 } 141 end1 = ap; 142 143 while (*ap != '\0' && *ap == ' ') { 144 ap++; 145 } 146 147 if (*ap != '-') { 148 start2 = ap; 149 while (*ap != '\0' && *ap != ' ') { 150 ap++; 151 } 152 end2 = ap; 153 while (*ap != '\0' && *ap == ' ') { 154 ap++; 155 } 156 } 157 } 158 if (end2 == start2) { 159 start2 = end2 = NULL; 160 } 161 if (end1 == start1) { 162 start1 = end1 = NULL; 163 } 164 165 if (start1 == NULL) { 166 /* only options */ 167 } else if (start2 == NULL) { 168 memcpy(kernel, start1, (end1 - start1)); 169 kernel[end1 - start1] = '\0'; 170 path = filename(kernel, &partition); 171 if (path == NULL) { 172 strcpy(loaddev, kernel); 173 kernel[0] = '\0'; 174 } else if (path != kernel) { 175 /* copy device part */ 176 memcpy(loaddev, kernel, path-kernel); 177 loaddev[path-kernel] = '\0'; 178 if (partition) { 179 pp = loaddev + strlen(loaddev); 180 pp[0] = ':'; 181 pp[1] = partition; 182 pp[2] = '\0'; 183 } 184 /* and kernel path */ 185 strcpy(kernel, path); 186 } 187 } else { 188 memcpy(loaddev, start1, (end1-start1)); 189 loaddev[end1-start1] = '\0'; 190 memcpy(kernel, start2, (end2 - start2)); 191 kernel[end2 - start2] = '\0'; 192 } 193 194 twiddle_toggle = 1; 195 strcpy(options, ap); 196 while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') { 197 BOOT_FLAG(*ap, v); 198 switch(*ap++) { 199 case 'D': 200 debug = 2; 201 break; 202 case 'C': 203 compatmode = 1; 204 break; 205 case 'T': 206 twiddle_toggle = 1 - twiddle_toggle; 207 break; 208 default: 209 break; 210 } 211 } 212 213 if (((v & RB_KDB) != 0) && (debug == 0)) { 214 debug = 1; 215 } 216 217 DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n", 218 loaddev, kernel, options)); 219 return (v); 220 } 221 222 /* 223 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle 224 * ksyms information unless it resides in a dedicated memory allocated from 225 * PROM and aligned on NBPG boundary. This is because the kernels calculate 226 * their ends on their own, they use address of 'end[]' reference which follows 227 * text segment. Ok, allocate some memory from PROM and copy symbol information 228 * over there. 229 */ 230 static void 231 ksyms_copyout(void **ssym, void **esym) 232 { 233 uint8_t *addr; 234 int kssize = (int)(long)((char *)*esym - (char *)*ssym + 1); 235 236 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n", 237 *ssym, *esym, kssize)); 238 239 if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) { 240 panic("ksyms_copyout(): no space for symbol table"); 241 } 242 243 memcpy(addr, *ssym, kssize); 244 *ssym = addr; 245 *esym = addr + kssize - 1; 246 247 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym)); 248 } 249 250 /* 251 * Prepare boot information and jump directly to the kernel. 252 */ 253 static void 254 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw, 255 int boothowto) 256 { 257 int l, machine_tag; 258 long newargs[4]; 259 void *ssym, *esym; 260 vaddr_t bootinfo; 261 struct btinfo_symtab bi_sym; 262 struct btinfo_kernend bi_kend; 263 struct btinfo_boothowto bi_howto; 264 char *cp; 265 char bootline[PROM_MAX_PATH * 2]; 266 267 /* Compose kernel boot line. */ 268 strncpy(bootline, kernel, sizeof(bootline)); 269 cp = bootline + strlen(bootline); 270 if (*args) { 271 *cp++ = ' '; 272 strncpy(bootline, args, sizeof(bootline) - (cp - bootline)); 273 } 274 *cp = 0; args = bootline; 275 276 /* Record symbol information in the bootinfo. */ 277 bootinfo = bi_init(marks[MARK_END]); 278 bi_sym.nsym = marks[MARK_NSYM]; 279 bi_sym.ssym = marks[MARK_SYM]; 280 bi_sym.esym = marks[MARK_END]; 281 bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym)); 282 bi_kend.addr= bootinfo + BOOTINFO_SIZE; 283 bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend)); 284 bi_howto.boothowto = boothowto; 285 bi_add(&bi_howto, BTINFO_BOOTHOWTO, sizeof(bi_howto)); 286 if (bootinfo_pass_bootdev) { 287 struct { 288 struct btinfo_common common; 289 char name[256]; 290 } info; 291 292 strcpy(info.name, bootdev); 293 bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev) 294 +sizeof(struct btinfo_bootdev)); 295 } 296 297 sparc64_finalize_tlb(marks[MARK_DATA]); 298 sparc64_bi_add(); 299 300 ssym = (void*)(long)marks[MARK_SYM]; 301 esym = (void*)(long)marks[MARK_END]; 302 303 DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym)); 304 305 /* Adjust ksyms pointers, if needed. */ 306 if (COMPAT_BOOT(marks) || compatmode) { 307 ksyms_copyout(&ssym, &esym); 308 } 309 310 freeall(); 311 /* 312 * When we come in args consists of a pointer to the boot 313 * string. We need to fix it so it takes into account 314 * other params such as romp. 315 */ 316 317 /* 318 * Stash pointer to end of symbol table after the argument 319 * strings. 320 */ 321 l = strlen(args) + 1; 322 memcpy(args + l, &esym, sizeof(esym)); 323 l += sizeof(esym); 324 325 /* 326 * Tell the kernel we're an OpenFirmware system. 327 */ 328 machine_tag = SPARC_MACHINE_OPENFIRMWARE; 329 memcpy(args + l, &machine_tag, sizeof(machine_tag)); 330 l += sizeof(machine_tag); 331 332 /* 333 * Since we don't need the boot string (we can get it from /chosen) 334 * we won't pass it in. Just pass in esym and magic # 335 */ 336 newargs[0] = SPARC_MACHINE_OPENFIRMWARE; 337 newargs[1] = (long)esym; 338 newargs[2] = (long)ssym; 339 newargs[3] = (long)(void*)bootinfo; 340 args = (char *)newargs; 341 l = sizeof(newargs); 342 343 /* if -D is set then pause in the PROM. */ 344 if (debug > 1) callrom(); 345 346 /* 347 * Jump directly to the kernel. Solaris kernel and Sun PROM 348 * flash updates expect ROMP vector in %o0, so we do. Format 349 * of other parameters and their order reflect OF_chain() 350 * symantics since this is what older NetBSD kernels rely on. 351 * (see sparc64/include/bootinfo.h for specification). 352 */ 353 DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw, 354 (long)args, (long)l, (long)ofw, (long)ofw, 355 (void*)marks[MARK_ENTRY])); 356 (*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw, 357 (long)ofw); 358 printf("Returned from kernel entry point!\n"); 359 } 360 361 static void 362 start_kernel(char *kernel, char *bootline, void *ofw, int isfloppy, 363 int boothowto) 364 { 365 int fd; 366 u_long marks[MARK_MAX]; 367 int flags = LOAD_ALL; 368 if (isfloppy) 369 flags &= ~LOAD_BACKWARDS; 370 371 /* 372 * First, load headers using default allocator and check whether kernel 373 * entry address matches kernel text load address. If yes, this is the 374 * old kernel designed for ofwboot v1.8 and therefore it must be mapped 375 * by PROM. Otherwise, map the kernel with 4MB permanent pages. 376 */ 377 loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR); 378 if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) { 379 if (COMPAT_BOOT(marks) || compatmode) { 380 (void)printf("[c] "); 381 loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR); 382 } else { 383 loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR); 384 } 385 (void)printf("Loading %s: ", kernel); 386 387 if (fdloadfile(fd, marks, flags) != -1) { 388 close(fd); 389 jump_to_kernel(marks, kernel, bootline, ofw, boothowto); 390 } 391 } 392 (void)printf("Failed to load '%s'.\n", kernel); 393 } 394 395 static void 396 help(void) 397 { 398 printf( "enter a special command\n" 399 " halt\n" 400 " exit\n" 401 " to return to OpenFirmware\n" 402 " ?\n" 403 " help\n" 404 " to display this message\n" 405 "or a boot specification:\n" 406 " [device] [kernel] [options]\n" 407 "\n" 408 "for example:\n" 409 " disk:a netbsd -s\n"); 410 } 411 412 static void 413 do_config_command(const char *cmd, const char *arg) 414 { 415 DPRINTF(("do_config_command: %s\n", cmd)); 416 if (strcmp(cmd, "bootpartition") == 0) { 417 char *c; 418 419 DPRINTF(("switching boot partition to %s from %s\n", 420 arg, bootdev)); 421 c = strrchr(bootdev, ':'); 422 if (!c) return; 423 if (c[1] == 0) return; 424 if (strlen(arg) > strlen(c)) return; 425 strcpy(c, arg); 426 DPRINTF(("new boot device: %s\n", bootdev)); 427 bootinfo_pass_bootdev = true; 428 } 429 } 430 431 static void 432 parse_boot_config(char *cfg, size_t len) 433 { 434 const char *cmd = NULL, *arg = NULL; 435 436 while (len) { 437 if (isspace(*cfg)) { 438 cfg++; len--; continue; 439 } 440 if (*cfg == ';' || *cfg == '#') { 441 while (len && *cfg != '\r' && *cfg != '\n') { 442 cfg++; len--; 443 } 444 continue; 445 } 446 cmd = cfg; 447 while (len && !isspace(*cfg)) { 448 cfg++; len--; 449 } 450 *cfg = 0; 451 if (len > 0) { 452 cfg++; len--; 453 while (isspace(*cfg) && len) { 454 cfg++; len--; 455 } 456 if (len > 0 ) { 457 arg = cfg; 458 while (len && !isspace(*cfg)) { 459 cfg++; len--; 460 } 461 *cfg = 0; 462 } 463 } 464 do_config_command(cmd, arg); 465 if (len > 0) { 466 cfg++; len--; 467 } 468 } 469 } 470 471 static void 472 check_boot_config(void) 473 { 474 int fd, off, len; 475 struct stat st; 476 char *bc; 477 478 if (!root_fs_quickseekable) return; 479 DPRINTF(("checking for /boot.cfg...\n")); 480 fd = open("/boot.cfg", 0); 481 if (fd < 0) return; 482 DPRINTF(("found /boot.cfg\n")); 483 if (fstat(fd, &st) == -1 || st.st_size > 32*1024) { 484 close(fd); 485 return; 486 } 487 bc = alloc(st.st_size+1); 488 off = 0; 489 do { 490 len = read(fd, bc+off, 1024); 491 if (len <= 0) 492 break; 493 off += len; 494 } while (len > 0); 495 bc[off] = 0; 496 close(fd); 497 498 parse_boot_config(bc, off); 499 } 500 501 void 502 main(void *ofw) 503 { 504 int boothowto, i = 0, isfloppy, kboothowto; 505 506 char kernel[PROM_MAX_PATH]; 507 char bootline[PROM_MAX_PATH]; 508 509 /* Initialize OpenFirmware */ 510 romp = ofw; 511 prom_init(); 512 513 printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev); 514 515 /* Figure boot arguments */ 516 strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1); 517 kboothowto = boothowto = 518 bootoptions(prom_getbootargs(), bootdev, kernel, bootline); 519 isfloppy = bootdev_isfloppy(bootdev); 520 521 for (;; *kernel = '\0') { 522 if (boothowto & RB_ASKNAME) { 523 char cmdline[PROM_MAX_PATH]; 524 525 printf("Boot: "); 526 gets(cmdline); 527 528 if (!strcmp(cmdline,"exit") || 529 !strcmp(cmdline,"halt")) { 530 prom_halt(); 531 } else if (!strcmp(cmdline, "?") || 532 !strcmp(cmdline, "help")) { 533 help(); 534 continue; 535 } 536 537 boothowto = bootoptions(cmdline, bootdev, kernel, 538 bootline); 539 boothowto |= RB_ASKNAME; 540 i = 0; 541 } 542 543 if (*kernel == '\0') { 544 if (kernelnames[i] == NULL) { 545 boothowto |= RB_ASKNAME; 546 continue; 547 } 548 strncpy(kernel, kernelnames[i++], PROM_MAX_PATH); 549 } else if (i == 0) { 550 /* 551 * Kernel name was passed via command line -- ask user 552 * again if requested image fails to boot. 553 */ 554 boothowto |= RB_ASKNAME; 555 } 556 557 check_boot_config(); 558 start_kernel(kernel, bootline, ofw, isfloppy, kboothowto); 559 560 /* 561 * Try next name from kernel name list if not in askname mode, 562 * enter askname on reaching list's end. 563 */ 564 if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) { 565 printf(": trying %s...\n", kernelnames[i]); 566 } else { 567 printf("\n"); 568 boothowto |= RB_ASKNAME; 569 } 570 } 571 572 (void)printf("Boot failed! Exiting to the Firmware.\n"); 573 prom_halt(); 574 } 575