fdt_ro.c revision 204433
1204431Sraj/*
2204431Sraj * libfdt - Flat Device Tree manipulation
3204431Sraj * Copyright (C) 2006 David Gibson, IBM Corporation.
4204431Sraj *
5204431Sraj * libfdt is dual licensed: you can use it either under the terms of
6204431Sraj * the GPL, or the BSD license, at your option.
7204431Sraj *
8204431Sraj *  a) This library is free software; you can redistribute it and/or
9204431Sraj *     modify it under the terms of the GNU General Public License as
10204431Sraj *     published by the Free Software Foundation; either version 2 of the
11204431Sraj *     License, or (at your option) any later version.
12204431Sraj *
13204431Sraj *     This library is distributed in the hope that it will be useful,
14204431Sraj *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15204431Sraj *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16204431Sraj *     GNU General Public License for more details.
17204431Sraj *
18204431Sraj *     You should have received a copy of the GNU General Public
19204431Sraj *     License along with this library; if not, write to the Free
20204431Sraj *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21204431Sraj *     MA 02110-1301 USA
22204431Sraj *
23204431Sraj * Alternatively,
24204431Sraj *
25204431Sraj *  b) Redistribution and use in source and binary forms, with or
26204431Sraj *     without modification, are permitted provided that the following
27204431Sraj *     conditions are met:
28204431Sraj *
29204431Sraj *     1. Redistributions of source code must retain the above
30204431Sraj *        copyright notice, this list of conditions and the following
31204431Sraj *        disclaimer.
32204431Sraj *     2. Redistributions in binary form must reproduce the above
33204431Sraj *        copyright notice, this list of conditions and the following
34204431Sraj *        disclaimer in the documentation and/or other materials
35204431Sraj *        provided with the distribution.
36204431Sraj *
37204431Sraj *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38204431Sraj *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39204431Sraj *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40204431Sraj *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41204431Sraj *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42204431Sraj *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43204431Sraj *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44204431Sraj *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45204431Sraj *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46204431Sraj *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47204431Sraj *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48204431Sraj *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49204431Sraj *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50204431Sraj */
51204431Sraj#include "libfdt_env.h"
52204431Sraj
53204431Sraj#include <fdt.h>
54204431Sraj#include <libfdt.h>
55204431Sraj
56204431Sraj#include "libfdt_internal.h"
57204431Sraj
58204431Srajstatic int _fdt_nodename_eq(const void *fdt, int offset,
59204431Sraj			    const char *s, int len)
60204431Sraj{
61204431Sraj	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
62204431Sraj
63204431Sraj	if (! p)
64204431Sraj		/* short match */
65204431Sraj		return 0;
66204431Sraj
67204431Sraj	if (memcmp(p, s, len) != 0)
68204431Sraj		return 0;
69204431Sraj
70204431Sraj	if (p[len] == '\0')
71204431Sraj		return 1;
72204431Sraj	else if (!memchr(s, '@', len) && (p[len] == '@'))
73204431Sraj		return 1;
74204431Sraj	else
75204431Sraj		return 0;
76204431Sraj}
77204431Sraj
78204431Srajconst char *fdt_string(const void *fdt, int stroffset)
79204431Sraj{
80204431Sraj	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
81204431Sraj}
82204431Sraj
83204433Srajstatic int _fdt_string_eq(const void *fdt, int stroffset,
84204433Sraj			  const char *s, int len)
85204433Sraj{
86204433Sraj	const char *p = fdt_string(fdt, stroffset);
87204433Sraj
88204433Sraj	return (strlen(p) == len) && (memcmp(p, s, len) == 0);
89204433Sraj}
90204433Sraj
91204431Srajint fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
92204431Sraj{
93204431Sraj	FDT_CHECK_HEADER(fdt);
94204431Sraj	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
95204431Sraj	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
96204431Sraj	return 0;
97204431Sraj}
98204431Sraj
99204431Srajint fdt_num_mem_rsv(const void *fdt)
100204431Sraj{
101204431Sraj	int i = 0;
102204431Sraj
103204431Sraj	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
104204431Sraj		i++;
105204431Sraj	return i;
106204431Sraj}
107204431Sraj
108204431Srajint fdt_subnode_offset_namelen(const void *fdt, int offset,
109204431Sraj			       const char *name, int namelen)
110204431Sraj{
111204431Sraj	int depth;
112204431Sraj
113204431Sraj	FDT_CHECK_HEADER(fdt);
114204431Sraj
115204431Sraj	for (depth = 0;
116204433Sraj	     (offset >= 0) && (depth >= 0);
117204433Sraj	     offset = fdt_next_node(fdt, offset, &depth))
118204433Sraj		if ((depth == 1)
119204433Sraj		    && _fdt_nodename_eq(fdt, offset, name, namelen))
120204431Sraj			return offset;
121204431Sraj
122204433Sraj	if (depth < 0)
123204433Sraj		return -FDT_ERR_NOTFOUND;
124204431Sraj	return offset; /* error */
125204431Sraj}
126204431Sraj
127204431Srajint fdt_subnode_offset(const void *fdt, int parentoffset,
128204431Sraj		       const char *name)
129204431Sraj{
130204431Sraj	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
131204431Sraj}
132204431Sraj
133204431Srajint fdt_path_offset(const void *fdt, const char *path)
134204431Sraj{
135204431Sraj	const char *end = path + strlen(path);
136204431Sraj	const char *p = path;
137204431Sraj	int offset = 0;
138204431Sraj
139204431Sraj	FDT_CHECK_HEADER(fdt);
140204431Sraj
141204433Sraj	/* see if we have an alias */
142204433Sraj	if (*path != '/') {
143204433Sraj		const char *q = strchr(path, '/');
144204431Sraj
145204433Sraj		if (!q)
146204433Sraj			q = end;
147204433Sraj
148204433Sraj		p = fdt_get_alias_namelen(fdt, p, q - p);
149204433Sraj		if (!p)
150204433Sraj			return -FDT_ERR_BADPATH;
151204433Sraj		offset = fdt_path_offset(fdt, p);
152204433Sraj
153204433Sraj		p = q;
154204433Sraj	}
155204433Sraj
156204431Sraj	while (*p) {
157204431Sraj		const char *q;
158204431Sraj
159204431Sraj		while (*p == '/')
160204431Sraj			p++;
161204431Sraj		if (! *p)
162204431Sraj			return offset;
163204431Sraj		q = strchr(p, '/');
164204431Sraj		if (! q)
165204431Sraj			q = end;
166204431Sraj
167204431Sraj		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
168204431Sraj		if (offset < 0)
169204431Sraj			return offset;
170204431Sraj
171204431Sraj		p = q;
172204431Sraj	}
173204431Sraj
174204431Sraj	return offset;
175204431Sraj}
176204431Sraj
177204431Srajconst char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
178204431Sraj{
179204431Sraj	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
180204431Sraj	int err;
181204431Sraj
182204431Sraj	if (((err = fdt_check_header(fdt)) != 0)
183204431Sraj	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
184204431Sraj			goto fail;
185204431Sraj
186204431Sraj	if (len)
187204431Sraj		*len = strlen(nh->name);
188204431Sraj
189204431Sraj	return nh->name;
190204431Sraj
191204431Sraj fail:
192204431Sraj	if (len)
193204431Sraj		*len = err;
194204431Sraj	return NULL;
195204431Sraj}
196204431Sraj
197204433Srajconst struct fdt_property *fdt_get_property_namelen(const void *fdt,
198204433Sraj						    int nodeoffset,
199204433Sraj						    const char *name,
200204433Sraj						    int namelen, int *lenp)
201204431Sraj{
202204431Sraj	uint32_t tag;
203204431Sraj	const struct fdt_property *prop;
204204431Sraj	int offset, nextoffset;
205204431Sraj	int err;
206204431Sraj
207204431Sraj	if (((err = fdt_check_header(fdt)) != 0)
208204431Sraj	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
209204431Sraj			goto fail;
210204431Sraj
211204431Sraj	nextoffset = err;
212204431Sraj	do {
213204431Sraj		offset = nextoffset;
214204431Sraj
215204431Sraj		tag = fdt_next_tag(fdt, offset, &nextoffset);
216204431Sraj		switch (tag) {
217204431Sraj		case FDT_END:
218204433Sraj			if (nextoffset < 0)
219204433Sraj				err = nextoffset;
220204433Sraj			else
221204433Sraj				/* FDT_END tag with unclosed nodes */
222204433Sraj				err = -FDT_ERR_BADSTRUCTURE;
223204431Sraj			goto fail;
224204431Sraj
225204431Sraj		case FDT_PROP:
226204433Sraj			prop = _fdt_offset_ptr(fdt, offset);
227204433Sraj			if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
228204433Sraj					   name, namelen)) {
229204431Sraj				/* Found it! */
230204431Sraj				if (lenp)
231204433Sraj					*lenp = fdt32_to_cpu(prop->len);
232204431Sraj
233204431Sraj				return prop;
234204431Sraj			}
235204431Sraj			break;
236204431Sraj		}
237204431Sraj	} while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE));
238204431Sraj
239204431Sraj	err = -FDT_ERR_NOTFOUND;
240204431Sraj fail:
241204431Sraj	if (lenp)
242204431Sraj		*lenp = err;
243204431Sraj	return NULL;
244204431Sraj}
245204431Sraj
246204433Srajconst struct fdt_property *fdt_get_property(const void *fdt,
247204433Sraj					    int nodeoffset,
248204433Sraj					    const char *name, int *lenp)
249204431Sraj{
250204433Sraj	return fdt_get_property_namelen(fdt, nodeoffset, name,
251204433Sraj					strlen(name), lenp);
252204433Sraj}
253204433Sraj
254204433Srajconst void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
255204433Sraj				const char *name, int namelen, int *lenp)
256204433Sraj{
257204431Sraj	const struct fdt_property *prop;
258204431Sraj
259204433Sraj	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
260204431Sraj	if (! prop)
261204431Sraj		return NULL;
262204431Sraj
263204431Sraj	return prop->data;
264204431Sraj}
265204431Sraj
266204433Srajconst void *fdt_getprop(const void *fdt, int nodeoffset,
267204433Sraj			const char *name, int *lenp)
268204433Sraj{
269204433Sraj	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
270204433Sraj}
271204433Sraj
272204431Srajuint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
273204431Sraj{
274204431Sraj	const uint32_t *php;
275204431Sraj	int len;
276204431Sraj
277204433Sraj	/* FIXME: This is a bit sub-optimal, since we potentially scan
278204433Sraj	 * over all the properties twice. */
279204433Sraj	php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
280204433Sraj	if (!php || (len != sizeof(*php))) {
281204433Sraj		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
282204433Sraj		if (!php || (len != sizeof(*php)))
283204433Sraj			return 0;
284204433Sraj	}
285204431Sraj
286204431Sraj	return fdt32_to_cpu(*php);
287204431Sraj}
288204431Sraj
289204433Srajconst char *fdt_get_alias_namelen(const void *fdt,
290204433Sraj				  const char *name, int namelen)
291204433Sraj{
292204433Sraj	int aliasoffset;
293204433Sraj
294204433Sraj	aliasoffset = fdt_path_offset(fdt, "/aliases");
295204433Sraj	if (aliasoffset < 0)
296204433Sraj		return NULL;
297204433Sraj
298204433Sraj	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
299204433Sraj}
300204433Sraj
301204433Srajconst char *fdt_get_alias(const void *fdt, const char *name)
302204433Sraj{
303204433Sraj	return fdt_get_alias_namelen(fdt, name, strlen(name));
304204433Sraj}
305204433Sraj
306204431Srajint fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
307204431Sraj{
308204431Sraj	int pdepth = 0, p = 0;
309204431Sraj	int offset, depth, namelen;
310204431Sraj	const char *name;
311204431Sraj
312204431Sraj	FDT_CHECK_HEADER(fdt);
313204431Sraj
314204431Sraj	if (buflen < 2)
315204431Sraj		return -FDT_ERR_NOSPACE;
316204431Sraj
317204431Sraj	for (offset = 0, depth = 0;
318204431Sraj	     (offset >= 0) && (offset <= nodeoffset);
319204431Sraj	     offset = fdt_next_node(fdt, offset, &depth)) {
320204431Sraj		while (pdepth > depth) {
321204431Sraj			do {
322204431Sraj				p--;
323204431Sraj			} while (buf[p-1] != '/');
324204431Sraj			pdepth--;
325204431Sraj		}
326204431Sraj
327204433Sraj		if (pdepth >= depth) {
328204433Sraj			name = fdt_get_name(fdt, offset, &namelen);
329204433Sraj			if (!name)
330204433Sraj				return namelen;
331204433Sraj			if ((p + namelen + 1) <= buflen) {
332204433Sraj				memcpy(buf + p, name, namelen);
333204433Sraj				p += namelen;
334204433Sraj				buf[p++] = '/';
335204433Sraj				pdepth++;
336204433Sraj			}
337204431Sraj		}
338204431Sraj
339204431Sraj		if (offset == nodeoffset) {
340204431Sraj			if (pdepth < (depth + 1))
341204431Sraj				return -FDT_ERR_NOSPACE;
342204431Sraj
343204431Sraj			if (p > 1) /* special case so that root path is "/", not "" */
344204431Sraj				p--;
345204431Sraj			buf[p] = '\0';
346204433Sraj			return 0;
347204431Sraj		}
348204431Sraj	}
349204431Sraj
350204431Sraj	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
351204431Sraj		return -FDT_ERR_BADOFFSET;
352204431Sraj	else if (offset == -FDT_ERR_BADOFFSET)
353204431Sraj		return -FDT_ERR_BADSTRUCTURE;
354204431Sraj
355204431Sraj	return offset; /* error from fdt_next_node() */
356204431Sraj}
357204431Sraj
358204431Srajint fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
359204431Sraj				 int supernodedepth, int *nodedepth)
360204431Sraj{
361204431Sraj	int offset, depth;
362204431Sraj	int supernodeoffset = -FDT_ERR_INTERNAL;
363204431Sraj
364204431Sraj	FDT_CHECK_HEADER(fdt);
365204431Sraj
366204431Sraj	if (supernodedepth < 0)
367204431Sraj		return -FDT_ERR_NOTFOUND;
368204431Sraj
369204431Sraj	for (offset = 0, depth = 0;
370204431Sraj	     (offset >= 0) && (offset <= nodeoffset);
371204431Sraj	     offset = fdt_next_node(fdt, offset, &depth)) {
372204431Sraj		if (depth == supernodedepth)
373204431Sraj			supernodeoffset = offset;
374204431Sraj
375204431Sraj		if (offset == nodeoffset) {
376204431Sraj			if (nodedepth)
377204431Sraj				*nodedepth = depth;
378204431Sraj
379204431Sraj			if (supernodedepth > depth)
380204431Sraj				return -FDT_ERR_NOTFOUND;
381204431Sraj			else
382204431Sraj				return supernodeoffset;
383204431Sraj		}
384204431Sraj	}
385204431Sraj
386204431Sraj	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
387204431Sraj		return -FDT_ERR_BADOFFSET;
388204431Sraj	else if (offset == -FDT_ERR_BADOFFSET)
389204431Sraj		return -FDT_ERR_BADSTRUCTURE;
390204431Sraj
391204431Sraj	return offset; /* error from fdt_next_node() */
392204431Sraj}
393204431Sraj
394204431Srajint fdt_node_depth(const void *fdt, int nodeoffset)
395204431Sraj{
396204431Sraj	int nodedepth;
397204431Sraj	int err;
398204431Sraj
399204431Sraj	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
400204431Sraj	if (err)
401204431Sraj		return (err < 0) ? err : -FDT_ERR_INTERNAL;
402204431Sraj	return nodedepth;
403204431Sraj}
404204431Sraj
405204431Srajint fdt_parent_offset(const void *fdt, int nodeoffset)
406204431Sraj{
407204431Sraj	int nodedepth = fdt_node_depth(fdt, nodeoffset);
408204431Sraj
409204431Sraj	if (nodedepth < 0)
410204431Sraj		return nodedepth;
411204431Sraj	return fdt_supernode_atdepth_offset(fdt, nodeoffset,
412204431Sraj					    nodedepth - 1, NULL);
413204431Sraj}
414204431Sraj
415204431Srajint fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
416204431Sraj				  const char *propname,
417204431Sraj				  const void *propval, int proplen)
418204431Sraj{
419204431Sraj	int offset;
420204431Sraj	const void *val;
421204431Sraj	int len;
422204431Sraj
423204431Sraj	FDT_CHECK_HEADER(fdt);
424204431Sraj
425204431Sraj	/* FIXME: The algorithm here is pretty horrible: we scan each
426204431Sraj	 * property of a node in fdt_getprop(), then if that didn't
427204431Sraj	 * find what we want, we scan over them again making our way
428204431Sraj	 * to the next node.  Still it's the easiest to implement
429204431Sraj	 * approach; performance can come later. */
430204431Sraj	for (offset = fdt_next_node(fdt, startoffset, NULL);
431204431Sraj	     offset >= 0;
432204431Sraj	     offset = fdt_next_node(fdt, offset, NULL)) {
433204431Sraj		val = fdt_getprop(fdt, offset, propname, &len);
434204431Sraj		if (val && (len == proplen)
435204431Sraj		    && (memcmp(val, propval, len) == 0))
436204431Sraj			return offset;
437204431Sraj	}
438204431Sraj
439204431Sraj	return offset; /* error from fdt_next_node() */
440204431Sraj}
441204431Sraj
442204431Srajint fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
443204431Sraj{
444204433Sraj	int offset;
445204433Sraj
446204431Sraj	if ((phandle == 0) || (phandle == -1))
447204431Sraj		return -FDT_ERR_BADPHANDLE;
448204433Sraj
449204433Sraj	FDT_CHECK_HEADER(fdt);
450204433Sraj
451204433Sraj	/* FIXME: The algorithm here is pretty horrible: we
452204433Sraj	 * potentially scan each property of a node in
453204433Sraj	 * fdt_get_phandle(), then if that didn't find what
454204433Sraj	 * we want, we scan over them again making our way to the next
455204433Sraj	 * node.  Still it's the easiest to implement approach;
456204433Sraj	 * performance can come later. */
457204433Sraj	for (offset = fdt_next_node(fdt, -1, NULL);
458204433Sraj	     offset >= 0;
459204433Sraj	     offset = fdt_next_node(fdt, offset, NULL)) {
460204433Sraj		if (fdt_get_phandle(fdt, offset) == phandle)
461204433Sraj			return offset;
462204433Sraj	}
463204433Sraj
464204433Sraj	return offset; /* error from fdt_next_node() */
465204431Sraj}
466204431Sraj
467204433Srajstatic int _fdt_stringlist_contains(const char *strlist, int listlen,
468204433Sraj				    const char *str)
469204431Sraj{
470204431Sraj	int len = strlen(str);
471204431Sraj	const char *p;
472204431Sraj
473204431Sraj	while (listlen >= len) {
474204431Sraj		if (memcmp(str, strlist, len+1) == 0)
475204431Sraj			return 1;
476204431Sraj		p = memchr(strlist, '\0', listlen);
477204431Sraj		if (!p)
478204431Sraj			return 0; /* malformed strlist.. */
479204431Sraj		listlen -= (p-strlist) + 1;
480204431Sraj		strlist = p + 1;
481204431Sraj	}
482204431Sraj	return 0;
483204431Sraj}
484204431Sraj
485204431Srajint fdt_node_check_compatible(const void *fdt, int nodeoffset,
486204431Sraj			      const char *compatible)
487204431Sraj{
488204431Sraj	const void *prop;
489204431Sraj	int len;
490204431Sraj
491204431Sraj	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
492204431Sraj	if (!prop)
493204431Sraj		return len;
494204433Sraj	if (_fdt_stringlist_contains(prop, len, compatible))
495204431Sraj		return 0;
496204431Sraj	else
497204431Sraj		return 1;
498204431Sraj}
499204431Sraj
500204431Srajint fdt_node_offset_by_compatible(const void *fdt, int startoffset,
501204431Sraj				  const char *compatible)
502204431Sraj{
503204431Sraj	int offset, err;
504204431Sraj
505204431Sraj	FDT_CHECK_HEADER(fdt);
506204431Sraj
507204431Sraj	/* FIXME: The algorithm here is pretty horrible: we scan each
508204431Sraj	 * property of a node in fdt_node_check_compatible(), then if
509204431Sraj	 * that didn't find what we want, we scan over them again
510204431Sraj	 * making our way to the next node.  Still it's the easiest to
511204431Sraj	 * implement approach; performance can come later. */
512204431Sraj	for (offset = fdt_next_node(fdt, startoffset, NULL);
513204431Sraj	     offset >= 0;
514204431Sraj	     offset = fdt_next_node(fdt, offset, NULL)) {
515204431Sraj		err = fdt_node_check_compatible(fdt, offset, compatible);
516204431Sraj		if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
517204431Sraj			return err;
518204431Sraj		else if (err == 0)
519204431Sraj			return offset;
520204431Sraj	}
521204431Sraj
522204431Sraj	return offset; /* error from fdt_next_node() */
523204431Sraj}
524