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