1 /*
2 * Early Device Tree
3 *
4 * Copyright (C) 2012-2014 Citrix Systems, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10 #include <xen/types.h>
11 #include <xen/lib.h>
12 #include <xen/kernel.h>
13 #include <xen/init.h>
14 #include <xen/device_tree.h>
15 #include <xen/libfdt/libfdt.h>
16 #include <xsm/xsm.h>
17 #include <asm/setup.h>
18
device_tree_node_matches(const void * fdt,int node,const char * match)19 static bool __init device_tree_node_matches(const void *fdt, int node,
20 const char *match)
21 {
22 const char *name;
23 size_t match_len;
24
25 name = fdt_get_name(fdt, node, NULL);
26 match_len = strlen(match);
27
28 /* Match both "match" and "match@..." patterns but not
29 "match-foo". */
30 return strncmp(name, match, match_len) == 0
31 && (name[match_len] == '@' || name[match_len] == '\0');
32 }
33
device_tree_node_compatible(const void * fdt,int node,const char * match)34 static bool __init device_tree_node_compatible(const void *fdt, int node,
35 const char *match)
36 {
37 int len, l;
38 int mlen;
39 const void *prop;
40
41 mlen = strlen(match);
42
43 prop = fdt_getprop(fdt, node, "compatible", &len);
44 if ( prop == NULL )
45 return false;
46
47 while ( len > 0 ) {
48 if ( !dt_compat_cmp(prop, match) )
49 return true;
50 l = strlen(prop) + 1;
51 prop += l;
52 len -= l;
53 }
54
55 return false;
56 }
57
device_tree_get_reg(const __be32 ** cell,u32 address_cells,u32 size_cells,u64 * start,u64 * size)58 static void __init device_tree_get_reg(const __be32 **cell, u32 address_cells,
59 u32 size_cells, u64 *start, u64 *size)
60 {
61 *start = dt_next_cell(address_cells, cell);
62 *size = dt_next_cell(size_cells, cell);
63 }
64
device_tree_get_u32(const void * fdt,int node,const char * prop_name,u32 dflt)65 static u32 __init device_tree_get_u32(const void *fdt, int node,
66 const char *prop_name, u32 dflt)
67 {
68 const struct fdt_property *prop;
69
70 prop = fdt_get_property(fdt, node, prop_name, NULL);
71 if ( !prop || prop->len < sizeof(u32) )
72 return dflt;
73
74 return fdt32_to_cpu(*(uint32_t*)prop->data);
75 }
76
77 /**
78 * device_tree_for_each_node - iterate over all device tree nodes
79 * @fdt: flat device tree.
80 * @func: function to call for each node.
81 * @data: data to pass to @func.
82 *
83 * Any nodes nested at DEVICE_TREE_MAX_DEPTH or deeper are ignored.
84 *
85 * Returns 0 if all nodes were iterated over successfully. If @func
86 * returns a value different from 0, that value is returned immediately.
87 */
device_tree_for_each_node(const void * fdt,device_tree_node_func func,void * data)88 int __init device_tree_for_each_node(const void *fdt,
89 device_tree_node_func func,
90 void *data)
91 {
92 int node;
93 int depth;
94 u32 address_cells[DEVICE_TREE_MAX_DEPTH];
95 u32 size_cells[DEVICE_TREE_MAX_DEPTH];
96 int ret;
97
98 for ( node = 0, depth = 0;
99 node >=0 && depth >= 0;
100 node = fdt_next_node(fdt, node, &depth) )
101 {
102 const char *name = fdt_get_name(fdt, node, NULL);
103 u32 as, ss;
104
105 if ( depth >= DEVICE_TREE_MAX_DEPTH )
106 {
107 printk("Warning: device tree node `%s' is nested too deep\n",
108 name);
109 continue;
110 }
111
112 as = depth > 0 ? address_cells[depth-1] : 0;
113 ss = depth > 0 ? size_cells[depth-1] : 0;
114
115 address_cells[depth] = device_tree_get_u32(fdt, node,
116 "#address-cells", as);
117 size_cells[depth] = device_tree_get_u32(fdt, node,
118 "#size-cells", ss);
119
120 ret = func(fdt, node, name, depth, as, ss, data);
121 if ( ret != 0 )
122 return ret;
123 }
124 return 0;
125 }
126
process_memory_node(const void * fdt,int node,const char * name,u32 address_cells,u32 size_cells)127 static void __init process_memory_node(const void *fdt, int node,
128 const char *name,
129 u32 address_cells, u32 size_cells)
130 {
131 const struct fdt_property *prop;
132 int i;
133 int banks;
134 const __be32 *cell;
135 paddr_t start, size;
136 u32 reg_cells = address_cells + size_cells;
137
138 if ( address_cells < 1 || size_cells < 1 )
139 {
140 printk("fdt: node `%s': invalid #address-cells or #size-cells",
141 name);
142 return;
143 }
144
145 prop = fdt_get_property(fdt, node, "reg", NULL);
146 if ( !prop )
147 {
148 printk("fdt: node `%s': missing `reg' property\n", name);
149 return;
150 }
151
152 cell = (const __be32 *)prop->data;
153 banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32));
154
155 for ( i = 0; i < banks && bootinfo.mem.nr_banks < NR_MEM_BANKS; i++ )
156 {
157 device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
158 if ( !size )
159 continue;
160 bootinfo.mem.bank[bootinfo.mem.nr_banks].start = start;
161 bootinfo.mem.bank[bootinfo.mem.nr_banks].size = size;
162 bootinfo.mem.nr_banks++;
163 }
164 }
165
process_multiboot_node(const void * fdt,int node,const char * name,u32 address_cells,u32 size_cells)166 static void __init process_multiboot_node(const void *fdt, int node,
167 const char *name,
168 u32 address_cells, u32 size_cells)
169 {
170 static int __initdata kind_guess = 0;
171 const struct fdt_property *prop;
172 const __be32 *cell;
173 bootmodule_kind kind;
174 paddr_t start, size;
175 const char *cmdline;
176 int len;
177
178 prop = fdt_get_property(fdt, node, "reg", &len);
179 if ( !prop )
180 panic("node %s missing `reg' property\n", name);
181
182 if ( len < dt_cells_to_size(address_cells + size_cells) )
183 panic("fdt: node `%s': `reg` property length is too short\n",
184 name);
185
186 cell = (const __be32 *)prop->data;
187 device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
188
189 if ( fdt_node_check_compatible(fdt, node, "xen,linux-zimage") == 0 ||
190 fdt_node_check_compatible(fdt, node, "multiboot,kernel") == 0 )
191 kind = BOOTMOD_KERNEL;
192 else if ( fdt_node_check_compatible(fdt, node, "xen,linux-initrd") == 0 ||
193 fdt_node_check_compatible(fdt, node, "multiboot,ramdisk") == 0 )
194 kind = BOOTMOD_RAMDISK;
195 else if ( fdt_node_check_compatible(fdt, node, "xen,xsm-policy") == 0 )
196 kind = BOOTMOD_XSM;
197 else
198 kind = BOOTMOD_UNKNOWN;
199
200 /**
201 * Guess the kind of these first two unknowns respectively:
202 * (1) The first unknown must be kernel.
203 * (2) Detect the XSM Magic from the 2nd unknown:
204 * a. If it's XSM, set the kind as XSM, and that also means we
205 * won't load ramdisk;
206 * b. if it's not XSM, set the kind as ramdisk.
207 * So if user want to load ramdisk, it must be the 2nd unknown.
208 * We also detect the XSM Magic for the following unknowns,
209 * then set its kind according to the return value of has_xsm_magic.
210 */
211 if ( kind == BOOTMOD_UNKNOWN )
212 {
213 switch ( kind_guess++ )
214 {
215 case 0: kind = BOOTMOD_KERNEL; break;
216 case 1: kind = BOOTMOD_RAMDISK; break;
217 default: break;
218 }
219 if ( kind_guess > 1 && has_xsm_magic(start) )
220 kind = BOOTMOD_XSM;
221 }
222
223 prop = fdt_get_property(fdt, node, "bootargs", &len);
224 if ( prop )
225 {
226 if ( len > BOOTMOD_MAX_CMDLINE )
227 panic("module %s command line too long\n", name);
228 cmdline = prop->data;
229 }
230 else
231 cmdline = NULL;
232
233 add_boot_module(kind, start, size, cmdline);
234 }
235
process_chosen_node(const void * fdt,int node,const char * name,u32 address_cells,u32 size_cells)236 static void __init process_chosen_node(const void *fdt, int node,
237 const char *name,
238 u32 address_cells, u32 size_cells)
239 {
240 const struct fdt_property *prop;
241 paddr_t start, end;
242 int len;
243
244 printk("Checking for initrd in /chosen\n");
245
246 prop = fdt_get_property(fdt, node, "linux,initrd-start", &len);
247 if ( !prop )
248 /* No initrd present. */
249 return;
250 if ( len != sizeof(u32) && len != sizeof(u64) )
251 {
252 printk("linux,initrd-start property has invalid length %d\n", len);
253 return;
254 }
255 start = dt_read_number((void *)&prop->data, dt_size_to_cells(len));
256
257 prop = fdt_get_property(fdt, node, "linux,initrd-end", &len);
258 if ( !prop )
259 {
260 printk("linux,initrd-end not present but -start was\n");
261 return;
262 }
263 if ( len != sizeof(u32) && len != sizeof(u64) )
264 {
265 printk("linux,initrd-end property has invalid length %d\n", len);
266 return;
267 }
268 end = dt_read_number((void *)&prop->data, dt_size_to_cells(len));
269
270 if ( start >= end )
271 {
272 printk("linux,initrd limits invalid: %"PRIpaddr" >= %"PRIpaddr"\n",
273 start, end);
274 return;
275 }
276
277 printk("Initrd %"PRIpaddr"-%"PRIpaddr"\n", start, end);
278
279 add_boot_module(BOOTMOD_RAMDISK, start, end-start, NULL);
280 }
281
early_scan_node(const void * fdt,int node,const char * name,int depth,u32 address_cells,u32 size_cells,void * data)282 static int __init early_scan_node(const void *fdt,
283 int node, const char *name, int depth,
284 u32 address_cells, u32 size_cells,
285 void *data)
286 {
287 if ( device_tree_node_matches(fdt, node, "memory") )
288 process_memory_node(fdt, node, name, address_cells, size_cells);
289 else if ( device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) ||
290 device_tree_node_compatible(fdt, node, "multiboot,module" ))
291 process_multiboot_node(fdt, node, name, address_cells, size_cells);
292 else if ( depth == 1 && device_tree_node_matches(fdt, node, "chosen") )
293 process_chosen_node(fdt, node, name, address_cells, size_cells);
294
295 return 0;
296 }
297
early_print_info(void)298 static void __init early_print_info(void)
299 {
300 struct meminfo *mi = &bootinfo.mem;
301 struct bootmodules *mods = &bootinfo.modules;
302 int i, nr_rsvd;
303
304 for ( i = 0; i < mi->nr_banks; i++ )
305 printk("RAM: %"PRIpaddr" - %"PRIpaddr"\n",
306 mi->bank[i].start,
307 mi->bank[i].start + mi->bank[i].size - 1);
308 printk("\n");
309 for ( i = 0 ; i < mods->nr_mods; i++ )
310 printk("MODULE[%d]: %"PRIpaddr" - %"PRIpaddr" %-12s %s\n",
311 i,
312 mods->module[i].start,
313 mods->module[i].start + mods->module[i].size,
314 boot_module_kind_as_string(mods->module[i].kind),
315 mods->module[i].cmdline);
316 nr_rsvd = fdt_num_mem_rsv(device_tree_flattened);
317 for ( i = 0; i < nr_rsvd; i++ )
318 {
319 paddr_t s, e;
320 if ( fdt_get_mem_rsv(device_tree_flattened, i, &s, &e) < 0 )
321 continue;
322 /* fdt_get_mem_rsv returns length */
323 e += s;
324 printk(" RESVD[%d]: %"PRIpaddr" - %"PRIpaddr"\n",
325 i, s, e);
326 }
327 printk("\n");
328 }
329
330 /**
331 * boot_fdt_info - initialize bootinfo from a DTB
332 * @fdt: flattened device tree binary
333 *
334 * Returns the size of the DTB.
335 */
boot_fdt_info(const void * fdt,paddr_t paddr)336 size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)
337 {
338 int ret;
339
340 ret = fdt_check_header(fdt);
341 if ( ret < 0 )
342 panic("No valid device tree\n");
343
344 add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), NULL);
345
346 device_tree_for_each_node((void *)fdt, early_scan_node, NULL);
347 early_print_info();
348
349 return fdt_totalsize(fdt);
350 }
351
boot_fdt_cmdline(const void * fdt)352 const char *boot_fdt_cmdline(const void *fdt)
353 {
354 int node;
355 const struct fdt_property *prop;
356
357 node = fdt_path_offset(fdt, "/chosen");
358 if ( node < 0 )
359 return NULL;
360
361 prop = fdt_get_property(fdt, node, "xen,xen-bootargs", NULL);
362 if ( prop == NULL )
363 {
364 struct bootmodule *dom0_mod =
365 boot_module_find_by_kind(BOOTMOD_KERNEL);
366
367 if (fdt_get_property(fdt, node, "xen,dom0-bootargs", NULL) ||
368 ( dom0_mod && dom0_mod->cmdline[0] ) )
369 prop = fdt_get_property(fdt, node, "bootargs", NULL);
370 }
371 if ( prop == NULL )
372 return NULL;
373
374 return prop->data;
375 }
376
377 /*
378 * Local variables:
379 * mode: C
380 * c-file-style: "BSD"
381 * c-basic-offset: 4
382 * indent-tabs-mode: nil
383 * End:
384 */
385