Changeset View
Changeset View
Standalone View
Standalone View
sys/contrib/libfdt/fdt_ro.c
Show First 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | static int fdt_nodename_eq_(const void *fdt, int offset, | ||||
if (p[len] == '\0') | if (p[len] == '\0') | ||||
return 1; | return 1; | ||||
else if (!memchr(s, '@', len) && (p[len] == '@')) | else if (!memchr(s, '@', len) && (p[len] == '@')) | ||||
return 1; | return 1; | ||||
else | else | ||||
return 0; | return 0; | ||||
} | } | ||||
const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) | |||||
{ | |||||
uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); | |||||
size_t len; | |||||
int err; | |||||
const char *s, *n; | |||||
err = fdt_ro_probe_(fdt); | |||||
if (err != 0) | |||||
goto fail; | |||||
err = -FDT_ERR_BADOFFSET; | |||||
if (absoffset >= fdt_totalsize(fdt)) | |||||
goto fail; | |||||
len = fdt_totalsize(fdt) - absoffset; | |||||
if (fdt_magic(fdt) == FDT_MAGIC) { | |||||
if (stroffset < 0) | |||||
goto fail; | |||||
if (fdt_version(fdt) >= 17) { | |||||
if (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) { | |||||
if ((stroffset >= 0) | |||||
|| (stroffset < -fdt_size_dt_strings(fdt))) | |||||
goto fail; | |||||
if ((-stroffset) < len) | |||||
len = -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) | const char *fdt_string(const void *fdt, int stroffset) | ||||
{ | { | ||||
return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; | return fdt_get_string(fdt, stroffset, NULL); | ||||
} | } | ||||
static int fdt_string_eq_(const void *fdt, int stroffset, | static int fdt_string_eq_(const void *fdt, int stroffset, | ||||
const char *s, int len) | const char *s, int len) | ||||
{ | { | ||||
const char *p = fdt_string(fdt, stroffset); | int slen; | ||||
const char *p = fdt_get_string(fdt, stroffset, &slen); | |||||
return (strlen(p) == len) && (memcmp(p, s, len) == 0); | return p && (slen == len) && (memcmp(p, s, len) == 0); | ||||
} | } | ||||
uint32_t fdt_get_max_phandle(const void *fdt) | uint32_t fdt_get_max_phandle(const void *fdt) | ||||
{ | { | ||||
uint32_t max_phandle = 0; | uint32_t max_phandle = 0; | ||||
int offset; | int offset; | ||||
for (offset = fdt_next_node(fdt, -1, NULL);; | for (offset = fdt_next_node(fdt, -1, NULL);; | ||||
Show All 12 Lines | for (offset = fdt_next_node(fdt, -1, NULL);; | ||||
if (phandle > max_phandle) | if (phandle > max_phandle) | ||||
max_phandle = phandle; | max_phandle = phandle; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) | |||||
{ | |||||
int offset = n * sizeof(struct fdt_reserve_entry); | |||||
int absoffset = fdt_off_mem_rsvmap(fdt) + offset; | |||||
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) | int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) | ||||
{ | { | ||||
FDT_CHECK_HEADER(fdt); | const struct fdt_reserve_entry *re; | ||||
*address = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->address); | |||||
*size = fdt64_to_cpu(fdt_mem_rsv_(fdt, n)->size); | FDT_RO_PROBE(fdt); | ||||
re = fdt_mem_rsv(fdt, n); | |||||
if (!re) | |||||
return -FDT_ERR_BADOFFSET; | |||||
*address = fdt64_ld(&re->address); | |||||
*size = fdt64_ld(&re->size); | |||||
return 0; | return 0; | ||||
} | } | ||||
int fdt_num_mem_rsv(const void *fdt) | int fdt_num_mem_rsv(const void *fdt) | ||||
{ | { | ||||
int i = 0; | int i; | ||||
const struct fdt_reserve_entry *re; | |||||
while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0) | for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { | ||||
i++; | if (fdt64_ld(&re->size) == 0) | ||||
return i; | return i; | ||||
} | } | ||||
return -FDT_ERR_TRUNCATED; | |||||
} | |||||
static int nextprop_(const void *fdt, int offset) | static int nextprop_(const void *fdt, int offset) | ||||
{ | { | ||||
uint32_t tag; | uint32_t tag; | ||||
int nextoffset; | int nextoffset; | ||||
do { | do { | ||||
tag = fdt_next_tag(fdt, offset, &nextoffset); | tag = fdt_next_tag(fdt, offset, &nextoffset); | ||||
Show All 14 Lines | static int nextprop_(const void *fdt, int offset) | ||||
return -FDT_ERR_NOTFOUND; | return -FDT_ERR_NOTFOUND; | ||||
} | } | ||||
int fdt_subnode_offset_namelen(const void *fdt, int offset, | int fdt_subnode_offset_namelen(const void *fdt, int offset, | ||||
const char *name, int namelen) | const char *name, int namelen) | ||||
{ | { | ||||
int depth; | int depth; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
for (depth = 0; | for (depth = 0; | ||||
(offset >= 0) && (depth >= 0); | (offset >= 0) && (depth >= 0); | ||||
offset = fdt_next_node(fdt, offset, &depth)) | offset = fdt_next_node(fdt, offset, &depth)) | ||||
if ((depth == 1) | if ((depth == 1) | ||||
&& fdt_nodename_eq_(fdt, offset, name, namelen)) | && fdt_nodename_eq_(fdt, offset, name, namelen)) | ||||
return offset; | return offset; | ||||
Show All 9 Lines | |||||
} | } | ||||
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) | int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) | ||||
{ | { | ||||
const char *end = path + namelen; | const char *end = path + namelen; | ||||
const char *p = path; | const char *p = path; | ||||
int offset = 0; | int offset = 0; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
/* see if we have an alias */ | /* see if we have an alias */ | ||||
if (*path != '/') { | if (*path != '/') { | ||||
const char *q = memchr(path, '/', end - p); | const char *q = memchr(path, '/', end - p); | ||||
if (!q) | if (!q) | ||||
q = end; | q = end; | ||||
Show All 33 Lines | |||||
} | } | ||||
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) | const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) | ||||
{ | { | ||||
const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); | const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); | ||||
const char *nameptr; | const char *nameptr; | ||||
int err; | int err; | ||||
if (((err = fdt_check_header(fdt)) != 0) | if (((err = fdt_ro_probe_(fdt)) != 0) | ||||
|| ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) | || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) | ||||
goto fail; | goto fail; | ||||
nameptr = nh->name; | nameptr = nh->name; | ||||
if (fdt_version(fdt) < 0x10) { | if (fdt_version(fdt) < 0x10) { | ||||
/* | /* | ||||
* For old FDT versions, match the naming conventions of V16: | * For old FDT versions, match the naming conventions of V16: | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { | ||||
if (lenp) | if (lenp) | ||||
*lenp = err; | *lenp = err; | ||||
return NULL; | return NULL; | ||||
} | } | ||||
prop = fdt_offset_ptr_(fdt, offset); | prop = fdt_offset_ptr_(fdt, offset); | ||||
if (lenp) | if (lenp) | ||||
*lenp = fdt32_to_cpu(prop->len); | *lenp = fdt32_ld(&prop->len); | ||||
return prop; | return prop; | ||||
} | } | ||||
const struct fdt_property *fdt_get_property_by_offset(const void *fdt, | const struct fdt_property *fdt_get_property_by_offset(const void *fdt, | ||||
int offset, | int offset, | ||||
int *lenp) | int *lenp) | ||||
{ | { | ||||
Show All 20 Lines | for (offset = fdt_first_property_offset(fdt, offset); | ||||
(offset >= 0); | (offset >= 0); | ||||
(offset = fdt_next_property_offset(fdt, offset))) { | (offset = fdt_next_property_offset(fdt, offset))) { | ||||
const struct fdt_property *prop; | const struct fdt_property *prop; | ||||
if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { | if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { | ||||
offset = -FDT_ERR_INTERNAL; | offset = -FDT_ERR_INTERNAL; | ||||
break; | break; | ||||
} | } | ||||
if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff), | if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), | ||||
name, namelen)) { | name, namelen)) { | ||||
if (poffset) | if (poffset) | ||||
*poffset = offset; | *poffset = offset; | ||||
return prop; | return prop; | ||||
} | } | ||||
} | } | ||||
if (lenp) | if (lenp) | ||||
Show All 36 Lines | const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, | ||||
prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, | prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, | ||||
&poffset); | &poffset); | ||||
if (!prop) | if (!prop) | ||||
return NULL; | return NULL; | ||||
/* Handle realignment */ | /* Handle realignment */ | ||||
if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && | if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && | ||||
fdt32_to_cpu(prop->len) >= 8) | fdt32_ld(&prop->len) >= 8) | ||||
return prop->data + 4; | return prop->data + 4; | ||||
return prop->data; | return prop->data; | ||||
} | } | ||||
const void *fdt_getprop_by_offset(const void *fdt, int offset, | const void *fdt_getprop_by_offset(const void *fdt, int offset, | ||||
const char **namep, int *lenp) | const char **namep, int *lenp) | ||||
{ | { | ||||
const struct fdt_property *prop; | const struct fdt_property *prop; | ||||
prop = fdt_get_property_by_offset_(fdt, offset, lenp); | prop = fdt_get_property_by_offset_(fdt, offset, lenp); | ||||
if (!prop) | if (!prop) | ||||
return NULL; | return NULL; | ||||
if (namep) | if (namep) { | ||||
*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); | const char *name; | ||||
int namelen; | |||||
name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), | |||||
&namelen); | |||||
if (!name) { | |||||
if (lenp) | |||||
*lenp = namelen; | |||||
return NULL; | |||||
} | |||||
*namep = name; | |||||
} | |||||
/* Handle realignment */ | /* Handle realignment */ | ||||
if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && | if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && | ||||
fdt32_to_cpu(prop->len) >= 8) | fdt32_ld(&prop->len) >= 8) | ||||
return prop->data + 4; | return prop->data + 4; | ||||
return prop->data; | return prop->data; | ||||
} | } | ||||
const void *fdt_getprop(const void *fdt, int nodeoffset, | const void *fdt_getprop(const void *fdt, int nodeoffset, | ||||
const char *name, int *lenp) | const char *name, int *lenp) | ||||
{ | { | ||||
return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); | return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); | ||||
} | } | ||||
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) | uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) | ||||
{ | { | ||||
const fdt32_t *php; | const fdt32_t *php; | ||||
int len; | int len; | ||||
/* FIXME: This is a bit sub-optimal, since we potentially scan | /* FIXME: This is a bit sub-optimal, since we potentially scan | ||||
* over all the properties twice. */ | * over all the properties twice. */ | ||||
php = fdt_getprop(fdt, nodeoffset, "phandle", &len); | php = fdt_getprop(fdt, nodeoffset, "phandle", &len); | ||||
if (!php || (len != sizeof(*php))) { | if (!php || (len != sizeof(*php))) { | ||||
php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); | php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); | ||||
if (!php || (len != sizeof(*php))) | if (!php || (len != sizeof(*php))) | ||||
return 0; | return 0; | ||||
} | } | ||||
return fdt32_to_cpu(*php); | return fdt32_ld(php); | ||||
} | } | ||||
const char *fdt_get_alias_namelen(const void *fdt, | const char *fdt_get_alias_namelen(const void *fdt, | ||||
const char *name, int namelen) | const char *name, int namelen) | ||||
{ | { | ||||
int aliasoffset; | int aliasoffset; | ||||
aliasoffset = fdt_path_offset(fdt, "/aliases"); | aliasoffset = fdt_path_offset(fdt, "/aliases"); | ||||
Show All 9 Lines | |||||
} | } | ||||
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) | int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) | ||||
{ | { | ||||
int pdepth = 0, p = 0; | int pdepth = 0, p = 0; | ||||
int offset, depth, namelen; | int offset, depth, namelen; | ||||
const char *name; | const char *name; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
if (buflen < 2) | if (buflen < 2) | ||||
return -FDT_ERR_NOSPACE; | return -FDT_ERR_NOSPACE; | ||||
for (offset = 0, depth = 0; | for (offset = 0, depth = 0; | ||||
(offset >= 0) && (offset <= nodeoffset); | (offset >= 0) && (offset <= nodeoffset); | ||||
offset = fdt_next_node(fdt, offset, &depth)) { | offset = fdt_next_node(fdt, offset, &depth)) { | ||||
while (pdepth > depth) { | while (pdepth > depth) { | ||||
Show All 35 Lines | |||||
} | } | ||||
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, | int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, | ||||
int supernodedepth, int *nodedepth) | int supernodedepth, int *nodedepth) | ||||
{ | { | ||||
int offset, depth; | int offset, depth; | ||||
int supernodeoffset = -FDT_ERR_INTERNAL; | int supernodeoffset = -FDT_ERR_INTERNAL; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
if (supernodedepth < 0) | if (supernodedepth < 0) | ||||
return -FDT_ERR_NOTFOUND; | return -FDT_ERR_NOTFOUND; | ||||
for (offset = 0, depth = 0; | for (offset = 0, depth = 0; | ||||
(offset >= 0) && (offset <= nodeoffset); | (offset >= 0) && (offset <= nodeoffset); | ||||
offset = fdt_next_node(fdt, offset, &depth)) { | offset = fdt_next_node(fdt, offset, &depth)) { | ||||
if (depth == supernodedepth) | if (depth == supernodedepth) | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, | int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, | ||||
const char *propname, | const char *propname, | ||||
const void *propval, int proplen) | const void *propval, int proplen) | ||||
{ | { | ||||
int offset; | int offset; | ||||
const void *val; | const void *val; | ||||
int len; | int len; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
/* FIXME: The algorithm here is pretty horrible: we scan each | /* FIXME: The algorithm here is pretty horrible: we scan each | ||||
* property of a node in fdt_getprop(), then if that didn't | * property of a node in fdt_getprop(), then if that didn't | ||||
* find what we want, we scan over them again making our way | * find what we want, we scan over them again making our way | ||||
* to the next node. Still it's the easiest to implement | * to the next node. Still it's the easiest to implement | ||||
* approach; performance can come later. */ | * approach; performance can come later. */ | ||||
for (offset = fdt_next_node(fdt, startoffset, NULL); | for (offset = fdt_next_node(fdt, startoffset, NULL); | ||||
offset >= 0; | offset >= 0; | ||||
Show All 9 Lines | |||||
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) | int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) | ||||
{ | { | ||||
int offset; | int offset; | ||||
if ((phandle == 0) || (phandle == -1)) | if ((phandle == 0) || (phandle == -1)) | ||||
return -FDT_ERR_BADPHANDLE; | return -FDT_ERR_BADPHANDLE; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
/* FIXME: The algorithm here is pretty horrible: we | /* FIXME: The algorithm here is pretty horrible: we | ||||
* potentially scan each property of a node in | * potentially scan each property of a node in | ||||
* fdt_get_phandle(), then if that didn't find what | * fdt_get_phandle(), then if that didn't find what | ||||
* we want, we scan over them again making our way to the next | * we want, we scan over them again making our way to the next | ||||
* node. Still it's the easiest to implement approach; | * node. Still it's the easiest to implement approach; | ||||
* performance can come later. */ | * performance can come later. */ | ||||
for (offset = fdt_next_node(fdt, -1, NULL); | for (offset = fdt_next_node(fdt, -1, NULL); | ||||
▲ Show 20 Lines • Show All 136 Lines • ▼ Show 20 Lines | int fdt_node_check_compatible(const void *fdt, int nodeoffset, | ||||
return !fdt_stringlist_contains(prop, len, compatible); | return !fdt_stringlist_contains(prop, len, compatible); | ||||
} | } | ||||
int fdt_node_offset_by_compatible(const void *fdt, int startoffset, | int fdt_node_offset_by_compatible(const void *fdt, int startoffset, | ||||
const char *compatible) | const char *compatible) | ||||
{ | { | ||||
int offset, err; | int offset, err; | ||||
FDT_CHECK_HEADER(fdt); | FDT_RO_PROBE(fdt); | ||||
/* FIXME: The algorithm here is pretty horrible: we scan each | /* FIXME: The algorithm here is pretty horrible: we scan each | ||||
* property of a node in fdt_node_check_compatible(), then if | * property of a node in fdt_node_check_compatible(), then if | ||||
* that didn't find what we want, we scan over them again | * 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 | * making our way to the next node. Still it's the easiest to | ||||
* implement approach; performance can come later. */ | * implement approach; performance can come later. */ | ||||
for (offset = fdt_next_node(fdt, startoffset, NULL); | for (offset = fdt_next_node(fdt, startoffset, NULL); | ||||
offset >= 0; | offset >= 0; | ||||
offset = fdt_next_node(fdt, offset, NULL)) { | offset = fdt_next_node(fdt, offset, NULL)) { | ||||
err = fdt_node_check_compatible(fdt, offset, compatible); | err = fdt_node_check_compatible(fdt, offset, compatible); | ||||
if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) | if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) | ||||
return err; | return err; | ||||
else if (err == 0) | else if (err == 0) | ||||
return offset; | return offset; | ||||
} | } | ||||
return offset; /* error from fdt_next_node() */ | return offset; /* error from fdt_next_node() */ | ||||
} | |||||
int fdt_check_full(const void *fdt, size_t bufsize) | |||||
{ | |||||
int err; | |||||
int num_memrsv; | |||||
int offset, nextoffset = 0; | |||||
uint32_t tag; | |||||
unsigned depth = 0; | |||||
const void *prop; | |||||
const char *propname; | |||||
if (bufsize < FDT_V1_SIZE) | |||||
return -FDT_ERR_TRUNCATED; | |||||
err = fdt_check_header(fdt); | |||||
if (err != 0) | |||||
return err; | |||||
if (bufsize < fdt_totalsize(fdt)) | |||||
return -FDT_ERR_TRUNCATED; | |||||
num_memrsv = fdt_num_mem_rsv(fdt); | |||||
if (num_memrsv < 0) | |||||
return num_memrsv; | |||||
while (1) { | |||||
offset = nextoffset; | |||||
tag = fdt_next_tag(fdt, offset, &nextoffset); | |||||
if (nextoffset < 0) | |||||
return nextoffset; | |||||
switch (tag) { | |||||
case FDT_NOP: | |||||
break; | |||||
case FDT_END: | |||||
if (depth != 0) | |||||
return -FDT_ERR_BADSTRUCTURE; | |||||
return 0; | |||||
case FDT_BEGIN_NODE: | |||||
depth++; | |||||
if (depth > INT_MAX) | |||||
return -FDT_ERR_BADSTRUCTURE; | |||||
break; | |||||
case FDT_END_NODE: | |||||
if (depth == 0) | |||||
return -FDT_ERR_BADSTRUCTURE; | |||||
depth--; | |||||
break; | |||||
case FDT_PROP: | |||||
prop = fdt_getprop_by_offset(fdt, offset, &propname, | |||||
&err); | |||||
if (!prop) | |||||
return err; | |||||
break; | |||||
default: | |||||
return -FDT_ERR_INTERNAL; | |||||
} | |||||
} | |||||
} | } |