1 // SPDX-License-Identifier: (GPL-2.0-or-later 1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) 2 /* 2 /* 3 * libfdt - Flat Device Tree manipulation 3 * libfdt - Flat Device Tree manipulation 4 * Copyright (C) 2006 David Gibson, IBM Corpor 4 * Copyright (C) 2006 David Gibson, IBM Corporation. 5 */ 5 */ 6 #include "libfdt_env.h" 6 #include "libfdt_env.h" 7 7 8 #include <fdt.h> 8 #include <fdt.h> 9 #include <libfdt.h> 9 #include <libfdt.h> 10 10 11 #include "libfdt_internal.h" 11 #include "libfdt_internal.h" 12 12 13 /* 13 /* 14 * Minimal sanity check for a read-only tree. 14 * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks 15 * that the given buffer contains what appears 15 * that the given buffer contains what appears to be a flattened 16 * device tree with sane information in its he 16 * device tree with sane information in its header. 17 */ 17 */ 18 int32_t fdt_ro_probe_(const void *fdt) !! 18 int fdt_ro_probe_(const void *fdt) 19 { 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-byt << 26 if ((uintptr_t)fdt & 7) << 27 return -FDT_ERR_ALIGNMENT; << 28 << 29 if (fdt_magic(fdt) == FDT_MAGIC) { 20 if (fdt_magic(fdt) == FDT_MAGIC) { 30 /* Complete tree */ 21 /* Complete tree */ 31 if (!can_assume(LATEST)) { !! 22 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 32 if (fdt_version(fdt) < !! 23 return -FDT_ERR_BADVERSION; 33 return -FDT_ER !! 24 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) 34 if (fdt_last_comp_vers !! 25 return -FDT_ERR_BADVERSION; 35 FDT_LA << 36 return -FDT_ER << 37 } << 38 } else if (fdt_magic(fdt) == FDT_SW_MA 26 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 39 /* Unfinished sequential-write 27 /* Unfinished sequential-write blob */ 40 if (!can_assume(VALID_INPUT) & !! 28 if (fdt_size_dt_struct(fdt) == 0) 41 return -FDT_ERR_BADSTA 29 return -FDT_ERR_BADSTATE; 42 } else { 30 } else { 43 return -FDT_ERR_BADMAGIC; 31 return -FDT_ERR_BADMAGIC; 44 } 32 } 45 33 46 if (totalsize < INT32_MAX) !! 34 return 0; 47 return totalsize; << 48 else << 49 return -FDT_ERR_TRUNCATED; << 50 } 35 } 51 36 52 static int check_off_(uint32_t hdrsize, uint32 37 static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) 53 { 38 { 54 return (off >= hdrsize) && (off <= tot 39 return (off >= hdrsize) && (off <= totalsize); 55 } 40 } 56 41 57 static int check_block_(uint32_t hdrsize, uint 42 static int check_block_(uint32_t hdrsize, uint32_t totalsize, 58 uint32_t base, uint32_ 43 uint32_t base, uint32_t size) 59 { 44 { 60 if (!check_off_(hdrsize, totalsize, ba 45 if (!check_off_(hdrsize, totalsize, base)) 61 return 0; /* block start out o 46 return 0; /* block start out of bounds */ 62 if ((base + size) < base) 47 if ((base + size) < base) 63 return 0; /* overflow */ 48 return 0; /* overflow */ 64 if (!check_off_(hdrsize, totalsize, ba 49 if (!check_off_(hdrsize, totalsize, base + size)) 65 return 0; /* block end out of 50 return 0; /* block end out of bounds */ 66 return 1; 51 return 1; 67 } 52 } 68 53 69 size_t fdt_header_size_(uint32_t version) 54 size_t fdt_header_size_(uint32_t version) 70 { 55 { 71 if (version <= 1) 56 if (version <= 1) 72 return FDT_V1_SIZE; 57 return FDT_V1_SIZE; 73 else if (version <= 2) 58 else if (version <= 2) 74 return FDT_V2_SIZE; 59 return FDT_V2_SIZE; 75 else if (version <= 3) 60 else if (version <= 3) 76 return FDT_V3_SIZE; 61 return FDT_V3_SIZE; 77 else if (version <= 16) 62 else if (version <= 16) 78 return FDT_V16_SIZE; 63 return FDT_V16_SIZE; 79 else 64 else 80 return FDT_V17_SIZE; 65 return FDT_V17_SIZE; 81 } 66 } 82 67 83 size_t fdt_header_size(const void *fdt) << 84 { << 85 return can_assume(LATEST) ? FDT_V17_SI << 86 fdt_header_size_(fdt_version(f << 87 } << 88 << 89 int fdt_check_header(const void *fdt) 68 int fdt_check_header(const void *fdt) 90 { 69 { 91 size_t hdrsize; 70 size_t hdrsize; 92 71 93 /* The device tree must be at an 8-byt << 94 if ((uintptr_t)fdt & 7) << 95 return -FDT_ERR_ALIGNMENT; << 96 << 97 if (fdt_magic(fdt) != FDT_MAGIC) 72 if (fdt_magic(fdt) != FDT_MAGIC) 98 return -FDT_ERR_BADMAGIC; 73 return -FDT_ERR_BADMAGIC; 99 if (!can_assume(LATEST)) { << 100 if ((fdt_version(fdt) < FDT_FI << 101 || (fdt_last_comp_version( << 102 FDT_LAST_SUPPORTED_VER << 103 return -FDT_ERR_BADVER << 104 if (fdt_version(fdt) < fdt_las << 105 return -FDT_ERR_BADVER << 106 } << 107 hdrsize = fdt_header_size(fdt); 74 hdrsize = fdt_header_size(fdt); 108 if (!can_assume(VALID_DTB)) { !! 75 if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 109 if ((fdt_totalsize(fdt) < hdrs !! 76 || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) 110 || (fdt_totalsize(fdt) > I !! 77 return -FDT_ERR_BADVERSION; 111 return -FDT_ERR_TRUNCA !! 78 if (fdt_version(fdt) < fdt_last_comp_version(fdt)) >> 79 return -FDT_ERR_BADVERSION; 112 80 113 /* Bounds check memrsv block * !! 81 if ((fdt_totalsize(fdt) < hdrsize) 114 if (!check_off_(hdrsize, fdt_t !! 82 || (fdt_totalsize(fdt) > INT_MAX)) 115 fdt_off_mem_rs !! 83 return -FDT_ERR_TRUNCATED; 116 return -FDT_ERR_TRUNCA << 117 84 118 /* Bounds check structure bloc !! 85 /* Bounds check memrsv block */ 119 if (!can_assume(LATEST) && fdt !! 86 if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) 120 if (!check_off_(hdrsiz !! 87 return -FDT_ERR_TRUNCATED; 121 fdt_of << 122 return -FDT_ER << 123 } else { << 124 if (!check_block_(hdrs << 125 fdt_ << 126 fdt_ << 127 return -FDT_ER << 128 } << 129 88 130 /* Bounds check strings block !! 89 /* Bounds check structure block */ >> 90 if (fdt_version(fdt) < 17) { >> 91 if (!check_off_(hdrsize, fdt_totalsize(fdt), >> 92 fdt_off_dt_struct(fdt))) >> 93 return -FDT_ERR_TRUNCATED; >> 94 } else { 131 if (!check_block_(hdrsize, fdt 95 if (!check_block_(hdrsize, fdt_totalsize(fdt), 132 fdt_off_dt_s !! 96 fdt_off_dt_struct(fdt), 133 fdt_size_dt_ !! 97 fdt_size_dt_struct(fdt))) 134 return -FDT_ERR_TRUNCA 98 return -FDT_ERR_TRUNCATED; 135 } 99 } 136 100 >> 101 /* Bounds check strings block */ >> 102 if (!check_block_(hdrsize, fdt_totalsize(fdt), >> 103 fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) >> 104 return -FDT_ERR_TRUNCATED; >> 105 137 return 0; 106 return 0; 138 } 107 } 139 108 140 const void *fdt_offset_ptr(const void *fdt, in 109 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 141 { 110 { 142 unsigned int uoffset = offset; !! 111 unsigned absoffset = offset + fdt_off_dt_struct(fdt); 143 unsigned int absoffset = offset + fdt_ << 144 112 145 if (offset < 0) !! 113 if ((absoffset < offset) >> 114 || ((absoffset + len) < absoffset) >> 115 || (absoffset + len) > fdt_totalsize(fdt)) 146 return NULL; 116 return NULL; 147 117 148 if (!can_assume(VALID_INPUT)) !! 118 if (fdt_version(fdt) >= 0x11) 149 if ((absoffset < uoffset) !! 119 if (((offset + len) < offset) 150 || ((absoffset + len) < ab << 151 || (absoffset + len) > fdt << 152 return NULL; << 153 << 154 if (can_assume(LATEST) || fdt_version( << 155 if (((uoffset + len) < uoffset << 156 || ((offset + len) > fdt_s 120 || ((offset + len) > fdt_size_dt_struct(fdt))) 157 return NULL; 121 return NULL; 158 122 159 return fdt_offset_ptr_(fdt, offset); 123 return fdt_offset_ptr_(fdt, offset); 160 } 124 } 161 125 162 uint32_t fdt_next_tag(const void *fdt, int sta 126 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 163 { 127 { 164 const fdt32_t *tagp, *lenp; 128 const fdt32_t *tagp, *lenp; 165 uint32_t tag, len, sum; !! 129 uint32_t tag; 166 int offset = startoffset; 130 int offset = startoffset; 167 const char *p; 131 const char *p; 168 132 169 *nextoffset = -FDT_ERR_TRUNCATED; 133 *nextoffset = -FDT_ERR_TRUNCATED; 170 tagp = fdt_offset_ptr(fdt, offset, FDT 134 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 171 if (!can_assume(VALID_DTB) && !tagp) !! 135 if (!tagp) 172 return FDT_END; /* premature e 136 return FDT_END; /* premature end */ 173 tag = fdt32_to_cpu(*tagp); 137 tag = fdt32_to_cpu(*tagp); 174 offset += FDT_TAGSIZE; 138 offset += FDT_TAGSIZE; 175 139 176 *nextoffset = -FDT_ERR_BADSTRUCTURE; 140 *nextoffset = -FDT_ERR_BADSTRUCTURE; 177 switch (tag) { 141 switch (tag) { 178 case FDT_BEGIN_NODE: 142 case FDT_BEGIN_NODE: 179 /* skip name */ 143 /* skip name */ 180 do { 144 do { 181 p = fdt_offset_ptr(fdt 145 p = fdt_offset_ptr(fdt, offset++, 1); 182 } while (p && (*p != '\0')); 146 } while (p && (*p != '\0')); 183 if (!can_assume(VALID_DTB) && !! 147 if (!p) 184 return FDT_END; /* pre 148 return FDT_END; /* premature end */ 185 break; 149 break; 186 150 187 case FDT_PROP: 151 case FDT_PROP: 188 lenp = fdt_offset_ptr(fdt, off 152 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 189 if (!can_assume(VALID_DTB) && !! 153 if (!lenp) 190 return FDT_END; /* pre 154 return FDT_END; /* premature end */ 191 << 192 len = fdt32_to_cpu(*lenp); << 193 sum = len + offset; << 194 if (!can_assume(VALID_DTB) && << 195 (INT_MAX <= sum || sum < ( << 196 return FDT_END; /* pre << 197 << 198 /* skip-name offset, length an 155 /* skip-name offset, length and value */ 199 offset += sizeof(struct fdt_pr !! 156 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 200 !! 157 + fdt32_to_cpu(*lenp); 201 if (!can_assume(LATEST) && !! 158 if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && 202 fdt_version(fdt) < 0x10 && !! 159 ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) 203 ((offset - len) % 8) != 0) << 204 offset += 4; 160 offset += 4; 205 break; 161 break; 206 162 207 case FDT_END: 163 case FDT_END: 208 case FDT_END_NODE: 164 case FDT_END_NODE: 209 case FDT_NOP: 165 case FDT_NOP: 210 break; 166 break; 211 167 212 default: 168 default: 213 return FDT_END; 169 return FDT_END; 214 } 170 } 215 171 216 if (!fdt_offset_ptr(fdt, startoffset, 172 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 217 return FDT_END; /* premature e 173 return FDT_END; /* premature end */ 218 174 219 *nextoffset = FDT_TAGALIGN(offset); 175 *nextoffset = FDT_TAGALIGN(offset); 220 return tag; 176 return tag; 221 } 177 } 222 178 223 int fdt_check_node_offset_(const void *fdt, in 179 int fdt_check_node_offset_(const void *fdt, int offset) 224 { 180 { 225 if (!can_assume(VALID_INPUT) !! 181 if ((offset < 0) || (offset % FDT_TAGSIZE) 226 && ((offset < 0) || (offset % FDT_ !! 182 || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 227 return -FDT_ERR_BADOFFSET; << 228 << 229 if (fdt_next_tag(fdt, offset, &offset) << 230 return -FDT_ERR_BADOFFSET; 183 return -FDT_ERR_BADOFFSET; 231 184 232 return offset; 185 return offset; 233 } 186 } 234 187 235 int fdt_check_prop_offset_(const void *fdt, in 188 int fdt_check_prop_offset_(const void *fdt, int offset) 236 { 189 { 237 if (!can_assume(VALID_INPUT) !! 190 if ((offset < 0) || (offset % FDT_TAGSIZE) 238 && ((offset < 0) || (offset % FDT_ !! 191 || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 239 return -FDT_ERR_BADOFFSET; << 240 << 241 if (fdt_next_tag(fdt, offset, &offset) << 242 return -FDT_ERR_BADOFFSET; 192 return -FDT_ERR_BADOFFSET; 243 193 244 return offset; 194 return offset; 245 } 195 } 246 196 247 int fdt_next_node(const void *fdt, int offset, 197 int fdt_next_node(const void *fdt, int offset, int *depth) 248 { 198 { 249 int nextoffset = 0; 199 int nextoffset = 0; 250 uint32_t tag; 200 uint32_t tag; 251 201 252 if (offset >= 0) 202 if (offset >= 0) 253 if ((nextoffset = fdt_check_no 203 if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) 254 return nextoffset; 204 return nextoffset; 255 205 256 do { 206 do { 257 offset = nextoffset; 207 offset = nextoffset; 258 tag = fdt_next_tag(fdt, offset 208 tag = fdt_next_tag(fdt, offset, &nextoffset); 259 209 260 switch (tag) { 210 switch (tag) { 261 case FDT_PROP: 211 case FDT_PROP: 262 case FDT_NOP: 212 case FDT_NOP: 263 break; 213 break; 264 214 265 case FDT_BEGIN_NODE: 215 case FDT_BEGIN_NODE: 266 if (depth) 216 if (depth) 267 (*depth)++; 217 (*depth)++; 268 break; 218 break; 269 219 270 case FDT_END_NODE: 220 case FDT_END_NODE: 271 if (depth && ((--(*dep 221 if (depth && ((--(*depth)) < 0)) 272 return nextoff 222 return nextoffset; 273 break; 223 break; 274 224 275 case FDT_END: 225 case FDT_END: 276 if ((nextoffset >= 0) 226 if ((nextoffset >= 0) 277 || ((nextoffset == 227 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 278 return -FDT_ER 228 return -FDT_ERR_NOTFOUND; 279 else 229 else 280 return nextoff 230 return nextoffset; 281 } 231 } 282 } while (tag != FDT_BEGIN_NODE); 232 } while (tag != FDT_BEGIN_NODE); 283 233 284 return offset; 234 return offset; 285 } 235 } 286 236 287 int fdt_first_subnode(const void *fdt, int off 237 int fdt_first_subnode(const void *fdt, int offset) 288 { 238 { 289 int depth = 0; 239 int depth = 0; 290 240 291 offset = fdt_next_node(fdt, offset, &d 241 offset = fdt_next_node(fdt, offset, &depth); 292 if (offset < 0 || depth != 1) 242 if (offset < 0 || depth != 1) 293 return -FDT_ERR_NOTFOUND; 243 return -FDT_ERR_NOTFOUND; 294 244 295 return offset; 245 return offset; 296 } 246 } 297 247 298 int fdt_next_subnode(const void *fdt, int offs 248 int fdt_next_subnode(const void *fdt, int offset) 299 { 249 { 300 int depth = 1; 250 int depth = 1; 301 251 302 /* 252 /* 303 * With respect to the parent, the dep 253 * With respect to the parent, the depth of the next subnode will be 304 * the same as the last. 254 * the same as the last. 305 */ 255 */ 306 do { 256 do { 307 offset = fdt_next_node(fdt, of 257 offset = fdt_next_node(fdt, offset, &depth); 308 if (offset < 0 || depth < 1) 258 if (offset < 0 || depth < 1) 309 return -FDT_ERR_NOTFOU 259 return -FDT_ERR_NOTFOUND; 310 } while (depth > 1); 260 } while (depth > 1); 311 261 312 return offset; 262 return offset; 313 } 263 } 314 264 315 const char *fdt_find_string_(const char *strta 265 const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) 316 { 266 { 317 int len = strlen(s) + 1; 267 int len = strlen(s) + 1; 318 const char *last = strtab + tabsize - 268 const char *last = strtab + tabsize - len; 319 const char *p; 269 const char *p; 320 270 321 for (p = strtab; p <= last; p++) 271 for (p = strtab; p <= last; p++) 322 if (memcmp(p, s, len) == 0) 272 if (memcmp(p, s, len) == 0) 323 return p; 273 return p; 324 return NULL; 274 return NULL; 325 } 275 } 326 276 327 int fdt_move(const void *fdt, void *buf, int b 277 int fdt_move(const void *fdt, void *buf, int bufsize) 328 { 278 { 329 if (!can_assume(VALID_INPUT) && bufsiz << 330 return -FDT_ERR_NOSPACE; << 331 << 332 FDT_RO_PROBE(fdt); 279 FDT_RO_PROBE(fdt); 333 280 334 if (fdt_totalsize(fdt) > (unsigned int !! 281 if (fdt_totalsize(fdt) > bufsize) 335 return -FDT_ERR_NOSPACE; 282 return -FDT_ERR_NOSPACE; 336 283 337 memmove(buf, fdt, fdt_totalsize(fdt)); 284 memmove(buf, fdt, fdt_totalsize(fdt)); 338 return 0; 285 return 0; 339 } 286 } 340 287
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.