xref: /arm-trusted-firmware/lib/libfdt/fdt.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2*91f16700Schasinglulu /*
3*91f16700Schasinglulu  * libfdt - Flat Device Tree manipulation
4*91f16700Schasinglulu  * Copyright (C) 2006 David Gibson, IBM Corporation.
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu #include "libfdt_env.h"
7*91f16700Schasinglulu 
8*91f16700Schasinglulu #include <fdt.h>
9*91f16700Schasinglulu #include <libfdt.h>
10*91f16700Schasinglulu 
11*91f16700Schasinglulu #include "libfdt_internal.h"
12*91f16700Schasinglulu 
13*91f16700Schasinglulu /*
14*91f16700Schasinglulu  * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
15*91f16700Schasinglulu  * that the given buffer contains what appears to be a flattened
16*91f16700Schasinglulu  * device tree with sane information in its header.
17*91f16700Schasinglulu  */
18*91f16700Schasinglulu int32_t fdt_ro_probe_(const void *fdt)
19*91f16700Schasinglulu {
20*91f16700Schasinglulu 	uint32_t totalsize = fdt_totalsize(fdt);
21*91f16700Schasinglulu 
22*91f16700Schasinglulu 	if (can_assume(VALID_DTB))
23*91f16700Schasinglulu 		return totalsize;
24*91f16700Schasinglulu 
25*91f16700Schasinglulu 	/* The device tree must be at an 8-byte aligned address */
26*91f16700Schasinglulu 	if ((uintptr_t)fdt & 7)
27*91f16700Schasinglulu 		return -FDT_ERR_ALIGNMENT;
28*91f16700Schasinglulu 
29*91f16700Schasinglulu 	if (fdt_magic(fdt) == FDT_MAGIC) {
30*91f16700Schasinglulu 		/* Complete tree */
31*91f16700Schasinglulu 		if (!can_assume(LATEST)) {
32*91f16700Schasinglulu 			if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
33*91f16700Schasinglulu 				return -FDT_ERR_BADVERSION;
34*91f16700Schasinglulu 			if (fdt_last_comp_version(fdt) >
35*91f16700Schasinglulu 					FDT_LAST_SUPPORTED_VERSION)
36*91f16700Schasinglulu 				return -FDT_ERR_BADVERSION;
37*91f16700Schasinglulu 		}
38*91f16700Schasinglulu 	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
39*91f16700Schasinglulu 		/* Unfinished sequential-write blob */
40*91f16700Schasinglulu 		if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
41*91f16700Schasinglulu 			return -FDT_ERR_BADSTATE;
42*91f16700Schasinglulu 	} else {
43*91f16700Schasinglulu 		return -FDT_ERR_BADMAGIC;
44*91f16700Schasinglulu 	}
45*91f16700Schasinglulu 
46*91f16700Schasinglulu 	if (totalsize < INT32_MAX)
47*91f16700Schasinglulu 		return totalsize;
48*91f16700Schasinglulu 	else
49*91f16700Schasinglulu 		return -FDT_ERR_TRUNCATED;
50*91f16700Schasinglulu }
51*91f16700Schasinglulu 
52*91f16700Schasinglulu static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
53*91f16700Schasinglulu {
54*91f16700Schasinglulu 	return (off >= hdrsize) && (off <= totalsize);
55*91f16700Schasinglulu }
56*91f16700Schasinglulu 
57*91f16700Schasinglulu static int check_block_(uint32_t hdrsize, uint32_t totalsize,
58*91f16700Schasinglulu 			uint32_t base, uint32_t size)
59*91f16700Schasinglulu {
60*91f16700Schasinglulu 	if (!check_off_(hdrsize, totalsize, base))
61*91f16700Schasinglulu 		return 0; /* block start out of bounds */
62*91f16700Schasinglulu 	if ((base + size) < base)
63*91f16700Schasinglulu 		return 0; /* overflow */
64*91f16700Schasinglulu 	if (!check_off_(hdrsize, totalsize, base + size))
65*91f16700Schasinglulu 		return 0; /* block end out of bounds */
66*91f16700Schasinglulu 	return 1;
67*91f16700Schasinglulu }
68*91f16700Schasinglulu 
69*91f16700Schasinglulu size_t fdt_header_size_(uint32_t version)
70*91f16700Schasinglulu {
71*91f16700Schasinglulu 	if (version <= 1)
72*91f16700Schasinglulu 		return FDT_V1_SIZE;
73*91f16700Schasinglulu 	else if (version <= 2)
74*91f16700Schasinglulu 		return FDT_V2_SIZE;
75*91f16700Schasinglulu 	else if (version <= 3)
76*91f16700Schasinglulu 		return FDT_V3_SIZE;
77*91f16700Schasinglulu 	else if (version <= 16)
78*91f16700Schasinglulu 		return FDT_V16_SIZE;
79*91f16700Schasinglulu 	else
80*91f16700Schasinglulu 		return FDT_V17_SIZE;
81*91f16700Schasinglulu }
82*91f16700Schasinglulu 
83*91f16700Schasinglulu size_t fdt_header_size(const void *fdt)
84*91f16700Schasinglulu {
85*91f16700Schasinglulu 	return can_assume(LATEST) ? FDT_V17_SIZE :
86*91f16700Schasinglulu 		fdt_header_size_(fdt_version(fdt));
87*91f16700Schasinglulu }
88*91f16700Schasinglulu 
89*91f16700Schasinglulu int fdt_check_header(const void *fdt)
90*91f16700Schasinglulu {
91*91f16700Schasinglulu 	size_t hdrsize;
92*91f16700Schasinglulu 
93*91f16700Schasinglulu 	/* The device tree must be at an 8-byte aligned address */
94*91f16700Schasinglulu 	if ((uintptr_t)fdt & 7)
95*91f16700Schasinglulu 		return -FDT_ERR_ALIGNMENT;
96*91f16700Schasinglulu 
97*91f16700Schasinglulu 	if (fdt_magic(fdt) != FDT_MAGIC)
98*91f16700Schasinglulu 		return -FDT_ERR_BADMAGIC;
99*91f16700Schasinglulu 	if (!can_assume(LATEST)) {
100*91f16700Schasinglulu 		if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
101*91f16700Schasinglulu 		    || (fdt_last_comp_version(fdt) >
102*91f16700Schasinglulu 			FDT_LAST_SUPPORTED_VERSION))
103*91f16700Schasinglulu 			return -FDT_ERR_BADVERSION;
104*91f16700Schasinglulu 		if (fdt_version(fdt) < fdt_last_comp_version(fdt))
105*91f16700Schasinglulu 			return -FDT_ERR_BADVERSION;
106*91f16700Schasinglulu 	}
107*91f16700Schasinglulu 	hdrsize = fdt_header_size(fdt);
108*91f16700Schasinglulu 	if (!can_assume(VALID_DTB)) {
109*91f16700Schasinglulu 		if ((fdt_totalsize(fdt) < hdrsize)
110*91f16700Schasinglulu 		    || (fdt_totalsize(fdt) > INT_MAX))
111*91f16700Schasinglulu 			return -FDT_ERR_TRUNCATED;
112*91f16700Schasinglulu 
113*91f16700Schasinglulu 		/* Bounds check memrsv block */
114*91f16700Schasinglulu 		if (!check_off_(hdrsize, fdt_totalsize(fdt),
115*91f16700Schasinglulu 				fdt_off_mem_rsvmap(fdt)))
116*91f16700Schasinglulu 			return -FDT_ERR_TRUNCATED;
117*91f16700Schasinglulu 
118*91f16700Schasinglulu 		/* Bounds check structure block */
119*91f16700Schasinglulu 		if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
120*91f16700Schasinglulu 			if (!check_off_(hdrsize, fdt_totalsize(fdt),
121*91f16700Schasinglulu 					fdt_off_dt_struct(fdt)))
122*91f16700Schasinglulu 				return -FDT_ERR_TRUNCATED;
123*91f16700Schasinglulu 		} else {
124*91f16700Schasinglulu 			if (!check_block_(hdrsize, fdt_totalsize(fdt),
125*91f16700Schasinglulu 					  fdt_off_dt_struct(fdt),
126*91f16700Schasinglulu 					  fdt_size_dt_struct(fdt)))
127*91f16700Schasinglulu 				return -FDT_ERR_TRUNCATED;
128*91f16700Schasinglulu 		}
129*91f16700Schasinglulu 
130*91f16700Schasinglulu 		/* Bounds check strings block */
131*91f16700Schasinglulu 		if (!check_block_(hdrsize, fdt_totalsize(fdt),
132*91f16700Schasinglulu 				  fdt_off_dt_strings(fdt),
133*91f16700Schasinglulu 				  fdt_size_dt_strings(fdt)))
134*91f16700Schasinglulu 			return -FDT_ERR_TRUNCATED;
135*91f16700Schasinglulu 	}
136*91f16700Schasinglulu 
137*91f16700Schasinglulu 	return 0;
138*91f16700Schasinglulu }
139*91f16700Schasinglulu 
140*91f16700Schasinglulu const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
141*91f16700Schasinglulu {
142*91f16700Schasinglulu 	unsigned int uoffset = offset;
143*91f16700Schasinglulu 	unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
144*91f16700Schasinglulu 
145*91f16700Schasinglulu 	if (offset < 0)
146*91f16700Schasinglulu 		return NULL;
147*91f16700Schasinglulu 
148*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT))
149*91f16700Schasinglulu 		if ((absoffset < uoffset)
150*91f16700Schasinglulu 		    || ((absoffset + len) < absoffset)
151*91f16700Schasinglulu 		    || (absoffset + len) > fdt_totalsize(fdt))
152*91f16700Schasinglulu 			return NULL;
153*91f16700Schasinglulu 
154*91f16700Schasinglulu 	if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
155*91f16700Schasinglulu 		if (((uoffset + len) < uoffset)
156*91f16700Schasinglulu 		    || ((offset + len) > fdt_size_dt_struct(fdt)))
157*91f16700Schasinglulu 			return NULL;
158*91f16700Schasinglulu 
159*91f16700Schasinglulu 	return fdt_offset_ptr_(fdt, offset);
160*91f16700Schasinglulu }
161*91f16700Schasinglulu 
162*91f16700Schasinglulu uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
163*91f16700Schasinglulu {
164*91f16700Schasinglulu 	const fdt32_t *tagp, *lenp;
165*91f16700Schasinglulu 	uint32_t tag, len, sum;
166*91f16700Schasinglulu 	int offset = startoffset;
167*91f16700Schasinglulu 	const char *p;
168*91f16700Schasinglulu 
169*91f16700Schasinglulu 	*nextoffset = -FDT_ERR_TRUNCATED;
170*91f16700Schasinglulu 	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
171*91f16700Schasinglulu 	if (!can_assume(VALID_DTB) && !tagp)
172*91f16700Schasinglulu 		return FDT_END; /* premature end */
173*91f16700Schasinglulu 	tag = fdt32_to_cpu(*tagp);
174*91f16700Schasinglulu 	offset += FDT_TAGSIZE;
175*91f16700Schasinglulu 
176*91f16700Schasinglulu 	*nextoffset = -FDT_ERR_BADSTRUCTURE;
177*91f16700Schasinglulu 	switch (tag) {
178*91f16700Schasinglulu 	case FDT_BEGIN_NODE:
179*91f16700Schasinglulu 		/* skip name */
180*91f16700Schasinglulu 		do {
181*91f16700Schasinglulu 			p = fdt_offset_ptr(fdt, offset++, 1);
182*91f16700Schasinglulu 		} while (p && (*p != '\0'));
183*91f16700Schasinglulu 		if (!can_assume(VALID_DTB) && !p)
184*91f16700Schasinglulu 			return FDT_END; /* premature end */
185*91f16700Schasinglulu 		break;
186*91f16700Schasinglulu 
187*91f16700Schasinglulu 	case FDT_PROP:
188*91f16700Schasinglulu 		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
189*91f16700Schasinglulu 		if (!can_assume(VALID_DTB) && !lenp)
190*91f16700Schasinglulu 			return FDT_END; /* premature end */
191*91f16700Schasinglulu 
192*91f16700Schasinglulu 		len = fdt32_to_cpu(*lenp);
193*91f16700Schasinglulu 		sum = len + offset;
194*91f16700Schasinglulu 		if (!can_assume(VALID_DTB) &&
195*91f16700Schasinglulu 		    (INT_MAX <= sum || sum < (uint32_t) offset))
196*91f16700Schasinglulu 			return FDT_END; /* premature end */
197*91f16700Schasinglulu 
198*91f16700Schasinglulu 		/* skip-name offset, length and value */
199*91f16700Schasinglulu 		offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len;
200*91f16700Schasinglulu 
201*91f16700Schasinglulu 		if (!can_assume(LATEST) &&
202*91f16700Schasinglulu 		    fdt_version(fdt) < 0x10 && len >= 8 &&
203*91f16700Schasinglulu 		    ((offset - len) % 8) != 0)
204*91f16700Schasinglulu 			offset += 4;
205*91f16700Schasinglulu 		break;
206*91f16700Schasinglulu 
207*91f16700Schasinglulu 	case FDT_END:
208*91f16700Schasinglulu 	case FDT_END_NODE:
209*91f16700Schasinglulu 	case FDT_NOP:
210*91f16700Schasinglulu 		break;
211*91f16700Schasinglulu 
212*91f16700Schasinglulu 	default:
213*91f16700Schasinglulu 		return FDT_END;
214*91f16700Schasinglulu 	}
215*91f16700Schasinglulu 
216*91f16700Schasinglulu 	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
217*91f16700Schasinglulu 		return FDT_END; /* premature end */
218*91f16700Schasinglulu 
219*91f16700Schasinglulu 	*nextoffset = FDT_TAGALIGN(offset);
220*91f16700Schasinglulu 	return tag;
221*91f16700Schasinglulu }
222*91f16700Schasinglulu 
223*91f16700Schasinglulu int fdt_check_node_offset_(const void *fdt, int offset)
224*91f16700Schasinglulu {
225*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT)
226*91f16700Schasinglulu 	    && ((offset < 0) || (offset % FDT_TAGSIZE)))
227*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
228*91f16700Schasinglulu 
229*91f16700Schasinglulu 	if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
230*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
231*91f16700Schasinglulu 
232*91f16700Schasinglulu 	return offset;
233*91f16700Schasinglulu }
234*91f16700Schasinglulu 
235*91f16700Schasinglulu int fdt_check_prop_offset_(const void *fdt, int offset)
236*91f16700Schasinglulu {
237*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT)
238*91f16700Schasinglulu 	    && ((offset < 0) || (offset % FDT_TAGSIZE)))
239*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
240*91f16700Schasinglulu 
241*91f16700Schasinglulu 	if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
242*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
243*91f16700Schasinglulu 
244*91f16700Schasinglulu 	return offset;
245*91f16700Schasinglulu }
246*91f16700Schasinglulu 
247*91f16700Schasinglulu int fdt_next_node(const void *fdt, int offset, int *depth)
248*91f16700Schasinglulu {
249*91f16700Schasinglulu 	int nextoffset = 0;
250*91f16700Schasinglulu 	uint32_t tag;
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	if (offset >= 0)
253*91f16700Schasinglulu 		if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
254*91f16700Schasinglulu 			return nextoffset;
255*91f16700Schasinglulu 
256*91f16700Schasinglulu 	do {
257*91f16700Schasinglulu 		offset = nextoffset;
258*91f16700Schasinglulu 		tag = fdt_next_tag(fdt, offset, &nextoffset);
259*91f16700Schasinglulu 
260*91f16700Schasinglulu 		switch (tag) {
261*91f16700Schasinglulu 		case FDT_PROP:
262*91f16700Schasinglulu 		case FDT_NOP:
263*91f16700Schasinglulu 			break;
264*91f16700Schasinglulu 
265*91f16700Schasinglulu 		case FDT_BEGIN_NODE:
266*91f16700Schasinglulu 			if (depth)
267*91f16700Schasinglulu 				(*depth)++;
268*91f16700Schasinglulu 			break;
269*91f16700Schasinglulu 
270*91f16700Schasinglulu 		case FDT_END_NODE:
271*91f16700Schasinglulu 			if (depth && ((--(*depth)) < 0))
272*91f16700Schasinglulu 				return nextoffset;
273*91f16700Schasinglulu 			break;
274*91f16700Schasinglulu 
275*91f16700Schasinglulu 		case FDT_END:
276*91f16700Schasinglulu 			if ((nextoffset >= 0)
277*91f16700Schasinglulu 			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
278*91f16700Schasinglulu 				return -FDT_ERR_NOTFOUND;
279*91f16700Schasinglulu 			else
280*91f16700Schasinglulu 				return nextoffset;
281*91f16700Schasinglulu 		}
282*91f16700Schasinglulu 	} while (tag != FDT_BEGIN_NODE);
283*91f16700Schasinglulu 
284*91f16700Schasinglulu 	return offset;
285*91f16700Schasinglulu }
286*91f16700Schasinglulu 
287*91f16700Schasinglulu int fdt_first_subnode(const void *fdt, int offset)
288*91f16700Schasinglulu {
289*91f16700Schasinglulu 	int depth = 0;
290*91f16700Schasinglulu 
291*91f16700Schasinglulu 	offset = fdt_next_node(fdt, offset, &depth);
292*91f16700Schasinglulu 	if (offset < 0 || depth != 1)
293*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
294*91f16700Schasinglulu 
295*91f16700Schasinglulu 	return offset;
296*91f16700Schasinglulu }
297*91f16700Schasinglulu 
298*91f16700Schasinglulu int fdt_next_subnode(const void *fdt, int offset)
299*91f16700Schasinglulu {
300*91f16700Schasinglulu 	int depth = 1;
301*91f16700Schasinglulu 
302*91f16700Schasinglulu 	/*
303*91f16700Schasinglulu 	 * With respect to the parent, the depth of the next subnode will be
304*91f16700Schasinglulu 	 * the same as the last.
305*91f16700Schasinglulu 	 */
306*91f16700Schasinglulu 	do {
307*91f16700Schasinglulu 		offset = fdt_next_node(fdt, offset, &depth);
308*91f16700Schasinglulu 		if (offset < 0 || depth < 1)
309*91f16700Schasinglulu 			return -FDT_ERR_NOTFOUND;
310*91f16700Schasinglulu 	} while (depth > 1);
311*91f16700Schasinglulu 
312*91f16700Schasinglulu 	return offset;
313*91f16700Schasinglulu }
314*91f16700Schasinglulu 
315*91f16700Schasinglulu const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
316*91f16700Schasinglulu {
317*91f16700Schasinglulu 	int len = strlen(s) + 1;
318*91f16700Schasinglulu 	const char *last = strtab + tabsize - len;
319*91f16700Schasinglulu 	const char *p;
320*91f16700Schasinglulu 
321*91f16700Schasinglulu 	for (p = strtab; p <= last; p++)
322*91f16700Schasinglulu 		if (memcmp(p, s, len) == 0)
323*91f16700Schasinglulu 			return p;
324*91f16700Schasinglulu 	return NULL;
325*91f16700Schasinglulu }
326*91f16700Schasinglulu 
327*91f16700Schasinglulu int fdt_move(const void *fdt, void *buf, int bufsize)
328*91f16700Schasinglulu {
329*91f16700Schasinglulu 	if (!can_assume(VALID_INPUT) && bufsize < 0)
330*91f16700Schasinglulu 		return -FDT_ERR_NOSPACE;
331*91f16700Schasinglulu 
332*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
333*91f16700Schasinglulu 
334*91f16700Schasinglulu 	if (fdt_totalsize(fdt) > (unsigned int)bufsize)
335*91f16700Schasinglulu 		return -FDT_ERR_NOSPACE;
336*91f16700Schasinglulu 
337*91f16700Schasinglulu 	memmove(buf, fdt, fdt_totalsize(fdt));
338*91f16700Schasinglulu 	return 0;
339*91f16700Schasinglulu }
340