summaryrefslogtreecommitdiffstats
path: root/arch/arm/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/kernel')
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/atags.c86
-rw-r--r--arch/arm/kernel/atags.h5
-rw-r--r--arch/arm/kernel/machine_kexec.c2
-rw-r--r--arch/arm/kernel/relocate_kernel.S30
-rw-r--r--arch/arm/kernel/setup.c33
-rw-r--r--arch/arm/kernel/smp.c39
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;
OpenPOWER on IntegriCloud