250 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			250 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * libfdt - Flat Device Tree manipulation | ||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||
|  |  * | ||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||
|  |  * the GPL, or the BSD license, at your option. | ||
|  |  * | ||
|  |  *  a) This library is free software; you can redistribute it and/or | ||
|  |  *     modify it under the terms of the GNU General Public License as | ||
|  |  *     published by the Free Software Foundation; either version 2 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 General Public License for more details. | ||
|  |  * | ||
|  |  *     You should have received a copy of the GNU 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 | ||
|  |  * | ||
|  |  * Alternatively, | ||
|  |  * | ||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||
|  |  *     without modification, are permitted provided that the following | ||
|  |  *     conditions are met: | ||
|  |  * | ||
|  |  *     1. Redistributions of source code must retain the above | ||
|  |  *        copyright notice, this list of conditions and the following | ||
|  |  *        disclaimer. | ||
|  |  *     2. Redistributions in binary form must reproduce the above | ||
|  |  *        copyright notice, this list of conditions and the following | ||
|  |  *        disclaimer in the documentation and/or other materials | ||
|  |  *        provided with the distribution. | ||
|  |  * | ||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|  |  */ | ||
|  | #include "libfdt_env.h"
 | ||
|  | #include "fdt.h"
 | ||
|  | #include "libfdt.h"
 | ||
|  | #include "libfdt_internal.h"
 | ||
|  | 
 | ||
|  | int fdt_check_header(const void *fdt) | ||
|  | { | ||
|  |     if (fdt_magic(fdt) == FDT_MAGIC) { | ||
|  |         /* Complete tree */ | ||
|  |         if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) | ||
|  |             return -FDT_ERR_BADVERSION; | ||
|  |         if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) | ||
|  |             return -FDT_ERR_BADVERSION; | ||
|  |     } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { | ||
|  |         /* Unfinished sequential-write blob */ | ||
|  |         if (fdt_size_dt_struct(fdt) == 0) | ||
|  |             return -FDT_ERR_BADSTATE; | ||
|  |     } else { | ||
|  |         return -FDT_ERR_BADMAGIC; | ||
|  |     } | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) | ||
|  | { | ||
|  |     unsigned absoffset = offset + fdt_off_dt_struct(fdt); | ||
|  | 
 | ||
|  |     if ((absoffset < offset) | ||
|  |         || ((absoffset + len) < absoffset) | ||
|  |         || (absoffset + len) > fdt_totalsize(fdt)) | ||
|  |         return NULL; | ||
|  | 
 | ||
|  |     if (fdt_version(fdt) >= 0x11) | ||
|  |         if (((offset + len) < offset) | ||
|  |             || ((offset + len) > fdt_size_dt_struct(fdt))) | ||
|  |             return NULL; | ||
|  | 
 | ||
|  |     return _fdt_offset_ptr(fdt, offset); | ||
|  | } | ||
|  | 
 | ||
|  | uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) | ||
|  | { | ||
|  |     const fdt32_t *tagp, *lenp; | ||
|  |     uint32_t tag; | ||
|  |     int offset = startoffset; | ||
|  |     const char *p; | ||
|  | 
 | ||
|  |     *nextoffset = -FDT_ERR_TRUNCATED; | ||
|  |     tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); | ||
|  |     if (!tagp) | ||
|  |         return FDT_END; /* premature end */ | ||
|  |     tag = fdt32_to_cpu(*tagp); | ||
|  |     offset += FDT_TAGSIZE; | ||
|  | 
 | ||
|  |     *nextoffset = -FDT_ERR_BADSTRUCTURE; | ||
|  |     switch (tag) { | ||
|  |     case FDT_BEGIN_NODE: | ||
|  |         /* skip name */ | ||
|  |         do { | ||
|  |             p = fdt_offset_ptr(fdt, offset++, 1); | ||
|  |         } while (p && (*p != '\0')); | ||
|  |         if (!p) | ||
|  |             return FDT_END; /* premature end */ | ||
|  |         break; | ||
|  | 
 | ||
|  |     case FDT_PROP: | ||
|  |         lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); | ||
|  |         if (!lenp) | ||
|  |             return FDT_END; /* premature end */ | ||
|  |         /* skip-name offset, length and value */ | ||
|  |         offset += sizeof(struct fdt_property) - FDT_TAGSIZE | ||
|  |             + fdt32_to_cpu(*lenp); | ||
|  |         break; | ||
|  | 
 | ||
|  |     case FDT_END: | ||
|  |     case FDT_END_NODE: | ||
|  |     case FDT_NOP: | ||
|  |         break; | ||
|  | 
 | ||
|  |     default: | ||
|  |         return FDT_END; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) | ||
|  |         return FDT_END; /* premature end */ | ||
|  | 
 | ||
|  |     *nextoffset = FDT_TAGALIGN(offset); | ||
|  |     return tag; | ||
|  | } | ||
|  | 
 | ||
|  | int _fdt_check_node_offset(const void *fdt, int offset) | ||
|  | { | ||
|  |     if ((offset < 0) || (offset % FDT_TAGSIZE) | ||
|  |         || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) | ||
|  |         return -FDT_ERR_BADOFFSET; | ||
|  | 
 | ||
|  |     return offset; | ||
|  | } | ||
|  | 
 | ||
|  | int _fdt_check_prop_offset(const void *fdt, int offset) | ||
|  | { | ||
|  |     if ((offset < 0) || (offset % FDT_TAGSIZE) | ||
|  |         || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) | ||
|  |         return -FDT_ERR_BADOFFSET; | ||
|  | 
 | ||
|  |     return offset; | ||
|  | } | ||
|  | 
 | ||
|  | int fdt_next_node(const void *fdt, int offset, int *depth) | ||
|  | { | ||
|  |     int nextoffset = 0; | ||
|  |     uint32_t tag; | ||
|  | 
 | ||
|  |     if (offset >= 0) | ||
|  |         if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) | ||
|  |             return nextoffset; | ||
|  | 
 | ||
|  |     do { | ||
|  |         offset = nextoffset; | ||
|  |         tag = fdt_next_tag(fdt, offset, &nextoffset); | ||
|  | 
 | ||
|  |         switch (tag) { | ||
|  |         case FDT_PROP: | ||
|  |         case FDT_NOP: | ||
|  |             break; | ||
|  | 
 | ||
|  |         case FDT_BEGIN_NODE: | ||
|  |             if (depth) | ||
|  |                 (*depth)++; | ||
|  |             break; | ||
|  | 
 | ||
|  |         case FDT_END_NODE: | ||
|  |             if (depth && ((--(*depth)) < 0)) | ||
|  |                 return nextoffset; | ||
|  |             break; | ||
|  | 
 | ||
|  |         case FDT_END: | ||
|  |             if ((nextoffset >= 0) | ||
|  |                 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) | ||
|  |                 return -FDT_ERR_NOTFOUND; | ||
|  |             else | ||
|  |                 return nextoffset; | ||
|  |         } | ||
|  |     } while (tag != FDT_BEGIN_NODE); | ||
|  | 
 | ||
|  |     return offset; | ||
|  | } | ||
|  | 
 | ||
|  | int fdt_first_subnode(const void *fdt, int offset) | ||
|  | { | ||
|  |     int depth = 0; | ||
|  | 
 | ||
|  |     offset = fdt_next_node(fdt, offset, &depth); | ||
|  |     if (offset < 0 || depth != 1) | ||
|  |         return -FDT_ERR_NOTFOUND; | ||
|  | 
 | ||
|  |     return offset; | ||
|  | } | ||
|  | 
 | ||
|  | int fdt_next_subnode(const void *fdt, int offset) | ||
|  | { | ||
|  |     int depth = 1; | ||
|  | 
 | ||
|  |     /*
 | ||
|  |      * With respect to the parent, the depth of the next subnode will be | ||
|  |      * the same as the last. | ||
|  |      */ | ||
|  |     do { | ||
|  |         offset = fdt_next_node(fdt, offset, &depth); | ||
|  |         if (offset < 0 || depth < 1) | ||
|  |             return -FDT_ERR_NOTFOUND; | ||
|  |     } while (depth > 1); | ||
|  | 
 | ||
|  |     return offset; | ||
|  | } | ||
|  | 
 | ||
|  | const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) | ||
|  | { | ||
|  |     int len = strlen(s) + 1; | ||
|  |     const char *last = strtab + tabsize - len; | ||
|  |     const char *p; | ||
|  | 
 | ||
|  |     for (p = strtab; p <= last; p++) | ||
|  |         if (memcmp(p, s, len) == 0) | ||
|  |             return p; | ||
|  |     return NULL; | ||
|  | } | ||
|  | 
 | ||
|  | int fdt_move(const void *fdt, void *buf, int bufsize) | ||
|  | { | ||
|  |     FDT_CHECK_HEADER(fdt); | ||
|  | 
 | ||
|  |     if (fdt_totalsize(fdt) > bufsize) | ||
|  |         return -FDT_ERR_NOSPACE; | ||
|  | 
 | ||
|  |     memmove(buf, fdt, fdt_totalsize(fdt)); | ||
|  |     return 0; | ||
|  | } |