diff options
-rw-r--r-- | drivers/of/fdt.c | 149 | ||||
-rw-r--r-- | include/linux/of_fdt.h | 2 |
2 files changed, 100 insertions, 51 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 71edc9cecd62..8a90ee42071a 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c | |||
@@ -11,10 +11,12 @@ | |||
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/initrd.h> | 13 | #include <linux/initrd.h> |
14 | #include <linux/module.h> | ||
14 | #include <linux/of.h> | 15 | #include <linux/of.h> |
15 | #include <linux/of_fdt.h> | 16 | #include <linux/of_fdt.h> |
16 | #include <linux/string.h> | 17 | #include <linux/string.h> |
17 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
19 | #include <linux/slab.h> | ||
18 | 20 | ||
19 | #ifdef CONFIG_PPC | 21 | #ifdef CONFIG_PPC |
20 | #include <asm/machdep.h> | 22 | #include <asm/machdep.h> |
@@ -312,6 +314,94 @@ unsigned long unflatten_dt_node(struct boot_param_header *blob, | |||
312 | return mem; | 314 | return mem; |
313 | } | 315 | } |
314 | 316 | ||
317 | /** | ||
318 | * __unflatten_device_tree - create tree of device_nodes from flat blob | ||
319 | * | ||
320 | * unflattens a device-tree, creating the | ||
321 | * tree of struct device_node. It also fills the "name" and "type" | ||
322 | * pointers of the nodes so the normal device-tree walking functions | ||
323 | * can be used. | ||
324 | * @blob: The blob to expand | ||
325 | * @mynodes: The device_node tree created by the call | ||
326 | * @dt_alloc: An allocator that provides a virtual address to memory | ||
327 | * for the resulting tree | ||
328 | */ | ||
329 | void __unflatten_device_tree(struct boot_param_header *blob, | ||
330 | struct device_node **mynodes, | ||
331 | void * (*dt_alloc)(u64 size, u64 align)) | ||
332 | { | ||
333 | unsigned long start, mem, size; | ||
334 | struct device_node **allnextp = mynodes; | ||
335 | |||
336 | pr_debug(" -> unflatten_device_tree()\n"); | ||
337 | |||
338 | if (!blob) { | ||
339 | pr_debug("No device tree pointer\n"); | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | pr_debug("Unflattening device tree:\n"); | ||
344 | pr_debug("magic: %08x\n", be32_to_cpu(blob->magic)); | ||
345 | pr_debug("size: %08x\n", be32_to_cpu(blob->totalsize)); | ||
346 | pr_debug("version: %08x\n", be32_to_cpu(blob->version)); | ||
347 | |||
348 | if (be32_to_cpu(blob->magic) != OF_DT_HEADER) { | ||
349 | pr_err("Invalid device tree blob header\n"); | ||
350 | return; | ||
351 | } | ||
352 | |||
353 | /* First pass, scan for size */ | ||
354 | start = ((unsigned long)blob) + | ||
355 | be32_to_cpu(blob->off_dt_struct); | ||
356 | size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); | ||
357 | size = (size | 3) + 1; | ||
358 | |||
359 | pr_debug(" size is %lx, allocating...\n", size); | ||
360 | |||
361 | /* Allocate memory for the expanded device tree */ | ||
362 | mem = (unsigned long) | ||
363 | dt_alloc(size + 4, __alignof__(struct device_node)); | ||
364 | |||
365 | ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); | ||
366 | |||
367 | pr_debug(" unflattening %lx...\n", mem); | ||
368 | |||
369 | /* Second pass, do actual unflattening */ | ||
370 | start = ((unsigned long)blob) + | ||
371 | be32_to_cpu(blob->off_dt_struct); | ||
372 | unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); | ||
373 | if (be32_to_cpup((__be32 *)start) != OF_DT_END) | ||
374 | pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start)); | ||
375 | if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef) | ||
376 | pr_warning("End of tree marker overwritten: %08x\n", | ||
377 | be32_to_cpu(((__be32 *)mem)[size / 4])); | ||
378 | *allnextp = NULL; | ||
379 | |||
380 | pr_debug(" <- unflatten_device_tree()\n"); | ||
381 | } | ||
382 | |||
383 | static void *kernel_tree_alloc(u64 size, u64 align) | ||
384 | { | ||
385 | return kzalloc(size, GFP_KERNEL); | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * of_fdt_unflatten_tree - create tree of device_nodes from flat blob | ||
390 | * | ||
391 | * unflattens the device-tree passed by the firmware, creating the | ||
392 | * tree of struct device_node. It also fills the "name" and "type" | ||
393 | * pointers of the nodes so the normal device-tree walking functions | ||
394 | * can be used. | ||
395 | */ | ||
396 | void of_fdt_unflatten_tree(unsigned long *blob, | ||
397 | struct device_node **mynodes) | ||
398 | { | ||
399 | struct boot_param_header *device_tree = | ||
400 | (struct boot_param_header *)blob; | ||
401 | __unflatten_device_tree(device_tree, mynodes, &kernel_tree_alloc); | ||
402 | } | ||
403 | EXPORT_SYMBOL_GPL(of_fdt_unflatten_tree); | ||
404 | |||
315 | /* Everything below here references initial_boot_params directly. */ | 405 | /* Everything below here references initial_boot_params directly. */ |
316 | int __initdata dt_root_addr_cells; | 406 | int __initdata dt_root_addr_cells; |
317 | int __initdata dt_root_size_cells; | 407 | int __initdata dt_root_size_cells; |
@@ -569,6 +659,12 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, | |||
569 | return 1; | 659 | return 1; |
570 | } | 660 | } |
571 | 661 | ||
662 | static void *__init early_device_tree_alloc(u64 size, u64 align) | ||
663 | { | ||
664 | unsigned long mem = early_init_dt_alloc_memory_arch(size, align); | ||
665 | return __va(mem); | ||
666 | } | ||
667 | |||
572 | /** | 668 | /** |
573 | * unflatten_device_tree - create tree of device_nodes from flat blob | 669 | * unflatten_device_tree - create tree of device_nodes from flat blob |
574 | * | 670 | * |
@@ -579,62 +675,13 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, | |||
579 | */ | 675 | */ |
580 | void __init unflatten_device_tree(void) | 676 | void __init unflatten_device_tree(void) |
581 | { | 677 | { |
582 | unsigned long start, mem, size; | 678 | __unflatten_device_tree(initial_boot_params, &allnodes, |
583 | struct device_node **allnextp = &allnodes; | 679 | early_device_tree_alloc); |
584 | |||
585 | pr_debug(" -> unflatten_device_tree()\n"); | ||
586 | |||
587 | if (!initial_boot_params) { | ||
588 | pr_debug("No device tree pointer\n"); | ||
589 | return; | ||
590 | } | ||
591 | |||
592 | pr_debug("Unflattening device tree:\n"); | ||
593 | pr_debug("magic: %08x\n", be32_to_cpu(initial_boot_params->magic)); | ||
594 | pr_debug("size: %08x\n", be32_to_cpu(initial_boot_params->totalsize)); | ||
595 | pr_debug("version: %08x\n", be32_to_cpu(initial_boot_params->version)); | ||
596 | |||
597 | if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) { | ||
598 | pr_err("Invalid device tree blob header\n"); | ||
599 | return; | ||
600 | } | ||
601 | |||
602 | /* First pass, scan for size */ | ||
603 | start = ((unsigned long)initial_boot_params) + | ||
604 | be32_to_cpu(initial_boot_params->off_dt_struct); | ||
605 | size = unflatten_dt_node(initial_boot_params, 0, &start, | ||
606 | NULL, NULL, 0); | ||
607 | size = (size | 3) + 1; | ||
608 | |||
609 | pr_debug(" size is %lx, allocating...\n", size); | ||
610 | |||
611 | /* Allocate memory for the expanded device tree */ | ||
612 | mem = early_init_dt_alloc_memory_arch(size + 4, | ||
613 | __alignof__(struct device_node)); | ||
614 | mem = (unsigned long) __va(mem); | ||
615 | |||
616 | ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); | ||
617 | |||
618 | pr_debug(" unflattening %lx...\n", mem); | ||
619 | |||
620 | /* Second pass, do actual unflattening */ | ||
621 | start = ((unsigned long)initial_boot_params) + | ||
622 | be32_to_cpu(initial_boot_params->off_dt_struct); | ||
623 | unflatten_dt_node(initial_boot_params, mem, &start, | ||
624 | NULL, &allnextp, 0); | ||
625 | if (be32_to_cpup((__be32 *)start) != OF_DT_END) | ||
626 | pr_warning("Weird tag at end of tree: %08x\n", *((u32 *)start)); | ||
627 | if (be32_to_cpu(((__be32 *)mem)[size / 4]) != 0xdeadbeef) | ||
628 | pr_warning("End of tree marker overwritten: %08x\n", | ||
629 | be32_to_cpu(((__be32 *)mem)[size / 4])); | ||
630 | *allnextp = NULL; | ||
631 | 680 | ||
632 | /* Get pointer to OF "/chosen" node for use everywhere */ | 681 | /* Get pointer to OF "/chosen" node for use everywhere */ |
633 | of_chosen = of_find_node_by_path("/chosen"); | 682 | of_chosen = of_find_node_by_path("/chosen"); |
634 | if (of_chosen == NULL) | 683 | if (of_chosen == NULL) |
635 | of_chosen = of_find_node_by_path("/chosen@0"); | 684 | of_chosen = of_find_node_by_path("/chosen@0"); |
636 | |||
637 | pr_debug(" <- unflatten_device_tree()\n"); | ||
638 | } | 685 | } |
639 | 686 | ||
640 | #endif /* CONFIG_OF_EARLY_FLATTREE */ | 687 | #endif /* CONFIG_OF_EARLY_FLATTREE */ |
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 70c5b736f0a3..9ce5dfd2186a 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h | |||
@@ -68,6 +68,8 @@ extern void *of_fdt_get_property(struct boot_param_header *blob, | |||
68 | extern int of_fdt_is_compatible(struct boot_param_header *blob, | 68 | extern int of_fdt_is_compatible(struct boot_param_header *blob, |
69 | unsigned long node, | 69 | unsigned long node, |
70 | const char *compat); | 70 | const char *compat); |
71 | extern void of_fdt_unflatten_tree(unsigned long *blob, | ||
72 | struct device_node **mynodes); | ||
71 | 73 | ||
72 | /* TBD: Temporary export of fdt globals - remove when code fully merged */ | 74 | /* TBD: Temporary export of fdt globals - remove when code fully merged */ |
73 | extern int __initdata dt_root_addr_cells; | 75 | extern int __initdata dt_root_addr_cells; |