1 /* $NetBSD: boot.c,v 1.21 2010/01/27 22:18:37 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 93 int debug = 0; 94 int compatmode = 0; 95 96 #if 0 97 static void 98 prom2boot(char *dev) 99 { 100 char *cp, *lp = 0; 101 int handle; 102 char devtype[16]; 103 104 for (cp = dev; *cp; cp++) 105 if (*cp == ':') 106 lp = cp; 107 if (!lp) 108 lp = cp; 109 *lp = 0; 110 } 111 #endif 112 113 static int 114 bootoptions(const char *ap, char *loaddev, char *kernel, char *options) 115 { 116 int v = 0; 117 const char *start1 = NULL, *end1 = NULL, *start2 = NULL, *end2 = NULL; 118 const char *path; 119 char partition, *pp; 120 121 *kernel = '\0'; 122 *options = '\0'; 123 124 if (ap == NULL) { 125 return (0); 126 } 127 128 while (*ap == ' ') { 129 ap++; 130 } 131 132 if (*ap != '-') { 133 start1 = ap; 134 while (*ap != '\0' && *ap != ' ') { 135 ap++; 136 } 137 end1 = ap; 138 139 while (*ap != '\0' && *ap == ' ') { 140 ap++; 141 } 142 143 if (*ap != '-') { 144 start2 = ap; 145 while (*ap != '\0' && *ap != ' ') { 146 ap++; 147 } 148 end2 = ap; 149 while (*ap != '\0' && *ap == ' ') { 150 ap++; 151 } 152 } 153 } 154 if (end2 == start2) { 155 start2 = end2 = NULL; 156 } 157 if (end1 == start1) { 158 start1 = end1 = NULL; 159 } 160 161 if (start1 == NULL) { 162 /* only options */ 163 } else if (start2 == NULL) { 164 memcpy(kernel, start1, (end1 - start1)); 165 kernel[end1 - start1] = '\0'; 166 path = filename(kernel, &partition); 167 if (path == NULL) { 168 strcpy(loaddev, kernel); 169 kernel[0] = '\0'; 170 } else if (path != kernel) { 171 /* copy device part */ 172 memcpy(loaddev, kernel, path-kernel); 173 loaddev[path-kernel] = '\0'; 174 if (partition) { 175 pp = loaddev + strlen(loaddev); 176 pp[0] = ':'; 177 pp[1] = partition; 178 pp[2] = '\0'; 179 } 180 /* and kernel path */ 181 strcpy(kernel, path); 182 } 183 } else { 184 memcpy(loaddev, start1, (end1-start1)); 185 loaddev[end1-start1] = '\0'; 186 memcpy(kernel, start2, (end2 - start2)); 187 kernel[end2 - start2] = '\0'; 188 } 189 190 strcpy(options, ap); 191 while (*ap != '\0' && *ap != ' ' && *ap != '\t' && *ap != '\n') { 192 BOOT_FLAG(*ap, v); 193 switch(*ap++) { 194 case 'D': 195 debug = 2; 196 break; 197 case 'C': 198 compatmode = 1; 199 break; 200 default: 201 break; 202 } 203 } 204 205 if (((v & RB_KDB) != 0) && (debug == 0)) { 206 debug = 1; 207 } 208 209 DPRINTF(("bootoptions: device='%s', kernel='%s', options='%s'\n", 210 loaddev, kernel, options)); 211 return (v); 212 } 213 214 /* 215 * The older (those relying on ofwboot v1.8 and earlier) kernels can't handle 216 * ksyms information unless it resides in a dedicated memory allocated from 217 * PROM and aligned on NBPG boundary. This is because the kernels calculate 218 * their ends on their own, they use address of 'end[]' reference which follows 219 * text segment. Ok, allocate some memory from PROM and copy symbol information 220 * over there. 221 */ 222 static void 223 ksyms_copyout(void **ssym, void **esym) 224 { 225 void *addr; 226 int kssize = (int)(long)(*esym - *ssym + 1); 227 228 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p, kssize = %d\n", 229 *ssym, *esym, kssize)); 230 231 if ( (addr = OF_claim(0, kssize, NBPG)) == (void *)-1) { 232 panic("ksyms_copyout(): no space for symbol table"); 233 } 234 235 memcpy(addr, *ssym, kssize); 236 *ssym = addr; 237 *esym = addr + kssize - 1; 238 239 DPRINTF(("ksyms_copyout(): ssym = %p, esym = %p\n", *ssym, *esym)); 240 } 241 242 /* 243 * Prepare boot information and jump directly to the kernel. 244 */ 245 static void 246 jump_to_kernel(u_long *marks, char *kernel, char *args, void *ofw) 247 { 248 extern char end[]; 249 int l, machine_tag; 250 long newargs[4]; 251 void *ssym, *esym; 252 vaddr_t bootinfo; 253 struct btinfo_symtab bi_sym; 254 struct btinfo_kernend bi_kend; 255 char *cp; 256 char bootline[PROM_MAX_PATH * 2]; 257 258 /* Compose kernel boot line. */ 259 strncpy(bootline, kernel, sizeof(bootline)); 260 cp = bootline + strlen(bootline); 261 if (*args) { 262 *cp++ = ' '; 263 strncpy(bootline, args, sizeof(bootline) - (cp - bootline)); 264 } 265 *cp = 0; args = bootline; 266 267 /* Record symbol information in the bootinfo. */ 268 bootinfo = bi_init(marks[MARK_END]); 269 bi_sym.nsym = marks[MARK_NSYM]; 270 bi_sym.ssym = marks[MARK_SYM]; 271 bi_sym.esym = marks[MARK_END]; 272 bi_add(&bi_sym, BTINFO_SYMTAB, sizeof(bi_sym)); 273 bi_kend.addr= bootinfo + BOOTINFO_SIZE; 274 bi_add(&bi_kend, BTINFO_KERNEND, sizeof(bi_kend)); 275 sparc64_finalize_tlb(marks[MARK_DATA]); 276 sparc64_bi_add(); 277 278 ssym = (void*)(long)marks[MARK_SYM]; 279 esym = (void*)(long)marks[MARK_END]; 280 281 DPRINTF(("jump_to_kernel(): ssym = %p, esym = %p\n", ssym, esym)); 282 283 /* Adjust ksyms pointers, if needed. */ 284 if (COMPAT_BOOT(marks) || compatmode) { 285 ksyms_copyout(&ssym, &esym); 286 } 287 288 freeall(); 289 /* 290 * When we come in args consists of a pointer to the boot 291 * string. We need to fix it so it takes into account 292 * other params such as romp. 293 */ 294 295 /* 296 * Stash pointer to end of symbol table after the argument 297 * strings. 298 */ 299 l = strlen(args) + 1; 300 memcpy(args + l, &esym, sizeof(esym)); 301 l += sizeof(esym); 302 303 /* 304 * Tell the kernel we're an OpenFirmware system. 305 */ 306 machine_tag = SPARC_MACHINE_OPENFIRMWARE; 307 memcpy(args + l, &machine_tag, sizeof(machine_tag)); 308 l += sizeof(machine_tag); 309 310 /* 311 * Since we don't need the boot string (we can get it from /chosen) 312 * we won't pass it in. Just pass in esym and magic # 313 */ 314 newargs[0] = SPARC_MACHINE_OPENFIRMWARE; 315 newargs[1] = (long)esym; 316 newargs[2] = (long)ssym; 317 newargs[3] = (long)(void*)bootinfo; 318 args = (char *)newargs; 319 l = sizeof(newargs); 320 321 /* if -D is set then pause in the PROM. */ 322 if (debug > 1) callrom(); 323 324 /* 325 * Jump directly to the kernel. Solaris kernel and Sun PROM 326 * flash updates expect ROMP vector in %o0, so we do. Format 327 * of other parameters and their order reflect OF_chain() 328 * symantics since this is what older NetBSD kernels rely on. 329 * (see sparc64/include/bootinfo.h for specification). 330 */ 331 DPRINTF(("jump_to_kernel(%lx, %lx, %lx, %lx, %lx) @ %p\n", (long)ofw, 332 (long)args, (long)l, (long)ofw, (long)ofw, 333 (void*)marks[MARK_ENTRY])); 334 (*(entry_t)marks[MARK_ENTRY])((long)ofw, (long)args, (long)l, (long)ofw, 335 (long)ofw); 336 printf("Returned from kernel entry point!\n"); 337 } 338 339 static void 340 start_kernel(char *kernel, char *bootline, void *ofw) 341 { 342 int fd; 343 u_long marks[MARK_MAX]; 344 345 /* 346 * First, load headers using default allocator and check whether kernel 347 * entry address matches kernel text load address. If yes, this is the 348 * old kernel designed for ofwboot v1.8 and therefore it must be mapped 349 * by PROM. Otherwise, map the kernel with 4MB permanent pages. 350 */ 351 loadfile_set_allocator(LOADFILE_NOP_ALLOCATOR); 352 if ( (fd = loadfile(kernel, marks, LOAD_HDR|COUNT_TEXT)) != -1) { 353 if (COMPAT_BOOT(marks) || compatmode) { 354 (void)printf("[c] "); 355 loadfile_set_allocator(LOADFILE_OFW_ALLOCATOR); 356 } else { 357 loadfile_set_allocator(LOADFILE_MMU_ALLOCATOR); 358 } 359 (void)printf("Loading %s: ", kernel); 360 361 if (fdloadfile(fd, marks, LOAD_ALL) != -1) { 362 close(fd); 363 jump_to_kernel(marks, kernel, bootline, ofw); 364 } 365 } 366 (void)printf("Failed to load '%s'.\n", kernel); 367 } 368 369 static void 370 help(void) 371 { 372 printf( "enter a special command\n" 373 " halt\n" 374 " exit\n" 375 " to return to OpenFirmware\n" 376 " ?\n" 377 " help\n" 378 " to display this message\n" 379 "or a boot specification:\n" 380 " [device] [kernel] [options]\n" 381 "\n" 382 "for example:\n" 383 " disk:a netbsd -s\n"); 384 } 385 386 void 387 main(void *ofw) 388 { 389 int boothowto, i = 0; 390 391 char kernel[PROM_MAX_PATH]; 392 char bootline[PROM_MAX_PATH]; 393 394 /* Initialize OpenFirmware */ 395 romp = ofw; 396 prom_init(); 397 398 printf("\r>> %s, Revision %s\n", bootprog_name, bootprog_rev); 399 DPRINTF((">> (%s, %s)\n", bootprog_maker, bootprog_date)); 400 401 /* Figure boot arguments */ 402 strncpy(bootdev, prom_getbootpath(), sizeof(bootdev) - 1); 403 boothowto = bootoptions(prom_getbootargs(), bootdev, kernel, bootline); 404 405 for (;; *kernel = '\0') { 406 if (boothowto & RB_ASKNAME) { 407 char *cp, cmdline[PROM_MAX_PATH]; 408 409 printf("Boot: "); 410 gets(cmdline); 411 412 if (!strcmp(cmdline,"exit") || 413 !strcmp(cmdline,"halt")) { 414 prom_halt(); 415 } else if (!strcmp(cmdline, "?") || 416 !strcmp(cmdline, "help")) { 417 help(); 418 continue; 419 } 420 421 boothowto = bootoptions(cmdline, bootdev, kernel, 422 bootline); 423 boothowto |= RB_ASKNAME; 424 i = 0; 425 } 426 427 if (*kernel == '\0') { 428 if (kernelnames[i] == NULL) { 429 boothowto |= RB_ASKNAME; 430 continue; 431 } 432 strncpy(kernel, kernelnames[i++], PROM_MAX_PATH); 433 } else if (i == 0) { 434 /* 435 * Kernel name was passed via command line -- ask user 436 * again if requested image fails to boot. 437 */ 438 boothowto |= RB_ASKNAME; 439 } 440 441 start_kernel(kernel, bootline, ofw); 442 443 /* 444 * Try next name from kernel name list if not in askname mode, 445 * enter askname on reaching list's end. 446 */ 447 if ((boothowto & RB_ASKNAME) == 0 && (kernelnames[i] != NULL)) { 448 printf(": trying %s...\n", kernelnames[i]); 449 } else { 450 printf("\n"); 451 boothowto |= RB_ASKNAME; 452 } 453 } 454 455 (void)printf("Boot failed! Exiting to the Firmware.\n"); 456 prom_halt(); 457 } 458