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