Viewing file: fdt_ro.c (18.12 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */ #include "libfdt_env.h"
#include <fdt.h> #include <libfdt.h>
#include "libfdt_internal.h"
static int fdt_nodename_eq_(const void *fdt, int offset, const char *s, int len) { int olen; const char *p = fdt_get_name(fdt, offset, &olen);
if (!p || olen < len) /* short match */ return 0;
if (memcmp(p, s, len) != 0) return 0;
if (p[len] == '\0') return 1; else if (!memchr(s, '@', len) && (p[len] == '@')) return 1; else return 0; }
const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) { int32_t totalsize; uint32_t absoffset; size_t len; int err; const char *s, *n;
if (can_assume(VALID_INPUT)) { s = (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
if (lenp) *lenp = strlen(s); return s; } totalsize = fdt_ro_probe_(fdt); err = totalsize; if (totalsize < 0) goto fail;
err = -FDT_ERR_BADOFFSET; absoffset = stroffset + fdt_off_dt_strings(fdt); if (absoffset >= (unsigned)totalsize) goto fail; len = totalsize - absoffset;
if (fdt_magic(fdt) == FDT_MAGIC) { if (stroffset < 0) goto fail; if (can_assume(LATEST) || fdt_version(fdt) >= 17) { if ((unsigned)stroffset >= fdt_size_dt_strings(fdt)) goto fail; if ((fdt_size_dt_strings(fdt) - stroffset) < len) len = fdt_size_dt_strings(fdt) - stroffset; } } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { unsigned int sw_stroffset = -stroffset;
if ((stroffset >= 0) || (sw_stroffset > fdt_size_dt_strings(fdt))) goto fail; if (sw_stroffset < len) len = sw_stroffset; } else { err = -FDT_ERR_INTERNAL; goto fail; }
s = (const char *)fdt + absoffset; n = memchr(s, '\0', len); if (!n) { /* missing terminating NULL */ err = -FDT_ERR_TRUNCATED; goto fail; }
if (lenp) *lenp = n - s; return s;
fail: if (lenp) *lenp = err; return NULL; }
const char *fdt_string(const void *fdt, int stroffset) { return fdt_get_string(fdt, stroffset, NULL); }
static int fdt_string_eq_(const void *fdt, int stroffset, const char *s, int len) { int slen; const char *p = fdt_get_string(fdt, stroffset, &slen);
return p && (slen == len) && (memcmp(p, s, len) == 0); }
int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) { uint32_t max = 0; int offset = -1;
while (true) { uint32_t value;
offset = fdt_next_node(fdt, offset, NULL); if (offset < 0) { if (offset == -FDT_ERR_NOTFOUND) break;
return offset; }
value = fdt_get_phandle(fdt, offset);
if (value > max) max = value; }
if (phandle) *phandle = max;
return 0; }
int fdt_generate_phandle(const void *fdt, uint32_t *phandle) { uint32_t max; int err;
err = fdt_find_max_phandle(fdt, &max); if (err < 0) return err;
if (max == FDT_MAX_PHANDLE) return -FDT_ERR_NOPHANDLES;
if (phandle) *phandle = max + 1;
return 0; }
static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) { unsigned int offset = n * sizeof(struct fdt_reserve_entry); unsigned int absoffset = fdt_off_mem_rsvmap(fdt) + offset;
if (!can_assume(VALID_INPUT)) { if (absoffset < fdt_off_mem_rsvmap(fdt)) return NULL; if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) return NULL; } return fdt_mem_rsv_(fdt, n); }
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) { const struct fdt_reserve_entry *re;
FDT_RO_PROBE(fdt); re = fdt_mem_rsv(fdt, n); if (!can_assume(VALID_INPUT) && !re) return -FDT_ERR_BADOFFSET;
*address = fdt64_ld_(&re->address); *size = fdt64_ld_(&re->size); return 0; }
int fdt_num_mem_rsv(const void *fdt) { int i; const struct fdt_reserve_entry *re;
for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { if (fdt64_ld_(&re->size) == 0) return i; } return -FDT_ERR_TRUNCATED; }
static int nextprop_(const void *fdt, int offset) { uint32_t tag; int nextoffset;
do { tag = fdt_next_tag(fdt, offset, &nextoffset);
switch (tag) { case FDT_END: if (nextoffset >= 0) return -FDT_ERR_BADSTRUCTURE; else return nextoffset;
case FDT_PROP: return offset; } offset = nextoffset; } while (tag == FDT_NOP);
return -FDT_ERR_NOTFOUND; }
int fdt_subnode_offset_namelen(const void *fdt, int offset, const char *name, int namelen) { int depth;
FDT_RO_PROBE(fdt);
for (depth = 0; (offset >= 0) && (depth >= 0); offset = fdt_next_node(fdt, offset, &depth)) if ((depth == 1) && fdt_nodename_eq_(fdt, offset, name, namelen)) return offset;
if (depth < 0) return -FDT_ERR_NOTFOUND; return offset; /* error */ }
int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name) { return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); }
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) { const char *end = path + namelen; const char *p = path; int offset = 0;
FDT_RO_PROBE(fdt);
/* see if we have an alias */ if (*path != '/') { const char *q = memchr(path, '/', end - p);
if (!q) q = end;
p = fdt_get_alias_namelen(fdt, p, q - p); if (!p) return -FDT_ERR_BADPATH; offset = fdt_path_offset(fdt, p);
p = q; }
while (p < end) { const char *q;
while (*p == '/') { p++; if (p == end) return offset; } q = memchr(p, '/', end - p); if (! q) q = end;
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); if (offset < 0) return offset;
p = q; }
return offset; }
int fdt_path_offset(const void *fdt, const char *path) { return fdt_path_offset_namelen(fdt, path, strlen(path)); }
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) { const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); const char *nameptr; int err;
if (((err = fdt_ro_probe_(fdt)) < 0) || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) goto fail;
nameptr = nh->name;
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { /* * For old FDT versions, match the naming conventions of V16: * give only the leaf name (after all /). The actual tree * contents are loosely checked. */ const char *leaf; leaf = strrchr(nameptr, '/'); if (leaf == NULL) { err = -FDT_ERR_BADSTRUCTURE; goto fail; } nameptr = leaf+1; }
if (len) *len = strlen(nameptr);
return nameptr;
fail: if (len) *len = err; return NULL; }
int fdt_first_property_offset(const void *fdt, int nodeoffset) { int offset;
if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) return offset;
return nextprop_(fdt, offset); }
int fdt_next_property_offset(const void *fdt, int offset) { if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) return offset;
return nextprop_(fdt, offset); }
static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, int offset, int *lenp) { int err; const struct fdt_property *prop;
if (!can_assume(VALID_INPUT) && (err = fdt_check_prop_offset_(fdt, offset)) < 0) { if (lenp) *lenp = err; return NULL; }
prop = fdt_offset_ptr_(fdt, offset);
if (lenp) *lenp = fdt32_ld_(&prop->len);
return prop; }
const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp) { /* Prior to version 16, properties may need realignment * and this API does not work. fdt_getprop_*() will, however. */
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { if (lenp) *lenp = -FDT_ERR_BADVERSION; return NULL; }
return fdt_get_property_by_offset_(fdt, offset, lenp); }
static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, int offset, const char *name, int namelen, int *lenp, int *poffset) { for (offset = fdt_first_property_offset(fdt, offset); (offset >= 0); (offset = fdt_next_property_offset(fdt, offset))) { const struct fdt_property *prop;
prop = fdt_get_property_by_offset_(fdt, offset, lenp); if (!can_assume(LIBFDT_FLAWLESS) && !prop) { offset = -FDT_ERR_INTERNAL; break; } if (fdt_string_eq_(fdt, fdt32_ld_(&prop->nameoff), name, namelen)) { if (poffset) *poffset = offset; return prop; } }
if (lenp) *lenp = offset; return NULL; }
const struct fdt_property *fdt_get_property_namelen(const void *fdt, int offset, const char *name, int namelen, int *lenp) { /* Prior to version 16, properties may need realignment * and this API does not work. fdt_getprop_*() will, however. */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) { if (lenp) *lenp = -FDT_ERR_BADVERSION; return NULL; }
return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, NULL); }
const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_get_property_namelen(fdt, nodeoffset, name, strlen(name), lenp); }
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { int poffset; const struct fdt_property *prop;
prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, &poffset); if (!prop) return NULL;
/* Handle realignment */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) return prop->data + 4; return prop->data; }
const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *lenp) { const struct fdt_property *prop;
prop = fdt_get_property_by_offset_(fdt, offset, lenp); if (!prop) return NULL; if (namep) { const char *name; int namelen;
if (!can_assume(VALID_INPUT)) { name = fdt_get_string(fdt, fdt32_ld_(&prop->nameoff), &namelen); if (!name) { if (lenp) *lenp = namelen; return NULL; } *namep = name; } else { *namep = fdt_string(fdt, fdt32_ld_(&prop->nameoff)); } }
/* Handle realignment */ if (!can_assume(LATEST) && fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && fdt32_ld_(&prop->len) >= 8) return prop->data + 4; return prop->data; }
const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); }
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) { const fdt32_t *php; int len;
/* FIXME: This is a bit sub-optimal, since we potentially scan * over all the properties twice. */ php = fdt_getprop(fdt, nodeoffset, "phandle", &len); if (!php || (len != sizeof(*php))) { php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); if (!php || (len != sizeof(*php))) return 0; }
return fdt32_ld_(php); }
const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen) { int aliasoffset;
aliasoffset = fdt_path_offset(fdt, "/aliases"); if (aliasoffset < 0) return NULL;
return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); }
const char *fdt_get_alias(const void *fdt, const char *name) { return fdt_get_alias_namelen(fdt, name, strlen(name)); }
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) { int pdepth = 0, p = 0; int offset, depth, namelen; const char *name;
FDT_RO_PROBE(fdt);
if (buflen < 2) return -FDT_ERR_NOSPACE;
for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { while (pdepth > depth) { do { p--; } while (buf[p-1] != '/'); pdepth--; }
if (pdepth >= depth) { name = fdt_get_name(fdt, offset, &namelen); if (!name) return namelen; if ((p + namelen + 1) <= buflen) { memcpy(buf + p, name, namelen); p += namelen; buf[p++] = '/'; pdepth++; } }
if (offset == nodeoffset) { if (pdepth < (depth + 1)) return -FDT_ERR_NOSPACE;
if (p > 1) /* special case so that root path is "/", not "" */ p--; buf[p] = '\0'; return 0; } }
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE;
return offset; /* error from fdt_next_node() */ }
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) { int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL;
FDT_RO_PROBE(fdt);
if (supernodedepth < 0) return -FDT_ERR_NOTFOUND;
for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { if (depth == supernodedepth) supernodeoffset = offset;
if (offset == nodeoffset) { if (nodedepth) *nodedepth = depth;
if (supernodedepth > depth) return -FDT_ERR_NOTFOUND; else return supernodeoffset; } }
if (!can_assume(VALID_INPUT)) { if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; }
return offset; /* error from fdt_next_node() */ }
int fdt_node_depth(const void *fdt, int nodeoffset) { int nodedepth; int err;
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); if (err) return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err : -FDT_ERR_INTERNAL; return nodedepth; }
int fdt_parent_offset(const void *fdt, int nodeoffset) { int nodedepth = fdt_node_depth(fdt, nodeoffset);
if (nodedepth < 0) return nodedepth; return fdt_supernode_atdepth_offset(fdt, nodeoffset, nodedepth - 1, NULL); }
int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen) { int offset; const void *val; int len;
FDT_RO_PROBE(fdt);
/* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_getprop(), then if that didn't * find what we want, we scan over them again making our way * to the next node. Still it's the easiest to implement * approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { val = fdt_getprop(fdt, offset, propname, &len); if (val && (len == proplen) && (memcmp(val, propval, len) == 0)) return offset; }
return offset; /* error from fdt_next_node() */ }
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) { int offset;
if ((phandle == 0) || (phandle == ~0U)) return -FDT_ERR_BADPHANDLE;
FDT_RO_PROBE(fdt);
/* FIXME: The algorithm here is pretty horrible: we * potentially scan each property of a node in * fdt_get_phandle(), then if that didn't find what * we want, we scan over them again making our way to the next * node. Still it's the easiest to implement approach; * performance can come later. */ for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { if (fdt_get_phandle(fdt, offset) == phandle) return offset; }
return offset; /* error from fdt_next_node() */ }
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) { int len = strlen(str); const char *p;
while (listlen >= len) { if (memcmp(str, strlist, len+1) == 0) return 1; p = memchr(strlist, '\0', listlen); if (!p) return 0; /* malformed strlist.. */ listlen -= (p-strlist) + 1; strlist = p + 1; } return 0; }
int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) { const char *list, *end; int length, count = 0;
list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length;
end = list + length;
while (list < end) { length = strnlen(list, end - list) + 1;
/* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE;
list += length; count++; }
return count; }
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string) { int length, len, idx = 0; const char *list, *end;
list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length;
len = strlen(string) + 1; end = list + length;
while (list < end) { length = strnlen(list, end - list) + 1;
/* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE;
if (length == len && memcmp(list, string, length) == 0) return idx;
list += length; idx++; }
return -FDT_ERR_NOTFOUND; }
const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int idx, int *lenp) { const char *list, *end; int length;
list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) { if (lenp) *lenp = length;
return NULL; }
end = list + length;
while (list < end) { length = strnlen(list, end - list) + 1;
/* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) { if (lenp) *lenp = -FDT_ERR_BADVALUE;
return NULL; }
if (idx == 0) { if (lenp) *lenp = length - 1;
return list; }
list += length; idx--; }
if (lenp) *lenp = -FDT_ERR_NOTFOUND;
return NULL; }
int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) { const void *prop; int len;
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); if (!prop) return len;
return !fdt_stringlist_contains(prop, len, compatible); }
int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible) { int offset, err;
FDT_RO_PROBE(fdt);
/* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_node_check_compatible(), then if * that didn't find what we want, we scan over them again * making our way to the next node. Still it's the easiest to * implement approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { err = fdt_node_check_compatible(fdt, offset, compatible); if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) return err; else if (err == 0) return offset; }
return offset; /* error from fdt_next_node() */ }
|