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