baseport/src/cedar/generic/base/syborg/svpplatform/libfdt/fdt_ro.c
changeset 0 ffa851df0825
equal deleted inserted replaced
-1:000000000000 0:ffa851df0825
       
     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 #define CHECK_HEADER(fdt) \
       
    59 	{ \
       
    60 		int err; \
       
    61 		if ((err = fdt_check_header(fdt)) != 0) \
       
    62 			return err; \
       
    63 	}
       
    64 
       
    65 static int nodename_eq(const void *fdt, int offset,
       
    66 		       const char *s, int len)
       
    67 {
       
    68 	const char *p = fdt_offset_ptr(fdt, offset, len+1);
       
    69 
       
    70 	if (! p)
       
    71 		/* short match */
       
    72 		return 0;
       
    73 
       
    74 	if (memcmp(p, s, len) != 0)
       
    75 		return 0;
       
    76 
       
    77 	if (p[len] == '\0')
       
    78 		return 1;
       
    79 	else if (!memchr(s, '@', len) && (p[len] == '@'))
       
    80 		return 1;
       
    81 	else
       
    82 		return 0;
       
    83 }
       
    84 
       
    85 const char *fdt_string(const void *fdt, int stroffset)
       
    86 {
       
    87 	return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
       
    88 }
       
    89 
       
    90 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
       
    91 {
       
    92 	CHECK_HEADER(fdt);
       
    93 	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
       
    94 	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
       
    95 	return 0;
       
    96 }
       
    97 
       
    98 int fdt_num_mem_rsv(const void *fdt)
       
    99 {
       
   100 	int i = 0;
       
   101 
       
   102 	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
       
   103 		i++;
       
   104 	return i;
       
   105 }
       
   106 
       
   107 int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
       
   108 			       const char *name, int namelen)
       
   109 {
       
   110 	int level = 0;
       
   111 	uint32_t tag;
       
   112 	int offset, nextoffset;
       
   113 
       
   114 	CHECK_HEADER(fdt);
       
   115 
       
   116 	tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
       
   117 	if (tag != FDT_BEGIN_NODE)
       
   118 		return -FDT_ERR_BADOFFSET;
       
   119 
       
   120 	do {
       
   121 		offset = nextoffset;
       
   122 		tag = fdt_next_tag(fdt, offset, &nextoffset);
       
   123 
       
   124 		switch (tag) {
       
   125 		case FDT_END:
       
   126 			return -FDT_ERR_TRUNCATED;
       
   127 
       
   128 		case FDT_BEGIN_NODE:
       
   129 			level++;
       
   130 			if (level != 1)
       
   131 				continue;
       
   132 			if (nodename_eq(fdt, offset+FDT_TAGSIZE, name, namelen))
       
   133 				/* Found it! */
       
   134 				return offset;
       
   135 			break;
       
   136 
       
   137 		case FDT_END_NODE:
       
   138 			level--;
       
   139 			break;
       
   140 
       
   141 		case FDT_PROP:
       
   142 		case FDT_NOP:
       
   143 			break;
       
   144 
       
   145 		default:
       
   146 			return -FDT_ERR_BADSTRUCTURE;
       
   147 		}
       
   148 	} while (level >= 0);
       
   149 
       
   150 	return -FDT_ERR_NOTFOUND;
       
   151 }
       
   152 
       
   153 int fdt_subnode_offset(const void *fdt, int parentoffset,
       
   154 		       const char *name)
       
   155 {
       
   156 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
       
   157 }
       
   158 
       
   159 int fdt_path_offset(const void *fdt, const char *path)
       
   160 {
       
   161 	const char *end = path + strlen(path);
       
   162 	const char *p = path;
       
   163 	int offset = 0;
       
   164 
       
   165 	CHECK_HEADER(fdt);
       
   166 
       
   167 	if (*path != '/')
       
   168 		return -FDT_ERR_BADPATH;
       
   169 
       
   170 	while (*p) {
       
   171 		const char *q;
       
   172 
       
   173 		while (*p == '/')
       
   174 			p++;
       
   175 		if (! *p)
       
   176 			return offset;
       
   177 		q = strchr(p, '/');
       
   178 		if (! q)
       
   179 			q = end;
       
   180 
       
   181 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
       
   182 		if (offset < 0)
       
   183 			return offset;
       
   184 
       
   185 		p = q;
       
   186 	}
       
   187 
       
   188 	return offset;
       
   189 }
       
   190 
       
   191 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
       
   192 {
       
   193 	const struct fdt_node_header *nh;
       
   194 	int err;
       
   195 
       
   196 	if ((err = fdt_check_header(fdt)) != 0)
       
   197 		goto fail;
       
   198 
       
   199 	err = -FDT_ERR_BADOFFSET;
       
   200 	nh = fdt_offset_ptr(fdt, nodeoffset, sizeof(*nh));
       
   201 	if (!nh || (fdt32_to_cpu(nh->tag) != FDT_BEGIN_NODE))
       
   202 		goto fail;
       
   203 
       
   204 	if (len)
       
   205 		*len = strlen(nh->name);
       
   206 
       
   207 	return nh->name;
       
   208 
       
   209  fail:
       
   210 	if (len)
       
   211 		*len = err;
       
   212 	return NULL;
       
   213 }
       
   214 
       
   215 const struct fdt_property *fdt_get_property(const void *fdt,
       
   216 					    int nodeoffset,
       
   217 					    const char *name, int *lenp)
       
   218 {
       
   219 	uint32_t tag;
       
   220 	const struct fdt_property *prop;
       
   221 	int namestroff;
       
   222 	int offset, nextoffset;
       
   223 	int err;
       
   224 
       
   225 	if ((err = fdt_check_header(fdt)) != 0)
       
   226 		goto fail;
       
   227 
       
   228 	err = -FDT_ERR_BADOFFSET;
       
   229 	if (nodeoffset % FDT_TAGSIZE)
       
   230 		goto fail;
       
   231 
       
   232 	tag = fdt_next_tag(fdt, nodeoffset, &nextoffset);
       
   233 	if (tag != FDT_BEGIN_NODE)
       
   234 		goto fail;
       
   235 
       
   236 	do {
       
   237 		offset = nextoffset;
       
   238 
       
   239 		tag = fdt_next_tag(fdt, offset, &nextoffset);
       
   240 		switch (tag) {
       
   241 		case FDT_END:
       
   242 			err = -FDT_ERR_TRUNCATED;
       
   243 			goto fail;
       
   244 
       
   245 		case FDT_BEGIN_NODE:
       
   246 		case FDT_END_NODE:
       
   247 		case FDT_NOP:
       
   248 			break;
       
   249 
       
   250 		case FDT_PROP:
       
   251 			err = -FDT_ERR_BADSTRUCTURE;
       
   252 			prop = fdt_offset_ptr(fdt, offset, sizeof(*prop));
       
   253 			if (! prop)
       
   254 				goto fail;
       
   255 			namestroff = fdt32_to_cpu(prop->nameoff);
       
   256 			if (streq(fdt_string(fdt, namestroff), name)) {
       
   257 				/* Found it! */
       
   258 				int len = fdt32_to_cpu(prop->len);
       
   259 				prop = fdt_offset_ptr(fdt, offset,
       
   260 						      sizeof(*prop)+len);
       
   261 				if (! prop)
       
   262 					goto fail;
       
   263 
       
   264 				if (lenp)
       
   265 					*lenp = len;
       
   266 
       
   267 				return prop;
       
   268 			}
       
   269 			break;
       
   270 
       
   271 		default:
       
   272 			err = -FDT_ERR_BADSTRUCTURE;
       
   273 			goto fail;
       
   274 		}
       
   275 	} while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
       
   276 
       
   277 	err = -FDT_ERR_NOTFOUND;
       
   278  fail:
       
   279 	if (lenp)
       
   280 		*lenp = err;
       
   281 	return NULL;
       
   282 }
       
   283 
       
   284 const void *fdt_getprop(const void *fdt, int nodeoffset,
       
   285 		  const char *name, int *lenp)
       
   286 {
       
   287 	const struct fdt_property *prop;
       
   288 
       
   289 	prop = fdt_get_property(fdt, nodeoffset, name, lenp);
       
   290 	if (! prop)
       
   291 		return NULL;
       
   292 
       
   293 	return prop->data;
       
   294 }
       
   295 
       
   296 uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
       
   297 {
       
   298 	const uint32_t *php;
       
   299 	int len;
       
   300 
       
   301 	php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
       
   302 	if (!php || (len != sizeof(*php)))
       
   303 		return 0;
       
   304 
       
   305 	return fdt32_to_cpu(*php);
       
   306 }
       
   307 
       
   308 int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
       
   309 {
       
   310 	uint32_t tag;
       
   311 	int p = 0, overflow = 0;
       
   312 	int offset, nextoffset, namelen;
       
   313 	const char *name;
       
   314 
       
   315 	CHECK_HEADER(fdt);
       
   316 
       
   317 	tag = fdt_next_tag(fdt, 0, &nextoffset);
       
   318 	if (tag != FDT_BEGIN_NODE)
       
   319 		return -FDT_ERR_BADSTRUCTURE;
       
   320 
       
   321 	if (buflen < 2)
       
   322 		return -FDT_ERR_NOSPACE;
       
   323 	buf[0] = '/';
       
   324 	p = 1;
       
   325 
       
   326 	while (nextoffset <= nodeoffset) {
       
   327 		offset = nextoffset;
       
   328 		tag = fdt_next_tag(fdt, offset, &nextoffset);
       
   329 		switch (tag) {
       
   330 		case FDT_END:
       
   331 			return -FDT_ERR_BADOFFSET;
       
   332 
       
   333 		case FDT_BEGIN_NODE:
       
   334 			name = fdt_get_name(fdt, offset, &namelen);
       
   335 			if (!name)
       
   336 				return namelen;
       
   337 			if (overflow || ((p + namelen + 1) > buflen)) {
       
   338 				overflow++;
       
   339 				break;
       
   340 			}
       
   341 			memcpy(buf + p, name, namelen);
       
   342 			p += namelen;
       
   343 			buf[p++] = '/';
       
   344 			break;
       
   345 
       
   346 		case FDT_END_NODE:
       
   347 			if (overflow) {
       
   348 				overflow--;
       
   349 				break;
       
   350 			}
       
   351 			do {
       
   352 				p--;
       
   353 			} while  (buf[p-1] != '/');
       
   354 			break;
       
   355 
       
   356 		case FDT_PROP:
       
   357 		case FDT_NOP:
       
   358 			break;
       
   359 
       
   360 		default:
       
   361 			return -FDT_ERR_BADSTRUCTURE;
       
   362 		}
       
   363 	}
       
   364 
       
   365 	if (overflow)
       
   366 		return -FDT_ERR_NOSPACE;
       
   367 
       
   368 	if (p > 1) /* special case so that root path is "/", not "" */
       
   369 		p--;
       
   370 	buf[p] = '\0';
       
   371 	return p;
       
   372 }
       
   373 
       
   374 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
       
   375 				 int supernodedepth, int *nodedepth)
       
   376 {
       
   377 	int level = -1;
       
   378 	uint32_t tag;
       
   379 	int offset, nextoffset = 0;
       
   380 	int supernodeoffset = -FDT_ERR_INTERNAL;
       
   381 
       
   382 	CHECK_HEADER(fdt);
       
   383 
       
   384 	if (supernodedepth < 0)
       
   385 		return -FDT_ERR_NOTFOUND;
       
   386 
       
   387 	do {
       
   388 		offset = nextoffset;
       
   389 		tag = fdt_next_tag(fdt, offset, &nextoffset);
       
   390 		switch (tag) {
       
   391 		case FDT_END:
       
   392 			return -FDT_ERR_BADOFFSET;
       
   393 
       
   394 		case FDT_BEGIN_NODE:
       
   395 			level++;
       
   396 			if (level == supernodedepth)
       
   397 				supernodeoffset = offset;
       
   398 			break;
       
   399 
       
   400 		case FDT_END_NODE:
       
   401 			level--;
       
   402 			break;
       
   403 
       
   404 		case FDT_PROP:
       
   405 		case FDT_NOP:
       
   406 			break;
       
   407 
       
   408 		default:
       
   409 			return -FDT_ERR_BADSTRUCTURE;
       
   410 		}
       
   411 	} while (offset < nodeoffset);
       
   412 
       
   413 	if (nodedepth)
       
   414 		*nodedepth = level;
       
   415 
       
   416 	if (supernodedepth > level)
       
   417 		return -FDT_ERR_NOTFOUND;
       
   418 	return supernodeoffset;
       
   419 }
       
   420 
       
   421 int fdt_node_depth(const void *fdt, int nodeoffset)
       
   422 {
       
   423 	int nodedepth;
       
   424 	int err;
       
   425 
       
   426 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
       
   427 	if (err)
       
   428 		return (err < 0) ? err : -FDT_ERR_INTERNAL;
       
   429 	return nodedepth;
       
   430 }
       
   431 
       
   432 int fdt_parent_offset(const void *fdt, int nodeoffset)
       
   433 {
       
   434 	int nodedepth = fdt_node_depth(fdt, nodeoffset);
       
   435 
       
   436 	if (nodedepth < 0)
       
   437 		return nodedepth;
       
   438 	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
       
   439 					    nodedepth - 1, NULL);
       
   440 }
       
   441 
       
   442 int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
       
   443 				  const char *propname,
       
   444 				  const void *propval, int proplen)
       
   445 {
       
   446 	uint32_t tag;
       
   447 	int offset, nextoffset;
       
   448 	const void *val;
       
   449 	int len;
       
   450 
       
   451 	CHECK_HEADER(fdt);
       
   452 
       
   453 	if (startoffset >= 0) {
       
   454 		tag = fdt_next_tag(fdt, startoffset, &nextoffset);
       
   455 		if (tag != FDT_BEGIN_NODE)
       
   456 			return -FDT_ERR_BADOFFSET;
       
   457 	} else {
       
   458 		nextoffset = 0;
       
   459 	}
       
   460 
       
   461 	/* FIXME: The algorithm here is pretty horrible: we scan each
       
   462 	 * property of a node in fdt_getprop(), then if that didn't
       
   463 	 * find what we want, we scan over them again making our way
       
   464 	 * to the next node.  Still it's the easiest to implement
       
   465 	 * approach; performance can come later. */
       
   466 	do {
       
   467 		offset = nextoffset;
       
   468 		tag = fdt_next_tag(fdt, offset, &nextoffset);
       
   469 
       
   470 		switch (tag) {
       
   471 		case FDT_BEGIN_NODE:
       
   472 			val = fdt_getprop(fdt, offset, propname, &len);
       
   473 			if (val
       
   474 			    && (len == proplen)
       
   475 			    && (memcmp(val, propval, len) == 0))
       
   476 				return offset;
       
   477 			break;
       
   478 
       
   479 		case FDT_PROP:
       
   480 		case FDT_END:
       
   481 		case FDT_END_NODE:
       
   482 		case FDT_NOP:
       
   483 			break;
       
   484 
       
   485 		default:
       
   486 			return -FDT_ERR_BADSTRUCTURE;
       
   487 		}
       
   488 	} while (tag != FDT_END);
       
   489 
       
   490 	return -FDT_ERR_NOTFOUND;
       
   491 }
       
   492 
       
   493 int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
       
   494 {
       
   495 	if ((phandle == 0) || (phandle == -1))
       
   496 		return -FDT_ERR_BADPHANDLE;
       
   497 	phandle = cpu_to_fdt32(phandle);
       
   498 	return fdt_node_offset_by_prop_value(fdt, -1, "linux,phandle",
       
   499 					     &phandle, sizeof(phandle));
       
   500 }
       
   501 
       
   502 int _stringlist_contains(const void *strlist, int listlen, const char *str)
       
   503 {
       
   504 	int len = strlen(str);
       
   505 	const void *p;
       
   506 
       
   507 	while (listlen >= len) {
       
   508 		if (memcmp(str, strlist, len+1) == 0)
       
   509 			return 1;
       
   510 		p = memchr(strlist, '\0', listlen);
       
   511 		if (!p)
       
   512 			return 0; /* malformed strlist.. */
       
   513 		listlen -= (p-strlist) + 1;
       
   514 		strlist = p + 1;
       
   515 	}
       
   516 	return 0;
       
   517 }
       
   518 
       
   519 int fdt_node_check_compatible(const void *fdt, int nodeoffset,
       
   520 			      const char *compatible)
       
   521 {
       
   522 	const void *prop;
       
   523 	int len;
       
   524 
       
   525 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
       
   526 	if (!prop)
       
   527 		return len;
       
   528 	if (_stringlist_contains(prop, len, compatible))
       
   529 		return 0;
       
   530 	else
       
   531 		return 1;
       
   532 }
       
   533 
       
   534 int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
       
   535 				  const char *compatible)
       
   536 {
       
   537 	uint32_t tag;
       
   538 	int offset, nextoffset;
       
   539 	int err;
       
   540 
       
   541 	CHECK_HEADER(fdt);
       
   542 
       
   543 	if (startoffset >= 0) {
       
   544 		tag = fdt_next_tag(fdt, startoffset, &nextoffset);
       
   545 		if (tag != FDT_BEGIN_NODE)
       
   546 			return -FDT_ERR_BADOFFSET;
       
   547 	} else {
       
   548 		nextoffset = 0;
       
   549 	}
       
   550 
       
   551 	/* FIXME: The algorithm here is pretty horrible: we scan each
       
   552 	 * property of a node in fdt_node_check_compatible(), then if
       
   553 	 * that didn't find what we want, we scan over them again
       
   554 	 * making our way to the next node.  Still it's the easiest to
       
   555 	 * implement approach; performance can come later. */
       
   556 	do {
       
   557 		offset = nextoffset;
       
   558 		tag = fdt_next_tag(fdt, offset, &nextoffset);
       
   559 
       
   560 		switch (tag) {
       
   561 		case FDT_BEGIN_NODE:
       
   562 			err = fdt_node_check_compatible(fdt, offset,
       
   563 							compatible);
       
   564 			if ((err < 0)
       
   565 			    && (err != -FDT_ERR_NOTFOUND))
       
   566 				return err;
       
   567 			else if (err == 0)
       
   568 				return offset;
       
   569 			break;
       
   570 
       
   571 		case FDT_PROP:
       
   572 		case FDT_END:
       
   573 		case FDT_END_NODE:
       
   574 		case FDT_NOP:
       
   575 			break;
       
   576 
       
   577 		default:
       
   578 			return -FDT_ERR_BADSTRUCTURE;
       
   579 		}
       
   580 	} while (tag != FDT_END);
       
   581 
       
   582 	return -FDT_ERR_NOTFOUND;
       
   583 }