1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
6 #include "libfdt_env.h"
11 #include "libfdt_internal.h"
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.
18 int32_t fdt_ro_probe_(const void *fdt
)
20 uint32_t totalsize
= fdt_totalsize(fdt
);
22 if (fdt_magic(fdt
) == FDT_MAGIC
) {
24 if (fdt_version(fdt
) < FDT_FIRST_SUPPORTED_VERSION
)
25 return -FDT_ERR_BADVERSION
;
26 if (fdt_last_comp_version(fdt
) > FDT_LAST_SUPPORTED_VERSION
)
27 return -FDT_ERR_BADVERSION
;
28 } else if (fdt_magic(fdt
) == FDT_SW_MAGIC
) {
29 /* Unfinished sequential-write blob */
30 if (fdt_size_dt_struct(fdt
) == 0)
31 return -FDT_ERR_BADSTATE
;
33 return -FDT_ERR_BADMAGIC
;
36 if (totalsize
< INT32_MAX
)
39 return -FDT_ERR_TRUNCATED
;
42 static int check_off_(uint32_t hdrsize
, uint32_t totalsize
, uint32_t off
)
44 return (off
>= hdrsize
) && (off
<= totalsize
);
47 static int check_block_(uint32_t hdrsize
, uint32_t totalsize
,
48 uint32_t base
, uint32_t size
)
50 if (!check_off_(hdrsize
, totalsize
, base
))
51 return 0; /* block start out of bounds */
52 if ((base
+ size
) < base
)
53 return 0; /* overflow */
54 if (!check_off_(hdrsize
, totalsize
, base
+ size
))
55 return 0; /* block end out of bounds */
59 size_t fdt_header_size_(uint32_t version
)
63 else if (version
<= 2)
65 else if (version
<= 3)
67 else if (version
<= 16)
73 int fdt_check_header(const void *fdt
)
77 if (fdt_magic(fdt
) != FDT_MAGIC
)
78 return -FDT_ERR_BADMAGIC
;
79 hdrsize
= fdt_header_size(fdt
);
80 if ((fdt_version(fdt
) < FDT_FIRST_SUPPORTED_VERSION
)
81 || (fdt_last_comp_version(fdt
) > FDT_LAST_SUPPORTED_VERSION
))
82 return -FDT_ERR_BADVERSION
;
83 if (fdt_version(fdt
) < fdt_last_comp_version(fdt
))
84 return -FDT_ERR_BADVERSION
;
86 if ((fdt_totalsize(fdt
) < hdrsize
)
87 || (fdt_totalsize(fdt
) > INT_MAX
))
88 return -FDT_ERR_TRUNCATED
;
90 /* Bounds check memrsv block */
91 if (!check_off_(hdrsize
, fdt_totalsize(fdt
), fdt_off_mem_rsvmap(fdt
)))
92 return -FDT_ERR_TRUNCATED
;
94 /* Bounds check structure block */
95 if (fdt_version(fdt
) < 17) {
96 if (!check_off_(hdrsize
, fdt_totalsize(fdt
),
97 fdt_off_dt_struct(fdt
)))
98 return -FDT_ERR_TRUNCATED
;
100 if (!check_block_(hdrsize
, fdt_totalsize(fdt
),
101 fdt_off_dt_struct(fdt
),
102 fdt_size_dt_struct(fdt
)))
103 return -FDT_ERR_TRUNCATED
;
106 /* Bounds check strings block */
107 if (!check_block_(hdrsize
, fdt_totalsize(fdt
),
108 fdt_off_dt_strings(fdt
), fdt_size_dt_strings(fdt
)))
109 return -FDT_ERR_TRUNCATED
;
114 const void *fdt_offset_ptr(const void *fdt
, int offset
, unsigned int len
)
116 unsigned absoffset
= offset
+ fdt_off_dt_struct(fdt
);
118 if ((absoffset
< offset
)
119 || ((absoffset
+ len
) < absoffset
)
120 || (absoffset
+ len
) > fdt_totalsize(fdt
))
123 if (fdt_version(fdt
) >= 0x11)
124 if (((offset
+ len
) < offset
)
125 || ((offset
+ len
) > fdt_size_dt_struct(fdt
)))
128 return fdt_offset_ptr_(fdt
, offset
);
131 uint32_t fdt_next_tag(const void *fdt
, int startoffset
, int *nextoffset
)
133 const fdt32_t
*tagp
, *lenp
;
135 int offset
= startoffset
;
138 *nextoffset
= -FDT_ERR_TRUNCATED
;
139 tagp
= fdt_offset_ptr(fdt
, offset
, FDT_TAGSIZE
);
141 return FDT_END
; /* premature end */
142 tag
= fdt32_to_cpu(*tagp
);
143 offset
+= FDT_TAGSIZE
;
145 *nextoffset
= -FDT_ERR_BADSTRUCTURE
;
150 p
= fdt_offset_ptr(fdt
, offset
++, 1);
151 } while (p
&& (*p
!= '\0'));
153 return FDT_END
; /* premature end */
157 lenp
= fdt_offset_ptr(fdt
, offset
, sizeof(*lenp
));
159 return FDT_END
; /* premature end */
160 /* skip-name offset, length and value */
161 offset
+= sizeof(struct fdt_property
) - FDT_TAGSIZE
162 + fdt32_to_cpu(*lenp
);
163 if (fdt_version(fdt
) < 0x10 && fdt32_to_cpu(*lenp
) >= 8 &&
164 ((offset
- fdt32_to_cpu(*lenp
)) % 8) != 0)
177 if (!fdt_offset_ptr(fdt
, startoffset
, offset
- startoffset
))
178 return FDT_END
; /* premature end */
180 *nextoffset
= FDT_TAGALIGN(offset
);
184 int fdt_check_node_offset_(const void *fdt
, int offset
)
186 if ((offset
< 0) || (offset
% FDT_TAGSIZE
)
187 || (fdt_next_tag(fdt
, offset
, &offset
) != FDT_BEGIN_NODE
))
188 return -FDT_ERR_BADOFFSET
;
193 int fdt_check_prop_offset_(const void *fdt
, int offset
)
195 if ((offset
< 0) || (offset
% FDT_TAGSIZE
)
196 || (fdt_next_tag(fdt
, offset
, &offset
) != FDT_PROP
))
197 return -FDT_ERR_BADOFFSET
;
202 int fdt_next_node(const void *fdt
, int offset
, int *depth
)
208 if ((nextoffset
= fdt_check_node_offset_(fdt
, offset
)) < 0)
213 tag
= fdt_next_tag(fdt
, offset
, &nextoffset
);
226 if (depth
&& ((--(*depth
)) < 0))
231 if ((nextoffset
>= 0)
232 || ((nextoffset
== -FDT_ERR_TRUNCATED
) && !depth
))
233 return -FDT_ERR_NOTFOUND
;
237 } while (tag
!= FDT_BEGIN_NODE
);
242 int fdt_first_subnode(const void *fdt
, int offset
)
246 offset
= fdt_next_node(fdt
, offset
, &depth
);
247 if (offset
< 0 || depth
!= 1)
248 return -FDT_ERR_NOTFOUND
;
253 int fdt_next_subnode(const void *fdt
, int offset
)
258 * With respect to the parent, the depth of the next subnode will be
259 * the same as the last.
262 offset
= fdt_next_node(fdt
, offset
, &depth
);
263 if (offset
< 0 || depth
< 1)
264 return -FDT_ERR_NOTFOUND
;
270 const char *fdt_find_string_(const char *strtab
, int tabsize
, const char *s
)
272 int len
= strlen(s
) + 1;
273 const char *last
= strtab
+ tabsize
- len
;
276 for (p
= strtab
; p
<= last
; p
++)
277 if (memcmp(p
, s
, len
) == 0)
282 int fdt_move(const void *fdt
, void *buf
, int bufsize
)
286 if (fdt_totalsize(fdt
) > bufsize
)
287 return -FDT_ERR_NOSPACE
;
289 memmove(buf
, fdt
, fdt_totalsize(fdt
));