1 // SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0+) 2 /* 3 * libfdt - Flat Device Tree manipulation 4 * Copyright (C) 2006 David Gibson, IBM Corporation. 5 * 6 * libfdt is dual licensed: you can use it either under the terms of 7 * the GPL, or the BSD license, at your option. 8 * 9 * a) This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation; either version 2 of the 12 * License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public 20 * License along with this library; if not, write to the Free 21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 22 * MA 02110-1301 USA 23 * 24 * Alternatively, 25 * 26 * b) Redistribution and use in source and binary forms, with or 27 * without modification, are permitted provided that the following 28 * conditions are met: 29 * 30 * 1. Redistributions of source code must retain the above 31 * copyright notice, this list of conditions and the following 32 * disclaimer. 33 * 2. Redistributions in binary form must reproduce the above 34 * copyright notice, this list of conditions and the following 35 * disclaimer in the documentation and/or other materials 36 * provided with the distribution. 37 * 38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 39 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 40 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 41 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 42 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 43 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 48 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 49 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 50 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 */ 52 #include "libfdt_env.h" 53 54 #include <fdt.h> 55 #include <libfdt.h> 56 57 #include "libfdt_internal.h" 58 59 static int _fdt_nodename_eq(const void *fdt, int offset, 60 const char *s, int len) 61 { 62 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 63 64 if (! p) 65 /* short match */ 66 return 0; 67 68 if (memcmp(p, s, len) != 0) 69 return 0; 70 71 if (p[len] == '\0') 72 return 1; 73 else if (!memchr(s, '@', len) && (p[len] == '@')) 74 return 1; 75 else 76 return 0; 77 } 78 79 const char *fdt_string(const void *fdt, int stroffset) 80 { 81 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 82 } 83 84 static int _fdt_string_eq(const void *fdt, int stroffset, 85 const char *s, int len) 86 { 87 const char *p = fdt_string(fdt, stroffset); 88 89 return (strlen(p) == len) && (memcmp(p, s, len) == 0); 90 } 91 92 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 93 { 94 FDT_CHECK_HEADER(fdt); 95 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 96 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 97 return 0; 98 } 99 100 int fdt_num_mem_rsv(const void *fdt) 101 { 102 int i = 0; 103 104 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 105 i++; 106 return i; 107 } 108 109 static int _nextprop(const void *fdt, int offset) 110 { 111 uint32_t tag; 112 int nextoffset; 113 114 do { 115 tag = fdt_next_tag(fdt, offset, &nextoffset); 116 117 switch (tag) { 118 case FDT_END: 119 if (nextoffset >= 0) 120 return -FDT_ERR_BADSTRUCTURE; 121 else 122 return nextoffset; 123 124 case FDT_PROP: 125 return offset; 126 } 127 offset = nextoffset; 128 } while (tag == FDT_NOP); 129 130 return -FDT_ERR_NOTFOUND; 131 } 132 133 int fdt_subnode_offset_namelen(const void *fdt, int offset, 134 const char *name, int namelen) 135 { 136 int depth; 137 138 FDT_CHECK_HEADER(fdt); 139 140 for (depth = 0; 141 (offset >= 0) && (depth >= 0); 142 offset = fdt_next_node(fdt, offset, &depth)) 143 if ((depth == 1) 144 && _fdt_nodename_eq(fdt, offset, name, namelen)) 145 return offset; 146 147 if (depth < 0) 148 return -FDT_ERR_NOTFOUND; 149 return offset; /* error */ 150 } 151 152 int fdt_subnode_offset(const void *fdt, int parentoffset, 153 const char *name) 154 { 155 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 156 } 157 158 int fdt_path_offset(const void *fdt, const char *path) 159 { 160 const char *end = path + strlen(path); 161 const char *p = path; 162 int offset = 0; 163 164 FDT_CHECK_HEADER(fdt); 165 166 /* see if we have an alias */ 167 if (*path != '/') { 168 const char *q = strchr(path, '/'); 169 170 if (!q) 171 q = end; 172 173 p = fdt_get_alias_namelen(fdt, p, q - p); 174 if (!p) 175 return -FDT_ERR_BADPATH; 176 offset = fdt_path_offset(fdt, p); 177 178 p = q; 179 } 180 181 while (*p) { 182 const char *q; 183 184 while (*p == '/') 185 p++; 186 if (! *p) 187 return offset; 188 q = strchr(p, '/'); 189 if (! q) 190 q = end; 191 192 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 193 if (offset < 0) 194 return offset; 195 196 p = q; 197 } 198 199 return offset; 200 } 201 202 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 203 { 204 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 205 int err; 206 207 if (((err = fdt_check_header(fdt)) != 0) 208 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 209 goto fail; 210 211 if (len) 212 *len = strlen(nh->name); 213 214 return nh->name; 215 216 fail: 217 if (len) 218 *len = err; 219 return NULL; 220 } 221 222 int fdt_first_property_offset(const void *fdt, int nodeoffset) 223 { 224 int offset; 225 226 if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 227 return offset; 228 229 return _nextprop(fdt, offset); 230 } 231 232 int fdt_next_property_offset(const void *fdt, int offset) 233 { 234 if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 235 return offset; 236 237 return _nextprop(fdt, offset); 238 } 239 240 const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 241 int offset, 242 int *lenp) 243 { 244 int err; 245 const struct fdt_property *prop; 246 247 if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 248 if (lenp) 249 *lenp = err; 250 return NULL; 251 } 252 253 prop = _fdt_offset_ptr(fdt, offset); 254 255 if (lenp) 256 *lenp = fdt32_to_cpu(prop->len); 257 258 return prop; 259 } 260 261 const struct fdt_property *fdt_get_property_namelen(const void *fdt, 262 int offset, 263 const char *name, 264 int namelen, int *lenp) 265 { 266 for (offset = fdt_first_property_offset(fdt, offset); 267 (offset >= 0); 268 (offset = fdt_next_property_offset(fdt, offset))) { 269 const struct fdt_property *prop; 270 271 if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 272 offset = -FDT_ERR_INTERNAL; 273 break; 274 } 275 if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 276 name, namelen)) 277 return prop; 278 } 279 280 if (lenp) 281 *lenp = offset; 282 return NULL; 283 } 284 285 const struct fdt_property *fdt_get_property(const void *fdt, 286 int nodeoffset, 287 const char *name, int *lenp) 288 { 289 return fdt_get_property_namelen(fdt, nodeoffset, name, 290 strlen(name), lenp); 291 } 292 293 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 294 const char *name, int namelen, int *lenp) 295 { 296 const struct fdt_property *prop; 297 298 prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 299 if (! prop) 300 return NULL; 301 302 return prop->data; 303 } 304 305 const void *fdt_getprop_by_offset(const void *fdt, int offset, 306 const char **namep, int *lenp) 307 { 308 const struct fdt_property *prop; 309 310 prop = fdt_get_property_by_offset(fdt, offset, lenp); 311 if (!prop) 312 return NULL; 313 if (namep) 314 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 315 return prop->data; 316 } 317 318 const void *fdt_getprop(const void *fdt, int nodeoffset, 319 const char *name, int *lenp) 320 { 321 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 322 } 323 324 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 325 { 326 const fdt32_t *php; 327 int len; 328 329 /* FIXME: This is a bit sub-optimal, since we potentially scan 330 * over all the properties twice. */ 331 php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 332 if (!php || (len != sizeof(*php))) { 333 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 334 if (!php || (len != sizeof(*php))) 335 return 0; 336 } 337 338 return fdt32_to_cpu(*php); 339 } 340 341 const char *fdt_get_alias_namelen(const void *fdt, 342 const char *name, int namelen) 343 { 344 int aliasoffset; 345 346 aliasoffset = fdt_path_offset(fdt, "/aliases"); 347 if (aliasoffset < 0) 348 return NULL; 349 350 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 351 } 352 353 const char *fdt_get_alias(const void *fdt, const char *name) 354 { 355 return fdt_get_alias_namelen(fdt, name, strlen(name)); 356 } 357 358 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 359 { 360 int pdepth = 0, p = 0; 361 int offset, depth, namelen; 362 const char *name; 363 364 FDT_CHECK_HEADER(fdt); 365 366 if (buflen < 2) 367 return -FDT_ERR_NOSPACE; 368 369 for (offset = 0, depth = 0; 370 (offset >= 0) && (offset <= nodeoffset); 371 offset = fdt_next_node(fdt, offset, &depth)) { 372 while (pdepth > depth) { 373 do { 374 p--; 375 } while (buf[p-1] != '/'); 376 pdepth--; 377 } 378 379 if (pdepth >= depth) { 380 name = fdt_get_name(fdt, offset, &namelen); 381 if (!name) 382 return namelen; 383 if ((p + namelen + 1) <= buflen) { 384 memcpy(buf + p, name, namelen); 385 p += namelen; 386 buf[p++] = '/'; 387 pdepth++; 388 } 389 } 390 391 if (offset == nodeoffset) { 392 if (pdepth < (depth + 1)) 393 return -FDT_ERR_NOSPACE; 394 395 if (p > 1) /* special case so that root path is "/", not "" */ 396 p--; 397 buf[p] = '\0'; 398 return 0; 399 } 400 } 401 402 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 403 return -FDT_ERR_BADOFFSET; 404 else if (offset == -FDT_ERR_BADOFFSET) 405 return -FDT_ERR_BADSTRUCTURE; 406 407 return offset; /* error from fdt_next_node() */ 408 } 409 410 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 411 int supernodedepth, int *nodedepth) 412 { 413 int offset, depth; 414 int supernodeoffset = -FDT_ERR_INTERNAL; 415 416 FDT_CHECK_HEADER(fdt); 417 418 if (supernodedepth < 0) 419 return -FDT_ERR_NOTFOUND; 420 421 for (offset = 0, depth = 0; 422 (offset >= 0) && (offset <= nodeoffset); 423 offset = fdt_next_node(fdt, offset, &depth)) { 424 if (depth == supernodedepth) 425 supernodeoffset = offset; 426 427 if (offset == nodeoffset) { 428 if (nodedepth) 429 *nodedepth = depth; 430 431 if (supernodedepth > depth) 432 return -FDT_ERR_NOTFOUND; 433 else 434 return supernodeoffset; 435 } 436 } 437 438 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 439 return -FDT_ERR_BADOFFSET; 440 else if (offset == -FDT_ERR_BADOFFSET) 441 return -FDT_ERR_BADSTRUCTURE; 442 443 return offset; /* error from fdt_next_node() */ 444 } 445 446 int fdt_node_depth(const void *fdt, int nodeoffset) 447 { 448 int nodedepth; 449 int err; 450 451 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 452 if (err) 453 return (err < 0) ? err : -FDT_ERR_INTERNAL; 454 return nodedepth; 455 } 456 457 int fdt_parent_offset(const void *fdt, int nodeoffset) 458 { 459 int nodedepth = fdt_node_depth(fdt, nodeoffset); 460 461 if (nodedepth < 0) 462 return nodedepth; 463 return fdt_supernode_atdepth_offset(fdt, nodeoffset, 464 nodedepth - 1, NULL); 465 } 466 467 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 468 const char *propname, 469 const void *propval, int proplen) 470 { 471 int offset; 472 const void *val; 473 int len; 474 475 FDT_CHECK_HEADER(fdt); 476 477 /* FIXME: The algorithm here is pretty horrible: we scan each 478 * property of a node in fdt_getprop(), then if that didn't 479 * find what we want, we scan over them again making our way 480 * to the next node. Still it's the easiest to implement 481 * approach; performance can come later. */ 482 for (offset = fdt_next_node(fdt, startoffset, NULL); 483 offset >= 0; 484 offset = fdt_next_node(fdt, offset, NULL)) { 485 val = fdt_getprop(fdt, offset, propname, &len); 486 if (val && (len == proplen) 487 && (memcmp(val, propval, len) == 0)) 488 return offset; 489 } 490 491 return offset; /* error from fdt_next_node() */ 492 } 493 494 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 495 { 496 int offset; 497 498 if ((phandle == 0) || (phandle == -1)) 499 return -FDT_ERR_BADPHANDLE; 500 501 FDT_CHECK_HEADER(fdt); 502 503 /* FIXME: The algorithm here is pretty horrible: we 504 * potentially scan each property of a node in 505 * fdt_get_phandle(), then if that didn't find what 506 * we want, we scan over them again making our way to the next 507 * node. Still it's the easiest to implement approach; 508 * performance can come later. */ 509 for (offset = fdt_next_node(fdt, -1, NULL); 510 offset >= 0; 511 offset = fdt_next_node(fdt, offset, NULL)) { 512 if (fdt_get_phandle(fdt, offset) == phandle) 513 return offset; 514 } 515 516 return offset; /* error from fdt_next_node() */ 517 } 518 519 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 520 { 521 int len = strlen(str); 522 const char *p; 523 524 while (listlen >= len) { 525 if (memcmp(str, strlist, len+1) == 0) 526 return 1; 527 p = memchr(strlist, '\0', listlen); 528 if (!p) 529 return 0; /* malformed strlist.. */ 530 listlen -= (p-strlist) + 1; 531 strlist = p + 1; 532 } 533 return 0; 534 } 535 536 int fdt_node_check_compatible(const void *fdt, int nodeoffset, 537 const char *compatible) 538 { 539 const void *prop; 540 int len; 541 542 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 543 if (!prop) 544 return len; 545 if (fdt_stringlist_contains(prop, len, compatible)) 546 return 0; 547 else 548 return 1; 549 } 550 551 int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 552 const char *compatible) 553 { 554 int offset, err; 555 556 FDT_CHECK_HEADER(fdt); 557 558 /* FIXME: The algorithm here is pretty horrible: we scan each 559 * property of a node in fdt_node_check_compatible(), then if 560 * that didn't find what we want, we scan over them again 561 * making our way to the next node. Still it's the easiest to 562 * implement approach; performance can come later. */ 563 for (offset = fdt_next_node(fdt, startoffset, NULL); 564 offset >= 0; 565 offset = fdt_next_node(fdt, offset, NULL)) { 566 err = fdt_node_check_compatible(fdt, offset, compatible); 567 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 568 return err; 569 else if (err == 0) 570 return offset; 571 } 572 573 return offset; /* error from fdt_next_node() */ 574 } 575