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 /* 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 52 static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) 53 { 54 return (off >= hdrsize) && (off <= totalsize); 55 } 56 57 static 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 69 size_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 83 size_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 89 int 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 if ((fdt_totalsize(fdt) < hdrsize) 110 || (fdt_totalsize(fdt) > INT_MAX)) 111 return -FDT_ERR_TRUNCATED; 112 113 /* memrsv block must be 8 byte aligned */ 114 if (fdt_off_mem_rsvmap(fdt) % sizeof(uint64_t)) 115 return -FDT_ERR_ALIGNMENT; 116 117 /* Structure block must be 4 byte aligned */ 118 if (fdt_off_dt_struct(fdt) % FDT_TAGSIZE) 119 return -FDT_ERR_ALIGNMENT; 120 121 /* Bounds check memrsv block */ 122 if (!check_off_(hdrsize, fdt_totalsize(fdt), 123 fdt_off_mem_rsvmap(fdt))) 124 return -FDT_ERR_TRUNCATED; 125 126 /* Bounds check structure block */ 127 if (!can_assume(LATEST) && fdt_version(fdt) < 17) { 128 if (!check_off_(hdrsize, fdt_totalsize(fdt), 129 fdt_off_dt_struct(fdt))) 130 return -FDT_ERR_TRUNCATED; 131 } else { 132 if (!check_block_(hdrsize, fdt_totalsize(fdt), 133 fdt_off_dt_struct(fdt), 134 fdt_size_dt_struct(fdt))) 135 return -FDT_ERR_TRUNCATED; 136 } 137 138 /* Bounds check strings block */ 139 if (!check_block_(hdrsize, fdt_totalsize(fdt), 140 fdt_off_dt_strings(fdt), 141 fdt_size_dt_strings(fdt))) 142 return -FDT_ERR_TRUNCATED; 143 } 144 145 return 0; 146 } 147 148 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 149 { 150 unsigned int uoffset = offset; 151 unsigned int absoffset = offset + fdt_off_dt_struct(fdt); 152 153 if (offset < 0) 154 return NULL; 155 156 if (!can_assume(VALID_INPUT)) 157 if ((absoffset < uoffset) 158 || ((absoffset + len) < absoffset) 159 || (absoffset + len) > fdt_totalsize(fdt)) 160 return NULL; 161 162 if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) 163 if (((uoffset + len) < uoffset) 164 || ((offset + len) > fdt_size_dt_struct(fdt))) 165 return NULL; 166 167 return fdt_offset_ptr_(fdt, offset); 168 } 169 170 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 171 { 172 const fdt32_t *tagp, *lenp; 173 uint32_t tag, len, sum; 174 int offset = startoffset; 175 const char *p; 176 177 *nextoffset = -FDT_ERR_TRUNCATED; 178 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 179 if (!can_assume(VALID_DTB) && !tagp) 180 return FDT_END; /* premature end */ 181 tag = fdt32_to_cpu(*tagp); 182 offset += FDT_TAGSIZE; 183 184 *nextoffset = -FDT_ERR_BADSTRUCTURE; 185 switch (tag) { 186 case FDT_BEGIN_NODE: 187 /* skip name */ 188 do { 189 p = fdt_offset_ptr(fdt, offset++, 1); 190 } while (p && (*p != '\0')); 191 if (!can_assume(VALID_DTB) && !p) 192 return FDT_END; /* premature end */ 193 break; 194 195 case FDT_PROP: 196 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 197 if (!can_assume(VALID_DTB) && !lenp) 198 return FDT_END; /* premature end */ 199 200 len = fdt32_to_cpu(*lenp); 201 sum = len + offset; 202 if (!can_assume(VALID_DTB) && 203 (INT_MAX <= sum || sum < (uint32_t) offset)) 204 return FDT_END; /* premature end */ 205 206 /* skip-name offset, length and value */ 207 offset += sizeof(struct fdt_property) - FDT_TAGSIZE + len; 208 209 if (!can_assume(LATEST) && 210 fdt_version(fdt) < 0x10 && len >= 8 && 211 ((offset - len) % 8) != 0) 212 offset += 4; 213 break; 214 215 case FDT_END: 216 case FDT_END_NODE: 217 case FDT_NOP: 218 break; 219 220 default: 221 return FDT_END; 222 } 223 224 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 225 return FDT_END; /* premature end */ 226 227 *nextoffset = FDT_TAGALIGN(offset); 228 return tag; 229 } 230 231 int fdt_check_node_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_BEGIN_NODE) 238 return -FDT_ERR_BADOFFSET; 239 240 return offset; 241 } 242 243 int fdt_check_prop_offset_(const void *fdt, int offset) 244 { 245 if (!can_assume(VALID_INPUT) 246 && ((offset < 0) || (offset % FDT_TAGSIZE))) 247 return -FDT_ERR_BADOFFSET; 248 249 if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) 250 return -FDT_ERR_BADOFFSET; 251 252 return offset; 253 } 254 255 int fdt_next_node(const void *fdt, int offset, int *depth) 256 { 257 int nextoffset = 0; 258 uint32_t tag; 259 260 if (offset >= 0) 261 if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) 262 return nextoffset; 263 264 do { 265 offset = nextoffset; 266 tag = fdt_next_tag(fdt, offset, &nextoffset); 267 268 switch (tag) { 269 case FDT_PROP: 270 case FDT_NOP: 271 break; 272 273 case FDT_BEGIN_NODE: 274 if (depth) 275 (*depth)++; 276 break; 277 278 case FDT_END_NODE: 279 if (depth && ((--(*depth)) < 0)) 280 return nextoffset; 281 break; 282 283 case FDT_END: 284 if ((nextoffset >= 0) 285 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 286 return -FDT_ERR_NOTFOUND; 287 else 288 return nextoffset; 289 } 290 } while (tag != FDT_BEGIN_NODE); 291 292 return offset; 293 } 294 295 int fdt_first_subnode(const void *fdt, int offset) 296 { 297 int depth = 0; 298 299 offset = fdt_next_node(fdt, offset, &depth); 300 if (offset < 0 || depth != 1) 301 return -FDT_ERR_NOTFOUND; 302 303 return offset; 304 } 305 306 int fdt_next_subnode(const void *fdt, int offset) 307 { 308 int depth = 1; 309 310 /* 311 * With respect to the parent, the depth of the next subnode will be 312 * the same as the last. 313 */ 314 do { 315 offset = fdt_next_node(fdt, offset, &depth); 316 if (offset < 0 || depth < 1) 317 return -FDT_ERR_NOTFOUND; 318 } while (depth > 1); 319 320 return offset; 321 } 322 323 const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s, 324 int slen) 325 { 326 const char *last = strtab + tabsize - (slen + 1); 327 const char *p; 328 329 for (p = strtab; p <= last; p++) 330 if (memcmp(p, s, slen) == 0 && p[slen] == '\0') 331 return p; 332 return NULL; 333 } 334 335 int fdt_move(const void *fdt, void *buf, int bufsize) 336 { 337 if (!can_assume(VALID_INPUT) && bufsize < 0) 338 return -FDT_ERR_NOSPACE; 339 340 FDT_RO_PROBE(fdt); 341 342 if (fdt_totalsize(fdt) > (unsigned int)bufsize) 343 return -FDT_ERR_NOSPACE; 344 345 memmove(buf, fdt, fdt_totalsize(fdt)); 346 return 0; 347 } 348