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