Skip to content
Snippets Groups Projects
fdt.c 4.32 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * libfdt - Flat Device Tree manipulation
     * Copyright (C) 2006 David Gibson, IBM Corporation.
    
     * SPDX-License-Identifier:	GPL-2.0+ BSD-2-Clause
    
    #include <libfdt_env.h>
    
    #ifndef USE_HOSTCC
    
    #include <fdt.h>
    #include <libfdt.h>
    
    #else
    #include "fdt_host.h"
    #endif
    
    
    #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;
    
    	int offset = startoffset;
    
    	*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 */
    
    	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:
    
    
    	default:
    		return FDT_END;
    
    	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
    		return FDT_END; /* premature end */
    
    	*nextoffset = FDT_TAGALIGN(offset);
    
    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;
    
    			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;
    }