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 int uoffset = offset; 138 unsigned int absoffset = offset + fdt_off_dt_struct(fdt); 139 140 if (offset < 0) 141 return NULL; 142 143 if (!can_assume(VALID_INPUT)) 144 if ((absoffset < uoffset) 145 || ((absoffset + len) < absoffset) 146 || (absoffset + len) > fdt_totalsize(fdt)) 147 return NULL; 148 149 if (can_assume(LATEST) || fdt_version(fdt) >= 0x11) 150 if (((uoffset + len) < uoffset) 151 || ((offset + len) > fdt_size_dt_struct(fdt))) 152 return NULL; 153 154 return fdt_offset_ptr_(fdt, offset); 155 } 156 157 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 158 { 159 const fdt32_t *tagp, *lenp; 160 uint32_t tag; 161 int offset = startoffset; 162 const char *p; 163 164 *nextoffset = -FDT_ERR_TRUNCATED; 165 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 166 if (!can_assume(VALID_DTB) && !tagp) 167 return FDT_END; /* premature end */ 168 tag = fdt32_to_cpu(*tagp); 169 offset += FDT_TAGSIZE; 170 171 *nextoffset = -FDT_ERR_BADSTRUCTURE; 172 switch (tag) { 173 case FDT_BEGIN_NODE: 174 /* skip name */ 175 do { 176 p = fdt_offset_ptr(fdt, offset++, 1); 177 } while (p && (*p != '\0')); 178 if (!can_assume(VALID_DTB) && !p) 179 return FDT_END; /* premature end */ 180 break; 181 182 case FDT_PROP: 183 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 184 if (!can_assume(VALID_DTB) && !lenp) 185 return FDT_END; /* premature end */ 186 /* skip-name offset, length and value */ 187 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 188 + fdt32_to_cpu(*lenp); 189 if (!can_assume(LATEST) && 190 fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && 191 ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) 192 offset += 4; 193 break; 194 195 case FDT_END: 196 case FDT_END_NODE: 197 case FDT_NOP: 198 break; 199 200 default: 201 return FDT_END; 202 } 203 204 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 205 return FDT_END; /* premature end */ 206 207 *nextoffset = FDT_TAGALIGN(offset); 208 return tag; 209 } 210 211 int fdt_check_node_offset_(const void *fdt, int offset) 212 { 213 if (!can_assume(VALID_INPUT) 214 && ((offset < 0) || (offset % FDT_TAGSIZE))) 215 return -FDT_ERR_BADOFFSET; 216 217 if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE) 218 return -FDT_ERR_BADOFFSET; 219 220 return offset; 221 } 222 223 int fdt_check_prop_offset_(const void *fdt, int offset) 224 { 225 if (!can_assume(VALID_INPUT) 226 && ((offset < 0) || (offset % FDT_TAGSIZE))) 227 return -FDT_ERR_BADOFFSET; 228 229 if (fdt_next_tag(fdt, offset, &offset) != FDT_PROP) 230 return -FDT_ERR_BADOFFSET; 231 232 return offset; 233 } 234 235 int fdt_next_node(const void *fdt, int offset, int *depth) 236 { 237 int nextoffset = 0; 238 uint32_t tag; 239 240 if (offset >= 0) 241 if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) 242 return nextoffset; 243 244 do { 245 offset = nextoffset; 246 tag = fdt_next_tag(fdt, offset, &nextoffset); 247 248 switch (tag) { 249 case FDT_PROP: 250 case FDT_NOP: 251 break; 252 253 case FDT_BEGIN_NODE: 254 if (depth) 255 (*depth)++; 256 break; 257 258 case FDT_END_NODE: 259 if (depth && ((--(*depth)) < 0)) 260 return nextoffset; 261 break; 262 263 case FDT_END: 264 if ((nextoffset >= 0) 265 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 266 return -FDT_ERR_NOTFOUND; 267 else 268 return nextoffset; 269 } 270 } while (tag != FDT_BEGIN_NODE); 271 272 return offset; 273 } 274 275 int fdt_first_subnode(const void *fdt, int offset) 276 { 277 int depth = 0; 278 279 offset = fdt_next_node(fdt, offset, &depth); 280 if (offset < 0 || depth != 1) 281 return -FDT_ERR_NOTFOUND; 282 283 return offset; 284 } 285 286 int fdt_next_subnode(const void *fdt, int offset) 287 { 288 int depth = 1; 289 290 /* 291 * With respect to the parent, the depth of the next subnode will be 292 * the same as the last. 293 */ 294 do { 295 offset = fdt_next_node(fdt, offset, &depth); 296 if (offset < 0 || depth < 1) 297 return -FDT_ERR_NOTFOUND; 298 } while (depth > 1); 299 300 return offset; 301 } 302 303 const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) 304 { 305 int len = strlen(s) + 1; 306 const char *last = strtab + tabsize - len; 307 const char *p; 308 309 for (p = strtab; p <= last; p++) 310 if (memcmp(p, s, len) == 0) 311 return p; 312 return NULL; 313 } 314 315 int fdt_move(const void *fdt, void *buf, int bufsize) 316 { 317 if (!can_assume(VALID_INPUT) && bufsize < 0) 318 return -FDT_ERR_NOSPACE; 319 320 FDT_RO_PROBE(fdt); 321 322 if (fdt_totalsize(fdt) > (unsigned int)bufsize) 323 return -FDT_ERR_NOSPACE; 324 325 memmove(buf, fdt, fdt_totalsize(fdt)); 326 return 0; 327 } 328