From 93c02ab40ae6e06cb24d14845d9f008fdd24f43d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 28 Apr 2011 14:27:21 -0600 Subject: arm/dt: probe for platforms via the device tree If a dtb is passed to the kernel then the kernel needs to iterate through compiled-in mdescs looking for one that matches and move the dtb data to a safe location before it gets accidentally overwritten by the kernel. This patch creates a new function, setup_machine_fdt() which is analogous to the setup_machine_atags() created in the previous patch. It does all the early setup needed to use a device tree machine description. v5: - Print warning with neither dtb nor atags are passed to the kernel - Fix bug in setting of __machine_arch_type to the selected machine, not just the last machine in the list. Reported-by: Tixy - Copy command line directly into boot_command_line instead of cmd_line v4: - Dump some output when a matching machine_desc cannot be found v3: - Added processing of reserved list. - Backed out the v2 change that copied instead of reserved the dtb. dtb is reserved again and the real problem was fixed by using alloc_bootmem_align() for early allocation of RAM for unflattening the tree. - Moved cmd_line and initrd changes to earlier patch to make series bisectable. v2: Changed to save the dtb by copying into an allocated buffer. - Since the dtb will very likely be passed in the first 16k of ram where the interrupt vectors live, memblock_reserve() is insufficient to protect the dtb data. [based on work originally written by Jeremy Kerr ] Tested-by: Tony Lindgren Acked-by: Nicolas Pitre Acked-by: Russell King Signed-off-by: Grant Likely --- arch/arm/kernel/devtree.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'arch/arm/kernel/devtree.c') diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index 75e3df85b88c..a701e4226a6c 100644 --- a/arch/arm/kernel/devtree.c +++ b/arch/arm/kernel/devtree.c @@ -21,6 +21,8 @@ #include #include +#include +#include void __init early_init_dt_add_memory_arch(u64 base, u64 size) { @@ -32,6 +34,102 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) return alloc_bootmem_align(size, align); } +void __init arm_dt_memblock_reserve(void) +{ + u64 *reserve_map, base, size; + + if (!initial_boot_params) + return; + + /* Reserve the dtb region */ + memblock_reserve(virt_to_phys(initial_boot_params), + be32_to_cpu(initial_boot_params->totalsize)); + + /* + * Process the reserve map. This will probably overlap the initrd + * and dtb locations which are already reserved, but overlaping + * doesn't hurt anything + */ + reserve_map = ((void*)initial_boot_params) + + be32_to_cpu(initial_boot_params->off_mem_rsvmap); + while (1) { + base = be64_to_cpup(reserve_map++); + size = be64_to_cpup(reserve_map++); + if (!size) + break; + memblock_reserve(base, size); + } +} + +/** + * setup_machine_fdt - Machine setup when an dtb was passed to the kernel + * @dt_phys: physical address of dt blob + * + * If a dtb was passed to the kernel in r2, then use it to choose the + * correct machine_desc and to setup the system. + */ +struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) +{ + struct boot_param_header *devtree; + struct machine_desc *mdesc, *mdesc_best = NULL; + unsigned int score, mdesc_score = ~1; + unsigned long dt_root; + const char *model; + + devtree = phys_to_virt(dt_phys); + + /* check device tree validity */ + if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) + return NULL; + + /* Search the mdescs for the 'best' compatible value match */ + initial_boot_params = devtree; + dt_root = of_get_flat_dt_root(); + for_each_machine_desc(mdesc) { + score = of_flat_dt_match(dt_root, mdesc->dt_compat); + if (score > 0 && score < mdesc_score) { + mdesc_best = mdesc; + mdesc_score = score; + } + } + if (!mdesc_best) { + const char *prop; + long size; + + early_print("\nError: unrecognized/unsupported " + "device tree compatible list:\n[ "); + + prop = of_get_flat_dt_prop(dt_root, "compatible", &size); + while (size > 0) { + early_print("'%s' ", prop); + size -= strlen(prop) + 1; + prop += strlen(prop) + 1; + } + early_print("]\n\n"); + + dump_machine_table(); /* does not return */ + } + + model = of_get_flat_dt_prop(dt_root, "model", NULL); + if (!model) + model = of_get_flat_dt_prop(dt_root, "compatible", NULL); + if (!model) + model = ""; + pr_info("Machine: %s, model: %s\n", mdesc_best->name, model); + + /* Retrieve various information from the /chosen node */ + of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + /* Initialize {size,address}-cells info */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + /* Setup memory, calling early_init_dt_add_memory_arch */ + of_scan_flat_dt(early_init_dt_scan_memory, NULL); + + /* Change machine number to match the mdesc we're using */ + __machine_arch_type = mdesc_best->nr; + + return mdesc_best; +} + /** * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq# * -- cgit