1 /* 2 * libfdt - Flat Device Tree manipulation 3 * Copyright (C) 2006 David Gibson, IBM Corporation. 4 * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause 5 */ 6 #include <libfdt_env.h> 7 8 #ifndef USE_HOSTCC 9 #include <fdt.h> 10 #include <libfdt.h> 11 #else 12 #include "fdt_host.h" 13 #endif 14 15 #include "libfdt_internal.h" 16 17 int fdt_check_header(const void *fdt) 18 { 19 if (fdt_magic(fdt) == FDT_MAGIC) { 20 /* Complete tree */ 21 if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) 22 return -FDT_ERR_BADVERSION; 23 if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) 24 return -FDT_ERR_BADVERSION; 25 } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 26 /* Unfinished sequential-write blob */ 27 if (fdt_size_dt_struct(fdt) == 0) 28 return -FDT_ERR_BADSTATE; 29 } else { 30 return -FDT_ERR_BADMAGIC; 31 } 32 33 return 0; 34 } 35 36 const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) 37 { 38 unsigned absoffset = offset + fdt_off_dt_struct(fdt); 39 40 if ((absoffset < offset) 41 || ((absoffset + len) < absoffset) 42 || (absoffset + len) > fdt_totalsize(fdt)) 43 return NULL; 44 45 if (fdt_version(fdt) >= 0x11) 46 if (((offset + len) < offset) 47 || ((offset + len) > fdt_size_dt_struct(fdt))) 48 return NULL; 49 50 return _fdt_offset_ptr(fdt, offset); 51 } 52 53 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 54 { 55 const fdt32_t *tagp, *lenp; 56 uint32_t tag; 57 int offset = startoffset; 58 const char *p; 59 60 *nextoffset = -FDT_ERR_TRUNCATED; 61 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 62 if (!tagp) 63 return FDT_END; /* premature end */ 64 tag = fdt32_to_cpu(*tagp); 65 offset += FDT_TAGSIZE; 66 67 *nextoffset = -FDT_ERR_BADSTRUCTURE; 68 switch (tag) { 69 case FDT_BEGIN_NODE: 70 /* skip name */ 71 do { 72 p = fdt_offset_ptr(fdt, offset++, 1); 73 } while (p && (*p != '\0')); 74 if (!p) 75 return FDT_END; /* premature end */ 76 break; 77 78 case FDT_PROP: 79 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 80 if (!lenp) 81 return FDT_END; /* premature end */ 82 /* skip-name offset, length and value */ 83 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 84 + fdt32_to_cpu(*lenp); 85 break; 86 87 case FDT_END: 88 case FDT_END_NODE: 89 case FDT_NOP: 90 break; 91 92 default: 93 return FDT_END; 94 } 95 96 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 97 return FDT_END; /* premature end */ 98 99 *nextoffset = FDT_TAGALIGN(offset); 100 return tag; 101 } 102 103 int _fdt_check_node_offset(const void *fdt, int offset) 104 { 105 if ((offset < 0) || (offset % FDT_TAGSIZE) 106 || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 107 return -FDT_ERR_BADOFFSET; 108 109 return offset; 110 } 111 112 int _fdt_check_prop_offset(const void *fdt, int offset) 113 { 114 if ((offset < 0) || (offset % FDT_TAGSIZE) 115 || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 116 return -FDT_ERR_BADOFFSET; 117 118 return offset; 119 } 120 121 int fdt_next_node(const void *fdt, int offset, int *depth) 122 { 123 int nextoffset = 0; 124 uint32_t tag; 125 126 if (offset >= 0) 127 if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) 128 return nextoffset; 129 130 do { 131 offset = nextoffset; 132 tag = fdt_next_tag(fdt, offset, &nextoffset); 133 134 switch (tag) { 135 case FDT_PROP: 136 case FDT_NOP: 137 break; 138 139 case FDT_BEGIN_NODE: 140 if (depth) 141 (*depth)++; 142 break; 143 144 case FDT_END_NODE: 145 if (depth && ((--(*depth)) < 0)) 146 return nextoffset; 147 break; 148 149 case FDT_END: 150 if ((nextoffset >= 0) 151 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 152 return -FDT_ERR_NOTFOUND; 153 else 154 return nextoffset; 155 } 156 } while (tag != FDT_BEGIN_NODE); 157 158 return offset; 159 } 160 161 int fdt_first_subnode(const void *fdt, int offset) 162 { 163 int depth = 0; 164 165 offset = fdt_next_node(fdt, offset, &depth); 166 if (offset < 0 || depth != 1) 167 return -FDT_ERR_NOTFOUND; 168 169 return offset; 170 } 171 172 int fdt_next_subnode(const void *fdt, int offset) 173 { 174 int depth = 1; 175 176 /* 177 * With respect to the parent, the depth of the next subnode will be 178 * the same as the last. 179 */ 180 do { 181 offset = fdt_next_node(fdt, offset, &depth); 182 if (offset < 0 || depth < 1) 183 return -FDT_ERR_NOTFOUND; 184 } while (depth > 1); 185 186 return offset; 187 } 188 189 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) 190 { 191 int len = strlen(s) + 1; 192 const char *last = strtab + tabsize - len; 193 const char *p; 194 195 for (p = strtab; p <= last; p++) 196 if (memcmp(p, s, len) == 0) 197 return p; 198 return NULL; 199 } 200 201 int fdt_move(const void *fdt, void *buf, int bufsize) 202 { 203 FDT_CHECK_HEADER(fdt); 204 205 if (fdt_totalsize(fdt) > bufsize) 206 return -FDT_ERR_NOSPACE; 207 208 memmove(buf, fdt, fdt_totalsize(fdt)); 209 return 0; 210 } 211