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