diff options
author | AKASHI Takahiro <takahiro.akashi@linaro.org> | 2017-04-02 22:24:38 -0400 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2017-04-05 13:31:38 -0400 |
commit | e62aaeac426ab1ddbdde524797b2a7835f606d91 (patch) | |
tree | 75448116e9973b7ae28fe063aa363b9ae2ea2c88 | |
parent | 20a166243328c14a0c24bd8c7919223ab4174917 (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/Kconfig | 11 | ||||
-rw-r--r-- | arch/arm64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/crash_dump.c | 71 | ||||
-rw-r--r-- | arch/arm64/mm/init.c | 53 |
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 | ||
739 | config 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 | |||
739 | config XEN_DOM0 | 750 | config 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 |
53 | arm64-obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o | 53 | arm64-obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o |
54 | arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o | 54 | arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o |
55 | arm64-obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | ||
55 | 56 | ||
56 | obj-y += $(arm64-obj-y) vdso/ probes/ | 57 | obj-y += $(arm64-obj-y) vdso/ probes/ |
57 | obj-m += $(arm64-obj-m) | 58 | obj-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 | */ | ||
31 | ssize_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 | */ | ||
67 | ssize_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 | ||
170 | static 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, ®); | ||
184 | elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, ®); | ||
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 | */ | ||
197 | static 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 | ||
215 | static 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(); |