blob: 5f77870e4dbde92775edbe5a81426ffe29341653 [file] [log] [blame]
Patrick Rudolph67aca3e2018-04-12 11:44:43 +02001/*
2 * Copyright 2013 Google Inc.
3 *
4 * See file CREDITS for list of people who contributed to this
5 * project.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but without any warranty; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <assert.h>
19#include <endian.h>
20#include <libpayload.h>
21#include <stdint.h>
22
23#include "base/device_tree.h"
24
25/*
26 * Functions for picking apart flattened trees.
27 */
28
29static uint32_t size32(uint32_t val)
30{
31 return (val + sizeof(uint32_t) - 1) / sizeof(uint32_t);
32}
33
34int fdt_next_property(void *blob, uint32_t offset, FdtProperty *prop)
35{
36 FdtHeader *header = (FdtHeader *)blob;
37 uint32_t *ptr = (uint32_t *)(((uint8_t *)blob) + offset);
38
39 int index = 0;
40 if (betohl(ptr[index++]) != TokenProperty)
41 return 0;
42
43 uint32_t size = betohl(ptr[index++]);
44 uint32_t name_offset = betohl(ptr[index++]);
45 name_offset += betohl(header->strings_offset);
46
47 if (prop) {
48 prop->name = (char *)((uint8_t *)blob + name_offset);
49 prop->data = &ptr[index];
50 prop->size = size;
51 }
52
53 index += size32(size);
54
55 return index * 4;
56}
57
58int fdt_node_name(void *blob, uint32_t offset, const char **name)
59{
60 uint8_t *ptr = ((uint8_t *)blob) + offset;
61
62 if (betohl(*(uint32_t *)ptr) != TokenBeginNode)
63 return 0;
64
65 ptr += 4;
66 if (name)
67 *name = (char *)ptr;
68 return size32(strlen((char *)ptr) + 1) * sizeof(uint32_t) + 4;
69}
70
71
72
73/*
74 * Functions for printing flattened trees.
75 */
76
77static void print_indent(int depth)
78{
79 while (depth--)
80 printf(" ");
81}
82
83static void print_property(FdtProperty *prop, int depth)
84{
85 print_indent(depth);
86 printf("prop \"%s\" (%d bytes).\n", prop->name, prop->size);
87 print_indent(depth + 1);
88 for (int i = 0; i < MIN(25, prop->size); i++) {
89 printf("%02x ", ((uint8_t *)prop->data)[i]);
90 }
91 if (prop->size > 25)
92 printf("...");
93 printf("\n");
94}
95
96static int print_flat_node(void *blob, uint32_t start_offset, int depth)
97{
98 int offset = start_offset;
99 const char *name;
100 int size;
101
102 size = fdt_node_name(blob, offset, &name);
103 if (!size)
104 return 0;
105 offset += size;
106
107 print_indent(depth);
108 printf("name = %s\n", name);
109
110 FdtProperty prop;
111 while ((size = fdt_next_property(blob, offset, &prop))) {
112 print_property(&prop, depth + 1);
113
114 offset += size;
115 }
116
117 while ((size = print_flat_node(blob, offset, depth + 1)))
118 offset += size;
119
120 return offset - start_offset + sizeof(uint32_t);
121}
122
123void fdt_print_node(void *blob, uint32_t offset)
124{
125 print_flat_node(blob, offset, 0);
126}
127
128
129
130/*
131 * A utility function to skip past nodes in flattened trees.
132 */
133
134int fdt_skip_node(void *blob, uint32_t start_offset)
135{
136 int offset = start_offset;
137 int size;
138
139 const char *name;
140 size = fdt_node_name(blob, offset, &name);
141 if (!size)
142 return 0;
143 offset += size;
144
145 while ((size = fdt_next_property(blob, offset, NULL)))
146 offset += size;
147
148 while ((size = fdt_skip_node(blob, offset)))
149 offset += size;
150
151 return offset - start_offset + sizeof(uint32_t);
152}
153
154
155
156/*
157 * Functions to turn a flattened tree into an unflattened one.
158 */
159
160static DeviceTreeNode node_cache[1000];
161static int node_counter = 0;
162static DeviceTreeProperty prop_cache[5000];
163static int prop_counter = 0;
164
165/*
166 * Libpayload's malloc() has linear allocation complexity and goes completely
167 * mental after a few thousand small requests. This little hack will absorb
168 * the worst of it to avoid increasing boot time for no reason.
169 */
170static DeviceTreeNode *alloc_node(void)
171{
172 if (node_counter >= ARRAY_SIZE(node_cache))
173 return xzalloc(sizeof(DeviceTreeNode));
174 return &node_cache[node_counter++];
175}
176static DeviceTreeProperty *alloc_prop(void)
177{
178 if (prop_counter >= ARRAY_SIZE(prop_cache))
179 return xzalloc(sizeof(DeviceTreeProperty));
180 return &prop_cache[prop_counter++];
181}
182
183static int fdt_unflatten_node(void *blob, uint32_t start_offset,
184 DeviceTreeNode **new_node)
185{
186 ListNode *last;
187 int offset = start_offset;
188 const char *name;
189 int size;
190
191 size = fdt_node_name(blob, offset, &name);
192 if (!size)
193 return 0;
194 offset += size;
195
196 DeviceTreeNode *node = alloc_node();
197 *new_node = node;
198 node->name = name;
199
200 FdtProperty fprop;
201 last = &node->properties;
202 while ((size = fdt_next_property(blob, offset, &fprop))) {
203 DeviceTreeProperty *prop = alloc_prop();
204 prop->prop = fprop;
205
206 list_insert_after(&prop->list_node, last);
207 last = &prop->list_node;
208
209 offset += size;
210 }
211
212 DeviceTreeNode *child;
213 last = &node->children;
214 while ((size = fdt_unflatten_node(blob, offset, &child))) {
215 list_insert_after(&child->list_node, last);
216 last = &child->list_node;
217
218 offset += size;
219 }
220
221 return offset - start_offset + sizeof(uint32_t);
222}
223
224static int fdt_unflatten_map_entry(void *blob, uint32_t offset,
225 DeviceTreeReserveMapEntry **new_entry)
226{
227 uint64_t *ptr = (uint64_t *)(((uint8_t *)blob) + offset);
228 uint64_t start = betohll(ptr[0]);
229 uint64_t size = betohll(ptr[1]);
230
231 if (!size)
232 return 0;
233
234 DeviceTreeReserveMapEntry *entry = xzalloc(sizeof(*entry));
235 *new_entry = entry;
236 entry->start = start;
237 entry->size = size;
238
239 return sizeof(uint64_t) * 2;
240}
241
242DeviceTree *fdt_unflatten(void *blob)
243{
244 DeviceTree *tree = xzalloc(sizeof(*tree));
245 FdtHeader *header = (FdtHeader *)blob;
246 tree->header = header;
247
248 uint32_t struct_offset = betohl(header->structure_offset);
249 uint32_t strings_offset = betohl(header->strings_offset);
250 uint32_t reserve_offset = betohl(header->reserve_map_offset);
251 uint32_t min_offset = 0;
252 min_offset = MIN(struct_offset, strings_offset);
253 min_offset = MIN(min_offset, reserve_offset);
254 // Assume everything up to the first non-header component is part of
255 // the header and needs to be preserved. This will protect us against
256 // new elements being added in the future.
257 tree->header_size = min_offset;
258
259 DeviceTreeReserveMapEntry *entry;
260 uint32_t offset = reserve_offset;
261 int size;
262 ListNode *last = &tree->reserve_map;
263 while ((size = fdt_unflatten_map_entry(blob, offset, &entry))) {
264 list_insert_after(&entry->list_node, last);
265 last = &entry->list_node;
266
267 offset += size;
268 }
269
270 fdt_unflatten_node(blob, struct_offset, &tree->root);
271
272 return tree;
273}
274
275
276
277/*
278 * Functions to find the size of device tree would take if it was flattened.
279 */
280
281static void dt_flat_prop_size(DeviceTreeProperty *prop, uint32_t *struct_size,
282 uint32_t *strings_size)
283{
284 // Starting token.
285 *struct_size += sizeof(uint32_t);
286 // Size.
287 *struct_size += sizeof(uint32_t);
288 // Name offset.
289 *struct_size += sizeof(uint32_t);
290 // Property value.
291 *struct_size += size32(prop->prop.size) * sizeof(uint32_t);
292
293 // Property name.
294 *strings_size += strlen(prop->prop.name) + 1;
295}
296
297static void dt_flat_node_size(DeviceTreeNode *node, uint32_t *struct_size,
298 uint32_t *strings_size)
299{
300 // Starting token.
301 *struct_size += sizeof(uint32_t);
302 // Node name.
303 *struct_size += size32(strlen(node->name) + 1) * sizeof(uint32_t);
304
305 DeviceTreeProperty *prop;
306 list_for_each(prop, node->properties, list_node)
307 dt_flat_prop_size(prop, struct_size, strings_size);
308
309 DeviceTreeNode *child;
310 list_for_each(child, node->children, list_node)
311 dt_flat_node_size(child, struct_size, strings_size);
312
313 // End token.
314 *struct_size += sizeof(uint32_t);
315}
316
317uint32_t dt_flat_size(DeviceTree *tree)
318{
319 uint32_t size = tree->header_size;
320 DeviceTreeReserveMapEntry *entry;
321 list_for_each(entry, tree->reserve_map, list_node)
322 size += sizeof(uint64_t) * 2;
323 size += sizeof(uint64_t) * 2;
324
325 uint32_t struct_size = 0;
326 uint32_t strings_size = 0;
327 dt_flat_node_size(tree->root, &struct_size, &strings_size);
328
329 size += struct_size;
330 // End token.
331 size += sizeof(uint32_t);
332
333 size += strings_size;
334
335 return size;
336}
337
338
339
340/*
341 * Functions to flatten a device tree.
342 */
343
344static void dt_flatten_map_entry(DeviceTreeReserveMapEntry *entry,
345 void **map_start)
346{
347 ((uint64_t *)*map_start)[0] = htobell(entry->start);
348 ((uint64_t *)*map_start)[1] = htobell(entry->size);
349 *map_start = ((uint8_t *)*map_start) + sizeof(uint64_t) * 2;
350}
351
352static void dt_flatten_prop(DeviceTreeProperty *prop, void **struct_start,
353 void *strings_base, void **strings_start)
354{
355 uint8_t *dstruct = (uint8_t *)*struct_start;
356 uint8_t *dstrings = (uint8_t *)*strings_start;
357
358 *((uint32_t *)dstruct) = htobel(TokenProperty);
359 dstruct += sizeof(uint32_t);
360
361 *((uint32_t *)dstruct) = htobel(prop->prop.size);
362 dstruct += sizeof(uint32_t);
363
364 uint32_t name_offset = (uintptr_t)dstrings - (uintptr_t)strings_base;
365 *((uint32_t *)dstruct) = htobel(name_offset);
366 dstruct += sizeof(uint32_t);
367
368 strcpy((char *)dstrings, prop->prop.name);
369 dstrings += strlen(prop->prop.name) + 1;
370
371 memcpy(dstruct, prop->prop.data, prop->prop.size);
372 dstruct += size32(prop->prop.size) * 4;
373
374 *struct_start = dstruct;
375 *strings_start = dstrings;
376}
377
378static void dt_flatten_node(DeviceTreeNode *node, void **struct_start,
379 void *strings_base, void **strings_start)
380{
381 uint8_t *dstruct = (uint8_t *)*struct_start;
382 uint8_t *dstrings = (uint8_t *)*strings_start;
383
384 *((uint32_t *)dstruct) = htobel(TokenBeginNode);
385 dstruct += sizeof(uint32_t);
386
387 strcpy((char *)dstruct, node->name);
388 dstruct += size32(strlen(node->name) + 1) * 4;
389
390 DeviceTreeProperty *prop;
391 list_for_each(prop, node->properties, list_node)
392 dt_flatten_prop(prop, (void **)&dstruct, strings_base,
393 (void **)&dstrings);
394
395 DeviceTreeNode *child;
396 list_for_each(child, node->children, list_node)
397 dt_flatten_node(child, (void **)&dstruct, strings_base,
398 (void **)&dstrings);
399
400 *((uint32_t *)dstruct) = htobel(TokenEndNode);
401 dstruct += sizeof(uint32_t);
402
403 *struct_start = dstruct;
404 *strings_start = dstrings;
405}
406
407void dt_flatten(DeviceTree *tree, void *start_dest)
408{
409 uint8_t *dest = (uint8_t *)start_dest;
410
411 memcpy(dest, tree->header, tree->header_size);
412 FdtHeader *header = (FdtHeader *)dest;
413 dest += tree->header_size;
414
415 DeviceTreeReserveMapEntry *entry;
416 list_for_each(entry, tree->reserve_map, list_node)
417 dt_flatten_map_entry(entry, (void **)&dest);
418 ((uint64_t *)dest)[0] = ((uint64_t *)dest)[1] = 0;
419 dest += sizeof(uint64_t) * 2;
420
421 uint32_t struct_size = 0;
422 uint32_t strings_size = 0;
423 dt_flat_node_size(tree->root, &struct_size, &strings_size);
424
425 uint8_t *struct_start = dest;
426 header->structure_offset = htobel(dest - (uint8_t *)start_dest);
427 header->structure_size = htobel(struct_size);
428 dest += struct_size;
429
430 *((uint32_t *)dest) = htobel(TokenEnd);
431 dest += sizeof(uint32_t);
432
433 uint8_t *strings_start = dest;
434 header->strings_offset = htobel(dest - (uint8_t *)start_dest);
435 header->strings_size = htobel(strings_size);
436 dest += strings_size;
437
438 dt_flatten_node(tree->root, (void **)&struct_start, strings_start,
439 (void **)&strings_start);
440
441 header->totalsize = htobel(dest - (uint8_t *)start_dest);
442}
443
444
445
446/*
447 * Functions for printing a non-flattened device tree.
448 */
449
450static void print_node(DeviceTreeNode *node, int depth)
451{
452 print_indent(depth);
453 printf("name = %s\n", node->name);
454
455 DeviceTreeProperty *prop;
456 list_for_each(prop, node->properties, list_node)
457 print_property(&prop->prop, depth + 1);
458
459 DeviceTreeNode *child;
460 list_for_each(child, node->children, list_node)
461 print_node(child, depth + 1);
462}
463
464void dt_print_node(DeviceTreeNode *node)
465{
466 print_node(node, 0);
467}
468
469
470
471/*
472 * Functions for reading and manipulating an unflattened device tree.
473 */
474
475/*
476 * Read #address-cells and #size-cells properties from a node.
477 *
478 * @param node The device tree node to read from.
479 * @param addrcp Pointer to store #address-cells in, skipped if NULL.
480 * @param sizecp Pointer to store #size-cells in, skipped if NULL.
481 */
482void dt_read_cell_props(DeviceTreeNode *node, u32 *addrcp, u32 *sizecp)
483{
484 DeviceTreeProperty *prop;
485 list_for_each(prop, node->properties, list_node) {
486 if (addrcp && !strcmp("#address-cells", prop->prop.name))
487 *addrcp = betohl(*(u32 *)prop->prop.data);
488 if (sizecp && !strcmp("#size-cells", prop->prop.name))
489 *sizecp = betohl(*(u32 *)prop->prop.data);
490 }
491}
492
493/*
494 * Find a node from a device tree path, relative to a parent node.
495 *
496 * @param parent The node from which to start the relative path lookup.
497 * @param path An array of path component strings that will be looked
498 * up in order to find the node. Must be terminated with
499 * a NULL pointer. Example: {'firmware', 'coreboot', NULL}
500 * @param addrcp Pointer that will be updated with any #address-cells
501 * value found in the path. May be NULL to ignore.
502 * @param sizecp Pointer that will be updated with any #size-cells
503 * value found in the path. May be NULL to ignore.
504 * @param create 1: Create node(s) if not found. 0: Return NULL instead.
505 * @return The found/created node, or NULL.
506 */
507DeviceTreeNode *dt_find_node(DeviceTreeNode *parent, const char **path,
508 u32 *addrcp, u32 *sizecp, int create)
509{
510 DeviceTreeNode *node, *found = NULL;
511
512 // Update #address-cells and #size-cells for this level.
513 dt_read_cell_props(parent, addrcp, sizecp);
514
515 if (!*path)
516 return parent;
517
518 // Find the next node in the path, if it exists.
519 list_for_each(node, parent->children, list_node) {
520 if (!strcmp(node->name, *path)) {
521 found = node;
522 break;
523 }
524 }
525
526 // Otherwise create it or return NULL.
527 if (!found) {
528 if (!create)
529 return NULL;
530
531 found = alloc_node();
532 found->name = strdup(*path);
533 if (!found->name)
534 return NULL;
535
536 list_insert_after(&found->list_node, &parent->children);
537 }
538
539 return dt_find_node(found, path + 1, addrcp, sizecp, create);
540}
541
542/*
543 * Find a node from a string device tree path, relative to a parent node.
544 *
545 * @param parent The node from which to start the relative path lookup.
546 * @param path A string representing a path in the device tree, with
547 * nodes separated by '/'. Example: "soc/firmware/coreboot"
548 * @param addrcp Pointer that will be updated with any #address-cells
549 * value found in the path. May be NULL to ignore.
550 * @param sizecp Pointer that will be updated with any #size-cells
551 * value found in the path. May be NULL to ignore.
552 * @param create 1: Create node(s) if not found. 0: Return NULL instead.
553 * @return The found/created node, or NULL.
554 *
555 * It is the caller responsibility to provide the correct path string, namely
556 * not starting or ending with a '/', and not having "//" anywhere in it.
557 */
558DeviceTreeNode *dt_find_node_by_path(DeviceTreeNode *parent, const char *path,
559 u32 *addrcp, u32 *sizecp, int create)
560{
561 char *dup_path = strdup(path);
562 /* Hopefully enough depth for any node. */
563 const char *path_array[15];
564 int i;
565 char *next_slash;
566 DeviceTreeNode *node = NULL;
567
568 if (!dup_path)
569 return NULL;
570
571 next_slash = dup_path;
572 path_array[0] = dup_path;
573 for (i = 1; i < (ARRAY_SIZE(path_array) - 1); i++) {
574
575 next_slash = strchr(next_slash, '/');
576 if (!next_slash)
577 break;
578
579 *next_slash++ = '\0';
580 path_array[i] = next_slash;
581 }
582
583 if (!next_slash) {
584 path_array[i] = NULL;
585 node = dt_find_node(parent, path_array,
586 addrcp, sizecp, create);
587 }
588
589 free(dup_path);
590 return node;
591}
592
593/*
594 * Check if given node is compatible.
595 *
596 * @param node The node which is to be checked for compatible property.
597 * @param compat The compatible string to match.
598 * @return 1 = compatible, 0 = not compatible.
599 */
600static int dt_check_compat_match(DeviceTreeNode *node, const char *compat)
601{
602 DeviceTreeProperty *prop;
603
604 list_for_each(prop, node->properties, list_node) {
605 if (!strcmp("compatible", prop->prop.name)) {
606 size_t bytes = prop->prop.size;
607 const char *str = prop->prop.data;
608 while (bytes > 0) {
609 if (!strncmp(compat, str, bytes))
610 return 1;
611 size_t len = strnlen(str, bytes) + 1;
612 if (bytes <= len)
613 break;
614 str += len;
615 bytes -= len;
616 }
617 break;
618 }
619 }
620
621 return 0;
622}
623
624/*
625 * Find a node from a compatible string, in the subtree of a parent node.
626 *
627 * @param parent The parent node under which to look.
628 * @param compat The compatible string to find.
629 * @return The found node, or NULL.
630 */
631DeviceTreeNode *dt_find_compat(DeviceTreeNode *parent, const char *compat)
632{
633 // Check if the parent node itself is compatible.
634 if (dt_check_compat_match(parent, compat))
635 return parent;
636
637 DeviceTreeNode *child;
638 list_for_each(child, parent->children, list_node) {
639 DeviceTreeNode *found = dt_find_compat(child, compat);
640 if (found)
641 return found;
642 }
643
644 return NULL;
645}
646
647/*
648 * Find the next compatible child of a given parent. All children upto the
649 * child passed in by caller are ignored. If child is NULL, it considers all the
650 * children to find the first child which is compatible.
651 *
652 * @param parent The parent node under which to look.
653 * @param child The child node to start search from (exclusive). If NULL
654 * consider all children.
655 * @param compat The compatible string to find.
656 * @return The found node, or NULL.
657 */
658DeviceTreeNode *dt_find_next_compat_child(DeviceTreeNode *parent,
659 DeviceTreeNode *child,
660 const char *compat)
661{
662 DeviceTreeNode *next;
663 int ignore = 0;
664
665 if (child)
666 ignore = 1;
667
668 list_for_each(next, parent->children, list_node) {
669 if (ignore) {
670 if (child == next)
671 ignore = 0;
672 continue;
673 }
674
675 if (dt_check_compat_match(next, compat))
676 return next;
677 }
678
679 return NULL;
680}
681
682/*
683 * Find a node with matching property value, in the subtree of a parent node.
684 *
685 * @param parent The parent node under which to look.
686 * @param name The property name to look for.
687 * @param data The property value to look for.
688 * @param size The property size.
689 */
690DeviceTreeNode *dt_find_prop_value(DeviceTreeNode *parent, const char *name,
691 void *data, size_t size)
692{
693 DeviceTreeProperty *prop;
694
695 /* Check if parent itself has the required property value. */
696 list_for_each(prop, parent->properties, list_node) {
697 if (!strcmp(name, prop->prop.name)) {
698 size_t bytes = prop->prop.size;
699 void *prop_data = prop->prop.data;
700 if (size != bytes)
701 break;
702 if (!memcmp(data, prop_data, size))
703 return parent;
704 break;
705 }
706 }
707
708 DeviceTreeNode *child;
709 list_for_each(child, parent->children, list_node) {
710 DeviceTreeNode *found = dt_find_prop_value(child, name, data,
711 size);
712 if (found)
713 return found;
714 }
715 return NULL;
716}
717
718/*
719 * Write an arbitrary sized big-endian integer into a pointer.
720 *
721 * @param dest Pointer to the DT property data buffer to write.
722 * @param src The integer to write (in CPU endianess).
723 * @param length the length of the destination integer in bytes.
724 */
725void dt_write_int(u8 *dest, u64 src, size_t length)
726{
727 while (length--) {
728 dest[length] = (u8)src;
729 src >>= 8;
730 }
731}
732
733/*
734 * Add an arbitrary property to a node, or update it if it already exists.
735 *
736 * @param node The device tree node to add to.
737 * @param name The name of the new property.
738 * @param data The raw data blob to be stored in the property.
739 * @param size The size of data in bytes.
740 */
741void dt_add_bin_prop(DeviceTreeNode *node, char *name, void *data, size_t size)
742{
743 DeviceTreeProperty *prop;
744
745 list_for_each(prop, node->properties, list_node) {
746 if (!strcmp(prop->prop.name, name)) {
747 prop->prop.data = data;
748 prop->prop.size = size;
749 return;
750 }
751 }
752
753 prop = alloc_prop();
754 list_insert_after(&prop->list_node, &node->properties);
755 prop->prop.name = name;
756 prop->prop.data = data;
757 prop->prop.size = size;
758}
759
760/*
761 * Find given string property in a node and return its content.
762 *
763 * @param node The device tree node to search.
764 * @param name The name of the property.
765 * @return The found string, or NULL.
766 */
767const char *dt_find_string_prop(DeviceTreeNode *node, const char *name)
768{
769 void *content;
770 size_t size;
771
772 dt_find_bin_prop(node, name, &content, &size);
773
774 return content;
775}
776
777/*
778 * Find given property in a node.
779 *
780 * @param node The device tree node to search.
781 * @param name The name of the property.
782 * @param data Pointer to return raw data blob in the property.
783 * @param size Pointer to return the size of data in bytes.
784 */
785void dt_find_bin_prop(DeviceTreeNode *node, const char *name, void **data,
786 size_t *size)
787{
788 DeviceTreeProperty *prop;
789
790 *data = NULL;
791 *size = 0;
792
793 list_for_each(prop, node->properties, list_node) {
794 if (!strcmp(prop->prop.name, name)) {
795 *data = prop->prop.data;
796 *size = prop->prop.size;
797 return;
798 }
799 }
800}
801
802/*
803 * Add a string property to a node, or update it if it already exists.
804 *
805 * @param node The device tree node to add to.
806 * @param name The name of the new property.
807 * @param str The zero-terminated string to be stored in the property.
808 */
809void dt_add_string_prop(DeviceTreeNode *node, char *name, char *str)
810{
811 dt_add_bin_prop(node, name, str, strlen(str) + 1);
812}
813
814/*
815 * Add a 32-bit integer property to a node, or update it if it already exists.
816 *
817 * @param node The device tree node to add to.
818 * @param name The name of the new property.
819 * @param val The integer to be stored in the property.
820 */
821void dt_add_u32_prop(DeviceTreeNode *node, char *name, u32 val)
822{
823 u32 *val_ptr = xmalloc(sizeof(val));
824 *val_ptr = htobel(val);
825 dt_add_bin_prop(node, name, val_ptr, sizeof(*val_ptr));
826}
827
828/*
829 * Add a 'reg' address list property to a node, or update it if it exists.
830 *
831 * @param node The device tree node to add to.
832 * @param addrs Array of address values to be stored in the property.
833 * @param sizes Array of corresponding size values to 'addrs'.
834 * @param count Number of values in 'addrs' and 'sizes' (must be equal).
835 * @param addr_cells Value of #address-cells property valid for this node.
836 * @param size_cells Value of #size-cells property valid for this node.
837 */
838void dt_add_reg_prop(DeviceTreeNode *node, u64 *addrs, u64 *sizes,
839 int count, u32 addr_cells, u32 size_cells)
840{
841 int i;
842 size_t length = (addr_cells + size_cells) * sizeof(u32) * count;
843 u8 *data = xmalloc(length);
844 u8 *cur = data;
845
846 for (i = 0; i < count; i++) {
847 dt_write_int(cur, addrs[i], addr_cells * sizeof(u32));
848 cur += addr_cells * sizeof(u32);
849 dt_write_int(cur, sizes[i], size_cells * sizeof(u32));
850 cur += size_cells * sizeof(u32);
851 }
852
853 dt_add_bin_prop(node, "reg", data, length);
854}
855
856/*
857 * Fixups to apply to a kernel's device tree before booting it.
858 */
859
860ListNode device_tree_fixups;
861
862int dt_apply_fixups(DeviceTree *tree)
863{
864 DeviceTreeFixup *fixup;
865 list_for_each(fixup, device_tree_fixups, list_node) {
866 assert(fixup->fixup);
867 if (fixup->fixup(fixup, tree))
868 return 1;
869 }
870 return 0;
871}
872
873int dt_set_bin_prop_by_path(DeviceTree *tree, const char *path,
874 void *data, size_t data_size, int create)
875{
876 char *path_copy, *prop_name;
877 DeviceTreeNode *dt_node;
878
879 path_copy = strdup(path);
880
881 if (!path_copy) {
882 printf("Failed to allocate a copy of path %s\n", path);
883 return 1;
884 }
885
886 prop_name = strrchr(path_copy, '/');
887 if (!prop_name) {
888 printf("Path %s does not include '/'\n", path);
889 free(path_copy);
890 return 1;
891 }
892
893 *prop_name++ = '\0'; /* Separate path from the property name. */
894
895 dt_node = dt_find_node_by_path(tree->root, path_copy, NULL,
896 NULL, create);
897
898 if (!dt_node) {
899 printf("Failed to %s %s in the device tree\n",
900 create ? "create" : "find", path_copy);
901 free(path_copy);
902 return 1;
903 }
904
905 dt_add_bin_prop(dt_node, prop_name, data, data_size);
906
907 return 0;
908}
909
910/*
911 * Prepare the /reserved-memory/ node.
912 *
913 * Technically, this can be called more than one time, to init and/or retrieve
914 * the node. But dt_add_u32_prop() may leak a bit of memory if you do.
915 *
916 * @tree: Device tree to add/retrieve from.
917 * @return: The /reserved-memory/ node (or NULL, if error).
918 */
919DeviceTreeNode *dt_init_reserved_memory_node(DeviceTree *tree)
920{
921 DeviceTreeNode *reserved;
922 u32 addr = 0, size = 0;
923
924 reserved = dt_find_node_by_path(tree->root, "reserved-memory", &addr,
925 &size, 1);
926 if (!reserved)
927 return NULL;
928
929 // Binding doc says this should have the same #{address,size}-cells as
930 // the root.
931 dt_add_u32_prop(reserved, "#address-cells", addr);
932 dt_add_u32_prop(reserved, "#size-cells", size);
933 // Binding doc says this should be empty (i.e., 1:1 mapping from root).
934 dt_add_bin_prop(reserved, "ranges", NULL, 0);
935
936 return reserved;
937}