fdt.c revision 204433
143166Snsouch/*
243166Snsouch * libfdt - Flat Device Tree manipulation
343166Snsouch * Copyright (C) 2006 David Gibson, IBM Corporation.
443166Snsouch *
543166Snsouch * libfdt is dual licensed: you can use it either under the terms of
643166Snsouch * the GPL, or the BSD license, at your option.
743166Snsouch *
843166Snsouch *  a) This library is free software; you can redistribute it and/or
943166Snsouch *     modify it under the terms of the GNU General Public License as
1043166Snsouch *     published by the Free Software Foundation; either version 2 of the
1143166Snsouch *     License, or (at your option) any later version.
1243166Snsouch *
1343166Snsouch *     This library is distributed in the hope that it will be useful,
1443166Snsouch *     but WITHOUT ANY WARRANTY; without even the implied warranty of
1543166Snsouch *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1643166Snsouch *     GNU General Public License for more details.
1743166Snsouch *
1843166Snsouch *     You should have received a copy of the GNU General Public
1943166Snsouch *     License along with this library; if not, write to the Free
2043166Snsouch *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
2143166Snsouch *     MA 02110-1301 USA
2243166Snsouch *
2343166Snsouch * Alternatively,
2443166Snsouch *
2543166Snsouch *  b) Redistribution and use in source and binary forms, with or
2643166Snsouch *     without modification, are permitted provided that the following
27116192Sobrien *     conditions are met:
28116192Sobrien *
29116192Sobrien *     1. Redistributions of source code must retain the above
3043166Snsouch *        copyright notice, this list of conditions and the following
3143166Snsouch *        disclaimer.
32165951Sjhb *     2. Redistributions in binary form must reproduce the above
3343166Snsouch *        copyright notice, this list of conditions and the following
34165951Sjhb *        disclaimer in the documentation and/or other materials
3543166Snsouch *        provided with the distribution.
36165951Sjhb *
3746651Speter *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38165951Sjhb *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
3943166Snsouch *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
4043166Snsouch *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
4143166Snsouch *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
4243166Snsouch *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43119288Simp *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44119288Simp *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45272017Srpaulo *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46306814Savg *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
4743166Snsouch *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
4843166Snsouch *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
4943166Snsouch *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50165951Sjhb */
51165951Sjhb#include "libfdt_env.h"
52165951Sjhb
53165951Sjhb#include <fdt.h>
54165951Sjhb#include <libfdt.h>
55165951Sjhb
56305462Savg#include "libfdt_internal.h"
57165951Sjhb
58197128Savgint fdt_check_header(const void *fdt)
59305462Savg{
60197128Savg	if (fdt_magic(fdt) == FDT_MAGIC) {
61165951Sjhb		/* Complete tree */
6243166Snsouch		if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
63162289Sjhb			return -FDT_ERR_BADVERSION;
64165951Sjhb		if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
65165951Sjhb			return -FDT_ERR_BADVERSION;
66165951Sjhb	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
67165951Sjhb		/* Unfinished sequential-write blob */
6843166Snsouch		if (fdt_size_dt_struct(fdt) == 0)
6943166Snsouch			return -FDT_ERR_BADSTATE;
70165951Sjhb	} else {
71165951Sjhb		return -FDT_ERR_BADMAGIC;
72165951Sjhb	}
73165951Sjhb
74162234Sjhb	return 0;
7543166Snsouch}
7643166Snsouch
7743166Snsouchconst void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
7843166Snsouch{
7943166Snsouch	const char *p;
8043166Snsouch
8143166Snsouch	if (fdt_version(fdt) >= 0x11)
8243166Snsouch		if (((offset + len) < offset)
8343166Snsouch		    || ((offset + len) > fdt_size_dt_struct(fdt)))
84162234Sjhb			return NULL;
85165951Sjhb
86165951Sjhb	p = _fdt_offset_ptr(fdt, offset);
87165951Sjhb
88165951Sjhb	if (p + len < p)
89165951Sjhb		return NULL;
90162289Sjhb	return p;
91165951Sjhb}
92165951Sjhb
93165951Sjhbuint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
9443166Snsouch{
95165951Sjhb	const uint32_t *tagp, *lenp;
96165951Sjhb	uint32_t tag;
97165951Sjhb	int offset = startoffset;
98165951Sjhb	const char *p;
99165951Sjhb
100165951Sjhb	*nextoffset = -FDT_ERR_TRUNCATED;
101165951Sjhb	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
102165951Sjhb	if (!tagp)
103165951Sjhb		return FDT_END; /* premature end */
104234338Savg	tag = fdt32_to_cpu(*tagp);
105234338Savg	offset += FDT_TAGSIZE;
106234338Savg
107306814Savg	*nextoffset = -FDT_ERR_BADSTRUCTURE;
108306120Savg	switch (tag) {
109197128Savg	case FDT_BEGIN_NODE:
110306814Savg		/* skip name */
111306814Savg		do {
112306120Savg			p = fdt_offset_ptr(fdt, offset++, 1);
113306120Savg		} while (p && (*p != '\0'));
114165951Sjhb		if (!p)
115165951Sjhb			return FDT_END; /* premature end */
116165951Sjhb		break;
11743166Snsouch
118165951Sjhb	case FDT_PROP:
119165951Sjhb		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
120162289Sjhb		if (!lenp)
121305462Savg			return FDT_END; /* premature end */
122306814Savg		/* skip-name offset, length and value */
123305462Savg		offset += sizeof(struct fdt_property) - FDT_TAGSIZE
124305462Savg			+ fdt32_to_cpu(*lenp);
125305462Savg		break;
126305462Savg
127305462Savg	case FDT_END:
128165951Sjhb	case FDT_END_NODE:
129305462Savg	case FDT_NOP:
130305462Savg		break;
131305462Savg
132305462Savg	default:
133305462Savg		return FDT_END;
134306814Savg	}
135306814Savg
136305462Savg	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
137305462Savg		return FDT_END; /* premature end */
138305462Savg
139306814Savg	*nextoffset = FDT_TAGALIGN(offset);
140305462Savg	return tag;
141305462Savg}
142306814Savg
143305462Savgint _fdt_check_node_offset(const void *fdt, int offset)
144305462Savg{
145305462Savg	if ((offset < 0) || (offset % FDT_TAGSIZE)
146305462Savg	    || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
147305462Savg		return -FDT_ERR_BADOFFSET;
148305462Savg
149305462Savg	return offset;
150306814Savg}
151305462Savg
152305462Savgint fdt_next_node(const void *fdt, int offset, int *depth)
153305462Savg{
154305462Savg	int nextoffset = 0;
155305462Savg	uint32_t tag;
156306814Savg
157306814Savg	if (offset >= 0)
158306814Savg		if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
159306814Savg			return nextoffset;
160306814Savg
161306814Savg	do {
162306814Savg		offset = nextoffset;
163306814Savg		tag = fdt_next_tag(fdt, offset, &nextoffset);
164306814Savg
165306814Savg		switch (tag) {
166306814Savg		case FDT_PROP:
167306814Savg		case FDT_NOP:
168306814Savg			break;
169306814Savg
170306814Savg		case FDT_BEGIN_NODE:
171306814Savg			if (depth)
172305462Savg				(*depth)++;
173305462Savg			break;
174305462Savg
175305462Savg		case FDT_END_NODE:
176306814Savg			if (depth && ((--(*depth)) < 0))
177306814Savg				return nextoffset;
178305462Savg			break;
179305462Savg
180305462Savg		case FDT_END:
181306814Savg			if ((nextoffset >= 0)
182305462Savg			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
183305462Savg				return -FDT_ERR_NOTFOUND;
184305462Savg			else
185305462Savg				return nextoffset;
186305462Savg		}
187305462Savg	} while (tag != FDT_BEGIN_NODE);
188305462Savg
189305462Savg	return offset;
190305462Savg}
191305462Savg
192305462Savgconst char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
193306814Savg{
194305462Savg	int len = strlen(s) + 1;
195305462Savg	const char *last = strtab + tabsize - len;
196305462Savg	const char *p;
197305462Savg
198306122Savg	for (p = strtab; p <= last; p++)
199306122Savg		if (memcmp(p, s, len) == 0)
200306122Savg			return p;
201306122Savg	return NULL;
202306122Savg}
203306122Savg
204306122Savgint fdt_move(const void *fdt, void *buf, int bufsize)
205306122Savg{
206306122Savg	FDT_CHECK_HEADER(fdt);
207306122Savg
208306122Savg	if (fdt_totalsize(fdt) > bufsize)
209306122Savg		return -FDT_ERR_NOSPACE;
210306122Savg
211306122Savg	memmove(buf, fdt, fdt_totalsize(fdt));
212306122Savg	return 0;
213306122Savg}
214306122Savg