1 /* $NetBSD: ofw_subr.c,v 1.62 2025/10/04 01:12:15 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2021, 2025 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright 1998 31 * Digital Equipment Corporation. All rights reserved. 32 * 33 * This software is furnished under license and may be used and 34 * copied only in accordance with the following terms and conditions. 35 * Subject to these conditions, you may download, copy, install, 36 * use, modify and distribute this software in source and/or binary 37 * form. No title or ownership is transferred hereby. 38 * 39 * 1) Any source code used, modified or distributed must reproduce 40 * and retain this copyright notice and list of conditions as 41 * they appear in the source file. 42 * 43 * 2) No right is granted to use any trade name, trademark, or logo of 44 * Digital Equipment Corporation. Neither the "Digital Equipment 45 * Corporation" name nor any trademark or logo of Digital Equipment 46 * Corporation may be used to endorse or promote products derived 47 * from this software without the prior written permission of 48 * Digital Equipment Corporation. 49 * 50 * 3) This software is provided "AS-IS" and any express or implied 51 * warranties, including but not limited to, any implied warranties 52 * of merchantability, fitness for a particular purpose, or 53 * non-infringement are disclaimed. In no event shall DIGITAL be 54 * liable for any damages whatsoever, and in particular, DIGITAL 55 * shall not be liable for special, indirect, consequential, or 56 * incidental damages or damages for lost profits, loss of 57 * revenue or loss of use, whether such damages arise in contract, 58 * negligence, tort, under statute, in equity, at law or otherwise, 59 * even if advised of the possibility of such damage. 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: ofw_subr.c,v 1.62 2025/10/04 01:12:15 thorpej Exp $"); 64 65 #include <sys/param.h> 66 #include <sys/device.h> 67 #include <sys/kmem.h> 68 #include <sys/systm.h> 69 70 #include <sys/device_calls.h> 71 72 #include <dev/ofw/openfirm.h> 73 74 #define OFW_MAX_STACK_BUF_SIZE 256 75 #define OFW_PATH_BUF_SIZE 512 76 77 /* 78 * OpenFirmware device handle support. 79 */ 80 81 static device_call_t 82 of_devhandle_lookup_device_call(devhandle_t handle, const char *name, 83 devhandle_t *call_handlep) 84 { 85 __link_set_decl(of_device_calls, struct device_call_descriptor); 86 struct device_call_descriptor * const *desc; 87 88 __link_set_foreach(desc, of_device_calls) { 89 if (strcmp((*desc)->name, name) == 0) { 90 return (*desc)->call; 91 } 92 } 93 return NULL; 94 } 95 96 static const struct devhandle_impl of_devhandle_impl = { 97 .type = DEVHANDLE_TYPE_OF, 98 .lookup_device_call = of_devhandle_lookup_device_call, 99 }; 100 101 devhandle_t 102 devhandle_from_of(devhandle_t super_handle, int phandle) 103 { 104 devhandle_type_t super_type = devhandle_type(super_handle); 105 devhandle_t handle = { 0 }; 106 107 if (super_type == DEVHANDLE_TYPE_OF) { 108 handle.impl = super_handle.impl; 109 } else { 110 KASSERT(super_type == DEVHANDLE_TYPE_INVALID); 111 handle.impl = &of_devhandle_impl; 112 } 113 handle.integer = phandle; 114 115 return handle; 116 } 117 118 int 119 devhandle_to_of(devhandle_t const handle) 120 { 121 KASSERT(devhandle_type(handle) == DEVHANDLE_TYPE_OF); 122 123 return handle.integer; 124 } 125 126 static int 127 of_device_enumerate_children(device_t dev, devhandle_t call_handle, void *v) 128 { 129 struct device_enumerate_children_args *args = v; 130 int phandle = devhandle_to_of(call_handle); 131 int child; 132 133 for (child = OF_child(phandle); child != 0; child = OF_peer(child)) { 134 if (!args->callback(dev, devhandle_from_of(call_handle, child), 135 args->callback_arg)) { 136 break; 137 } 138 } 139 140 return 0; 141 } 142 OF_DEVICE_CALL_REGISTER(DEVICE_ENUMERATE_CHILDREN_STR, 143 of_device_enumerate_children) 144 145 static int 146 of_device_register(device_t dev, devhandle_t call_handle, void *v __unused) 147 { 148 int phandle = devhandle_to_of(call_handle); 149 150 /* 151 * The OpenFirmware on PowerMac10,1 at least is dodgy about 152 * NUL-terminating this... 153 */ 154 char *path = kmem_zalloc(OFW_PATH_BUF_SIZE, KM_SLEEP); 155 156 if (OF_package_to_path(phandle, path, OFW_PATH_BUF_SIZE) > 0) { 157 device_setprop_string(dev, "device-path", path); 158 } 159 160 kmem_free(path, OFW_PATH_BUF_SIZE); 161 return 0; 162 } 163 OF_DEVICE_CALL_REGISTER(DEVICE_REGISTER_STR, 164 of_device_register) 165 166 static bool 167 of_is_bool_prop(int node, const char *prop, bool *valp) 168 { 169 char propval[8] = { 0 }; 170 171 /* 172 * Alas, while this is the convention (mainly in the /options 173 * node), apparently it is not universal, so we'll be liberal 174 * in what we accept. 175 */ 176 #if 0 177 /* 178 * Naming convention is "propname?" is a boolean property 179 * with a "true" or "false" value. 180 */ 181 if (prop[strlen(prop) - 1] != '?') { 182 return false; 183 } 184 #endif 185 186 int propsize = OF_getprop(node, prop, propval, sizeof(propval)); 187 188 if (propsize >= 4 && memcmp(propval, "true", 4) == 0) { 189 *valp = true; 190 return true; 191 } 192 if (propsize >= 5 && memcmp(propval, "false", 5) == 0) { 193 *valp = false; 194 return true; 195 } 196 197 return false; 198 } 199 200 static int 201 of_device_get_property(device_t dev, devhandle_t call_handle, void *v) 202 { 203 struct device_get_property_args *args = v; 204 int node = devhandle_to_of(call_handle); 205 int propsize, rv, error = 0; 206 bool boolval = true; 207 prop_type_t proptype = PROP_TYPE_UNKNOWN; 208 209 propsize = OF_getproplen(node, args->prop); 210 if (propsize < 0) { 211 return ENOENT; 212 } 213 214 if (propsize == 0) { 215 goto done; 216 } 217 218 /* 219 * Check for the boolean property naming convention, and 220 * cache the value while we're at it. 221 */ 222 if (of_is_bool_prop(node, args->prop, &boolval)) { 223 proptype = PROP_TYPE_BOOL; 224 } 225 226 if (args->buf == NULL) { 227 goto done; 228 } 229 KASSERT(args->buflen != 0); 230 231 switch (args->reqtype) { 232 case PROP_TYPE_NUMBER: 233 KASSERT(args->buflen == sizeof(uint64_t)); 234 if (propsize == sizeof(uint32_t)) { 235 uint32_t val32; 236 237 if (OF_getprop(node, args->prop, &val32, 238 sizeof(val32)) != sizeof(val32)) { 239 error = EIO; 240 goto done; 241 } 242 val32 = be32toh(val32); 243 *(uint64_t *)args->buf = val32; 244 } else if (propsize == sizeof(uint64_t)) { 245 uint64_t val64; 246 247 if (OF_getprop(node, args->prop, &val64, 248 sizeof(val64)) != sizeof(val64)) { 249 error = EIO; 250 goto done; 251 } 252 val64 = be64toh(val64); 253 *(uint64_t *)args->buf = val64; 254 } else { 255 error = EFTYPE; 256 } 257 break; 258 259 case PROP_TYPE_STRING: 260 memset(args->buf, 0, args->buflen); 261 /* FALLTHROUGH */ 262 263 case PROP_TYPE_DATA: 264 if (args->buflen < propsize) { 265 error = EFBIG; 266 goto done; 267 } 268 rv = OF_getprop(node, args->prop, args->buf, args->buflen); 269 if (rv < 0) { 270 error = EIO; 271 } else if (args->buflen < (propsize = rv)) { 272 error = EFBIG; 273 } 274 break; 275 276 case PROP_TYPE_BOOL: 277 KASSERT(args->buflen == sizeof(bool)); 278 /* 279 * If we noticed the boolean property naming convention, 280 * use the cached value from earlier. 281 */ 282 if (proptype == PROP_TYPE_BOOL) { 283 *(bool *)args->buf = boolval; 284 } else { 285 error = EFTYPE; 286 } 287 break; 288 289 default: 290 error = EFTYPE; 291 break; 292 } 293 294 done: 295 args->propsize = propsize; 296 args->encoding = _BIG_ENDIAN; 297 args->type = proptype; 298 return error; 299 } 300 OF_DEVICE_CALL_REGISTER(DEVICE_GET_PROPERTY_STR, 301 of_device_get_property) 302 303 /* 304 * int of_decode_int(p) 305 * 306 * This routine converts OFW encoded-int datums 307 * into the integer format of the host machine. 308 * 309 * It is primarily used to convert integer properties 310 * returned by the OF_getprop routine. 311 * 312 * Arguments: 313 * p pointer to unsigned char array which is an 314 * OFW-encoded integer. 315 * 316 * Return Value: 317 * Decoded integer value of argument p. 318 * 319 * Side Effects: 320 * None. 321 */ 322 int 323 of_decode_int(const unsigned char *p) 324 { 325 unsigned int i = *p++ << 8; 326 i = (i + *p++) << 8; 327 i = (i + *p++) << 8; 328 return (i + *p); 329 } 330 331 /* 332 * int of_compatible(phandle, strings) 333 * 334 * This routine checks an OFW node's "compatible" entry to see if 335 * it matches any of the provided strings. 336 * 337 * of_compatible_match() is the preferred way to perform driver 338 * compatibility match. However, this routine that deals with 339 * only strings is useful in some situations and is provided for 340 * convenience. 341 * 342 * Arguments: 343 * phandle OFW phandle of device to be checked for 344 * compatibility. 345 * strings Array of containing expected "compatibility" 346 * property values, presence of any of which 347 * indicates compatibility. 348 * 349 * Return Value: 350 * 0 if none of the strings are found in phandle's "compatibility" 351 * property, or the reverse index of the matching string in the 352 * phandle's "compatibility" property plus 1. 353 * 354 * Side Effects: 355 * None. 356 */ 357 int 358 of_compatible(int phandle, const char * const *strings) 359 { 360 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; 361 const char *cp; 362 int proplen, match = 0; 363 364 proplen = OF_getproplen(phandle, "compatible"); 365 if (proplen <= 0) { 366 return 0; 367 } 368 369 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); 370 371 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { 372 goto out; 373 } 374 375 for (; (cp = *strings) != NULL; strings++) { 376 if ((match = strlist_match(prop, proplen, cp)) != 0) { 377 break; 378 } 379 } 380 381 out: 382 kmem_tmpbuf_free(prop, proplen, propbuf); 383 return match; 384 } 385 386 /* 387 * int of_compatible_match(phandle, compat_data) 388 * 389 * This routine searches an array of device_compatible_entry structures 390 * for a matching "compatible" entry matching the supplied OFW node, 391 * and returns a weighted match value corresponding to which string 392 * from the "compatible" property was matched, which more weight given 393 * to the first string than the last. 394 * 395 * It should be used when determining whether a driver can drive 396 * a particular device. 397 * 398 * Arguments: 399 * phandle OFW phandle of device to be checked for 400 * compatibility. 401 * compat_data Array of possible compat entry strings and 402 * associated metadata. The last entry in the 403 * list should have a "compat" of NULL to terminate 404 * the list. 405 * 406 * Return Value: 407 * 0 if none of the strings are found in phandle's "compatibility" 408 * property, or a positive number based on the reverse index of the 409 * matching string in the phandle's "compatibility" property, plus 1. 410 * 411 * Side Effects: 412 * None. 413 */ 414 int 415 of_compatible_match(int phandle, 416 const struct device_compatible_entry *compat_data) 417 { 418 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; 419 int proplen, match = 0; 420 421 proplen = OF_getproplen(phandle, "compatible"); 422 if (proplen <= 0) { 423 return 0; 424 } 425 426 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); 427 428 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { 429 goto out; 430 } 431 432 match = device_compatible_match_strlist(prop, proplen, compat_data); 433 434 out: 435 kmem_tmpbuf_free(prop, proplen, propbuf); 436 return match; 437 } 438 439 /* 440 * const struct device_compatible_entry *of_compatible_lookup(phandle, 441 * compat_data) 442 * 443 * This routine searches an array of device_compatible_entry structures 444 * for a "compatible" entry matching the supplied OFW node. 445 * 446 * Arguments: 447 * phandle OFW phandle of device to be checked for 448 * compatibility. 449 * compat_data Array of possible compat entry strings and 450 * associated metadata. The last entry in the 451 * list should have a "compat" of NULL to terminate 452 * the list. 453 * 454 * Return Value: 455 * The first matching compat_data entry in the array. If no matches 456 * are found, NULL is returned. 457 * 458 * Side Effects: 459 * None. 460 */ 461 const struct device_compatible_entry * 462 of_compatible_lookup(int phandle, 463 const struct device_compatible_entry *compat_data) 464 { 465 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; 466 const struct device_compatible_entry *match = NULL; 467 int proplen; 468 469 proplen = OF_getproplen(phandle, "compatible"); 470 if (proplen <= 0) { 471 return 0; 472 } 473 474 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); 475 476 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { 477 goto out; 478 } 479 480 match = device_compatible_lookup_strlist(prop, proplen, compat_data); 481 482 out: 483 kmem_tmpbuf_free(prop, proplen, propbuf); 484 return match; 485 } 486 487 /* 488 * int of_packagename(phandle, buf, bufsize) 489 * 490 * This routine places the last component of an OFW node's name 491 * into a user-provided buffer. 492 * 493 * It can be used during autoconfiguration to make printing of 494 * device names more informative. 495 * 496 * Arguments: 497 * phandle OFW phandle of device whose name name is 498 * desired. 499 * buf Buffer to contain device name, provided by 500 * caller. (For now, must be at least 4 501 * bytes long.) 502 * bufsize Length of buffer referenced by 'buf', in 503 * bytes. 504 * 505 * Return Value: 506 * -1 if the device path name could not be obtained or would 507 * not fit in the allocated temporary buffer, or zero otherwise 508 * (meaning that the leaf node name was successfully extracted). 509 * 510 * Side Effects: 511 * If the leaf node name was successfully extracted, 'buf' is 512 * filled in with at most 'bufsize' bytes of the leaf node 513 * name. If the leaf node was not successfully extracted, a 514 * somewhat meaningful string is placed in the buffer. In 515 * either case, the contents of 'buf' will be NUL-terminated. 516 */ 517 int 518 of_packagename(int phandle, char *buf, int bufsize) 519 { 520 char *pbuf; 521 const char *lastslash; 522 int l, rv; 523 524 pbuf = kmem_alloc(OFW_PATH_BUF_SIZE, KM_SLEEP); 525 l = OF_package_to_path(phandle, pbuf, OFW_PATH_BUF_SIZE); 526 527 /* check that we could get the name, and that it's not too long. */ 528 if (l < 0 || 529 (l == OFW_PATH_BUF_SIZE && pbuf[OFW_PATH_BUF_SIZE - 1] != '\0')) { 530 if (bufsize >= 25) 531 snprintf(buf, bufsize, "??? (phandle 0x%x)", phandle); 532 else if (bufsize >= 4) 533 strlcpy(buf, "???", bufsize); 534 else 535 panic("of_packagename: bufsize = %d is silly", 536 bufsize); 537 rv = -1; 538 } else { 539 pbuf[l] = '\0'; 540 lastslash = strrchr(pbuf, '/'); 541 strlcpy(buf, (lastslash == NULL) ? pbuf : (lastslash + 1), 542 bufsize); 543 rv = 0; 544 } 545 546 kmem_free(pbuf, OFW_PATH_BUF_SIZE); 547 return (rv); 548 } 549 550 /* 551 * Find the first child of a given node that matches name. Does not recurse. 552 */ 553 int 554 of_find_firstchild_byname(int node, const char *name) 555 { 556 char namex[32]; 557 int nn; 558 559 for (nn = OF_child(node); nn; nn = OF_peer(nn)) { 560 memset(namex, 0, sizeof(namex)); 561 if (OF_getprop(nn, "name", namex, sizeof(namex)) == -1) 562 continue; 563 if (strcmp(name, namex) == 0) 564 return nn; 565 } 566 return -1; 567 } 568 569 /* 570 * Find a child node that is compatible with str. Recurses, starting at node. 571 */ 572 int 573 of_find_bycompat(int node, const char *str) 574 { 575 const char * compatible[] = { str, NULL }; 576 int child, ret; 577 578 for (child = OF_child(node); child; child = OF_peer(child)) { 579 if (of_compatible(child, compatible)) 580 return child; 581 ret = of_find_bycompat(child, str); 582 if (ret != -1) 583 return ret; 584 } 585 586 return -1; 587 } 588 589 /* 590 * Find a give node by name. Recurses, and seems to walk upwards too. 591 */ 592 593 int 594 of_getnode_byname(int start, const char *target) 595 { 596 int node, next; 597 char name[64]; 598 599 if (start == 0) 600 start = OF_peer(0); 601 602 for (node = start; node; node = next) { 603 memset(name, 0, sizeof name); 604 OF_getprop(node, "name", name, sizeof name - 1); 605 if (strcmp(name, target) == 0) 606 break; 607 608 if ((next = OF_child(node)) != 0) 609 continue; 610 611 while (node) { 612 if ((next = OF_peer(node)) != 0) 613 break; 614 node = OF_parent(node); 615 } 616 } 617 618 /* XXX is this correct? */ 619 return node; 620 } 621 622 /* 623 * Create a uint32_t integer property from an OFW node property. 624 */ 625 626 bool 627 of_to_uint32_prop(prop_dictionary_t dict, int node, const char *ofname, 628 const char *propname) 629 { 630 uint32_t prop; 631 632 if (OF_getprop(node, ofname, &prop, sizeof(prop)) != sizeof(prop)) 633 return FALSE; 634 635 return(prop_dictionary_set_uint32(dict, propname, prop)); 636 } 637 638 /* 639 * Create a data property from an OFW node property. Max size of 256bytes. 640 */ 641 642 bool 643 of_to_dataprop(prop_dictionary_t dict, int node, const char *ofname, 644 const char *propname) 645 { 646 int len; 647 uint8_t prop[256]; 648 649 len = OF_getprop(node, ofname, prop, 256); 650 if (len < 1) 651 return FALSE; 652 653 return prop_dictionary_set_data(dict, propname, prop, len); 654 } 655 656 /* 657 * look at output-device, see if there's a Sun-typical video mode specifier as 658 * in screen:r1024x768x60 attached. If found copy it into *buffer, otherwise 659 * return NULL 660 */ 661 662 char * 663 of_get_mode_string(char *buffer, int len) 664 { 665 int options; 666 char *pos, output_device[256]; 667 668 /* 669 * finally, let's see if there's a video mode specified in 670 * output-device and pass it on so there's at least some way 671 * to program video modes 672 */ 673 options = OF_finddevice("/options"); 674 if ((options == 0) || (options == -1)) 675 return NULL; 676 if (OF_getprop(options, "output-device", output_device, 256) == 0) 677 return NULL; 678 679 /* find the mode string if there is one */ 680 pos = strstr(output_device, ":r"); 681 if (pos == NULL) 682 return NULL; 683 strncpy(buffer, pos + 2, len); 684 return buffer; 685 } 686 687 /* 688 * of_device_from_phandle -- 689 * 690 * Return a device_t associated with the specified phandle. 691 * 692 * This is expected to be used rarely, so we don't care if 693 * it's fast. Also, it can only find devices that have 694 * gone through of_device_register() (obviously). 695 */ 696 device_t 697 of_device_from_phandle(int phandle) 698 { 699 devhandle_t devhandle; 700 deviter_t di; 701 device_t dev; 702 703 for (dev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 704 dev != NULL; 705 dev = deviter_next(&di)) { 706 devhandle = device_handle(dev); 707 if (devhandle_type(devhandle) == DEVHANDLE_TYPE_OF) { 708 if (devhandle_to_of(devhandle) == phandle) { 709 /* Found it! */ 710 break; 711 } 712 } 713 } 714 deviter_release(&di); 715 return dev; 716 } 717 718 /* 719 * Returns true if the specified property is present. 720 */ 721 bool 722 of_hasprop(int node, const char *prop) 723 { 724 return OF_getproplen(node, prop) >= 0; 725 } 726 727 /* 728 * Get the value of a uint32 property, compensating for host byte order. 729 * Returns 0 on success, non-zero on failure. 730 */ 731 int 732 of_getprop_uint32(int node, const char *prop, uint32_t *val) 733 { 734 uint32_t v; 735 int len; 736 737 len = OF_getprop(node, prop, &v, sizeof(v)); 738 if (len != sizeof(v)) 739 return -1; 740 741 *val = be32toh(v); 742 return 0; 743 } 744 745 int 746 of_getprop_uint32_array(int node, const char *prop, uint32_t *array, int n) 747 { 748 uint32_t *v = array; 749 int len; 750 751 len = OF_getprop(node, prop, array, n * sizeof(*v)); 752 if (len < (int)(n * sizeof(*v))) 753 return -1; 754 755 for (; n > 0; n--) { 756 BE32TOH(*v); 757 v++; 758 } 759 760 return 0; 761 } 762 /* 763 * Get the value of a uint64 property, compensating for host byte order. 764 * Returns 0 on success, non-zero on failure. 765 */ 766 int 767 of_getprop_uint64(int node, const char *prop, uint64_t *val) 768 { 769 uint64_t v; 770 int len; 771 772 len = OF_getprop(node, prop, &v, sizeof(v)); 773 if (len != sizeof(v)) 774 return -1; 775 776 *val = be64toh(v); 777 return 0; 778 } 779