mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-31 03:58:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			403 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * libfdt - Flat Device Tree manipulation
 | |
|  * Copyright (C) 2006 David Gibson, IBM Corporation.
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public License
 | |
|  * as published by the Free Software Foundation; either version 2.1 of
 | |
|  * the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| #include "libfdt_env.h"
 | |
| 
 | |
| #include <fdt.h>
 | |
| #include <libfdt.h>
 | |
| 
 | |
| #include "libfdt_internal.h"
 | |
| 
 | |
| #define CHECK_HEADER(fdt)	{ \
 | |
| 	int err; \
 | |
| 	if ((err = fdt_check_header(fdt)) != 0) \
 | |
| 		return err; \
 | |
| }
 | |
| 
 | |
| static int offset_streq(const void *fdt, int offset,
 | |
| 			const char *s, int len)
 | |
| {
 | |
| 	const char *p = fdt_offset_ptr(fdt, offset, len+1);
 | |
| 
 | |
| 	if (! p)
 | |
| 		/* short match */
 | |
| 		return 0;
 | |
| 
 | |
| 	if (memcmp(p, s, len) != 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (p[len] != '\0')
 | |
| 		return 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return a pointer to the string at the given string offset.
 | |
|  */
 | |
| char *fdt_string(const void *fdt, int stroffset)
 | |
| {
 | |
| 	return (char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return the node offset of the node specified by:
 | |
|  *   parentoffset - starting place (0 to start at the root)
 | |
|  *   name         - name being searched for
 | |
|  *   namelen      - length of the name: typically strlen(name)
 | |
|  *
 | |
|  * Notes:
 | |
|  *   If the start node has subnodes, the subnodes are _not_ searched for the
 | |
|  *     requested name.
 | |
|  */
 | |
| int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
 | |
| 			       const char *name, int namelen)
 | |
| {
 | |
| 	int level = 0;
 | |
| 	uint32_t tag;
 | |
| 	int offset, nextoffset;
 | |
| 
 | |
| 	CHECK_HEADER(fdt);
 | |
| 
 | |
| 	tag = fdt_next_tag(fdt, parentoffset, &nextoffset, NULL);
 | |
| 	if (tag != FDT_BEGIN_NODE)
 | |
| 		return -FDT_ERR_BADOFFSET;
 | |
| 
 | |
| 	do {
 | |
| 		offset = nextoffset;
 | |
| 		tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
 | |
| 
 | |
| 		switch (tag) {
 | |
| 		case FDT_END:
 | |
| 			return -FDT_ERR_TRUNCATED;
 | |
| 
 | |
| 		case FDT_BEGIN_NODE:
 | |
| 			level++;
 | |
| 			/*
 | |
| 			 * If we are nested down levels, ignore the strings
 | |
| 			 * until we get back to the proper level.
 | |
| 			 */
 | |
| 			if (level != 1)
 | |
| 				continue;
 | |
| 
 | |
| 			/* Return the offset if this is "our" string. */
 | |
| 			if (offset_streq(fdt, offset+FDT_TAGSIZE, name, namelen))
 | |
| 				return offset;
 | |
| 			break;
 | |
| 
 | |
| 		case FDT_END_NODE:
 | |
| 			level--;
 | |
| 			break;
 | |
| 
 | |
| 		case FDT_PROP:
 | |
| 		case FDT_NOP:
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			return -FDT_ERR_BADSTRUCTURE;
 | |
| 		}
 | |
| 	} while (level >= 0);
 | |
| 
 | |
| 	return -FDT_ERR_NOTFOUND;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * See fdt_subnode_offset_namelen()
 | |
|  */
 | |
| int fdt_subnode_offset(const void *fdt, int parentoffset,
 | |
| 		       const char *name)
 | |
| {
 | |
| 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Searches for the node corresponding to the given path and returns the
 | |
|  * offset of that node.
 | |
|  */
 | |
| int fdt_path_offset(const void *fdt, const char *path)
 | |
| {
 | |
| 	const char *end = path + strlen(path);
 | |
| 	const char *p = path;
 | |
| 	int offset = 0;
 | |
| 
 | |
| 	CHECK_HEADER(fdt);
 | |
| 
 | |
| 	/* Paths must be absolute */
 | |
| 	if (*path != '/')
 | |
| 		return -FDT_ERR_BADPATH;
 | |
| 
 | |
| 	while (*p) {
 | |
| 		const char *q;
 | |
| 
 | |
| 		/* Skip path separator(s) */
 | |
| 		while (*p == '/')
 | |
| 			p++;
 | |
| 		if (! *p)
 | |
| 			return -FDT_ERR_BADPATH;
 | |
| 
 | |
| 		/*
 | |
| 		 * Find the next path separator.  The characters between
 | |
| 		 * p and q are the next segment of the the path to find.
 | |
| 		 */
 | |
| 		q = strchr(p, '/');
 | |
| 		if (! q)
 | |
| 			q = end;
 | |
| 
 | |
| 		/*
 | |
| 		 * Find the offset corresponding to the this path segment.
 | |
| 		 */
 | |
| 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
 | |
| 
 | |
| 		/* Oops, error, abort abort abort */
 | |
| 		if (offset < 0)
 | |
| 			return offset;
 | |
| 
 | |
| 		p = q;
 | |
| 	}
 | |
| 
 | |
| 	return offset;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Given the offset of a node and a name of a property in that node, return
 | |
|  * a pointer to the property struct.
 | |
|  */
 | |
| struct fdt_property *fdt_get_property(const void *fdt,
 | |
| 				      int nodeoffset,
 | |
| 				      const char *name, int *lenp)
 | |
| {
 | |
| 	int level = 0;
 | |
| 	uint32_t tag;
 | |
| 	struct fdt_property *prop;
 | |
| 	int namestroff;
 | |
| 	int offset, nextoffset;
 | |
| 	int err;
 | |
| 
 | |
| 	if ((err = fdt_check_header(fdt)) != 0)
 | |
| 		goto fail;
 | |
| 
 | |
| 	err = -FDT_ERR_BADOFFSET;
 | |
| 	if (nodeoffset % FDT_TAGSIZE)
 | |
| 		goto fail;
 | |
| 
 | |
| 	tag = fdt_next_tag(fdt, nodeoffset, &nextoffset, NULL);
 | |
| 	if (tag != FDT_BEGIN_NODE)
 | |
| 		goto fail;
 | |
| 
 | |
| 	do {
 | |
| 		offset = nextoffset;
 | |
| 
 | |
| 		tag = fdt_next_tag(fdt, offset, &nextoffset, NULL);
 | |
| 		switch (tag) {
 | |
| 		case FDT_END:
 | |
| 			err = -FDT_ERR_TRUNCATED;
 | |
| 			goto fail;
 | |
| 
 | |
| 		case FDT_BEGIN_NODE:
 | |
| 			level++;
 | |
| 			break;
 | |
| 
 | |
| 		case FDT_END_NODE:
 | |
| 			level--;
 | |
| 			break;
 | |
| 
 | |
| 		case FDT_PROP:
 | |
| 			/*
 | |
| 			 * If we are nested down levels, ignore the strings
 | |
| 			 * until we get back to the proper level.
 | |
| 			 */
 | |
| 			if (level != 0)
 | |
| 				continue;
 | |
| 
 | |
| 			err = -FDT_ERR_BADSTRUCTURE;
 | |
| 			prop = fdt_offset_ptr_typed(fdt, offset, prop);
 | |
| 			if (! prop)
 | |
| 				goto fail;
 | |
| 			namestroff = fdt32_to_cpu(prop->nameoff);
 | |
| 			if (streq(fdt_string(fdt, namestroff), name)) {
 | |
| 				/* Found it! */
 | |
| 				int len = fdt32_to_cpu(prop->len);
 | |
| 				prop = fdt_offset_ptr(fdt, offset,
 | |
| 						      sizeof(*prop)+len);
 | |
| 				if (! prop)
 | |
| 					goto fail;
 | |
| 
 | |
| 				if (lenp)
 | |
| 					*lenp = len;
 | |
| 
 | |
| 				return prop;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case FDT_NOP:
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			err = -FDT_ERR_BADSTRUCTURE;
 | |
| 			goto fail;
 | |
| 		}
 | |
| 	} while (level >= 0);
 | |
| 
 | |
| 	err = -FDT_ERR_NOTFOUND;
 | |
| fail:
 | |
| 	if (lenp)
 | |
| 		*lenp = err;
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Given the offset of a node and a name of a property in that node, return
 | |
|  * a pointer to the property data (ONLY).
 | |
|  */
 | |
| void *fdt_getprop(const void *fdt, int nodeoffset,
 | |
| 		  const char *name, int *lenp)
 | |
| {
 | |
| 	const struct fdt_property *prop;
 | |
| 
 | |
| 	prop = fdt_get_property(fdt, nodeoffset, name, lenp);
 | |
| 	if (! prop)
 | |
| 		return NULL;
 | |
| 
 | |
| 	return (void *)prop->data;
 | |
| }
 | |
| 
 | |
| 
 | |
| uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset, char **namep)
 | |
| {
 | |
| 	const uint32_t *tagp, *lenp;
 | |
| 	uint32_t tag;
 | |
| 	const char *p;
 | |
| 
 | |
| 	if (offset % FDT_TAGSIZE)
 | |
| 		return -1;
 | |
| 
 | |
| 	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
 | |
| 	if (! tagp)
 | |
| 		return FDT_END; /* premature end */
 | |
| 	tag = fdt32_to_cpu(*tagp);
 | |
| 	offset += FDT_TAGSIZE;
 | |
| 
 | |
| 	switch (tag) {
 | |
| 	case FDT_BEGIN_NODE:
 | |
| 		if(namep)
 | |
| 			*namep = fdt_offset_ptr(fdt, offset, 1);
 | |
| 
 | |
| 		/* skip name */
 | |
| 		do {
 | |
| 			p = fdt_offset_ptr(fdt, offset++, 1);
 | |
| 		} while (p && (*p != '\0'));
 | |
| 		if (! p)
 | |
| 			return FDT_END;
 | |
| 		break;
 | |
| 	case FDT_PROP:
 | |
| 		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
 | |
| 		if (! lenp)
 | |
| 			return FDT_END;
 | |
| 		/*
 | |
| 		 * Get the property and set the namep to the name.
 | |
| 		 */
 | |
| 		if(namep) {
 | |
| 			struct fdt_property *prop;
 | |
| 
 | |
| 			prop = fdt_offset_ptr_typed(fdt, offset - FDT_TAGSIZE, prop);
 | |
| 			if (! prop)
 | |
| 				return -FDT_ERR_BADSTRUCTURE;
 | |
| 			*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
 | |
| 		}
 | |
| 		/* skip name offset, length and value */
 | |
| 		offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (nextoffset)
 | |
| 		*nextoffset = ALIGN(offset, FDT_TAGSIZE);
 | |
| 
 | |
| 	return tag;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return the number of used reserve map entries and total slots available.
 | |
|  */
 | |
| int fdt_num_reservemap(void *fdt, int *used, int *total)
 | |
| {
 | |
| 	struct fdt_reserve_entry *re;
 | |
| 	int  start;
 | |
| 	int  end;
 | |
| 	int  err = fdt_check_header(fdt);
 | |
| 
 | |
| 	if (err != 0)
 | |
| 		return err;
 | |
| 
 | |
| 	start = fdt_off_mem_rsvmap(fdt);
 | |
| 
 | |
| 	/*
 | |
| 	 * Convention is that the reserve map is before the dt_struct,
 | |
| 	 * but it does not have to be.
 | |
| 	 */
 | |
| 	end = fdt_totalsize(fdt);
 | |
| 	if (end > fdt_off_dt_struct(fdt))
 | |
| 		end = fdt_off_dt_struct(fdt);
 | |
| 	if (end > fdt_off_dt_strings(fdt))
 | |
| 		end = fdt_off_dt_strings(fdt);
 | |
| 
 | |
| 	/*
 | |
| 	 * Since the reserved area list is zero terminated, you get one fewer.
 | |
| 	 */
 | |
| 	if (total)
 | |
| 		*total = ((end - start) / sizeof(struct fdt_reserve_entry)) - 1;
 | |
| 
 | |
| 	if (used) {
 | |
| 		*used = 0;
 | |
| 		while (start < end) {
 | |
| 			re = (struct fdt_reserve_entry *)(fdt + start);
 | |
| 			if (re->size == 0)
 | |
| 				return 0;	/* zero size terminates the list */
 | |
| 
 | |
| 			*used += 1;
 | |
| 			start += sizeof(struct fdt_reserve_entry);
 | |
| 		}
 | |
| 		/*
 | |
| 		 * If we get here, there was no zero size termination.
 | |
| 		 */
 | |
| 		return -FDT_ERR_BADLAYOUT;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Return the nth reserve map entry.
 | |
|  */
 | |
| int fdt_get_reservemap(void *fdt, int n, struct fdt_reserve_entry *re)
 | |
| {
 | |
| 	int  used;
 | |
| 	int  total;
 | |
| 	int  err;
 | |
| 
 | |
| 	err = fdt_num_reservemap(fdt, &used, &total);
 | |
| 	if (err != 0)
 | |
| 		return err;
 | |
| 
 | |
| 	if (n >= total)
 | |
| 		return -FDT_ERR_NOSPACE;
 | |
| 	if (re) {
 | |
| 		*re = *(struct fdt_reserve_entry *)
 | |
| 			_fdt_offset_ptr(fdt, n * sizeof(struct fdt_reserve_entry));
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 |