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 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 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 sub-nodes
79  * @fdt: flat device tree.
80  * @node: parent node to start the search from
81  * @func: function to call for each sub-node.
82  * @data: data to pass to @func.
83  *
84  * Any nodes nested at DEVICE_TREE_MAX_DEPTH or deeper are ignored.
85  *
86  * Returns 0 if all nodes were iterated over successfully.  If @func
87  * returns a value different from 0, that value is returned immediately.
88  */
device_tree_for_each_node(const void * fdt,int node,device_tree_node_func func,void * data)89 int __init device_tree_for_each_node(const void *fdt, int node,
90                                      device_tree_node_func func,
91                                      void *data)
92 {
93     /*
94      * We only care about relative depth increments, assume depth of
95      * node is 0 for simplicity.
96      */
97     int depth = 0;
98     const int first_node = node;
99     u32 address_cells[DEVICE_TREE_MAX_DEPTH];
100     u32 size_cells[DEVICE_TREE_MAX_DEPTH];
101     int ret;
102 
103     do {
104         const char *name = fdt_get_name(fdt, node, NULL);
105         u32 as, ss;
106 
107         if ( depth >= DEVICE_TREE_MAX_DEPTH )
108         {
109             printk("Warning: device tree node `%s' is nested too deep\n",
110                    name);
111             continue;
112         }
113 
114         as = depth > 0 ? address_cells[depth-1] : DT_ROOT_NODE_ADDR_CELLS_DEFAULT;
115         ss = depth > 0 ? size_cells[depth-1] : DT_ROOT_NODE_SIZE_CELLS_DEFAULT;
116 
117         address_cells[depth] = device_tree_get_u32(fdt, node,
118                                                    "#address-cells", as);
119         size_cells[depth] = device_tree_get_u32(fdt, node,
120                                                 "#size-cells", ss);
121 
122         /* skip the first node */
123         if ( node != first_node )
124         {
125             ret = func(fdt, node, name, depth, as, ss, data);
126             if ( ret != 0 )
127                 return ret;
128         }
129 
130         node = fdt_next_node(fdt, node, &depth);
131     } while ( node >= 0 && depth > 0 );
132 
133     return 0;
134 }
135 
process_memory_node(const void * fdt,int node,const char * name,int depth,u32 address_cells,u32 size_cells,void * data)136 static int __init process_memory_node(const void *fdt, int node,
137                                       const char *name, int depth,
138                                       u32 address_cells, u32 size_cells,
139                                       void *data)
140 {
141     const struct fdt_property *prop;
142     int i;
143     int banks;
144     const __be32 *cell;
145     paddr_t start, size;
146     u32 reg_cells = address_cells + size_cells;
147     struct meminfo *mem = data;
148 
149     if ( address_cells < 1 || size_cells < 1 )
150     {
151         printk("fdt: node `%s': invalid #address-cells or #size-cells",
152                name);
153         return -EINVAL;
154     }
155 
156     prop = fdt_get_property(fdt, node, "reg", NULL);
157     if ( !prop )
158         return -ENOENT;
159 
160     cell = (const __be32 *)prop->data;
161     banks = fdt32_to_cpu(prop->len) / (reg_cells * sizeof (u32));
162 
163     for ( i = 0; i < banks && mem->nr_banks < NR_MEM_BANKS; i++ )
164     {
165         device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
166         /* Some DT may describe empty bank, ignore them */
167         if ( !size )
168             continue;
169         mem->bank[mem->nr_banks].start = start;
170         mem->bank[mem->nr_banks].size = size;
171         mem->nr_banks++;
172     }
173 
174     if ( i < banks )
175         return -ENOSPC;
176     return 0;
177 }
178 
process_reserved_memory_node(const void * fdt,int node,const char * name,int depth,u32 address_cells,u32 size_cells,void * data)179 static int __init process_reserved_memory_node(const void *fdt, int node,
180                                                const char *name, int depth,
181                                                u32 address_cells,
182                                                u32 size_cells,
183                                                void *data)
184 {
185     int rc = process_memory_node(fdt, node, name, depth, address_cells,
186                                  size_cells, data);
187 
188     if ( rc == -ENOSPC )
189         panic("Max number of supported reserved-memory regions reached.");
190     else if ( rc != -ENOENT )
191         return rc;
192     return 0;
193 }
194 
process_reserved_memory(const void * fdt,int node,const char * name,int depth,u32 address_cells,u32 size_cells)195 static int __init process_reserved_memory(const void *fdt, int node,
196                                           const char *name, int depth,
197                                           u32 address_cells, u32 size_cells)
198 {
199     return device_tree_for_each_node(fdt, node,
200                                      process_reserved_memory_node,
201                                      &bootinfo.reserved_mem);
202 }
203 
process_multiboot_node(const void * fdt,int node,const char * name,u32 address_cells,u32 size_cells)204 static void __init process_multiboot_node(const void *fdt, int node,
205                                           const char *name,
206                                           u32 address_cells, u32 size_cells)
207 {
208     static int __initdata kind_guess = 0;
209     const struct fdt_property *prop;
210     const __be32 *cell;
211     bootmodule_kind kind;
212     paddr_t start, size;
213     int len;
214     /* sizeof("/chosen/") + DT_MAX_NAME + '/' + DT_MAX_NAME + '/0' => 92 */
215     char path[92];
216     int parent_node, ret;
217     bool domU;
218 
219     parent_node = fdt_parent_offset(fdt, node);
220     ASSERT(parent_node >= 0);
221 
222     /* Check that the node is under "/chosen" (first 7 chars of path) */
223     ret = fdt_get_path(fdt, node, path, sizeof (path));
224     if ( ret != 0 || strncmp(path, "/chosen", 7) )
225         return;
226 
227     prop = fdt_get_property(fdt, node, "reg", &len);
228     if ( !prop )
229         panic("node %s missing `reg' property\n", name);
230 
231     if ( len < dt_cells_to_size(address_cells + size_cells) )
232         panic("fdt: node `%s': `reg` property length is too short\n",
233                     name);
234 
235     cell = (const __be32 *)prop->data;
236     device_tree_get_reg(&cell, address_cells, size_cells, &start, &size);
237 
238     if ( fdt_node_check_compatible(fdt, node, "xen,linux-zimage") == 0 ||
239          fdt_node_check_compatible(fdt, node, "multiboot,kernel") == 0 )
240         kind = BOOTMOD_KERNEL;
241     else if ( fdt_node_check_compatible(fdt, node, "xen,linux-initrd") == 0 ||
242               fdt_node_check_compatible(fdt, node, "multiboot,ramdisk") == 0 )
243         kind = BOOTMOD_RAMDISK;
244     else if ( fdt_node_check_compatible(fdt, node, "xen,xsm-policy") == 0 )
245         kind = BOOTMOD_XSM;
246     else if ( fdt_node_check_compatible(fdt, node, "multiboot,device-tree") == 0 )
247         kind = BOOTMOD_GUEST_DTB;
248     else
249         kind = BOOTMOD_UNKNOWN;
250 
251     /**
252      * Guess the kind of these first two unknowns respectively:
253      * (1) The first unknown must be kernel.
254      * (2) Detect the XSM Magic from the 2nd unknown:
255      *     a. If it's XSM, set the kind as XSM, and that also means we
256      *     won't load ramdisk;
257      *     b. if it's not XSM, set the kind as ramdisk.
258      *     So if user want to load ramdisk, it must be the 2nd unknown.
259      * We also detect the XSM Magic for the following unknowns,
260      * then set its kind according to the return value of has_xsm_magic.
261      */
262     if ( kind == BOOTMOD_UNKNOWN )
263     {
264         switch ( kind_guess++ )
265         {
266         case 0: kind = BOOTMOD_KERNEL; break;
267         case 1: kind = BOOTMOD_RAMDISK; break;
268         default: break;
269         }
270         if ( kind_guess > 1 && has_xsm_magic(start) )
271             kind = BOOTMOD_XSM;
272     }
273 
274     domU = fdt_node_check_compatible(fdt, parent_node, "xen,domain") == 0;
275     add_boot_module(kind, start, size, domU);
276 
277     prop = fdt_get_property(fdt, node, "bootargs", &len);
278     if ( !prop )
279         return;
280     add_boot_cmdline(fdt_get_name(fdt, parent_node, &len), prop->data,
281                      kind, start, domU);
282 }
283 
process_chosen_node(const void * fdt,int node,const char * name,u32 address_cells,u32 size_cells)284 static void __init process_chosen_node(const void *fdt, int node,
285                                        const char *name,
286                                        u32 address_cells, u32 size_cells)
287 {
288     const struct fdt_property *prop;
289     paddr_t start, end;
290     int len;
291 
292     printk("Checking for initrd in /chosen\n");
293 
294     prop = fdt_get_property(fdt, node, "linux,initrd-start", &len);
295     if ( !prop )
296         /* No initrd present. */
297         return;
298     if ( len != sizeof(u32) && len != sizeof(u64) )
299     {
300         printk("linux,initrd-start property has invalid length %d\n", len);
301         return;
302     }
303     start = dt_read_number((void *)&prop->data, dt_size_to_cells(len));
304 
305     prop = fdt_get_property(fdt, node, "linux,initrd-end", &len);
306     if ( !prop )
307     {
308         printk("linux,initrd-end not present but -start was\n");
309         return;
310     }
311     if ( len != sizeof(u32) && len != sizeof(u64) )
312     {
313         printk("linux,initrd-end property has invalid length %d\n", len);
314         return;
315     }
316     end = dt_read_number((void *)&prop->data, dt_size_to_cells(len));
317 
318     if ( start >= end )
319     {
320         printk("linux,initrd limits invalid: %"PRIpaddr" >= %"PRIpaddr"\n",
321                   start, end);
322         return;
323     }
324 
325     printk("Initrd %"PRIpaddr"-%"PRIpaddr"\n", start, end);
326 
327     add_boot_module(BOOTMOD_RAMDISK, start, end-start, false);
328 }
329 
early_scan_node(const void * fdt,int node,const char * name,int depth,u32 address_cells,u32 size_cells,void * data)330 static int __init early_scan_node(const void *fdt,
331                                   int node, const char *name, int depth,
332                                   u32 address_cells, u32 size_cells,
333                                   void *data)
334 {
335     int rc = 0;
336 
337     if ( device_tree_node_matches(fdt, node, "memory") )
338         rc = process_memory_node(fdt, node, name, depth,
339                                  address_cells, size_cells, &bootinfo.mem);
340     else if ( depth == 1 && !dt_node_cmp(name, "reserved-memory") )
341         rc = process_reserved_memory(fdt, node, name, depth,
342                                      address_cells, size_cells);
343     else if ( depth <= 3 && (device_tree_node_compatible(fdt, node, "xen,multiboot-module" ) ||
344               device_tree_node_compatible(fdt, node, "multiboot,module" )))
345         process_multiboot_node(fdt, node, name, address_cells, size_cells);
346     else if ( depth == 1 && device_tree_node_matches(fdt, node, "chosen") )
347         process_chosen_node(fdt, node, name, address_cells, size_cells);
348 
349     if ( rc < 0 )
350         printk("fdt: node `%s': parsing failed\n", name);
351     return rc;
352 }
353 
early_print_info(void)354 static void __init early_print_info(void)
355 {
356     struct meminfo *mi = &bootinfo.mem;
357     struct meminfo *mem_resv = &bootinfo.reserved_mem;
358     struct bootmodules *mods = &bootinfo.modules;
359     struct bootcmdlines *cmds = &bootinfo.cmdlines;
360     unsigned int i, j, nr_rsvd;
361 
362     for ( i = 0; i < mi->nr_banks; i++ )
363         printk("RAM: %"PRIpaddr" - %"PRIpaddr"\n",
364                 mi->bank[i].start,
365                 mi->bank[i].start + mi->bank[i].size - 1);
366     printk("\n");
367     for ( i = 0 ; i < mods->nr_mods; i++ )
368         printk("MODULE[%d]: %"PRIpaddr" - %"PRIpaddr" %-12s\n",
369                 i,
370                 mods->module[i].start,
371                 mods->module[i].start + mods->module[i].size,
372                 boot_module_kind_as_string(mods->module[i].kind));
373 
374     nr_rsvd = fdt_num_mem_rsv(device_tree_flattened);
375     for ( i = 0; i < nr_rsvd; i++ )
376     {
377         paddr_t s, e;
378         if ( fdt_get_mem_rsv(device_tree_flattened, i, &s, &e) < 0 )
379             continue;
380         /* fdt_get_mem_rsv returns length */
381         e += s;
382         printk(" RESVD[%u]: %"PRIpaddr" - %"PRIpaddr"\n", i, s, e);
383     }
384     for ( j = 0; j < mem_resv->nr_banks; j++, i++ )
385     {
386         printk(" RESVD[%u]: %"PRIpaddr" - %"PRIpaddr"\n", i,
387                mem_resv->bank[j].start,
388                mem_resv->bank[j].start + mem_resv->bank[j].size - 1);
389     }
390     printk("\n");
391     for ( i = 0 ; i < cmds->nr_mods; i++ )
392         printk("CMDLINE[%"PRIpaddr"]:%s %s\n", cmds->cmdline[i].start,
393                cmds->cmdline[i].dt_name,
394                &cmds->cmdline[i].cmdline[0]);
395     printk("\n");
396 }
397 
398 /**
399  * boot_fdt_info - initialize bootinfo from a DTB
400  * @fdt: flattened device tree binary
401  *
402  * Returns the size of the DTB.
403  */
boot_fdt_info(const void * fdt,paddr_t paddr)404 size_t __init boot_fdt_info(const void *fdt, paddr_t paddr)
405 {
406     int ret;
407 
408     ret = fdt_check_header(fdt);
409     if ( ret < 0 )
410         panic("No valid device tree\n");
411 
412     add_boot_module(BOOTMOD_FDT, paddr, fdt_totalsize(fdt), false);
413 
414     device_tree_for_each_node((void *)fdt, 0, early_scan_node, NULL);
415     early_print_info();
416 
417     return fdt_totalsize(fdt);
418 }
419 
boot_fdt_cmdline(const void * fdt)420 const __init char *boot_fdt_cmdline(const void *fdt)
421 {
422     int node;
423     const struct fdt_property *prop;
424 
425     node = fdt_path_offset(fdt, "/chosen");
426     if ( node < 0 )
427         return NULL;
428 
429     prop = fdt_get_property(fdt, node, "xen,xen-bootargs", NULL);
430     if ( prop == NULL )
431     {
432         struct bootcmdline *dom0_cmdline =
433             boot_cmdline_find_by_kind(BOOTMOD_KERNEL);
434 
435         if (fdt_get_property(fdt, node, "xen,dom0-bootargs", NULL) ||
436             ( dom0_cmdline && dom0_cmdline->cmdline[0] ) )
437             prop = fdt_get_property(fdt, node, "bootargs", NULL);
438     }
439     if ( prop == NULL )
440         return NULL;
441 
442     return prop->data;
443 }
444 
445 /*
446  * Local variables:
447  * mode: C
448  * c-file-style: "BSD"
449  * c-basic-offset: 4
450  * indent-tabs-mode: nil
451  * End:
452  */
453