cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fdt_ro.c (18552B)


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