diff options
Diffstat (limited to 'arch/arm/kernel')
| -rw-r--r-- | arch/arm/kernel/Makefile | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/crash_dump.c | 60 | ||||
| -rw-r--r-- | arch/arm/kernel/entry-armv.S | 39 | ||||
| -rw-r--r-- | arch/arm/kernel/irq.c | 41 | ||||
| -rw-r--r-- | arch/arm/kernel/kprobes-decode.c | 5 | ||||
| -rw-r--r-- | arch/arm/kernel/machine_kexec.c | 4 | ||||
| -rw-r--r-- | arch/arm/kernel/perf_event.c | 2 | ||||
| -rw-r--r-- | arch/arm/kernel/process.c | 23 | ||||
| -rw-r--r-- | arch/arm/kernel/ptrace.c | 96 | ||||
| -rw-r--r-- | arch/arm/kernel/relocate_kernel.S | 6 | ||||
| -rw-r--r-- | arch/arm/kernel/setup.c | 100 | ||||
| -rw-r--r-- | arch/arm/kernel/traps.c | 41 | 
12 files changed, 353 insertions, 65 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 26d302c28e1..ea023c6aa31 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_ARM_THUMBEE)	+= thumbee.o  obj-$(CONFIG_KGDB)		+= kgdb.o  obj-$(CONFIG_ARM_UNWIND)	+= unwind.o  obj-$(CONFIG_HAVE_TCM)		+= tcm.o +obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o  obj-$(CONFIG_CRUNCH)		+= crunch.o crunch-bits.o  AFLAGS_crunch-bits.o		:= -Wa,-mcpu=ep9312 diff --git a/arch/arm/kernel/crash_dump.c b/arch/arm/kernel/crash_dump.c new file mode 100644 index 00000000000..cd3b853a8a6 --- /dev/null +++ b/arch/arm/kernel/crash_dump.c @@ -0,0 +1,60 @@ +/* + * arch/arm/kernel/crash_dump.c + * + * Copyright (C) 2010 Nokia Corporation. + * Author: Mika Westerberg + * + * This code is taken from arch/x86/kernel/crash_dump_64.c + *   Created by: Hariprasad Nellitheertha (hari@in.ibm.com) + *   Copyright (C) IBM Corporation, 2004. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/errno.h> +#include <linux/crash_dump.h> +#include <linux/uaccess.h> +#include <linux/io.h> + +/* stores the physical address of elf header of crash image */ +unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX; + +/** + * copy_oldmem_page() - copy one page from old kernel memory + * @pfn: page frame number to be copied + * @buf: buffer where the copied page is placed + * @csize: number of bytes to copy + * @offset: offset in bytes into the page + * @userbuf: if set, @buf is int he user address space + * + * This function copies one page from old kernel memory into buffer pointed by + * @buf. If @buf is in userspace, set @userbuf to %1. Returns number of bytes + * copied or negative error in case of failure. + */ +ssize_t copy_oldmem_page(unsigned long pfn, char *buf, +			 size_t csize, unsigned long offset, +			 int userbuf) +{ +	void *vaddr; + +	if (!csize) +		return 0; + +	vaddr = ioremap(pfn << PAGE_SHIFT, PAGE_SIZE); +	if (!vaddr) +		return -ENOMEM; + +	if (userbuf) { +		if (copy_to_user(buf, vaddr + offset, csize)) { +			iounmap(vaddr); +			return -EFAULT; +		} +	} else { +		memcpy(buf, vaddr + offset, csize); +	} + +	iounmap(vaddr); +	return csize; +} diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 7ee48e7f8f3..e864e482118 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -22,6 +22,7 @@  #include <asm/thread_notify.h>  #include <asm/unwind.h>  #include <asm/unistd.h> +#include <asm/tls.h>  #include "entry-header.S" @@ -162,8 +163,6 @@ ENDPROC(__und_invalid)  	@  r4 - orig_r0 (see pt_regs definition in ptrace.h)  	@  	stmia	r5, {r0 - r4} - -	asm_trace_hardirqs_off  	.endm  	.align	5 @@ -204,7 +203,7 @@ __dabt_svc:  	@  	@ IRQs off again before pulling preserved data off the stack  	@ -	disable_irq +	disable_irq_notrace  	@  	@ restore SPSR and restart the instruction @@ -218,6 +217,9 @@ ENDPROC(__dabt_svc)  __irq_svc:  	svc_entry +#ifdef CONFIG_TRACE_IRQFLAGS +	bl	trace_hardirqs_off +#endif  #ifdef CONFIG_PREEMPT  	get_thread_info tsk  	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count @@ -291,7 +293,7 @@ __und_svc:  	@  	@ IRQs off again before pulling preserved data off the stack  	@ -1:	disable_irq +1:	disable_irq_notrace  	@  	@ restore SPSR and restart the instruction @@ -327,7 +329,7 @@ __pabt_svc:  	@  	@ IRQs off again before pulling preserved data off the stack  	@ -	disable_irq +	disable_irq_notrace  	@  	@ restore SPSR and restart the instruction @@ -393,8 +395,6 @@ ENDPROC(__pabt_svc)  	@ Clear FP to mark the first stack frame  	@  	zero_fp - -	asm_trace_hardirqs_off  	.endm  	.macro	kuser_cmpxchg_check @@ -465,9 +465,6 @@ __irq_usr:   THUMB(	movne	r0, #0		)   THUMB(	strne	r0, [r0]	)  #endif -#ifdef CONFIG_TRACE_IRQFLAGS -	bl	trace_hardirqs_on -#endif  	mov	why, #0  	b	ret_to_user @@ -739,12 +736,7 @@ ENTRY(__switch_to)  #ifdef CONFIG_MMU  	ldr	r6, [r2, #TI_CPU_DOMAIN]  #endif -#if defined(CONFIG_HAS_TLS_REG) -	mcr	p15, 0, r3, c13, c0, 3		@ set TLS register -#elif !defined(CONFIG_TLS_REG_EMUL) -	mov	r4, #0xffff0fff -	str	r3, [r4, #-15]			@ TLS val at 0xffff0ff0 -#endif +	set_tls	r3, r4, r5  #ifdef CONFIG_MMU  	mcr	p15, 0, r6, c3, c0, 0		@ Set domain register  #endif @@ -1009,17 +1001,12 @@ kuser_cmpxchg_fixup:   */  __kuser_get_tls:				@ 0xffff0fe0 - -#if !defined(CONFIG_HAS_TLS_REG) && !defined(CONFIG_TLS_REG_EMUL) -	ldr	r0, [pc, #(16 - 8)]		@ TLS stored at 0xffff0ff0 -#else -	mrc	p15, 0, r0, c13, c0, 3		@ read TLS register -#endif +	ldr	r0, [pc, #(16 - 8)]	@ read TLS, set in kuser_get_tls_init  	usr_ret	lr - -	.rep	5 -	.word	0			@ pad up to __kuser_helper_version -	.endr +	mrc	p15, 0, r0, c13, c0, 3	@ 0xffff0fe8 hardware TLS code +	.rep	4 +	.word	0			@ 0xffff0ff0 software TLS value, then +	.endr				@ pad up to __kuser_helper_version  /*   * Reference declaration: diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 3b3d2c80509..c0d5c3b3a76 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -47,12 +47,14 @@  #define irq_finish(irq) do { } while (0)  #endif +unsigned int arch_nr_irqs;  void (*init_arch_irq)(void) __initdata = NULL;  unsigned long irq_err_count;  int show_interrupts(struct seq_file *p, void *v)  {  	int i = *(loff_t *) v, cpu; +	struct irq_desc *desc;  	struct irqaction * action;  	unsigned long flags; @@ -67,24 +69,25 @@ int show_interrupts(struct seq_file *p, void *v)  		seq_putc(p, '\n');  	} -	if (i < NR_IRQS) { -		raw_spin_lock_irqsave(&irq_desc[i].lock, flags); -		action = irq_desc[i].action; +	if (i < nr_irqs) { +		desc = irq_to_desc(i); +		raw_spin_lock_irqsave(&desc->lock, flags); +		action = desc->action;  		if (!action)  			goto unlock;  		seq_printf(p, "%3d: ", i);  		for_each_present_cpu(cpu)  			seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu)); -		seq_printf(p, " %10s", irq_desc[i].chip->name ? : "-"); +		seq_printf(p, " %10s", desc->chip->name ? : "-");  		seq_printf(p, "  %s", action->name);  		for (action = action->next; action; action = action->next)  			seq_printf(p, ", %s", action->name);  		seq_putc(p, '\n');  unlock: -		raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); -	} else if (i == NR_IRQS) { +		raw_spin_unlock_irqrestore(&desc->lock, flags); +	} else if (i == nr_irqs) {  #ifdef CONFIG_FIQ  		show_fiq_list(p, v);  #endif @@ -112,7 +115,7 @@ asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)  	 * Some hardware gives randomly wrong interrupts.  Rather  	 * than crashing, do something sensible.  	 */ -	if (unlikely(irq >= NR_IRQS)) { +	if (unlikely(irq >= nr_irqs)) {  		if (printk_ratelimit())  			printk(KERN_WARNING "Bad IRQ%u\n", irq);  		ack_bad_irq(irq); @@ -132,12 +135,12 @@ void set_irq_flags(unsigned int irq, unsigned int iflags)  	struct irq_desc *desc;  	unsigned long flags; -	if (irq >= NR_IRQS) { +	if (irq >= nr_irqs) {  		printk(KERN_ERR "Trying to set irq flags for IRQ%d\n", irq);  		return;  	} -	desc = irq_desc + irq; +	desc = irq_to_desc(irq);  	raw_spin_lock_irqsave(&desc->lock, flags);  	desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;  	if (iflags & IRQF_VALID) @@ -151,14 +154,25 @@ void set_irq_flags(unsigned int irq, unsigned int iflags)  void __init init_IRQ(void)  { +	struct irq_desc *desc;  	int irq; -	for (irq = 0; irq < NR_IRQS; irq++) -		irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE; +	for (irq = 0; irq < nr_irqs; irq++) { +		desc = irq_to_desc_alloc_node(irq, 0); +		desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE; +	}  	init_arch_irq();  } +#ifdef CONFIG_SPARSE_IRQ +int __init arch_probe_nr_irqs(void) +{ +	nr_irqs = arch_nr_irqs ? arch_nr_irqs : NR_IRQS; +	return 0; +} +#endif +  #ifdef CONFIG_HOTPLUG_CPU  static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu) @@ -178,10 +192,9 @@ static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu)  void migrate_irqs(void)  {  	unsigned int i, cpu = smp_processor_id(); +	struct irq_desc *desc; -	for (i = 0; i < NR_IRQS; i++) { -		struct irq_desc *desc = irq_desc + i; - +	for_each_irq_desc(i, desc) {  		if (desc->node == cpu) {  			unsigned int newcpu = cpumask_any_and(desc->affinity,  							      cpu_online_mask); diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c index da1f94906a4..8bccbfa693f 100644 --- a/arch/arm/kernel/kprobes-decode.c +++ b/arch/arm/kernel/kprobes-decode.c @@ -583,13 +583,14 @@ static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)  {  	insn_llret_3arg_fn_t *i_fn = (insn_llret_3arg_fn_t *)&p->ainsn.insn[0];  	kprobe_opcode_t insn = p->opcode; +	long ppc = (long)p->addr + 8;  	union reg_pair fnr;  	int rd = (insn >> 12) & 0xf;  	int rn = (insn >> 16) & 0xf;  	int rm = insn & 0xf;  	long rdv; -	long rnv  = regs->uregs[rn]; -	long rmv  = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ +	long rnv = (rn == 15) ? ppc : regs->uregs[rn]; +	long rmv = (rm == 15) ? ppc : regs->uregs[rm];  	long cpsr = regs->ARM_cpsr;  	fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn); diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 598ca61e7bc..81e989858d4 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -43,6 +43,10 @@ void machine_shutdown(void)  void machine_crash_shutdown(struct pt_regs *regs)  { +	local_irq_disable(); +	crash_save_cpu(regs, smp_processor_id()); + +	printk(KERN_INFO "Loading crashdump kernel...\n");  }  void machine_kexec(struct kimage *image) diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index c45768614c8..de12536d687 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -201,7 +201,7 @@ armpmu_event_update(struct perf_event *event,  {  	int shift = 64 - 32;  	s64 prev_raw_count, new_raw_count; -	s64 delta; +	u64 delta;  again:  	prev_raw_count = atomic64_read(&hwc->prev_count); diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index acf5e6fdb6d..a4a9cc88bec 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -351,17 +351,21 @@ EXPORT_SYMBOL(dump_fpu);  /*   * Shuffle the argument into the correct register before calling the - * thread function.  r1 is the thread argument, r2 is the pointer to - * the thread function, and r3 points to the exit function. + * thread function.  r4 is the thread argument, r5 is the pointer to + * the thread function, and r6 points to the exit function.   */  extern void kernel_thread_helper(void);  asm(	".pushsection .text\n"  "	.align\n"  "	.type	kernel_thread_helper, #function\n"  "kernel_thread_helper:\n" -"	mov	r0, r1\n" -"	mov	lr, r3\n" -"	mov	pc, r2\n" +#ifdef CONFIG_TRACE_IRQFLAGS +"	bl	trace_hardirqs_on\n" +#endif +"	msr	cpsr_c, r7\n" +"	mov	r0, r4\n" +"	mov	lr, r6\n" +"	mov	pc, r5\n"  "	.size	kernel_thread_helper, . - kernel_thread_helper\n"  "	.popsection"); @@ -391,11 +395,12 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)  	memset(®s, 0, sizeof(regs)); -	regs.ARM_r1 = (unsigned long)arg; -	regs.ARM_r2 = (unsigned long)fn; -	regs.ARM_r3 = (unsigned long)kernel_thread_exit; +	regs.ARM_r4 = (unsigned long)arg; +	regs.ARM_r5 = (unsigned long)fn; +	regs.ARM_r6 = (unsigned long)kernel_thread_exit; +	regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;  	regs.ARM_pc = (unsigned long)kernel_thread_helper; -	regs.ARM_cpsr = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE; +	regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;  	return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);  } diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 3f562a7c0a9..f99d489822d 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -52,6 +52,102 @@  #define BREAKINST_THUMB	0xde01  #endif +struct pt_regs_offset { +	const char *name; +	int offset; +}; + +#define REG_OFFSET_NAME(r) \ +	{.name = #r, .offset = offsetof(struct pt_regs, ARM_##r)} +#define REG_OFFSET_END {.name = NULL, .offset = 0} + +static const struct pt_regs_offset regoffset_table[] = { +	REG_OFFSET_NAME(r0), +	REG_OFFSET_NAME(r1), +	REG_OFFSET_NAME(r2), +	REG_OFFSET_NAME(r3), +	REG_OFFSET_NAME(r4), +	REG_OFFSET_NAME(r5), +	REG_OFFSET_NAME(r6), +	REG_OFFSET_NAME(r7), +	REG_OFFSET_NAME(r8), +	REG_OFFSET_NAME(r9), +	REG_OFFSET_NAME(r10), +	REG_OFFSET_NAME(fp), +	REG_OFFSET_NAME(ip), +	REG_OFFSET_NAME(sp), +	REG_OFFSET_NAME(lr), +	REG_OFFSET_NAME(pc), +	REG_OFFSET_NAME(cpsr), +	REG_OFFSET_NAME(ORIG_r0), +	REG_OFFSET_END, +}; + +/** + * regs_query_register_offset() - query register offset from its name + * @name:	the name of a register + * + * regs_query_register_offset() returns the offset of a register in struct + * pt_regs from its name. If the name is invalid, this returns -EINVAL; + */ +int regs_query_register_offset(const char *name) +{ +	const struct pt_regs_offset *roff; +	for (roff = regoffset_table; roff->name != NULL; roff++) +		if (!strcmp(roff->name, name)) +			return roff->offset; +	return -EINVAL; +} + +/** + * regs_query_register_name() - query register name from its offset + * @offset:	the offset of a register in struct pt_regs. + * + * regs_query_register_name() returns the name of a register from its + * offset in struct pt_regs. If the @offset is invalid, this returns NULL; + */ +const char *regs_query_register_name(unsigned int offset) +{ +	const struct pt_regs_offset *roff; +	for (roff = regoffset_table; roff->name != NULL; roff++) +		if (roff->offset == offset) +			return roff->name; +	return NULL; +} + +/** + * regs_within_kernel_stack() - check the address in the stack + * @regs:      pt_regs which contains kernel stack pointer. + * @addr:      address which is checked. + * + * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). + * If @addr is within the kernel stack, it returns true. If not, returns false. + */ +bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) +{ +	return ((addr & ~(THREAD_SIZE - 1))  == +		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))); +} + +/** + * regs_get_kernel_stack_nth() - get Nth entry of the stack + * @regs:	pt_regs which contains kernel stack pointer. + * @n:		stack entry number. + * + * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which + * is specified by @regs. If the @n th entry is NOT in the kernel stack, + * this returns 0. + */ +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) +{ +	unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); +	addr += n; +	if (regs_within_kernel_stack(regs, (unsigned long)addr)) +		return *addr; +	else +		return 0; +} +  /*   * this routine will get a word off of the processes privileged stack.   * the offset is how far from the base addr as stored in the THREAD. diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S index 61930eb0902..fd26f8d6515 100644 --- a/arch/arm/kernel/relocate_kernel.S +++ b/arch/arm/kernel/relocate_kernel.S @@ -10,6 +10,12 @@ relocate_new_kernel:  	ldr	r0,kexec_indirection_page  	ldr	r1,kexec_start_address +	/* +	 * If there is no indirection page (we are doing crashdumps) +	 * skip any relocation. +	 */ +	cmp	r0, #0 +	beq	2f  0:	/* top, read another word for the indirection page */  	ldr	r3, [r0],#4 diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 122d999bdc7..776ea1aa974 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -19,12 +19,15 @@  #include <linux/seq_file.h>  #include <linux/screen_info.h>  #include <linux/init.h> +#include <linux/kexec.h> +#include <linux/crash_dump.h>  #include <linux/root_dev.h>  #include <linux/cpu.h>  #include <linux/interrupt.h>  #include <linux/smp.h>  #include <linux/fs.h>  #include <linux/proc_fs.h> +#include <linux/memblock.h>  #include <asm/unified.h>  #include <asm/cpu.h> @@ -269,6 +272,21 @@ static void __init cacheid_init(void)  extern struct proc_info_list *lookup_processor_type(unsigned int);  extern struct machine_desc *lookup_machine_type(unsigned int); +static void __init feat_v6_fixup(void) +{ +	int id = read_cpuid_id(); + +	if ((id & 0xff0f0000) != 0x41070000) +		return; + +	/* +	 * HWCAP_TLS is available only on 1136 r1p0 and later, +	 * see also kuser_get_tls_init. +	 */ +	if ((((id >> 4) & 0xfff) == 0xb36) && (((id >> 20) & 3) == 0)) +		elf_hwcap &= ~HWCAP_TLS; +} +  static void __init setup_processor(void)  {  	struct proc_info_list *list; @@ -311,6 +329,8 @@ static void __init setup_processor(void)  	elf_hwcap &= ~HWCAP_THUMB;  #endif +	feat_v6_fixup(); +  	cacheid_init();  	cpu_proc_init();  } @@ -402,13 +422,12 @@ static int __init arm_add_memory(unsigned long start, unsigned long size)  	size -= start & ~PAGE_MASK;  	bank->start = PAGE_ALIGN(start);  	bank->size  = size & PAGE_MASK; -	bank->node  = PHYS_TO_NID(start);  	/*  	 * Check whether this memory region has non-zero size or  	 * invalid node number.  	 */ -	if (bank->size == 0 || bank->node >= MAX_NUMNODES) +	if (bank->size == 0)  		return -EINVAL;  	meminfo.nr_banks++; @@ -663,6 +682,79 @@ static int __init customize_machine(void)  }  arch_initcall(customize_machine); +#ifdef CONFIG_KEXEC +static inline unsigned long long get_total_mem(void) +{ +	unsigned long total; + +	total = max_low_pfn - min_low_pfn; +	return total << PAGE_SHIFT; +} + +/** + * reserve_crashkernel() - reserves memory are for crash kernel + * + * This function reserves memory area given in "crashkernel=" kernel command + * line parameter. The memory reserved is used by a dump capture kernel when + * primary kernel is crashing. + */ +static void __init reserve_crashkernel(void) +{ +	unsigned long long crash_size, crash_base; +	unsigned long long total_mem; +	int ret; + +	total_mem = get_total_mem(); +	ret = parse_crashkernel(boot_command_line, total_mem, +				&crash_size, &crash_base); +	if (ret) +		return; + +	ret = reserve_bootmem(crash_base, crash_size, BOOTMEM_EXCLUSIVE); +	if (ret < 0) { +		printk(KERN_WARNING "crashkernel reservation failed - " +		       "memory is in use (0x%lx)\n", (unsigned long)crash_base); +		return; +	} + +	printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " +	       "for crashkernel (System RAM: %ldMB)\n", +	       (unsigned long)(crash_size >> 20), +	       (unsigned long)(crash_base >> 20), +	       (unsigned long)(total_mem >> 20)); + +	crashk_res.start = crash_base; +	crashk_res.end = crash_base + crash_size - 1; +	insert_resource(&iomem_resource, &crashk_res); +} +#else +static inline void reserve_crashkernel(void) {} +#endif /* CONFIG_KEXEC */ + +/* + * Note: elfcorehdr_addr is not just limited to vmcore. It is also used by + * is_kdump_kernel() to determine if we are booting after a panic. Hence + * ifdef it under CONFIG_CRASH_DUMP and not CONFIG_PROC_VMCORE. + */ + +#ifdef CONFIG_CRASH_DUMP +/* + * elfcorehdr= specifies the location of elf core header stored by the crashed + * kernel. This option will be passed by kexec loader to the capture kernel. + */ +static int __init setup_elfcorehdr(char *arg) +{ +	char *end; + +	if (!arg) +		return -EINVAL; + +	elfcorehdr_addr = memparse(arg, &end); +	return end > arg ? 0 : -EINVAL; +} +early_param("elfcorehdr", setup_elfcorehdr); +#endif /* CONFIG_CRASH_DUMP */ +  void __init setup_arch(char **cmdline_p)  {  	struct tag *tags = (struct tag *)&init_tags; @@ -716,12 +808,15 @@ void __init setup_arch(char **cmdline_p)  	parse_early_param(); +	arm_memblock_init(&meminfo, mdesc); +  	paging_init(mdesc);  	request_standard_resources(&meminfo, mdesc);  #ifdef CONFIG_SMP  	smp_init_cpus();  #endif +	reserve_crashkernel();  	cpu_init();  	tcm_init(); @@ -729,6 +824,7 @@ void __init setup_arch(char **cmdline_p)  	/*  	 * Set up various architecture-specific pointers  	 */ +	arch_nr_irqs = mdesc->nr_irqs;  	init_arch_irq = mdesc->init_irq;  	system_timer = mdesc->timer;  	init_machine = mdesc->init_machine; diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 1621e5327b2..cda78d59aa3 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -30,6 +30,7 @@  #include <asm/unistd.h>  #include <asm/traps.h>  #include <asm/unwind.h> +#include <asm/tls.h>  #include "ptrace.h"  #include "signal.h" @@ -518,17 +519,20 @@ asmlinkage int arm_syscall(int no, struct pt_regs *regs)  	case NR(set_tls):  		thread->tp_value = regs->ARM_r0; -#if defined(CONFIG_HAS_TLS_REG) -		asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) ); -#elif !defined(CONFIG_TLS_REG_EMUL) -		/* -		 * User space must never try to access this directly. -		 * Expect your app to break eventually if you do so. -		 * The user helper at 0xffff0fe0 must be used instead. -		 * (see entry-armv.S for details) -		 */ -		*((unsigned int *)0xffff0ff0) = regs->ARM_r0; -#endif +		if (tls_emu) +			return 0; +		if (has_tls_reg) { +			asm ("mcr p15, 0, %0, c13, c0, 3" +				: : "r" (regs->ARM_r0)); +		} else { +			/* +			 * User space must never try to access this directly. +			 * Expect your app to break eventually if you do so. +			 * The user helper at 0xffff0fe0 must be used instead. +			 * (see entry-armv.S for details) +			 */ +			*((unsigned int *)0xffff0ff0) = regs->ARM_r0; +		}  		return 0;  #ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG @@ -743,6 +747,16 @@ void __init trap_init(void)  	return;  } +static void __init kuser_get_tls_init(unsigned long vectors) +{ +	/* +	 * vectors + 0xfe0 = __kuser_get_tls +	 * vectors + 0xfe8 = hardware TLS instruction at 0xffff0fe8 +	 */ +	if (tls_emu || has_tls_reg) +		memcpy((void *)vectors + 0xfe0, (void *)vectors + 0xfe8, 4); +} +  void __init early_trap_init(void)  {  	unsigned long vectors = CONFIG_VECTORS_BASE; @@ -761,6 +775,11 @@ void __init early_trap_init(void)  	memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);  	/* +	 * Do processor specific fixups for the kuser helpers +	 */ +	kuser_get_tls_init(vectors); + +	/*  	 * Copy signal return handlers into the vector page, and  	 * set sigreturn to be a pointer to these.  	 */  |