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