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