aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAKASHI Takahiro <takahiro.akashi@linaro.org>2017-04-02 22:24:38 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2017-04-05 13:31:38 -0400
commite62aaeac426ab1ddbdde524797b2a7835f606d91 (patch)
tree75448116e9973b7ae28fe063aa363b9ae2ea2c88
parent20a166243328c14a0c24bd8c7919223ab4174917 (diff)
arm64: kdump: provide /proc/vmcore file
Arch-specific functions are added to allow for implementing a crash dump file interface, /proc/vmcore, which can be viewed as a ELF file. A user space tool, like kexec-tools, is responsible for allocating a separate region for the core's ELF header within crash kdump kernel memory and filling it in when executing kexec_load(). Then, its location will be advertised to crash dump kernel via a new device-tree property, "linux,elfcorehdr", and crash dump kernel preserves the region for later use with reserve_elfcorehdr() at boot time. On crash dump kernel, /proc/vmcore will access the primary kernel's memory with copy_oldmem_page(), which feeds the data page-by-page by ioremap'ing it since it does not reside in linear mapping on crash dump kernel. Meanwhile, elfcorehdr_read() is simple as the region is always mapped. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Reviewed-by: James Morse <james.morse@arm.com> Acked-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--arch/arm64/Kconfig11
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/crash_dump.c71
-rw-r--r--arch/arm64/mm/init.c53
4 files changed, 136 insertions, 0 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3741859765cf..e7f043efff41 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -736,6 +736,17 @@ config KEXEC
736 but it is independent of the system firmware. And like a reboot 736 but it is independent of the system firmware. And like a reboot
737 you can start any kernel with it, not just Linux. 737 you can start any kernel with it, not just Linux.
738 738
739config CRASH_DUMP
740 bool "Build kdump crash kernel"
741 help
742 Generate crash dump after being started by kexec. This should
743 be normally only set in special crash dump kernels which are
744 loaded in the main kernel with kexec-tools into a specially
745 reserved region and then later executed after a crash by
746 kdump/kexec.
747
748 For more details see Documentation/kdump/kdump.txt
749
739config XEN_DOM0 750config XEN_DOM0
740 def_bool y 751 def_bool y
741 depends on XEN 752 depends on XEN
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index aaaf06b117e3..1dcb69d3d0e5 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -52,6 +52,7 @@ arm64-obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o \
52 cpu-reset.o 52 cpu-reset.o
53arm64-obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o 53arm64-obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o
54arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o 54arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
55arm64-obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
55 56
56obj-y += $(arm64-obj-y) vdso/ probes/ 57obj-y += $(arm64-obj-y) vdso/ probes/
57obj-m += $(arm64-obj-m) 58obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/crash_dump.c b/arch/arm64/kernel/crash_dump.c
new file mode 100644
index 000000000000..f46d57c31443
--- /dev/null
+++ b/arch/arm64/kernel/crash_dump.c
@@ -0,0 +1,71 @@
1/*
2 * Routines for doing kexec-based kdump
3 *
4 * Copyright (C) 2017 Linaro Limited
5 * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/crash_dump.h>
13#include <linux/errno.h>
14#include <linux/io.h>
15#include <linux/memblock.h>
16#include <linux/uaccess.h>
17#include <asm/memory.h>
18
19/**
20 * copy_oldmem_page() - copy one page from old kernel memory
21 * @pfn: page frame number to be copied
22 * @buf: buffer where the copied page is placed
23 * @csize: number of bytes to copy
24 * @offset: offset in bytes into the page
25 * @userbuf: if set, @buf is in a user address space
26 *
27 * This function copies one page from old kernel memory into buffer pointed by
28 * @buf. If @buf is in userspace, set @userbuf to %1. Returns number of bytes
29 * copied or negative error in case of failure.
30 */
31ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
32 size_t csize, unsigned long offset,
33 int userbuf)
34{
35 void *vaddr;
36
37 if (!csize)
38 return 0;
39
40 vaddr = memremap(__pfn_to_phys(pfn), PAGE_SIZE, MEMREMAP_WB);
41 if (!vaddr)
42 return -ENOMEM;
43
44 if (userbuf) {
45 if (copy_to_user((char __user *)buf, vaddr + offset, csize)) {
46 memunmap(vaddr);
47 return -EFAULT;
48 }
49 } else {
50 memcpy(buf, vaddr + offset, csize);
51 }
52
53 memunmap(vaddr);
54
55 return csize;
56}
57
58/**
59 * elfcorehdr_read - read from ELF core header
60 * @buf: buffer where the data is placed
61 * @csize: number of bytes to read
62 * @ppos: address in the memory
63 *
64 * This function reads @count bytes from elf core header which exists
65 * on crash dump kernel's memory.
66 */
67ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos)
68{
69 memcpy(buf, phys_to_virt((phys_addr_t)*ppos), count);
70 return count;
71}
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 89ba3cd0fe44..5960bef0170d 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -39,6 +39,7 @@
39#include <linux/vmalloc.h> 39#include <linux/vmalloc.h>
40#include <linux/mm.h> 40#include <linux/mm.h>
41#include <linux/kexec.h> 41#include <linux/kexec.h>
42#include <linux/crash_dump.h>
42 43
43#include <asm/boot.h> 44#include <asm/boot.h>
44#include <asm/fixmap.h> 45#include <asm/fixmap.h>
@@ -165,6 +166,56 @@ static void __init kexec_reserve_crashkres_pages(void)
165} 166}
166#endif /* CONFIG_KEXEC_CORE */ 167#endif /* CONFIG_KEXEC_CORE */
167 168
169#ifdef CONFIG_CRASH_DUMP
170static int __init early_init_dt_scan_elfcorehdr(unsigned long node,
171 const char *uname, int depth, void *data)
172{
173 const __be32 *reg;
174 int len;
175
176 if (depth != 1 || strcmp(uname, "chosen") != 0)
177 return 0;
178
179 reg = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len);
180 if (!reg || (len < (dt_root_addr_cells + dt_root_size_cells)))
181 return 1;
182
183 elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &reg);
184 elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &reg);
185
186 return 1;
187}
188
189/*
190 * reserve_elfcorehdr() - reserves memory for elf core header
191 *
192 * This function reserves the memory occupied by an elf core header
193 * described in the device tree. This region contains all the
194 * information about primary kernel's core image and is used by a dump
195 * capture kernel to access the system memory on primary kernel.
196 */
197static void __init reserve_elfcorehdr(void)
198{
199 of_scan_flat_dt(early_init_dt_scan_elfcorehdr, NULL);
200
201 if (!elfcorehdr_size)
202 return;
203
204 if (memblock_is_region_reserved(elfcorehdr_addr, elfcorehdr_size)) {
205 pr_warn("elfcorehdr is overlapped\n");
206 return;
207 }
208
209 memblock_reserve(elfcorehdr_addr, elfcorehdr_size);
210
211 pr_info("Reserving %lldKB of memory at 0x%llx for elfcorehdr\n",
212 elfcorehdr_size >> 10, elfcorehdr_addr);
213}
214#else
215static void __init reserve_elfcorehdr(void)
216{
217}
218#endif /* CONFIG_CRASH_DUMP */
168/* 219/*
169 * Return the maximum physical address for ZONE_DMA (DMA_BIT_MASK(32)). It 220 * Return the maximum physical address for ZONE_DMA (DMA_BIT_MASK(32)). It
170 * currently assumes that for memory starting above 4G, 32-bit devices will 221 * currently assumes that for memory starting above 4G, 32-bit devices will
@@ -423,6 +474,8 @@ void __init arm64_memblock_init(void)
423 474
424 reserve_crashkernel(); 475 reserve_crashkernel();
425 476
477 reserve_elfcorehdr();
478
426 dma_contiguous_reserve(arm64_dma_phys_limit); 479 dma_contiguous_reserve(arm64_dma_phys_limit);
427 480
428 memblock_allow_resize(); 481 memblock_allow_resize();