fdt_ro.c 13.8 KB
Newer Older
1 2 3
/*
 * libfdt - Flat Device Tree manipulation
 * 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 10
#include <fdt.h>
#include <libfdt.h>
11 12 13
#else
#include "fdt_host.h"
#endif
14 15 16

#include "libfdt_internal.h"

17 18
static int _fdt_nodename_eq(const void *fdt, int offset,
			    const char *s, int len)
19
{
20
	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
21

22
	if (!p)
23 24 25 26 27 28
		/* short match */
		return 0;

	if (memcmp(p, s, len) != 0)
		return 0;

29 30 31 32 33
	if (p[len] == '\0')
		return 1;
	else if (!memchr(s, '@', len) && (p[len] == '@'))
		return 1;
	else
34 35 36
		return 0;
}

37
const char *fdt_string(const void *fdt, int stroffset)
38
{
39
	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
40
}
41

42 43 44 45 46
static int _fdt_string_eq(const void *fdt, int stroffset,
			  const char *s, int len)
{
	const char *p = fdt_string(fdt, stroffset);

47
	return (strnlen(p, len + 1) == len) && (memcmp(p, s, len) == 0);
48 49
}

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
uint32_t fdt_get_max_phandle(const void *fdt)
{
	uint32_t max_phandle = 0;
	int offset;

	for (offset = fdt_next_node(fdt, -1, NULL);;
	     offset = fdt_next_node(fdt, offset, NULL)) {
		uint32_t phandle;

		if (offset == -FDT_ERR_NOTFOUND)
			return max_phandle;

		if (offset < 0)
			return 0;

		phandle = fdt_get_phandle(fdt, offset);
		if (phandle == (uint32_t)-1)
			return 0;

		if (phandle > max_phandle)
			max_phandle = phandle;
	}

	return 0;
}

76 77
int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
{
78
	FDT_CHECK_HEADER(fdt);
79 80
	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
81 82 83
	return 0;
}

84
int fdt_num_mem_rsv(const void *fdt)
85
{
86 87 88 89 90
	int i = 0;

	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
		i++;
	return i;
91 92
}

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
static int _nextprop(const void *fdt, int offset)
{
	uint32_t tag;
	int nextoffset;

	do {
		tag = fdt_next_tag(fdt, offset, &nextoffset);

		switch (tag) {
		case FDT_END:
			if (nextoffset >= 0)
				return -FDT_ERR_BADSTRUCTURE;
			else
				return nextoffset;

		case FDT_PROP:
			return offset;
		}
		offset = nextoffset;
	} while (tag == FDT_NOP);

	return -FDT_ERR_NOTFOUND;
}

117
int fdt_subnode_offset_namelen(const void *fdt, int offset,
118 119
			       const char *name, int namelen)
{
D
David Gibson 已提交
120
	int depth;
121

122
	FDT_CHECK_HEADER(fdt);
123

D
David Gibson 已提交
124 125 126 127 128
	for (depth = 0;
	     (offset >= 0) && (depth >= 0);
	     offset = fdt_next_node(fdt, offset, &depth))
		if ((depth == 1)
		    && _fdt_nodename_eq(fdt, offset, name, namelen))
129
			return offset;
130

D
David Gibson 已提交
131
	if (depth < 0)
132
		return -FDT_ERR_NOTFOUND;
D
David Gibson 已提交
133
	return offset; /* error */
134 135 136 137 138 139 140 141
}

int fdt_subnode_offset(const void *fdt, int parentoffset,
		       const char *name)
{
	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
}

142 143
/*
 * Find the next of path seperator, note we need to search for both '/' and ':'
144
 * and then take the first one so that we do the right thing for e.g.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
 * "foo/bar:option" and "bar:option/otheroption", both of which happen, so
 * first searching for either ':' or '/' does not work.
 */
static const char *fdt_path_next_seperator(const char *path)
{
	const char *sep1 = strchr(path, '/');
	const char *sep2 = strchr(path, ':');

	if (sep1 && sep2)
		return (sep1 < sep2) ? sep1 : sep2;
	else if (sep1)
		return sep1;
	else
		return sep2;
}

161
int fdt_path_offset(const void *fdt, const char *path)
162 163 164 165 166
{
	const char *end = path + strlen(path);
	const char *p = path;
	int offset = 0;

167
	FDT_CHECK_HEADER(fdt);
168

169 170
	/* see if we have an alias */
	if (*path != '/') {
171
		const char *q = fdt_path_next_seperator(path);
172 173 174 175

		if (!q)
			q = end;

176
		p = fdt_get_alias_namelen(fdt, p, q - p);
177 178 179 180 181 182
		if (!p)
			return -FDT_ERR_BADPATH;
		offset = fdt_path_offset(fdt, p);

		p = q;
	}
183 184 185 186 187 188

	while (*p) {
		const char *q;

		while (*p == '/')
			p++;
189
		if (*p == '\0' || *p == ':')
190
			return offset;
191
		q = fdt_path_next_seperator(p);
192
		if (!q)
193 194 195 196 197 198 199 200 201
			q = end;

		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
		if (offset < 0)
			return offset;

		p = q;
	}

202
	return offset;
203 204
}

205 206
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
207
	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
208 209
	int err;

210 211 212
	if (((err = fdt_check_header(fdt)) != 0)
	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
			goto fail;
213 214 215 216 217 218 219 220 221 222 223 224

	if (len)
		*len = strlen(nh->name);

	return nh->name;

 fail:
	if (len)
		*len = err;
	return NULL;
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
int fdt_first_property_offset(const void *fdt, int nodeoffset)
{
	int offset;

	if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
		return offset;

	return _nextprop(fdt, offset);
}

int fdt_next_property_offset(const void *fdt, int offset)
{
	if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
		return offset;

	return _nextprop(fdt, offset);
}

const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
						      int offset,
						      int *lenp)
246 247
{
	int err;
248
	const struct fdt_property *prop;
249

250 251 252 253 254
	if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
		if (lenp)
			*lenp = err;
		return NULL;
	}
255

256
	prop = _fdt_offset_ptr(fdt, offset);
257

258 259
	if (lenp)
		*lenp = fdt32_to_cpu(prop->len);
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
	return prop;
}

const struct fdt_property *fdt_get_property_namelen(const void *fdt,
						    int offset,
						    const char *name,
						    int namelen, int *lenp)
{
	for (offset = fdt_first_property_offset(fdt, offset);
	     (offset >= 0);
	     (offset = fdt_next_property_offset(fdt, offset))) {
		const struct fdt_property *prop;

		if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
			offset = -FDT_ERR_INTERNAL;
276 277
			break;
		}
278 279 280 281
		if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
				   name, namelen))
			return prop;
	}
282 283

	if (lenp)
284
		*lenp = offset;
285 286 287
	return NULL;
}

288 289 290 291 292 293 294 295 296 297
const struct fdt_property *fdt_get_property(const void *fdt,
					    int nodeoffset,
					    const char *name, int *lenp)
{
	return fdt_get_property_namelen(fdt, nodeoffset, name,
					strlen(name), lenp);
}

const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
				const char *name, int namelen, int *lenp)
298 299 300
{
	const struct fdt_property *prop;

301
	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
302
	if (!prop)
303 304
		return NULL;

305
	return prop->data;
306 307
}

308 309 310 311 312 313 314 315 316 317 318 319 320
const void *fdt_getprop_by_offset(const void *fdt, int offset,
				  const char **namep, int *lenp)
{
	const struct fdt_property *prop;

	prop = fdt_get_property_by_offset(fdt, offset, lenp);
	if (!prop)
		return NULL;
	if (namep)
		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
	return prop->data;
}

321 322 323 324 325 326
const void *fdt_getprop(const void *fdt, int nodeoffset,
			const char *name, int *lenp)
{
	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
}

327 328
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
{
329
	const fdt32_t *php;
330
	int len;
331

332 333 334 335 336 337 338 339
	/* FIXME: This is a bit sub-optimal, since we potentially scan
	 * over all the properties twice. */
	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
	if (!php || (len != sizeof(*php))) {
		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
		if (!php || (len != sizeof(*php)))
			return 0;
	}
340 341 342 343

	return fdt32_to_cpu(*php);
}

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
const char *fdt_get_alias_namelen(const void *fdt,
				  const char *name, int namelen)
{
	int aliasoffset;

	aliasoffset = fdt_path_offset(fdt, "/aliases");
	if (aliasoffset < 0)
		return NULL;

	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
}

const char *fdt_get_alias(const void *fdt, const char *name)
{
	return fdt_get_alias_namelen(fdt, name, strlen(name));
}

361
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
362
{
363 364
	int pdepth = 0, p = 0;
	int offset, depth, namelen;
365 366
	const char *name;

367
	FDT_CHECK_HEADER(fdt);
368 369 370 371

	if (buflen < 2)
		return -FDT_ERR_NOSPACE;

372 373 374 375 376 377 378 379 380 381
	for (offset = 0, depth = 0;
	     (offset >= 0) && (offset <= nodeoffset);
	     offset = fdt_next_node(fdt, offset, &depth)) {
		while (pdepth > depth) {
			do {
				p--;
			} while (buf[p-1] != '/');
			pdepth--;
		}

D
David Gibson 已提交
382 383 384 385 386 387 388 389 390 391
		if (pdepth >= depth) {
			name = fdt_get_name(fdt, offset, &namelen);
			if (!name)
				return namelen;
			if ((p + namelen + 1) <= buflen) {
				memcpy(buf + p, name, namelen);
				p += namelen;
				buf[p++] = '/';
				pdepth++;
			}
392
		}
393

394 395 396
		if (offset == nodeoffset) {
			if (pdepth < (depth + 1))
				return -FDT_ERR_NOSPACE;
397

398 399 400
			if (p > 1) /* special case so that root path is "/", not "" */
				p--;
			buf[p] = '\0';
D
David Gibson 已提交
401
			return 0;
402 403 404
		}
	}

405 406 407 408
	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
		return -FDT_ERR_BADOFFSET;
	else if (offset == -FDT_ERR_BADOFFSET)
		return -FDT_ERR_BADSTRUCTURE;
409

410
	return offset; /* error from fdt_next_node() */
411
}
412

413 414 415
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
				 int supernodedepth, int *nodedepth)
{
416
	int offset, depth;
417 418
	int supernodeoffset = -FDT_ERR_INTERNAL;

419
	FDT_CHECK_HEADER(fdt);
420 421 422 423

	if (supernodedepth < 0)
		return -FDT_ERR_NOTFOUND;

424 425 426 427 428
	for (offset = 0, depth = 0;
	     (offset >= 0) && (offset <= nodeoffset);
	     offset = fdt_next_node(fdt, offset, &depth)) {
		if (depth == supernodedepth)
			supernodeoffset = offset;
429

430 431 432
		if (offset == nodeoffset) {
			if (nodedepth)
				*nodedepth = depth;
433

434 435 436 437
			if (supernodedepth > depth)
				return -FDT_ERR_NOTFOUND;
			else
				return supernodeoffset;
438
		}
439
	}
440

441 442 443 444
	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
		return -FDT_ERR_BADOFFSET;
	else if (offset == -FDT_ERR_BADOFFSET)
		return -FDT_ERR_BADSTRUCTURE;
445

446
	return offset; /* error from fdt_next_node() */
447 448 449
}

int fdt_node_depth(const void *fdt, int nodeoffset)
450
{
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
	int nodedepth;
	int err;

	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
	if (err)
		return (err < 0) ? err : -FDT_ERR_INTERNAL;
	return nodedepth;
}

int fdt_parent_offset(const void *fdt, int nodeoffset)
{
	int nodedepth = fdt_node_depth(fdt, nodeoffset);

	if (nodedepth < 0)
		return nodedepth;
	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
					    nodedepth - 1, NULL);
}

int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
				  const char *propname,
				  const void *propval, int proplen)
{
474
	int offset;
475 476 477
	const void *val;
	int len;

478
	FDT_CHECK_HEADER(fdt);
479 480 481 482 483 484

	/* FIXME: The algorithm here is pretty horrible: we scan each
	 * property of a node in fdt_getprop(), then if that didn't
	 * find what we want, we scan over them again making our way
	 * to the next node.  Still it's the easiest to implement
	 * approach; performance can come later. */
485 486 487 488 489 490 491 492
	for (offset = fdt_next_node(fdt, startoffset, NULL);
	     offset >= 0;
	     offset = fdt_next_node(fdt, offset, NULL)) {
		val = fdt_getprop(fdt, offset, propname, &len);
		if (val && (len == proplen)
		    && (memcmp(val, propval, len) == 0))
			return offset;
	}
493

494
	return offset; /* error from fdt_next_node() */
495 496 497 498
}

int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
{
499 500
	int offset;

501 502
	if ((phandle == 0) || (phandle == -1))
		return -FDT_ERR_BADPHANDLE;
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

	FDT_CHECK_HEADER(fdt);

	/* FIXME: The algorithm here is pretty horrible: we
	 * potentially scan each property of a node in
	 * fdt_get_phandle(), then if that didn't find what
	 * we want, we scan over them again making our way to the next
	 * node.  Still it's the easiest to implement approach;
	 * performance can come later. */
	for (offset = fdt_next_node(fdt, -1, NULL);
	     offset >= 0;
	     offset = fdt_next_node(fdt, offset, NULL)) {
		if (fdt_get_phandle(fdt, offset) == phandle)
			return offset;
	}

	return offset; /* error from fdt_next_node() */
520 521
}

S
Simon Glass 已提交
522
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
523 524
{
	int len = strlen(str);
525
	const char *p;
526 527 528 529 530 531 532 533 534

	while (listlen >= len) {
		if (memcmp(str, strlist, len+1) == 0)
			return 1;
		p = memchr(strlist, '\0', listlen);
		if (!p)
			return 0; /* malformed strlist.. */
		listlen -= (p-strlist) + 1;
		strlist = p + 1;
535 536 537 538
	}
	return 0;
}

539 540 541 542 543 544 545
int fdt_count_strings(const void *fdt, int node, const char *property)
{
	int length, i, count = 0;
	const char *list;

	list = fdt_getprop(fdt, node, property, &length);
	if (!list)
546
		return length;
547 548 549 550 551 552 553 554 555 556 557 558

	for (i = 0; i < length; i++) {
		int len = strlen(list);

		list += len + 1;
		i += len;
		count++;
	}

	return count;
}

559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
int fdt_find_string(const void *fdt, int node, const char *property,
		    const char *string)
{
	const char *list, *end;
	int len, index = 0;

	list = fdt_getprop(fdt, node, property, &len);
	if (!list)
		return len;

	end = list + len;
	len = strlen(string);

	while (list < end) {
		int l = strlen(list);

		if (l == len && memcmp(list, string, len) == 0)
			return index;

		list += l + 1;
		index++;
	}

	return -FDT_ERR_NOTFOUND;
}

585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
int fdt_get_string_index(const void *fdt, int node, const char *property,
			 int index, const char **output)
{
	const char *list;
	int length, i;

	list = fdt_getprop(fdt, node, property, &length);

	for (i = 0; i < length; i++) {
		int len = strlen(list);

		if (index == 0) {
			*output = list;
			return 0;
		}

		list += len + 1;
		i += len;
		index--;
	}

606
	return -FDT_ERR_NOTFOUND;
607 608 609 610 611 612 613 614
}

int fdt_get_string(const void *fdt, int node, const char *property,
		   const char **output)
{
	return fdt_get_string_index(fdt, node, property, 0, output);
}

615 616
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
			      const char *compatible)
617
{
618 619
	const void *prop;
	int len;
620

621 622 623
	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
	if (!prop)
		return len;
S
Simon Glass 已提交
624
	if (fdt_stringlist_contains(prop, len, compatible))
625 626 627 628
		return 0;
	else
		return 1;
}
629

630 631 632
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
				  const char *compatible)
{
633
	int offset, err;
634

635
	FDT_CHECK_HEADER(fdt);
636 637 638 639 640 641

	/* FIXME: The algorithm here is pretty horrible: we scan each
	 * property of a node in fdt_node_check_compatible(), then if
	 * that didn't find what we want, we scan over them again
	 * making our way to the next node.  Still it's the easiest to
	 * implement approach; performance can come later. */
642 643 644 645 646 647 648 649 650
	for (offset = fdt_next_node(fdt, startoffset, NULL);
	     offset >= 0;
	     offset = fdt_next_node(fdt, offset, NULL)) {
		err = fdt_node_check_compatible(fdt, offset, compatible);
		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
			return err;
		else if (err == 0)
			return offset;
	}
651

652
	return offset; /* error from fdt_next_node() */
653
}