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 const char *p; 39 40 if (fdt_version(fdt) >= 0x11) 41 if (((offset + len) < offset) 42 || ((offset + len) > fdt_size_dt_struct(fdt))) 43 return NULL; 44 45 p = _fdt_offset_ptr(fdt, offset); 46 47 if (p + len < p) 48 return NULL; 49 return p; 50 } 51 52 uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) 53 { 54 const fdt32_t *tagp, *lenp; 55 uint32_t tag; 56 int offset = startoffset; 57 const char *p; 58 59 *nextoffset = -FDT_ERR_TRUNCATED; 60 tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); 61 if (!tagp) 62 return FDT_END; /* premature end */ 63 tag = fdt32_to_cpu(*tagp); 64 offset += FDT_TAGSIZE; 65 66 *nextoffset = -FDT_ERR_BADSTRUCTURE; 67 switch (tag) { 68 case FDT_BEGIN_NODE: 69 /* skip name */ 70 do { 71 p = fdt_offset_ptr(fdt, offset++, 1); 72 } while (p && (*p != '\0')); 73 if (!p) 74 return FDT_END; /* premature end */ 75 break; 76 77 case FDT_PROP: 78 lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); 79 if (!lenp) 80 return FDT_END; /* premature end */ 81 /* skip-name offset, length and value */ 82 offset += sizeof(struct fdt_property) - FDT_TAGSIZE 83 + fdt32_to_cpu(*lenp); 84 break; 85 86 case FDT_END: 87 case FDT_END_NODE: 88 case FDT_NOP: 89 break; 90 91 default: 92 return FDT_END; 93 } 94 95 if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) 96 return FDT_END; /* premature end */ 97 98 *nextoffset = FDT_TAGALIGN(offset); 99 return tag; 100 } 101 102 int _fdt_check_node_offset(const void *fdt, int offset) 103 { 104 if ((offset < 0) || (offset % FDT_TAGSIZE) 105 || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) 106 return -FDT_ERR_BADOFFSET; 107 108 return offset; 109 } 110 111 int _fdt_check_prop_offset(const void *fdt, int offset) 112 { 113 if ((offset < 0) || (offset % FDT_TAGSIZE) 114 || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) 115 return -FDT_ERR_BADOFFSET; 116 117 return offset; 118 } 119 120 int fdt_next_node(const void *fdt, int offset, int *depth) 121 { 122 int nextoffset = 0; 123 uint32_t tag; 124 125 if (offset >= 0) 126 if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) 127 return nextoffset; 128 129 do { 130 offset = nextoffset; 131 tag = fdt_next_tag(fdt, offset, &nextoffset); 132 133 switch (tag) { 134 case FDT_PROP: 135 case FDT_NOP: 136 break; 137 138 case FDT_BEGIN_NODE: 139 if (depth) 140 (*depth)++; 141 break; 142 143 case FDT_END_NODE: 144 if (depth && ((--(*depth)) < 0)) 145 return nextoffset; 146 break; 147 148 case FDT_END: 149 if ((nextoffset >= 0) 150 || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) 151 return -FDT_ERR_NOTFOUND; 152 else 153 return nextoffset; 154 } 155 } while (tag != FDT_BEGIN_NODE); 156 157 return offset; 158 } 159 160 int fdt_first_subnode(const void *fdt, int offset) 161 { 162 int depth = 0; 163 164 offset = fdt_next_node(fdt, offset, &depth); 165 if (offset < 0 || depth != 1) 166 return -FDT_ERR_NOTFOUND; 167 168 return offset; 169 } 170 171 int fdt_next_subnode(const void *fdt, int offset) 172 { 173 int depth = 1; 174 175 /* 176 * With respect to the parent, the depth of the next subnode will be 177 * the same as the last. 178 */ 179 do { 180 offset = fdt_next_node(fdt, offset, &depth); 181 if (offset < 0 || depth < 1) 182 return -FDT_ERR_NOTFOUND; 183 } while (depth > 1); 184 185 return offset; 186 } 187 188 const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) 189 { 190 int len = strlen(s) + 1; 191 const char *last = strtab + tabsize - len; 192 const char *p; 193 194 for (p = strtab; p <= last; p++) 195 if (memcmp(p, s, len) == 0) 196 return p; 197 return NULL; 198 } 199 200 int fdt_move(const void *fdt, void *buf, int bufsize) 201 { 202 FDT_CHECK_HEADER(fdt); 203 204 if (fdt_totalsize(fdt) > bufsize) 205 return -FDT_ERR_NOSPACE; 206 207 memmove(buf, fdt, fdt_totalsize(fdt)); 208 return 0; 209 } 210