1/* $NetBSD: ofw_subr.c,v 1.60 2022/01/22 11:49:18 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2021 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.60 2022/01/22 11:49:18 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 81static device_call_t 82of_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 96static const struct devhandle_impl of_devhandle_impl = { 97 .type = DEVHANDLE_TYPE_OF, 98 .lookup_device_call = of_devhandle_lookup_device_call, 99}; 100 101devhandle_t 102devhandle_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 118int 119devhandle_to_of(devhandle_t const handle) 120{ 121 KASSERT(devhandle_type(handle) == DEVHANDLE_TYPE_OF); 122 123 return handle.integer; 124} 125 126static int 127of_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} 142OF_DEVICE_CALL_REGISTER(DEVICE_ENUMERATE_CHILDREN_STR, 143 of_device_enumerate_children) 144 145/* 146 * int of_decode_int(p) 147 * 148 * This routine converts OFW encoded-int datums 149 * into the integer format of the host machine. 150 * 151 * It is primarily used to convert integer properties 152 * returned by the OF_getprop routine. 153 * 154 * Arguments: 155 * p pointer to unsigned char array which is an 156 * OFW-encoded integer. 157 * 158 * Return Value: 159 * Decoded integer value of argument p. 160 * 161 * Side Effects: 162 * None. 163 */ 164int 165of_decode_int(const unsigned char *p) 166{ 167 unsigned int i = *p++ << 8; 168 i = (i + *p++) << 8; 169 i = (i + *p++) << 8; 170 return (i + *p); 171} 172 173/* 174 * int of_compatible(phandle, strings) 175 * 176 * This routine checks an OFW node's "compatible" entry to see if 177 * it matches any of the provided strings. 178 * 179 * of_compatible_match() is the preferred way to perform driver 180 * compatibility match. However, this routine that deals with 181 * only strings is useful in some situations and is provided for 182 * convenience. 183 * 184 * Arguments: 185 * phandle OFW phandle of device to be checked for 186 * compatibility. 187 * strings Array of containing expected "compatibility" 188 * property values, presence of any of which 189 * indicates compatibility. 190 * 191 * Return Value: 192 * 0 if none of the strings are found in phandle's "compatibility" 193 * property, or the reverse index of the matching string in the 194 * phandle's "compatibility" property plus 1. 195 * 196 * Side Effects: 197 * None. 198 */ 199int 200of_compatible(int phandle, const char * const *strings) 201{ 202 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; 203 const char *cp; 204 int proplen, match = 0; 205 206 proplen = OF_getproplen(phandle, "compatible"); 207 if (proplen <= 0) { 208 return 0; 209 } 210 211 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); 212 213 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { 214 goto out; 215 } 216 217 for (; (cp = *strings) != NULL; strings++) { 218 if ((match = strlist_match(prop, proplen, cp)) != 0) { 219 break; 220 } 221 } 222 223 out: 224 kmem_tmpbuf_free(prop, proplen, propbuf); 225 return match; 226} 227 228/* 229 * int of_compatible_match(phandle, compat_data) 230 * 231 * This routine searches an array of device_compatible_entry structures 232 * for a matching "compatible" entry matching the supplied OFW node, 233 * and returns a weighted match value corresponding to which string 234 * from the "compatible" property was matched, which more weight given 235 * to the first string than the last. 236 * 237 * It should be used when determining whether a driver can drive 238 * a particular device. 239 * 240 * Arguments: 241 * phandle OFW phandle of device to be checked for 242 * compatibility. 243 * compat_data Array of possible compat entry strings and 244 * associated metadata. The last entry in the 245 * list should have a "compat" of NULL to terminate 246 * the list. 247 * 248 * Return Value: 249 * 0 if none of the strings are found in phandle's "compatibility" 250 * property, or a positive number based on the reverse index of the 251 * matching string in the phandle's "compatibility" property, plus 1. 252 * 253 * Side Effects: 254 * None. 255 */ 256int 257of_compatible_match(int phandle, 258 const struct device_compatible_entry *compat_data) 259{ 260 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; 261 int proplen, match = 0; 262 263 proplen = OF_getproplen(phandle, "compatible"); 264 if (proplen <= 0) { 265 return 0; 266 } 267 268 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); 269 270 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { 271 goto out; 272 } 273 274 match = device_compatible_match_strlist(prop, proplen, compat_data); 275 276 out: 277 kmem_tmpbuf_free(prop, proplen, propbuf); 278 return match; 279} 280 281/* 282 * const struct device_compatible_entry *of_compatible_lookup(phandle, 283 * compat_data) 284 * 285 * This routine searches an array of device_compatible_entry structures 286 * for a "compatible" entry matching the supplied OFW node. 287 * 288 * Arguments: 289 * phandle OFW phandle of device to be checked for 290 * compatibility. 291 * compat_data Array of possible compat entry strings and 292 * associated metadata. The last entry in the 293 * list should have a "compat" of NULL to terminate 294 * the list. 295 * 296 * Return Value: 297 * The first matching compat_data entry in the array. If no matches 298 * are found, NULL is returned. 299 * 300 * Side Effects: 301 * None. 302 */ 303const struct device_compatible_entry * 304of_compatible_lookup(int phandle, 305 const struct device_compatible_entry *compat_data) 306{ 307 char *prop, propbuf[OFW_MAX_STACK_BUF_SIZE]; 308 const struct device_compatible_entry *match = NULL; 309 int proplen; 310 311 proplen = OF_getproplen(phandle, "compatible"); 312 if (proplen <= 0) { 313 return 0; 314 } 315 316 prop = kmem_tmpbuf_alloc(proplen, propbuf, sizeof(propbuf), KM_SLEEP); 317 318 if (OF_getprop(phandle, "compatible", prop, proplen) != proplen) { 319 goto out; 320 } 321 322 match = device_compatible_lookup_strlist(prop, proplen, compat_data); 323 324 out: 325 kmem_tmpbuf_free(prop, proplen, propbuf); 326 return match; 327} 328 329/* 330 * int of_packagename(phandle, buf, bufsize) 331 * 332 * This routine places the last component of an OFW node's name 333 * into a user-provided buffer. 334 * 335 * It can be used during autoconfiguration to make printing of 336 * device names more informative. 337 * 338 * Arguments: 339 * phandle OFW phandle of device whose name name is 340 * desired. 341 * buf Buffer to contain device name, provided by 342 * caller. (For now, must be at least 4 343 * bytes long.) 344 * bufsize Length of buffer referenced by 'buf', in 345 * bytes. 346 * 347 * Return Value: 348 * -1 if the device path name could not be obtained or would 349 * not fit in the allocated temporary buffer, or zero otherwise 350 * (meaning that the leaf node name was successfully extracted). 351 * 352 * Side Effects: 353 * If the leaf node name was successfully extracted, 'buf' is 354 * filled in with at most 'bufsize' bytes of the leaf node 355 * name. If the leaf node was not successfully extracted, a 356 * somewhat meaningful string is placed in the buffer. In 357 * either case, the contents of 'buf' will be NUL-terminated. 358 */ 359int 360of_packagename(int phandle, char *buf, int bufsize) 361{ 362 char *pbuf; 363 const char *lastslash; 364 int l, rv; 365 366 pbuf = kmem_alloc(OFW_PATH_BUF_SIZE, KM_SLEEP); 367 l = OF_package_to_path(phandle, pbuf, OFW_PATH_BUF_SIZE); 368 369 /* check that we could get the name, and that it's not too long. */ 370 if (l < 0 || 371 (l == OFW_PATH_BUF_SIZE && pbuf[OFW_PATH_BUF_SIZE - 1] != '\0')) { 372 if (bufsize >= 25) 373 snprintf(buf, bufsize, "??? (phandle 0x%x)", phandle); 374 else if (bufsize >= 4) 375 strlcpy(buf, "???", bufsize); 376 else 377 panic("of_packagename: bufsize = %d is silly", 378 bufsize); 379 rv = -1; 380 } else { 381 pbuf[l] = '\0'; 382 lastslash = strrchr(pbuf, '/'); 383 strlcpy(buf, (lastslash == NULL) ? pbuf : (lastslash + 1), 384 bufsize); 385 rv = 0; 386 } 387 388 kmem_free(pbuf, OFW_PATH_BUF_SIZE); 389 return (rv); 390} 391 392/* 393 * Find the first child of a given node that matches name. Does not recurse. 394 */ 395int 396of_find_firstchild_byname(int node, const char *name) 397{ 398 char namex[32]; 399 int nn; 400 401 for (nn = OF_child(node); nn; nn = OF_peer(nn)) { 402 memset(namex, 0, sizeof(namex)); 403 if (OF_getprop(nn, "name", namex, sizeof(namex)) == -1) 404 continue; 405 if (strcmp(name, namex) == 0) 406 return nn; 407 } 408 return -1; 409} 410 411/* 412 * Find a child node that is compatible with str. Recurses, starting at node. 413 */ 414int 415of_find_bycompat(int node, const char *str) 416{ 417 const char * compatible[] = { str, NULL }; 418 int child, ret; 419 420 for (child = OF_child(node); child; child = OF_peer(child)) { 421 if (of_compatible(child, compatible)) 422 return child; 423 ret = of_find_bycompat(child, str); 424 if (ret != -1) 425 return ret; 426 } 427 428 return -1; 429} 430 431/* 432 * Find a give node by name. Recurses, and seems to walk upwards too. 433 */ 434 435int 436of_getnode_byname(int start, const char *target) 437{ 438 int node, next; 439 char name[64]; 440 441 if (start == 0) 442 start = OF_peer(0); 443 444 for (node = start; node; node = next) { 445 memset(name, 0, sizeof name); 446 OF_getprop(node, "name", name, sizeof name - 1); 447 if (strcmp(name, target) == 0) 448 break; 449 450 if ((next = OF_child(node)) != 0) 451 continue; 452 453 while (node) { 454 if ((next = OF_peer(node)) != 0) 455 break; 456 node = OF_parent(node); 457 } 458 } 459 460 /* XXX is this correct? */ 461 return node; 462} 463 464/* 465 * Create a uint32_t integer property from an OFW node property. 466 */ 467 468bool 469of_to_uint32_prop(prop_dictionary_t dict, int node, const char *ofname, 470 const char *propname) 471{ 472 uint32_t prop; 473 474 if (OF_getprop(node, ofname, &prop, sizeof(prop)) != sizeof(prop)) 475 return FALSE; 476 477 return(prop_dictionary_set_uint32(dict, propname, prop)); 478} 479 480/* 481 * Create a data property from an OFW node property. Max size of 256bytes. 482 */ 483 484bool 485of_to_dataprop(prop_dictionary_t dict, int node, const char *ofname, 486 const char *propname) 487{ 488 int len; 489 uint8_t prop[256]; 490 491 len = OF_getprop(node, ofname, prop, 256); 492 if (len < 1) 493 return FALSE; 494 495 return prop_dictionary_set_data(dict, propname, prop, len); 496} 497 498/* 499 * look at output-device, see if there's a Sun-typical video mode specifier as 500 * in screen:r1024x768x60 attached. If found copy it into *buffer, otherwise 501 * return NULL 502 */ 503 504char * 505of_get_mode_string(char *buffer, int len) 506{ 507 int options; 508 char *pos, output_device[256]; 509 510 /* 511 * finally, let's see if there's a video mode specified in 512 * output-device and pass it on so there's at least some way 513 * to program video modes 514 */ 515 options = OF_finddevice("/options"); 516 if ((options == 0) || (options == -1)) 517 return NULL; 518 if (OF_getprop(options, "output-device", output_device, 256) == 0) 519 return NULL; 520 521 /* find the mode string if there is one */ 522 pos = strstr(output_device, ":r"); 523 if (pos == NULL) 524 return NULL; 525 strncpy(buffer, pos + 2, len); 526 return buffer; 527} 528 529/* 530 * of_device_from_phandle -- 531 * 532 * Return a device_t associated with the specified phandle. 533 * 534 * This is expected to be used rarely, so we don't care if 535 * it's fast. Also, it can only find devices that have 536 * gone through of_device_register() (obviously). 537 */ 538device_t 539of_device_from_phandle(int phandle) 540{ 541 devhandle_t devhandle; 542 deviter_t di; 543 device_t dev; 544 545 for (dev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 546 dev != NULL; 547 dev = deviter_next(&di)) { 548 devhandle = device_handle(dev); 549 if (devhandle_type(devhandle) == DEVHANDLE_TYPE_OF) { 550 if (devhandle_to_of(devhandle) == phandle) { 551 /* Found it! */ 552 break; 553 } 554 } 555 } 556 deviter_release(&di); 557 return dev; 558} 559 560/* 561 * Returns true if the specified property is present. 562 */ 563bool 564of_hasprop(int node, const char *prop) 565{ 566 return OF_getproplen(node, prop) >= 0; 567} 568 569/* 570 * Get the value of a uint32 property, compensating for host byte order. 571 * Returns 0 on success, non-zero on failure. 572 */ 573int 574of_getprop_uint32(int node, const char *prop, uint32_t *val) 575{ 576 uint32_t v; 577 int len; 578 579 len = OF_getprop(node, prop, &v, sizeof(v)); 580 if (len != sizeof(v)) 581 return -1; 582 583 *val = be32toh(v); 584 return 0; 585} 586 587int 588of_getprop_uint32_array(int node, const char *prop, uint32_t *array, int n) 589{ 590 uint32_t *v = array; 591 int len; 592 593 len = OF_getprop(node, prop, array, n * sizeof(*v)); 594 if (len < (int)(n * sizeof(*v))) 595 return -1; 596 597 for (; n > 0; n--) { 598 BE32TOH(*v); 599 v++; 600 } 601 602 return 0; 603} 604/* 605 * Get the value of a uint64 property, compensating for host byte order. 606 * Returns 0 on success, non-zero on failure. 607 */ 608int 609of_getprop_uint64(int node, const char *prop, uint64_t *val) 610{ 611 uint64_t v; 612 int len; 613 614 len = OF_getprop(node, prop, &v, sizeof(v)); 615 if (len != sizeof(v)) 616 return -1; 617 618 *val = be64toh(v); 619 return 0; 620} 621