diff options
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r-- | arch/arm/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/kernel/atags.c | 86 | ||||
-rw-r--r-- | arch/arm/kernel/atags.h | 5 | ||||
-rw-r--r-- | arch/arm/kernel/machine_kexec.c | 2 | ||||
-rw-r--r-- | arch/arm/kernel/relocate_kernel.S | 30 | ||||
-rw-r--r-- | arch/arm/kernel/setup.c | 33 | ||||
-rw-r--r-- | arch/arm/kernel/smp.c | 39 |
7 files changed, 133 insertions, 63 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index faa761921153..00d44c6fbfe9 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o +obj-$(CONFIG_ATAGS_PROC) += atags.o obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o diff --git a/arch/arm/kernel/atags.c b/arch/arm/kernel/atags.c new file mode 100644 index 000000000000..e2e934c38080 --- /dev/null +++ b/arch/arm/kernel/atags.c @@ -0,0 +1,86 @@ +#include <linux/slab.h> +#include <linux/kexec.h> +#include <linux/proc_fs.h> +#include <asm/setup.h> +#include <asm/types.h> +#include <asm/page.h> + +struct buffer { + size_t size; + char *data; +}; +static struct buffer tags_buffer; + +static int +read_buffer(char* page, char** start, off_t off, int count, + int* eof, void* data) +{ + struct buffer *buffer = (struct buffer *)data; + + if (off >= buffer->size) { + *eof = 1; + return 0; + } + + count = min((int) (buffer->size - off), count); + + memcpy(page, &buffer->data[off], count); + + return count; +} + + +static int +create_proc_entries(void) +{ + struct proc_dir_entry* tags_entry; + + tags_entry = create_proc_read_entry("atags", 0400, &proc_root, read_buffer, &tags_buffer); + if (!tags_entry) + return -ENOMEM; + + return 0; +} + + +static char __initdata atags_copy_buf[KEXEC_BOOT_PARAMS_SIZE]; +static char __initdata *atags_copy; + +void __init save_atags(const struct tag *tags) +{ + atags_copy = atags_copy_buf; + memcpy(atags_copy, tags, KEXEC_BOOT_PARAMS_SIZE); +} + + +static int __init init_atags_procfs(void) +{ + struct tag *tag; + int error; + + if (!atags_copy) { + printk(KERN_WARNING "Exporting ATAGs: No saved tags found\n"); + return -EIO; + } + + for (tag = (struct tag *) atags_copy; tag->hdr.size; tag = tag_next(tag)) + ; + + tags_buffer.size = ((char *) tag - atags_copy) + sizeof(tag->hdr); + tags_buffer.data = kmalloc(tags_buffer.size, GFP_KERNEL); + if (tags_buffer.data == NULL) + return -ENOMEM; + memcpy(tags_buffer.data, atags_copy, tags_buffer.size); + + error = create_proc_entries(); + if (error) { + printk(KERN_ERR "Exporting ATAGs: not enough memory\n"); + kfree(tags_buffer.data); + tags_buffer.size = 0; + tags_buffer.data = NULL; + } + + return error; +} + +arch_initcall(init_atags_procfs); diff --git a/arch/arm/kernel/atags.h b/arch/arm/kernel/atags.h new file mode 100644 index 000000000000..e5f028d214a1 --- /dev/null +++ b/arch/arm/kernel/atags.h @@ -0,0 +1,5 @@ +#ifdef CONFIG_ATAGS_PROC +extern void save_atags(struct tag *tags); +#else +static inline void save_atags(struct tag *tags) { } +#endif diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 863c66454f2b..db8f54a3451f 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -21,6 +21,7 @@ extern void setup_mm_for_reboot(char mode); extern unsigned long kexec_start_address; extern unsigned long kexec_indirection_page; extern unsigned long kexec_mach_type; +extern unsigned long kexec_boot_atags; /* * Provide a dummy crash_notes definition while crash dump arrives to arm. @@ -62,6 +63,7 @@ void machine_kexec(struct kimage *image) kexec_start_address = image->start; kexec_indirection_page = page_list; kexec_mach_type = machine_arch_type; + kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET; /* copy our kernel relocation code to the control code page */ memcpy(reboot_code_buffer, diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S index 062c111c572f..61930eb09029 100644 --- a/arch/arm/kernel/relocate_kernel.S +++ b/arch/arm/kernel/relocate_kernel.S @@ -7,23 +7,6 @@ .globl relocate_new_kernel relocate_new_kernel: - /* Move boot params back to where the kernel expects them */ - - ldr r0,kexec_boot_params_address - teq r0,#0 - beq 8f - - ldr r1,kexec_boot_params_copy - mov r6,#KEXEC_BOOT_PARAMS_SIZE/4 -7: - ldr r5,[r1],#4 - str r5,[r0],#4 - subs r6,r6,#1 - bne 7b - -8: - /* Boot params moved, now go on with the kernel */ - ldr r0,kexec_indirection_page ldr r1,kexec_start_address @@ -67,7 +50,7 @@ relocate_new_kernel: mov lr,r1 mov r0,#0 ldr r1,kexec_mach_type - ldr r2,kexec_boot_params_address + ldr r2,kexec_boot_atags mov pc,lr .globl kexec_start_address @@ -82,14 +65,9 @@ kexec_indirection_page: kexec_mach_type: .long 0x0 - /* phy addr where new kernel will expect to find boot params */ - .globl kexec_boot_params_address -kexec_boot_params_address: - .long 0x0 - - /* phy addr where old kernel put a copy of orig boot params */ - .globl kexec_boot_params_copy -kexec_boot_params_copy: + /* phy addr of the atags for the new kernel */ + .globl kexec_boot_atags +kexec_boot_atags: .long 0x0 relocate_new_kernel_end: diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index bf56eb337df1..d3941a7b0455 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -24,7 +24,6 @@ #include <linux/interrupt.h> #include <linux/smp.h> #include <linux/fs.h> -#include <linux/kexec.h> #include <asm/cpu.h> #include <asm/elf.h> @@ -39,6 +38,7 @@ #include <asm/mach/time.h> #include "compat.h" +#include "atags.h" #ifndef MEM_SIZE #define MEM_SIZE (16*1024*1024) @@ -62,6 +62,7 @@ extern int root_mountflags; extern void _stext, _text, _etext, __data_start, _edata, _end; unsigned int processor_id; +EXPORT_SYMBOL(processor_id); unsigned int __machine_arch_type; EXPORT_SYMBOL(__machine_arch_type); @@ -784,23 +785,6 @@ static int __init customize_machine(void) } arch_initcall(customize_machine); -#ifdef CONFIG_KEXEC - -/* Physical addr of where the boot params should be for this machine */ -extern unsigned long kexec_boot_params_address; - -/* Physical addr of the buffer into which the boot params are copied */ -extern unsigned long kexec_boot_params_copy; - -/* Pointer to the boot params buffer, for manipulation and display */ -unsigned long kexec_boot_params; -EXPORT_SYMBOL(kexec_boot_params); - -/* The buffer itself - make sure it is sized correctly */ -static unsigned long kexec_boot_params_buf[(KEXEC_BOOT_PARAMS_SIZE + 3) / 4]; - -#endif - void __init setup_arch(char **cmdline_p) { struct tag *tags = (struct tag *)&init_tags; @@ -819,18 +803,6 @@ void __init setup_arch(char **cmdline_p) else if (mdesc->boot_params) tags = phys_to_virt(mdesc->boot_params); -#ifdef CONFIG_KEXEC - kexec_boot_params_copy = virt_to_phys(kexec_boot_params_buf); - kexec_boot_params = (unsigned long)kexec_boot_params_buf; - if (__atags_pointer) { - kexec_boot_params_address = __atags_pointer; - memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE); - } else if (mdesc->boot_params) { - kexec_boot_params_address = mdesc->boot_params; - memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE); - } -#endif - /* * If we have the old style parameters, convert them to * a tag list. @@ -846,6 +818,7 @@ void __init setup_arch(char **cmdline_p) if (tags->hdr.tag == ATAG_CORE) { if (meminfo.nr_banks != 0) squash_mem_tags(tags); + save_atags(tags); parse_tags(tags); } diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index eafbb2b05eb8..e9dfbab46cb6 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -290,6 +290,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void) local_irq_enable(); local_fiq_enable(); + /* + * Setup local timer for this CPU. + */ + local_timer_setup(cpu); + calibrate_delay(); smp_store_cpu_info(cpu); @@ -300,11 +305,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void) cpu_set(cpu, cpu_online_map); /* - * Setup local timer for this CPU. - */ - local_timer_setup(cpu); - - /* * OK, it's off to the idle thread for us */ cpu_idle(); @@ -454,6 +454,27 @@ int smp_call_function(void (*func)(void *info), void *info, int retry, } EXPORT_SYMBOL_GPL(smp_call_function); +int smp_call_function_single(int cpu, void (*func)(void *info), void *info, + int retry, int wait) +{ + /* prevent preemption and reschedule on another processor */ + int current_cpu = get_cpu(); + int ret = 0; + + if (cpu == current_cpu) { + local_irq_disable(); + func(info); + local_irq_enable(); + } else + ret = smp_call_function_on_cpu(func, info, retry, wait, + cpumask_of_cpu(cpu)); + + put_cpu(); + + return ret; +} +EXPORT_SYMBOL_GPL(smp_call_function_single); + void show_ipi_list(struct seq_file *p) { unsigned int cpu; @@ -481,8 +502,7 @@ void show_local_irqs(struct seq_file *p) static void ipi_timer(void) { irq_enter(); - profile_tick(CPU_PROFILING); - update_process_times(user_mode(get_irq_regs())); + local_timer_interrupt(); irq_exit(); } @@ -621,6 +641,11 @@ void smp_send_timer(void) send_ipi_message(mask, IPI_TIMER); } +void smp_timer_broadcast(cpumask_t mask) +{ + send_ipi_message(mask, IPI_TIMER); +} + void smp_send_stop(void) { cpumask_t mask = cpu_online_map; |