Linux-RISC-V Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Nick Kossifidis <mick@ics.forth.gr>
To: linux-riscv@lists.infradead.org
Cc: david.abdurachmanov@sifive.com, anup@brainfault.org,
	atish.patra@wdc.com, yibin_liu@c-sky.com, palmer@dabbelt.com,
	paul.walmsley@sifive.com, Nick Kossifidis <mick@ics.forth.gr>
Subject: [PATCH 3/3] RISC-V: Add crash kernel support
Date: Fri, 24 Apr 2020 20:12:14 +0300	[thread overview]
Message-ID: <20200424171214.1515457-4-mick@ics.forth.gr> (raw)
In-Reply-To: <20200424171214.1515457-1-mick@ics.forth.gr>

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-range
property, and about the memory region where the elf core
header of the previous kernel is saved, through the
linux,elfcorehdr property (both on /chosen). These dtb bindings
are also used on arm64.

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.

Signed-off-by: Nick Kossifidis <mick@ics.forth.gr>
---
 arch/riscv/Kconfig             | 10 ++++
 arch/riscv/kernel/Makefile     |  4 +-
 arch/riscv/kernel/crash_dump.c | 46 ++++++++++++++++
 arch/riscv/kernel/setup.c      | 13 +++++
 arch/riscv/mm/init.c           | 97 +++++++++++++++++++++++++++++++++-
 5 files changed, 167 insertions(+), 3 deletions(-)
 create mode 100644 arch/riscv/kernel/crash_dump.c

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 4934b2f62..3802c8888 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -344,6 +344,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 65f6c9f1d..6374b0db5 100644
--- a/arch/riscv/kernel/Makefile
+++ b/arch/riscv/kernel/Makefile
@@ -51,6 +51,6 @@ ifeq ($(CONFIG_RISCV_SBI), y)
 obj-$(CONFIG_SMP) += cpu_ops_sbi.o
 endif
 obj-$(CONFIG_HOTPLUG_CPU)	+= cpu-hotplug.o
-obj-${CONFIG_KEXEC}		+= kexec_relocate.o crash_save_regs.o machine_kexec.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 is taken from arch/arm64/kernel/crash_dump.c
+ * Created by: AKASHI Takahiro <takahiro.akashi@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 52d057bde..cbd8c8ba6 100644
--- a/arch/riscv/kernel/setup.c
+++ b/arch/riscv/kernel/setup.c
@@ -64,6 +64,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 void __init kdump_resource_init(void)
 {
@@ -97,6 +100,16 @@ static void __init kdump_resource_init(void)
 	if (crashk_res.end > crashk_res.start)
 		insert_resource(&sysram_res, &crashk_res);
 #endif
+
+#ifdef CONFIG_CRASH_DUMP
+	if (elfcorehdr_size) {
+		elfcorehdr_res.start = elfcorehdr_addr;
+		elfcorehdr_res.end = elfcorehdr_addr + elfcorehdr_size;
+		elfcorehdr_res.flags = IORESOURCE_BUSY | IORESOURCE_SYSTEM_RAM;
+		insert_resource(&sysram_res, &elfcorehdr_res);
+	}
+#endif
+
 }
 
 void __init parse_dtb(void)
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 1c4461590..93cd434c5 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -123,6 +123,80 @@ static void __init setup_initrd(void)
 }
 #endif /* CONFIG_BLK_DEV_INITRD */
 
+#ifdef CONFIG_CRASH_DUMP
+/*
+ * These come from arm64
+ */
+static int __init early_init_dt_scan_elfcorehdr(unsigned long node,
+		const char *uname, int depth, void *data)
+{
+	struct memblock_region *elfcorehdr_mem = 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_mem->base = dt_mem_next_cell(dt_root_addr_cells, &reg);
+	elfcorehdr_mem->size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+	return 1;
+}
+
+static void __init reserve_elfcorehdr(void)
+{
+	struct memblock_region elfcorehdr_region = {0};
+
+	of_scan_flat_dt(early_init_dt_scan_elfcorehdr, &elfcorehdr_region);
+
+	if (elfcorehdr_region.size) {
+		memblock_reserve(elfcorehdr_region.base,
+				 elfcorehdr_region.size);
+
+		pr_info("Reserving %lldKB of memory at 0x%llx for elfcorehdr\n",
+			elfcorehdr_region.size >> 10, elfcorehdr_region.base);
+
+		elfcorehdr_addr = elfcorehdr_region.base;
+		elfcorehdr_size = elfcorehdr_region.size;
+	}
+}
+
+static int __init early_init_dt_scan_usablemem(unsigned long node,
+		const char *uname, int depth, void *data)
+{
+	struct memblock_region *usable_mem = data;
+	const __be32 *reg;
+	int len;
+
+	if (depth != 1 || strcmp(uname, "chosen") != 0)
+		return 0;
+
+	reg = of_get_flat_dt_prop(node, "linux,usable-memory-range", &len);
+	if (!reg || (len < (dt_root_addr_cells + dt_root_size_cells)))
+		return 1;
+
+	usable_mem->base = dt_mem_next_cell(dt_root_addr_cells, &reg);
+	usable_mem->size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+	return 1;
+}
+
+static void __init fdt_enforce_memory_region(void)
+{
+	struct memblock_region usablemem_region = {0};
+
+	of_scan_flat_dt(early_init_dt_scan_usablemem, &usablemem_region);
+
+	if (usablemem_region.size)
+		memblock_cap_memory_range(usablemem_region.base,
+					  usablemem_region.size);
+}
+#endif
+
 static phys_addr_t dtb_early_pa __initdata;
 
 void __init setup_bootmem(void)
@@ -132,6 +206,9 @@ void __init setup_bootmem(void)
 	phys_addr_t vmlinux_end = __pa_symbol(&_end);
 	phys_addr_t vmlinux_start = __pa_symbol(&_start);
 
+#ifdef CONFIG_CRASH_DUMP
+	fdt_enforce_memory_region();
+#endif
 	/* Find the memory region containing the kernel */
 	for_each_memblock(memory, reg) {
 		phys_addr_t end = reg->base + reg->size;
@@ -166,6 +243,13 @@ void __init setup_bootmem(void)
 	 */
 	memblock_reserve(dtb_early_pa, fdt_totalsize(dtb_early_va));
 
+	/*
+	 * We need to reserve elfcorehdr here so that it doesn't
+	 * get overwritten later on.
+	 */
+#ifdef CONFIG_CRASH_DUMP
+	reserve_elfcorehdr();
+#endif
 	early_init_fdt_scan_reserved_mem();
 	memblock_allow_resize();
 	memblock_dump_all();
@@ -559,9 +643,20 @@ static void __init reserve_crashkernel(void)
 	unsigned long start_pfn = find_min_pfn_with_active_regions();
 	unsigned long search_start = start_pfn << PAGE_SHIFT;
 	unsigned long search_end = (unsigned long) PFN_PHYS(max_low_pfn) - 1;
-
 	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: ignore reservation request\n");
+		return;
+	}
+#endif
+
 	ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
 				&crash_size, &crash_base);
 	if (ret || !crash_size)
-- 
2.24.1



  parent reply	other threads:[~2020-04-24 17:12 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-04-24 17:12 [PATCH 0/3] RISC-V: Add kexec/kdump support Nick Kossifidis
2020-04-24 17:12 ` [PATCH 1/3] RISC-V: Add kexec support Nick Kossifidis
2020-04-24 17:12 ` [PATCH 2/3] RISC-V: Add kdump support Nick Kossifidis
2020-04-24 17:12 ` Nick Kossifidis [this message]
2020-05-21 18:42 ` [PATCH 0/3] RISC-V: Add kexec/kdump support Nick Kossifidis
2020-06-11 19:09   ` Nick Kossifidis
2020-06-11 19:39     ` Palmer Dabbelt
2020-06-13  7:24       ` Nick Kossifidis
2020-06-13  8:17         ` Anup Patel
2020-06-13  8:21           ` Nick Kossifidis
2020-06-18 23:06         ` Palmer Dabbelt
2020-06-19  8:27           ` Nick Kossifidis
2020-06-19 17:23             ` Atish Patra

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200424171214.1515457-4-mick@ics.forth.gr \
    --to=mick@ics.forth.gr \
    --cc=anup@brainfault.org \
    --cc=atish.patra@wdc.com \
    --cc=david.abdurachmanov@sifive.com \
    --cc=linux-riscv@lists.infradead.org \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=yibin_liu@c-sky.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox