arm64: kdump: provide /proc/vmcore file
authorAKASHI Takahiro <takahiro.akashi@linaro.org>
Mon, 3 Apr 2017 02:24:38 +0000 (11:24 +0900)
committerCatalin Marinas <catalin.marinas@arm.com>
Wed, 5 Apr 2017 17:31:38 +0000 (18:31 +0100)
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>
arch/arm64/Kconfig
arch/arm64/kernel/Makefile
arch/arm64/kernel/crash_dump.c [new file with mode: 0644]
arch/arm64/mm/init.c

index 3741859765cfe050d2c4a174d613ff90e1074be0..e7f043efff416c3f978a6ec2da2e0afb9ab14139 100644 (file)
@@ -736,6 +736,17 @@ config KEXEC
          but it is independent of the system firmware.   And like a reboot
          you can start any kernel with it, not just Linux.
 
+config CRASH_DUMP
+       bool "Build kdump crash kernel"
+       help
+         Generate crash dump after being started by kexec. This should
+         be normally only set in special crash dump kernels which are
+         loaded in the main kernel with kexec-tools into a specially
+         reserved region and then later executed after a crash by
+         kdump/kexec.
+
+         For more details see Documentation/kdump/kdump.txt
+
 config XEN_DOM0
        def_bool y
        depends on XEN
index aaaf06b117e38fe9f120c123590cca58341f9293..1dcb69d3d0e59ac7ed6bff935b6b06450e9c68be 100644 (file)
@@ -52,6 +52,7 @@ arm64-obj-$(CONFIG_KEXEC)             += machine_kexec.o relocate_kernel.o    \
                                           cpu-reset.o
 arm64-obj-$(CONFIG_ARM64_RELOC_TEST)   += arm64-reloc-test.o
 arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
+arm64-obj-$(CONFIG_CRASH_DUMP)         += crash_dump.o
 
 obj-y                                  += $(arm64-obj-y) vdso/ probes/
 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 (file)
index 0000000..f46d57c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Routines for doing kexec-based kdump
+ *
+ * Copyright (C) 2017 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * 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/crash_dump.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/memblock.h>
+#include <linux/uaccess.h>
+#include <asm/memory.h>
+
+/**
+ * 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 in a 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 = memremap(__pfn_to_phys(pfn), PAGE_SIZE, MEMREMAP_WB);
+       if (!vaddr)
+               return -ENOMEM;
+
+       if (userbuf) {
+               if (copy_to_user((char __user *)buf, vaddr + offset, csize)) {
+                       memunmap(vaddr);
+                       return -EFAULT;
+               }
+       } else {
+               memcpy(buf, vaddr + offset, csize);
+       }
+
+       memunmap(vaddr);
+
+       return csize;
+}
+
+/**
+ * elfcorehdr_read - read from ELF core header
+ * @buf: buffer where the data is placed
+ * @csize: number of bytes to read
+ * @ppos: address in the memory
+ *
+ * This function reads @count bytes from elf core header which exists
+ * on crash dump kernel's memory.
+ */
+ssize_t elfcorehdr_read(char *buf, size_t count, u64 *ppos)
+{
+       memcpy(buf, phys_to_virt((phys_addr_t)*ppos), count);
+       return count;
+}
index 89ba3cd0fe448795bf49ff99a1c13e4e78ef8de5..5960bef0170df85916d0c1ac3b65f570f0af67ea 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/kexec.h>
+#include <linux/crash_dump.h>
 
 #include <asm/boot.h>
 #include <asm/fixmap.h>
@@ -165,6 +166,56 @@ static void __init kexec_reserve_crashkres_pages(void)
 }
 #endif /* CONFIG_KEXEC_CORE */
 
+#ifdef CONFIG_CRASH_DUMP
+static int __init early_init_dt_scan_elfcorehdr(unsigned long node,
+               const char *uname, int depth, void *data)
+{
+       const __be32 *reg;
+       int len;
+
+       if (depth != 1 || strcmp(uname, "chosen") != 0)
+               return 0;
+
+       reg = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len);
+       if (!reg || (len < (dt_root_addr_cells + dt_root_size_cells)))
+               return 1;
+
+       elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &reg);
+       elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+       return 1;
+}
+
+/*
+ * reserve_elfcorehdr() - reserves memory for elf core header
+ *
+ * This function reserves the memory occupied by an elf core header
+ * described in the device tree. This region contains all the
+ * information about primary kernel's core image and is used by a dump
+ * capture kernel to access the system memory on primary kernel.
+ */
+static void __init reserve_elfcorehdr(void)
+{
+       of_scan_flat_dt(early_init_dt_scan_elfcorehdr, NULL);
+
+       if (!elfcorehdr_size)
+               return;
+
+       if (memblock_is_region_reserved(elfcorehdr_addr, elfcorehdr_size)) {
+               pr_warn("elfcorehdr is overlapped\n");
+               return;
+       }
+
+       memblock_reserve(elfcorehdr_addr, elfcorehdr_size);
+
+       pr_info("Reserving %lldKB of memory at 0x%llx for elfcorehdr\n",
+               elfcorehdr_size >> 10, elfcorehdr_addr);
+}
+#else
+static void __init reserve_elfcorehdr(void)
+{
+}
+#endif /* CONFIG_CRASH_DUMP */
 /*
  * Return the maximum physical address for ZONE_DMA (DMA_BIT_MASK(32)). It
  * currently assumes that for memory starting above 4G, 32-bit devices will
@@ -423,6 +474,8 @@ void __init arm64_memblock_init(void)
 
        reserve_crashkernel();
 
+       reserve_elfcorehdr();
+
        dma_contiguous_reserve(arm64_dma_phys_limit);
 
        memblock_allow_resize();