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/machine_kexec.c | 4 | ||||
| -rw-r--r-- | arch/arm/kernel/relocate_kernel.S | 6 | ||||
| -rw-r--r-- | arch/arm/kernel/setup.c | 82 | 
5 files changed, 151 insertions, 2 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/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index df5958f6864..1fc74cbd1a1 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -39,6 +39,10 @@ void machine_kexec_cleanup(struct kimage *image)  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/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 648c3c1e16c..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> @@ -419,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++; @@ -680,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; @@ -733,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();  |