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