[PATCH v2 3/3] RISC-V: Add crash kernel support
Palmer Dabbelt
palmer at dabbelt.com
Fri Jul 10 23:59:23 EDT 2020
On Tue, 23 Jun 2020 08:05:12 PDT (-0700), mick at ics.forth.gr wrote:
> This patch allows Linux to act as a crash kernel for use with
> kdump. Userspace will let the crash kernel know about the
> memory region it can use through linux,usable-memory property
> on the /memory node (overriding its reg property), and about the
> memory region where the elf core header of the previous kernel
> is saved, through a reserved-memory node with a compatible string
> of "linux,elfcorehdr". This approach is the least invasive and
> re-uses functionality already present.
>
> I tested this on riscv64 qemu and it works as expected, you
> may test it by retrieving the dmesg of the previous kernel
> through /proc/vmcore, using the vmcore-dmesg utility from
> kexec-tools.
>
> v2:
> * Use linux,usable-memory on /memory instead of a new binding
> * Use a reserved-memory node for ELF core header
>
> Signed-off-by: Nick Kossifidis <mick at ics.forth.gr>
> ---
> arch/riscv/Kconfig | 10 ++++++++
> arch/riscv/kernel/Makefile | 1 +
> arch/riscv/kernel/crash_dump.c | 46 ++++++++++++++++++++++++++++++++++
> arch/riscv/kernel/setup.c | 35 +++++++++++++++++++++++---
> arch/riscv/mm/init.c | 33 ++++++++++++++++++++++++
> 5 files changed, 122 insertions(+), 3 deletions(-)
> create mode 100644 arch/riscv/kernel/crash_dump.c
>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index e7dd2d1c5..6c59987f0 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -353,6 +353,16 @@ config KEXEC
>
> The name comes from the similarity to the exec system call.
>
> +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/admin-guide/kdump/kdump.rst
>
> endmenu
>
> diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> index b9a3842ad..f067f55f1 100644
> --- a/arch/riscv/kernel/Makefile
> +++ b/arch/riscv/kernel/Makefile
> @@ -53,5 +53,6 @@ endif
> obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o
> obj-$(CONFIG_KGDB) += kgdb.o
> obj-${CONFIG_KEXEC} += kexec_relocate.o crash_save_regs.o machine_kexec.o
> +obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
>
> clean:
> diff --git a/arch/riscv/kernel/crash_dump.c b/arch/riscv/kernel/crash_dump.c
> new file mode 100644
> index 000000000..81b9d2a71
> --- /dev/null
> +++ b/arch/riscv/kernel/crash_dump.c
> @@ -0,0 +1,46 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * This code comes from arch/arm64/kernel/crash_dump.c
Which is "GPL-2.0-only", not "GPL-2.0". I just sent out a patch set to make
this generic, as a handful of architectures use it exactly (and some others may
be able to).
> + * Created by: AKASHI Takahiro <takahiro.akashi at linaro.org>
> + * Copyright (C) 2017 Linaro Limited
> + */
> +
> +#include <linux/crash_dump.h>
> +#include <linux/io.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;
> +}
> diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
> index 4dc940389..469470b0b 100644
> --- a/arch/riscv/kernel/setup.c
> +++ b/arch/riscv/kernel/setup.c
> @@ -63,6 +63,9 @@ static struct resource code_res = { .name = "Kernel code", };
> static struct resource data_res = { .name = "Kernel data", };
> static struct resource rodata_res = { .name = "Kernel rodata", };
> static struct resource bss_res = { .name = "Kernel bss", };
> +#ifdef CONFIG_CRASH_DUMP
> +static struct resource elfcorehdr_res = { .name = "ELF Core hdr", };
> +#endif
>
> static int __init add_resource(struct resource *parent,
> struct resource *res)
> @@ -133,6 +135,16 @@ static int __init add_crashk_resource(struct resource *res)
> }
> #endif
>
> +#ifdef CONFIG_CRASH_DUMP
> +static int __init add_elfcorehdr_resource(struct resource *res)
> +{
> + if (res->start <= elfcorehdr_res.start && res->end >= elfcorehdr_res.end)
> + return add_resource(&iomem_resource, &elfcorehdr_res);
> +
> + return 0;
> +}
> +#endif
> +
> static void __init init_resources(void)
> {
> struct memblock_region *region = NULL;
> @@ -155,6 +167,14 @@ static void __init init_resources(void)
> bss_res.end = __pa_symbol(__bss_stop) - 1;
> bss_res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
>
> +#ifdef CONFIG_CRASH_DUMP
> + if (elfcorehdr_size) {
> + elfcorehdr_res.start = elfcorehdr_addr;
> + elfcorehdr_res.end = elfcorehdr_addr + elfcorehdr_size - 1;
> + elfcorehdr_res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
> + }
> +#endif
> +
> /*
> * Start by adding the reserved regions, if they overlap
> * with /memory regions, insert_resource later on will take
> @@ -185,6 +206,14 @@ static void __init init_resources(void)
> continue;
> #endif
>
> +#ifdef CONFIG_CRASH_DUMP
> + ret = add_elfcorehdr_resource(res);
> + if (ret < 0)
> + goto error;
> + else if (ret)
> + continue;
> +#endif
> +
> /*
> * Ignore any other reserved regions within
> * system memory.
> diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
> index e4ce3a2af..c85ddfe44 100644
> --- a/arch/riscv/mm/init.c
> +++ b/arch/riscv/mm/init.c
> @@ -13,6 +13,7 @@
> #include <linux/swap.h>
> #include <linux/sizes.h>
> #include <linux/of_fdt.h>
> +#include <linux/of_reserved_mem.h>
> #include <linux/libfdt.h>
> #include <linux/set_memory.h>
> #include <linux/crash_dump.h>
> @@ -123,6 +124,26 @@ static void __init setup_initrd(void)
> }
> #endif /* CONFIG_BLK_DEV_INITRD */
>
> +#ifdef CONFIG_CRASH_DUMP
> +/*
> + * We keep track of the ELF core header of the crashed
> + * kernel with a reserved-memory region with compatible
> + * string "linux,elfcorehdr". Here we register a callback
> + * to populate elfcorehdr_addr/size when this region is
> + * present. Note that this region will be marked as
> + * reserved once we call early_init_fdt_scan_reserved_mem()
> + * later on.
> + */
> +static int elfcore_hdr_setup(struct reserved_mem *rmem)
> +{
> + elfcorehdr_addr = rmem->base;
> + elfcorehdr_size = rmem->size;
> + return 0;
> +}
> +
> +RESERVEDMEM_OF_DECLARE(elfcorehdr, "linux,elfcorehdr", elfcore_hdr_setup);
> +#endif
> +
> static phys_addr_t dtb_early_pa __initdata;
>
> void __init setup_bootmem(void)
> @@ -537,6 +558,18 @@ static void __init reserve_crashkernel(void)
>
> int ret = 0;
>
> + /*
> + * Don't reserve a region for a crash kernel on a crash kernel
> + * since it doesn't make much sense and we have limited memory
> + * resources.
> + */
> +#ifdef CONFIG_CRASH_DUMP
> + if (is_kdump_kernel()) {
> + pr_info("crashkernel: ignoring reservation request\n");
> + return;
> + }
> +#endif
> +
> ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
> &crash_size, &crash_base);
> if (ret || !crash_size)
Reviewed-by: Palmer Dabbelt <palmerdabbelt at google.com>
More information about the linux-riscv
mailing list