]> nv-tegra.nvidia Code Review - linux-3.10.git/blob - drivers/of/fdt.c
of/flattree: Merge early_init_dt_check_for_initrd()
[linux-3.10.git] / drivers / of / fdt.c
1 /*
2  * Functions for working with the Flattened Device Tree data format
3  *
4  * Copyright 2009 Benjamin Herrenschmidt, IBM Corp
5  * benh@kernel.crashing.org
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
9  * version 2 as published by the Free Software Foundation.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/lmb.h>
14 #include <linux/initrd.h>
15 #include <linux/of.h>
16 #include <linux/of_fdt.h>
17
18 struct boot_param_header *initial_boot_params;
19
20 char *find_flat_dt_string(u32 offset)
21 {
22         return ((char *)initial_boot_params) +
23                 initial_boot_params->off_dt_strings + offset;
24 }
25
26 /**
27  * of_scan_flat_dt - scan flattened tree blob and call callback on each.
28  * @it: callback function
29  * @data: context data pointer
30  *
31  * This function is used to scan the flattened device-tree, it is
32  * used to extract the memory information at boot before we can
33  * unflatten the tree
34  */
35 int __init of_scan_flat_dt(int (*it)(unsigned long node,
36                                      const char *uname, int depth,
37                                      void *data),
38                            void *data)
39 {
40         unsigned long p = ((unsigned long)initial_boot_params) +
41                 initial_boot_params->off_dt_struct;
42         int rc = 0;
43         int depth = -1;
44
45         do {
46                 u32 tag = *((u32 *)p);
47                 char *pathp;
48
49                 p += 4;
50                 if (tag == OF_DT_END_NODE) {
51                         depth--;
52                         continue;
53                 }
54                 if (tag == OF_DT_NOP)
55                         continue;
56                 if (tag == OF_DT_END)
57                         break;
58                 if (tag == OF_DT_PROP) {
59                         u32 sz = *((u32 *)p);
60                         p += 8;
61                         if (initial_boot_params->version < 0x10)
62                                 p = _ALIGN(p, sz >= 8 ? 8 : 4);
63                         p += sz;
64                         p = _ALIGN(p, 4);
65                         continue;
66                 }
67                 if (tag != OF_DT_BEGIN_NODE) {
68                         pr_err("Invalid tag %x in flat device tree!\n", tag);
69                         return -EINVAL;
70                 }
71                 depth++;
72                 pathp = (char *)p;
73                 p = _ALIGN(p + strlen(pathp) + 1, 4);
74                 if ((*pathp) == '/') {
75                         char *lp, *np;
76                         for (lp = NULL, np = pathp; *np; np++)
77                                 if ((*np) == '/')
78                                         lp = np+1;
79                         if (lp != NULL)
80                                 pathp = lp;
81                 }
82                 rc = it(p, pathp, depth, data);
83                 if (rc != 0)
84                         break;
85         } while (1);
86
87         return rc;
88 }
89
90 /**
91  * of_get_flat_dt_root - find the root node in the flat blob
92  */
93 unsigned long __init of_get_flat_dt_root(void)
94 {
95         unsigned long p = ((unsigned long)initial_boot_params) +
96                 initial_boot_params->off_dt_struct;
97
98         while (*((u32 *)p) == OF_DT_NOP)
99                 p += 4;
100         BUG_ON(*((u32 *)p) != OF_DT_BEGIN_NODE);
101         p += 4;
102         return _ALIGN(p + strlen((char *)p) + 1, 4);
103 }
104
105 /**
106  * of_get_flat_dt_prop - Given a node in the flat blob, return the property ptr
107  *
108  * This function can be used within scan_flattened_dt callback to get
109  * access to properties
110  */
111 void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
112                                  unsigned long *size)
113 {
114         unsigned long p = node;
115
116         do {
117                 u32 tag = *((u32 *)p);
118                 u32 sz, noff;
119                 const char *nstr;
120
121                 p += 4;
122                 if (tag == OF_DT_NOP)
123                         continue;
124                 if (tag != OF_DT_PROP)
125                         return NULL;
126
127                 sz = *((u32 *)p);
128                 noff = *((u32 *)(p + 4));
129                 p += 8;
130                 if (initial_boot_params->version < 0x10)
131                         p = _ALIGN(p, sz >= 8 ? 8 : 4);
132
133                 nstr = find_flat_dt_string(noff);
134                 if (nstr == NULL) {
135                         pr_warning("Can't find property index name !\n");
136                         return NULL;
137                 }
138                 if (strcmp(name, nstr) == 0) {
139                         if (size)
140                                 *size = sz;
141                         return (void *)p;
142                 }
143                 p += sz;
144                 p = _ALIGN(p, 4);
145         } while (1);
146 }
147
148 /**
149  * of_flat_dt_is_compatible - Return true if given node has compat in compatible list
150  * @node: node to test
151  * @compat: compatible string to compare with compatible list.
152  */
153 int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
154 {
155         const char *cp;
156         unsigned long cplen, l;
157
158         cp = of_get_flat_dt_prop(node, "compatible", &cplen);
159         if (cp == NULL)
160                 return 0;
161         while (cplen > 0) {
162                 if (strncasecmp(cp, compat, strlen(compat)) == 0)
163                         return 1;
164                 l = strlen(cp) + 1;
165                 cp += l;
166                 cplen -= l;
167         }
168
169         return 0;
170 }
171
172 static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
173                                        unsigned long align)
174 {
175         void *res;
176
177         *mem = _ALIGN(*mem, align);
178         res = (void *)*mem;
179         *mem += size;
180
181         return res;
182 }
183
184 /**
185  * unflatten_dt_node - Alloc and populate a device_node from the flat tree
186  * @p: pointer to node in flat tree
187  * @dad: Parent struct device_node
188  * @allnextpp: pointer to ->allnext from last allocated device_node
189  * @fpsize: Size of the node path up at the current depth.
190  */
191 unsigned long __init unflatten_dt_node(unsigned long mem,
192                                         unsigned long *p,
193                                         struct device_node *dad,
194                                         struct device_node ***allnextpp,
195                                         unsigned long fpsize)
196 {
197         struct device_node *np;
198         struct property *pp, **prev_pp = NULL;
199         char *pathp;
200         u32 tag;
201         unsigned int l, allocl;
202         int has_name = 0;
203         int new_format = 0;
204
205         tag = *((u32 *)(*p));
206         if (tag != OF_DT_BEGIN_NODE) {
207                 pr_err("Weird tag at start of node: %x\n", tag);
208                 return mem;
209         }
210         *p += 4;
211         pathp = (char *)*p;
212         l = allocl = strlen(pathp) + 1;
213         *p = _ALIGN(*p + l, 4);
214
215         /* version 0x10 has a more compact unit name here instead of the full
216          * path. we accumulate the full path size using "fpsize", we'll rebuild
217          * it later. We detect this because the first character of the name is
218          * not '/'.
219          */
220         if ((*pathp) != '/') {
221                 new_format = 1;
222                 if (fpsize == 0) {
223                         /* root node: special case. fpsize accounts for path
224                          * plus terminating zero. root node only has '/', so
225                          * fpsize should be 2, but we want to avoid the first
226                          * level nodes to have two '/' so we use fpsize 1 here
227                          */
228                         fpsize = 1;
229                         allocl = 2;
230                 } else {
231                         /* account for '/' and path size minus terminal 0
232                          * already in 'l'
233                          */
234                         fpsize += l;
235                         allocl = fpsize;
236                 }
237         }
238
239         np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
240                                 __alignof__(struct device_node));
241         if (allnextpp) {
242                 memset(np, 0, sizeof(*np));
243                 np->full_name = ((char *)np) + sizeof(struct device_node);
244                 if (new_format) {
245                         char *fn = np->full_name;
246                         /* rebuild full path for new format */
247                         if (dad && dad->parent) {
248                                 strcpy(fn, dad->full_name);
249 #ifdef DEBUG
250                                 if ((strlen(fn) + l + 1) != allocl) {
251                                         pr_debug("%s: p: %d, l: %d, a: %d\n",
252                                                 pathp, (int)strlen(fn),
253                                                 l, allocl);
254                                 }
255 #endif
256                                 fn += strlen(fn);
257                         }
258                         *(fn++) = '/';
259                         memcpy(fn, pathp, l);
260                 } else
261                         memcpy(np->full_name, pathp, l);
262                 prev_pp = &np->properties;
263                 **allnextpp = np;
264                 *allnextpp = &np->allnext;
265                 if (dad != NULL) {
266                         np->parent = dad;
267                         /* we temporarily use the next field as `last_child'*/
268                         if (dad->next == NULL)
269                                 dad->child = np;
270                         else
271                                 dad->next->sibling = np;
272                         dad->next = np;
273                 }
274                 kref_init(&np->kref);
275         }
276         while (1) {
277                 u32 sz, noff;
278                 char *pname;
279
280                 tag = *((u32 *)(*p));
281                 if (tag == OF_DT_NOP) {
282                         *p += 4;
283                         continue;
284                 }
285                 if (tag != OF_DT_PROP)
286                         break;
287                 *p += 4;
288                 sz = *((u32 *)(*p));
289                 noff = *((u32 *)((*p) + 4));
290                 *p += 8;
291                 if (initial_boot_params->version < 0x10)
292                         *p = _ALIGN(*p, sz >= 8 ? 8 : 4);
293
294                 pname = find_flat_dt_string(noff);
295                 if (pname == NULL) {
296                         pr_info("Can't find property name in list !\n");
297                         break;
298                 }
299                 if (strcmp(pname, "name") == 0)
300                         has_name = 1;
301                 l = strlen(pname) + 1;
302                 pp = unflatten_dt_alloc(&mem, sizeof(struct property),
303                                         __alignof__(struct property));
304                 if (allnextpp) {
305                         if (strcmp(pname, "linux,phandle") == 0) {
306                                 np->node = *((u32 *)*p);
307                                 if (np->linux_phandle == 0)
308                                         np->linux_phandle = np->node;
309                         }
310                         if (strcmp(pname, "ibm,phandle") == 0)
311                                 np->linux_phandle = *((u32 *)*p);
312                         pp->name = pname;
313                         pp->length = sz;
314                         pp->value = (void *)*p;
315                         *prev_pp = pp;
316                         prev_pp = &pp->next;
317                 }
318                 *p = _ALIGN((*p) + sz, 4);
319         }
320         /* with version 0x10 we may not have the name property, recreate
321          * it here from the unit name if absent
322          */
323         if (!has_name) {
324                 char *p1 = pathp, *ps = pathp, *pa = NULL;
325                 int sz;
326
327                 while (*p1) {
328                         if ((*p1) == '@')
329                                 pa = p1;
330                         if ((*p1) == '/')
331                                 ps = p1 + 1;
332                         p1++;
333                 }
334                 if (pa < ps)
335                         pa = p1;
336                 sz = (pa - ps) + 1;
337                 pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
338                                         __alignof__(struct property));
339                 if (allnextpp) {
340                         pp->name = "name";
341                         pp->length = sz;
342                         pp->value = pp + 1;
343                         *prev_pp = pp;
344                         prev_pp = &pp->next;
345                         memcpy(pp->value, ps, sz - 1);
346                         ((char *)pp->value)[sz - 1] = 0;
347                         pr_debug("fixed up name for %s -> %s\n", pathp,
348                                 (char *)pp->value);
349                 }
350         }
351         if (allnextpp) {
352                 *prev_pp = NULL;
353                 np->name = of_get_property(np, "name", NULL);
354                 np->type = of_get_property(np, "device_type", NULL);
355
356                 if (!np->name)
357                         np->name = "<NULL>";
358                 if (!np->type)
359                         np->type = "<NULL>";
360         }
361         while (tag == OF_DT_BEGIN_NODE) {
362                 mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize);
363                 tag = *((u32 *)(*p));
364         }
365         if (tag != OF_DT_END_NODE) {
366                 pr_err("Weird tag at end of node: %x\n", tag);
367                 return mem;
368         }
369         *p += 4;
370         return mem;
371 }
372
373 #ifdef CONFIG_BLK_DEV_INITRD
374 /**
375  * early_init_dt_check_for_initrd - Decode initrd location from flat tree
376  * @node: reference to node containing initrd location ('chosen')
377  */
378 void __init early_init_dt_check_for_initrd(unsigned long node)
379 {
380         unsigned long len;
381         u32 *prop;
382
383         pr_debug("Looking for initrd properties... ");
384
385         prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
386         if (prop) {
387                 initrd_start = (unsigned long)
388                                 __va(of_read_ulong(prop, len/4));
389
390                 prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
391                 if (prop) {
392                         initrd_end = (unsigned long)
393                                 __va(of_read_ulong(prop, len/4));
394                         initrd_below_start_ok = 1;
395                 } else {
396                         initrd_start = 0;
397                 }
398         }
399
400         pr_debug("initrd_start=0x%lx  initrd_end=0x%lx\n",
401                  initrd_start, initrd_end);
402 }
403 #else
404 inline void early_init_dt_check_for_initrd(unsigned long node)
405 {
406 }
407 #endif /* CONFIG_BLK_DEV_INITRD */
408
409 /**
410  * unflatten_device_tree - create tree of device_nodes from flat blob
411  *
412  * unflattens the device-tree passed by the firmware, creating the
413  * tree of struct device_node. It also fills the "name" and "type"
414  * pointers of the nodes so the normal device-tree walking functions
415  * can be used.
416  */
417 void __init unflatten_device_tree(void)
418 {
419         unsigned long start, mem, size;
420         struct device_node **allnextp = &allnodes;
421
422         pr_debug(" -> unflatten_device_tree()\n");
423
424         /* First pass, scan for size */
425         start = ((unsigned long)initial_boot_params) +
426                 initial_boot_params->off_dt_struct;
427         size = unflatten_dt_node(0, &start, NULL, NULL, 0);
428         size = (size | 3) + 1;
429
430         pr_debug("  size is %lx, allocating...\n", size);
431
432         /* Allocate memory for the expanded device tree */
433         mem = lmb_alloc(size + 4, __alignof__(struct device_node));
434         mem = (unsigned long) __va(mem);
435
436         ((u32 *)mem)[size / 4] = 0xdeadbeef;
437
438         pr_debug("  unflattening %lx...\n", mem);
439
440         /* Second pass, do actual unflattening */
441         start = ((unsigned long)initial_boot_params) +
442                 initial_boot_params->off_dt_struct;
443         unflatten_dt_node(mem, &start, NULL, &allnextp, 0);
444         if (*((u32 *)start) != OF_DT_END)
445                 pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start));
446         if (((u32 *)mem)[size / 4] != 0xdeadbeef)
447                 pr_warning("End of tree marker overwritten: %08x\n",
448                            ((u32 *)mem)[size / 4]);
449         *allnextp = NULL;
450
451         /* Get pointer to OF "/chosen" node for use everywhere */
452         of_chosen = of_find_node_by_path("/chosen");
453         if (of_chosen == NULL)
454                 of_chosen = of_find_node_by_path("/chosen@0");
455
456         pr_debug(" <- unflatten_device_tree()\n");
457 }