1 1.1.1.2 skrll /* $NetBSD: fdt_ro.c,v 1.1.1.3 2019/12/22 12:30:36 skrll Exp $ */ 2 1.1.1.2 skrll 3 1.1.1.3 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.1.1.3 skrll static int fdt_nodename_eq_(const void *fdt, int offset, 16 1.1 macallan const char *s, int len) 17 1.1 macallan { 18 1.1.1.3 skrll int olen; 19 1.1.1.3 skrll const char *p = fdt_get_name(fdt, offset, &olen); 20 1.1 macallan 21 1.1.1.3 skrll if (!p || olen < len) 22 1.1 macallan /* short match */ 23 1.1 macallan return 0; 24 1.1 macallan 25 1.1 macallan if (memcmp(p, s, len) != 0) 26 1.1 macallan return 0; 27 1.1 macallan 28 1.1 macallan if (p[len] == '\0') 29 1.1 macallan return 1; 30 1.1 macallan else if (!memchr(s, '@', len) && (p[len] == '@')) 31 1.1 macallan return 1; 32 1.1 macallan else 33 1.1 macallan return 0; 34 1.1 macallan } 35 1.1 macallan 36 1.1.1.3 skrll const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) 37 1.1.1.3 skrll { 38 1.1.1.3 skrll int32_t totalsize = fdt_ro_probe_(fdt); 39 1.1.1.3 skrll uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); 40 1.1.1.3 skrll size_t len; 41 1.1.1.3 skrll int err; 42 1.1.1.3 skrll const char *s, *n; 43 1.1.1.3 skrll 44 1.1.1.3 skrll err = totalsize; 45 1.1.1.3 skrll if (totalsize < 0) 46 1.1.1.3 skrll goto fail; 47 1.1.1.3 skrll 48 1.1.1.3 skrll err = -FDT_ERR_BADOFFSET; 49 1.1.1.3 skrll if (absoffset >= totalsize) 50 1.1.1.3 skrll goto fail; 51 1.1.1.3 skrll len = totalsize - absoffset; 52 1.1.1.3 skrll 53 1.1.1.3 skrll if (fdt_magic(fdt) == FDT_MAGIC) { 54 1.1.1.3 skrll if (stroffset < 0) 55 1.1.1.3 skrll goto fail; 56 1.1.1.3 skrll if (fdt_version(fdt) >= 17) { 57 1.1.1.3 skrll if (stroffset >= fdt_size_dt_strings(fdt)) 58 1.1.1.3 skrll goto fail; 59 1.1.1.3 skrll if ((fdt_size_dt_strings(fdt) - stroffset) < len) 60 1.1.1.3 skrll len = fdt_size_dt_strings(fdt) - stroffset; 61 1.1.1.3 skrll } 62 1.1.1.3 skrll } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { 63 1.1.1.3 skrll if ((stroffset >= 0) 64 1.1.1.3 skrll || (stroffset < -fdt_size_dt_strings(fdt))) 65 1.1.1.3 skrll goto fail; 66 1.1.1.3 skrll if ((-stroffset) < len) 67 1.1.1.3 skrll len = -stroffset; 68 1.1.1.3 skrll } else { 69 1.1.1.3 skrll err = -FDT_ERR_INTERNAL; 70 1.1.1.3 skrll goto fail; 71 1.1.1.3 skrll } 72 1.1.1.3 skrll 73 1.1.1.3 skrll s = (const char *)fdt + absoffset; 74 1.1.1.3 skrll n = memchr(s, '\0', len); 75 1.1.1.3 skrll if (!n) { 76 1.1.1.3 skrll /* missing terminating NULL */ 77 1.1.1.3 skrll err = -FDT_ERR_TRUNCATED; 78 1.1.1.3 skrll goto fail; 79 1.1.1.3 skrll } 80 1.1.1.3 skrll 81 1.1.1.3 skrll if (lenp) 82 1.1.1.3 skrll *lenp = n - s; 83 1.1.1.3 skrll return s; 84 1.1.1.3 skrll 85 1.1.1.3 skrll fail: 86 1.1.1.3 skrll if (lenp) 87 1.1.1.3 skrll *lenp = err; 88 1.1.1.3 skrll return NULL; 89 1.1.1.3 skrll } 90 1.1.1.3 skrll 91 1.1 macallan const char *fdt_string(const void *fdt, int stroffset) 92 1.1 macallan { 93 1.1.1.3 skrll return fdt_get_string(fdt, stroffset, NULL); 94 1.1 macallan } 95 1.1 macallan 96 1.1.1.3 skrll static int fdt_string_eq_(const void *fdt, int stroffset, 97 1.1 macallan const char *s, int len) 98 1.1 macallan { 99 1.1.1.3 skrll int slen; 100 1.1.1.3 skrll const char *p = fdt_get_string(fdt, stroffset, &slen); 101 1.1 macallan 102 1.1.1.3 skrll return p && (slen == len) && (memcmp(p, s, len) == 0); 103 1.1 macallan } 104 1.1 macallan 105 1.1.1.3 skrll int fdt_find_max_phandle(const void *fdt, uint32_t *phandle) 106 1.1.1.2 skrll { 107 1.1.1.3 skrll uint32_t max = 0; 108 1.1.1.3 skrll int offset = -1; 109 1.1.1.2 skrll 110 1.1.1.3 skrll while (true) { 111 1.1.1.3 skrll uint32_t value; 112 1.1.1.2 skrll 113 1.1.1.3 skrll offset = fdt_next_node(fdt, offset, NULL); 114 1.1.1.3 skrll if (offset < 0) { 115 1.1.1.3 skrll if (offset == -FDT_ERR_NOTFOUND) 116 1.1.1.3 skrll break; 117 1.1.1.2 skrll 118 1.1.1.3 skrll return offset; 119 1.1.1.3 skrll } 120 1.1.1.2 skrll 121 1.1.1.3 skrll value = fdt_get_phandle(fdt, offset); 122 1.1.1.2 skrll 123 1.1.1.3 skrll if (value > max) 124 1.1.1.3 skrll max = value; 125 1.1.1.2 skrll } 126 1.1.1.2 skrll 127 1.1.1.3 skrll if (phandle) 128 1.1.1.3 skrll *phandle = max; 129 1.1.1.3 skrll 130 1.1.1.3 skrll return 0; 131 1.1.1.3 skrll } 132 1.1.1.3 skrll 133 1.1.1.3 skrll int fdt_generate_phandle(const void *fdt, uint32_t *phandle) 134 1.1.1.3 skrll { 135 1.1.1.3 skrll uint32_t max; 136 1.1.1.3 skrll int err; 137 1.1.1.3 skrll 138 1.1.1.3 skrll err = fdt_find_max_phandle(fdt, &max); 139 1.1.1.3 skrll if (err < 0) 140 1.1.1.3 skrll return err; 141 1.1.1.3 skrll 142 1.1.1.3 skrll if (max == FDT_MAX_PHANDLE) 143 1.1.1.3 skrll return -FDT_ERR_NOPHANDLES; 144 1.1.1.3 skrll 145 1.1.1.3 skrll if (phandle) 146 1.1.1.3 skrll *phandle = max + 1; 147 1.1.1.3 skrll 148 1.1.1.2 skrll return 0; 149 1.1.1.2 skrll } 150 1.1.1.2 skrll 151 1.1.1.3 skrll static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) 152 1.1.1.3 skrll { 153 1.1.1.3 skrll int offset = n * sizeof(struct fdt_reserve_entry); 154 1.1.1.3 skrll int absoffset = fdt_off_mem_rsvmap(fdt) + offset; 155 1.1.1.3 skrll 156 1.1.1.3 skrll if (absoffset < fdt_off_mem_rsvmap(fdt)) 157 1.1.1.3 skrll return NULL; 158 1.1.1.3 skrll if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) 159 1.1.1.3 skrll return NULL; 160 1.1.1.3 skrll return fdt_mem_rsv_(fdt, n); 161 1.1.1.3 skrll } 162 1.1.1.3 skrll 163 1.1 macallan int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) 164 1.1 macallan { 165 1.1.1.3 skrll const struct fdt_reserve_entry *re; 166 1.1.1.3 skrll 167 1.1.1.3 skrll FDT_RO_PROBE(fdt); 168 1.1.1.3 skrll re = fdt_mem_rsv(fdt, n); 169 1.1.1.3 skrll if (!re) 170 1.1.1.3 skrll return -FDT_ERR_BADOFFSET; 171 1.1.1.3 skrll 172 1.1.1.3 skrll *address = fdt64_ld(&re->address); 173 1.1.1.3 skrll *size = fdt64_ld(&re->size); 174 1.1 macallan return 0; 175 1.1 macallan } 176 1.1 macallan 177 1.1 macallan int fdt_num_mem_rsv(const void *fdt) 178 1.1 macallan { 179 1.1.1.3 skrll int i; 180 1.1.1.3 skrll const struct fdt_reserve_entry *re; 181 1.1 macallan 182 1.1.1.3 skrll for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { 183 1.1.1.3 skrll if (fdt64_ld(&re->size) == 0) 184 1.1.1.3 skrll return i; 185 1.1.1.3 skrll } 186 1.1.1.3 skrll return -FDT_ERR_TRUNCATED; 187 1.1 macallan } 188 1.1 macallan 189 1.1.1.3 skrll static int nextprop_(const void *fdt, int offset) 190 1.1 macallan { 191 1.1 macallan uint32_t tag; 192 1.1 macallan int nextoffset; 193 1.1 macallan 194 1.1 macallan do { 195 1.1 macallan tag = fdt_next_tag(fdt, offset, &nextoffset); 196 1.1 macallan 197 1.1 macallan switch (tag) { 198 1.1 macallan case FDT_END: 199 1.1 macallan if (nextoffset >= 0) 200 1.1 macallan return -FDT_ERR_BADSTRUCTURE; 201 1.1 macallan else 202 1.1 macallan return nextoffset; 203 1.1 macallan 204 1.1 macallan case FDT_PROP: 205 1.1 macallan return offset; 206 1.1 macallan } 207 1.1 macallan offset = nextoffset; 208 1.1 macallan } while (tag == FDT_NOP); 209 1.1 macallan 210 1.1 macallan return -FDT_ERR_NOTFOUND; 211 1.1 macallan } 212 1.1 macallan 213 1.1 macallan int fdt_subnode_offset_namelen(const void *fdt, int offset, 214 1.1 macallan const char *name, int namelen) 215 1.1 macallan { 216 1.1 macallan int depth; 217 1.1 macallan 218 1.1.1.3 skrll FDT_RO_PROBE(fdt); 219 1.1 macallan 220 1.1 macallan for (depth = 0; 221 1.1 macallan (offset >= 0) && (depth >= 0); 222 1.1 macallan offset = fdt_next_node(fdt, offset, &depth)) 223 1.1 macallan if ((depth == 1) 224 1.1.1.3 skrll && fdt_nodename_eq_(fdt, offset, name, namelen)) 225 1.1 macallan return offset; 226 1.1 macallan 227 1.1 macallan if (depth < 0) 228 1.1 macallan return -FDT_ERR_NOTFOUND; 229 1.1 macallan return offset; /* error */ 230 1.1 macallan } 231 1.1 macallan 232 1.1 macallan int fdt_subnode_offset(const void *fdt, int parentoffset, 233 1.1 macallan const char *name) 234 1.1 macallan { 235 1.1 macallan return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); 236 1.1 macallan } 237 1.1 macallan 238 1.1 macallan int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) 239 1.1 macallan { 240 1.1 macallan const char *end = path + namelen; 241 1.1 macallan const char *p = path; 242 1.1 macallan int offset = 0; 243 1.1 macallan 244 1.1.1.3 skrll FDT_RO_PROBE(fdt); 245 1.1 macallan 246 1.1 macallan /* see if we have an alias */ 247 1.1 macallan if (*path != '/') { 248 1.1 macallan const char *q = memchr(path, '/', end - p); 249 1.1 macallan 250 1.1 macallan if (!q) 251 1.1 macallan q = end; 252 1.1 macallan 253 1.1 macallan p = fdt_get_alias_namelen(fdt, p, q - p); 254 1.1 macallan if (!p) 255 1.1 macallan return -FDT_ERR_BADPATH; 256 1.1 macallan offset = fdt_path_offset(fdt, p); 257 1.1 macallan 258 1.1 macallan p = q; 259 1.1 macallan } 260 1.1 macallan 261 1.1 macallan while (p < end) { 262 1.1 macallan const char *q; 263 1.1 macallan 264 1.1 macallan while (*p == '/') { 265 1.1 macallan p++; 266 1.1 macallan if (p == end) 267 1.1 macallan return offset; 268 1.1 macallan } 269 1.1 macallan q = memchr(p, '/', end - p); 270 1.1 macallan if (! q) 271 1.1 macallan q = end; 272 1.1 macallan 273 1.1 macallan offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); 274 1.1 macallan if (offset < 0) 275 1.1 macallan return offset; 276 1.1 macallan 277 1.1 macallan p = q; 278 1.1 macallan } 279 1.1 macallan 280 1.1 macallan return offset; 281 1.1 macallan } 282 1.1 macallan 283 1.1 macallan int fdt_path_offset(const void *fdt, const char *path) 284 1.1 macallan { 285 1.1 macallan return fdt_path_offset_namelen(fdt, path, strlen(path)); 286 1.1 macallan } 287 1.1 macallan 288 1.1 macallan const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) 289 1.1 macallan { 290 1.1.1.3 skrll const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); 291 1.1.1.3 skrll const char *nameptr; 292 1.1 macallan int err; 293 1.1 macallan 294 1.1.1.3 skrll if (((err = fdt_ro_probe_(fdt)) < 0) 295 1.1.1.3 skrll || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) 296 1.1 macallan goto fail; 297 1.1 macallan 298 1.1.1.3 skrll nameptr = nh->name; 299 1.1.1.3 skrll 300 1.1.1.3 skrll if (fdt_version(fdt) < 0x10) { 301 1.1.1.3 skrll /* 302 1.1.1.3 skrll * For old FDT versions, match the naming conventions of V16: 303 1.1.1.3 skrll * give only the leaf name (after all /). The actual tree 304 1.1.1.3 skrll * contents are loosely checked. 305 1.1.1.3 skrll */ 306 1.1.1.3 skrll const char *leaf; 307 1.1.1.3 skrll leaf = strrchr(nameptr, '/'); 308 1.1.1.3 skrll if (leaf == NULL) { 309 1.1.1.3 skrll err = -FDT_ERR_BADSTRUCTURE; 310 1.1.1.3 skrll goto fail; 311 1.1.1.3 skrll } 312 1.1.1.3 skrll nameptr = leaf+1; 313 1.1.1.3 skrll } 314 1.1.1.3 skrll 315 1.1 macallan if (len) 316 1.1.1.3 skrll *len = strlen(nameptr); 317 1.1 macallan 318 1.1.1.3 skrll return nameptr; 319 1.1 macallan 320 1.1 macallan fail: 321 1.1 macallan if (len) 322 1.1 macallan *len = err; 323 1.1 macallan return NULL; 324 1.1 macallan } 325 1.1 macallan 326 1.1 macallan int fdt_first_property_offset(const void *fdt, int nodeoffset) 327 1.1 macallan { 328 1.1 macallan int offset; 329 1.1 macallan 330 1.1.1.3 skrll if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) 331 1.1 macallan return offset; 332 1.1 macallan 333 1.1.1.3 skrll return nextprop_(fdt, offset); 334 1.1 macallan } 335 1.1 macallan 336 1.1 macallan int fdt_next_property_offset(const void *fdt, int offset) 337 1.1 macallan { 338 1.1.1.3 skrll if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) 339 1.1 macallan return offset; 340 1.1 macallan 341 1.1.1.3 skrll return nextprop_(fdt, offset); 342 1.1 macallan } 343 1.1 macallan 344 1.1.1.3 skrll static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, 345 1.1.1.3 skrll int offset, 346 1.1.1.3 skrll int *lenp) 347 1.1 macallan { 348 1.1 macallan int err; 349 1.1 macallan const struct fdt_property *prop; 350 1.1 macallan 351 1.1.1.3 skrll if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { 352 1.1 macallan if (lenp) 353 1.1 macallan *lenp = err; 354 1.1 macallan return NULL; 355 1.1 macallan } 356 1.1 macallan 357 1.1.1.3 skrll prop = fdt_offset_ptr_(fdt, offset); 358 1.1 macallan 359 1.1 macallan if (lenp) 360 1.1.1.3 skrll *lenp = fdt32_ld(&prop->len); 361 1.1 macallan 362 1.1 macallan return prop; 363 1.1 macallan } 364 1.1 macallan 365 1.1.1.3 skrll const struct fdt_property *fdt_get_property_by_offset(const void *fdt, 366 1.1.1.3 skrll int offset, 367 1.1.1.3 skrll int *lenp) 368 1.1.1.3 skrll { 369 1.1.1.3 skrll /* Prior to version 16, properties may need realignment 370 1.1.1.3 skrll * and this API does not work. fdt_getprop_*() will, however. */ 371 1.1.1.3 skrll 372 1.1.1.3 skrll if (fdt_version(fdt) < 0x10) { 373 1.1.1.3 skrll if (lenp) 374 1.1.1.3 skrll *lenp = -FDT_ERR_BADVERSION; 375 1.1.1.3 skrll return NULL; 376 1.1.1.3 skrll } 377 1.1.1.3 skrll 378 1.1.1.3 skrll return fdt_get_property_by_offset_(fdt, offset, lenp); 379 1.1.1.3 skrll } 380 1.1.1.3 skrll 381 1.1.1.3 skrll static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, 382 1.1.1.3 skrll int offset, 383 1.1.1.3 skrll const char *name, 384 1.1.1.3 skrll int namelen, 385 1.1.1.3 skrll int *lenp, 386 1.1.1.3 skrll int *poffset) 387 1.1 macallan { 388 1.1 macallan for (offset = fdt_first_property_offset(fdt, offset); 389 1.1 macallan (offset >= 0); 390 1.1 macallan (offset = fdt_next_property_offset(fdt, offset))) { 391 1.1 macallan const struct fdt_property *prop; 392 1.1 macallan 393 1.1.1.3 skrll if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { 394 1.1 macallan offset = -FDT_ERR_INTERNAL; 395 1.1 macallan break; 396 1.1 macallan } 397 1.1.1.3 skrll if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), 398 1.1.1.3 skrll name, namelen)) { 399 1.1.1.3 skrll if (poffset) 400 1.1.1.3 skrll *poffset = offset; 401 1.1 macallan return prop; 402 1.1.1.3 skrll } 403 1.1 macallan } 404 1.1 macallan 405 1.1 macallan if (lenp) 406 1.1 macallan *lenp = offset; 407 1.1 macallan return NULL; 408 1.1 macallan } 409 1.1 macallan 410 1.1.1.3 skrll 411 1.1.1.3 skrll const struct fdt_property *fdt_get_property_namelen(const void *fdt, 412 1.1.1.3 skrll int offset, 413 1.1.1.3 skrll const char *name, 414 1.1.1.3 skrll int namelen, int *lenp) 415 1.1.1.3 skrll { 416 1.1.1.3 skrll /* Prior to version 16, properties may need realignment 417 1.1.1.3 skrll * and this API does not work. fdt_getprop_*() will, however. */ 418 1.1.1.3 skrll if (fdt_version(fdt) < 0x10) { 419 1.1.1.3 skrll if (lenp) 420 1.1.1.3 skrll *lenp = -FDT_ERR_BADVERSION; 421 1.1.1.3 skrll return NULL; 422 1.1.1.3 skrll } 423 1.1.1.3 skrll 424 1.1.1.3 skrll return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, 425 1.1.1.3 skrll NULL); 426 1.1.1.3 skrll } 427 1.1.1.3 skrll 428 1.1.1.3 skrll 429 1.1 macallan const struct fdt_property *fdt_get_property(const void *fdt, 430 1.1 macallan int nodeoffset, 431 1.1 macallan const char *name, int *lenp) 432 1.1 macallan { 433 1.1 macallan return fdt_get_property_namelen(fdt, nodeoffset, name, 434 1.1 macallan strlen(name), lenp); 435 1.1 macallan } 436 1.1 macallan 437 1.1 macallan const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, 438 1.1 macallan const char *name, int namelen, int *lenp) 439 1.1 macallan { 440 1.1.1.3 skrll int poffset; 441 1.1 macallan const struct fdt_property *prop; 442 1.1 macallan 443 1.1.1.3 skrll prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, 444 1.1.1.3 skrll &poffset); 445 1.1.1.3 skrll if (!prop) 446 1.1 macallan return NULL; 447 1.1 macallan 448 1.1.1.3 skrll /* Handle realignment */ 449 1.1.1.3 skrll if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && 450 1.1.1.3 skrll fdt32_ld(&prop->len) >= 8) 451 1.1.1.3 skrll return prop->data + 4; 452 1.1 macallan return prop->data; 453 1.1 macallan } 454 1.1 macallan 455 1.1 macallan const void *fdt_getprop_by_offset(const void *fdt, int offset, 456 1.1 macallan const char **namep, int *lenp) 457 1.1 macallan { 458 1.1 macallan const struct fdt_property *prop; 459 1.1 macallan 460 1.1.1.3 skrll prop = fdt_get_property_by_offset_(fdt, offset, lenp); 461 1.1 macallan if (!prop) 462 1.1 macallan return NULL; 463 1.1.1.3 skrll if (namep) { 464 1.1.1.3 skrll const char *name; 465 1.1.1.3 skrll int namelen; 466 1.1.1.3 skrll name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), 467 1.1.1.3 skrll &namelen); 468 1.1.1.3 skrll if (!name) { 469 1.1.1.3 skrll if (lenp) 470 1.1.1.3 skrll *lenp = namelen; 471 1.1.1.3 skrll return NULL; 472 1.1.1.3 skrll } 473 1.1.1.3 skrll *namep = name; 474 1.1.1.3 skrll } 475 1.1.1.3 skrll 476 1.1.1.3 skrll /* Handle realignment */ 477 1.1.1.3 skrll if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && 478 1.1.1.3 skrll fdt32_ld(&prop->len) >= 8) 479 1.1.1.3 skrll return prop->data + 4; 480 1.1 macallan return prop->data; 481 1.1 macallan } 482 1.1 macallan 483 1.1 macallan const void *fdt_getprop(const void *fdt, int nodeoffset, 484 1.1 macallan const char *name, int *lenp) 485 1.1 macallan { 486 1.1 macallan return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); 487 1.1 macallan } 488 1.1 macallan 489 1.1 macallan uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) 490 1.1 macallan { 491 1.1 macallan const fdt32_t *php; 492 1.1 macallan int len; 493 1.1 macallan 494 1.1 macallan /* FIXME: This is a bit sub-optimal, since we potentially scan 495 1.1 macallan * over all the properties twice. */ 496 1.1 macallan php = fdt_getprop(fdt, nodeoffset, "phandle", &len); 497 1.1 macallan if (!php || (len != sizeof(*php))) { 498 1.1 macallan php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); 499 1.1 macallan if (!php || (len != sizeof(*php))) 500 1.1 macallan return 0; 501 1.1 macallan } 502 1.1 macallan 503 1.1.1.3 skrll return fdt32_ld(php); 504 1.1 macallan } 505 1.1 macallan 506 1.1 macallan const char *fdt_get_alias_namelen(const void *fdt, 507 1.1 macallan const char *name, int namelen) 508 1.1 macallan { 509 1.1 macallan int aliasoffset; 510 1.1 macallan 511 1.1 macallan aliasoffset = fdt_path_offset(fdt, "/aliases"); 512 1.1 macallan if (aliasoffset < 0) 513 1.1 macallan return NULL; 514 1.1 macallan 515 1.1 macallan return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); 516 1.1 macallan } 517 1.1 macallan 518 1.1 macallan const char *fdt_get_alias(const void *fdt, const char *name) 519 1.1 macallan { 520 1.1 macallan return fdt_get_alias_namelen(fdt, name, strlen(name)); 521 1.1 macallan } 522 1.1 macallan 523 1.1 macallan int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) 524 1.1 macallan { 525 1.1 macallan int pdepth = 0, p = 0; 526 1.1 macallan int offset, depth, namelen; 527 1.1 macallan const char *name; 528 1.1 macallan 529 1.1.1.3 skrll FDT_RO_PROBE(fdt); 530 1.1 macallan 531 1.1 macallan if (buflen < 2) 532 1.1 macallan return -FDT_ERR_NOSPACE; 533 1.1 macallan 534 1.1 macallan for (offset = 0, depth = 0; 535 1.1 macallan (offset >= 0) && (offset <= nodeoffset); 536 1.1 macallan offset = fdt_next_node(fdt, offset, &depth)) { 537 1.1 macallan while (pdepth > depth) { 538 1.1 macallan do { 539 1.1 macallan p--; 540 1.1 macallan } while (buf[p-1] != '/'); 541 1.1 macallan pdepth--; 542 1.1 macallan } 543 1.1 macallan 544 1.1 macallan if (pdepth >= depth) { 545 1.1 macallan name = fdt_get_name(fdt, offset, &namelen); 546 1.1 macallan if (!name) 547 1.1 macallan return namelen; 548 1.1 macallan if ((p + namelen + 1) <= buflen) { 549 1.1 macallan memcpy(buf + p, name, namelen); 550 1.1 macallan p += namelen; 551 1.1 macallan buf[p++] = '/'; 552 1.1 macallan pdepth++; 553 1.1 macallan } 554 1.1 macallan } 555 1.1 macallan 556 1.1 macallan if (offset == nodeoffset) { 557 1.1 macallan if (pdepth < (depth + 1)) 558 1.1 macallan return -FDT_ERR_NOSPACE; 559 1.1 macallan 560 1.1 macallan if (p > 1) /* special case so that root path is "/", not "" */ 561 1.1 macallan p--; 562 1.1 macallan buf[p] = '\0'; 563 1.1 macallan return 0; 564 1.1 macallan } 565 1.1 macallan } 566 1.1 macallan 567 1.1 macallan if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 568 1.1 macallan return -FDT_ERR_BADOFFSET; 569 1.1 macallan else if (offset == -FDT_ERR_BADOFFSET) 570 1.1 macallan return -FDT_ERR_BADSTRUCTURE; 571 1.1 macallan 572 1.1 macallan return offset; /* error from fdt_next_node() */ 573 1.1 macallan } 574 1.1 macallan 575 1.1 macallan int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, 576 1.1 macallan int supernodedepth, int *nodedepth) 577 1.1 macallan { 578 1.1 macallan int offset, depth; 579 1.1 macallan int supernodeoffset = -FDT_ERR_INTERNAL; 580 1.1 macallan 581 1.1.1.3 skrll FDT_RO_PROBE(fdt); 582 1.1 macallan 583 1.1 macallan if (supernodedepth < 0) 584 1.1 macallan return -FDT_ERR_NOTFOUND; 585 1.1 macallan 586 1.1 macallan for (offset = 0, depth = 0; 587 1.1 macallan (offset >= 0) && (offset <= nodeoffset); 588 1.1 macallan offset = fdt_next_node(fdt, offset, &depth)) { 589 1.1 macallan if (depth == supernodedepth) 590 1.1 macallan supernodeoffset = offset; 591 1.1 macallan 592 1.1 macallan if (offset == nodeoffset) { 593 1.1 macallan if (nodedepth) 594 1.1 macallan *nodedepth = depth; 595 1.1 macallan 596 1.1 macallan if (supernodedepth > depth) 597 1.1 macallan return -FDT_ERR_NOTFOUND; 598 1.1 macallan else 599 1.1 macallan return supernodeoffset; 600 1.1 macallan } 601 1.1 macallan } 602 1.1 macallan 603 1.1 macallan if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) 604 1.1 macallan return -FDT_ERR_BADOFFSET; 605 1.1 macallan else if (offset == -FDT_ERR_BADOFFSET) 606 1.1 macallan return -FDT_ERR_BADSTRUCTURE; 607 1.1 macallan 608 1.1 macallan return offset; /* error from fdt_next_node() */ 609 1.1 macallan } 610 1.1 macallan 611 1.1 macallan int fdt_node_depth(const void *fdt, int nodeoffset) 612 1.1 macallan { 613 1.1 macallan int nodedepth; 614 1.1 macallan int err; 615 1.1 macallan 616 1.1 macallan err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); 617 1.1 macallan if (err) 618 1.1 macallan return (err < 0) ? err : -FDT_ERR_INTERNAL; 619 1.1 macallan return nodedepth; 620 1.1 macallan } 621 1.1 macallan 622 1.1 macallan int fdt_parent_offset(const void *fdt, int nodeoffset) 623 1.1 macallan { 624 1.1 macallan int nodedepth = fdt_node_depth(fdt, nodeoffset); 625 1.1 macallan 626 1.1 macallan if (nodedepth < 0) 627 1.1 macallan return nodedepth; 628 1.1 macallan return fdt_supernode_atdepth_offset(fdt, nodeoffset, 629 1.1 macallan nodedepth - 1, NULL); 630 1.1 macallan } 631 1.1 macallan 632 1.1 macallan int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, 633 1.1 macallan const char *propname, 634 1.1 macallan const void *propval, int proplen) 635 1.1 macallan { 636 1.1 macallan int offset; 637 1.1 macallan const void *val; 638 1.1 macallan int len; 639 1.1 macallan 640 1.1.1.3 skrll FDT_RO_PROBE(fdt); 641 1.1 macallan 642 1.1 macallan /* FIXME: The algorithm here is pretty horrible: we scan each 643 1.1 macallan * property of a node in fdt_getprop(), then if that didn't 644 1.1 macallan * find what we want, we scan over them again making our way 645 1.1 macallan * to the next node. Still it's the easiest to implement 646 1.1 macallan * approach; performance can come later. */ 647 1.1 macallan for (offset = fdt_next_node(fdt, startoffset, NULL); 648 1.1 macallan offset >= 0; 649 1.1 macallan offset = fdt_next_node(fdt, offset, NULL)) { 650 1.1 macallan val = fdt_getprop(fdt, offset, propname, &len); 651 1.1 macallan if (val && (len == proplen) 652 1.1 macallan && (memcmp(val, propval, len) == 0)) 653 1.1 macallan return offset; 654 1.1 macallan } 655 1.1 macallan 656 1.1 macallan return offset; /* error from fdt_next_node() */ 657 1.1 macallan } 658 1.1 macallan 659 1.1 macallan int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) 660 1.1 macallan { 661 1.1 macallan int offset; 662 1.1 macallan 663 1.1 macallan if ((phandle == 0) || (phandle == -1)) 664 1.1 macallan return -FDT_ERR_BADPHANDLE; 665 1.1 macallan 666 1.1.1.3 skrll FDT_RO_PROBE(fdt); 667 1.1 macallan 668 1.1 macallan /* FIXME: The algorithm here is pretty horrible: we 669 1.1 macallan * potentially scan each property of a node in 670 1.1 macallan * fdt_get_phandle(), then if that didn't find what 671 1.1 macallan * we want, we scan over them again making our way to the next 672 1.1 macallan * node. Still it's the easiest to implement approach; 673 1.1 macallan * performance can come later. */ 674 1.1 macallan for (offset = fdt_next_node(fdt, -1, NULL); 675 1.1 macallan offset >= 0; 676 1.1 macallan offset = fdt_next_node(fdt, offset, NULL)) { 677 1.1 macallan if (fdt_get_phandle(fdt, offset) == phandle) 678 1.1 macallan return offset; 679 1.1 macallan } 680 1.1 macallan 681 1.1 macallan return offset; /* error from fdt_next_node() */ 682 1.1 macallan } 683 1.1 macallan 684 1.1 macallan int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 685 1.1 macallan { 686 1.1 macallan int len = strlen(str); 687 1.1 macallan const char *p; 688 1.1 macallan 689 1.1 macallan while (listlen >= len) { 690 1.1 macallan if (memcmp(str, strlist, len+1) == 0) 691 1.1 macallan return 1; 692 1.1 macallan p = memchr(strlist, '\0', listlen); 693 1.1 macallan if (!p) 694 1.1 macallan return 0; /* malformed strlist.. */ 695 1.1 macallan listlen -= (p-strlist) + 1; 696 1.1 macallan strlist = p + 1; 697 1.1 macallan } 698 1.1 macallan return 0; 699 1.1 macallan } 700 1.1 macallan 701 1.1 macallan int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) 702 1.1 macallan { 703 1.1 macallan const char *list, *end; 704 1.1 macallan int length, count = 0; 705 1.1 macallan 706 1.1 macallan list = fdt_getprop(fdt, nodeoffset, property, &length); 707 1.1 macallan if (!list) 708 1.1.1.2 skrll return length; 709 1.1 macallan 710 1.1 macallan end = list + length; 711 1.1 macallan 712 1.1 macallan while (list < end) { 713 1.1 macallan length = strnlen(list, end - list) + 1; 714 1.1 macallan 715 1.1 macallan /* Abort if the last string isn't properly NUL-terminated. */ 716 1.1 macallan if (list + length > end) 717 1.1 macallan return -FDT_ERR_BADVALUE; 718 1.1 macallan 719 1.1 macallan list += length; 720 1.1 macallan count++; 721 1.1 macallan } 722 1.1 macallan 723 1.1 macallan return count; 724 1.1 macallan } 725 1.1 macallan 726 1.1 macallan int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, 727 1.1 macallan const char *string) 728 1.1 macallan { 729 1.1 macallan int length, len, idx = 0; 730 1.1 macallan const char *list, *end; 731 1.1 macallan 732 1.1 macallan list = fdt_getprop(fdt, nodeoffset, property, &length); 733 1.1 macallan if (!list) 734 1.1.1.2 skrll return length; 735 1.1 macallan 736 1.1 macallan len = strlen(string) + 1; 737 1.1 macallan end = list + length; 738 1.1 macallan 739 1.1 macallan while (list < end) { 740 1.1 macallan length = strnlen(list, end - list) + 1; 741 1.1 macallan 742 1.1 macallan /* Abort if the last string isn't properly NUL-terminated. */ 743 1.1 macallan if (list + length > end) 744 1.1 macallan return -FDT_ERR_BADVALUE; 745 1.1 macallan 746 1.1 macallan if (length == len && memcmp(list, string, length) == 0) 747 1.1 macallan return idx; 748 1.1 macallan 749 1.1 macallan list += length; 750 1.1 macallan idx++; 751 1.1 macallan } 752 1.1 macallan 753 1.1 macallan return -FDT_ERR_NOTFOUND; 754 1.1 macallan } 755 1.1 macallan 756 1.1 macallan const char *fdt_stringlist_get(const void *fdt, int nodeoffset, 757 1.1 macallan const char *property, int idx, 758 1.1 macallan int *lenp) 759 1.1 macallan { 760 1.1 macallan const char *list, *end; 761 1.1 macallan int length; 762 1.1 macallan 763 1.1 macallan list = fdt_getprop(fdt, nodeoffset, property, &length); 764 1.1 macallan if (!list) { 765 1.1 macallan if (lenp) 766 1.1 macallan *lenp = length; 767 1.1 macallan 768 1.1 macallan return NULL; 769 1.1 macallan } 770 1.1 macallan 771 1.1 macallan end = list + length; 772 1.1 macallan 773 1.1 macallan while (list < end) { 774 1.1 macallan length = strnlen(list, end - list) + 1; 775 1.1 macallan 776 1.1 macallan /* Abort if the last string isn't properly NUL-terminated. */ 777 1.1 macallan if (list + length > end) { 778 1.1 macallan if (lenp) 779 1.1 macallan *lenp = -FDT_ERR_BADVALUE; 780 1.1 macallan 781 1.1 macallan return NULL; 782 1.1 macallan } 783 1.1 macallan 784 1.1 macallan if (idx == 0) { 785 1.1 macallan if (lenp) 786 1.1 macallan *lenp = length - 1; 787 1.1 macallan 788 1.1 macallan return list; 789 1.1 macallan } 790 1.1 macallan 791 1.1 macallan list += length; 792 1.1 macallan idx--; 793 1.1 macallan } 794 1.1 macallan 795 1.1 macallan if (lenp) 796 1.1 macallan *lenp = -FDT_ERR_NOTFOUND; 797 1.1 macallan 798 1.1 macallan return NULL; 799 1.1 macallan } 800 1.1 macallan 801 1.1 macallan int fdt_node_check_compatible(const void *fdt, int nodeoffset, 802 1.1 macallan const char *compatible) 803 1.1 macallan { 804 1.1 macallan const void *prop; 805 1.1 macallan int len; 806 1.1 macallan 807 1.1 macallan prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); 808 1.1 macallan if (!prop) 809 1.1 macallan return len; 810 1.1.1.2 skrll 811 1.1.1.2 skrll return !fdt_stringlist_contains(prop, len, compatible); 812 1.1 macallan } 813 1.1 macallan 814 1.1 macallan int fdt_node_offset_by_compatible(const void *fdt, int startoffset, 815 1.1 macallan const char *compatible) 816 1.1 macallan { 817 1.1 macallan int offset, err; 818 1.1 macallan 819 1.1.1.3 skrll FDT_RO_PROBE(fdt); 820 1.1 macallan 821 1.1 macallan /* FIXME: The algorithm here is pretty horrible: we scan each 822 1.1 macallan * property of a node in fdt_node_check_compatible(), then if 823 1.1 macallan * that didn't find what we want, we scan over them again 824 1.1 macallan * making our way to the next node. Still it's the easiest to 825 1.1 macallan * implement approach; performance can come later. */ 826 1.1 macallan for (offset = fdt_next_node(fdt, startoffset, NULL); 827 1.1 macallan offset >= 0; 828 1.1 macallan offset = fdt_next_node(fdt, offset, NULL)) { 829 1.1 macallan err = fdt_node_check_compatible(fdt, offset, compatible); 830 1.1 macallan if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) 831 1.1 macallan return err; 832 1.1 macallan else if (err == 0) 833 1.1 macallan return offset; 834 1.1 macallan } 835 1.1 macallan 836 1.1 macallan return offset; /* error from fdt_next_node() */ 837 1.1 macallan } 838 1.1.1.3 skrll 839 1.1.1.3 skrll int fdt_check_full(const void *fdt, size_t bufsize) 840 1.1.1.3 skrll { 841 1.1.1.3 skrll int err; 842 1.1.1.3 skrll int num_memrsv; 843 1.1.1.3 skrll int offset, nextoffset = 0; 844 1.1.1.3 skrll uint32_t tag; 845 1.1.1.3 skrll unsigned depth = 0; 846 1.1.1.3 skrll const void *prop; 847 1.1.1.3 skrll const char *propname; 848 1.1.1.3 skrll 849 1.1.1.3 skrll if (bufsize < FDT_V1_SIZE) 850 1.1.1.3 skrll return -FDT_ERR_TRUNCATED; 851 1.1.1.3 skrll err = fdt_check_header(fdt); 852 1.1.1.3 skrll if (err != 0) 853 1.1.1.3 skrll return err; 854 1.1.1.3 skrll if (bufsize < fdt_totalsize(fdt)) 855 1.1.1.3 skrll return -FDT_ERR_TRUNCATED; 856 1.1.1.3 skrll 857 1.1.1.3 skrll num_memrsv = fdt_num_mem_rsv(fdt); 858 1.1.1.3 skrll if (num_memrsv < 0) 859 1.1.1.3 skrll return num_memrsv; 860 1.1.1.3 skrll 861 1.1.1.3 skrll while (1) { 862 1.1.1.3 skrll offset = nextoffset; 863 1.1.1.3 skrll tag = fdt_next_tag(fdt, offset, &nextoffset); 864 1.1.1.3 skrll 865 1.1.1.3 skrll if (nextoffset < 0) 866 1.1.1.3 skrll return nextoffset; 867 1.1.1.3 skrll 868 1.1.1.3 skrll switch (tag) { 869 1.1.1.3 skrll case FDT_NOP: 870 1.1.1.3 skrll break; 871 1.1.1.3 skrll 872 1.1.1.3 skrll case FDT_END: 873 1.1.1.3 skrll if (depth != 0) 874 1.1.1.3 skrll return -FDT_ERR_BADSTRUCTURE; 875 1.1.1.3 skrll return 0; 876 1.1.1.3 skrll 877 1.1.1.3 skrll case FDT_BEGIN_NODE: 878 1.1.1.3 skrll depth++; 879 1.1.1.3 skrll if (depth > INT_MAX) 880 1.1.1.3 skrll return -FDT_ERR_BADSTRUCTURE; 881 1.1.1.3 skrll break; 882 1.1.1.3 skrll 883 1.1.1.3 skrll case FDT_END_NODE: 884 1.1.1.3 skrll if (depth == 0) 885 1.1.1.3 skrll return -FDT_ERR_BADSTRUCTURE; 886 1.1.1.3 skrll depth--; 887 1.1.1.3 skrll break; 888 1.1.1.3 skrll 889 1.1.1.3 skrll case FDT_PROP: 890 1.1.1.3 skrll prop = fdt_getprop_by_offset(fdt, offset, &propname, 891 1.1.1.3 skrll &err); 892 1.1.1.3 skrll if (!prop) 893 1.1.1.3 skrll return err; 894 1.1.1.3 skrll break; 895 1.1.1.3 skrll 896 1.1.1.3 skrll default: 897 1.1.1.3 skrll return -FDT_ERR_INTERNAL; 898 1.1.1.3 skrll } 899 1.1.1.3 skrll } 900 1.1.1.3 skrll } 901