blob: 56a9787cd07f58e7ea0b8d649d8a4d1b821de7e6 [file] [log] [blame]
/*
* 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;
}