1 /* 2 * libfdt - Flat Device Tree manipulation 3 * Copyright (C) 2006 David Gibson, IBM Corporation. 4 * 5 * libfdt is dual licensed: you can use it either under the terms of 6 * the GPL, or the BSD license, at your option. 7 * 8 * a) This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of the 11 * License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public 19 * License along with this library; if not, write to the Free 20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21 * MA 02110-1301 USA 22 * 23 * Alternatively, 24 * 25 * b) Redistribution and use in source and binary forms, with or 26 * without modification, are permitted provided that the following 27 * conditions are met: 28 * 29 * 1. Redistributions of source code must retain the above 30 * copyright notice, this list of conditions and the following 31 * disclaimer. 32 * 2. Redistributions in binary form must reproduce the above 33 * copyright notice, this list of conditions and the following 34 * disclaimer in the documentation and/or other materials 35 * provided with the distribution. 36 * 37 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 38 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 39 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 40 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 41 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 42 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 48 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 49 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 */ 51 #include "libfdt_env.h" 52 53 #include <fdt.h> 54 #include <libfdt.h> 55 56 #include "libfdt_internal.h" 57 58 /* 59 * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks 60 * that the given buffer contains what appears to be a flattened 61 * device tree with sane information in its header. 62 */ 63 int fdt_ro_probe_(const void *fdt) 64 { 65 if (fdt_magic(fdt) == FDT_MAGIC) { 66 /* Complete tree */ 67 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 68 return -FDT_ERR_BADVERSION; 69 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) 70 return -FDT_ERR_BADVERSION; 71 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 72 /* Unfinished sequential-write blob */ 73 if (fdt_size_dt_struct(fdt) == 0) 74 return -FDT_ERR_BADSTATE; 75 } else { 76 return -FDT_ERR_BADMAGIC; 77 } 78 79 return 0; 80 } 81 82 static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) 83 { 84 return (off >= hdrsize) && (off <= totalsize); 85 } 86 87 static int check_block_(uint32_t hdrsize, uint32_t totalsize, 88 uint32_t base, uint32_t size) 89 { 90 if (!check_off_(hdrsize, totalsize, base)) 91 return 0; /* block start out of bounds */ 92 if ((base + size) < base) 93 return 0; /* overflow */ 94 if (!check_off_(hdrsize, totalsize, base + size)) 95 return 0; /* block end out of bounds */ 96 return 1; 97 } 98 99 size_t fdt_header_size_(uint32_t version) 100 { 101 if (version <= 1) 102 return FDT_V1_SIZE; 103 else if (version <= 2) 104 return FDT_V2_SIZE; 105 else if (version <= 3) 106 return FDT_V3_SIZE; 107 else if (version <= 16) 108 return FDT_V16_SIZE; 109 else 110 return FDT_V17_SIZE; 111 } 112 113 int fdt_check_header(const void *fdt) 114 { 115 size_t hdrsize; 116 117 if (fdt_magic(fdt) != FDT_MAGIC) 118 return -FDT_ERR_BADMAGIC; 119 hdrsize = fdt_header_size(fdt); 120 if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 121 || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) 122 return -FDT_ERR_BADVERSION; 123 if (fdt_version(fdt) < fdt_last_comp_version(fdt)) 124 return -FDT_ERR_BADVERSION; 125 126 if ((fdt_totalsize(fdt) < hdrsize) 127 || (fdt_totalsize(fdt) > INT_MAX)) 128 return -FDT_ERR_TRUNCATED; 129 130 /* Bounds check memrsv block */ 131 if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) 132 return -FDT_ERR_TRUNCATED; 133 134 /* Bounds check structure block */ 135 if (fdt_version(fdt) < 17) { 136 if (!check_off_(hdrsize, fdt_totalsize(fdt), 137 fdt_off_dt_struct(fdt))) 138 return -FDT_ERR_TRUNCATED; 139 } else { 140 if (!check_block_(hdrsize, fdt_totalsize(fdt), 141 fdt_off_dt_struct(fdt), 142 fdt_size_dt_struct(fdt))) 143 return -FDT_ERR_TRUNCATED; 144 } 145 146 /* Bounds check strings block */ 147 if (!check_block_(hdrsize, fdt_totalsize(fdt), 148 fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) 149 return -FDT_ERR_TRUNCATED; 150 151 return 0; 152 } 153 154 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 155 { 156 unsigned absoffset = offset + fdt_off_dt_struct(fdt); 157 158 if ((absoffset < offset) 159 || ((absoffset + len) < absoffset) 160 || (absoffset + len) > fdt_totalsize(fdt)) 161 return NULL; 162 163 if (fdt_version(fdt) >= 0x11) 164 if (((offset + len) < offset) 165 || ((offset + len) > fdt_size_dt_struct(fdt))) 166 return NULL; 167 168 return fdt_offset_ptr_(fdt, offset); 169 } 170 171 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 172 { 173 const fdt32_t *tagp, *lenp; 174 uint32_t tag; 175 int offset = startoffset; 176 const char *p; 177 178 *nextoffset = -FDT_ERR_TRUNCATED; 179 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 180 if (!tagp) 181 return FDT_END; /* premature end */ 182 tag = fdt32_to_cpu(*tagp); 183 offset += FDT_TAGSIZE; 184 185 *nextoffset = -FDT_ERR_BADSTRUCTURE; 186 switch (tag) { 187 case FDT_BEGIN_NODE: 188 /* skip name */ 189 do { 190 p = fdt_offset_ptr(fdt, offset++, 1); 191 } while (p && (*p != '\0')); 192 if (!p) 193 return FDT_END; /* premature end */ 194 break; 195 196 case FDT_PROP: 197 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 198 if (!lenp) 199 return FDT_END; /* premature end */ 200 /* skip-name offset, length and value */ 201 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 202 + fdt32_to_cpu(*lenp); 203 if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && 204 ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) 205 offset += 4; 206 break; 207 208 case FDT_END: 209 case FDT_END_NODE: 210 case FDT_NOP: 211 break; 212 213 default: 214 return FDT_END; 215 } 216 217 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 218 return FDT_END; /* premature end */ 219 220 *nextoffset = FDT_TAGALIGN(offset); 221 return tag; 222 } 223 224 int fdt_check_node_offset_(const void *fdt, int offset) 225 { 226 if ((offset < 0) || (offset % FDT_TAGSIZE) 227 || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 228 return -FDT_ERR_BADOFFSET; 229 230 return offset; 231 } 232 233 int fdt_check_prop_offset_(const void *fdt, int offset) 234 { 235 if ((offset < 0) || (offset % FDT_TAGSIZE) 236 || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 237 return -FDT_ERR_BADOFFSET; 238 239 return offset; 240 } 241 242 int fdt_next_node(const void *fdt, int offset, int *depth) 243 { 244 int nextoffset = 0; 245 uint32_t tag; 246 247 if (offset >= 0) 248 if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) 249 return nextoffset; 250 251 do { 252 offset = nextoffset; 253 tag = fdt_next_tag(fdt, offset, &nextoffset); 254 255 switch (tag) { 256 case FDT_PROP: 257 case FDT_NOP: 258 break; 259 260 case FDT_BEGIN_NODE: 261 if (depth) 262 (*depth)++; 263 break; 264 265 case FDT_END_NODE: 266 if (depth && ((--(*depth)) < 0)) 267 return nextoffset; 268 break; 269 270 case FDT_END: 271 if ((nextoffset >= 0) 272 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 273 return -FDT_ERR_NOTFOUND; 274 else 275 return nextoffset; 276 } 277 } while (tag != FDT_BEGIN_NODE); 278 279 return offset; 280 } 281 282 int fdt_first_subnode(const void *fdt, int offset) 283 { 284 int depth = 0; 285 286 offset = fdt_next_node(fdt, offset, &depth); 287 if (offset < 0 || depth != 1) 288 return -FDT_ERR_NOTFOUND; 289 290 return offset; 291 } 292 293 int fdt_next_subnode(const void *fdt, int offset) 294 { 295 int depth = 1; 296 297 /* 298 * With respect to the parent, the depth of the next subnode will be 299 * the same as the last. 300 */ 301 do { 302 offset = fdt_next_node(fdt, offset, &depth); 303 if (offset < 0 || depth < 1) 304 return -FDT_ERR_NOTFOUND; 305 } while (depth > 1); 306 307 return offset; 308 } 309 310 const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) 311 { 312 int len = strlen(s) + 1; 313 const char *last = strtab + tabsize - len; 314 const char *p; 315 316 for (p = strtab; p <= last; p++) 317 if (memcmp(p, s, len) == 0) 318 return p; 319 return NULL; 320 } 321 322 int fdt_move(const void *fdt, void *buf, int bufsize) 323 { 324 FDT_RO_PROBE(fdt); 325 326 if (fdt_totalsize(fdt) > bufsize) 327 return -FDT_ERR_NOSPACE; 328 329 memmove(buf, fdt, fdt_totalsize(fdt)); 330 return 0; 331 } 332