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