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