112869ecdSRob Herring // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
29fffb55fSDavid Gibson /*
39fffb55fSDavid Gibson * libfdt - Flat Device Tree manipulation
49fffb55fSDavid Gibson * Copyright (C) 2006 David Gibson, IBM Corporation.
59fffb55fSDavid Gibson */
69fffb55fSDavid Gibson #include "libfdt_env.h"
79fffb55fSDavid Gibson
89fffb55fSDavid Gibson #include <fdt.h>
99fffb55fSDavid Gibson #include <libfdt.h>
109fffb55fSDavid Gibson
119fffb55fSDavid Gibson #include "libfdt_internal.h"
129fffb55fSDavid Gibson
13f858927fSRob Herring /*
14f858927fSRob Herring * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
15f858927fSRob Herring * that the given buffer contains what appears to be a flattened
16f858927fSRob Herring * device tree with sane information in its header.
17f858927fSRob Herring */
fdt_ro_probe_(const void * fdt)180cec114eSRob Herring int32_t fdt_ro_probe_(const void *fdt)
199fffb55fSDavid Gibson {
200cec114eSRob Herring uint32_t totalsize = fdt_totalsize(fdt);
210cec114eSRob Herring
22d047cd8aSRob Herring if (can_assume(VALID_DTB))
23d047cd8aSRob Herring return totalsize;
24d047cd8aSRob Herring
2579edff12SRob Herring /* The device tree must be at an 8-byte aligned address */
2679edff12SRob Herring if ((uintptr_t)fdt & 7)
2779edff12SRob Herring return -FDT_ERR_ALIGNMENT;
2879edff12SRob Herring
299fffb55fSDavid Gibson if (fdt_magic(fdt) == FDT_MAGIC) {
309fffb55fSDavid Gibson /* Complete tree */
31d047cd8aSRob Herring if (!can_assume(LATEST)) {
329fffb55fSDavid Gibson if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
339fffb55fSDavid Gibson return -FDT_ERR_BADVERSION;
34d047cd8aSRob Herring if (fdt_last_comp_version(fdt) >
35d047cd8aSRob Herring FDT_LAST_SUPPORTED_VERSION)
369fffb55fSDavid Gibson return -FDT_ERR_BADVERSION;
37d047cd8aSRob Herring }
389fffb55fSDavid Gibson } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
399fffb55fSDavid Gibson /* Unfinished sequential-write blob */
40d047cd8aSRob Herring if (!can_assume(VALID_INPUT) && fdt_size_dt_struct(fdt) == 0)
419fffb55fSDavid Gibson return -FDT_ERR_BADSTATE;
429fffb55fSDavid Gibson } else {
439fffb55fSDavid Gibson return -FDT_ERR_BADMAGIC;
449fffb55fSDavid Gibson }
459fffb55fSDavid Gibson
460cec114eSRob Herring if (totalsize < INT32_MAX)
470cec114eSRob Herring return totalsize;
480cec114eSRob Herring else
490cec114eSRob Herring return -FDT_ERR_TRUNCATED;
509fffb55fSDavid Gibson }
519fffb55fSDavid Gibson
check_off_(uint32_t hdrsize,uint32_t totalsize,uint32_t off)52f858927fSRob Herring static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
53f858927fSRob Herring {
54f858927fSRob Herring return (off >= hdrsize) && (off <= totalsize);
55f858927fSRob Herring }
56f858927fSRob Herring
check_block_(uint32_t hdrsize,uint32_t totalsize,uint32_t base,uint32_t size)57f858927fSRob Herring static int check_block_(uint32_t hdrsize, uint32_t totalsize,
58f858927fSRob Herring uint32_t base, uint32_t size)
59f858927fSRob Herring {
60f858927fSRob Herring if (!check_off_(hdrsize, totalsize, base))
61f858927fSRob Herring return 0; /* block start out of bounds */
62f858927fSRob Herring if ((base + size) < base)
63f858927fSRob Herring return 0; /* overflow */
64f858927fSRob Herring if (!check_off_(hdrsize, totalsize, base + size))
65f858927fSRob Herring return 0; /* block end out of bounds */
66f858927fSRob Herring return 1;
67f858927fSRob Herring }
68f858927fSRob Herring
fdt_header_size_(uint32_t version)69f858927fSRob Herring size_t fdt_header_size_(uint32_t version)
70f858927fSRob Herring {
71f858927fSRob Herring if (version <= 1)
72f858927fSRob Herring return FDT_V1_SIZE;
73f858927fSRob Herring else if (version <= 2)
74f858927fSRob Herring return FDT_V2_SIZE;
75f858927fSRob Herring else if (version <= 3)
76f858927fSRob Herring return FDT_V3_SIZE;
77f858927fSRob Herring else if (version <= 16)
78f858927fSRob Herring return FDT_V16_SIZE;
79f858927fSRob Herring else
80f858927fSRob Herring return FDT_V17_SIZE;
81f858927fSRob Herring }
82f858927fSRob Herring
fdt_header_size(const void * fdt)83d047cd8aSRob Herring size_t fdt_header_size(const void *fdt)
84d047cd8aSRob Herring {
85d047cd8aSRob Herring return can_assume(LATEST) ? FDT_V17_SIZE :
86d047cd8aSRob Herring fdt_header_size_(fdt_version(fdt));
87d047cd8aSRob Herring }
88d047cd8aSRob Herring
fdt_check_header(const void * fdt)89f858927fSRob Herring int fdt_check_header(const void *fdt)
90f858927fSRob Herring {
91f858927fSRob Herring size_t hdrsize;
92f858927fSRob Herring
93a77725a9SRob Herring /* The device tree must be at an 8-byte aligned address */
94a77725a9SRob Herring if ((uintptr_t)fdt & 7)
95a77725a9SRob Herring return -FDT_ERR_ALIGNMENT;
96a77725a9SRob Herring
97f858927fSRob Herring if (fdt_magic(fdt) != FDT_MAGIC)
98f858927fSRob Herring return -FDT_ERR_BADMAGIC;
99d047cd8aSRob Herring if (!can_assume(LATEST)) {
100f858927fSRob Herring if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
101d047cd8aSRob Herring || (fdt_last_comp_version(fdt) >
102d047cd8aSRob Herring FDT_LAST_SUPPORTED_VERSION))
103f858927fSRob Herring return -FDT_ERR_BADVERSION;
104f858927fSRob Herring if (fdt_version(fdt) < fdt_last_comp_version(fdt))
105f858927fSRob Herring return -FDT_ERR_BADVERSION;
106d047cd8aSRob Herring }
107d047cd8aSRob Herring hdrsize = fdt_header_size(fdt);
108d047cd8aSRob Herring if (!can_assume(VALID_DTB)) {
109f858927fSRob Herring if ((fdt_totalsize(fdt) < hdrsize)
110f858927fSRob Herring || (fdt_totalsize(fdt) > INT_MAX))
111f858927fSRob Herring return -FDT_ERR_TRUNCATED;
112f858927fSRob Herring
113f858927fSRob Herring /* Bounds check memrsv block */
114d047cd8aSRob Herring if (!check_off_(hdrsize, fdt_totalsize(fdt),
115d047cd8aSRob Herring fdt_off_mem_rsvmap(fdt)))
116f858927fSRob Herring return -FDT_ERR_TRUNCATED;
117f858927fSRob Herring
118f858927fSRob Herring /* Bounds check structure block */
119d047cd8aSRob Herring if (!can_assume(LATEST) && fdt_version(fdt) < 17) {
120f858927fSRob Herring if (!check_off_(hdrsize, fdt_totalsize(fdt),
121f858927fSRob Herring fdt_off_dt_struct(fdt)))
122f858927fSRob Herring return -FDT_ERR_TRUNCATED;
123f858927fSRob Herring } else {
124f858927fSRob Herring if (!check_block_(hdrsize, fdt_totalsize(fdt),
125f858927fSRob Herring fdt_off_dt_struct(fdt),
126f858927fSRob Herring fdt_size_dt_struct(fdt)))
127f858927fSRob Herring return -FDT_ERR_TRUNCATED;
128f858927fSRob Herring }
129f858927fSRob Herring
130f858927fSRob Herring /* Bounds check strings block */
131f858927fSRob Herring if (!check_block_(hdrsize, fdt_totalsize(fdt),
132d047cd8aSRob Herring fdt_off_dt_strings(fdt),
133d047cd8aSRob Herring fdt_size_dt_strings(fdt)))
134f858927fSRob Herring return -FDT_ERR_TRUNCATED;
135d047cd8aSRob Herring }
136f858927fSRob Herring
137f858927fSRob Herring return 0;
138f858927fSRob Herring }
139f858927fSRob Herring
fdt_offset_ptr(const void * fdt,int offset,unsigned int len)140cd296721SStephen Warren const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
1419fffb55fSDavid Gibson {
1426e9c9686SRob Herring unsigned int uoffset = offset;
1436e9c9686SRob Herring unsigned int absoffset = offset + fdt_off_dt_struct(fdt);
1446e9c9686SRob Herring
1456e9c9686SRob Herring if (offset < 0)
1466e9c9686SRob Herring return NULL;
14791feabc2SRob Herring
148d047cd8aSRob Herring if (!can_assume(VALID_INPUT))
1496e9c9686SRob Herring if ((absoffset < uoffset)
15091feabc2SRob Herring || ((absoffset + len) < absoffset)
15191feabc2SRob Herring || (absoffset + len) > fdt_totalsize(fdt))
15291feabc2SRob Herring return NULL;
1539fffb55fSDavid Gibson
154d047cd8aSRob Herring if (can_assume(LATEST) || fdt_version(fdt) >= 0x11)
1556e9c9686SRob Herring if (((uoffset + len) < uoffset)
1569fffb55fSDavid Gibson || ((offset + len) > fdt_size_dt_struct(fdt)))
1579fffb55fSDavid Gibson return NULL;
1589fffb55fSDavid Gibson
1599130ba88SRob Herring return fdt_offset_ptr_(fdt, offset);
1609fffb55fSDavid Gibson }
1619fffb55fSDavid Gibson
fdt_next_tag(const void * fdt,int startoffset,int * nextoffset)162cd296721SStephen Warren uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
1639fffb55fSDavid Gibson {
16447605971SRob Herring const fdt32_t *tagp, *lenp;
165*ea3723a5SRob Herring uint32_t tag, len, sum;
166cd296721SStephen Warren int offset = startoffset;
1679fffb55fSDavid Gibson const char *p;
1689fffb55fSDavid Gibson
169cd296721SStephen Warren *nextoffset = -FDT_ERR_TRUNCATED;
1709fffb55fSDavid Gibson tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
171d047cd8aSRob Herring if (!can_assume(VALID_DTB) && !tagp)
1729fffb55fSDavid Gibson return FDT_END; /* premature end */
1739fffb55fSDavid Gibson tag = fdt32_to_cpu(*tagp);
1749fffb55fSDavid Gibson offset += FDT_TAGSIZE;
1759fffb55fSDavid Gibson
176cd296721SStephen Warren *nextoffset = -FDT_ERR_BADSTRUCTURE;
1779fffb55fSDavid Gibson switch (tag) {
1789fffb55fSDavid Gibson case FDT_BEGIN_NODE:
1799fffb55fSDavid Gibson /* skip name */
1809fffb55fSDavid Gibson do {
1819fffb55fSDavid Gibson p = fdt_offset_ptr(fdt, offset++, 1);
1829fffb55fSDavid Gibson } while (p && (*p != '\0'));
183d047cd8aSRob Herring if (!can_assume(VALID_DTB) && !p)
184cd296721SStephen Warren return FDT_END; /* premature end */
1859fffb55fSDavid Gibson break;
186cd296721SStephen Warren
1879fffb55fSDavid Gibson case FDT_PROP:
1889fffb55fSDavid Gibson lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
189d047cd8aSRob Herring if (!can_assume(VALID_DTB) && !lenp)
190cd296721SStephen Warren return FDT_END; /* premature end */
191*ea3723a5SRob Herring
192*ea3723a5SRob Herring len = fdt32_to_cpu(*lenp);
193*ea3723a5SRob Herring sum = len + offset;
194*ea3723a5SRob Herring if (!can_assume(VALID_DTB) &&
195*ea3723a5SRob Herring (INT_MAX <= sum || sum < (uint32_t) offset))
196*ea3723a5SRob Herring return FDT_END; /* premature end */
197*ea3723a5SRob Herring
198cd296721SStephen Warren /* skip-name offset, length and value */
199*ea3723a5SRob Herring offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len;
200*ea3723a5SRob Herring
201d047cd8aSRob Herring if (!can_assume(LATEST) &&
202*ea3723a5SRob Herring fdt_version(fdt) < 0x10 && len >= 8 &&
203*ea3723a5SRob Herring ((offset - len) % 8) != 0)
2049130ba88SRob Herring offset += 4;
2059fffb55fSDavid Gibson break;
206cd296721SStephen Warren
207cd296721SStephen Warren case FDT_END:
208cd296721SStephen Warren case FDT_END_NODE:
209cd296721SStephen Warren case FDT_NOP:
210cd296721SStephen Warren break;
211cd296721SStephen Warren
212cd296721SStephen Warren default:
213cd296721SStephen Warren return FDT_END;
2149fffb55fSDavid Gibson }
2159fffb55fSDavid Gibson
216cd296721SStephen Warren if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
217cd296721SStephen Warren return FDT_END; /* premature end */
2189fffb55fSDavid Gibson
219cd296721SStephen Warren *nextoffset = FDT_TAGALIGN(offset);
2209fffb55fSDavid Gibson return tag;
2219fffb55fSDavid Gibson }
2229fffb55fSDavid Gibson
fdt_check_node_offset_(const void * fdt,int offset)2239130ba88SRob Herring int fdt_check_node_offset_(const void *fdt, int offset)
2249fffb55fSDavid Gibson {
2256e9c9686SRob Herring if (!can_assume(VALID_INPUT)
2266e9c9686SRob Herring && ((offset < 0) || (offset % FDT_TAGSIZE)))
2276e9c9686SRob Herring return -FDT_ERR_BADOFFSET;
2286e9c9686SRob Herring
2296e9c9686SRob Herring if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
2309fffb55fSDavid Gibson return -FDT_ERR_BADOFFSET;
2319fffb55fSDavid Gibson
2329fffb55fSDavid Gibson return offset;
2339fffb55fSDavid Gibson }
2349fffb55fSDavid Gibson
fdt_check_prop_offset_(const void * fdt,int offset)2359130ba88SRob Herring int fdt_check_prop_offset_(const void *fdt, int offset)
236cd296721SStephen Warren {
2376e9c9686SRob Herring if (!can_assume(VALID_INPUT)
2386e9c9686SRob Herring && ((offset < 0) || (offset % FDT_TAGSIZE)))
2396e9c9686SRob Herring return -FDT_ERR_BADOFFSET;
2406e9c9686SRob Herring
2416e9c9686SRob Herring if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)
242cd296721SStephen Warren return -FDT_ERR_BADOFFSET;
243cd296721SStephen Warren
244cd296721SStephen Warren return offset;
245cd296721SStephen Warren }
246cd296721SStephen Warren
fdt_next_node(const void * fdt,int offset,int * depth)2479fffb55fSDavid Gibson int fdt_next_node(const void *fdt, int offset, int *depth)
2489fffb55fSDavid Gibson {
2499fffb55fSDavid Gibson int nextoffset = 0;
2509fffb55fSDavid Gibson uint32_t tag;
2519fffb55fSDavid Gibson
2529fffb55fSDavid Gibson if (offset >= 0)
2539130ba88SRob Herring if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
2549fffb55fSDavid Gibson return nextoffset;
2559fffb55fSDavid Gibson
2569fffb55fSDavid Gibson do {
2579fffb55fSDavid Gibson offset = nextoffset;
2589fffb55fSDavid Gibson tag = fdt_next_tag(fdt, offset, &nextoffset);
2599fffb55fSDavid Gibson
2609fffb55fSDavid Gibson switch (tag) {
2619fffb55fSDavid Gibson case FDT_PROP:
2629fffb55fSDavid Gibson case FDT_NOP:
2639fffb55fSDavid Gibson break;
2649fffb55fSDavid Gibson
2659fffb55fSDavid Gibson case FDT_BEGIN_NODE:
2669fffb55fSDavid Gibson if (depth)
2679fffb55fSDavid Gibson (*depth)++;
2689fffb55fSDavid Gibson break;
2699fffb55fSDavid Gibson
2709fffb55fSDavid Gibson case FDT_END_NODE:
271cd296721SStephen Warren if (depth && ((--(*depth)) < 0))
272cd296721SStephen Warren return nextoffset;
2739fffb55fSDavid Gibson break;
2749fffb55fSDavid Gibson
2759fffb55fSDavid Gibson case FDT_END:
276cd296721SStephen Warren if ((nextoffset >= 0)
277cd296721SStephen Warren || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
2789fffb55fSDavid Gibson return -FDT_ERR_NOTFOUND;
279cd296721SStephen Warren else
280cd296721SStephen Warren return nextoffset;
2819fffb55fSDavid Gibson }
2829fffb55fSDavid Gibson } while (tag != FDT_BEGIN_NODE);
2839fffb55fSDavid Gibson
2849fffb55fSDavid Gibson return offset;
2859fffb55fSDavid Gibson }
2869fffb55fSDavid Gibson
fdt_first_subnode(const void * fdt,int offset)28747605971SRob Herring int fdt_first_subnode(const void *fdt, int offset)
28847605971SRob Herring {
28947605971SRob Herring int depth = 0;
29047605971SRob Herring
29147605971SRob Herring offset = fdt_next_node(fdt, offset, &depth);
29247605971SRob Herring if (offset < 0 || depth != 1)
29347605971SRob Herring return -FDT_ERR_NOTFOUND;
29447605971SRob Herring
29547605971SRob Herring return offset;
29647605971SRob Herring }
29747605971SRob Herring
fdt_next_subnode(const void * fdt,int offset)29847605971SRob Herring int fdt_next_subnode(const void *fdt, int offset)
29947605971SRob Herring {
30047605971SRob Herring int depth = 1;
30147605971SRob Herring
30247605971SRob Herring /*
30347605971SRob Herring * With respect to the parent, the depth of the next subnode will be
30447605971SRob Herring * the same as the last.
30547605971SRob Herring */
30647605971SRob Herring do {
30747605971SRob Herring offset = fdt_next_node(fdt, offset, &depth);
30847605971SRob Herring if (offset < 0 || depth < 1)
30947605971SRob Herring return -FDT_ERR_NOTFOUND;
31047605971SRob Herring } while (depth > 1);
31147605971SRob Herring
31247605971SRob Herring return offset;
31347605971SRob Herring }
31447605971SRob Herring
fdt_find_string_(const char * strtab,int tabsize,const char * s)3159130ba88SRob Herring const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
3169fffb55fSDavid Gibson {
3179fffb55fSDavid Gibson int len = strlen(s) + 1;
3189fffb55fSDavid Gibson const char *last = strtab + tabsize - len;
3199fffb55fSDavid Gibson const char *p;
3209fffb55fSDavid Gibson
3219fffb55fSDavid Gibson for (p = strtab; p <= last; p++)
3229fffb55fSDavid Gibson if (memcmp(p, s, len) == 0)
3239fffb55fSDavid Gibson return p;
3249fffb55fSDavid Gibson return NULL;
3259fffb55fSDavid Gibson }
3269fffb55fSDavid Gibson
fdt_move(const void * fdt,void * buf,int bufsize)3279fffb55fSDavid Gibson int fdt_move(const void *fdt, void *buf, int bufsize)
3289fffb55fSDavid Gibson {
3296e9c9686SRob Herring if (!can_assume(VALID_INPUT) && bufsize < 0)
3306e9c9686SRob Herring return -FDT_ERR_NOSPACE;
3316e9c9686SRob Herring
332f858927fSRob Herring FDT_RO_PROBE(fdt);
3339fffb55fSDavid Gibson
3346e9c9686SRob Herring if (fdt_totalsize(fdt) > (unsigned int)bufsize)
3359fffb55fSDavid Gibson return -FDT_ERR_NOSPACE;
3369fffb55fSDavid Gibson
3379fffb55fSDavid Gibson memmove(buf, fdt, fdt_totalsize(fdt));
3389fffb55fSDavid Gibson return 0;
3399fffb55fSDavid Gibson }
340