1 /* $NetBSD: promlib.c,v 1.52 2022/01/22 11:49:16 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Paul Kranenburg. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * OPENPROM functions. These are here mainly to hide the OPENPROM interface 34 * from the rest of the kernel. 35 */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: promlib.c,v 1.52 2022/01/22 11:49:16 thorpej Exp $"); 39 40 #if defined(_KERNEL_OPT) 41 #include "opt_sparc_arch.h" 42 #endif 43 44 #include <sys/param.h> 45 #include <sys/kernel.h> 46 #include <sys/device.h> 47 48 #ifdef _STANDALONE 49 #include <lib/libsa/stand.h> 50 #define malloc(s,t,f) alloc(s) 51 #else 52 #include <sys/device_calls.h> 53 #include <sys/systm.h> 54 #include <sys/malloc.h> 55 #endif /* _STANDALONE */ 56 57 #include <machine/oldmon.h> 58 #include <machine/promlib.h> 59 #include <machine/ctlreg.h> 60 61 #include <sparc/sparc/asm.h> 62 #include <sparc/sparc/cache.h> 63 64 #include <lib/libkern/libkern.h> 65 66 #define obpvec ((struct promvec *)romp) 67 68 static void notimplemented(void); 69 static void obp_v0_fortheval(const char *); 70 static void obp_set_callback(void (*)(void)); 71 static int obp_v0_read(int, void *, int); 72 static int obp_v0_write(int, const void *, int); 73 static int obp_v2_getchar(void); 74 static int obp_v2_peekchar(void); 75 static void obp_v2_putchar(int); 76 static void obp_v2_putstr(const char *, int); 77 static int obp_v2_seek(int, u_quad_t); 78 static char *parse_bootfile(char *); 79 static char *parse_bootargs(char *); 80 static const char *obp_v0_getbootpath(void); 81 static const char *obp_v0_getbootfile(void); 82 static const char *obp_v0_getbootargs(void); 83 static const char *obp_v2_getbootpath(void); 84 static const char *obp_v2_getbootfile(void); 85 static const char *obp_v2_getbootargs(void); 86 static int obp_v2_finddevice(const char *); 87 static int obp_ticks(void); 88 89 static int findchosen(void); 90 static const char *opf_getbootpath(void); 91 static const char *opf_getbootfile(void); 92 static const char *opf_getbootargs(void); 93 static int opf_finddevice(const char *); 94 static int opf_instance_to_package(int); 95 static char *opf_nextprop(int, const char *); 96 static void opf_interpret_simple(const char *); 97 98 #ifndef _STANDALONE 99 static devhandle_t 100 null_prom_to_devhandle(int node __unused) 101 { 102 return devhandle_invalid(); 103 } 104 105 static int 106 null_devhandle_to_prom(devhandle_t devhandle __unused) 107 { 108 return 0; 109 } 110 #endif /* ! _STANDALONE */ 111 112 /* 113 * PROM entry points. 114 * Note: only PROM functions we use ar represented here; add as required. 115 */ 116 struct promops promops = { 117 -1, /* version */ 118 -1, /* revision */ 119 -1, /* stdin handle */ 120 -1, /* stdout handle */ 121 NULL, /* bootargs */ 122 123 (void *)notimplemented, /* bootpath */ 124 (void *)notimplemented, /* bootargs */ 125 (void *)notimplemented, /* bootfile */ 126 127 (void *)notimplemented, /* getchar */ 128 (void *)notimplemented, /* peekchar */ 129 (void *)notimplemented, /* putchar */ 130 (void *)notimplemented, /* putstr */ 131 (void *)notimplemented, /* open */ 132 (void *)notimplemented, /* close */ 133 (void *)notimplemented, /* read */ 134 (void *)notimplemented, /* write */ 135 (void *)notimplemented, /* seek */ 136 137 (void *)notimplemented, /* instance_to_package */ 138 139 (void *)notimplemented, /* halt */ 140 (void *)notimplemented, /* boot */ 141 (void *)notimplemented, /* call */ 142 (void *)notimplemented, /* interpret */ 143 (void *)notimplemented, /* callback */ 144 (void *)notimplemented, /* ticks */ 145 NULL, /* ticker data */ 146 147 (void *)notimplemented, /* setcontext */ 148 (void *)notimplemented, /* cpustart */ 149 (void *)notimplemented, /* cpustop */ 150 (void *)notimplemented, /* cpuidle */ 151 (void *)notimplemented, /* cpuresume */ 152 153 (void *)notimplemented, /* firstchild */ 154 (void *)notimplemented, /* nextsibling */ 155 156 (void *)notimplemented, /* getproplen */ 157 (void *)notimplemented, /* getprop */ 158 (void *)notimplemented, /* setprop */ 159 (void *)notimplemented, /* nextprop */ 160 (void *)notimplemented, /* finddevice */ 161 162 /* 163 * These should never be called in the STANDALONE environment, 164 * but when we're in the kernel environment, it's not really 165 * invalid to do so. 166 */ 167 #ifdef STANDALONE 168 (void *)notimplemented, /* node_to_devhandle */ 169 (void *)notimplemented, /* devhandle_to_node */ 170 #else 171 (void *)null_prom_to_devhandle, /* node_to_devhandle */ 172 (void *)null_devhandle_to_prom, /* devhandle_to_node */ 173 #endif /* STANDALONE */ 174 }; 175 176 static void 177 notimplemented(void) 178 { 179 char str[64]; 180 int n; 181 182 n = snprintf(str, sizeof(str), 183 "Operation not implemented on ROM version %d\r\n", 184 promops.po_version); 185 186 /* 187 * Use PROM vector directly, in case we're called before prom_init(). 188 */ 189 #if defined(SUN4) 190 if (CPU_ISSUN4) { 191 struct om_vector *sun4pvec = (struct om_vector *)PROM_BASE; 192 (*sun4pvec->fbWriteStr)(str, n); 193 } else 194 #endif 195 if (obpvec->pv_magic == OBP_MAGIC) { 196 if (obpvec->pv_romvec_vers < 2) { 197 (*obpvec->pv_putstr)(str, n); 198 } else { 199 int fd = *obpvec->pv_v2bootargs.v2_fd1; 200 (*obpvec->pv_v2devops.v2_write)(fd, str, n); 201 } 202 } else { /* assume OFW */ 203 static int stdout_node; 204 if (stdout_node == 0) { 205 int chosen = findchosen(); 206 OF_getprop(chosen, "stdout", &stdout_node, sizeof(int)); 207 } 208 OF_write(stdout_node, str, n); 209 } 210 } 211 212 #ifndef _STANDALONE 213 /* 214 * OBP device handle support. We subsume v0-v3 into a single implementation 215 * and use promops to handle differences. 216 * 217 * On 32-bit SPARC, the OpenFirmware variant also gets redirected here. 218 * See prom_init_opf(). 219 */ 220 221 static device_call_t 222 obp_devhandle_lookup_device_call(devhandle_t handle, const char *name, 223 devhandle_t *call_handlep) 224 { 225 __link_set_decl(obp_device_calls, struct device_call_descriptor); 226 struct device_call_descriptor * const *desc; 227 228 __link_set_foreach(desc, obp_device_calls) { 229 if (strcmp((*desc)->name, name) == 0) { 230 return (*desc)->call; 231 } 232 } 233 return NULL; 234 } 235 236 static const struct devhandle_impl obp_devhandle_impl = { 237 .type = DEVHANDLE_TYPE_OPENBOOT, 238 .lookup_device_call = obp_devhandle_lookup_device_call, 239 }; 240 241 static devhandle_t 242 devhandle_from_obp(devhandle_t super_handle, int node) 243 { 244 devhandle_type_t super_type = devhandle_type(super_handle); 245 devhandle_t handle = { 0 }; 246 247 if (super_type == DEVHANDLE_TYPE_OPENBOOT) { 248 handle.impl = super_handle.impl; 249 } else { 250 KASSERT(super_type == DEVHANDLE_TYPE_INVALID); 251 handle.impl = &obp_devhandle_impl; 252 } 253 handle.integer = node; 254 255 return handle; 256 } 257 258 static int 259 devhandle_to_obp(devhandle_t const handle) 260 { 261 KASSERT(devhandle_type(handle) == DEVHANDLE_TYPE_OPENBOOT); 262 263 return handle.integer; 264 } 265 266 static int 267 obp_device_enumerate_children(device_t dev, devhandle_t call_handle, void *v) 268 { 269 struct device_enumerate_children_args *args = v; 270 int node = devhandle_to_obp(call_handle); 271 272 for (node = prom_firstchild(node); node != 0; 273 node = prom_nextsibling(node)) { 274 if (!args->callback(dev, devhandle_from_obp(call_handle, node), 275 args->callback_arg)) { 276 break; 277 } 278 } 279 280 return 0; 281 } 282 OBP_DEVICE_CALL_REGISTER(DEVICE_ENUMERATE_CHILDREN_STR, 283 obp_device_enumerate_children) 284 #endif /* ! _STANDALONE */ 285 286 /* 287 * prom_getprop() reads the named property data from a given node. 288 * A buffer for the data may be passed in `*bufp'; if NULL, a 289 * buffer is allocated. The argument `size' specifies the data 290 * element size of the property data. This function checks that 291 * the actual property data length is an integral multiple of 292 * the element size. The number of data elements read into the 293 * buffer is returned into the integer pointed at by `nitem'. 294 */ 295 296 int 297 prom_getprop(int node, const char *name, size_t size, int *nitem, void *bufp) 298 { 299 void *buf; 300 int len; 301 302 len = prom_getproplen(node, name); 303 if (len <= 0) 304 return (ENOENT); 305 306 if ((len % size) != 0) 307 return (EINVAL); 308 309 buf = *(void **)bufp; 310 if (buf == NULL) { 311 /* No storage provided, so we allocate some */ 312 buf = malloc(len, M_DEVBUF, M_NOWAIT); 313 if (buf == NULL) 314 return (ENOMEM); 315 } else { 316 if (size * (*nitem) < len) 317 return (ENOMEM); 318 } 319 320 _prom_getprop(node, name, buf, len); 321 *(void **)bufp = buf; 322 *nitem = len / size; 323 return (0); 324 } 325 326 /* 327 * Return a string property. There is a (small) limit on the length; 328 * the string is fetched into a static buffer which is overwritten on 329 * subsequent calls. 330 */ 331 char * 332 prom_getpropstring(int node, const char *name) 333 { 334 static char stringbuf[32]; 335 336 return (prom_getpropstringA(node, name, stringbuf, sizeof stringbuf)); 337 } 338 339 /* 340 * Alternative prom_getpropstring(), where caller provides the buffer 341 */ 342 char * 343 prom_getpropstringA(int node, const char *name, char *buf, size_t bufsize) 344 { 345 int len = bufsize - 1; 346 347 if (prom_getprop(node, name, 1, &len, &buf) != 0) 348 len = 0; 349 350 buf[len] = '\0'; /* usually unnecessary */ 351 return (buf); 352 } 353 354 /* 355 * Fetch an integer (or pointer) property. 356 * The return value is the property, or the default if there was none. 357 */ 358 int 359 prom_getpropint(int node, const char *name, int deflt) 360 { 361 int intbuf, *ip = &intbuf; 362 int len = 1; 363 364 if (prom_getprop(node, name, sizeof(int), &len, &ip) != 0) 365 return (deflt); 366 367 return (*ip); 368 } 369 370 /* 371 * Fetch an unsigned 64-bit integer (or pointer) property. 372 * The return value is the property, or the default if there was none. 373 */ 374 uint64_t 375 prom_getpropuint64(int node, const char *name, uint64_t deflt) 376 { 377 uint64_t uint64buf, *uint64p = &uint64buf; 378 int len = 2; 379 380 if (prom_getprop(node, name, sizeof(uint64_t), &len, &uint64p) != 0) 381 return deflt; 382 383 return uint64buf; 384 } 385 386 /* 387 * Node Name Matching per IEEE 1275, section 4.3.6. 388 */ 389 static int 390 prom_matchname(int node, const char *name) 391 { 392 char buf[32], *cp; 393 394 prom_getpropstringA(node, "name", buf, sizeof buf); 395 if (strcmp(buf, name) == 0) 396 /* Exact match */ 397 return (1); 398 399 /* If name has a comma, an exact match is required */ 400 if (strchr(name, ',')) 401 return (0); 402 403 /* 404 * Otherwise, if the node's name contains a comma, we can match 405 * against the trailing string defined by the first comma. 406 */ 407 if ((cp = strchr(buf, ',')) != NULL) { 408 if (strcmp(cp + 1, name) == 0) 409 return (1); 410 } 411 412 return (0); 413 } 414 415 /* 416 * Translate device path to node 417 */ 418 int 419 prom_opennode(const char *path) 420 { 421 int fd; 422 423 if (prom_version() < 2) { 424 printf("WARNING: opennode not valid on PROM version %d\n", 425 promops.po_version); 426 return (0); 427 } 428 fd = prom_open(path); 429 if (fd == 0) 430 return (0); 431 432 return (prom_instance_to_package(fd)); 433 } 434 435 int 436 prom_findroot(void) 437 { 438 static int rootnode; 439 int node; 440 441 if ((node = rootnode) == 0 && (node = prom_nextsibling(0)) == 0) 442 panic("no PROM root device"); 443 rootnode = node; 444 return (node); 445 } 446 447 /* 448 * Given a `first child' node number, locate the node with the given name. 449 * Return the node number, or 0 if not found. 450 */ 451 int 452 prom_findnode(int first, const char *name) 453 { 454 int node; 455 456 for (node = first; node != 0; node = prom_nextsibling(node)) { 457 if (prom_matchname(node, name)) 458 return (node); 459 } 460 return (0); 461 } 462 463 /* 464 * Determine whether a node has the given property. 465 */ 466 int 467 prom_node_has_property(int node, const char *prop) 468 { 469 470 return (prom_getproplen(node, prop) != -1); 471 } 472 473 /* 474 * prom_search() recursively searches a PROM subtree for a given node name 475 * See IEEE 1275 `Search for matching child node', section 4.3.3. 476 */ 477 int 478 prom_search(int node, const char *name) 479 { 480 481 if (node == 0) 482 node = prom_findroot(); 483 484 if (prom_matchname(node, name)) 485 return (node); 486 487 for (node = prom_firstchild(node); node != 0; 488 node = prom_nextsibling(node)) { 489 int cnode; 490 if ((cnode = prom_search(node, name)) != 0) 491 return (cnode); 492 } 493 494 return (0); 495 } 496 497 /* 498 * Find the named device in the PROM device tree. 499 * XXX - currently we discard any qualifiers attached to device component names 500 */ 501 int 502 obp_v2_finddevice(const char *path) 503 { 504 int node; 505 char component[64]; 506 char c, *cp; 507 const char *startp, *endp; 508 #define IS_SEP(c) ((c) == '/' || (c) == '@' || (c) == ':') 509 510 if (path == NULL) 511 return (-1); 512 513 node = prom_findroot(); 514 515 for (startp = path; *startp != '\0'; ) { 516 /* 517 * Identify next component in path 518 */ 519 while (*startp == '/') 520 startp++; 521 522 endp = startp; 523 while ((c = *endp) != '\0' && !IS_SEP(c)) 524 endp++; 525 526 /* Copy component */ 527 for (cp = component; startp != endp;) { 528 /* Check component bounds */ 529 if (cp > component + sizeof component - 1) 530 return (-1); 531 *cp++ = *startp++; 532 } 533 534 /* Zero terminate this component */ 535 *cp = '\0'; 536 537 /* Advance `startp' over any non-slash separators */ 538 while ((c = *startp) != '\0' && c != '/') 539 startp++; 540 541 node = prom_findnode(prom_firstchild(node), component); 542 if (node == 0) 543 return (-1); 544 } 545 546 return (node); 547 } 548 549 550 /* 551 * Get the global "options" node Id. 552 */ 553 int prom_getoptionsnode(void) 554 { 555 static int optionsnode; 556 557 if (optionsnode == 0) { 558 optionsnode = prom_findnode(prom_firstchild(prom_findroot()), 559 "options"); 560 } 561 return optionsnode; 562 } 563 564 /* 565 * Return a property string value from the global "options" node. 566 */ 567 int prom_getoption(const char *name, char *buf, int buflen) 568 { 569 int node = prom_getoptionsnode(); 570 int error, len; 571 572 if (buflen == 0) 573 return (EINVAL); 574 575 if (node == 0) 576 return (ENOENT); 577 578 len = buflen - 1; 579 if ((error = prom_getprop(node, name, 1, &len, &buf)) != 0) 580 return error; 581 582 buf[len] = '\0'; 583 return (0); 584 } 585 586 void 587 prom_halt(void) 588 { 589 590 prom_setcallback(NULL); 591 _prom_halt(); 592 panic("PROM exit failed"); 593 } 594 595 void 596 prom_boot(char *str) 597 { 598 599 prom_setcallback(NULL); 600 _prom_boot(str); 601 panic("PROM boot failed"); 602 } 603 604 605 /* 606 * print debug info to prom. 607 * This is not safe, but then what do you expect? 608 */ 609 void 610 prom_printf(const char *fmt, ...) 611 { 612 static char buf[256]; 613 int i, len; 614 va_list ap; 615 616 va_start(ap, fmt); 617 len = vsnprintf(buf, sizeof(buf), fmt, ap); 618 va_end(ap); 619 620 #if _obp_not_cooked_ 621 (*promops.po_write)(promops.po_stdout, buf, len); 622 #endif 623 624 for (i = 0; i < len; i++) { 625 int c = buf[i]; 626 if (c == '\n') 627 (*promops.po_putchar)('\r'); 628 (*promops.po_putchar)(c); 629 } 630 } 631 632 633 /* 634 * Pass a string to the FORTH PROM to be interpreted. 635 * (Note: may fail silently) 636 */ 637 static void 638 obp_v0_fortheval(const char *s) 639 { 640 641 obpvec->pv_fortheval.v0_eval(strlen(s), s); 642 } 643 644 int 645 obp_v0_read(int fd, void *buf, int len) 646 { 647 if (fd != prom_stdin()) 648 prom_printf("obp_v0_read: unimplemented read from %d\n", fd); 649 return (-1); 650 } 651 652 int 653 obp_v0_write(int fd, const void *buf, int len) 654 { 655 if (fd != prom_stdout()) 656 prom_printf("obp_v0_write: unimplemented write on %d\n", fd); 657 (*obpvec->pv_putstr)(buf, len); 658 return (-1); 659 } 660 661 inline void 662 obp_v2_putchar(int c) 663 { 664 char c0; 665 666 c0 = (c & 0x7f); 667 (*promops.po_write)(promops.po_stdout, &c0, 1); 668 } 669 670 #if 0 671 void 672 obp_v2_putchar_cooked(int c) 673 { 674 675 if (c == '\n') 676 obp_v2_putchar('\r'); 677 obp_v2_putchar(c); 678 } 679 #endif 680 681 int 682 obp_v2_getchar(void) 683 { 684 char c; 685 int n; 686 687 while ((n = (*promops.po_read)(promops.po_stdin, &c, 1)) != 1) 688 /*void*/; 689 if (c == '\r') 690 c = '\n'; 691 return (c); 692 } 693 694 int 695 obp_v2_peekchar(void) 696 { 697 char c; 698 int n; 699 700 n = (*promops.po_read)(promops.po_stdin, &c, 1); 701 if (n < 0) 702 return (-1); 703 704 if (c == '\r') 705 c = '\n'; 706 return (c); 707 } 708 709 int 710 obp_v2_seek(int handle, u_quad_t offset) 711 { 712 uint32_t hi, lo; 713 714 lo = offset & ((uint32_t)-1); 715 hi = (offset >> 32) & ((uint32_t)-1); 716 (*obpvec->pv_v2devops.v2_seek)(handle, hi, lo); 717 return (0); 718 } 719 720 /* 721 * On SS1s (and also IPCs, SLCs), `promvec->pv_v0bootargs->ba_argv[1]' 722 * contains the flags that were given after the boot command. On SS2s 723 * (and ELCs, IPXs, etc. and any sun4m class machine), `pv_v0bootargs' 724 * is NULL but `*promvec->pv_v2bootargs.v2_bootargs' points to 725 * "netbsd -s" or whatever. 726 */ 727 const char * 728 obp_v0_getbootpath(void) 729 { 730 struct v0bootargs *ba = promops.po_bootcookie; 731 return (ba->ba_argv[0]); 732 } 733 734 const char * 735 obp_v0_getbootargs(void) 736 { 737 struct v0bootargs *ba = promops.po_bootcookie; 738 return (ba->ba_argv[1]); 739 } 740 741 const char * 742 obp_v0_getbootfile(void) 743 { 744 struct v0bootargs *ba = promops.po_bootcookie; 745 return (ba->ba_kernel); 746 } 747 748 char * 749 parse_bootargs(char *args) 750 { 751 char *cp; 752 753 for (cp = args; *cp != '\0'; cp++) { 754 if (*cp == '-') { 755 int c; 756 /* 757 * Looks like options start here, but check this 758 * `-' is not part of the kernel name. 759 */ 760 if (cp == args) 761 break; 762 if ((c = *(cp-1)) == ' ' || c == '\t') 763 break; 764 } 765 } 766 return (cp); 767 } 768 769 const char * 770 obp_v2_getbootpath(void) 771 { 772 struct v2bootargs *ba = promops.po_bootcookie; 773 return (*ba->v2_bootpath); 774 } 775 776 const char * 777 obp_v2_getbootargs(void) 778 { 779 struct v2bootargs *ba = promops.po_bootcookie; 780 781 return (parse_bootargs(*ba->v2_bootargs)); 782 } 783 784 /* 785 * Static storage shared by prom_getbootfile(), prom_getbootargs() and 786 * prom_getbootpath(). 787 * Overwritten on each call! 788 */ 789 static char storage[128]; 790 791 char * 792 parse_bootfile(char *args) 793 { 794 char *cp, *dp; 795 796 cp = args; 797 dp = storage; 798 while (*cp != 0 && *cp != ' ' && *cp != '\t') { 799 if (dp >= storage + sizeof(storage) - 1) { 800 prom_printf("v2_bootargs too long\n"); 801 return (NULL); 802 } 803 if (*cp == '-') { 804 int c; 805 /* 806 * If this `-' is most likely the start of boot 807 * options, we're done. 808 */ 809 if (cp == args) 810 break; 811 if ((c = *(cp-1)) == ' ' || c == '\t') 812 break; 813 } 814 *dp++ = *cp++; 815 } 816 *dp = '\0'; 817 return (storage); 818 } 819 820 const char * 821 obp_v2_getbootfile(void) 822 { 823 struct v2bootargs *ba = promops.po_bootcookie; 824 char *kernel = parse_bootfile(*ba->v2_bootargs); 825 char buf[4+1]; 826 const char *prop; 827 828 if (kernel[0] != '\0') 829 return kernel; 830 831 /* 832 * The PROM does not insert the `boot-file' variable if any argument 833 * was given to the `boot' command (e.g `boot -s'). If we determine 834 * in parse_bootfile() above, that boot args contain only switches 835 * then get the `boot-file' value (if any) ourselves. 836 * If the `diag-switch?' PROM variable is set to true, we use 837 * `diag-file' instead. 838 */ 839 prop = (prom_getoption("diag-switch?", buf, sizeof buf) != 0 || 840 strcmp(buf, "true") != 0) 841 ? "diag-file" 842 : "boot-file"; 843 844 if (prom_getoption(prop, storage, sizeof storage) != 0) 845 return (NULL); 846 847 return (storage); 848 } 849 850 void 851 obp_v2_putstr(const char *str, int len) 852 { 853 prom_write(prom_stdout(), str, len); 854 } 855 856 void 857 obp_set_callback(void (*f)(void)) 858 { 859 *obpvec->pv_synchook = f; 860 } 861 862 int 863 obp_ticks(void) 864 { 865 866 return (*((int *)promops.po_tickdata)); 867 } 868 869 static int 870 findchosen(void) 871 { 872 static int chosennode; 873 int node; 874 875 if ((node = chosennode) == 0 && (node = OF_finddevice("/chosen")) == -1) 876 panic("no CHOSEN node"); 877 878 chosennode = node; 879 return (node); 880 } 881 882 static int 883 opf_finddevice(const char *name) 884 { 885 int phandle = OF_finddevice(name); 886 if (phandle == -1) 887 return (0); 888 else 889 return (phandle); 890 } 891 892 static int 893 opf_instance_to_package(int ihandle) 894 { 895 int phandle = OF_instance_to_package(ihandle); 896 if (phandle == -1) 897 return (0); 898 else 899 return (phandle); 900 } 901 902 903 static const char * 904 opf_getbootpath(void) 905 { 906 int node = findchosen(); 907 char *buf = storage; 908 int blen = sizeof storage; 909 910 if (prom_getprop(node, "bootpath", 1, &blen, &buf) != 0) 911 return (""); 912 913 return (buf); 914 } 915 916 static const char * 917 opf_getbootargs(void) 918 { 919 int node = findchosen(); 920 char *buf = storage; 921 int blen = sizeof storage; 922 923 if (prom_getprop(node, "bootargs", 1, &blen, &buf) != 0) 924 return (""); 925 926 return (parse_bootargs(buf)); 927 } 928 929 static const char * 930 opf_getbootfile(void) 931 { 932 int node = findchosen(); 933 char *buf = storage; 934 int blen = sizeof storage; 935 936 if (prom_getprop(node, "bootargs", 1, &blen, &buf) != 0) 937 return (""); 938 939 return (parse_bootfile(buf)); 940 } 941 942 static char * 943 opf_nextprop(int node, const char *prop) 944 { 945 #define OF_NEXTPROP_BUF_SIZE 32 /* specified by the standard */ 946 static char buf[OF_NEXTPROP_BUF_SIZE]; 947 OF_nextprop(node, prop, buf); 948 return (buf); 949 } 950 951 void 952 opf_interpret_simple(const char *s) 953 { 954 (void)OF_interpret(s, 0, 0); 955 } 956 957 /* 958 * Retrieve physical memory information from the PROM. 959 * If ap is NULL, return the required length of the array. 960 */ 961 int 962 prom_makememarr(struct memarr *ap, int xmax, int which) 963 { 964 struct v0mlist *mp; 965 int node, n; 966 const char *prop; 967 968 if (which != MEMARR_AVAILPHYS && which != MEMARR_TOTALPHYS) 969 panic("makememarr"); 970 971 /* 972 * `struct memarr' is in V2 memory property format. 973 * On previous ROM versions we must convert. 974 */ 975 switch (prom_version()) { 976 struct promvec *promvec; 977 struct om_vector *oldpvec; 978 case PROM_OLDMON: 979 oldpvec = (struct om_vector *)PROM_BASE; 980 n = 1; 981 if (ap != NULL) { 982 ap[0].zero = 0; 983 ap[0].addr = 0; 984 ap[0].len = (which == MEMARR_AVAILPHYS) 985 ? *oldpvec->memoryAvail 986 : *oldpvec->memorySize; 987 } 988 break; 989 990 case PROM_OBP_V0: 991 /* 992 * Version 0 PROMs use a linked list to describe these 993 * guys. 994 */ 995 promvec = romp; 996 mp = (which == MEMARR_AVAILPHYS) 997 ? *promvec->pv_v0mem.v0_physavail 998 : *promvec->pv_v0mem.v0_phystot; 999 for (n = 0; mp != NULL; mp = mp->next, n++) { 1000 if (ap == NULL) 1001 continue; 1002 if (n >= xmax) { 1003 printf("makememarr: WARNING: lost some memory\n"); 1004 break; 1005 } 1006 ap->zero = 0; 1007 ap->addr = (u_long)mp->addr; 1008 ap->len = mp->nbytes; 1009 ap++; 1010 } 1011 break; 1012 1013 default: 1014 printf("makememarr: hope version %d PROM is like version 2\n", 1015 prom_version()); 1016 /* FALLTHROUGH */ 1017 1018 case PROM_OBP_V3: 1019 case PROM_OBP_V2: 1020 /* 1021 * Version 2 PROMs use a property array to describe them. 1022 */ 1023 1024 /* Consider emulating `OF_finddevice' */ 1025 node = findnode(firstchild(findroot()), "memory"); 1026 goto case_common; 1027 1028 case PROM_OPENFIRM: 1029 node = OF_finddevice("/memory"); 1030 if (node == -1) 1031 node = 0; 1032 1033 case_common: 1034 if (node == 0) 1035 panic("makememarr: cannot find \"memory\" node"); 1036 1037 prop = (which == MEMARR_AVAILPHYS) ? "available" : "reg"; 1038 if (ap == NULL) { 1039 n = prom_getproplen(node, prop); 1040 } else { 1041 n = xmax; 1042 if (prom_getprop(node, prop, sizeof(struct memarr), 1043 &n, &ap) != 0) 1044 panic("makememarr: cannot get property"); 1045 } 1046 break; 1047 } 1048 1049 if (n <= 0) 1050 panic("makememarr: no memory found"); 1051 /* 1052 * Success! (Hooray) 1053 */ 1054 return (n); 1055 } 1056 1057 static struct idprom idprom; 1058 #ifdef _STANDALONE 1059 long hostid; 1060 #endif 1061 1062 struct idprom * 1063 prom_getidprom(void) 1064 { 1065 int node, len; 1066 u_long h; 1067 u_char *dst; 1068 1069 if (idprom.idp_format != 0) 1070 /* Already got it */ 1071 return (&idprom); 1072 1073 dst = (u_char *)&idprom; 1074 len = sizeof(struct idprom); 1075 1076 switch (prom_version()) { 1077 case PROM_OLDMON: 1078 #ifdef AC_IDPROM 1079 { 1080 u_char *src = (u_char *)AC_IDPROM; 1081 do { 1082 *dst++ = lduba(src++, ASI_CONTROL); 1083 } while (--len > 0); 1084 } 1085 #endif 1086 break; 1087 1088 /* 1089 * Fetch the `idprom' property at the root node. 1090 */ 1091 case PROM_OBP_V0: 1092 case PROM_OBP_V2: 1093 case PROM_OPENFIRM: 1094 case PROM_OBP_V3: 1095 node = prom_findroot(); 1096 if (prom_getprop(node, "idprom", 1, &len, &dst) != 0) { 1097 printf("`idprom' property cannot be read: " 1098 "cannot get ethernet address"); 1099 } 1100 break; 1101 } 1102 1103 /* Establish hostid */ 1104 h = (u_int)idprom.idp_machtype << 24; 1105 h |= idprom.idp_serialnum[0] << 16; 1106 h |= idprom.idp_serialnum[1] << 8; 1107 h |= idprom.idp_serialnum[2]; 1108 hostid = h; 1109 1110 return (&idprom); 1111 } 1112 1113 void prom_getether(int node, u_char *cp) 1114 { 1115 struct idprom *idp; 1116 1117 if (prom_get_node_ether(node, cp)) 1118 return; 1119 1120 /* Fall back on the machine's global ethernet address */ 1121 idp = prom_getidprom(); 1122 memcpy(cp, idp->idp_etheraddr, 6); 1123 } 1124 1125 bool 1126 prom_get_node_ether(int node, u_char *cp) 1127 { 1128 char buf[6+1], *bp; 1129 int nitem; 1130 1131 if (node == 0) 1132 return false; 1133 1134 /* 1135 * First, try the node's "mac-address" property. 1136 * This property is set by the adapter's firmware if the 1137 * device has already been opened for traffic, e.g. for 1138 * net booting. Its value might be `0-terminated', probably 1139 * because the Forth ROMs uses `xdrstring' instead of `xdrbytes' 1140 * to construct the property. 1141 */ 1142 nitem = 6+1; 1143 bp = buf; 1144 if (prom_getprop(node, "mac-address", 1, &nitem, &bp) == 0 && 1145 nitem >= 6) { 1146 memcpy(cp, bp, 6); 1147 return true; 1148 } 1149 1150 /* 1151 * Next, check the global "local-mac-address?" switch to see 1152 * if we should try to extract the node's "local-mac-address" 1153 * property. 1154 */ 1155 if (prom_getoption("local-mac-address?", buf, sizeof buf) != 0 || 1156 strcmp(buf, "true") != 0) 1157 return false; 1158 1159 /* Retrieve the node's "local-mac-address" property, if any */ 1160 nitem = 6; 1161 if (prom_getprop(node, "local-mac-address", 1, &nitem, &cp) == 0 && 1162 nitem == 6) 1163 return true; 1164 1165 return false; 1166 } 1167 1168 /* 1169 * The integer property "get-unum" on the root device is the address 1170 * of a callable function in the PROM that takes a physical address 1171 * (in lo/hipart format) and returns a string identifying the chip 1172 * location of the corresponding memory cell. 1173 */ 1174 const char * 1175 prom_pa_location(u_int phys_lo, u_int phys_hi) 1176 { 1177 static char *(*unum)(u_int, u_int); 1178 char *str; 1179 const char *unk = "<Unknown>"; 1180 1181 switch (prom_version()) { 1182 case PROM_OLDMON: 1183 case PROM_OPENFIRM: 1184 /* to do */ 1185 default: 1186 break; 1187 case PROM_OBP_V0: 1188 case PROM_OBP_V2: 1189 case PROM_OBP_V3: 1190 if (unum == NULL) 1191 unum = (char *(*)(u_int,u_int))(u_long) 1192 prom_getpropint(prom_findroot(), "get-unum", 0); 1193 1194 if (unum == NULL || (str = unum(phys_lo, phys_hi)) == NULL) 1195 break; 1196 1197 return (str); 1198 } 1199 1200 return (unk); 1201 } 1202 1203 static void prom_init_oldmon(void); 1204 static void prom_init_obp(void); 1205 static void prom_init_opf(void); 1206 1207 static inline void 1208 prom_init_oldmon(void) 1209 { 1210 struct om_vector *oldpvec = (struct om_vector *)PROM_BASE; 1211 1212 promops.po_version = PROM_OLDMON; 1213 promops.po_revision = oldpvec->monId[0]; /*XXX*/ 1214 1215 promops.po_stdin = *oldpvec->inSource; 1216 promops.po_stdout = *oldpvec->outSink; 1217 1218 promops.po_bootcookie = *oldpvec->bootParam; /* deref 1 lvl */ 1219 promops.po_bootpath = obp_v0_getbootpath; 1220 promops.po_bootfile = obp_v0_getbootfile; 1221 promops.po_bootargs = obp_v0_getbootargs; 1222 1223 promops.po_putchar = oldpvec->putChar; 1224 promops.po_getchar = oldpvec->getChar; 1225 promops.po_peekchar = oldpvec->mayGet; 1226 promops.po_putstr = oldpvec->fbWriteStr; 1227 promops.po_reboot = oldpvec->reBoot; 1228 promops.po_abort = oldpvec->abortEntry; 1229 promops.po_halt = oldpvec->exitToMon; 1230 promops.po_ticks = obp_ticks; 1231 promops.po_tickdata = oldpvec->nmiClock; 1232 promops.po_setcallback = (void *)sparc_noop; 1233 promops.po_setcontext = oldpvec->setcxsegmap; 1234 1235 #ifdef SUN4 1236 #ifndef _STANDALONE 1237 if (oldpvec->romvecVersion >= 2) { 1238 *oldpvec->vector_cmd = oldmon_w_cmd; 1239 } 1240 #endif 1241 #endif 1242 } 1243 1244 static inline void 1245 prom_init_obp(void) 1246 { 1247 struct nodeops *no; 1248 1249 /* 1250 * OBP v0, v2 & v3 1251 */ 1252 switch (obpvec->pv_romvec_vers) { 1253 case 0: 1254 promops.po_version = PROM_OBP_V0; 1255 break; 1256 case 2: 1257 promops.po_version = PROM_OBP_V2; 1258 break; 1259 case 3: 1260 promops.po_version = PROM_OBP_V3; 1261 break; 1262 default: 1263 obpvec->pv_halt(); /* What else? */ 1264 } 1265 1266 promops.po_revision = obpvec->pv_printrev; 1267 1268 promops.po_halt = obpvec->pv_halt; 1269 promops.po_reboot = obpvec->pv_reboot; 1270 promops.po_abort = obpvec->pv_abort; 1271 promops.po_setcontext = obpvec->pv_setctxt; 1272 promops.po_setcallback = obp_set_callback; 1273 promops.po_ticks = obp_ticks; 1274 promops.po_tickdata = obpvec->pv_ticks; 1275 1276 /* 1277 * Remove indirection through `pv_nodeops' while we're here. 1278 * Hopefully, the PROM has no need to change this pointer on the fly.. 1279 */ 1280 no = obpvec->pv_nodeops; 1281 promops.po_firstchild = no->no_child; 1282 promops.po_nextsibling = no->no_nextnode; 1283 promops.po_getproplen = no->no_proplen; 1284 /* XXX - silently discard getprop's `len' argument */ 1285 promops.po_getprop = (void *)no->no_getprop; 1286 promops.po_setprop = no->no_setprop; 1287 promops.po_nextprop = no->no_nextprop; 1288 1289 #ifndef _STANDALONE 1290 promops.po_node_to_devhandle = devhandle_from_obp; 1291 promops.po_devhandle_to_node = devhandle_to_obp; 1292 #endif /* _STANDALONE */ 1293 1294 /* 1295 * Next, deal with prom vector differences between versions. 1296 */ 1297 switch (promops.po_version) { 1298 case PROM_OBP_V0: 1299 promops.po_stdin = *obpvec->pv_stdin; 1300 promops.po_stdout = *obpvec->pv_stdout; 1301 promops.po_bootcookie = *obpvec->pv_v0bootargs; /* deref 1 lvl */ 1302 promops.po_bootpath = obp_v0_getbootpath; 1303 promops.po_bootfile = obp_v0_getbootfile; 1304 promops.po_bootargs = obp_v0_getbootargs; 1305 promops.po_putchar = obpvec->pv_putchar; 1306 promops.po_getchar = obpvec->pv_getchar; 1307 promops.po_peekchar = obpvec->pv_nbgetchar; 1308 promops.po_putstr = obpvec->pv_putstr; 1309 promops.po_open = obpvec->pv_v0devops.v0_open; 1310 promops.po_close = (void *)obpvec->pv_v0devops.v0_close; 1311 promops.po_read = obp_v0_read; 1312 promops.po_write = obp_v0_write; 1313 promops.po_interpret = obp_v0_fortheval; 1314 break; 1315 case PROM_OBP_V3: 1316 promops.po_cpustart = obpvec->pv_v3cpustart; 1317 promops.po_cpustop = obpvec->pv_v3cpustop; 1318 promops.po_cpuidle = obpvec->pv_v3cpuidle; 1319 promops.po_cpuresume = obpvec->pv_v3cpuresume; 1320 /*FALLTHROUGH*/ 1321 case PROM_OBP_V2: 1322 /* Deref stdio handles one level */ 1323 promops.po_stdin = *obpvec->pv_v2bootargs.v2_fd0; 1324 promops.po_stdout = *obpvec->pv_v2bootargs.v2_fd1; 1325 1326 promops.po_bootcookie = &obpvec->pv_v2bootargs; 1327 promops.po_bootpath = obp_v2_getbootpath; 1328 promops.po_bootfile = obp_v2_getbootfile; 1329 promops.po_bootargs = obp_v2_getbootargs; 1330 1331 promops.po_interpret = obpvec->pv_fortheval.v2_eval; 1332 1333 promops.po_putchar = obp_v2_putchar; 1334 promops.po_getchar = obp_v2_getchar; 1335 promops.po_peekchar = obp_v2_peekchar; 1336 promops.po_putstr = obp_v2_putstr; 1337 promops.po_open = obpvec->pv_v2devops.v2_open; 1338 promops.po_close = (void *)obpvec->pv_v2devops.v2_close; 1339 promops.po_read = obpvec->pv_v2devops.v2_read; 1340 promops.po_write = obpvec->pv_v2devops.v2_write; 1341 promops.po_seek = obp_v2_seek; 1342 promops.po_instance_to_package = obpvec->pv_v2devops.v2_fd_phandle; 1343 promops.po_finddevice = obp_v2_finddevice; 1344 1345 #ifndef _STANDALONE 1346 prom_printf("OBP version %d, revision %d.%d (plugin rev %x)\n", 1347 obpvec->pv_romvec_vers, 1348 obpvec->pv_printrev >> 16, obpvec->pv_printrev & 0xffff, 1349 obpvec->pv_plugin_vers); 1350 #endif 1351 break; 1352 } 1353 } 1354 1355 static inline void 1356 prom_init_opf(void) 1357 { 1358 int node; 1359 1360 promops.po_version = PROM_OPENFIRM; 1361 1362 /* 1363 * OpenFirmware ops are mostly straightforward. 1364 */ 1365 promops.po_halt = OF_exit; 1366 promops.po_reboot = OF_boot; 1367 promops.po_abort = OF_enter; 1368 promops.po_interpret = opf_interpret_simple; 1369 promops.po_setcallback = (void *)OF_set_callback; 1370 promops.po_ticks = OF_milliseconds; 1371 1372 promops.po_bootpath = opf_getbootpath; 1373 promops.po_bootfile = opf_getbootfile; 1374 promops.po_bootargs = opf_getbootargs; 1375 1376 promops.po_firstchild = OF_child; 1377 promops.po_nextsibling = OF_peer; 1378 promops.po_getproplen = OF_getproplen; 1379 promops.po_getprop = OF_getprop; 1380 promops.po_nextprop = opf_nextprop; 1381 promops.po_setprop = OF_setprop; 1382 1383 /* We can re-use OBP v2 emulation */ 1384 promops.po_putchar = obp_v2_putchar; 1385 promops.po_getchar = obp_v2_getchar; 1386 promops.po_peekchar = obp_v2_peekchar; 1387 promops.po_putstr = obp_v2_putstr; 1388 1389 promops.po_open = OF_open; 1390 promops.po_close = OF_close; 1391 promops.po_read = OF_read; 1392 promops.po_write = OF_write; 1393 promops.po_seek = OF_seek; 1394 promops.po_instance_to_package = opf_instance_to_package; 1395 promops.po_finddevice = opf_finddevice; 1396 1397 #ifndef _STANDALONE 1398 #ifdef __sparc64__ 1399 promops.po_node_to_devhandle = devhandle_from_of; 1400 promops.po_devhandle_to_node = devhandle_to_of; 1401 #else 1402 /* 1403 * For 32-bit SPARC, pretend this is OpenBoot for now. 1404 * The differences will be hidden behind the promops 1405 * anyway. We do this because this platform doesn't 1406 * pull in all of the OpenFirmware support code that 1407 * 64-bit SPARC does. 1408 */ 1409 promops.po_node_to_devhandle = devhandle_from_obp; 1410 promops.po_devhandle_to_node = devhandle_to_obp; 1411 #endif /* __sparc64__ */ 1412 #endif /* _STANDALONE */ 1413 1414 /* Retrieve and cache stdio handles */ 1415 node = findchosen(); 1416 OF_getprop(node, "stdin", &promops.po_stdin, sizeof(int)); 1417 OF_getprop(node, "stdout", &promops.po_stdout, sizeof(int)); 1418 1419 OF_init(); 1420 } 1421 1422 /* 1423 * Initialize our PROM operations vector. 1424 */ 1425 void 1426 prom_init(void) 1427 { 1428 #ifdef _STANDALONE 1429 int node; 1430 char *cp; 1431 #endif 1432 1433 if (CPU_ISSUN4) { 1434 prom_init_oldmon(); 1435 } else if (obpvec->pv_magic == OBP_MAGIC) { 1436 prom_init_obp(); 1437 } else { 1438 /* 1439 * Assume this is an Openfirm machine. 1440 */ 1441 prom_init_opf(); 1442 } 1443 1444 #ifdef _STANDALONE 1445 /* 1446 * Find out what type of machine we're running on. 1447 * 1448 * This process is actually started in srt0.S, which has discovered 1449 * the minimal set of machine specific parameters for the 1st-level 1450 * boot program (bootxx) to run. The page size has already been set 1451 * and the CPU type is either CPU_SUN4, CPU_SUN4C or CPU_SUN4M. 1452 */ 1453 1454 if (cputyp == CPU_SUN4 || cputyp == CPU_SUN4M) 1455 return; 1456 1457 /* 1458 * We have SUN4C, SUN4M or SUN4D. 1459 * Use the PROM `compatible' property to determine which. 1460 * Absence of the `compatible' property means `sun4c'. 1461 */ 1462 1463 node = prom_findroot(); 1464 cp = prom_getpropstring(node, "compatible"); 1465 if (*cp == '\0' || strcmp(cp, "sun4c") == 0) 1466 cputyp = CPU_SUN4C; 1467 else if (strcmp(cp, "sun4m") == 0) 1468 cputyp = CPU_SUN4M; 1469 else if (strcmp(cp, "sun4d") == 0) 1470 cputyp = CPU_SUN4D; 1471 else 1472 printf("Unknown CPU type (compatible=`%s')\n", cp); 1473 #endif /* _STANDALONE */ 1474 } 1475