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