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