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 (fdt_magic(fdt) == FDT_MAGIC) { 23 /* Complete tree */ 24 if (fdt_chk_version()) { 25 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 26 return -FDT_ERR_BADVERSION; 27 if (fdt_last_comp_version(fdt) > 28 FDT_LAST_SUPPORTED_VERSION) 29 return -FDT_ERR_BADVERSION; 30 } 31 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 32 /* Unfinished sequential-write blob */ 33 if (fdt_size_dt_struct(fdt) == 0) 34 return -FDT_ERR_BADSTATE; 35 } else { 36 return -FDT_ERR_BADMAGIC; 37 } 38 39 if (totalsize < INT32_MAX) 40 return totalsize; 41 else 42 return -FDT_ERR_TRUNCATED; 43} 44 45static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) 46{ 47 return (off >= hdrsize) && (off <= totalsize); 48} 49 50static int check_block_(uint32_t hdrsize, uint32_t totalsize, 51 uint32_t base, uint32_t size) 52{ 53 if (!check_off_(hdrsize, totalsize, base)) 54 return 0; /* block start out of bounds */ 55 if ((base + size) < base) 56 return 0; /* overflow */ 57 if (!check_off_(hdrsize, totalsize, base + size)) 58 return 0; /* block end out of bounds */ 59 return 1; 60} 61 62size_t fdt_header_size_(uint32_t version) 63{ 64 if (version <= 1) 65 return FDT_V1_SIZE; 66 else if (version <= 2) 67 return FDT_V2_SIZE; 68 else if (version <= 3) 69 return FDT_V3_SIZE; 70 else if (version <= 16) 71 return FDT_V16_SIZE; 72 else 73 return FDT_V17_SIZE; 74} 75 76size_t fdt_header_size(const void *fdt) 77{ 78 return fdt_chk_version() ? fdt_header_size_(fdt_version(fdt)) : 79 FDT_V17_SIZE; 80} 81 82int fdt_check_header(const void *fdt) 83{ 84 size_t hdrsize; 85 86 if (fdt_magic(fdt) != FDT_MAGIC) 87 return -FDT_ERR_BADMAGIC; 88 if (fdt_chk_version()) { 89 if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 90 || (fdt_last_comp_version(fdt) > 91 FDT_LAST_SUPPORTED_VERSION)) 92 return -FDT_ERR_BADVERSION; 93 if (fdt_version(fdt) < fdt_last_comp_version(fdt)) 94 return -FDT_ERR_BADVERSION; 95 } 96 hdrsize = fdt_header_size(fdt); 97 if (fdt_chk_basic()) { 98 99 if ((fdt_totalsize(fdt) < hdrsize) 100 || (fdt_totalsize(fdt) > INT_MAX)) 101 return -FDT_ERR_TRUNCATED; 102 103 /* Bounds check memrsv block */ 104 if (!check_off_(hdrsize, fdt_totalsize(fdt), 105 fdt_off_mem_rsvmap(fdt))) 106 return -FDT_ERR_TRUNCATED; 107 } 108 109 if (fdt_chk_extra()) { 110 /* Bounds check structure block */ 111 if (fdt_chk_version() && fdt_version(fdt) < 17) { 112 if (!check_off_(hdrsize, fdt_totalsize(fdt), 113 fdt_off_dt_struct(fdt))) 114 return -FDT_ERR_TRUNCATED; 115 } else { 116 if (!check_block_(hdrsize, fdt_totalsize(fdt), 117 fdt_off_dt_struct(fdt), 118 fdt_size_dt_struct(fdt))) 119 return -FDT_ERR_TRUNCATED; 120 } 121 122 /* Bounds check strings block */ 123 if (!check_block_(hdrsize, fdt_totalsize(fdt), 124 fdt_off_dt_strings(fdt), 125 fdt_size_dt_strings(fdt))) 126 return -FDT_ERR_TRUNCATED; 127 } 128 129 return 0; 130} 131 132const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 133{ 134 unsigned int uoffset = offset; 135 unsigned int absoffset = offset + fdt_off_dt_struct(fdt); 136 137 if (offset < 0) 138 return NULL; 139 140 if (fdt_chk_basic()) 141 if ((absoffset < uoffset) 142 || ((absoffset + len) < absoffset) 143 || (absoffset + len) > fdt_totalsize(fdt)) 144 return NULL; 145 146 if (!fdt_chk_version() || fdt_version(fdt) >= 0x11) 147 if (((uoffset + len) < uoffset) 148 || ((offset + len) > fdt_size_dt_struct(fdt))) 149 return NULL; 150 151 return fdt_offset_ptr_(fdt, offset); 152} 153 154uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 155{ 156 const fdt32_t *tagp, *lenp; 157 uint32_t tag; 158 int offset = startoffset; 159 const char *p; 160 161 *nextoffset = -FDT_ERR_TRUNCATED; 162 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 163 if (fdt_chk_basic() && !tagp) 164 return FDT_END; /* premature end */ 165 tag = fdt32_to_cpu(*tagp); 166 offset += FDT_TAGSIZE; 167 168 *nextoffset = -FDT_ERR_BADSTRUCTURE; 169 switch (tag) { 170 case FDT_BEGIN_NODE: 171 /* skip name */ 172 do { 173 p = fdt_offset_ptr(fdt, offset++, 1); 174 } while (p && (*p != '\0')); 175 if (fdt_chk_basic() && !p) 176 return FDT_END; /* premature end */ 177 break; 178 179 case FDT_PROP: 180 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 181 if (fdt_chk_basic() && !lenp) 182 return FDT_END; /* premature end */ 183 /* skip-name offset, length and value */ 184 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 185 + fdt32_to_cpu(*lenp); 186 if (fdt_chk_version() && 187 fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && 188 ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) 189 offset += 4; 190 break; 191 192 case FDT_END: 193 case FDT_END_NODE: 194 case FDT_NOP: 195 break; 196 197 default: 198 return FDT_END; 199 } 200 201 if (fdt_chk_basic() && 202 !fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 203 return FDT_END; /* premature end */ 204 205 *nextoffset = FDT_TAGALIGN(offset); 206 return tag; 207} 208 209int fdt_check_node_offset_(const void *fdt, int offset) 210{ 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 218int 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 227int 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 267int 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 278int 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 295const 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 307int fdt_move(const void *fdt, void *buf, int bufsize) 308{ 309 if (fdt_chk_basic() && bufsize < 0) 310 return -FDT_ERR_NOSPACE; 311 312 FDT_RO_PROBE(fdt); 313 314 if (fdt_totalsize(fdt) > (unsigned int)bufsize) 315 return -FDT_ERR_NOSPACE; 316 317 memmove(buf, fdt, fdt_totalsize(fdt)); 318 return 0; 319} 320