xref: /openbmc/u-boot/lib/libfdt/fdt.c (revision fc47cf9d)
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 	unsigned absoffset = offset + fdt_off_dt_struct(fdt);
39 
40 	if ((absoffset < offset)
41 	    || ((absoffset + len) < absoffset)
42 	    || (absoffset + len) > fdt_totalsize(fdt))
43 		return NULL;
44 
45 	if (fdt_version(fdt) >= 0x11)
46 		if (((offset + len) < offset)
47 		    || ((offset + len) > fdt_size_dt_struct(fdt)))
48 			return NULL;
49 
50 	return _fdt_offset_ptr(fdt, offset);
51 }
52 
53 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
54 {
55 	const fdt32_t *tagp, *lenp;
56 	uint32_t tag;
57 	int offset = startoffset;
58 	const char *p;
59 
60 	*nextoffset = -FDT_ERR_TRUNCATED;
61 	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
62 	if (!tagp)
63 		return FDT_END; /* premature end */
64 	tag = fdt32_to_cpu(*tagp);
65 	offset += FDT_TAGSIZE;
66 
67 	*nextoffset = -FDT_ERR_BADSTRUCTURE;
68 	switch (tag) {
69 	case FDT_BEGIN_NODE:
70 		/* skip name */
71 		do {
72 			p = fdt_offset_ptr(fdt, offset++, 1);
73 		} while (p && (*p != '\0'));
74 		if (!p)
75 			return FDT_END; /* premature end */
76 		break;
77 
78 	case FDT_PROP:
79 		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
80 		if (!lenp)
81 			return FDT_END; /* premature end */
82 		/* skip-name offset, length and value */
83 		offset += sizeof(struct fdt_property) - FDT_TAGSIZE
84 			+ fdt32_to_cpu(*lenp);
85 		break;
86 
87 	case FDT_END:
88 	case FDT_END_NODE:
89 	case FDT_NOP:
90 		break;
91 
92 	default:
93 		return FDT_END;
94 	}
95 
96 	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
97 		return FDT_END; /* premature end */
98 
99 	*nextoffset = FDT_TAGALIGN(offset);
100 	return tag;
101 }
102 
103 int _fdt_check_node_offset(const void *fdt, int offset)
104 {
105 	if ((offset < 0) || (offset % FDT_TAGSIZE)
106 	    || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
107 		return -FDT_ERR_BADOFFSET;
108 
109 	return offset;
110 }
111 
112 int _fdt_check_prop_offset(const void *fdt, int offset)
113 {
114 	if ((offset < 0) || (offset % FDT_TAGSIZE)
115 	    || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
116 		return -FDT_ERR_BADOFFSET;
117 
118 	return offset;
119 }
120 
121 int fdt_next_node(const void *fdt, int offset, int *depth)
122 {
123 	int nextoffset = 0;
124 	uint32_t tag;
125 
126 	if (offset >= 0)
127 		if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
128 			return nextoffset;
129 
130 	do {
131 		offset = nextoffset;
132 		tag = fdt_next_tag(fdt, offset, &nextoffset);
133 
134 		switch (tag) {
135 		case FDT_PROP:
136 		case FDT_NOP:
137 			break;
138 
139 		case FDT_BEGIN_NODE:
140 			if (depth)
141 				(*depth)++;
142 			break;
143 
144 		case FDT_END_NODE:
145 			if (depth && ((--(*depth)) < 0))
146 				return nextoffset;
147 			break;
148 
149 		case FDT_END:
150 			if ((nextoffset >= 0)
151 			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
152 				return -FDT_ERR_NOTFOUND;
153 			else
154 				return nextoffset;
155 		}
156 	} while (tag != FDT_BEGIN_NODE);
157 
158 	return offset;
159 }
160 
161 int fdt_first_subnode(const void *fdt, int offset)
162 {
163 	int depth = 0;
164 
165 	offset = fdt_next_node(fdt, offset, &depth);
166 	if (offset < 0 || depth != 1)
167 		return -FDT_ERR_NOTFOUND;
168 
169 	return offset;
170 }
171 
172 int fdt_next_subnode(const void *fdt, int offset)
173 {
174 	int depth = 1;
175 
176 	/*
177 	 * With respect to the parent, the depth of the next subnode will be
178 	 * the same as the last.
179 	 */
180 	do {
181 		offset = fdt_next_node(fdt, offset, &depth);
182 		if (offset < 0 || depth < 1)
183 			return -FDT_ERR_NOTFOUND;
184 	} while (depth > 1);
185 
186 	return offset;
187 }
188 
189 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
190 {
191 	int len = strlen(s) + 1;
192 	const char *last = strtab + tabsize - len;
193 	const char *p;
194 
195 	for (p = strtab; p <= last; p++)
196 		if (memcmp(p, s, len) == 0)
197 			return p;
198 	return NULL;
199 }
200 
201 int fdt_move(const void *fdt, void *buf, int bufsize)
202 {
203 	FDT_CHECK_HEADER(fdt);
204 
205 	if (fdt_totalsize(fdt) > bufsize)
206 		return -FDT_ERR_NOSPACE;
207 
208 	memmove(buf, fdt, fdt_totalsize(fdt));
209 	return 0;
210 }
211