xref: /openbsd/sys/arch/armv7/stand/efiboot/fdt.c (revision 9b7c3dbb)
1 /*	$OpenBSD: fdt.c,v 1.4 2016/05/19 19:32:07 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2009 Dariusz Swiderski <sfires@sfires.net>
5  * Copyright (c) 2009, 2016 Mark Kettenis <kettenis@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 
23 #include <lib/libkern/libkern.h>
24 
25 #include "fdt.h"
26 
27 unsigned int fdt_check_head(void *);
28 char	*fdt_get_str(uint32_t);
29 void	*skip_property(uint32_t *);
30 void	*skip_props(uint32_t *);
31 void	*skip_node_name(uint32_t *);
32 void	*skip_node(void *);
33 void	*fdt_parent_node_recurse(void *, void *);
34 
35 static int tree_inited = 0;
36 static struct fdt tree;
37 
38 unsigned int
39 fdt_check_head(void *fdt)
40 {
41 	struct fdt_head *fh;
42 	uint32_t *ptr;
43 
44 	fh = fdt;
45 	ptr = (uint32_t *)fdt;
46 
47 	if (betoh32(fh->fh_magic) != FDT_MAGIC)
48 		return 0;
49 
50 	if (betoh32(fh->fh_version) > FDT_CODE_VERSION)
51 		return 0;
52 
53 	if (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4))) !=
54 	    FDT_NODE_BEGIN)
55 		return 0;
56 
57 	/* check for end signature on version 17 blob */
58 	if ((betoh32(fh->fh_version) >= 17) &&
59 	    (betoh32(*(ptr + (betoh32(fh->fh_struct_off) / 4) +
60 	    (betoh32(fh->fh_struct_size) / 4) - 1)) != FDT_END))
61 		return 0;
62 
63 	return betoh32(fh->fh_version);
64 }
65 
66 /*
67  * Initializes internal structures of module.
68  * Has to be called once.
69  */
70 int
71 fdt_init(void *fdt)
72 {
73 	int version;
74 
75 	memset(&tree, 0, sizeof(struct fdt));
76 	tree_inited = 0;
77 
78 	if (!fdt)
79 		return 0;
80 
81 	if (!(version = fdt_check_head(fdt)))
82 		return 0;
83 
84 	tree.header = (struct fdt_head *)fdt;
85 	tree.tree = (char *)fdt + betoh32(tree.header->fh_struct_off);
86 	tree.strings = (char *)fdt + betoh32(tree.header->fh_strings_off);
87 	tree.memory = (char *)fdt + betoh32(tree.header->fh_reserve_off);
88 	tree.end = (char *)fdt + betoh32(tree.header->fh_size);
89 	tree.version = version;
90 	tree.strings_size = betoh32(tree.header->fh_strings_size);
91 	if (tree.version >= 17)
92 		tree.struct_size = betoh32(tree.header->fh_struct_size);
93 	tree_inited = 1;
94 
95 	return version;
96 }
97 
98 void
99 fdt_finalize(void)
100 {
101 	char *start = (char *)tree.header;
102 
103 	tree.header->fh_size = htobe32(tree.end - start);
104 	tree.header->fh_struct_off = htobe32(tree.tree - start);
105 	tree.header->fh_strings_off = htobe32(tree.strings - start);
106 	tree.header->fh_reserve_off = htobe32(tree.memory - start);
107 	tree.header->fh_strings_size = htobe32(tree.strings_size);
108 	if (tree.version >= 17)
109 		tree.header->fh_struct_size = htobe32(tree.struct_size);
110 }
111 
112 /*
113  * Return the size of the FDT.
114  */
115 size_t
116 fdt_get_size(void *fdt)
117 {
118 	if (!fdt)
119 		return 0;
120 
121 	if (!fdt_check_head(fdt))
122 		return 0;
123 
124 	return betoh32(((struct fdt_head *)fdt)->fh_size);
125 }
126 
127 /*
128  * Retrieve string pointer from strings table.
129  */
130 char *
131 fdt_get_str(uint32_t num)
132 {
133 	if (num > tree.strings_size)
134 		return NULL;
135 	return (tree.strings) ? (tree.strings + num) : NULL;
136 }
137 
138 int
139 fdt_add_str(char *name)
140 {
141 	size_t len = roundup(strlen(name) + 1, sizeof(uint32_t));
142 	char *end = tree.strings + tree.strings_size;
143 
144 	memmove(end + len, end, tree.end - end);
145 	tree.strings_size += len;
146 	if (tree.tree > tree.strings)
147 		tree.tree += len;
148 	if (tree.memory > tree.strings)
149 		tree.memory += len;
150 	tree.end += len;
151 	memcpy(end, name, len);
152 
153 	return (end - tree.strings);
154 }
155 
156 /*
157  * Utility functions for skipping parts of tree.
158  */
159 void *
160 skip_property(uint32_t *ptr)
161 {
162 	uint32_t size;
163 
164 	size = betoh32(*(ptr + 1));
165 	/* move forward by magic + size + nameid + rounded up property size */
166 	ptr += 3 + roundup(size, sizeof(uint32_t)) / sizeof(uint32_t);
167 
168 	return ptr;
169 }
170 
171 void *
172 skip_props(uint32_t *ptr)
173 {
174 	while (betoh32(*ptr) == FDT_PROPERTY) {
175 		ptr = skip_property(ptr);
176 	}
177 	return ptr;
178 }
179 
180 void *
181 skip_node_name(uint32_t *ptr)
182 {
183 	/* skip name, aligned to 4 bytes, this is NULL term., so must add 1 */
184 	return ptr + roundup(strlen((char *)ptr) + 1,
185 	    sizeof(uint32_t)) / sizeof(uint32_t);
186 }
187 
188 /*
189  * Retrieves node property, the returned pointer is inside the fdt tree,
190  * so we should not modify content pointed by it directly.
191  */
192 int
193 fdt_node_property(void *node, char *name, char **out)
194 {
195 	uint32_t *ptr;
196 	uint32_t nameid;
197 	char *tmp;
198 
199 	if (!tree_inited)
200 		return 0;
201 
202 	ptr = (uint32_t *)node;
203 
204 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
205 		return 0;
206 
207 	ptr = skip_node_name(ptr + 1);
208 
209 	while (betoh32(*ptr) == FDT_PROPERTY) {
210 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
211 		tmp = fdt_get_str(nameid);
212 		if (!strcmp(name, tmp)) {
213 			*out = (char *)(ptr + 3); /* beginning of the value */
214 			return betoh32(*(ptr + 1)); /* size of value */
215 		}
216 		ptr = skip_property(ptr);
217 	}
218 	return 0;
219 }
220 
221 int
222 fdt_node_set_property(void *node, char *name, char *data, int len)
223 {
224 	uint32_t *ptr, *next;
225 	uint32_t nameid;
226 	uint32_t curlen;
227 	size_t delta;
228 	char *tmp;
229 
230 	if (!tree_inited)
231 		return 0;
232 
233 	ptr = (uint32_t *)node;
234 
235 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
236 		return 0;
237 
238 	ptr = skip_node_name(ptr + 1);
239 
240 	while (betoh32(*ptr) == FDT_PROPERTY) {
241 		nameid = betoh32(*(ptr + 2)); /* id of name in strings table */
242 		tmp = fdt_get_str(nameid);
243 		next = skip_property(ptr);
244 		if (!strcmp(name, tmp)) {
245 			curlen = betoh32(*(ptr + 1));
246 			delta = roundup(len, sizeof(uint32_t)) -
247 			    roundup(curlen, sizeof(uint32_t));
248 			memmove((char *)next + delta, next,
249 			    tree.end - (char *)next);
250 			tree.struct_size += delta;
251 			if (tree.strings > tree.tree)
252 				tree.strings += delta;
253 			if (tree.memory > tree.tree)
254 				tree.memory += delta;
255 			tree.end += delta;
256 			*(ptr + 1) = htobe32(len);
257 			memcpy(ptr + 3, data, len);
258 			return 1;
259 		}
260 		ptr = next;
261 	}
262 	return 0;
263 }
264 
265 int
266 fdt_node_add_property(void *node, char *name, char *data, int len)
267 {
268 	char *dummy;
269 
270 	if (!tree_inited)
271 		return 0;
272 
273 	if (!fdt_node_property(node, name, &dummy)) {
274 		uint32_t *ptr = (uint32_t *)node;
275 
276 		if (betoh32(*ptr) != FDT_NODE_BEGIN)
277 			return 0;
278 
279 		ptr = skip_node_name(ptr + 1);
280 
281 		memmove(ptr + 3, ptr, tree.end - (char *)ptr);
282 		tree.struct_size += 3 * sizeof(uint32_t);
283 		if (tree.strings > tree.tree)
284 			tree.strings += 3 * sizeof(uint32_t);
285 		if (tree.memory > tree.tree)
286 			tree.memory += 3 * sizeof(uint32_t);
287 		tree.end += 3 * sizeof(uint32_t);
288 		*ptr++ = htobe32(FDT_PROPERTY);
289 		*ptr++ = htobe32(0);
290 		*ptr++ = htobe32(fdt_add_str(name));
291 	}
292 
293 	return fdt_node_set_property(node, name, data, len);
294 }
295 
296 /*
297  * Retrieves next node, skipping all the children nodes of the pointed node,
298  * returns pointer to next node, no matter if it exists or not.
299  */
300 void *
301 skip_node(void *node)
302 {
303 	uint32_t *ptr = node;
304 
305 	ptr++;
306 
307 	ptr = skip_node_name(ptr);
308 	ptr = skip_props(ptr);
309 
310 	/* skip children */
311 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
312 		ptr = skip_node(ptr);
313 
314 	return (ptr + 1);
315 }
316 
317 /*
318  * Retrieves next node, skipping all the children nodes of the pointed node,
319  * returns pointer to next node if exists, otherwise returns NULL.
320  * If passed 0 will return first node of the tree (root).
321  */
322 void *
323 fdt_next_node(void *node)
324 {
325 	uint32_t *ptr;
326 
327 	if (!tree_inited)
328 		return NULL;
329 
330 	ptr = node;
331 
332 	if (node == NULL) {
333 		ptr = (uint32_t *)tree.tree;
334 		return (betoh32(*ptr) == FDT_NODE_BEGIN) ? ptr : NULL;
335 	}
336 
337 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
338 		return NULL;
339 
340 	ptr++;
341 
342 	ptr = skip_node_name(ptr);
343 	ptr = skip_props(ptr);
344 
345 	/* skip children */
346 	while (betoh32(*ptr) == FDT_NODE_BEGIN)
347 		ptr = skip_node(ptr);
348 
349 	if (betoh32(*ptr) != FDT_NODE_END)
350 		return NULL;
351 
352 	if (betoh32(*(ptr + 1)) != FDT_NODE_BEGIN)
353 		return NULL;
354 
355 	return (ptr + 1);
356 }
357 
358 /*
359  * Retrieves next node, skipping all the children nodes of the pointed node
360  */
361 void *
362 fdt_child_node(void *node)
363 {
364 	uint32_t *ptr;
365 
366 	if (!tree_inited)
367 		return NULL;
368 
369 	ptr = node;
370 
371 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
372 		return NULL;
373 
374 	ptr++;
375 
376 	ptr = skip_node_name(ptr);
377 	ptr = skip_props(ptr);
378 	/* check if there is a child node */
379 	return (betoh32(*ptr) == FDT_NODE_BEGIN) ? (ptr) : NULL;
380 }
381 
382 /*
383  * Retrieves node name.
384  */
385 char *
386 fdt_node_name(void *node)
387 {
388 	uint32_t *ptr;
389 
390 	if (!tree_inited)
391 		return NULL;
392 
393 	ptr = node;
394 
395 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
396 		return NULL;
397 
398 	return (char *)(ptr + 1);
399 }
400 
401 void *
402 fdt_find_node(char *name)
403 {
404 	void *node = fdt_next_node(0);
405 	const char *p = name;
406 
407 	if (!tree_inited)
408 		return NULL;
409 
410 	if (*p != '/')
411 		return NULL;
412 
413 	while (*p) {
414 		void *child;
415 		const char *q;
416 
417 		while (*p == '/')
418 			p++;
419 		if (*p == 0)
420 			return node;
421 		q = strchr(p, '/');
422 		if (q == NULL)
423 			q = p + strlen(p);
424 
425 		for (child = fdt_child_node(node); child;
426 		     child = fdt_next_node(child)) {
427 			if (strncmp(p, fdt_node_name(child), q - p) == 0) {
428 				node = child;
429 				break;
430 			}
431 		}
432 
433 		p = q;
434 	}
435 
436 	return node;
437 }
438 
439 void *
440 fdt_parent_node_recurse(void *pnode, void *child)
441 {
442 	void *node = fdt_child_node(pnode);
443 	void *tmp;
444 
445 	while (node && (node != child)) {
446 		if ((tmp = fdt_parent_node_recurse(node, child)))
447 			return tmp;
448 		node = fdt_next_node(node);
449 	}
450 	return (node) ? pnode : NULL;
451 }
452 
453 void *
454 fdt_parent_node(void *node)
455 {
456 	void *pnode = fdt_next_node(0);
457 
458 	if (!tree_inited)
459 		return NULL;
460 
461 	if (node == pnode)
462 		return NULL;
463 
464 	return fdt_parent_node_recurse(pnode, node);
465 }
466 
467 int
468 fdt_node_is_compatible(void *node, const char *name)
469 {
470 	char *data;
471 	int len;
472 
473 	len = fdt_node_property(node, "compatible", &data);
474 	while (len > 0) {
475 		if (strcmp(data, name) == 0)
476 			return 1;
477 		len -= strlen(data) + 1;
478 		data += strlen(data) + 1;
479 	}
480 
481 	return 0;
482 }
483 
484 #ifdef DEBUG
485 /*
486  * Debug methods for printing whole tree, particular odes and properies
487  */
488 void *
489 fdt_print_property(void *node, int level)
490 {
491 	uint32_t *ptr;
492 	char *tmp, *value;
493 	int cnt;
494 	uint32_t nameid, size;
495 
496 	ptr = (uint32_t *)node;
497 
498 	if (!tree_inited)
499 		return NULL;
500 
501 	if (betoh32(*ptr) != FDT_PROPERTY)
502 		return ptr; /* should never happen */
503 
504 	/* extract property name_id and size */
505 	size = betoh32(*++ptr);
506 	nameid = betoh32(*++ptr);
507 
508 	for (cnt = 0; cnt < level; cnt++)
509 		printf("\t");
510 
511 	tmp = fdt_get_str(nameid);
512 	printf("\t%s : ", tmp ? tmp : "NO_NAME");
513 
514 	ptr++;
515 	value = (char *)ptr;
516 
517 	if (!strcmp(tmp, "device_type") || !strcmp(tmp, "compatible") ||
518 	    !strcmp(tmp, "model") || !strcmp(tmp, "bootargs") ||
519 	    !strcmp(tmp, "linux,stdout-path")) {
520 		printf("%s", value);
521 	} else if (!strcmp(tmp, "clock-frequency") ||
522 	    !strcmp(tmp, "timebase-frequency")) {
523 		printf("%d", betoh32(*((unsigned int *)value)));
524 	} else {
525 		for (cnt = 0; cnt < size; cnt++) {
526 			if ((cnt % sizeof(uint32_t)) == 0)
527 				printf(" ");
528 			printf("%x%x", value[cnt] >> 4, value[cnt] & 0xf);
529 		}
530 	}
531 	ptr += roundup(size, sizeof(uint32_t)) / sizeof(uint32_t);
532 	printf("\n");
533 
534 	return ptr;
535 }
536 
537 void
538 fdt_print_node(void *node, int level)
539 {
540 	uint32_t *ptr;
541 	int cnt;
542 
543 	ptr = (uint32_t *)node;
544 
545 	if (betoh32(*ptr) != FDT_NODE_BEGIN)
546 		return;
547 
548 	ptr++;
549 
550 	for (cnt = 0; cnt < level; cnt++)
551 		printf("\t");
552 	printf("%s :\n", fdt_node_name(node));
553 	ptr = skip_node_name(ptr);
554 
555 	while (betoh32(*ptr) == FDT_PROPERTY)
556 		ptr = fdt_print_property(ptr, level);
557 }
558 
559 void
560 fdt_print_node_recurse(void *node, int level)
561 {
562 	void *child;
563 
564 	fdt_print_node(node, level);
565 	for (child = fdt_child_node(node); child; child = fdt_next_node(child))
566 		fdt_print_node_recurse(child, level + 1);
567 }
568 
569 void
570 fdt_print_tree(void)
571 {
572 	fdt_print_node_recurse(fdt_next_node(0), 0);
573 }
574 #endif
575