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