1 /* 2 * libfdt - Flat Device Tree manipulation 3 * Copyright (C) 2006 David Gibson, IBM Corporation. 4 * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause 5 */ 6 #include "libfdt_env.h" 7 8 #ifndef USE_HOSTCC 9 #include <fdt.h> 10 #include <libfdt.h> 11 #else 12 #include "fdt_host.h" 13 #endif 14 15 #include "libfdt_internal.h" 16 17 static int _fdt_nodename_eq(const void *fdt, int offset, 18 const char *s, int len) 19 { 20 const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); 21 22 if (! p) 23 /* short match */ 24 return 0; 25 26 if (memcmp(p, s, len) != 0) 27 return 0; 28 29 if (p[len] == '\0') 30 return 1; 31 else if (!memchr(s, '@', len) && (p[len] == '@')) 32 return 1; 33 else 34 return 0; 35 } 36 37 const char *fdt_string(const void *fdt, int stroffset) 38 { 39 return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 40 } 41 42 static int _fdt_string_eq(const void *fdt, int stroffset, 43 const char *s, int len) 44 { 45 const char *p = fdt_string(fdt, stroffset); 46 47 return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0); 48 } 49 50 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 51 { 52 FDT_CHECK_HEADER(fdt); 53 *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); 54 *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); 55 return 0; 56 } 57 58 int fdt_num_mem_rsv(const void *fdt) 59 { 60 int i = 0; 61 62 while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) 63 i++; 64 return i; 65 } 66 67 static int _nextprop(const void *fdt, int offset) 68 { 69 uint32_t tag; 70 int nextoffset; 71 72 do { 73 tag = fdt_next_tag(fdt, offset, &nextoffset); 74 75 switch (tag) { 76 case FDT_END: 77 if (nextoffset >= 0) 78 return -FDT_ERR_BADSTRUCTURE; 79 else 80 return nextoffset; 81 82 case FDT_PROP: 83 return offset; 84 } 85 offset = nextoffset; 86 } while (tag == FDT_NOP); 87 88 return -FDT_ERR_NOTFOUND; 89 } 90 91 int fdt_subnode_offset_namelen(const void *fdt, int offset, 92 const char *name, int namelen) 93 { 94 int depth; 95 96 FDT_CHECK_HEADER(fdt); 97 98 for (depth = 0; 99 (offset >= 0) && (depth >= 0); 100 offset = fdt_next_node(fdt, offset, &depth)) 101 if ((depth == 1) 102 && _fdt_nodename_eq(fdt, offset, name, namelen)) 103 return offset; 104 105 if (depth < 0) 106 return -FDT_ERR_NOTFOUND; 107 return offset; /* error */ 108 } 109 110 int fdt_subnode_offset(const void *fdt, int parentoffset, 111 const char *name) 112 { 113 return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 114 } 115 116 int fdt_path_offset(const void *fdt, const char *path) 117 { 118 const char *end = path + strlen(path); 119 const char *p = path; 120 int offset = 0; 121 122 FDT_CHECK_HEADER(fdt); 123 124 /* see if we have an alias */ 125 if (*path != '/') { 126 const char *q = strchr(path, '/'); 127 128 if (!q) 129 q = end; 130 131 p = fdt_get_alias_namelen(fdt, p, q - p); 132 if (!p) 133 return -FDT_ERR_BADPATH; 134 offset = fdt_path_offset(fdt, p); 135 136 p = q; 137 } 138 139 while (*p) { 140 const char *q; 141 142 while (*p == '/') 143 p++; 144 if (! *p) 145 return offset; 146 q = strchr(p, '/'); 147 if (! q) 148 q = end; 149 150 offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 151 if (offset < 0) 152 return offset; 153 154 p = q; 155 } 156 157 return offset; 158 } 159 160 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 161 { 162 const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); 163 int err; 164 165 if (((err = fdt_check_header(fdt)) != 0) 166 || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) 167 goto fail; 168 169 if (len) 170 *len = strlen(nh->name); 171 172 return nh->name; 173 174 fail: 175 if (len) 176 *len = err; 177 return NULL; 178 } 179 180 int fdt_first_property_offset(const void *fdt, int nodeoffset) 181 { 182 int offset; 183 184 if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) 185 return offset; 186 187 return _nextprop(fdt, offset); 188 } 189 190 int fdt_next_property_offset(const void *fdt, int offset) 191 { 192 if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) 193 return offset; 194 195 return _nextprop(fdt, offset); 196 } 197 198 const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 199 int offset, 200 int *lenp) 201 { 202 int err; 203 const struct fdt_property *prop; 204 205 if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { 206 if (lenp) 207 *lenp = err; 208 return NULL; 209 } 210 211 prop = _fdt_offset_ptr(fdt, offset); 212 213 if (lenp) 214 *lenp = fdt32_to_cpu(prop->len); 215 216 return prop; 217 } 218 219 const struct fdt_property *fdt_get_property_namelen(const void *fdt, 220 int offset, 221 const char *name, 222 int namelen, int *lenp) 223 { 224 for (offset = fdt_first_property_offset(fdt, offset); 225 (offset >= 0); 226 (offset = fdt_next_property_offset(fdt, offset))) { 227 const struct fdt_property *prop; 228 229 if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { 230 offset = -FDT_ERR_INTERNAL; 231 break; 232 } 233 if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), 234 name, namelen)) 235 return prop; 236 } 237 238 if (lenp) 239 *lenp = offset; 240 return NULL; 241 } 242 243 const struct fdt_property *fdt_get_property(const void *fdt, 244 int nodeoffset, 245 const char *name, int *lenp) 246 { 247 return fdt_get_property_namelen(fdt, nodeoffset, name, 248 strlen(name), lenp); 249 } 250 251 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 252 const char *name, int namelen, int *lenp) 253 { 254 const struct fdt_property *prop; 255 256 prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); 257 if (! prop) 258 return NULL; 259 260 return prop->data; 261 } 262 263 const void *fdt_getprop_by_offset(const void *fdt, int offset, 264 const char **namep, int *lenp) 265 { 266 const struct fdt_property *prop; 267 268 prop = fdt_get_property_by_offset(fdt, offset, lenp); 269 if (!prop) 270 return NULL; 271 if (namep) 272 *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); 273 return prop->data; 274 } 275 276 const void *fdt_getprop(const void *fdt, int nodeoffset, 277 const char *name, int *lenp) 278 { 279 return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 280 } 281 282 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 283 { 284 const fdt32_t *php; 285 int len; 286 287 /* FIXME: This is a bit sub-optimal, since we potentially scan 288 * over all the properties twice. */ 289 php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 290 if (!php || (len != sizeof(*php))) { 291 php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 292 if (!php || (len != sizeof(*php))) 293 return 0; 294 } 295 296 return fdt32_to_cpu(*php); 297 } 298 299 const char *fdt_get_alias_namelen(const void *fdt, 300 const char *name, int namelen) 301 { 302 int aliasoffset; 303 304 aliasoffset = fdt_path_offset(fdt, "/aliases"); 305 if (aliasoffset < 0) 306 return NULL; 307 308 return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 309 } 310 311 const char *fdt_get_alias(const void *fdt, const char *name) 312 { 313 return fdt_get_alias_namelen(fdt, name, strlen(name)); 314 } 315 316 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 317 { 318 int pdepth = 0, p = 0; 319 int offset, depth, namelen; 320 const char *name; 321 322 FDT_CHECK_HEADER(fdt); 323 324 if (buflen < 2) 325 return -FDT_ERR_NOSPACE; 326 327 for (offset = 0, depth = 0; 328 (offset >= 0) && (offset <= nodeoffset); 329 offset = fdt_next_node(fdt, offset, &depth)) { 330 while (pdepth > depth) { 331 do { 332 p--; 333 } while (buf[p-1] != '/'); 334 pdepth--; 335 } 336 337 if (pdepth >= depth) { 338 name = fdt_get_name(fdt, offset, &namelen); 339 if (!name) 340 return namelen; 341 if ((p + namelen + 1) <= buflen) { 342 memcpy(buf + p, name, namelen); 343 p += namelen; 344 buf[p++] = '/'; 345 pdepth++; 346 } 347 } 348 349 if (offset == nodeoffset) { 350 if (pdepth < (depth + 1)) 351 return -FDT_ERR_NOSPACE; 352 353 if (p > 1) /* special case so that root path is "/", not "" */ 354 p--; 355 buf[p] = '\0'; 356 return 0; 357 } 358 } 359 360 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 361 return -FDT_ERR_BADOFFSET; 362 else if (offset == -FDT_ERR_BADOFFSET) 363 return -FDT_ERR_BADSTRUCTURE; 364 365 return offset; /* error from fdt_next_node() */ 366 } 367 368 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 369 int supernodedepth, int *nodedepth) 370 { 371 int offset, depth; 372 int supernodeoffset = -FDT_ERR_INTERNAL; 373 374 FDT_CHECK_HEADER(fdt); 375 376 if (supernodedepth < 0) 377 return -FDT_ERR_NOTFOUND; 378 379 for (offset = 0, depth = 0; 380 (offset >= 0) && (offset <= nodeoffset); 381 offset = fdt_next_node(fdt, offset, &depth)) { 382 if (depth == supernodedepth) 383 supernodeoffset = offset; 384 385 if (offset == nodeoffset) { 386 if (nodedepth) 387 *nodedepth = depth; 388 389 if (supernodedepth > depth) 390 return -FDT_ERR_NOTFOUND; 391 else 392 return supernodeoffset; 393 } 394 } 395 396 if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 397 return -FDT_ERR_BADOFFSET; 398 else if (offset == -FDT_ERR_BADOFFSET) 399 return -FDT_ERR_BADSTRUCTURE; 400 401 return offset; /* error from fdt_next_node() */ 402 } 403 404 int fdt_node_depth(const void *fdt, int nodeoffset) 405 { 406 int nodedepth; 407 int err; 408 409 err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 410 if (err) 411 return (err < 0) ? err : -FDT_ERR_INTERNAL; 412 return nodedepth; 413 } 414 415 int fdt_parent_offset(const void *fdt, int nodeoffset) 416 { 417 int nodedepth = fdt_node_depth(fdt, nodeoffset); 418 419 if (nodedepth < 0) 420 return nodedepth; 421 return fdt_supernode_atdepth_offset(fdt, nodeoffset, 422 nodedepth - 1, NULL); 423 } 424 425 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 426 const char *propname, 427 const void *propval, int proplen) 428 { 429 int offset; 430 const void *val; 431 int len; 432 433 FDT_CHECK_HEADER(fdt); 434 435 /* FIXME: The algorithm here is pretty horrible: we scan each 436 * property of a node in fdt_getprop(), then if that didn't 437 * find what we want, we scan over them again making our way 438 * to the next node. Still it's the easiest to implement 439 * approach; performance can come later. */ 440 for (offset = fdt_next_node(fdt, startoffset, NULL); 441 offset >= 0; 442 offset = fdt_next_node(fdt, offset, NULL)) { 443 val = fdt_getprop(fdt, offset, propname, &len); 444 if (val && (len == proplen) 445 && (memcmp(val, propval, len) == 0)) 446 return offset; 447 } 448 449 return offset; /* error from fdt_next_node() */ 450 } 451 452 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 453 { 454 int offset; 455 456 if ((phandle == 0) || (phandle == -1)) 457 return -FDT_ERR_BADPHANDLE; 458 459 FDT_CHECK_HEADER(fdt); 460 461 /* FIXME: The algorithm here is pretty horrible: we 462 * potentially scan each property of a node in 463 * fdt_get_phandle(), then if that didn't find what 464 * we want, we scan over them again making our way to the next 465 * node. Still it's the easiest to implement approach; 466 * performance can come later. */ 467 for (offset = fdt_next_node(fdt, -1, NULL); 468 offset >= 0; 469 offset = fdt_next_node(fdt, offset, NULL)) { 470 if (fdt_get_phandle(fdt, offset) == phandle) 471 return offset; 472 } 473 474 return offset; /* error from fdt_next_node() */ 475 } 476 477 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 478 { 479 int len = strlen(str); 480 const char *p; 481 482 while (listlen >= len) { 483 if (memcmp(str, strlist, len+1) == 0) 484 return 1; 485 p = memchr(strlist, '\0', listlen); 486 if (!p) 487 return 0; /* malformed strlist.. */ 488 listlen -= (p-strlist) + 1; 489 strlist = p + 1; 490 } 491 return 0; 492 } 493 494 int fdt_node_check_compatible(const void *fdt, int nodeoffset, 495 const char *compatible) 496 { 497 const void *prop; 498 int len; 499 500 prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 501 if (!prop) 502 return len; 503 if (fdt_stringlist_contains(prop, len, compatible)) 504 return 0; 505 else 506 return 1; 507 } 508 509 int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 510 const char *compatible) 511 { 512 int offset, err; 513 514 FDT_CHECK_HEADER(fdt); 515 516 /* FIXME: The algorithm here is pretty horrible: we scan each 517 * property of a node in fdt_node_check_compatible(), then if 518 * that didn't find what we want, we scan over them again 519 * making our way to the next node. Still it's the easiest to 520 * implement approach; performance can come later. */ 521 for (offset = fdt_next_node(fdt, startoffset, NULL); 522 offset >= 0; 523 offset = fdt_next_node(fdt, offset, NULL)) { 524 err = fdt_node_check_compatible(fdt, offset, compatible); 525 if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 526 return err; 527 else if (err == 0) 528 return offset; 529 } 530 531 return offset; /* error from fdt_next_node() */ 532 } 533