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