From 9eb8f6743b076b67f00776cda4330c802e157b41 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 28 Apr 2011 14:27:20 -0600 Subject: arm/dt: Allow CONFIG_OF on ARM Add some basic empty infrastructure for DT support on ARM. v5: - Fix off-by-one error in size calculation of initrd - Stop mucking with cmd_line, and load command line from dt into boot_command_line instead which matches the behaviour of ATAGS booting v3: - moved cmd_line export and initrd setup to this patch to make the series bisectable. - switched to alloc_bootmem_align() for allocation when unflattening the device tree. memblock_alloc() was not the right interface. Signed-off-by: Jeremy Kerr Tested-by: Tony Lindgren Acked-by: Nicolas Pitre Acked-by: Russell King Signed-off-by: Grant Likely --- arch/arm/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/arm/kernel/setup.c') diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 006c1e884ea..109997e3fe4 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -466,7 +466,7 @@ static struct machine_desc * __init setup_machine(unsigned int nr) /* can't use cpu_relax() here as it may require MMU setup */; } -static int __init arm_add_memory(phys_addr_t start, unsigned long size) +int __init arm_add_memory(phys_addr_t start, unsigned long size) { struct membank *bank = &meminfo.bank[meminfo.nr_banks]; -- cgit v1.2.3-70-g09d2 From 6291319d4864848efc7b5d81389e2404fb478cb9 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 28 Apr 2011 14:27:21 -0600 Subject: arm/dt: consolidate atags setup into setup_machine_atags In preparation for adding device tree support, this patch consolidates all of the atag-specific setup into a single function. v5: - drop double printk("Machine; %s\n", ...); call. - leave copying boot_command_line in setup_arch() since it isn't atags specific. v4: - adapt to the removal of lookup_machine_type() - break out dump of machine_desc table into dump_machine_table() because the device tree probe code will use it. - Add for_each_machine_desc() macro Tested-by: Tony Lindgren Acked-by: Nicolas Pitre Acked-by: Russell King Signed-off-by: Grant Likely --- arch/arm/include/asm/mach/arch.h | 7 ++++ arch/arm/kernel/setup.c | 69 +++++++++++++++++++++++----------------- 2 files changed, 47 insertions(+), 29 deletions(-) (limited to 'arch/arm/kernel/setup.c') diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h index bf13b814c1b..4764e67fb93 100644 --- a/arch/arm/include/asm/mach/arch.h +++ b/arch/arm/include/asm/mach/arch.h @@ -47,6 +47,13 @@ struct machine_desc { */ extern struct machine_desc *machine_desc; +/* + * Machine type table - also only accessible during boot + */ +extern struct machine_desc __arch_info_begin[], __arch_info_end[]; +#define for_each_machine_desc(p) \ + for (p = __arch_info_begin; p < __arch_info_end; p++) + /* * Set of macros to define architecture features. This is built into * a table by the linker. diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 109997e3fe4..42c2f0cedf1 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -439,25 +439,12 @@ void cpu_init(void) : "r14"); } -static struct machine_desc * __init setup_machine(unsigned int nr) +static void __init dump_machine_table(void) { - extern struct machine_desc __arch_info_begin[], __arch_info_end[]; struct machine_desc *p; - /* - * locate machine in the list of supported machines. - */ - for (p = __arch_info_begin; p < __arch_info_end; p++) - if (nr == p->nr) { - printk("Machine: %s\n", p->name); - return p; - } - - early_print("\n" - "Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n" - "Available machine support:\n\nID (hex)\tNAME\n", nr); - - for (p = __arch_info_begin; p < __arch_info_end; p++) + early_print("Available machine support:\n\nID (hex)\tNAME\n"); + for_each_machine_desc(p) early_print("%08x\t%s\n", p->nr, p->name); early_print("\nPlease check your kernel config and/or bootloader.\n"); @@ -796,23 +783,29 @@ static void __init squash_mem_tags(struct tag *tag) tag->hdr.tag = ATAG_NONE; } -void __init setup_arch(char **cmdline_p) +static struct machine_desc * __init setup_machine_tags(unsigned int nr) { struct tag *tags = (struct tag *)&init_tags; - struct machine_desc *mdesc; + struct machine_desc *mdesc = NULL, *p; char *from = default_command_line; init_tags.mem.start = PHYS_OFFSET; - unwind_init(); - - setup_processor(); - mdesc = setup_machine(machine_arch_type); - machine_desc = mdesc; - machine_name = mdesc->name; + /* + * locate machine in the list of supported machines. + */ + for_each_machine_desc(p) + if (nr == p->nr) { + printk("Machine: %s\n", p->name); + mdesc = p; + break; + } - if (mdesc->soft_reboot) - reboot_setup("s"); + if (!mdesc) { + early_print("\nError: unrecognized/unsupported machine ID" + " (r1 = 0x%08x).\n\n", nr); + dump_machine_table(); /* does not return */ + } if (__atags_pointer) tags = phys_to_virt(__atags_pointer); @@ -857,14 +850,32 @@ void __init setup_arch(char **cmdline_p) parse_tags(tags); } + /* parse_early_param needs a boot_command_line */ + strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); + + return mdesc; +} + + +void __init setup_arch(char **cmdline_p) +{ + struct machine_desc *mdesc; + + unwind_init(); + + setup_processor(); + mdesc = setup_machine_tags(machine_arch_type); + machine_desc = mdesc; + machine_name = mdesc->name; + + if (mdesc->soft_reboot) + reboot_setup("s"); + init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) _end; - /* parse_early_param needs a boot_command_line */ - strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); - /* populate cmd_line too for later use, preserving boot_command_line */ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = cmd_line; -- cgit v1.2.3-70-g09d2 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/include/asm/mach/arch.h | 2 + arch/arm/include/asm/prom.h | 12 +++++ arch/arm/include/asm/setup.h | 2 + arch/arm/kernel/devtree.c | 98 ++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/setup.c | 23 ++++++++-- arch/arm/mm/init.c | 2 + 6 files changed, 135 insertions(+), 4 deletions(-) (limited to 'arch/arm/kernel/setup.c') diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h index 4764e67fb93..946f4d778f7 100644 --- a/arch/arm/include/asm/mach/arch.h +++ b/arch/arm/include/asm/mach/arch.h @@ -18,6 +18,8 @@ struct machine_desc { unsigned int nr; /* architecture number */ const char *name; /* architecture name */ unsigned long boot_params; /* tagged list */ + const char **dt_compat; /* array of device tree + * 'compatible' strings */ unsigned int nr_irqs; /* number of IRQs */ diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h index 8f1037fdc08..11b8708fc4d 100644 --- a/arch/arm/include/asm/prom.h +++ b/arch/arm/include/asm/prom.h @@ -21,5 +21,17 @@ static inline void irq_dispose_mapping(unsigned int virq) return; } +extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys); +extern void arm_dt_memblock_reserve(void); + +#else /* CONFIG_OF */ + +static inline struct machine_desc *setup_machine_fdt(unsigned int dt_phys) +{ + return NULL; +} + +static inline void arm_dt_memblock_reserve(void) { } + #endif /* CONFIG_OF */ #endif /* ASMARM_PROM_H */ diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index 93b4702ffa0..ee2ad8ae07a 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -218,6 +218,8 @@ extern struct meminfo meminfo; #define bank_phys_size(bank) (bank)->size extern int arm_add_memory(phys_addr_t start, unsigned long size); +extern void early_print(const char *str, ...); +extern void dump_machine_table(void); #endif /* __KERNEL__ */ diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c index 75e3df85b88..a701e4226a6 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# * diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 42c2f0cedf1..05db25ef3dd 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include +#include #include #include #include @@ -309,7 +311,7 @@ static void __init cacheid_init(void) */ extern struct proc_info_list *lookup_processor_type(unsigned int); -static void __init early_print(const char *str, ...) +void __init early_print(const char *str, ...) { extern void printascii(const char *); char buf[256]; @@ -439,7 +441,7 @@ void cpu_init(void) : "r14"); } -static void __init dump_machine_table(void) +void __init dump_machine_table(void) { struct machine_desc *p; @@ -837,8 +839,17 @@ static struct machine_desc * __init setup_machine_tags(unsigned int nr) if (tags->hdr.tag != ATAG_CORE) convert_to_tag_list(tags); #endif - if (tags->hdr.tag != ATAG_CORE) + + if (tags->hdr.tag != ATAG_CORE) { +#if defined(CONFIG_OF) + /* + * If CONFIG_OF is set, then assume this is a reasonably + * modern system that should pass boot parameters + */ + early_print("Warning: Neither atags nor dtb found\n"); +#endif tags = (struct tag *)&init_tags; + } if (mdesc->fixup) mdesc->fixup(mdesc, tags, &from, &meminfo); @@ -864,7 +875,9 @@ void __init setup_arch(char **cmdline_p) unwind_init(); setup_processor(); - mdesc = setup_machine_tags(machine_arch_type); + mdesc = setup_machine_fdt(__atags_pointer); + if (!mdesc) + mdesc = setup_machine_tags(machine_arch_type); machine_desc = mdesc; machine_name = mdesc->name; @@ -887,6 +900,8 @@ void __init setup_arch(char **cmdline_p) paging_init(mdesc); request_standard_resources(mdesc); + unflatten_device_tree(); + #ifdef CONFIG_SMP if (is_smp()) smp_init_cpus(); diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 26c405421f4..b26597552eb 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -322,6 +323,7 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) #endif arm_mm_memblock_reserve(); + arm_dt_memblock_reserve(); /* reserve any platform specific memblock areas */ if (mdesc->reserve) -- cgit v1.2.3-70-g09d2