Home | History | Annotate | Line # | Download | only in dist
fdt_ro.c revision 1.1
      1 /*
      2  * libfdt - Flat Device Tree manipulation
      3  * Copyright (C) 2006 David Gibson, IBM Corporation.
      4  *
      5  * libfdt is dual licensed: you can use it either under the terms of
      6  * the GPL, or the BSD license, at your option.
      7  *
      8  *  a) This library is free software; you can redistribute it and/or
      9  *     modify it under the terms of the GNU General Public License as
     10  *     published by the Free Software Foundation; either version 2 of the
     11  *     License, or (at your option) any later version.
     12  *
     13  *     This library is distributed in the hope that it will be useful,
     14  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *     GNU General Public License for more details.
     17  *
     18  *     You should have received a copy of the GNU General Public
     19  *     License along with this library; if not, write to the Free
     20  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
     21  *     MA 02110-1301 USA
     22  *
     23  * Alternatively,
     24  *
     25  *  b) Redistribution and use in source and binary forms, with or
     26  *     without modification, are permitted provided that the following
     27  *     conditions are met:
     28  *
     29  *     1. Redistributions of source code must retain the above
     30  *        copyright notice, this list of conditions and the following
     31  *        disclaimer.
     32  *     2. Redistributions in binary form must reproduce the above
     33  *        copyright notice, this list of conditions and the following
     34  *        disclaimer in the documentation and/or other materials
     35  *        provided with the distribution.
     36  *
     37  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
     38  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
     39  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     40  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     41  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     42  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     43  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     44  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     45  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     46  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     47  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     48  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     49  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     50  */
     51 #include "libfdt_env.h"
     52 
     53 #include <fdt.h>
     54 #include <libfdt.h>
     55 
     56 #include "libfdt_internal.h"
     57 
     58 static int _fdt_nodename_eq(const void *fdt, int offset,
     59 			    const char *s, int len)
     60 {
     61 	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
     62 
     63 	if (! p)
     64 		/* short match */
     65 		return 0;
     66 
     67 	if (memcmp(p, s, len) != 0)
     68 		return 0;
     69 
     70 	if (p[len] == '\0')
     71 		return 1;
     72 	else if (!memchr(s, '@', len) && (p[len] == '@'))
     73 		return 1;
     74 	else
     75 		return 0;
     76 }
     77 
     78 const char *fdt_string(const void *fdt, int stroffset)
     79 {
     80 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
     81 }
     82 
     83 static int _fdt_string_eq(const void *fdt, int stroffset,
     84 			  const char *s, int len)
     85 {
     86 	const char *p = fdt_string(fdt, stroffset);
     87 
     88 	return (strlen(p) == len) && (memcmp(p, s, len) == 0);
     89 }
     90 
     91 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
     92 {
     93 	FDT_CHECK_HEADER(fdt);
     94 	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
     95 	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
     96 	return 0;
     97 }
     98 
     99 int fdt_num_mem_rsv(const void *fdt)
    100 {
    101 	int i = 0;
    102 
    103 	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
    104 		i++;
    105 	return i;
    106 }
    107 
    108 static int _nextprop(const void *fdt, int offset)
    109 {
    110 	uint32_t tag;
    111 	int nextoffset;
    112 
    113 	do {
    114 		tag = fdt_next_tag(fdt, offset, &nextoffset);
    115 
    116 		switch (tag) {
    117 		case FDT_END:
    118 			if (nextoffset >= 0)
    119 				return -FDT_ERR_BADSTRUCTURE;
    120 			else
    121 				return nextoffset;
    122 
    123 		case FDT_PROP:
    124 			return offset;
    125 		}
    126 		offset = nextoffset;
    127 	} while (tag == FDT_NOP);
    128 
    129 	return -FDT_ERR_NOTFOUND;
    130 }
    131 
    132 int fdt_subnode_offset_namelen(const void *fdt, int offset,
    133 			       const char *name, int namelen)
    134 {
    135 	int depth;
    136 
    137 	FDT_CHECK_HEADER(fdt);
    138 
    139 	for (depth = 0;
    140 	     (offset >= 0) && (depth >= 0);
    141 	     offset = fdt_next_node(fdt, offset, &depth))
    142 		if ((depth == 1)
    143 		    && _fdt_nodename_eq(fdt, offset, name, namelen))
    144 			return offset;
    145 
    146 	if (depth < 0)
    147 		return -FDT_ERR_NOTFOUND;
    148 	return offset; /* error */
    149 }
    150 
    151 int fdt_subnode_offset(const void *fdt, int parentoffset,
    152 		       const char *name)
    153 {
    154 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
    155 }
    156 
    157 int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
    158 {
    159 	const char *end = path + namelen;
    160 	const char *p = path;
    161 	int offset = 0;
    162 
    163 	FDT_CHECK_HEADER(fdt);
    164 
    165 	/* see if we have an alias */
    166 	if (*path != '/') {
    167 		const char *q = memchr(path, '/', end - p);
    168 
    169 		if (!q)
    170 			q = end;
    171 
    172 		p = fdt_get_alias_namelen(fdt, p, q - p);
    173 		if (!p)
    174 			return -FDT_ERR_BADPATH;
    175 		offset = fdt_path_offset(fdt, p);
    176 
    177 		p = q;
    178 	}
    179 
    180 	while (p < end) {
    181 		const char *q;
    182 
    183 		while (*p == '/') {
    184 			p++;
    185 			if (p == end)
    186 				return offset;
    187 		}
    188 		q = memchr(p, '/', end - p);
    189 		if (! q)
    190 			q = end;
    191 
    192 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
    193 		if (offset < 0)
    194 			return offset;
    195 
    196 		p = q;
    197 	}
    198 
    199 	return offset;
    200 }
    201 
    202 int fdt_path_offset(const void *fdt, const char *path)
    203 {
    204 	return fdt_path_offset_namelen(fdt, path, strlen(path));
    205 }
    206 
    207 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
    208 {
    209 	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
    210 	int err;
    211 
    212 	if (((err = fdt_check_header(fdt)) != 0)
    213 	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
    214 			goto fail;
    215 
    216 	if (len)
    217 		*len = strlen(nh->name);
    218 
    219 	return nh->name;
    220 
    221  fail:
    222 	if (len)
    223 		*len = err;
    224 	return NULL;
    225 }
    226 
    227 int fdt_first_property_offset(const void *fdt, int nodeoffset)
    228 {
    229 	int offset;
    230 
    231 	if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
    232 		return offset;
    233 
    234 	return _nextprop(fdt, offset);
    235 }
    236 
    237 int fdt_next_property_offset(const void *fdt, int offset)
    238 {
    239 	if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
    240 		return offset;
    241 
    242 	return _nextprop(fdt, offset);
    243 }
    244 
    245 const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
    246 						      int offset,
    247 						      int *lenp)
    248 {
    249 	int err;
    250 	const struct fdt_property *prop;
    251 
    252 	if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
    253 		if (lenp)
    254 			*lenp = err;
    255 		return NULL;
    256 	}
    257 
    258 	prop = _fdt_offset_ptr(fdt, offset);
    259 
    260 	if (lenp)
    261 		*lenp = fdt32_to_cpu(prop->len);
    262 
    263 	return prop;
    264 }
    265 
    266 const struct fdt_property *fdt_get_property_namelen(const void *fdt,
    267 						    int offset,
    268 						    const char *name,
    269 						    int namelen, int *lenp)
    270 {
    271 	for (offset = fdt_first_property_offset(fdt, offset);
    272 	     (offset >= 0);
    273 	     (offset = fdt_next_property_offset(fdt, offset))) {
    274 		const struct fdt_property *prop;
    275 
    276 		if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
    277 			offset = -FDT_ERR_INTERNAL;
    278 			break;
    279 		}
    280 		if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
    281 				   name, namelen))
    282 			return prop;
    283 	}
    284 
    285 	if (lenp)
    286 		*lenp = offset;
    287 	return NULL;
    288 }
    289 
    290 const struct fdt_property *fdt_get_property(const void *fdt,
    291 					    int nodeoffset,
    292 					    const char *name, int *lenp)
    293 {
    294 	return fdt_get_property_namelen(fdt, nodeoffset, name,
    295 					strlen(name), lenp);
    296 }
    297 
    298 const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
    299 				const char *name, int namelen, int *lenp)
    300 {
    301 	const struct fdt_property *prop;
    302 
    303 	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
    304 	if (! prop)
    305 		return NULL;
    306 
    307 	return prop->data;
    308 }
    309 
    310 const void *fdt_getprop_by_offset(const void *fdt, int offset,
    311 				  const char **namep, int *lenp)
    312 {
    313 	const struct fdt_property *prop;
    314 
    315 	prop = fdt_get_property_by_offset(fdt, offset, lenp);
    316 	if (!prop)
    317 		return NULL;
    318 	if (namep)
    319 		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
    320 	return prop->data;
    321 }
    322 
    323 const void *fdt_getprop(const void *fdt, int nodeoffset,
    324 			const char *name, int *lenp)
    325 {
    326 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
    327 }
    328 
    329 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
    330 {
    331 	const fdt32_t *php;
    332 	int len;
    333 
    334 	/* FIXME: This is a bit sub-optimal, since we potentially scan
    335 	 * over all the properties twice. */
    336 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
    337 	if (!php || (len != sizeof(*php))) {
    338 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
    339 		if (!php || (len != sizeof(*php)))
    340 			return 0;
    341 	}
    342 
    343 	return fdt32_to_cpu(*php);
    344 }
    345 
    346 const char *fdt_get_alias_namelen(const void *fdt,
    347 				  const char *name, int namelen)
    348 {
    349 	int aliasoffset;
    350 
    351 	aliasoffset = fdt_path_offset(fdt, "/aliases");
    352 	if (aliasoffset < 0)
    353 		return NULL;
    354 
    355 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
    356 }
    357 
    358 const char *fdt_get_alias(const void *fdt, const char *name)
    359 {
    360 	return fdt_get_alias_namelen(fdt, name, strlen(name));
    361 }
    362 
    363 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
    364 {
    365 	int pdepth = 0, p = 0;
    366 	int offset, depth, namelen;
    367 	const char *name;
    368 
    369 	FDT_CHECK_HEADER(fdt);
    370 
    371 	if (buflen < 2)
    372 		return -FDT_ERR_NOSPACE;
    373 
    374 	for (offset = 0, depth = 0;
    375 	     (offset >= 0) && (offset <= nodeoffset);
    376 	     offset = fdt_next_node(fdt, offset, &depth)) {
    377 		while (pdepth > depth) {
    378 			do {
    379 				p--;
    380 			} while (buf[p-1] != '/');
    381 			pdepth--;
    382 		}
    383 
    384 		if (pdepth >= depth) {
    385 			name = fdt_get_name(fdt, offset, &namelen);
    386 			if (!name)
    387 				return namelen;
    388 			if ((p + namelen + 1) <= buflen) {
    389 				memcpy(buf + p, name, namelen);
    390 				p += namelen;
    391 				buf[p++] = '/';
    392 				pdepth++;
    393 			}
    394 		}
    395 
    396 		if (offset == nodeoffset) {
    397 			if (pdepth < (depth + 1))
    398 				return -FDT_ERR_NOSPACE;
    399 
    400 			if (p > 1) /* special case so that root path is "/", not "" */
    401 				p--;
    402 			buf[p] = '\0';
    403 			return 0;
    404 		}
    405 	}
    406 
    407 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
    408 		return -FDT_ERR_BADOFFSET;
    409 	else if (offset == -FDT_ERR_BADOFFSET)
    410 		return -FDT_ERR_BADSTRUCTURE;
    411 
    412 	return offset; /* error from fdt_next_node() */
    413 }
    414 
    415 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
    416 				 int supernodedepth, int *nodedepth)
    417 {
    418 	int offset, depth;
    419 	int supernodeoffset = -FDT_ERR_INTERNAL;
    420 
    421 	FDT_CHECK_HEADER(fdt);
    422 
    423 	if (supernodedepth < 0)
    424 		return -FDT_ERR_NOTFOUND;
    425 
    426 	for (offset = 0, depth = 0;
    427 	     (offset >= 0) && (offset <= nodeoffset);
    428 	     offset = fdt_next_node(fdt, offset, &depth)) {
    429 		if (depth == supernodedepth)
    430 			supernodeoffset = offset;
    431 
    432 		if (offset == nodeoffset) {
    433 			if (nodedepth)
    434 				*nodedepth = depth;
    435 
    436 			if (supernodedepth > depth)
    437 				return -FDT_ERR_NOTFOUND;
    438 			else
    439 				return supernodeoffset;
    440 		}
    441 	}
    442 
    443 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
    444 		return -FDT_ERR_BADOFFSET;
    445 	else if (offset == -FDT_ERR_BADOFFSET)
    446 		return -FDT_ERR_BADSTRUCTURE;
    447 
    448 	return offset; /* error from fdt_next_node() */
    449 }
    450 
    451 int fdt_node_depth(const void *fdt, int nodeoffset)
    452 {
    453 	int nodedepth;
    454 	int err;
    455 
    456 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
    457 	if (err)
    458 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
    459 	return nodedepth;
    460 }
    461 
    462 int fdt_parent_offset(const void *fdt, int nodeoffset)
    463 {
    464 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
    465 
    466 	if (nodedepth < 0)
    467 		return nodedepth;
    468 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
    469 					    nodedepth - 1, NULL);
    470 }
    471 
    472 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
    473 				  const char *propname,
    474 				  const void *propval, int proplen)
    475 {
    476 	int offset;
    477 	const void *val;
    478 	int len;
    479 
    480 	FDT_CHECK_HEADER(fdt);
    481 
    482 	/* FIXME: The algorithm here is pretty horrible: we scan each
    483 	 * property of a node in fdt_getprop(), then if that didn't
    484 	 * find what we want, we scan over them again making our way
    485 	 * to the next node.  Still it's the easiest to implement
    486 	 * approach; performance can come later. */
    487 	for (offset = fdt_next_node(fdt, startoffset, NULL);
    488 	     offset >= 0;
    489 	     offset = fdt_next_node(fdt, offset, NULL)) {
    490 		val = fdt_getprop(fdt, offset, propname, &len);
    491 		if (val && (len == proplen)
    492 		    && (memcmp(val, propval, len) == 0))
    493 			return offset;
    494 	}
    495 
    496 	return offset; /* error from fdt_next_node() */
    497 }
    498 
    499 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
    500 {
    501 	int offset;
    502 
    503 	if ((phandle == 0) || (phandle == -1))
    504 		return -FDT_ERR_BADPHANDLE;
    505 
    506 	FDT_CHECK_HEADER(fdt);
    507 
    508 	/* FIXME: The algorithm here is pretty horrible: we
    509 	 * potentially scan each property of a node in
    510 	 * fdt_get_phandle(), then if that didn't find what
    511 	 * we want, we scan over them again making our way to the next
    512 	 * node.  Still it's the easiest to implement approach;
    513 	 * performance can come later. */
    514 	for (offset = fdt_next_node(fdt, -1, NULL);
    515 	     offset >= 0;
    516 	     offset = fdt_next_node(fdt, offset, NULL)) {
    517 		if (fdt_get_phandle(fdt, offset) == phandle)
    518 			return offset;
    519 	}
    520 
    521 	return offset; /* error from fdt_next_node() */
    522 }
    523 
    524 int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
    525 {
    526 	int len = strlen(str);
    527 	const char *p;
    528 
    529 	while (listlen >= len) {
    530 		if (memcmp(str, strlist, len+1) == 0)
    531 			return 1;
    532 		p = memchr(strlist, '\0', listlen);
    533 		if (!p)
    534 			return 0; /* malformed strlist.. */
    535 		listlen -= (p-strlist) + 1;
    536 		strlist = p + 1;
    537 	}
    538 	return 0;
    539 }
    540 
    541 int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
    542 {
    543 	const char *list, *end;
    544 	int length, count = 0;
    545 
    546 	list = fdt_getprop(fdt, nodeoffset, property, &length);
    547 	if (!list)
    548 		return -length;
    549 
    550 	end = list + length;
    551 
    552 	while (list < end) {
    553 		length = strnlen(list, end - list) + 1;
    554 
    555 		/* Abort if the last string isn't properly NUL-terminated. */
    556 		if (list + length > end)
    557 			return -FDT_ERR_BADVALUE;
    558 
    559 		list += length;
    560 		count++;
    561 	}
    562 
    563 	return count;
    564 }
    565 
    566 int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
    567 			  const char *string)
    568 {
    569 	int length, len, idx = 0;
    570 	const char *list, *end;
    571 
    572 	list = fdt_getprop(fdt, nodeoffset, property, &length);
    573 	if (!list)
    574 		return -length;
    575 
    576 	len = strlen(string) + 1;
    577 	end = list + length;
    578 
    579 	while (list < end) {
    580 		length = strnlen(list, end - list) + 1;
    581 
    582 		/* Abort if the last string isn't properly NUL-terminated. */
    583 		if (list + length > end)
    584 			return -FDT_ERR_BADVALUE;
    585 
    586 		if (length == len && memcmp(list, string, length) == 0)
    587 			return idx;
    588 
    589 		list += length;
    590 		idx++;
    591 	}
    592 
    593 	return -FDT_ERR_NOTFOUND;
    594 }
    595 
    596 const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
    597 			       const char *property, int idx,
    598 			       int *lenp)
    599 {
    600 	const char *list, *end;
    601 	int length;
    602 
    603 	list = fdt_getprop(fdt, nodeoffset, property, &length);
    604 	if (!list) {
    605 		if (lenp)
    606 			*lenp = length;
    607 
    608 		return NULL;
    609 	}
    610 
    611 	end = list + length;
    612 
    613 	while (list < end) {
    614 		length = strnlen(list, end - list) + 1;
    615 
    616 		/* Abort if the last string isn't properly NUL-terminated. */
    617 		if (list + length > end) {
    618 			if (lenp)
    619 				*lenp = -FDT_ERR_BADVALUE;
    620 
    621 			return NULL;
    622 		}
    623 
    624 		if (idx == 0) {
    625 			if (lenp)
    626 				*lenp = length - 1;
    627 
    628 			return list;
    629 		}
    630 
    631 		list += length;
    632 		idx--;
    633 	}
    634 
    635 	if (lenp)
    636 		*lenp = -FDT_ERR_NOTFOUND;
    637 
    638 	return NULL;
    639 }
    640 
    641 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
    642 			      const char *compatible)
    643 {
    644 	const void *prop;
    645 	int len;
    646 
    647 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
    648 	if (!prop)
    649 		return len;
    650 	if (fdt_stringlist_contains(prop, len, compatible))
    651 		return 0;
    652 	else
    653 		return 1;
    654 }
    655 
    656 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
    657 				  const char *compatible)
    658 {
    659 	int offset, err;
    660 
    661 	FDT_CHECK_HEADER(fdt);
    662 
    663 	/* FIXME: The algorithm here is pretty horrible: we scan each
    664 	 * property of a node in fdt_node_check_compatible(), then if
    665 	 * that didn't find what we want, we scan over them again
    666 	 * making our way to the next node.  Still it's the easiest to
    667 	 * implement approach; performance can come later. */
    668 	for (offset = fdt_next_node(fdt, startoffset, NULL);
    669 	     offset >= 0;
    670 	     offset = fdt_next_node(fdt, offset, NULL)) {
    671 		err = fdt_node_check_compatible(fdt, offset, compatible);
    672 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
    673 			return err;
    674 		else if (err == 0)
    675 			return offset;
    676 	}
    677 
    678 	return offset; /* error from fdt_next_node() */
    679 }
    680