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