1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #include <xen/bootfdt.h>
3 #include <xen/bug.h>
4 #include <xen/lib.h>
5 #include <xen/libfdt/libfdt.h>
6 #include <xen/unaligned.h>
7
fdt_node_to_kind(const void * fdt,int node)8 boot_module_kind __init fdt_node_to_kind(const void *fdt, int node)
9 {
10 if ( fdt_node_check_compatible(fdt, node, "xen,linux-zimage") == 0 ||
11 fdt_node_check_compatible(fdt, node, "multiboot,kernel") == 0 )
12 return BOOTMOD_KERNEL;
13 if ( fdt_node_check_compatible(fdt, node, "xen,linux-initrd") == 0 ||
14 fdt_node_check_compatible(fdt, node, "multiboot,ramdisk") == 0 )
15 return BOOTMOD_RAMDISK;
16 if ( fdt_node_check_compatible(fdt, node, "xen,xsm-policy") == 0 )
17 return BOOTMOD_XSM_POLICY;
18 if ( fdt_node_check_compatible(fdt, node, "multiboot,device-tree") == 0 )
19 return BOOTMOD_GUEST_DTB;
20 if ( fdt_node_check_compatible(fdt, node, "multiboot,microcode") == 0 )
21 return BOOTMOD_MICROCODE;
22
23 return BOOTMOD_UNKNOWN;
24 }
25
device_tree_get_reg(const __be32 ** cell,uint32_t address_cells,uint32_t size_cells,paddr_t * start,paddr_t * size)26 void __init device_tree_get_reg(const __be32 **cell, uint32_t address_cells,
27 uint32_t size_cells, paddr_t *start,
28 paddr_t *size)
29 {
30 uint64_t dt_start, dt_size;
31
32 /*
33 * dt_next_cell will return uint64_t whereas paddr_t may not be 64-bit.
34 * Thus, there is an implicit cast from uint64_t to paddr_t.
35 */
36 dt_start = dt_next_cell(address_cells, cell);
37 dt_size = dt_next_cell(size_cells, cell);
38
39 if ( dt_start != (paddr_t)dt_start )
40 {
41 printk("Physical address greater than max width supported\n");
42 WARN();
43 }
44
45 if ( dt_size != (paddr_t)dt_size )
46 {
47 printk("Physical size greater than max width supported\n");
48 WARN();
49 }
50
51 /*
52 * Xen will truncate the address/size if it is greater than the maximum
53 * supported width and it will give an appropriate warning.
54 */
55 *start = dt_start;
56 *size = dt_size;
57 }
58
device_tree_get_u32(const void * fdt,int node,const char * prop_name,u32 dflt)59 u32 __init device_tree_get_u32(const void *fdt, int node,
60 const char *prop_name, u32 dflt)
61 {
62 const struct fdt_property *prop;
63
64 prop = fdt_get_property(fdt, node, prop_name, NULL);
65 if ( !prop || prop->len < sizeof(u32) )
66 return dflt;
67
68 return fdt32_to_cpu(get_unaligned_t(uint32_t, prop->data));
69 }
70
device_tree_for_each_node(const void * fdt,int node,device_tree_node_func func,void * data)71 int __init device_tree_for_each_node(const void *fdt, int node,
72 device_tree_node_func func,
73 void *data)
74 {
75 /*
76 * We only care about relative depth increments, assume depth of
77 * node is 0 for simplicity.
78 */
79 int depth = 0;
80 const int first_node = node;
81 u32 address_cells[DEVICE_TREE_MAX_DEPTH];
82 u32 size_cells[DEVICE_TREE_MAX_DEPTH];
83 int ret;
84
85 do {
86 const char *name = fdt_get_name(fdt, node, NULL);
87 u32 as, ss;
88
89 if ( depth >= DEVICE_TREE_MAX_DEPTH )
90 {
91 printk("Warning: device tree node `%s' is nested too deep\n",
92 name);
93 continue;
94 }
95
96 as = depth > 0 ? address_cells[depth-1] : DT_ROOT_NODE_ADDR_CELLS_DEFAULT;
97 ss = depth > 0 ? size_cells[depth-1] : DT_ROOT_NODE_SIZE_CELLS_DEFAULT;
98
99 address_cells[depth] = device_tree_get_u32(fdt, node,
100 "#address-cells", as);
101 size_cells[depth] = device_tree_get_u32(fdt, node,
102 "#size-cells", ss);
103
104 /* skip the first node */
105 if ( node != first_node )
106 {
107 ret = func(fdt, node, name, depth, as, ss, data);
108 if ( ret != 0 )
109 return ret;
110 }
111
112 node = fdt_next_node(fdt, node, &depth);
113 } while ( node >= 0 && depth > 0 );
114
115 return 0;
116 }
117
118