1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/fcntl.h> 30 #include <sys/obpdefs.h> 31 #include <sys/reboot.h> 32 #include <sys/promif.h> 33 #include <sys/stat.h> 34 #include <sys/bootvfs.h> 35 #include <sys/platnames.h> 36 #include <sys/salib.h> 37 #include <sys/elf.h> 38 #include <sys/link.h> 39 #include <sys/auxv.h> 40 #include <sys/boot_policy.h> 41 #include <sys/boot_redirect.h> 42 #include <sys/bootconf.h> 43 #include <sys/boot.h> 44 #include "boot_plat.h" 45 46 #define SUCCESS 0 47 #define FAILURE -1 48 49 #define ISSPACE(c) (c == ' ' || c == '\t') 50 #define SKIP_WHITESPC(cp) while (*cp && ISSPACE(*cp)) cp++; 51 52 53 #ifdef DEBUG 54 int debug = 0; 55 #else 56 static const int debug = 0; 57 #endif 58 59 #define dprintf if (debug) printf 60 61 #ifdef DEBUG_LISTS 62 void print_memlist(struct memlist *av); 63 #endif 64 65 extern int (*readfile(int fd, int print))(); 66 extern void kmem_init(void); 67 extern void *kmem_alloc(size_t, int); 68 extern void kmem_free(void *, size_t); 69 extern void get_boot_args(char *buf); 70 extern void setup_bootops(void); 71 extern struct bootops bootops; 72 extern void exitto(int (*entrypoint)()); 73 extern void exitto64(int (*entrypoint)(), void *bootvec); 74 75 int openfile(char *filename); 76 77 int client_isLP64 = 1; /* SPARC clients are always LP64 */ 78 79 /* 80 * filename is the name of the standalone we're going to execute. 81 */ 82 char filename[MAXPATHLEN]; 83 84 char * const defname = "kernel/sparcv9/unix"; 85 86 /* 87 * We enable the cache by default 88 * but boot -n will leave it alone... 89 * that is, we use whatever state the PROM left it in. 90 */ 91 char *mfg_name; 92 int cache_state = 1; 93 char filename2[MAXPATHLEN]; 94 95 int boothowto = 0; 96 int verbosemode = 0; 97 98 99 /* 100 * Copy filename and bargs into v2args_buf, which will be exported as the 101 * boot-args boot property. We should probably warn the user if anything gets 102 * cut off. 103 */ 104 void 105 set_client_bootargs(const char *filename, const char *bargs) 106 { 107 int i = 0; 108 const char *s; 109 110 s = filename; 111 while (*s != '\0' && i < V2ARGS_BUF_SZ - 1) 112 v2args_buf[i++] = *s++; 113 114 if (i >= V2ARGS_BUF_SZ - 2) { 115 /* Not enough room for a space and any of bargs. */ 116 v2args_buf[i] = '\0'; 117 return; 118 } 119 120 v2args_buf[i++] = ' '; 121 122 s = bargs; 123 while (*s != '\0' && i < V2ARGS_BUF_SZ - 1) 124 v2args_buf[i++] = *s++; 125 126 v2args_buf[i] = '\0'; 127 } 128 129 /* 130 * The slice redirection file is used on the install CD 131 */ 132 static int 133 read_redirect(char *redirect) 134 { 135 int fd; 136 char slicec; 137 size_t nread = 0; 138 139 if ((fd = open(BOOT_REDIRECT, O_RDONLY)) != -1) { 140 /* 141 * Read the character out of the file - this is the 142 * slice to use, in base 36. 143 */ 144 nread = read(fd, &slicec, 1); 145 (void) close(fd); 146 if (nread == 1) 147 *redirect++ = slicec; 148 } 149 *redirect = '\0'; 150 151 return (nread == 1); 152 } 153 154 void 155 post_mountroot(char *bootfile, char *redirect) 156 { 157 int (*go2)(); 158 int fd; 159 160 /* Save the bootfile, just in case we need it again */ 161 (void) strcpy(filename2, bootfile); 162 163 for (;;) { 164 if (boothowto & RB_ASKNAME) { 165 char tmpname[MAXPATHLEN]; 166 167 printf("Enter filename [%s]: ", bootfile); 168 (void) cons_gets(tmpname, sizeof (tmpname)); 169 if (tmpname[0] != '\0') 170 (void) strcpy(bootfile, tmpname); 171 } 172 173 if (boothowto & RB_HALT) { 174 printf("Boot halted.\n"); 175 prom_enter_mon(); 176 } 177 178 if ((fd = openfile(bootfile)) == FAILURE) { 179 180 /* 181 * There are many reasons why this might've 182 * happened .. but one of them is that we're 183 * on the installation CD, and we need to 184 * revector ourselves off to a different partition 185 * of the CD. Check for the redirection file. 186 */ 187 if (redirect != NULL && 188 read_redirect(redirect)) { 189 /* restore bootfile */ 190 (void) strcpy(bootfile, filename2); 191 return; 192 /*NOTREACHED*/ 193 } 194 195 printf("%s: cannot open %s\n", my_own_name, bootfile); 196 boothowto |= RB_ASKNAME; 197 198 /* restore bootfile */ 199 (void) strcpy(bootfile, filename2); 200 continue; 201 } 202 203 if ((go2 = readfile(fd, boothowto & RB_VERBOSE)) != 204 (int(*)()) -1) { 205 #ifdef MPSAS 206 sas_bpts(); 207 #endif 208 (void) close(fd); 209 } else { 210 printf("boot failed\n"); 211 boothowto |= RB_ASKNAME; 212 continue; 213 } 214 215 if (boothowto & RB_HALT) { 216 printf("Boot halted before exit to 0x%p.\n", 217 (void *)go2); 218 prom_enter_mon(); 219 } 220 221 my_own_name = bootfile; 222 223 dprintf("Calling exitto64(%p, %p)\n", (void *)go2, 224 (void *)elfbootvecELF64); 225 exitto64(go2, (void *)elfbootvecELF64); 226 } 227 } 228 229 /*ARGSUSED*/ 230 static int 231 boot_open(char *pathname, void *arg) 232 { 233 dprintf("trying '%s'\n", pathname); 234 return (open(pathname, O_RDONLY)); 235 } 236 237 /* 238 * Open the given filename, expanding to it's 239 * platform-dependent location if necessary. 240 * 241 * Boot supports OBP and IEEE1275. 242 * 243 * XXX: Move side effects out of this function! 244 */ 245 int 246 openfile(char *filename) 247 { 248 static char *fullpath; 249 static int once; 250 int fd; 251 252 if (once == 0) { 253 254 ++once; 255 256 /* 257 * Setup exported 'boot' properties: 'mfg-name'. 258 * XXX: This shouldn't be a side effect of openfile(). 259 */ 260 if (mfg_name == NULL) 261 mfg_name = get_mfg_name(); 262 263 fullpath = (char *)kmem_alloc(MAXPATHLEN, 0); 264 } 265 266 if (*filename == '/') { 267 (void) strcpy(fullpath, filename); 268 fd = boot_open(fullpath, NULL); 269 return (fd); 270 } 271 272 fd = open_platform_file(filename, boot_open, NULL, fullpath); 273 if (fd == -1) 274 return (-1); 275 276 /* 277 * Copy back the name we actually found 278 */ 279 (void) strcpy(filename, fullpath); 280 return (fd); 281 } 282 283 /* 284 * Get the boot arguments from the PROM and split it into filename and 285 * options components. 286 * 287 * As per IEEE1275 and boot(1M), the boot arguments will have the syntax 288 * "[filename] [-options]". If filename is specified, it is copied into the 289 * first buffer. (Otherwise, the buffer is left alone.) The rest of the string 290 * is copied into the second buffer. 291 */ 292 static void 293 init_bootargs(char *fname_buf, int fname_buf_sz, char *bargs_buf, 294 int bargs_buf_sz) 295 { 296 const char *tp = prom_bootargs(); 297 298 if (!tp || *tp == '\0') { 299 *bargs_buf = '\0'; 300 return; 301 } 302 303 SKIP_WHITESPC(tp); 304 305 /* 306 * If we don't have an option indicator, then we 307 * already have our filename prepended. 308 */ 309 if (*tp && *tp != '-') { 310 int i; 311 312 /* 313 * Copy the filename into fname_buf. 314 */ 315 for (i = 0; i < fname_buf_sz && *tp && !ISSPACE(*tp); ++i) 316 *fname_buf++ = *tp++; 317 318 if (i >= fname_buf_sz) { 319 printf("boot: boot filename too long!\n"); 320 printf("boot halted.\n"); 321 prom_enter_mon(); 322 /*NOTREACHED*/ 323 } else { 324 *fname_buf = '\0'; 325 } 326 327 SKIP_WHITESPC(tp); 328 } 329 330 /* The rest of the line is the options. */ 331 while (bargs_buf_sz > 1 && *tp) { 332 *bargs_buf++ = *tp++; 333 --bargs_buf_sz; 334 } 335 *bargs_buf = '\0'; 336 337 if (bargs_buf_sz == 1) { 338 printf("boot: boot arguments too long!\n"); 339 printf("boot halted.\n"); 340 prom_enter_mon(); 341 /*NOTREACHED*/ 342 } 343 } 344 345 boolean_t 346 is_netdev(char *devpath) 347 { 348 pnode_t node = prom_finddevice(devpath); 349 char *options; 350 351 if ((node == OBP_NONODE) || (node == OBP_BADNODE)) 352 return (B_FALSE); 353 if (prom_devicetype(node, "network") != 0) 354 return (B_TRUE); 355 356 /* 357 * For Infiniband, network device names will be of the 358 * format XXX/ib@0:port=1,pkey=1234,protocol=ip[,YYY] where 359 * XXX is typically /pci@8,700000/pci@1. The device_type 360 * property will be "ib". 361 */ 362 if (prom_devicetype(node, "ib") != 0) { 363 options = prom_path_options(devpath); 364 if (options != NULL) { 365 366 #define SEARCHSTRING ",protocol=ip" 367 #define SEARCHSTRLEN strlen(SEARCHSTRING) 368 369 if (strstr(options, ",protocol=ip,") != NULL) 370 return (B_TRUE); 371 while ((options = strstr(options, SEARCHSTRING)) != 372 NULL) { 373 char nextc; 374 375 nextc = options[SEARCHSTRLEN]; 376 if ((nextc == ',') || (nextc == 0)) 377 return (B_TRUE); 378 options += SEARCHSTRLEN; 379 } 380 } 381 } 382 return (B_FALSE); 383 } 384 385 /* 386 * Hook for modifying the OS boot path. This hook allows us to handle 387 * device arguments that the OS can't handle. 388 */ 389 void 390 mangle_os_bootpath(char *bpath) 391 { 392 pnode_t node; 393 char *stripped_pathname; 394 395 node = prom_finddevice(bpath); 396 if (prom_devicetype(node, "network") == 0) 397 return; 398 399 /* 400 * The OS can't handle network device arguments 401 * eg: boot net:promiscuous,speed=100,duplex=full 402 * So, we remove any argument strings in the device 403 * pathname we hand off to the OS for network devices. 404 * 405 * Internally, within boot, bpath is used to access 406 * the device, but v2path (as the boot property "boot-path") 407 * is the pathname passed to the OS. 408 */ 409 410 stripped_pathname = kmem_alloc(OBP_MAXPATHLEN, 0); 411 prom_strip_options(bpath, stripped_pathname); 412 v2path = stripped_pathname; 413 } 414 415 /* 416 * Given the boot path in the native firmware format use 417 * the redirection string to mutate the boot path to the new device. 418 * Fix up the 'v2path' so that it matches the new firmware path. 419 */ 420 void 421 redirect_boot_path(char *bpath, char *redirect) 422 { 423 char slicec = *redirect; 424 char *p = bpath + strlen(bpath); 425 426 /* 427 * If the redirection character doesn't fall in this 428 * range, something went horribly wrong. 429 */ 430 if (slicec < '0' || slicec > '7') { 431 printf("boot: bad redirection slice '%c'\n", slicec); 432 return; 433 } 434 435 /* 436 * Handle fully qualified OpenBoot pathname. 437 */ 438 while (--p >= bpath && *p != '@' && *p != '/') 439 if (*p == ':') 440 break; 441 if (*p++ == ':') { 442 /* 443 * Convert slice number to partition 'letter'. 444 */ 445 *p++ = 'a' + slicec - '0'; 446 *p = '\0'; 447 v2path = bpath; 448 return; 449 } 450 prom_panic("redirect_boot_path: mangled boot path!"); 451 } 452 453 #define PROM_VERS_MAX_LEN 64 454 455 void 456 system_check(void) 457 { 458 char buf[PROM_VERS_MAX_LEN]; 459 460 if (cpu_is_ultrasparc_1()) { 461 printf("UltraSPARC I processors are not supported by this " 462 "release of Solaris.\n"); 463 prom_exit_to_mon(); 464 } 465 466 if (prom_version_check(buf, PROM_VERS_MAX_LEN, NULL) != PROM_VER64_OK) { 467 printf("The firmware on this system does not support the 64-bit" 468 " OS.\n\tPlease upgrade to at least the following version:" 469 "\n\n\t%s\n", buf); 470 prom_exit_to_mon(); 471 } 472 } 473 474 /* 475 * Reads in the standalone (client) program and jumps to it. If this 476 * attempt fails, prints "boot failed" and returns to its caller. 477 * 478 * It will try to determine if it is loading a Unix file by 479 * looking at what should be the magic number. If it makes 480 * sense, it will use it; otherwise it jumps to the first 481 * address of the blocks that it reads in. 482 * 483 * This new boot program will open a file, read the ELF header, 484 * attempt to allocate and map memory at the location at which 485 * the client desires to be linked, and load the program at 486 * that point. It will then jump there. 487 */ 488 /*ARGSUSED*/ 489 int 490 main(void *cookie, char **argv, int argc) 491 { 492 /* 493 * bpath is the boot device path buffer. 494 * bargs is the boot arguments buffer. 495 */ 496 static char bpath[OBP_MAXPATHLEN], bargs[OBP_MAXPATHLEN]; 497 boolean_t user_specified_filename; 498 499 prom_init("boot", cookie); 500 fiximp(); 501 502 system_check(); 503 504 dprintf("\nboot: V%d /boot interface.\n", BO_VERSION); 505 #ifdef HALTBOOT 506 prom_enter_mon(); 507 #endif /* HALTBOOT */ 508 509 init_memlists(); 510 511 #ifdef DEBUG_LISTS 512 dprintf("Physmem avail:\n"); 513 if (debug) print_memlist(pfreelistp); 514 dprintf("Virtmem avail:\n"); 515 if (debug) print_memlist(vfreelistp); 516 dprintf("Phys installed:\n"); 517 if (debug) print_memlist(pinstalledp); 518 prom_enter_mon(); 519 #endif /* DEBUG_LISTS */ 520 521 /* 522 * Initialize the default filename (exported as "default-name" and 523 * used by kadb). 524 */ 525 set_default_filename(defname); 526 527 /* 528 * Parse the arguments ASAP in case there are any flags which may 529 * affect execution. 530 */ 531 532 /* 533 * filename is the path to the standalone. Initialize it to the empty 534 * string so we can tell whether the user specified it in the 535 * arguments. 536 */ 537 filename[0] = '\0'; 538 539 /* 540 * Fetch the boot arguments from the PROM and split the filename off 541 * if it's there. 542 */ 543 init_bootargs(filename, sizeof (filename), bargs, sizeof (bargs)); 544 545 /* 546 * kadb was delivered as a standalone, and as such, people got used to 547 * typing `boot kadb'. kmdb isn't a standalone - it is loaded by krtld 548 * as just another kernel module. For compatibility, though, when we 549 * see an attempt to `boot kadb' or `boot kmdb', we'll transform that 550 * into a `boot -k' (or equivalent). 551 */ 552 if (strcmp(filename, "kmdb") == 0 || strcmp(filename, "kadb") == 0) { 553 boothowto |= RB_KMDB; 554 *filename = '\0'; /* let boot figure out which unix to use */ 555 } 556 557 bootflags(bargs, sizeof (bargs)); 558 559 user_specified_filename = (filename[0] != '\0'); 560 561 /* Fetch the boot path from the PROM. */ 562 (void) strncpy(bpath, prom_bootpath(), sizeof (bpath) - 1); 563 bpath[sizeof (bpath) - 1] = '\0'; 564 565 dprintf("bootpath: 0x%p %s\n", (void *)bpath, bpath); 566 dprintf("bootargs: 0x%p %s\n", (void *)bargs, bargs); 567 dprintf("filename: 0x%p %s\n", (void *)filename, filename); 568 dprintf("kernname: 0x%p %s\n", (void *)kernname, kernname); 569 570 /* 571 * *v2path will be exported to the standalone as the boot-path boot 572 * property. 573 */ 574 v2path = bpath; 575 576 /* 577 * Our memory lists should be "up" by this time 578 */ 579 580 setup_bootops(); 581 582 /* 583 * If bpath is a network card, set v2path to a copy of bpath with the 584 * options stripped off. 585 */ 586 mangle_os_bootpath(bpath); 587 588 #ifdef sun4u 589 retain_nvram_page(); 590 #endif 591 592 if (bootprog(bpath, bargs, user_specified_filename) == 0) { 593 post_mountroot(filename, NULL); 594 /*NOTREACHED*/ 595 } 596 597 return (0); 598 } 599