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