1 /* $NetBSD: fdt.c,v 1.1.1.2 2017/06/08 15:53:11 skrll Exp $ */ 2 3 /* 4 * libfdt - Flat Device Tree manipulation 5 * Copyright (C) 2006 David Gibson, IBM Corporation. 6 * 7 * libfdt is dual licensed: you can use it either under the terms of 8 * the GPL, or the BSD license, at your option. 9 * 10 * a) This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License as 12 * published by the Free Software Foundation; either version 2 of the 13 * License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public 21 * License along with this library; if not, write to the Free 22 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 23 * MA 02110-1301 USA 24 * 25 * Alternatively, 26 * 27 * b) Redistribution and use in source and binary forms, with or 28 * without modification, are permitted provided that the following 29 * conditions are met: 30 * 31 * 1. Redistributions of source code must retain the above 32 * copyright notice, this list of conditions and the following 33 * disclaimer. 34 * 2. Redistributions in binary form must reproduce the above 35 * copyright notice, this list of conditions and the following 36 * disclaimer in the documentation and/or other materials 37 * provided with the distribution. 38 * 39 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 40 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 41 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 42 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 43 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 44 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 49 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 50 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 51 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 */ 53 #include "libfdt_env.h" 54 55 #include <fdt.h> 56 #include <libfdt.h> 57 58 #include "libfdt_internal.h" 59 60 int fdt_check_header(const void *fdt) 61 { 62 if (fdt_magic(fdt) == FDT_MAGIC) { 63 /* Complete tree */ 64 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 65 return -FDT_ERR_BADVERSION; 66 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) 67 return -FDT_ERR_BADVERSION; 68 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 69 /* Unfinished sequential-write blob */ 70 if (fdt_size_dt_struct(fdt) == 0) 71 return -FDT_ERR_BADSTATE; 72 } else { 73 return -FDT_ERR_BADMAGIC; 74 } 75 76 return 0; 77 } 78 79 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 80 { 81 unsigned absoffset = offset + fdt_off_dt_struct(fdt); 82 83 if ((absoffset < offset) 84 || ((absoffset + len) < absoffset) 85 || (absoffset + len) > fdt_totalsize(fdt)) 86 return NULL; 87 88 if (fdt_version(fdt) >= 0x11) 89 if (((offset + len) < offset) 90 || ((offset + len) > fdt_size_dt_struct(fdt))) 91 return NULL; 92 93 return _fdt_offset_ptr(fdt, offset); 94 } 95 96 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 97 { 98 const fdt32_t *tagp, *lenp; 99 uint32_t tag; 100 int offset = startoffset; 101 const char *p; 102 103 *nextoffset = -FDT_ERR_TRUNCATED; 104 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 105 if (!tagp) 106 return FDT_END; /* premature end */ 107 tag = fdt32_to_cpu(*tagp); 108 offset += FDT_TAGSIZE; 109 110 *nextoffset = -FDT_ERR_BADSTRUCTURE; 111 switch (tag) { 112 case FDT_BEGIN_NODE: 113 /* skip name */ 114 do { 115 p = fdt_offset_ptr(fdt, offset++, 1); 116 } while (p && (*p != '\0')); 117 if (!p) 118 return FDT_END; /* premature end */ 119 break; 120 121 case FDT_PROP: 122 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 123 if (!lenp) 124 return FDT_END; /* premature end */ 125 /* skip-name offset, length and value */ 126 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 127 + fdt32_to_cpu(*lenp); 128 break; 129 130 case FDT_END: 131 case FDT_END_NODE: 132 case FDT_NOP: 133 break; 134 135 default: 136 return FDT_END; 137 } 138 139 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 140 return FDT_END; /* premature end */ 141 142 *nextoffset = FDT_TAGALIGN(offset); 143 return tag; 144 } 145 146 int _fdt_check_node_offset(const void *fdt, int offset) 147 { 148 if ((offset < 0) || (offset % FDT_TAGSIZE) 149 || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 150 return -FDT_ERR_BADOFFSET; 151 152 return offset; 153 } 154 155 int _fdt_check_prop_offset(const void *fdt, int offset) 156 { 157 if ((offset < 0) || (offset % FDT_TAGSIZE) 158 || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 159 return -FDT_ERR_BADOFFSET; 160 161 return offset; 162 } 163 164 int fdt_next_node(const void *fdt, int offset, int *depth) 165 { 166 int nextoffset = 0; 167 uint32_t tag; 168 169 if (offset >= 0) 170 if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) 171 return nextoffset; 172 173 do { 174 offset = nextoffset; 175 tag = fdt_next_tag(fdt, offset, &nextoffset); 176 177 switch (tag) { 178 case FDT_PROP: 179 case FDT_NOP: 180 break; 181 182 case FDT_BEGIN_NODE: 183 if (depth) 184 (*depth)++; 185 break; 186 187 case FDT_END_NODE: 188 if (depth && ((--(*depth)) < 0)) 189 return nextoffset; 190 break; 191 192 case FDT_END: 193 if ((nextoffset >= 0) 194 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 195 return -FDT_ERR_NOTFOUND; 196 else 197 return nextoffset; 198 } 199 } while (tag != FDT_BEGIN_NODE); 200 201 return offset; 202 } 203 204 int fdt_first_subnode(const void *fdt, int offset) 205 { 206 int depth = 0; 207 208 offset = fdt_next_node(fdt, offset, &depth); 209 if (offset < 0 || depth != 1) 210 return -FDT_ERR_NOTFOUND; 211 212 return offset; 213 } 214 215 int fdt_next_subnode(const void *fdt, int offset) 216 { 217 int depth = 1; 218 219 /* 220 * With respect to the parent, the depth of the next subnode will be 221 * the same as the last. 222 */ 223 do { 224 offset = fdt_next_node(fdt, offset, &depth); 225 if (offset < 0 || depth < 1) 226 return -FDT_ERR_NOTFOUND; 227 } while (depth > 1); 228 229 return offset; 230 } 231 232 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) 233 { 234 int len = strlen(s) + 1; 235 const char *last = strtab + tabsize - len; 236 const char *p; 237 238 for (p = strtab; p <= last; p++) 239 if (memcmp(p, s, len) == 0) 240 return p; 241 return NULL; 242 } 243 244 int fdt_move(const void *fdt, void *buf, int bufsize) 245 { 246 FDT_CHECK_HEADER(fdt); 247 248 if (fdt_totalsize(fdt) > bufsize) 249 return -FDT_ERR_NOSPACE; 250 251 memmove(buf, fdt, fdt_totalsize(fdt)); 252 return 0; 253 } 254