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