1 /* $NetBSD: boot.c,v 1.22 2010/04/02 18:39:44 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 59 #include "boot.h" 60 #include "ofdev.h" 61 #include "openfirm.h" 62 63 64 #define COMPAT_BOOT(marks) (marks[MARK_START] == marks[MARK_ENTRY]) 65 66 67 typedef void (*entry_t)(long o0, long bootargs, long bootsize, long o3, 68 long ofw); 69 70 /* 71 * Boot device is derived from ROM provided information, or if there is none, 72 * this list is used in sequence, to find a kernel. 73 */ 74 const char *kernelnames[] = { 75 "netbsd", 76 "netbsd.gz", 77 "netbsd.old", 78 "netbsd.old.gz", 79 "onetbsd", 80 "onetbsd.gz", 81 "vmunix ", 82 #ifdef notyet 83 "netbsd.pl ", 84 "netbsd.pl.gz ", 85 "netbsd.el ", 86 "netbsd.el.gz ", 87 #endif 88 NULL 89 }; 90 91 char bootdev[PROM_MAX_PATH]; 92 bool root_fs_quickseekable = true; /* unset for tftp boots */ 93 static bool bootinfo_pass_bootdev = false; 94 95 int debug = 0; 96 int compatmode = 0; 97 98 #if 0 99 static void 100 prom2boot(char *dev) 101 { 102 char *cp, *lp = 0; 103 int handle; 104 char devtype[16]; 105 106 for (cp = dev; *cp; cp++) 107 if (*cp == ':') 108 lp = cp; 109 if (!lp) 110 lp = cp; 111 *lp = 0; 112 } 113 #endif 114 115 static int 116 bootoptions(const char *ap, char *loaddev, char *kernel, char *options) 117 { 118 int v = 0; 119 const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL; 120 const char *path; 121 char partition, *pp; 122 123 *kernel = '\0'; 124 *options = '\0'; 125 126 if (ap == NULL) { 127 return (0); 128 } 129 130 while (*ap == ' ') { 131 ap++; 132 } 133 134 if (*ap != '-') { 135 start1 = ap; 136 while (*ap != '\0' && *ap != ' ') { 137 ap++; 138 } 139 end1 = ap; 140 141 while (*ap != '\0' && *ap == ' ') { 142 ap++; 143 } 144 145 if (*ap != '-') { 146 start2 = ap; 147 while (*ap != '\0' && *ap != ' ') { 148 ap++; 149 } 150 end2 = ap; 151 while (*ap != '\0' && *ap == ' ') { 152 ap++; 153 } 154 } 155 } 156 if (end2 == start2) { 157 start2 = end2 = NULL; 158 } 159 if (end1 == start1) { 160 start1 = end1 = NULL; 161 } 162 163 if (start1 == NULL) { 164 /* only options */ 165 } else if (start2 == NULL) { 166 memcpy(kernel, start1, (end1 - start1)); 167 kernel[end1 - start1] = '\0'; 168 path = filename(kernel, &partition); 169 if (path == NULL) { 170 strcpy(loaddev, kernel); 171 kernel[0] = '\0'; 172 } else if (path != kernel) { 173 /* copy device part */ 174 memcpy(loaddev, kernel, path-kernel); 175 loaddev[path-kernel] = '\0'; 176 if (partition) { 177 pp = loaddev + strlen(loaddev); 178 pp[0] = ':'; 179 pp[1] = partition; 180 pp[2] = '\0'; 181 } 182 /* and kernel path */ 183 strcpy(kernel, path); 184 } 185 } else { 186 memcpy(loaddev, start1, (end1-start1)); 187 loaddev[end1-start1] = '\0'; 188 memcpy(kernel, start2, (end2 - start2)); 189 kernel[end2 - start2] = '\0'; 190 } 191 192 strcpy(options, ap); 193 while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') { 194 BOOT_FLAG(*ap, v); 195 switch(*ap++) { 196 case 'D': 197 debug = 2; 198 break; 199 case 'C': 200 compatmode = 1; 201 break; 202 default: 203 break; 204 } 205 } 206 207 if (((v & RB_KDB) != 0) && (debug == 0)) { 208 debug = 1; 209 } 210 211 DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n", 212 loaddev, kernel, options)); 213 return (v); 214 } 215 216 /* 217 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle 218 * ksyms information unless it resides in a dedicated memory allocated from 219 * PROM and aligned on NBPG boundary. This is because the kernels calculate 220 * their ends on their own, they use address of 'end[]' reference which follows 221 * text segment. Ok, allocate some memory from PROM and copy symbol information 222 * over there. 223 */ 224 static void 225 ksyms_copyout(void **ssym, void **esym) 226 { 227 void *addr; 228 int kssize = (int)(long)(*esym - *ssym + 1); 229 230 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n", 231 *ssym, *esym, kssize)); 232 233 if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) { 234 panic("ksyms_copyout(): no space for symbol table"); 235 } 236 237 memcpy(addr, *ssym, kssize); 238 *ssym = addr; 239 *esym = addr + kssize - 1; 240 241 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym)); 242 } 243 244 /* 245 * Prepare boot information and jump directly to the kernel. 246 */ 247 static void 248 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw) 249 { 250 extern char end[]; 251 int l, machine_tag; 252 long newargs[4]; 253 void *ssym, *esym; 254 vaddr_t bootinfo; 255 struct btinfo_symtab bi_sym; 256 struct btinfo_kernend bi_kend; 257 char *cp; 258 char bootline[PROM_MAX_PATH * 2]; 259 260 /* Compose kernel boot line. */ 261 strncpy(bootline, kernel, sizeof(bootline)); 262 cp = bootline + strlen(bootline); 263 if (*args) { 264 *cp++ = ' '; 265 strncpy(bootline, args, sizeof(bootline) - (cp - bootline)); 266 } 267 *cp = 0; args = bootline; 268 269 /* Record symbol information in the bootinfo. */ 270 bootinfo = bi_init(marks[MARK_END]); 271 bi_sym.nsym = marks[MARK_NSYM]; 272 bi_sym.ssym = marks[MARK_SYM]; 273 bi_sym.esym = marks[MARK_END]; 274 bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym)); 275 bi_kend.addr= bootinfo + BOOTINFO_SIZE; 276 bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend)); 277 if (bootinfo_pass_bootdev) { 278 struct { 279 struct btinfo_common common; 280 char name[256]; 281 } info; 282 283 strcpy(info.name, bootdev); 284 bi_add(&info, BTINFO_BOOTDEV, strlen(bootdev) 285 +sizeof(struct btinfo_bootdev)); 286 } 287 288 sparc64_finalize_tlb(marks[MARK_DATA]); 289 sparc64_bi_add(); 290 291 ssym = (void*)(long)marks[MARK_SYM]; 292 esym = (void*)(long)marks[MARK_END]; 293 294 DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym)); 295 296 /* Adjust ksyms pointers, if needed. */ 297 if (COMPAT_BOOT(marks) || compatmode) { 298 ksyms_copyout(&ssym, &esym); 299 } 300 301 freeall(); 302 /* 303 * When we come in args consists of a pointer to the boot 304 * string. We need to fix it so it takes into account 305 * other params such as romp. 306 */ 307 308 /* 309 * Stash pointer to end of symbol table after the argument 310 * strings. 311 */ 312 l = strlen(args) + 1; 313 memcpy(args + l, &esym, sizeof(esym)); 314 l += sizeof(esym); 315 316 /* 317 * Tell the kernel we're an OpenFirmware system. 318 */ 319 machine_tag = SPARC_MACHINE_OPENFIRMWARE; 320 memcpy(args + l, &machine_tag, sizeof(machine_tag)); 321 l += sizeof(machine_tag); 322 323 /* 324 * Since we don't need the boot string (we can get it from /chosen) 325 * we won't pass it in. Just pass in esym and magic # 326 */ 327 newargs[0] = SPARC_MACHINE_OPENFIRMWARE; 328 newargs[1] = (long)esym; 329 newargs[2] = (long)ssym; 330 newargs[3] = (long)(void*)bootinfo; 331 args = (char *)newargs; 332 l = sizeof(newargs); 333 334 /* if -D is set then pause in the PROM. */ 335 if (debug > 1) callrom(); 336 337 /* 338 * Jump directly to the kernel. Solaris kernel and Sun PROM 339 * flash updates expect ROMP vector in %o0, so we do. Format 340 * of other parameters and their order reflect OF_chain() 341 * symantics since this is what older NetBSD kernels rely on. 342 * (see sparc64/include/bootinfo.h for specification). 343 */ 344 DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw, 345 (long)args, (long)l, (long)ofw, (long)ofw, 346 (void*)marks[MARK_ENTRY])); 347 (*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw, 348 (long)ofw); 349 printf("Returned from kernel entry point!\n"); 350 } 351 352 static void 353 start_kernel(char *kernel, char *bootline, void *ofw) 354 { 355 int fd; 356 u_long marks[MARK_MAX]; 357 358 /* 359 * First, load headers using default allocator and check whether kernel 360 * entry address matches kernel text load address. If yes, this is the 361 * old kernel designed for ofwboot v1.8 and therefore it must be mapped 362 * by PROM. Otherwise, map the kernel with 4MB permanent pages. 363 */ 364 loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR); 365 if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) { 366 if (COMPAT_BOOT(marks) || compatmode) { 367 (void)printf("[c] "); 368 loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR); 369 } else { 370 loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR); 371 } 372 (void)printf("Loading %s: ", kernel); 373 374 if (fdloadfile(fd, marks, LOAD_ALL) != -1) { 375 close(fd); 376 jump_to_kernel(marks, kernel, bootline, ofw); 377 } 378 } 379 (void)printf("Failed to load '%s'.\n", kernel); 380 } 381 382 static void 383 help(void) 384 { 385 printf( "enter a special command\n" 386 " halt\n" 387 " exit\n" 388 " to return to OpenFirmware\n" 389 " ?\n" 390 " help\n" 391 " to display this message\n" 392 "or a boot specification:\n" 393 " [device] [kernel] [options]\n" 394 "\n" 395 "for example:\n" 396 " disk:a netbsd -s\n"); 397 } 398 399 static void 400 do_config_command(const char *cmd, const char *arg) 401 { 402 DPRINTF(("do_config_command: %s\n", cmd)); 403 if (strcmp(cmd, "bootpartition") == 0) { 404 char *c; 405 406 DPRINTF(("switching boot partition to %s from %s\n", 407 arg, bootdev)); 408 c = strrchr(bootdev, ':'); 409 if (!c) return; 410 if (c[1] == 0) return; 411 if (strlen(arg) > strlen(c)) return; 412 strcpy(c, arg); 413 DPRINTF(("new boot device: %s\n", bootdev)); 414 bootinfo_pass_bootdev = true; 415 } 416 } 417 418 static void 419 parse_boot_config(char *cfg, size_t len) 420 { 421 const char *cmd = NULL, *arg = NULL; 422 423 while (len) { 424 if (isspace(*cfg)) { 425 cfg++; len--; continue; 426 } 427 if (*cfg == ';' || *cfg == '#') { 428 while (len && *cfg != '\r' && *cfg != '\n') { 429 cfg++; len--; 430 } 431 continue; 432 } 433 cmd = cfg; 434 while (len && !isspace(*cfg)) { 435 cfg++; len--; 436 } 437 *cfg = 0; 438 if (len > 0) { 439 cfg++; len--; 440 while (isspace(*cfg) && len) { 441 cfg++; len--; 442 } 443 if (len > 0 ) { 444 arg = cfg; 445 while (len && !isspace(*cfg)) { 446 cfg++; len--; 447 } 448 *cfg = 0; 449 } 450 } 451 do_config_command(cmd, arg); 452 if (len > 0) { 453 cfg++; len--; 454 } 455 } 456 } 457 458 static void 459 check_boot_config(void) 460 { 461 int fd, err, off, len; 462 struct stat st; 463 char *bc; 464 465 if (!root_fs_quickseekable) return; 466 DPRINTF(("checking for /boot.cfg...\n")); 467 fd = open("/boot.cfg", 0); 468 if (fd < 0) return; 469 DPRINTF(("found /boot.cfg\n")); 470 if (fstat(fd, &st) == -1 || st.st_size > 32*1024) { 471 close(fd); 472 return; 473 } 474 bc = alloc(st.st_size+1); 475 off = 0; 476 do { 477 len = read(fd, bc+off, 1024); 478 if (len <= 0) 479 break; 480 off += len; 481 } while (len > 0); 482 bc[off] = 0; 483 close(fd); 484 485 parse_boot_config(bc, off); 486 } 487 488 void 489 main(void *ofw) 490 { 491 int boothowto, i = 0; 492 493 char kernel[PROM_MAX_PATH]; 494 char bootline[PROM_MAX_PATH]; 495 496 /* Initialize OpenFirmware */ 497 romp = ofw; 498 prom_init(); 499 500 printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev); 501 DPRINTF((">> (%s, %s)\n", bootprog_maker, bootprog_date)); 502 503 /* Figure boot arguments */ 504 strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1); 505 boothowto = bootoptions(prom_getbootargs(), bootdev, kernel, bootline); 506 507 for (;; *kernel = '\0') { 508 if (boothowto & RB_ASKNAME) { 509 char *cp, cmdline[PROM_MAX_PATH]; 510 511 printf("Boot: "); 512 gets(cmdline); 513 514 if (!strcmp(cmdline,"exit") || 515 !strcmp(cmdline,"halt")) { 516 prom_halt(); 517 } else if (!strcmp(cmdline, "?") || 518 !strcmp(cmdline, "help")) { 519 help(); 520 continue; 521 } 522 523 boothowto = bootoptions(cmdline, bootdev, kernel, 524 bootline); 525 boothowto |= RB_ASKNAME; 526 i = 0; 527 } 528 529 if (*kernel == '\0') { 530 if (kernelnames[i] == NULL) { 531 boothowto |= RB_ASKNAME; 532 continue; 533 } 534 strncpy(kernel, kernelnames[i++], PROM_MAX_PATH); 535 } else if (i == 0) { 536 /* 537 * Kernel name was passed via command line -- ask user 538 * again if requested image fails to boot. 539 */ 540 boothowto |= RB_ASKNAME; 541 } 542 543 check_boot_config(); 544 start_kernel(kernel, bootline, ofw); 545 546 /* 547 * Try next name from kernel name list if not in askname mode, 548 * enter askname on reaching list's end. 549 */ 550 if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) { 551 printf(": trying %s...\n", kernelnames[i]); 552 } else { 553 printf("\n"); 554 boothowto |= RB_ASKNAME; 555 } 556 } 557 558 (void)printf("Boot failed! Exiting to the Firmware.\n"); 559 prom_halt(); 560 } 561