1*91f16700Schasinglulu // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 2*91f16700Schasinglulu /* 3*91f16700Schasinglulu * libfdt - Flat Device Tree manipulation 4*91f16700Schasinglulu * Copyright (C) 2006 David Gibson, IBM Corporation. 5*91f16700Schasinglulu */ 6*91f16700Schasinglulu #include "libfdt_env.h" 7*91f16700Schasinglulu 8*91f16700Schasinglulu #include <fdt.h> 9*91f16700Schasinglulu #include <libfdt.h> 10*91f16700Schasinglulu 11*91f16700Schasinglulu #include "libfdt_internal.h" 12*91f16700Schasinglulu 13*91f16700Schasinglulu static int fdt_nodename_eq_(const void *fdt, int offset, 14*91f16700Schasinglulu const char *s, int len) 15*91f16700Schasinglulu { 16*91f16700Schasinglulu int olen; 17*91f16700Schasinglulu const char *p = fdt_get_name(fdt, offset, &olen); 18*91f16700Schasinglulu 19*91f16700Schasinglulu if (!p || olen < len) 20*91f16700Schasinglulu /* short match */ 21*91f16700Schasinglulu return 0; 22*91f16700Schasinglulu 23*91f16700Schasinglulu if (memcmp(p, s, len) != 0) 24*91f16700Schasinglulu return 0; 25*91f16700Schasinglulu 26*91f16700Schasinglulu if (p[len] == '\0') 27*91f16700Schasinglulu return 1; 28*91f16700Schasinglulu else if (!memchr(s, '@', len) && (p[len] == '@')) 29*91f16700Schasinglulu return 1; 30*91f16700Schasinglulu else 31*91f16700Schasinglulu return 0; 32*91f16700Schasinglulu } 33*91f16700Schasinglulu 34*91f16700Schasinglulu const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) 35*91f16700Schasinglulu { 36*91f16700Schasinglulu int32_t totalsize; 37*91f16700Schasinglulu uint32_t absoffset; 38*91f16700Schasinglulu size_t len; 39*91f16700Schasinglulu int err; 40*91f16700Schasinglulu const char *s, *n; 41*91f16700Schasinglulu 42*91f16700Schasinglulu if (can_assume(VALID_INPUT)) { 43*91f16700Schasinglulu s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; 44*91f16700Schasinglulu 45*91f16700Schasinglulu if (lenp) 46*91f16700Schasinglulu *lenp = strlen(s); 47*91f16700Schasinglulu return s; 48*91f16700Schasinglulu } 49*91f16700Schasinglulu totalsize = fdt_ro_probe_(fdt); 50*91f16700Schasinglulu err = totalsize; 51*91f16700Schasinglulu if (totalsize < 0) 52*91f16700Schasinglulu goto fail; 53*91f16700Schasinglulu 54*91f16700Schasinglulu err = -FDT_ERR_BADOFFSET; 55*91f16700Schasinglulu absoffset = stroffset + fdt_off_dt_strings(fdt); 56*91f16700Schasinglulu if (absoffset >= (unsigned)totalsize) 57*91f16700Schasinglulu goto fail; 58*91f16700Schasinglulu len = totalsize - absoffset; 59*91f16700Schasinglulu 60*91f16700Schasinglulu if (fdt_magic(fdt) == FDT_MAGIC) { 61*91f16700Schasinglulu if (stroffset < 0) 62*91f16700Schasinglulu goto fail; 63*91f16700Schasinglulu if (can_assume(LATEST) || fdt_version(fdt) >= 17) { 64*91f16700Schasinglulu if ((unsigned)stroffset >= fdt_size_dt_strings(fdt)) 65*91f16700Schasinglulu goto fail; 66*91f16700Schasinglulu if ((fdt_size_dt_strings(fdt) - stroffset) < len) 67*91f16700Schasinglulu len = fdt_size_dt_strings(fdt) - stroffset; 68*91f16700Schasinglulu } 69*91f16700Schasinglulu } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 70*91f16700Schasinglulu unsigned int sw_stroffset = -stroffset; 71*91f16700Schasinglulu 72*91f16700Schasinglulu if ((stroffset >= 0) || 73*91f16700Schasinglulu (sw_stroffset > fdt_size_dt_strings(fdt))) 74*91f16700Schasinglulu goto fail; 75*91f16700Schasinglulu if (sw_stroffset < len) 76*91f16700Schasinglulu len = sw_stroffset; 77*91f16700Schasinglulu } else { 78*91f16700Schasinglulu err = -FDT_ERR_INTERNAL; 79*91f16700Schasinglulu goto fail; 80*91f16700Schasinglulu } 81*91f16700Schasinglulu 82*91f16700Schasinglulu s = (const char *)fdt + absoffset; 83*91f16700Schasinglulu n = memchr(s, '\0', len); 84*91f16700Schasinglulu if (!n) { 85*91f16700Schasinglulu /* missing terminating NULL */ 86*91f16700Schasinglulu err = -FDT_ERR_TRUNCATED; 87*91f16700Schasinglulu goto fail; 88*91f16700Schasinglulu } 89*91f16700Schasinglulu 90*91f16700Schasinglulu if (lenp) 91*91f16700Schasinglulu *lenp = n - s; 92*91f16700Schasinglulu return s; 93*91f16700Schasinglulu 94*91f16700Schasinglulu fail: 95*91f16700Schasinglulu if (lenp) 96*91f16700Schasinglulu *lenp = err; 97*91f16700Schasinglulu return NULL; 98*91f16700Schasinglulu } 99*91f16700Schasinglulu 100*91f16700Schasinglulu const char *fdt_string(const void *fdt, int stroffset) 101*91f16700Schasinglulu { 102*91f16700Schasinglulu return fdt_get_string(fdt, stroffset, NULL); 103*91f16700Schasinglulu } 104*91f16700Schasinglulu 105*91f16700Schasinglulu static int fdt_string_eq_(const void *fdt, int stroffset, 106*91f16700Schasinglulu const char *s, int len) 107*91f16700Schasinglulu { 108*91f16700Schasinglulu int slen; 109*91f16700Schasinglulu const char *p = fdt_get_string(fdt, stroffset, &slen); 110*91f16700Schasinglulu 111*91f16700Schasinglulu return p && (slen == len) && (memcmp(p, s, len) == 0); 112*91f16700Schasinglulu } 113*91f16700Schasinglulu 114*91f16700Schasinglulu int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) 115*91f16700Schasinglulu { 116*91f16700Schasinglulu uint32_t max = 0; 117*91f16700Schasinglulu int offset = -1; 118*91f16700Schasinglulu 119*91f16700Schasinglulu while (true) { 120*91f16700Schasinglulu uint32_t value; 121*91f16700Schasinglulu 122*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, NULL); 123*91f16700Schasinglulu if (offset < 0) { 124*91f16700Schasinglulu if (offset == -FDT_ERR_NOTFOUND) 125*91f16700Schasinglulu break; 126*91f16700Schasinglulu 127*91f16700Schasinglulu return offset; 128*91f16700Schasinglulu } 129*91f16700Schasinglulu 130*91f16700Schasinglulu value = fdt_get_phandle(fdt, offset); 131*91f16700Schasinglulu 132*91f16700Schasinglulu if (value > max) 133*91f16700Schasinglulu max = value; 134*91f16700Schasinglulu } 135*91f16700Schasinglulu 136*91f16700Schasinglulu if (phandle) 137*91f16700Schasinglulu *phandle = max; 138*91f16700Schasinglulu 139*91f16700Schasinglulu return 0; 140*91f16700Schasinglulu } 141*91f16700Schasinglulu 142*91f16700Schasinglulu int fdt_generate_phandle(const void *fdt, uint32_t *phandle) 143*91f16700Schasinglulu { 144*91f16700Schasinglulu uint32_t max; 145*91f16700Schasinglulu int err; 146*91f16700Schasinglulu 147*91f16700Schasinglulu err = fdt_find_max_phandle(fdt, &max); 148*91f16700Schasinglulu if (err < 0) 149*91f16700Schasinglulu return err; 150*91f16700Schasinglulu 151*91f16700Schasinglulu if (max == FDT_MAX_PHANDLE) 152*91f16700Schasinglulu return -FDT_ERR_NOPHANDLES; 153*91f16700Schasinglulu 154*91f16700Schasinglulu if (phandle) 155*91f16700Schasinglulu *phandle = max + 1; 156*91f16700Schasinglulu 157*91f16700Schasinglulu return 0; 158*91f16700Schasinglulu } 159*91f16700Schasinglulu 160*91f16700Schasinglulu static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) 161*91f16700Schasinglulu { 162*91f16700Schasinglulu unsigned int offset = n * sizeof(struct fdt_reserve_entry); 163*91f16700Schasinglulu unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset; 164*91f16700Schasinglulu 165*91f16700Schasinglulu if (!can_assume(VALID_INPUT)) { 166*91f16700Schasinglulu if (absoffset < fdt_off_mem_rsvmap(fdt)) 167*91f16700Schasinglulu return NULL; 168*91f16700Schasinglulu if (absoffset > fdt_totalsize(fdt) - 169*91f16700Schasinglulu sizeof(struct fdt_reserve_entry)) 170*91f16700Schasinglulu return NULL; 171*91f16700Schasinglulu } 172*91f16700Schasinglulu return fdt_mem_rsv_(fdt, n); 173*91f16700Schasinglulu } 174*91f16700Schasinglulu 175*91f16700Schasinglulu int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 176*91f16700Schasinglulu { 177*91f16700Schasinglulu const struct fdt_reserve_entry *re; 178*91f16700Schasinglulu 179*91f16700Schasinglulu FDT_RO_PROBE(fdt); 180*91f16700Schasinglulu re = fdt_mem_rsv(fdt, n); 181*91f16700Schasinglulu if (!can_assume(VALID_INPUT) && !re) 182*91f16700Schasinglulu return -FDT_ERR_BADOFFSET; 183*91f16700Schasinglulu 184*91f16700Schasinglulu *address = fdt64_ld_(&re->address); 185*91f16700Schasinglulu *size = fdt64_ld_(&re->size); 186*91f16700Schasinglulu return 0; 187*91f16700Schasinglulu } 188*91f16700Schasinglulu 189*91f16700Schasinglulu int fdt_num_mem_rsv(const void *fdt) 190*91f16700Schasinglulu { 191*91f16700Schasinglulu int i; 192*91f16700Schasinglulu const struct fdt_reserve_entry *re; 193*91f16700Schasinglulu 194*91f16700Schasinglulu for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { 195*91f16700Schasinglulu if (fdt64_ld_(&re->size) == 0) 196*91f16700Schasinglulu return i; 197*91f16700Schasinglulu } 198*91f16700Schasinglulu return -FDT_ERR_TRUNCATED; 199*91f16700Schasinglulu } 200*91f16700Schasinglulu 201*91f16700Schasinglulu static int nextprop_(const void *fdt, int offset) 202*91f16700Schasinglulu { 203*91f16700Schasinglulu uint32_t tag; 204*91f16700Schasinglulu int nextoffset; 205*91f16700Schasinglulu 206*91f16700Schasinglulu do { 207*91f16700Schasinglulu tag = fdt_next_tag(fdt, offset, &nextoffset); 208*91f16700Schasinglulu 209*91f16700Schasinglulu switch (tag) { 210*91f16700Schasinglulu case FDT_END: 211*91f16700Schasinglulu if (nextoffset >= 0) 212*91f16700Schasinglulu return -FDT_ERR_BADSTRUCTURE; 213*91f16700Schasinglulu else 214*91f16700Schasinglulu return nextoffset; 215*91f16700Schasinglulu 216*91f16700Schasinglulu case FDT_PROP: 217*91f16700Schasinglulu return offset; 218*91f16700Schasinglulu } 219*91f16700Schasinglulu offset = nextoffset; 220*91f16700Schasinglulu } while (tag == FDT_NOP); 221*91f16700Schasinglulu 222*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 223*91f16700Schasinglulu } 224*91f16700Schasinglulu 225*91f16700Schasinglulu int fdt_subnode_offset_namelen(const void *fdt, int offset, 226*91f16700Schasinglulu const char *name, int namelen) 227*91f16700Schasinglulu { 228*91f16700Schasinglulu int depth; 229*91f16700Schasinglulu 230*91f16700Schasinglulu FDT_RO_PROBE(fdt); 231*91f16700Schasinglulu 232*91f16700Schasinglulu for (depth = 0; 233*91f16700Schasinglulu (offset >= 0) && (depth >= 0); 234*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, &depth)) 235*91f16700Schasinglulu if ((depth == 1) 236*91f16700Schasinglulu && fdt_nodename_eq_(fdt, offset, name, namelen)) 237*91f16700Schasinglulu return offset; 238*91f16700Schasinglulu 239*91f16700Schasinglulu if (depth < 0) 240*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 241*91f16700Schasinglulu return offset; /* error */ 242*91f16700Schasinglulu } 243*91f16700Schasinglulu 244*91f16700Schasinglulu int fdt_subnode_offset(const void *fdt, int parentoffset, 245*91f16700Schasinglulu const char *name) 246*91f16700Schasinglulu { 247*91f16700Schasinglulu return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 248*91f16700Schasinglulu } 249*91f16700Schasinglulu 250*91f16700Schasinglulu int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 251*91f16700Schasinglulu { 252*91f16700Schasinglulu const char *end = path + namelen; 253*91f16700Schasinglulu const char *p = path; 254*91f16700Schasinglulu int offset = 0; 255*91f16700Schasinglulu 256*91f16700Schasinglulu FDT_RO_PROBE(fdt); 257*91f16700Schasinglulu 258*91f16700Schasinglulu /* see if we have an alias */ 259*91f16700Schasinglulu if (*path != '/') { 260*91f16700Schasinglulu const char *q = memchr(path, '/', end - p); 261*91f16700Schasinglulu 262*91f16700Schasinglulu if (!q) 263*91f16700Schasinglulu q = end; 264*91f16700Schasinglulu 265*91f16700Schasinglulu p = fdt_get_alias_namelen(fdt, p, q - p); 266*91f16700Schasinglulu if (!p) 267*91f16700Schasinglulu return -FDT_ERR_BADPATH; 268*91f16700Schasinglulu offset = fdt_path_offset(fdt, p); 269*91f16700Schasinglulu 270*91f16700Schasinglulu p = q; 271*91f16700Schasinglulu } 272*91f16700Schasinglulu 273*91f16700Schasinglulu while (p < end) { 274*91f16700Schasinglulu const char *q; 275*91f16700Schasinglulu 276*91f16700Schasinglulu while (*p == '/') { 277*91f16700Schasinglulu p++; 278*91f16700Schasinglulu if (p == end) 279*91f16700Schasinglulu return offset; 280*91f16700Schasinglulu } 281*91f16700Schasinglulu q = memchr(p, '/', end - p); 282*91f16700Schasinglulu if (! q) 283*91f16700Schasinglulu q = end; 284*91f16700Schasinglulu 285*91f16700Schasinglulu offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 286*91f16700Schasinglulu if (offset < 0) 287*91f16700Schasinglulu return offset; 288*91f16700Schasinglulu 289*91f16700Schasinglulu p = q; 290*91f16700Schasinglulu } 291*91f16700Schasinglulu 292*91f16700Schasinglulu return offset; 293*91f16700Schasinglulu } 294*91f16700Schasinglulu 295*91f16700Schasinglulu int fdt_path_offset(const void *fdt, const char *path) 296*91f16700Schasinglulu { 297*91f16700Schasinglulu return fdt_path_offset_namelen(fdt, path, strlen(path)); 298*91f16700Schasinglulu } 299*91f16700Schasinglulu 300*91f16700Schasinglulu const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 301*91f16700Schasinglulu { 302*91f16700Schasinglulu const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); 303*91f16700Schasinglulu const char *nameptr; 304*91f16700Schasinglulu int err; 305*91f16700Schasinglulu 306*91f16700Schasinglulu if (((err = fdt_ro_probe_(fdt)) < 0) 307*91f16700Schasinglulu || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) 308*91f16700Schasinglulu goto fail; 309*91f16700Schasinglulu 310*91f16700Schasinglulu nameptr = nh->name; 311*91f16700Schasinglulu 312*91f16700Schasinglulu if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { 313*91f16700Schasinglulu /* 314*91f16700Schasinglulu * For old FDT versions, match the naming conventions of V16: 315*91f16700Schasinglulu * give only the leaf name (after all /). The actual tree 316*91f16700Schasinglulu * contents are loosely checked. 317*91f16700Schasinglulu */ 318*91f16700Schasinglulu const char *leaf; 319*91f16700Schasinglulu leaf = strrchr(nameptr, '/'); 320*91f16700Schasinglulu if (leaf == NULL) { 321*91f16700Schasinglulu err = -FDT_ERR_BADSTRUCTURE; 322*91f16700Schasinglulu goto fail; 323*91f16700Schasinglulu } 324*91f16700Schasinglulu nameptr = leaf+1; 325*91f16700Schasinglulu } 326*91f16700Schasinglulu 327*91f16700Schasinglulu if (len) 328*91f16700Schasinglulu *len = strlen(nameptr); 329*91f16700Schasinglulu 330*91f16700Schasinglulu return nameptr; 331*91f16700Schasinglulu 332*91f16700Schasinglulu fail: 333*91f16700Schasinglulu if (len) 334*91f16700Schasinglulu *len = err; 335*91f16700Schasinglulu return NULL; 336*91f16700Schasinglulu } 337*91f16700Schasinglulu 338*91f16700Schasinglulu int fdt_first_property_offset(const void *fdt, int nodeoffset) 339*91f16700Schasinglulu { 340*91f16700Schasinglulu int offset; 341*91f16700Schasinglulu 342*91f16700Schasinglulu if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 343*91f16700Schasinglulu return offset; 344*91f16700Schasinglulu 345*91f16700Schasinglulu return nextprop_(fdt, offset); 346*91f16700Schasinglulu } 347*91f16700Schasinglulu 348*91f16700Schasinglulu int fdt_next_property_offset(const void *fdt, int offset) 349*91f16700Schasinglulu { 350*91f16700Schasinglulu if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) 351*91f16700Schasinglulu return offset; 352*91f16700Schasinglulu 353*91f16700Schasinglulu return nextprop_(fdt, offset); 354*91f16700Schasinglulu } 355*91f16700Schasinglulu 356*91f16700Schasinglulu static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, 357*91f16700Schasinglulu int offset, 358*91f16700Schasinglulu int *lenp) 359*91f16700Schasinglulu { 360*91f16700Schasinglulu int err; 361*91f16700Schasinglulu const struct fdt_property *prop; 362*91f16700Schasinglulu 363*91f16700Schasinglulu if (!can_assume(VALID_INPUT) && 364*91f16700Schasinglulu (err = fdt_check_prop_offset_(fdt, offset)) < 0) { 365*91f16700Schasinglulu if (lenp) 366*91f16700Schasinglulu *lenp = err; 367*91f16700Schasinglulu return NULL; 368*91f16700Schasinglulu } 369*91f16700Schasinglulu 370*91f16700Schasinglulu prop = fdt_offset_ptr_(fdt, offset); 371*91f16700Schasinglulu 372*91f16700Schasinglulu if (lenp) 373*91f16700Schasinglulu *lenp = fdt32_ld_(&prop->len); 374*91f16700Schasinglulu 375*91f16700Schasinglulu return prop; 376*91f16700Schasinglulu } 377*91f16700Schasinglulu 378*91f16700Schasinglulu const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 379*91f16700Schasinglulu int offset, 380*91f16700Schasinglulu int *lenp) 381*91f16700Schasinglulu { 382*91f16700Schasinglulu /* Prior to version 16, properties may need realignment 383*91f16700Schasinglulu * and this API does not work. fdt_getprop_*() will, however. */ 384*91f16700Schasinglulu 385*91f16700Schasinglulu if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { 386*91f16700Schasinglulu if (lenp) 387*91f16700Schasinglulu *lenp = -FDT_ERR_BADVERSION; 388*91f16700Schasinglulu return NULL; 389*91f16700Schasinglulu } 390*91f16700Schasinglulu 391*91f16700Schasinglulu return fdt_get_property_by_offset_(fdt, offset, lenp); 392*91f16700Schasinglulu } 393*91f16700Schasinglulu 394*91f16700Schasinglulu static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, 395*91f16700Schasinglulu int offset, 396*91f16700Schasinglulu const char *name, 397*91f16700Schasinglulu int namelen, 398*91f16700Schasinglulu int *lenp, 399*91f16700Schasinglulu int *poffset) 400*91f16700Schasinglulu { 401*91f16700Schasinglulu for (offset = fdt_first_property_offset(fdt, offset); 402*91f16700Schasinglulu (offset >= 0); 403*91f16700Schasinglulu (offset = fdt_next_property_offset(fdt, offset))) { 404*91f16700Schasinglulu const struct fdt_property *prop; 405*91f16700Schasinglulu 406*91f16700Schasinglulu prop = fdt_get_property_by_offset_(fdt, offset, lenp); 407*91f16700Schasinglulu if (!can_assume(LIBFDT_FLAWLESS) && !prop) { 408*91f16700Schasinglulu offset = -FDT_ERR_INTERNAL; 409*91f16700Schasinglulu break; 410*91f16700Schasinglulu } 411*91f16700Schasinglulu if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff), 412*91f16700Schasinglulu name, namelen)) { 413*91f16700Schasinglulu if (poffset) 414*91f16700Schasinglulu *poffset = offset; 415*91f16700Schasinglulu return prop; 416*91f16700Schasinglulu } 417*91f16700Schasinglulu } 418*91f16700Schasinglulu 419*91f16700Schasinglulu if (lenp) 420*91f16700Schasinglulu *lenp = offset; 421*91f16700Schasinglulu return NULL; 422*91f16700Schasinglulu } 423*91f16700Schasinglulu 424*91f16700Schasinglulu 425*91f16700Schasinglulu const struct fdt_property *fdt_get_property_namelen(const void *fdt, 426*91f16700Schasinglulu int offset, 427*91f16700Schasinglulu const char *name, 428*91f16700Schasinglulu int namelen, int *lenp) 429*91f16700Schasinglulu { 430*91f16700Schasinglulu /* Prior to version 16, properties may need realignment 431*91f16700Schasinglulu * and this API does not work. fdt_getprop_*() will, however. */ 432*91f16700Schasinglulu if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { 433*91f16700Schasinglulu if (lenp) 434*91f16700Schasinglulu *lenp = -FDT_ERR_BADVERSION; 435*91f16700Schasinglulu return NULL; 436*91f16700Schasinglulu } 437*91f16700Schasinglulu 438*91f16700Schasinglulu return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, 439*91f16700Schasinglulu NULL); 440*91f16700Schasinglulu } 441*91f16700Schasinglulu 442*91f16700Schasinglulu 443*91f16700Schasinglulu const struct fdt_property *fdt_get_property(const void *fdt, 444*91f16700Schasinglulu int nodeoffset, 445*91f16700Schasinglulu const char *name, int *lenp) 446*91f16700Schasinglulu { 447*91f16700Schasinglulu return fdt_get_property_namelen(fdt, nodeoffset, name, 448*91f16700Schasinglulu strlen(name), lenp); 449*91f16700Schasinglulu } 450*91f16700Schasinglulu 451*91f16700Schasinglulu const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 452*91f16700Schasinglulu const char *name, int namelen, int *lenp) 453*91f16700Schasinglulu { 454*91f16700Schasinglulu int poffset; 455*91f16700Schasinglulu const struct fdt_property *prop; 456*91f16700Schasinglulu 457*91f16700Schasinglulu prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, 458*91f16700Schasinglulu &poffset); 459*91f16700Schasinglulu if (!prop) 460*91f16700Schasinglulu return NULL; 461*91f16700Schasinglulu 462*91f16700Schasinglulu /* Handle realignment */ 463*91f16700Schasinglulu if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && 464*91f16700Schasinglulu (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) 465*91f16700Schasinglulu return prop->data + 4; 466*91f16700Schasinglulu return prop->data; 467*91f16700Schasinglulu } 468*91f16700Schasinglulu 469*91f16700Schasinglulu const void *fdt_getprop_by_offset(const void *fdt, int offset, 470*91f16700Schasinglulu const char **namep, int *lenp) 471*91f16700Schasinglulu { 472*91f16700Schasinglulu const struct fdt_property *prop; 473*91f16700Schasinglulu 474*91f16700Schasinglulu prop = fdt_get_property_by_offset_(fdt, offset, lenp); 475*91f16700Schasinglulu if (!prop) 476*91f16700Schasinglulu return NULL; 477*91f16700Schasinglulu if (namep) { 478*91f16700Schasinglulu const char *name; 479*91f16700Schasinglulu int namelen; 480*91f16700Schasinglulu 481*91f16700Schasinglulu if (!can_assume(VALID_INPUT)) { 482*91f16700Schasinglulu name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff), 483*91f16700Schasinglulu &namelen); 484*91f16700Schasinglulu *namep = name; 485*91f16700Schasinglulu if (!name) { 486*91f16700Schasinglulu if (lenp) 487*91f16700Schasinglulu *lenp = namelen; 488*91f16700Schasinglulu return NULL; 489*91f16700Schasinglulu } 490*91f16700Schasinglulu } else { 491*91f16700Schasinglulu *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff)); 492*91f16700Schasinglulu } 493*91f16700Schasinglulu } 494*91f16700Schasinglulu 495*91f16700Schasinglulu /* Handle realignment */ 496*91f16700Schasinglulu if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && 497*91f16700Schasinglulu (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) 498*91f16700Schasinglulu return prop->data + 4; 499*91f16700Schasinglulu return prop->data; 500*91f16700Schasinglulu } 501*91f16700Schasinglulu 502*91f16700Schasinglulu const void *fdt_getprop(const void *fdt, int nodeoffset, 503*91f16700Schasinglulu const char *name, int *lenp) 504*91f16700Schasinglulu { 505*91f16700Schasinglulu return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 506*91f16700Schasinglulu } 507*91f16700Schasinglulu 508*91f16700Schasinglulu uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 509*91f16700Schasinglulu { 510*91f16700Schasinglulu const fdt32_t *php; 511*91f16700Schasinglulu int len; 512*91f16700Schasinglulu 513*91f16700Schasinglulu /* FIXME: This is a bit sub-optimal, since we potentially scan 514*91f16700Schasinglulu * over all the properties twice. */ 515*91f16700Schasinglulu php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 516*91f16700Schasinglulu if (!php || (len != sizeof(*php))) { 517*91f16700Schasinglulu php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 518*91f16700Schasinglulu if (!php || (len != sizeof(*php))) 519*91f16700Schasinglulu return 0; 520*91f16700Schasinglulu } 521*91f16700Schasinglulu 522*91f16700Schasinglulu return fdt32_ld_(php); 523*91f16700Schasinglulu } 524*91f16700Schasinglulu 525*91f16700Schasinglulu const char *fdt_get_alias_namelen(const void *fdt, 526*91f16700Schasinglulu const char *name, int namelen) 527*91f16700Schasinglulu { 528*91f16700Schasinglulu int aliasoffset; 529*91f16700Schasinglulu 530*91f16700Schasinglulu aliasoffset = fdt_path_offset(fdt, "/aliases"); 531*91f16700Schasinglulu if (aliasoffset < 0) 532*91f16700Schasinglulu return NULL; 533*91f16700Schasinglulu 534*91f16700Schasinglulu return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 535*91f16700Schasinglulu } 536*91f16700Schasinglulu 537*91f16700Schasinglulu const char *fdt_get_alias(const void *fdt, const char *name) 538*91f16700Schasinglulu { 539*91f16700Schasinglulu return fdt_get_alias_namelen(fdt, name, strlen(name)); 540*91f16700Schasinglulu } 541*91f16700Schasinglulu 542*91f16700Schasinglulu int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 543*91f16700Schasinglulu { 544*91f16700Schasinglulu int pdepth = 0, p = 0; 545*91f16700Schasinglulu int offset, depth, namelen; 546*91f16700Schasinglulu const char *name; 547*91f16700Schasinglulu 548*91f16700Schasinglulu FDT_RO_PROBE(fdt); 549*91f16700Schasinglulu 550*91f16700Schasinglulu if (buflen < 2) 551*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 552*91f16700Schasinglulu 553*91f16700Schasinglulu for (offset = 0, depth = 0; 554*91f16700Schasinglulu (offset >= 0) && (offset <= nodeoffset); 555*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, &depth)) { 556*91f16700Schasinglulu while (pdepth > depth) { 557*91f16700Schasinglulu do { 558*91f16700Schasinglulu p--; 559*91f16700Schasinglulu } while (buf[p-1] != '/'); 560*91f16700Schasinglulu pdepth--; 561*91f16700Schasinglulu } 562*91f16700Schasinglulu 563*91f16700Schasinglulu if (pdepth >= depth) { 564*91f16700Schasinglulu name = fdt_get_name(fdt, offset, &namelen); 565*91f16700Schasinglulu if (!name) 566*91f16700Schasinglulu return namelen; 567*91f16700Schasinglulu if ((p + namelen + 1) <= buflen) { 568*91f16700Schasinglulu memcpy(buf + p, name, namelen); 569*91f16700Schasinglulu p += namelen; 570*91f16700Schasinglulu buf[p++] = '/'; 571*91f16700Schasinglulu pdepth++; 572*91f16700Schasinglulu } 573*91f16700Schasinglulu } 574*91f16700Schasinglulu 575*91f16700Schasinglulu if (offset == nodeoffset) { 576*91f16700Schasinglulu if (pdepth < (depth + 1)) 577*91f16700Schasinglulu return -FDT_ERR_NOSPACE; 578*91f16700Schasinglulu 579*91f16700Schasinglulu if (p > 1) /* special case so that root path is "/", not "" */ 580*91f16700Schasinglulu p--; 581*91f16700Schasinglulu buf[p] = '\0'; 582*91f16700Schasinglulu return 0; 583*91f16700Schasinglulu } 584*91f16700Schasinglulu } 585*91f16700Schasinglulu 586*91f16700Schasinglulu if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 587*91f16700Schasinglulu return -FDT_ERR_BADOFFSET; 588*91f16700Schasinglulu else if (offset == -FDT_ERR_BADOFFSET) 589*91f16700Schasinglulu return -FDT_ERR_BADSTRUCTURE; 590*91f16700Schasinglulu 591*91f16700Schasinglulu return offset; /* error from fdt_next_node() */ 592*91f16700Schasinglulu } 593*91f16700Schasinglulu 594*91f16700Schasinglulu int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 595*91f16700Schasinglulu int supernodedepth, int *nodedepth) 596*91f16700Schasinglulu { 597*91f16700Schasinglulu int offset, depth; 598*91f16700Schasinglulu int supernodeoffset = -FDT_ERR_INTERNAL; 599*91f16700Schasinglulu 600*91f16700Schasinglulu FDT_RO_PROBE(fdt); 601*91f16700Schasinglulu 602*91f16700Schasinglulu if (supernodedepth < 0) 603*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 604*91f16700Schasinglulu 605*91f16700Schasinglulu for (offset = 0, depth = 0; 606*91f16700Schasinglulu (offset >= 0) && (offset <= nodeoffset); 607*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, &depth)) { 608*91f16700Schasinglulu if (depth == supernodedepth) 609*91f16700Schasinglulu supernodeoffset = offset; 610*91f16700Schasinglulu 611*91f16700Schasinglulu if (offset == nodeoffset) { 612*91f16700Schasinglulu if (nodedepth) 613*91f16700Schasinglulu *nodedepth = depth; 614*91f16700Schasinglulu 615*91f16700Schasinglulu if (supernodedepth > depth) 616*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 617*91f16700Schasinglulu else 618*91f16700Schasinglulu return supernodeoffset; 619*91f16700Schasinglulu } 620*91f16700Schasinglulu } 621*91f16700Schasinglulu 622*91f16700Schasinglulu if (!can_assume(VALID_INPUT)) { 623*91f16700Schasinglulu if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 624*91f16700Schasinglulu return -FDT_ERR_BADOFFSET; 625*91f16700Schasinglulu else if (offset == -FDT_ERR_BADOFFSET) 626*91f16700Schasinglulu return -FDT_ERR_BADSTRUCTURE; 627*91f16700Schasinglulu } 628*91f16700Schasinglulu 629*91f16700Schasinglulu return offset; /* error from fdt_next_node() */ 630*91f16700Schasinglulu } 631*91f16700Schasinglulu 632*91f16700Schasinglulu int fdt_node_depth(const void *fdt, int nodeoffset) 633*91f16700Schasinglulu { 634*91f16700Schasinglulu int nodedepth; 635*91f16700Schasinglulu int err; 636*91f16700Schasinglulu 637*91f16700Schasinglulu err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 638*91f16700Schasinglulu if (err) 639*91f16700Schasinglulu return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err : 640*91f16700Schasinglulu -FDT_ERR_INTERNAL; 641*91f16700Schasinglulu return nodedepth; 642*91f16700Schasinglulu } 643*91f16700Schasinglulu 644*91f16700Schasinglulu int fdt_parent_offset(const void *fdt, int nodeoffset) 645*91f16700Schasinglulu { 646*91f16700Schasinglulu int nodedepth = fdt_node_depth(fdt, nodeoffset); 647*91f16700Schasinglulu 648*91f16700Schasinglulu if (nodedepth < 0) 649*91f16700Schasinglulu return nodedepth; 650*91f16700Schasinglulu return fdt_supernode_atdepth_offset(fdt, nodeoffset, 651*91f16700Schasinglulu nodedepth - 1, NULL); 652*91f16700Schasinglulu } 653*91f16700Schasinglulu 654*91f16700Schasinglulu int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 655*91f16700Schasinglulu const char *propname, 656*91f16700Schasinglulu const void *propval, int proplen) 657*91f16700Schasinglulu { 658*91f16700Schasinglulu int offset; 659*91f16700Schasinglulu const void *val; 660*91f16700Schasinglulu int len; 661*91f16700Schasinglulu 662*91f16700Schasinglulu FDT_RO_PROBE(fdt); 663*91f16700Schasinglulu 664*91f16700Schasinglulu /* FIXME: The algorithm here is pretty horrible: we scan each 665*91f16700Schasinglulu * property of a node in fdt_getprop(), then if that didn't 666*91f16700Schasinglulu * find what we want, we scan over them again making our way 667*91f16700Schasinglulu * to the next node. Still it's the easiest to implement 668*91f16700Schasinglulu * approach; performance can come later. */ 669*91f16700Schasinglulu for (offset = fdt_next_node(fdt, startoffset, NULL); 670*91f16700Schasinglulu offset >= 0; 671*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, NULL)) { 672*91f16700Schasinglulu val = fdt_getprop(fdt, offset, propname, &len); 673*91f16700Schasinglulu if (val && (len == proplen) 674*91f16700Schasinglulu && (memcmp(val, propval, len) == 0)) 675*91f16700Schasinglulu return offset; 676*91f16700Schasinglulu } 677*91f16700Schasinglulu 678*91f16700Schasinglulu return offset; /* error from fdt_next_node() */ 679*91f16700Schasinglulu } 680*91f16700Schasinglulu 681*91f16700Schasinglulu int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 682*91f16700Schasinglulu { 683*91f16700Schasinglulu int offset; 684*91f16700Schasinglulu 685*91f16700Schasinglulu if ((phandle == 0) || (phandle == ~0U)) 686*91f16700Schasinglulu return -FDT_ERR_BADPHANDLE; 687*91f16700Schasinglulu 688*91f16700Schasinglulu FDT_RO_PROBE(fdt); 689*91f16700Schasinglulu 690*91f16700Schasinglulu /* FIXME: The algorithm here is pretty horrible: we 691*91f16700Schasinglulu * potentially scan each property of a node in 692*91f16700Schasinglulu * fdt_get_phandle(), then if that didn't find what 693*91f16700Schasinglulu * we want, we scan over them again making our way to the next 694*91f16700Schasinglulu * node. Still it's the easiest to implement approach; 695*91f16700Schasinglulu * performance can come later. */ 696*91f16700Schasinglulu for (offset = fdt_next_node(fdt, -1, NULL); 697*91f16700Schasinglulu offset >= 0; 698*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, NULL)) { 699*91f16700Schasinglulu if (fdt_get_phandle(fdt, offset) == phandle) 700*91f16700Schasinglulu return offset; 701*91f16700Schasinglulu } 702*91f16700Schasinglulu 703*91f16700Schasinglulu return offset; /* error from fdt_next_node() */ 704*91f16700Schasinglulu } 705*91f16700Schasinglulu 706*91f16700Schasinglulu int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 707*91f16700Schasinglulu { 708*91f16700Schasinglulu int len = strlen(str); 709*91f16700Schasinglulu const char *p; 710*91f16700Schasinglulu 711*91f16700Schasinglulu while (listlen >= len) { 712*91f16700Schasinglulu if (memcmp(str, strlist, len+1) == 0) 713*91f16700Schasinglulu return 1; 714*91f16700Schasinglulu p = memchr(strlist, '\0', listlen); 715*91f16700Schasinglulu if (!p) 716*91f16700Schasinglulu return 0; /* malformed strlist.. */ 717*91f16700Schasinglulu listlen -= (p-strlist) + 1; 718*91f16700Schasinglulu strlist = p + 1; 719*91f16700Schasinglulu } 720*91f16700Schasinglulu return 0; 721*91f16700Schasinglulu } 722*91f16700Schasinglulu 723*91f16700Schasinglulu int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 724*91f16700Schasinglulu { 725*91f16700Schasinglulu const char *list, *end; 726*91f16700Schasinglulu int length, count = 0; 727*91f16700Schasinglulu 728*91f16700Schasinglulu list = fdt_getprop(fdt, nodeoffset, property, &length); 729*91f16700Schasinglulu if (!list) 730*91f16700Schasinglulu return length; 731*91f16700Schasinglulu 732*91f16700Schasinglulu end = list + length; 733*91f16700Schasinglulu 734*91f16700Schasinglulu while (list < end) { 735*91f16700Schasinglulu length = strnlen(list, end - list) + 1; 736*91f16700Schasinglulu 737*91f16700Schasinglulu /* Abort if the last string isn't properly NUL-terminated. */ 738*91f16700Schasinglulu if (list + length > end) 739*91f16700Schasinglulu return -FDT_ERR_BADVALUE; 740*91f16700Schasinglulu 741*91f16700Schasinglulu list += length; 742*91f16700Schasinglulu count++; 743*91f16700Schasinglulu } 744*91f16700Schasinglulu 745*91f16700Schasinglulu return count; 746*91f16700Schasinglulu } 747*91f16700Schasinglulu 748*91f16700Schasinglulu int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 749*91f16700Schasinglulu const char *string) 750*91f16700Schasinglulu { 751*91f16700Schasinglulu int length, len, idx = 0; 752*91f16700Schasinglulu const char *list, *end; 753*91f16700Schasinglulu 754*91f16700Schasinglulu list = fdt_getprop(fdt, nodeoffset, property, &length); 755*91f16700Schasinglulu if (!list) 756*91f16700Schasinglulu return length; 757*91f16700Schasinglulu 758*91f16700Schasinglulu len = strlen(string) + 1; 759*91f16700Schasinglulu end = list + length; 760*91f16700Schasinglulu 761*91f16700Schasinglulu while (list < end) { 762*91f16700Schasinglulu length = strnlen(list, end - list) + 1; 763*91f16700Schasinglulu 764*91f16700Schasinglulu /* Abort if the last string isn't properly NUL-terminated. */ 765*91f16700Schasinglulu if (list + length > end) 766*91f16700Schasinglulu return -FDT_ERR_BADVALUE; 767*91f16700Schasinglulu 768*91f16700Schasinglulu if (length == len && memcmp(list, string, length) == 0) 769*91f16700Schasinglulu return idx; 770*91f16700Schasinglulu 771*91f16700Schasinglulu list += length; 772*91f16700Schasinglulu idx++; 773*91f16700Schasinglulu } 774*91f16700Schasinglulu 775*91f16700Schasinglulu return -FDT_ERR_NOTFOUND; 776*91f16700Schasinglulu } 777*91f16700Schasinglulu 778*91f16700Schasinglulu const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 779*91f16700Schasinglulu const char *property, int idx, 780*91f16700Schasinglulu int *lenp) 781*91f16700Schasinglulu { 782*91f16700Schasinglulu const char *list, *end; 783*91f16700Schasinglulu int length; 784*91f16700Schasinglulu 785*91f16700Schasinglulu list = fdt_getprop(fdt, nodeoffset, property, &length); 786*91f16700Schasinglulu if (!list) { 787*91f16700Schasinglulu if (lenp) 788*91f16700Schasinglulu *lenp = length; 789*91f16700Schasinglulu 790*91f16700Schasinglulu return NULL; 791*91f16700Schasinglulu } 792*91f16700Schasinglulu 793*91f16700Schasinglulu end = list + length; 794*91f16700Schasinglulu 795*91f16700Schasinglulu while (list < end) { 796*91f16700Schasinglulu length = strnlen(list, end - list) + 1; 797*91f16700Schasinglulu 798*91f16700Schasinglulu /* Abort if the last string isn't properly NUL-terminated. */ 799*91f16700Schasinglulu if (list + length > end) { 800*91f16700Schasinglulu if (lenp) 801*91f16700Schasinglulu *lenp = -FDT_ERR_BADVALUE; 802*91f16700Schasinglulu 803*91f16700Schasinglulu return NULL; 804*91f16700Schasinglulu } 805*91f16700Schasinglulu 806*91f16700Schasinglulu if (idx == 0) { 807*91f16700Schasinglulu if (lenp) 808*91f16700Schasinglulu *lenp = length - 1; 809*91f16700Schasinglulu 810*91f16700Schasinglulu return list; 811*91f16700Schasinglulu } 812*91f16700Schasinglulu 813*91f16700Schasinglulu list += length; 814*91f16700Schasinglulu idx--; 815*91f16700Schasinglulu } 816*91f16700Schasinglulu 817*91f16700Schasinglulu if (lenp) 818*91f16700Schasinglulu *lenp = -FDT_ERR_NOTFOUND; 819*91f16700Schasinglulu 820*91f16700Schasinglulu return NULL; 821*91f16700Schasinglulu } 822*91f16700Schasinglulu 823*91f16700Schasinglulu int fdt_node_check_compatible(const void *fdt, int nodeoffset, 824*91f16700Schasinglulu const char *compatible) 825*91f16700Schasinglulu { 826*91f16700Schasinglulu const void *prop; 827*91f16700Schasinglulu int len; 828*91f16700Schasinglulu 829*91f16700Schasinglulu prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 830*91f16700Schasinglulu if (!prop) 831*91f16700Schasinglulu return len; 832*91f16700Schasinglulu 833*91f16700Schasinglulu return !fdt_stringlist_contains(prop, len, compatible); 834*91f16700Schasinglulu } 835*91f16700Schasinglulu 836*91f16700Schasinglulu int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 837*91f16700Schasinglulu const char *compatible) 838*91f16700Schasinglulu { 839*91f16700Schasinglulu int offset, err; 840*91f16700Schasinglulu 841*91f16700Schasinglulu FDT_RO_PROBE(fdt); 842*91f16700Schasinglulu 843*91f16700Schasinglulu /* FIXME: The algorithm here is pretty horrible: we scan each 844*91f16700Schasinglulu * property of a node in fdt_node_check_compatible(), then if 845*91f16700Schasinglulu * that didn't find what we want, we scan over them again 846*91f16700Schasinglulu * making our way to the next node. Still it's the easiest to 847*91f16700Schasinglulu * implement approach; performance can come later. */ 848*91f16700Schasinglulu for (offset = fdt_next_node(fdt, startoffset, NULL); 849*91f16700Schasinglulu offset >= 0; 850*91f16700Schasinglulu offset = fdt_next_node(fdt, offset, NULL)) { 851*91f16700Schasinglulu err = fdt_node_check_compatible(fdt, offset, compatible); 852*91f16700Schasinglulu if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 853*91f16700Schasinglulu return err; 854*91f16700Schasinglulu else if (err == 0) 855*91f16700Schasinglulu return offset; 856*91f16700Schasinglulu } 857*91f16700Schasinglulu 858*91f16700Schasinglulu return offset; /* error from fdt_next_node() */ 859*91f16700Schasinglulu } 860