xref: /openbmc/u-boot/lib/libfdt/fdt.c (revision 77b93e5e)
1 /*
2  * libfdt - Flat Device Tree manipulation
3  * Copyright (C) 2006 David Gibson, IBM Corporation.
4  * SPDX-License-Identifier:	GPL-2.0+ BSD-2-Clause
5  */
6 #include <libfdt_env.h>
7 
8 #ifndef USE_HOSTCC
9 #include <fdt.h>
10 #include <libfdt.h>
11 #else
12 #include "fdt_host.h"
13 #endif
14 
15 #include "libfdt_internal.h"
16 
17 int fdt_check_header(const void *fdt)
18 {
19 	if (fdt_magic(fdt) == FDT_MAGIC) {
20 		/* Complete tree */
21 		if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
22 			return -FDT_ERR_BADVERSION;
23 		if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
24 			return -FDT_ERR_BADVERSION;
25 	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
26 		/* Unfinished sequential-write blob */
27 		if (fdt_size_dt_struct(fdt) == 0)
28 			return -FDT_ERR_BADSTATE;
29 	} else {
30 		return -FDT_ERR_BADMAGIC;
31 	}
32 
33 	return 0;
34 }
35 
36 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
37 {
38 	const char *p;
39 
40 	if (fdt_version(fdt) >= 0x11)
41 		if (((offset + len) < offset)
42 		    || ((offset + len) > fdt_size_dt_struct(fdt)))
43 			return NULL;
44 
45 	p = _fdt_offset_ptr(fdt, offset);
46 
47 	if (p + len < p)
48 		return NULL;
49 	return p;
50 }
51 
52 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
53 {
54 	const fdt32_t *tagp, *lenp;
55 	uint32_t tag;
56 	int offset = startoffset;
57 	const char *p;
58 
59 	*nextoffset = -FDT_ERR_TRUNCATED;
60 	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
61 	if (!tagp)
62 		return FDT_END; /* premature end */
63 	tag = fdt32_to_cpu(*tagp);
64 	offset += FDT_TAGSIZE;
65 
66 	*nextoffset = -FDT_ERR_BADSTRUCTURE;
67 	switch (tag) {
68 	case FDT_BEGIN_NODE:
69 		/* skip name */
70 		do {
71 			p = fdt_offset_ptr(fdt, offset++, 1);
72 		} while (p && (*p != '\0'));
73 		if (!p)
74 			return FDT_END; /* premature end */
75 		break;
76 
77 	case FDT_PROP:
78 		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
79 		if (!lenp)
80 			return FDT_END; /* premature end */
81 		/* skip-name offset, length and value */
82 		offset += sizeof(struct fdt_property) - FDT_TAGSIZE
83 			+ fdt32_to_cpu(*lenp);
84 		break;
85 
86 	case FDT_END:
87 	case FDT_END_NODE:
88 	case FDT_NOP:
89 		break;
90 
91 	default:
92 		return FDT_END;
93 	}
94 
95 	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
96 		return FDT_END; /* premature end */
97 
98 	*nextoffset = FDT_TAGALIGN(offset);
99 	return tag;
100 }
101 
102 int _fdt_check_node_offset(const void *fdt, int offset)
103 {
104 	if ((offset < 0) || (offset % FDT_TAGSIZE)
105 	    || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
106 		return -FDT_ERR_BADOFFSET;
107 
108 	return offset;
109 }
110 
111 int _fdt_check_prop_offset(const void *fdt, int offset)
112 {
113 	if ((offset < 0) || (offset % FDT_TAGSIZE)
114 	    || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
115 		return -FDT_ERR_BADOFFSET;
116 
117 	return offset;
118 }
119 
120 int fdt_next_node(const void *fdt, int offset, int *depth)
121 {
122 	int nextoffset = 0;
123 	uint32_t tag;
124 
125 	if (offset >= 0)
126 		if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
127 			return nextoffset;
128 
129 	do {
130 		offset = nextoffset;
131 		tag = fdt_next_tag(fdt, offset, &nextoffset);
132 
133 		switch (tag) {
134 		case FDT_PROP:
135 		case FDT_NOP:
136 			break;
137 
138 		case FDT_BEGIN_NODE:
139 			if (depth)
140 				(*depth)++;
141 			break;
142 
143 		case FDT_END_NODE:
144 			if (depth && ((--(*depth)) < 0))
145 				return nextoffset;
146 			break;
147 
148 		case FDT_END:
149 			if ((nextoffset >= 0)
150 			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
151 				return -FDT_ERR_NOTFOUND;
152 			else
153 				return nextoffset;
154 		}
155 	} while (tag != FDT_BEGIN_NODE);
156 
157 	return offset;
158 }
159 
160 int fdt_first_subnode(const void *fdt, int offset)
161 {
162 	int depth = 0;
163 
164 	offset = fdt_next_node(fdt, offset, &depth);
165 	if (offset < 0 || depth != 1)
166 		return -FDT_ERR_NOTFOUND;
167 
168 	return offset;
169 }
170 
171 int fdt_next_subnode(const void *fdt, int offset)
172 {
173 	int depth = 1;
174 
175 	/*
176 	 * With respect to the parent, the depth of the next subnode will be
177 	 * the same as the last.
178 	 */
179 	do {
180 		offset = fdt_next_node(fdt, offset, &depth);
181 		if (offset < 0 || depth < 1)
182 			return -FDT_ERR_NOTFOUND;
183 	} while (depth > 1);
184 
185 	return offset;
186 }
187 
188 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
189 {
190 	int len = strlen(s) + 1;
191 	const char *last = strtab + tabsize - len;
192 	const char *p;
193 
194 	for (p = strtab; p <= last; p++)
195 		if (memcmp(p, s, len) == 0)
196 			return p;
197 	return NULL;
198 }
199 
200 int fdt_move(const void *fdt, void *buf, int bufsize)
201 {
202 	FDT_CHECK_HEADER(fdt);
203 
204 	if (fdt_totalsize(fdt) > bufsize)
205 		return -FDT_ERR_NOSPACE;
206 
207 	memmove(buf, fdt, fdt_totalsize(fdt));
208 	return 0;
209 }
210