From 7033d5da29de3ac009d36a9e5e516aaa6d58712d Mon Sep 17 00:00:00 2001 From: Mingzheng Xing Date: Thu, 18 Apr 2024 11:26:52 +0800 Subject: [PATCH] kdump: Add riscv64 support This patch provides support for enabling the kdump service on riscv. After starting the kdump service, the kdump kernel can be successfully loaded, and after triggering a kernel crash, the kdump kernel can be started normally and the vmcore can be captured. Due to some issues with initrd in kexec-tools, avoid it first to provide basic riscv architecture kdump support. Tested in openeuler riscv qemu, and can also pass Mugen testing. Signed-off-by: Mingzheng Xing --- kdump.sysconfig.riscv64 | 37 + kexec-tools-Add-riscv-support.patch | 1992 +++++++++++++++++++++++++++ kexec-tools.spec | 28 +- riscv-makedumpfile-1.7.3.patch | 910 ++++++++++++ 4 files changed, 2960 insertions(+), 7 deletions(-) create mode 100644 kdump.sysconfig.riscv64 create mode 100644 kexec-tools-Add-riscv-support.patch create mode 100644 riscv-makedumpfile-1.7.3.patch diff --git a/kdump.sysconfig.riscv64 b/kdump.sysconfig.riscv64 new file mode 100644 index 0000000..1e65df8 --- /dev/null +++ b/kdump.sysconfig.riscv64 @@ -0,0 +1,37 @@ +# Kernel Version string for the -kdump kernel, such as 2.6.13-1544.FC5kdump +# If no version is specified, then the init script will try to find a +# kdump kernel with the same version number as the running kernel. +KDUMP_KERNELVER="" + +# The kdump commandline is the command line that needs to be passed off to +# the kdump kernel. This will likely match the contents of the grub kernel +# line. For example: +# KDUMP_COMMANDLINE="ro root=LABEL=/" +# Dracut depends on proper root= options, so please make sure that appropriate +# root= options are copied from /proc/cmdline. In general it is best to append +# command line options using "KDUMP_COMMANDLINE_APPEND=". +# If a command line is not specified, the default will be taken from +# /proc/cmdline +KDUMP_COMMANDLINE="" + +# This variable lets us remove arguments from the current kdump commandline +# as taken from either KDUMP_COMMANDLINE above, or from /proc/cmdline +# NOTE: some arguments such as crashkernel will always be removed +KDUMP_COMMANDLINE_REMOVE="hugepages hugepagesz slub_debug quiet" + +# This variable lets us append arguments to the current kdump commandline +# after processed by KDUMP_COMMANDLINE_REMOVE +KDUMP_COMMANDLINE_APPEND="irqpoll nr_cpus=1 reset_devices novmcoredd noinitrd" + +# Any additional kexec arguments required. In most situations, this should +# be left empty +# +# Example: +# KEXEC_ARGS="--elf32-core-headers" +KEXEC_ARGS="" + +#Where to find the boot image +#KDUMP_BOOTDIR="/boot" + +#What is the image type used for kdump +KDUMP_IMG="vmlinuz" diff --git a/kexec-tools-Add-riscv-support.patch b/kexec-tools-Add-riscv-support.patch new file mode 100644 index 0000000..2411292 --- /dev/null +++ b/kexec-tools-Add-riscv-support.patch @@ -0,0 +1,1992 @@ +From 82e95543fb855e5686630ca4d86b0f55f8ac45cb Mon Sep 17 00:00:00 2001 +From: Mingzheng Xing +Date: Wed, 17 Apr 2024 15:42:31 +0800 +Subject: [PATCH] kexec-tools Add riscv support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The riscv support of kexec-tools has not entered the upstream mainline. +The support patch comes from the following commits from the upstream +community, which providing preliminary support for the riscv arch. + +Author: Nick Kossifidis +Date: Thu Oct 20 11:15:48 2022 +0800 + + RISC-V: Add support for riscv kexec/kdump on kexec-tools + + This patch adds support for loading the ELF kernel image. It parses + the current/provided device tree to determine the system's memory + layout, and /proc/iomem for the various kernel segments. + + This patch was firstly developed by Nick Kossifidis, and two fixes ( + 1: fail to find free memory area for dtb load when using initrd image, + lists.infradead.org/pipermail/linux-riscv/2022-August/018398.html; + 2: fix memory range size calculation, + kexec/arch/riscv/crashdump-riscv.c:line 85 + ) are contributed by Yixun Lan, Xianting Tian. + + Tested on Qemu's rv64 virt machine and SoC of T-Head RISC-V Xuantie 910 CPU. + + Tested-by: Yixun Lan + Co-developed-by: Xianting Tian + Co-developed-by: Yixun Lan + Signed-off-by: Nick Kossifidis + Signed-off-by: Simon Horman + +Author: Simon Horman +Date: Fri Oct 21 17:02:23 2022 +0200 + + local: RISC-V: distribute purgatory/riscv/Makefile + + Include purgatory/riscv/Makefile in distribution tarball. + + Local patch as it is planned to suggest this as a fix for + the patch that introduced this problem. + + http://lists.infradead.org/pipermail/kexec/2022-October/026057.html + + Signed-off-by: Simon Horman + +Author: Song Shuai +Date: Fri Aug 11 18:19:09 2023 +0800 + + RISC-V: Some fixes for riscv port + + RISC-V: Fix the undeclared ‘EM_RISCV’ build failure + =================================================== + + Use local `elf.h` instead of `linux/elf.h` to fix this build error: + + ``` + kexec/arch/riscv/crashdump-riscv.c:17:13: error: ‘EM_RISCV’ undeclared here (not in a function); did you mean ‘EM_CRIS’? + .machine = EM_RISCV, + ^~~~~~~~ + EM_CRIS + ``` + + RISC-V: Correct the usage of command line option + ================================================ + + RISC-V process OPT_CMDLINE with the "command-line" partten, + but the riscv_opts_usage shows the "cmdline" option. + So correct the usage's output. + + RISC-V: Use linux,usable-memory-range for crash kernel + ====================================================== + + Now we use "memeory::linux,usable-memory" to indicate the available + memory for the crash kernel. + + While booting with UEFI, the crash kernel would use efi.memmap to + re-populate memblock and then first kernel's memory would be corrputed. + Consequently, the /proc/vmcore file failed to create in my local test. + + And according to "chosen" dtschema [1], the available memory for the + crash kernel should be held via "chosen::linux,usable-memory-range" + property which will re-cap memblock even after UEFI's re-population. + + [1]: https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/chosen.yaml + + RISC-V: Get memory ranges from iomem + ==================================== + + When booting with UEFI, Linux marks the Runtime Code/Data memory + as no-map and then exports it to "Reserved" iomem_resource. + + Kexc-tools uses dtb_get_memory_ranges() function to get memory ranges + via parsing dtb, but it can't see the Reserved EFI Runtime memory. + That would corrupt EFI Runtime memory and fail the kexeced kernel to + deal EFI stuff. + + In my test, the kexeced kernel warned "efi: System table signature incorrect!" + and then paniced at efi_call_rts() due to the null efi.runtime. + + So we should use /proc/iomem to get memory ranges. + + Signed-off-by: Song Shuai + +Author: Li Zhengyu +Date: Thu Aug 31 18:35:43 2023 +0800 + + RISC-V: Enable kexec_file_load syscall + + Create prepare_kexec_file_options() function to prepare the options + to kexec_file_load syscall, and it would be used in elf_riscv_load() + or the future image_riscv_load(). + + The patch comes from the RISC-V Linux kernel_file_load support[1], + So its author should be Li Zhengyu. + + [1]: https://lore.kernel.org/all/20220408100914.150110-1-lizhengyu3@huawei.com/ + + Signed-off-by: Song Shuai + +Author: Song Shuai +Date: Wed Sep 13 15:53:39 2023 +0800 + + RISC-V: Separate elf_riscv_find_pbase out + + The is the preparative patch for RISC-V kexec Image file support. + + Separate the elf_riscv_find_pbase() function out to + allow kernel_load syscall load Image binary file. + + Signed-off-by: Song Shuai + +Author: Song Shuai +Date: Wed Sep 13 11:31:47 2023 +0800 + + RISC-V: Support loading Image binary file + + Add image-riscv file_type to probe/load Image file type, + + As for kexec_load, find the pbase aligned text_offset from image header + and prepare segments for this syscall. + + for kexec_file_load, setup the related options and let kernel part to + deal with the Image. + + Signed-off-by: Song Shuai + +Author: Chen Jiahao +Date: Sat Mar 18 19:49:40 2023 +0800 + + RISC-V: Fix locate_hole failed when loading device tree + + When adding the device tree in load_extra_segments, the min_usable + address should start from initrd_base, i.e. the start of target + memory range. While the max_usable should still be the max_addr + of search range. + + Signed-off-by: Chen Jiahao + +Author: Chen Jiahao +Date: Sat Mar 18 23:23:34 2023 +0800 + + kexec: Add support for crashkernel=X,[high,low] on RISC-V + + When allocating crash kernel above 4G memory, more than one iomem + region will be reserved for crash kernel. Support parsing high memory + address for kernel ELF. + + Signed-off-by: Chen Jiahao + +Link: https://lore.kernel.org/linux-riscv/20230726175000.2536220-1-chenjiahao16@huawei.com +Link: https://lore.kernel.org/kexec/20221020031548.47587-1-xianting.tian@linux.alibaba.com +Signed-off-by: Mingzheng Xing +--- + configure.ac | 3 + + include/elf.h | 1 + + kexec/Makefile | 1 + + kexec/arch/riscv/Makefile | 37 ++ + kexec/arch/riscv/crashdump-riscv.c | 140 +++++++ + kexec/arch/riscv/image-header.h | 88 +++++ + kexec/arch/riscv/include/arch/options.h | 43 +++ + kexec/arch/riscv/iomem.h | 10 + + kexec/arch/riscv/kexec-elf-riscv.c | 184 +++++++++ + kexec/arch/riscv/kexec-image-riscv.c | 95 +++++ + kexec/arch/riscv/kexec-riscv.c | 478 ++++++++++++++++++++++++ + kexec/arch/riscv/kexec-riscv.h | 53 +++ + kexec/dt-ops.c | 442 +++++++++++++++++++++- + kexec/dt-ops.h | 7 + + kexec/kexec-iomem.c | 4 +- + kexec/kexec-syscall.h | 7 + + purgatory/Makefile | 1 + + purgatory/arch/riscv/Makefile | 7 + + 18 files changed, 1598 insertions(+), 3 deletions(-) + create mode 100644 kexec/arch/riscv/Makefile + create mode 100644 kexec/arch/riscv/crashdump-riscv.c + create mode 100644 kexec/arch/riscv/image-header.h + create mode 100644 kexec/arch/riscv/include/arch/options.h + create mode 100644 kexec/arch/riscv/iomem.h + create mode 100644 kexec/arch/riscv/kexec-elf-riscv.c + create mode 100644 kexec/arch/riscv/kexec-image-riscv.c + create mode 100644 kexec/arch/riscv/kexec-riscv.c + create mode 100644 kexec/arch/riscv/kexec-riscv.h + create mode 100644 purgatory/arch/riscv/Makefile + +diff --git a/configure.ac b/configure.ac +index 767574e..a3e4e88 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -61,6 +61,9 @@ case $target_cpu in + loongarch*) + ARCH="loongarch" + ;; ++ riscv32|riscv64) ++ ARCH="riscv" ++ ;; + * ) + AC_MSG_ERROR([unsupported architecture $target_cpu]) + ;; +diff --git a/include/elf.h b/include/elf.h +index 1c8d2cc..93a5ee5 100644 +--- a/include/elf.h ++++ b/include/elf.h +@@ -259,6 +259,7 @@ typedef struct + #define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ + #define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ + #define EM_AARCH64 183 /* ARM AARCH64 */ ++#define EM_RISCV 243 /* RISC-V */ + #define EM_LOONGARCH 258 /* Loongson Loongarch*/ + #define EM_NUM 184 + +diff --git a/kexec/Makefile b/kexec/Makefile +index 8a52e8d..405864a 100644 +--- a/kexec/Makefile ++++ b/kexec/Makefile +@@ -88,6 +88,7 @@ include $(srcdir)/kexec/arch/mips/Makefile + include $(srcdir)/kexec/arch/cris/Makefile + include $(srcdir)/kexec/arch/ppc/Makefile + include $(srcdir)/kexec/arch/ppc64/Makefile ++include $(srcdir)/kexec/arch/riscv/Makefile + include $(srcdir)/kexec/arch/s390/Makefile + include $(srcdir)/kexec/arch/sh/Makefile + include $(srcdir)/kexec/arch/x86_64/Makefile +diff --git a/kexec/arch/riscv/Makefile b/kexec/arch/riscv/Makefile +new file mode 100644 +index 0000000..37ef760 +--- /dev/null ++++ b/kexec/arch/riscv/Makefile +@@ -0,0 +1,37 @@ ++# ++# kexec riscv ++# ++riscv_KEXEC_SRCS = kexec/arch/riscv/kexec-riscv.c ++riscv_KEXEC_SRCS += kexec/arch/riscv/kexec-elf-riscv.c ++riscv_KEXEC_SRCS += kexec/arch/riscv/kexec-image-riscv.c ++riscv_KEXEC_SRCS += kexec/arch/riscv/crashdump-riscv.c ++ ++riscv_MEM_REGIONS = kexec/mem_regions.c ++ ++riscv_DT_OPS += kexec/dt-ops.c ++ ++riscv_ARCH_REUSE_INITRD = ++ ++riscv_CPPFLAGS += -I $(srcdir)/kexec/ ++ ++dist += kexec/arch/riscv/Makefile $(riscv_KEXEC_SRCS) \ ++ kexec/arch/riscv/kexec-riscv.h \ ++ kexec/arch/riscv/image-header.h \ ++ kexec/arch/riscv/include/arch/options.h ++ ++ifdef HAVE_LIBFDT ++ ++LIBS += -lfdt ++ ++else ++ ++include $(srcdir)/kexec/libfdt/Makefile.libfdt ++ ++libfdt_SRCS += $(LIBFDT_SRCS:%=kexec/libfdt/%) ++ ++riscv_CPPFLAGS += -I$(srcdir)/kexec/libfdt ++ ++riscv_KEXEC_SRCS += $(libfdt_SRCS) ++ ++endif ++ +diff --git a/kexec/arch/riscv/crashdump-riscv.c b/kexec/arch/riscv/crashdump-riscv.c +new file mode 100644 +index 0000000..336d7a7 +--- /dev/null ++++ b/kexec/arch/riscv/crashdump-riscv.c +@@ -0,0 +1,140 @@ ++#include ++#include ++#include ++ ++#include "kexec.h" ++#include "crashdump.h" ++#include "kexec-elf.h" ++#include "mem_regions.h" ++ ++static struct crash_elf_info elf_info = { ++#if __riscv_xlen == 64 ++ .class = ELFCLASS64, ++#else ++ .class = ELFCLASS32, ++#endif ++ .data = ELFDATA2LSB, ++ .machine = EM_RISCV, ++}; ++ ++static struct memory_ranges crash_mem_ranges = {0}; ++struct memory_range elfcorehdr_mem = {0}; ++ ++static unsigned long long get_page_offset(struct kexec_info *info) ++{ ++ unsigned long long vaddr_off = 0; ++ unsigned long long page_size = sysconf(_SC_PAGESIZE); ++ unsigned long long init_start = get_kernel_sym("_sinittext"); ++ ++ /* ++ * Begining of init section is aligned to page size ++ */ ++ vaddr_off = init_start - page_size; ++ ++ return vaddr_off; ++} ++ ++int load_elfcorehdr(struct kexec_info *info) ++{ ++ struct memory_range crashkern_range = {0}; ++ struct memory_range *ranges = NULL; ++ unsigned long start = 0; ++ unsigned long end = 0; ++ unsigned long buf_size = 0; ++ unsigned long elfcorehdr_addr = 0; ++ void* buf = NULL; ++ int i = 0; ++ int ret = 0; ++ ++ ret = parse_iomem_single("Kernel code\n", &start, NULL); ++ if (ret) { ++ fprintf(stderr, "Cannot determine kernel physical base addr\n"); ++ return -EINVAL; ++ } ++ elf_info.kern_paddr_start = start; ++ ++ ret = parse_iomem_single("Kernel bss\n", NULL, &end); ++ if (ret) { ++ fprintf(stderr, "Cannot determine kernel physical bss addr\n"); ++ return -EINVAL; ++ } ++ elf_info.kern_paddr_start = start; ++ elf_info.kern_size = end - start; ++ ++ elf_info.kern_vaddr_start = get_kernel_sym("_text"); ++ if (!elf_info.kern_vaddr_start) { ++ elf_info.kern_vaddr_start = UINT64_MAX; ++ } ++ ++ elf_info.page_offset = get_page_offset(info); ++ dbgprintf("page_offset: %016llx\n", elf_info.page_offset); ++ ++ ret = parse_iomem_single("Crash kernel\n", &start, &end); ++ if (ret) { ++ fprintf(stderr, "Cannot determine kernel physical bss addr\n"); ++ return -EINVAL; ++ } ++ crashkern_range.start = start; ++ crashkern_range.end = end; ++ crashkern_range.type = RANGE_RESERVED; ++ ++ ranges = info->memory_range; ++ for (i = 0; i < info->memory_ranges; i++) { ++ ret = mem_regions_alloc_and_add(&crash_mem_ranges, ++ ranges[i].start, ++ ranges[i].end - ranges[i].start + 1, ++ ranges[i].type); ++ if (ret ) { ++ fprintf(stderr, "Could not create crash_mem_ranges\n"); ++ return ret; ++ } ++ } ++ ++ ret = mem_regions_alloc_and_exclude(&crash_mem_ranges, ++ &crashkern_range); ++ if (ret) { ++ fprintf(stderr, "Could not exclude crashkern_range\n"); ++ return ret; ++ } ++ ++#if __riscv_xlen == 64 ++ crash_create_elf64_headers(info, &elf_info, crash_mem_ranges.ranges, ++ crash_mem_ranges.size, &buf, &buf_size, ++ ELF_CORE_HEADER_ALIGN); ++ ++#else ++ crash_create_elf32_headers(info, &elf_info, crash_mem_ranges.ranges, ++ crash_mem_ranges.size, &buf, &buf_size, ++ ELF_CORE_HEADER_ALIGN); ++#endif ++ ++ ++ elfcorehdr_addr = add_buffer_phys_virt(info, buf, buf_size, ++ buf_size, 0, ++ crashkern_range.start, ++ crashkern_range.end, ++ -1, 0); ++ ++ elfcorehdr_mem.start = elfcorehdr_addr; ++ elfcorehdr_mem.end = elfcorehdr_addr + buf_size - 1; ++ ++ dbgprintf("%s: elfcorehdr 0x%llx-0x%llx\n", __func__, ++ elfcorehdr_mem.start, elfcorehdr_mem.end); ++ ++ return 0; ++} ++ ++int is_crashkernel_mem_reserved(void) ++{ ++ uint64_t start = 0; ++ uint64_t end = 0; ++ ++ return parse_iomem_single("Crash kernel\n", &start, &end) == 0 ? ++ (start != end) : 0; ++} ++ ++int get_crash_kernel_load_range(uint64_t *start, uint64_t *end) ++{ ++ return parse_iomem_single("Crash kernel\n", start, end); ++} ++ +diff --git a/kexec/arch/riscv/image-header.h b/kexec/arch/riscv/image-header.h +new file mode 100644 +index 0000000..a677546 +--- /dev/null ++++ b/kexec/arch/riscv/image-header.h +@@ -0,0 +1,88 @@ ++/* ++ * RISCV64 binary image header. ++ * token from arm64/image-header.h ++ */ ++ ++#if !defined(__RISCV_IMAGE_HEADER_H) ++#define __RISCV_IMAGE_HEADER_H ++ ++#include ++#include ++ ++/** ++ * struct riscv_image_header - riscv kernel image header. ++ * ++ **/ ++struct riscv_image_header { ++ uint32_t code0; ++ uint32_t code1; ++ uint64_t text_offset; ++ uint64_t image_size; ++ uint64_t flags; ++ uint32_t version; ++ uint32_t res1; ++ uint64_t res2; ++ uint64_t magic; ++ uint32_t magic2; ++ uint32_t res3; ++}; ++ ++#define RISCV_IMAGE_MAGIC 0x5643534952 ++#define RISCV_IMAGE_MAGIC2 0x05435352 ++ ++#define RISCV_HEADER_VERSION_MAJOR 0 ++#define RISCV_HEADER_VERSION_MINOR 2 ++ ++#define RISCV_HEADER_VERSION (RISCV_HEADER_VERSION_MAJOR << 16 | \ ++ RISCV_HEADER_VERSION_MINOR) ++ ++ ++static const uint64_t riscv_image_flag_be = (1UL << 0); ++ ++/** ++ * riscv_header_check_magic - Helper to check the riscv image header. ++ * ++ * Returns non-zero if header is OK. ++ */ ++ ++static inline int riscv_header_check_magic(const struct riscv_image_header *h) ++{ ++ if (!h) ++ return 0; ++ ++ return (h->version >= RISCV_HEADER_VERSION && h->magic2 == RISCV_IMAGE_MAGIC2); ++} ++ ++/** ++ * riscv_header_check_endiannes - Helper to check the riscv image header. ++ * ++ * Returns non-zero if the image was built as big endian. ++ */ ++ ++static inline int riscv_header_check_endiannes(const struct riscv_image_header *h) ++{ ++ if (!h) ++ return 0; ++ ++ return (le64toh(h->flags) & riscv_image_flag_be) >> 0; ++} ++ ++ ++ ++static inline uint64_t riscv_header_text_offset(const struct riscv_image_header *h) ++{ ++ if (!h) ++ return 0; ++ ++ return le64toh(h->text_offset); ++} ++ ++static inline uint64_t riscv_header_image_size(const struct riscv_image_header *h) ++{ ++ if (!h) ++ return 0; ++ ++ return le64toh(h->image_size); ++} ++ ++#endif +diff --git a/kexec/arch/riscv/include/arch/options.h b/kexec/arch/riscv/include/arch/options.h +new file mode 100644 +index 0000000..7c24184 +--- /dev/null ++++ b/kexec/arch/riscv/include/arch/options.h +@@ -0,0 +1,43 @@ ++#ifndef KEXEC_ARCH_RISCV_OPTIONS_H ++#define KEXEC_ARCH_RISCV_OPTIONS_H ++ ++#define OPT_APPEND ((OPT_MAX)+0) ++#define OPT_DTB ((OPT_MAX)+1) ++#define OPT_INITRD ((OPT_MAX)+2) ++#define OPT_CMDLINE ((OPT_MAX)+3) ++#define OPT_REUSE_CMDLINE ((OPT_MAX)+4) ++#define OPT_ARCH_MAX ((OPT_MAX)+5) ++ ++/* Options relevant to the architecture (excluding loader-specific ones), ++ * in this case none: ++ */ ++#define KEXEC_ARCH_OPTIONS \ ++ KEXEC_OPTIONS \ ++ { "append", 1, 0, OPT_APPEND}, \ ++ { "dtb", 1, 0, OPT_DTB }, \ ++ { "initrd", 1, 0, OPT_INITRD }, \ ++ { "command-line", 1, 0, OPT_CMDLINE}, \ ++ { "reuse-cmdline", 0, NULL, OPT_REUSE_CMDLINE }, \ ++ ++ ++#define KEXEC_ARCH_OPT_STR KEXEC_OPT_STR "" ++ ++/* The following two #defines list ALL of the options added by all of the ++ * architecture's loaders. ++ * o main() uses this complete list to scan for its options, ignoring ++ * arch-specific/loader-specific ones. ++ * o Then, arch_process_options() uses this complete list to scan for its ++ * options, ignoring general/loader-specific ones. ++ * o Then, the file_type[n].load re-scans for options, using ++ * KEXEC_ARCH_OPTIONS plus its loader-specific options subset. ++ * Any unrecognised options cause an error here. ++ * ++ * This is done so that main()'s/arch_process_options()'s getopt_long() calls ++ * don't choose a kernel filename from random arguments to options they don't ++ * recognise -- as they now recognise (if not act upon) all possible options. ++ */ ++#define KEXEC_ALL_OPTIONS KEXEC_ARCH_OPTIONS ++ ++#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR ++ ++#endif /* KEXEC_ARCH_RISCV_OPTIONS_H */ +diff --git a/kexec/arch/riscv/iomem.h b/kexec/arch/riscv/iomem.h +new file mode 100644 +index 0000000..7671e26 +--- /dev/null ++++ b/kexec/arch/riscv/iomem.h +@@ -0,0 +1,10 @@ ++#ifndef IOMEM_H ++#define IOMEM_H ++ ++#define SYSTEM_RAM "System RAM\n" ++#define KERNEL_CODE "Kernel code\n" ++#define KERNEL_DATA "Kernel data\n" ++#define CRASH_KERNEL "Crash kernel\n" ++#define IOMEM_RESERVED "Reserved\n" ++ ++#endif +diff --git a/kexec/arch/riscv/kexec-elf-riscv.c b/kexec/arch/riscv/kexec-elf-riscv.c +new file mode 100644 +index 0000000..434873c +--- /dev/null ++++ b/kexec/arch/riscv/kexec-elf-riscv.c +@@ -0,0 +1,184 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2019 FORTH-ICS/CARV ++ * Nick Kossifidis ++ */ ++ ++#include "kexec.h" ++#include "dt-ops.h" /* For dtb_set/clear_initrd() */ ++#include /* For ELF header handling */ ++#include /* For EFBIG/EINVAL */ ++#include /* For getpagesize() */ ++#include "kexec-syscall.h" /* For KEXEC_ON_CRASH */ ++#include "kexec-riscv.h" ++ ++/**************\ ++* ENTRY POINTS * ++\**************/ ++ ++int elf_riscv_probe(const char *buf, off_t len) ++{ ++ struct mem_ehdr ehdr = {0}; ++ int ret = 0; ++ ++ ret = build_elf_exec_info(buf, len, &ehdr, 0); ++ if (ret < 0) ++ goto cleanup; ++ ++ if (ehdr.e_machine != EM_RISCV) { ++ fprintf(stderr, "Not for this architecture.\n"); ++ ret = -EINVAL; ++ goto cleanup; ++ } ++ ++ ret = 0; ++ ++ cleanup: ++ free_elf_info(&ehdr); ++ return ret; ++} ++ ++void elf_riscv_usage(void) ++{ ++} ++ ++ ++int elf_riscv_load(int argc, char **argv, const char *buf, off_t len, ++ struct kexec_info *info) ++{ ++ struct mem_ehdr ehdr = {0}; ++ struct mem_phdr *phdr = NULL; ++ off_t new_base_addr = 0; ++ off_t kernel_size = 0; ++ off_t page_size = getpagesize(); ++ off_t max_addr = 0; ++ off_t old_base_addr = 0; ++ off_t old_start_addr = 0; ++ int i = 0; ++ int ret = 0; ++ ++ if (info->file_mode) { ++ return prepare_kexec_file_options(info); ++ } ++ ++ /* Parse the ELF file */ ++ ret = build_elf_exec_info(buf, len, &ehdr, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ELF exec parse failed\n"); ++ return -EINVAL; ++ } ++ ++ max_addr = elf_max_addr(&ehdr); ++ old_base_addr = max_addr; ++ old_start_addr = max_addr; ++ ++ /* ++ * Get the memory footprint, base physical ++ * and start address of the ELF image ++ */ ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ phdr = &ehdr.e_phdr[i]; ++ if (phdr->p_type != PT_LOAD) ++ continue; ++ ++ /* ++ * Note: According to ELF spec the loadable regions ++ * are sorted on p_vaddr, not p_paddr. ++ */ ++ if (old_base_addr > phdr->p_paddr) ++ old_base_addr = phdr->p_paddr; ++ ++ if (phdr->p_vaddr == ehdr.e_entry || ++ phdr->p_paddr == ehdr.e_entry) ++ old_start_addr = phdr->p_paddr; ++ ++ kernel_size += _ALIGN_UP(phdr->p_memsz, page_size); ++ } ++ ++ if (old_base_addr == max_addr || kernel_size == 0) { ++ fprintf(stderr, "No loadable segments present on the " ++ "provided ELF image\n"); ++ return -EINVAL; ++ } ++ ++ if (old_start_addr == max_addr) { ++ fprintf(stderr, "Could not find the entry point address of " ++ "provided ELF image\n"); ++ return -EINVAL; ++ } ++ ++ dbgprintf("Got ELF with total memsz %luKB\n" ++ "Base paddr: 0x%lX, start_addr: 0x%lX\n", ++ kernel_size / 1024, old_base_addr, old_start_addr); ++ ++ /* Get a continuous physical region that can hold the kernel */ ++ ret = riscv_find_pbase(info, &new_base_addr, kernel_size, KERNEL_ALIGN); ++ if (ret < 0) { ++ fprintf(stderr, "Could not find a memory region for the " ++ "provided ELF image\n"); ++ return ret; ++ } ++ ++ dbgprintf("New base paddr for the ELF: 0x%lX\n", new_base_addr); ++ ++ /* Re-set the base physical address of the ELF */ ++ for (i = 0; i < ehdr.e_phnum; i++) { ++ phdr = &ehdr.e_phdr[i]; ++ if (phdr->p_type != PT_LOAD) ++ continue; ++ ++ phdr->p_paddr -= old_base_addr; ++ phdr->p_paddr += new_base_addr; ++ } ++ ++ /* Re-set the entry point address */ ++ ehdr.e_entry = (old_start_addr - old_base_addr) + new_base_addr; ++ info->entry = (void *) ehdr.e_entry; ++ dbgprintf("New entry point for the ELF: 0x%llX\n", ehdr.e_entry); ++ ++ ++ /* Load the ELF executable */ ++ ret = elf_exec_load(&ehdr, info); ++ if (ret < 0) { ++ fprintf(stderr, "ELF exec load failed\n"); ++ return ret; ++ } ++ ++ ret = load_extra_segments(info, new_base_addr, ++ kernel_size, max_addr); ++ return ret; ++} ++ ++ ++/*******\ ++* STUBS * ++\*******/ ++ ++int machine_verify_elf_rel(struct mem_ehdr *ehdr) ++{ ++ if (ehdr->ei_data != ELFDATA2LSB) ++ return 0; ++#if __riscv_xlen == 64 ++ if (ehdr->ei_class != ELFCLASS64) ++#else ++ if (ehdr->ei_class != ELFCLASS32) ++#endif ++ return 0; ++ if (ehdr->e_machine != EM_RISCV) ++ return 0; ++ return 1; ++} ++ ++void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr), ++ struct mem_sym *UNUSED(sym), ++ unsigned long r_type, ++ void *UNUSED(location), ++ unsigned long UNUSED(address), ++ unsigned long UNUSED(value)) ++{ ++ switch (r_type) { ++ default: ++ die("Unknown rela relocation: %lu\n", r_type); ++ break; ++ } ++} +diff --git a/kexec/arch/riscv/kexec-image-riscv.c b/kexec/arch/riscv/kexec-image-riscv.c +new file mode 100644 +index 0000000..6ae7e57 +--- /dev/null ++++ b/kexec/arch/riscv/kexec-image-riscv.c +@@ -0,0 +1,95 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * RISC-V kexec binary image support. ++ * ++ * Author: Song Shuai ++ */ ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include "image-header.h" ++#include "kexec.h" ++#include "kexec-riscv.h" ++#include "kexec-syscall.h" ++#include "arch/options.h" ++ ++int image_riscv_probe(const char *kernel_buf, off_t kernel_size) ++{ ++ const struct riscv_image_header *h; ++ ++ if (kernel_size < sizeof(struct riscv_image_header)) { ++ dbgprintf("%s: No riscv image header.\n", __func__); ++ return -1; ++ } ++ ++ h = (const struct riscv_image_header *)(kernel_buf); ++ ++ if (!riscv_header_check_magic(h)) { ++ dbgprintf("%s: Bad riscv image header.\n", __func__); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int image_riscv_load(int argc, char **argv, const char *kernel_buf, ++ off_t kernel_size, struct kexec_info *info) ++{ ++ const struct riscv_image_header *h; ++ unsigned long text_offset, image_size; ++ off_t new_base_addr = 0; ++ ++ int ret; ++ ++ if (info->file_mode) { ++ return prepare_kexec_file_options(info); ++ } ++ ++ h = (const struct riscv_image_header *)(kernel_buf); ++ ++ /* Check header */ ++ if (!h->image_size){ ++ dbgprintf("Kernel image size is NULL\n"); ++ ret = EFAILED; ++ goto exit; ++ } ++ ++ if(riscv_header_check_endiannes(h)){ ++ dbgprintf("Kernel image was built as big endian\n"); ++ ret = EFAILED; ++ goto exit; ++ } ++ ++ text_offset = riscv_header_text_offset(h); ++ image_size = riscv_header_image_size(h); ++ ++ /* Setup the entry and segments */ ++ ++ ret = riscv_find_pbase(info, &new_base_addr, image_size, text_offset); ++ if (ret < 0) { ++ fprintf(stderr, "Could not find a memory region for the " ++ "provided Image\n"); ++ goto exit; ++ } ++ ++ info->entry = (void *) new_base_addr; ++ dbgprintf("Entry point for the Image: 0x%lX\n", new_base_addr); ++ ++ add_segment(info, kernel_buf, kernel_size, new_base_addr, image_size); ++ ++ ret = load_extra_segments(info, text_offset, image_size, ULONG_MAX); ++exit: ++ if (ret) ++ fprintf(stderr, "kexec: load failed.\n"); ++ return ret; ++} ++ ++void image_riscv_usage(void) ++{ ++ printf( ++" An RISC-V binary image, uncompressed, little endian.\n" ++" Typically an Image file.\n\n"); ++} +diff --git a/kexec/arch/riscv/kexec-riscv.c b/kexec/arch/riscv/kexec-riscv.c +new file mode 100644 +index 0000000..f5e0262 +--- /dev/null ++++ b/kexec/arch/riscv/kexec-riscv.c +@@ -0,0 +1,478 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2019 FORTH-ICS/CARV ++ * Nick Kossifidis ++ */ ++ ++#include "kexec-syscall.h" /* For KEXEC_ARCH_RISCV */ ++#include "kexec.h" /* For OPT_MAX and concat_cmdline() */ ++#include "mem_regions.h" /* For mem_regions_sort() */ ++#include "dt-ops.h" /* For dtb_set_bootargs() */ ++#include /* For KEXEC_ARCH_OPTIONS */ ++#include /* For struct option */ ++#include /* For stat() and struct stat */ ++#include /* For free() */ ++#include /* For EINVAL */ ++#include /* For DeviceTree handling */ ++#include "kexec-riscv.h" ++#include "iomem.h" ++#include ++#include ++#include ++#ifndef _O_BINARY ++#define _O_BINARY 0 ++#endif ++ ++ ++ ++const struct arch_map_entry arches[] = { ++ { "riscv32", KEXEC_ARCH_RISCV }, ++ { "riscv64", KEXEC_ARCH_RISCV }, ++ { NULL, 0 }, ++}; ++ ++ ++struct file_type file_type[] = { ++ {"elf-riscv", elf_riscv_probe, elf_riscv_load, elf_riscv_usage}, ++ {"image-riscv", image_riscv_probe, image_riscv_load, image_riscv_usage}, ++}; ++int file_types = sizeof(file_type) / sizeof(file_type[0]); ++ ++static const char riscv_opts_usage[] = ++" --append=STRING Append STRING to the kernel command line.\n" ++" --dtb=FILE Use FILE as the device tree blob.\n" ++" --initrd=FILE Use FILE as the kernel initial ramdisk.\n" ++" --command-line=STRING Use STRING as the kernel's command line.\n" ++" --reuse-cmdline Use kernel command line from running system.\n"; ++ ++static struct riscv_opts arch_options = {0}; ++static struct fdt_image provided_fdt = {0}; ++ ++/****************\ ++* COMMON HELPERS * ++\****************/ ++ ++/* ++ * Go through the available physical memory regions and ++ * find one that can hold an image of the specified size ++ * and start address should be aligned up with `align`. ++ * Note: This is called after get_memory_ranges so ++ * info->memory_range[] should be populated. Also note that ++ * memory ranges are sorted, so we'll return the first region ++ * that's big enough for holding the image. ++ */ ++int riscv_find_pbase(struct kexec_info *info, off_t *addr, ++ off_t size, off_t align) ++{ ++ int i = 0; ++ off_t start = 0; ++ off_t end = 0; ++ int ret = 0; ++ ++ /* ++ * If this image is for a crash kernel, use the region ++ * the primary kernel has already reserved for us. ++ */ ++ if (info->kexec_flags & KEXEC_ON_CRASH) { ++ ret = get_crash_kernel_load_range((uint64_t *) &start, ++ (uint64_t *) &end); ++ if (!ret) { ++ start = _ALIGN_UP(start, align); ++ ++ if (end > start && ((end - start) >= size)) { ++ *addr = start; ++ return 0; ++ } ++ ++ return -EFBIG; ++ } else ++ return ENOCRASHKERNEL; ++ } ++ ++ for (i = 0; i < info->memory_ranges; i++) { ++ if (info->memory_range[i].type != RANGE_RAM) ++ continue; ++ ++ start = info->memory_range[i].start; ++ end = info->memory_range[i].end; ++ ++ start = _ALIGN_UP(start, align); ++ ++ if (end > start && ((end - start) >= size)) { ++ *addr = start; ++ return 0; ++ } ++ } ++ ++ return -EFBIG; ++} ++ ++int load_extra_segments(struct kexec_info *info, uint64_t kernel_base, ++ uint64_t kernel_size, uint64_t max_addr) ++{ ++ struct fdt_image *fdt = arch_options.fdt; ++ char *initrd_buf = NULL; ++ off_t initrd_size = 0; ++ uint64_t initrd_base = 0; ++ uint64_t start = 0; ++ uint64_t end = 0; ++ uint64_t min_usable = kernel_base + kernel_size; ++ uint64_t max_usable = max_addr; ++ int ret = 0; ++ ++ /* Prepare the device tree */ ++ if (info->kexec_flags & KEXEC_ON_CRASH) { ++ ret = load_elfcorehdr(info); ++ if (ret) { ++ fprintf(stderr, "Couldn't create elfcorehdr\n"); ++ return ret; ++ } ++ ++ ret = dtb_add_range_property(&fdt->buf, &fdt->size, ++ elfcorehdr_mem.start, elfcorehdr_mem.end, ++ "chosen", "linux,elfcorehdr"); ++ if (ret) { ++ fprintf(stderr, "Couldn't add elfcorehdr to fdt\n"); ++ return ret; ++ } ++ ++ ret = get_crash_kernel_load_range(&start, &end); ++ if (ret) { ++ fprintf(stderr, "Couldn't get crashkenel region\n"); ++ return ret; ++ } ++ ++ ret = dtb_add_range_property(&fdt->buf, &fdt->size, start, end, ++ "chosen", "linux,usable-memory-range"); ++ if (ret) { ++ fprintf(stderr, "Couldn't add usable-memory-range to fdt\n"); ++ return ret; ++ } ++ ++ max_usable = end; ++ } else { ++ /* ++ * Make sure we remove elfcorehdr and usable-memory-range ++ * when switching from crash kernel to a normal one. ++ */ ++ dtb_delete_property(fdt->buf, "chosen", "linux,elfcorehdr"); ++ dtb_delete_property(fdt->buf, "chosen", "linux,usable-memory-range"); ++ } ++ ++ /* Do we need to include an initrd image ? */ ++ if (!arch_options.initrd_path && !arch_options.initrd_end) ++ dtb_clear_initrd(&fdt->buf, &fdt->size); ++ else if (arch_options.initrd_path) { ++ if (arch_options.initrd_end) ++ fprintf(stderr, "Warning: An initrd image was provided" ++ ", will ignore reuseinitrd\n"); ++ ++ initrd_buf = slurp_file(arch_options.initrd_path, ++ &initrd_size); ++ if (!initrd_buf) { ++ fprintf(stderr, "Couldn't read provided initrd\n"); ++ return -EINVAL; ++ } ++ ++ initrd_base = add_buffer_phys_virt(info, initrd_buf, ++ initrd_size, ++ initrd_size, 0, ++ min_usable, ++ max_usable, 1, 0); ++ ++ dtb_set_initrd(&fdt->buf, &fdt->size, initrd_base, ++ initrd_base + initrd_size); ++ ++ dbgprintf("Base addr for initrd image: 0x%lX\n", initrd_base); ++ min_usable = initrd_base; ++ } ++ ++ /* Add device tree */ ++ add_buffer_phys_virt(info, fdt->buf, fdt->size, fdt->size, 0, ++ min_usable, max_usable, -1, 0); ++ ++ return 0; ++} ++ ++ ++/**************\ ++* ENTRY POINTS * ++\**************/ ++ ++void arch_usage(void) ++{ ++ printf(riscv_opts_usage); ++} ++ ++int prepare_kexec_file_options(struct kexec_info *info) ++{ ++ int fd; ++ ssize_t result; ++ struct stat stats; ++ ++ if (arch_options.cmdline) { ++ info->command_line = (char *)arch_options.cmdline; ++ info->command_line_len = strlen(info->command_line) + 1; ++ } ++ ++ if (!arch_options.initrd_path) { ++ info->initrd_fd = -1; ++ return 0; ++ } ++ ++ fd = open(arch_options.initrd_path, O_RDONLY | _O_BINARY); ++ if (fd < 0) { ++ fprintf(stderr, "Cannot open `%s': %s\n", arch_options.initrd_path, ++ strerror(errno)); ++ return -EINVAL; ++ } ++ result = fstat(fd, &stats); ++ if (result < 0) { ++ close(fd); ++ fprintf(stderr, "Cannot stat: %s: %s\n", arch_options.initrd_path, ++ strerror(errno)); ++ return -EINVAL; ++ } ++ info->initrd_fd = fd; ++ return 0; ++} ++ ++int arch_process_options(int argc, char **argv) ++{ ++ static const struct option options[] = { ++ KEXEC_ARCH_OPTIONS ++ { 0 }, ++ }; ++ static const char short_options[] = KEXEC_ARCH_OPT_STR; ++ struct stat st = {0}; ++ char *append = NULL; ++ char *cmdline = NULL; ++ void *tmp = NULL; ++ off_t tmp_size = 0; ++ int opt = 0; ++ int ret = 0; ++ ++ while ((opt = getopt_long(argc, argv, short_options, ++ options, 0)) != -1) { ++ switch (opt) { ++ case OPT_APPEND: ++ append = optarg; ++ break; ++ case OPT_CMDLINE: ++ if (cmdline) ++ fprintf(stderr, ++ "Warning: Kernel's cmdline " ++ "set twice !\n"); ++ cmdline = optarg; ++ break; ++ case OPT_REUSE_CMDLINE: ++ if (cmdline) ++ fprintf(stderr, ++ "Warning: Kernel's cmdline " ++ "set twice !\n"); ++ cmdline = get_command_line(); ++ break; ++ case OPT_DTB: ++ ret = stat(optarg, &st); ++ if (ret) { ++ fprintf(stderr, ++ "Could not find the provided dtb !\n"); ++ return -EINVAL; ++ } ++ arch_options.fdt_path = optarg; ++ break; ++ case OPT_INITRD: ++ ret = stat(optarg, &st); ++ if (ret) { ++ fprintf(stderr, ++ "Could not find the provided " ++ "initrd image !\n"); ++ return -EINVAL; ++ } ++ arch_options.initrd_path = optarg; ++ break; ++ default: ++ break; ++ } ++ } ++ ++ /* Handle Kernel's command line */ ++ if (append && !cmdline) ++ fprintf(stderr, "Warning: No cmdline provided, " ++ "using append string as cmdline\n"); ++ if (!append && !cmdline) ++ fprintf(stderr, "Warning: No cmdline or append string " ++ "provided\n"); ++ ++ if (append || cmdline) ++ /* ++ * Note that this also handles the case where "cmdline" ++ * or "append" is NULL. ++ */ ++ arch_options.cmdline = concat_cmdline(cmdline, append); ++ ++ /* Handle FDT image */ ++ if (!arch_options.fdt_path) { ++ ret = stat("/sys/firmware/fdt", &st); ++ if (ret) { ++ fprintf(stderr, "No dtb provided and " ++ "/sys/firmware/fdt is not present\n"); ++ return -EINVAL; ++ } ++ fprintf(stderr, "Warning: No dtb provided, " ++ "using /sys/firmware/fdt\n"); ++ arch_options.fdt_path = "/sys/firmware/fdt"; ++ } ++ ++ tmp = slurp_file(arch_options.fdt_path, &tmp_size); ++ if (!tmp) { ++ fprintf(stderr, "Couldn't read provided fdt\n"); ++ return -EINVAL; ++ } ++ ++ ret = fdt_check_header(tmp); ++ if (ret) { ++ fprintf(stderr, "Got an ivalid fdt image !\n"); ++ free(tmp); ++ return -EINVAL; ++ } ++ provided_fdt.buf = tmp; ++ provided_fdt.size = tmp_size; ++ ++ if (arch_options.cmdline) { ++ ret = dtb_set_bootargs(&provided_fdt.buf, &provided_fdt.size, ++ arch_options.cmdline); ++ if (ret < 0) { ++ fprintf(stderr, "Could not set bootargs on " ++ "the fdt image\n"); ++ return ret; ++ } ++ } ++ ++ arch_options.fdt = &provided_fdt; ++ ++ return 0; ++} ++ ++/* ++ * This one is called after arch_process_options so we already ++ * have an fdt image in place. ++ */ ++void arch_reuse_initrd(void) ++{ ++ const uint32_t *prop32 = NULL; ++ uint32_t addr_cells = 0; ++ const void *prop = 0; ++ int prop_size = 0; ++ uint64_t initrd_start = 0; ++ uint64_t initrd_end = 0; ++ int chosen_offset = 0; ++ struct fdt_image *fdt = &provided_fdt; ++ ++ chosen_offset = fdt_subnode_offset(fdt->buf, 0, "chosen"); ++ if (chosen_offset < 0) { ++ fprintf(stderr, "No /chosen node found on fdt image " ++ "unable to reuse initrd\n"); ++ return; ++ } ++ ++ prop32 = fdt_getprop(fdt->buf, 0, "#address-cells", NULL); ++ if (!prop32) { ++ fprintf(stderr, "No #address-cells property on root node\n"); ++ return; ++ } ++ addr_cells = be32_to_cpu(*prop32); ++ ++ prop = fdt_getprop(fdt->buf, chosen_offset, ++ "linux,initrd-start", &prop_size); ++ if (!prop) { ++ fprintf(stderr, "Could not get linux,initrd-start\n"); ++ return; ++ } ++ dtb_extract_int_property(&initrd_start, prop, addr_cells); ++ ++ prop = fdt_getprop(fdt->buf, chosen_offset, ++ "linux,initrd-end", &prop_size); ++ if (!prop) { ++ fprintf(stderr, "Could not get linux,initrd-end\n"); ++ return; ++ } ++ dtb_extract_int_property(&initrd_end, prop, addr_cells); ++ ++ arch_options.initrd_start = initrd_start; ++ arch_options.initrd_end = initrd_end; ++ dbgprintf("initrd_start: 0x%lX, initrd_end: 0x%lX\n", ++ initrd_start, initrd_end); ++ ++} ++ ++int get_memory_ranges(struct memory_range **range, int *num_ranges, ++ unsigned long kexec_flags) ++{ ++ struct memory_ranges sysmem_ranges = {0}; ++ int ret = 0; ++ ++ const char *iomem = proc_iomem(); ++ char line[MAX_LINE], *str; ++ FILE *fp; ++ unsigned long long start, end; ++ int consumed, count; ++ ++ fp = fopen(iomem, "r"); ++ if (!fp) { ++ fprintf(stderr, "Cannot open %s: %s\n", iomem, strerror(errno)); ++ return -1; ++ } ++ ++ while (fgets(line, sizeof(line), fp) != 0) { ++ count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed); ++ if (count != 2) ++ continue; ++ str = line + consumed; ++ ++ if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM))){ ++ ret = mem_regions_alloc_and_add(&sysmem_ranges, ++ start, end - start + 1, RANGE_RAM); ++ if (ret) { ++ fprintf(stderr, ++ "Cannot allocate memory for ranges\n"); ++ fclose(fp); ++ return -ENOMEM; ++ } ++ ++ } else if (!strncmp(str, IOMEM_RESERVED, strlen(IOMEM_RESERVED))){ ++ ret = mem_regions_alloc_and_add(&sysmem_ranges, ++ start, end - start + 1, RANGE_RESERVED); ++ if (ret) { ++ fprintf(stderr, ++ "Cannot allocate memory for ranges\n"); ++ fclose(fp); ++ return -ENOMEM; ++ } ++ } else ++ continue; ++ } ++ ++ fclose(fp); ++ ++ *range = sysmem_ranges.ranges; ++ *num_ranges = sysmem_ranges.size; ++ ++ dbgprint_mem_range("System RAM ranges;", ++ sysmem_ranges.ranges, sysmem_ranges.size); ++ ++ return 0; ++} ++ ++/*******\ ++* STUBS * ++\*******/ ++ ++int arch_compat_trampoline(struct kexec_info *UNUSED(info)) ++{ ++ return 0; ++} ++ ++void arch_update_purgatory(struct kexec_info *UNUSED(info)) ++{ ++} +diff --git a/kexec/arch/riscv/kexec-riscv.h b/kexec/arch/riscv/kexec-riscv.h +new file mode 100644 +index 0000000..cfb0377 +--- /dev/null ++++ b/kexec/arch/riscv/kexec-riscv.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2019 FORTH-ICS/CARV ++ * Nick Kossifidis ++ */ ++ ++/* ++ * Kernel should be aligned to the nearest ++ * hugepage (2MB for RV64, 4MB for RV32). ++ */ ++ ++#if __riscv_xlen == 64 ++#define KERNEL_ALIGN 0x200000 ++#else ++#define KERNEL_ALIGN 0x400000 ++#endif ++ ++struct fdt_image { ++ char *buf; ++ off_t size; ++}; ++ ++struct riscv_opts { ++ char *cmdline; ++ char *fdt_path; ++ char *initrd_path; ++ uint64_t initrd_start; ++ uint64_t initrd_end; ++ struct fdt_image *fdt; ++}; ++ ++/* crashdump-riscv.c */ ++extern struct memory_range elfcorehdr_mem; ++int load_elfcorehdr(struct kexec_info *info); ++ ++/* kexec-riscv.c */ ++int prepare_kexec_file_options(struct kexec_info *info); ++int load_extra_segments(struct kexec_info *info, uint64_t kernel_base, ++ uint64_t kernel_size, uint64_t max_addr); ++int riscv_find_pbase(struct kexec_info *info, off_t *addr, ++ off_t size, off_t align); ++ ++/* kexec-elf-riscv.c */ ++int elf_riscv_probe(const char *buf, off_t len); ++void elf_riscv_usage(void); ++int elf_riscv_load(int argc, char **argv, const char *buf, off_t len, ++ struct kexec_info *info); ++ ++/* kexec-image-riscv.c */ ++int image_riscv_probe(const char *buf, off_t len); ++void image_riscv_usage(void); ++int image_riscv_load(int argc, char **argv, const char *buf, off_t len, ++ struct kexec_info *info); +diff --git a/kexec/dt-ops.c b/kexec/dt-ops.c +index 0a96b75..3e285ab 100644 +--- a/kexec/dt-ops.c ++++ b/kexec/dt-ops.c +@@ -4,9 +4,11 @@ + #include + #include + #include ++#include + + #include "kexec.h" + #include "dt-ops.h" ++#include "mem_regions.h" + + static const char n_chosen[] = "chosen"; + +@@ -95,7 +97,7 @@ int dtb_set_property(char **dtb, off_t *dtb_size, const char *node, + + strcpy(new_node, "/"); + strcat(new_node, node); +- ++ + nodeoffset = fdt_path_offset(new_dtb, new_node); + + if (nodeoffset == -FDT_ERR_NOTFOUND) { +@@ -174,3 +176,441 @@ int dtb_delete_property(char *dtb, const char *node, const char *prop) + free(new_node); + return result; + } ++ ++static int dtb_get_num_cells(char *dtb, int nodeoffset, uint32_t *addr_cells, ++ uint32_t *size_cells, bool recursive) ++{ ++ const uint32_t *prop32 = NULL; ++ int curr_offset = nodeoffset; ++ int prop_len = 0; ++ *addr_cells = 0; ++ *size_cells = 0; ++ ++ do { ++ prop32 = fdt_getprop(dtb, curr_offset, "#address-cells", &prop_len); ++ curr_offset = fdt_parent_offset(dtb, curr_offset); ++ } while (!prop32 && prop_len == -FDT_ERR_NOTFOUND && recursive); ++ ++ if (!prop32) { ++ dbgprintf("Could not get #address-cells property for %s (%s)\n", ++ fdt_get_name(dtb, nodeoffset, NULL), fdt_strerror(nodeoffset)); ++ return -EINVAL; ++ } ++ *addr_cells = fdt32_to_cpu(*prop32); ++ ++ curr_offset = nodeoffset; ++ do { ++ prop32 = fdt_getprop(dtb, curr_offset, "#size-cells", &prop_len); ++ curr_offset = fdt_parent_offset(dtb, curr_offset); ++ } while (!prop32 && prop_len == -FDT_ERR_NOTFOUND && recursive); ++ ++ if (!prop32) { ++ dbgprintf("Could not get #size-cells property for %s (%s)\n", ++ fdt_get_name(dtb, nodeoffset, NULL), fdt_strerror(nodeoffset)); ++ return -EINVAL; ++ } ++ *size_cells = fdt32_to_cpu(*prop32); ++ ++ dbgprintf("%s: #address-cells:%d #size-cells:%d\n", ++ fdt_get_name(dtb, nodeoffset, NULL), *addr_cells, *size_cells); ++ ++ return 0; ++} ++ ++void dtb_extract_int_property(uint64_t *val, const void *buf, uint32_t cells) ++{ ++ const uint32_t *prop32 = NULL; ++ const uint64_t *prop64 = NULL; ++ ++ if (cells == 1) { ++ prop32 = (const uint32_t *) buf; ++ *val = (uint64_t) be32_to_cpu(*prop32); ++ } else { ++ /* Skip any leading cells */ ++ prop64 = (const uint64_t *) (uint32_t *)buf + cells - 2; ++ *val = (uint64_t) be64_to_cpu(*prop64); ++ } ++} ++ ++void dtb_fill_int_property(void *buf, uint64_t val, uint32_t cells) ++{ ++ uint32_t prop32 = 0; ++ uint64_t prop64 = 0; ++ ++ if (cells == 1) { ++ prop32 = cpu_to_fdt32((uint32_t) val); ++ memcpy(buf, &prop32, sizeof(uint32_t)); ++ } else { ++ prop64 = cpu_to_fdt64(val); ++ /* Skip any leading cells */ ++ memcpy((uint64_t *)(uint32_t *)buf + cells - 2, ++ &prop64, sizeof(uint64_t)); ++ } ++} ++ ++int dtb_add_range_property(char **dtb, off_t *dtb_size, uint64_t start, uint64_t end, ++ const char *parent, const char *name) ++{ ++ uint32_t addr_cells = 0; ++ uint32_t size_cells = 0; ++ char *nodepath = NULL; ++ void *prop = NULL; ++ int nodeoffset = 0; ++ int prop_size = 0; ++ int ret = 0; ++ ++ nodepath = malloc(strlen("/") + strlen(parent) + 1); ++ if (!nodepath) { ++ dbgprintf("%s: malloc failed\n", __func__); ++ return -ENOMEM; ++ } ++ ++ strcpy(nodepath, "/"); ++ strcat(nodepath, parent); ++ ++ nodeoffset = fdt_path_offset(*dtb, nodepath); ++ if (nodeoffset < 0) { ++ dbgprintf("%s: fdt_path_offset(%s) failed: %s\n", __func__, ++ nodepath, fdt_strerror(nodeoffset)); ++ free(nodepath); ++ return nodeoffset; ++ } ++ free(nodepath); ++ ++ ret = dtb_get_num_cells(*dtb, nodeoffset, &addr_cells, &size_cells, true); ++ if (ret < 0) ++ return ret; ++ ++ /* Can the range fit with the given address/size cells ? */ ++ if ((addr_cells == 1) && (start >= (1ULL << 32))) ++ return -EINVAL; ++ ++ if ((size_cells == 1) && ((end - start + 1) >= (1ULL << 32))) ++ return -EINVAL; ++ ++ prop_size = sizeof(uint32_t) * (addr_cells + size_cells); ++ prop = malloc(prop_size); ++ ++ dtb_fill_int_property(prop, start, addr_cells); ++ dtb_fill_int_property((void *)((uint32_t *)prop + addr_cells), ++ end - start + 1, size_cells); ++ ++ /* Add by node path name */ ++ return dtb_set_property(dtb, dtb_size, parent, name, prop, prop_size); ++} ++ ++/************************\ ++* MEMORY RANGES HANDLING * ++\************************/ ++ ++static int dtb_add_memory_range(struct memory_ranges *mem_ranges, uint64_t start, ++ uint64_t end, unsigned type) ++{ ++ struct memory_range this_region = {0}; ++ struct memory_range *ranges = mem_ranges->ranges; ++ int i = 0; ++ int ret = 0; ++ ++ if (start == end) { ++ dbgprintf("Ignoring empty region\n"); ++ return -EINVAL; ++ } ++ ++ /* Check if we are adding an existing region */ ++ for (i = 0; i < mem_ranges->size; i++) { ++ if (start == ranges[i].start && end == ranges[i].end) { ++ dbgprintf("Duplicate: 0x%lx - 0x%lx\n", start, end); ++ ++ if (type == ranges[i].type) ++ return 0; ++ else if (type == RANGE_RESERVED) { ++ ranges[i].type = RANGE_RESERVED; ++ return 0; ++ } ++ ++ dbgprintf("Conflicting types for region: 0x%lx - 0x%lx\n", ++ start, end); ++ return -EINVAL; ++ } ++ } ++ ++ /* ++ * Reserved regions may be part of an existing /memory ++ * region and shouldn't overlap according to spec, so ++ * since we add /memory regions first, we can exclude ++ * reserved regions here from the existing /memory regions ++ * included in ranges[], so that we don't have the same ++ * region twice. ++ */ ++ if (type == RANGE_RESERVED) { ++ this_region.start = start; ++ this_region.end = end - 1; ++ this_region.type = type; ++ ret = mem_regions_exclude(mem_ranges, &this_region); ++ if (ret) ++ return ret; ++ } ++ ++ ret = mem_regions_alloc_and_add(mem_ranges, start, ++ end - start, type); ++ ++ return ret; ++} ++ ++static int dtb_add_memory_region(char *dtb, int nodeoffset, ++ struct memory_ranges *mem_ranges, int type) ++{ ++ uint32_t root_addr_cells = 0; ++ uint32_t root_size_cells = 0; ++ uint64_t addr = 0; ++ uint64_t size = 0; ++ const char *reg = NULL; ++ int prop_size = 0; ++ int offset = 0; ++ int entry_size = 0; ++ int num_entries = 0; ++ int ret = 0; ++ ++ /* ++ * Get address-cells and size-cells properties (according to ++ * binding spec these are the same as in the root node) ++ */ ++ ret = dtb_get_num_cells(dtb, 0, &root_addr_cells, &root_size_cells, false); ++ if (ret < 0) { ++ dbgprintf("No address/size cells on root node !\n"); ++ return ret; ++ } ++ ++ /* ++ * Parse the reg array, acording to device tree spec it includes ++ * an arbitary number of
pairs ++ */ ++ entry_size = (root_addr_cells + root_size_cells) * sizeof(uint32_t); ++ reg = fdt_getprop(dtb, nodeoffset, "reg", &prop_size); ++ if (!reg) { ++ dbgprintf("Warning: Malformed memory region with no reg property (%s) !\n", ++ fdt_get_name(dtb, nodeoffset, NULL)); ++ return -EINVAL; ++ } ++ ++ num_entries = prop_size / entry_size; ++ dbgprintf("Got region with %i entries: %s\n", num_entries, ++ fdt_get_name(dtb, nodeoffset, NULL)); ++ ++ for (num_entries--; num_entries >= 0; num_entries--) { ++ offset = num_entries * entry_size; ++ ++ dtb_extract_int_property(&addr, reg + offset, ++ root_addr_cells); ++ offset += root_addr_cells * sizeof(uint32_t); ++ ++ dtb_extract_int_property(&size, reg + offset, ++ root_size_cells); ++ ++ ret = dtb_add_memory_range(mem_ranges, addr, ++ addr + size, type); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int dtb_parse_memory_reservations_table(char *dtb, struct memory_ranges *mem_ranges) ++{ ++ int total_memrsrv = 0; ++ uint64_t addr = 0; ++ uint64_t size = 0; ++ int ret = 0; ++ int i = 0; ++ ++ total_memrsrv = fdt_num_mem_rsv(dtb); ++ for (i = 0; i < total_memrsrv; i++) { ++ ret = fdt_get_mem_rsv(dtb, i, &addr, &size); ++ if (ret) ++ continue; ++ ret = dtb_add_memory_range(mem_ranges, addr, addr + size - 1, ++ RANGE_RESERVED); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int dtb_get_reserved_memory_node(char *dtb) ++{ ++ uint32_t root_addr_cells = 0; ++ uint32_t root_size_cells = 0; ++ uint32_t addr_cells = 0; ++ uint32_t size_cells = 0; ++ int prop_size = 0; ++ int nodeoffset = 0; ++ int ret = 0; ++ ++ /* Get address / size cells from root node */ ++ ret = dtb_get_num_cells(dtb, 0, &root_addr_cells, &root_size_cells, false); ++ if (ret < 0) { ++ dbgprintf("No address/size cells on root node !\n"); ++ return ret; ++ } ++ ++ /* This calls fdt_next_node internaly */ ++ nodeoffset = fdt_subnode_offset(dtb, 0, "reserved-memory"); ++ if (nodeoffset == -FDT_ERR_NOTFOUND) { ++ return nodeoffset; ++ } else if (nodeoffset < 0) { ++ dbgprintf("Error while looking for reserved-memory: %s\n", ++ fdt_strerror(nodeoffset)); ++ return nodeoffset; ++ } ++ ++ /* Look for the ranges property */ ++ fdt_getprop(dtb, nodeoffset, "ranges", &prop_size); ++ if (prop_size < 0) { ++ fprintf(stderr, "Malformed reserved-memory node (no ranges property) !\n"); ++ return -EINVAL; ++ } ++ ++ /* Verify address-cells / size-cells */ ++ ret = dtb_get_num_cells(dtb, nodeoffset, &addr_cells, &size_cells, false); ++ if (ret < 0) { ++ dbgprintf("No address/size cells property on reserved-memory node\n"); ++ return ret; ++ } ++ ++ if (addr_cells != root_addr_cells) { ++ fprintf(stderr, "Invalid #address-cells property on reserved-memory node\n"); ++ return -EINVAL; ++ } ++ ++ if (size_cells != root_size_cells) { ++ fprintf(stderr, "Invalid #size-cells property on reserved-memory node\n"); ++ return -EINVAL; ++ ++ } ++ ++ return nodeoffset; ++} ++ ++static int dtb_parse_reserved_memory_node(char *dtb, struct memory_ranges *mem_ranges) ++{ ++ int nodeoffset = 0; ++ int node_depth = 0; ++ int parent_depth = 0; ++ int ret = 0; ++ ++ nodeoffset = dtb_get_reserved_memory_node(dtb); ++ if (nodeoffset == -FDT_ERR_NOTFOUND) ++ return 0; ++ else if (nodeoffset < 0) ++ return nodeoffset; ++ ++ /* Got the parent node, check for sub-nodes */ ++ ++ /* fdt_next_node() increases or decreases depth */ ++ node_depth = parent_depth; ++ nodeoffset = fdt_next_node(dtb, nodeoffset, &node_depth); ++ if (ret < 0) { ++ dbgprintf("Unable to get next node: %s\n", ++ fdt_strerror(ret)); ++ return -EINVAL; ++ } ++ ++ while (node_depth != parent_depth) { ++ ++ ret = dtb_add_memory_region(dtb, nodeoffset, ++ mem_ranges, RANGE_RESERVED); ++ if (ret) ++ return ret; ++ ++ nodeoffset = fdt_next_node(dtb, nodeoffset, &node_depth); ++ if (ret < 0) { ++ dbgprintf("Unable to get next node: %s\n", ++ fdt_strerror(ret)); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int dtb_parse_memory_nodes(char *dtb, struct memory_ranges *mem_ranges) ++{ ++ int nodeoffset = 0; ++ int num_regions = 0; ++ const char* dev_type = 0; ++ int prop_size = 0; ++ int ret = 0; ++ ++ for (; ; num_regions++) { ++ nodeoffset = fdt_subnode_offset(dtb, nodeoffset, ++ "memory"); ++ if (nodeoffset < 0) ++ break; ++ ++ dbgprintf("Got memory node at depth: %i\n", fdt_node_depth(dtb, nodeoffset)); ++ ++ /* Look for the device_type property */ ++ dev_type = fdt_getprop(dtb, nodeoffset, "device_type", &prop_size); ++ if (prop_size < 0) { ++ fprintf(stderr, "Malformed /memory node (no device-type property) !\n"); ++ return -EINVAL; ++ } ++ ++ if (strncmp(dev_type, "memory", prop_size)) { ++ dbgprintf("Got unknown dev_type property: %s\n", dev_type); ++ continue; ++ } ++ ++ ret = dtb_add_memory_region(dtb, nodeoffset, mem_ranges, RANGE_RAM); ++ if (ret) ++ return ret; ++ } ++ ++ if (!num_regions) { ++ dbgprintf("Malformed dtb, no /memory nodes present !\n"); ++ return -EINVAL; ++ } ++ ++ dbgprintf("Got %i /memory nodes\n", num_regions); ++ ++ return 0; ++} ++ ++int dtb_get_memory_ranges(char *dtb, struct memory_ranges *mem_ranges, struct memory_ranges *extra_ranges) ++{ ++ int i = 0; ++ int ret = 0; ++ ++ /* Fill mem_ranges[] by parsing the device tree */ ++ ret = dtb_parse_memory_nodes(dtb, mem_ranges); ++ if (ret) ++ return ret; ++ ++ ret = dtb_parse_memory_reservations_table(dtb, mem_ranges); ++ if (ret) ++ return ret; ++ ++ ret = dtb_parse_reserved_memory_node(dtb, mem_ranges); ++ if (ret) ++ return ret; ++ ++ /* Append any extra ranges provided by the caller (e.g. initrd) */ ++ for (i = 0; extra_ranges != NULL && i < extra_ranges->size; i++) { ++ dbgprintf("Adding extra range: 0x%llx - 0x%llx (%s)\n", ++ extra_ranges->ranges[i].start, ++ extra_ranges->ranges[i].end, ++ extra_ranges->ranges[i].type == RANGE_RESERVED ? ++ "RANGE_RESERVED" : "RANGE_RAM"); ++ ++ ret = dtb_add_memory_range(mem_ranges, extra_ranges->ranges[i].start, ++ extra_ranges->ranges[i].end, extra_ranges->ranges[i].type); ++ if (ret) ++ return ret; ++ } ++ ++ mem_regions_sort(mem_ranges); ++ ++ return 0; ++} +diff --git a/kexec/dt-ops.h b/kexec/dt-ops.h +index 03659ce..3014205 100644 +--- a/kexec/dt-ops.h ++++ b/kexec/dt-ops.h +@@ -11,4 +11,11 @@ int dtb_set_property(char **dtb, off_t *dtb_size, const char *node, + + int dtb_delete_property(char *dtb, const char *node, const char *prop); + ++void dtb_extract_int_property(uint64_t *val, const void *buf, uint32_t cells); ++void dtb_fill_int_property(void *buf, uint64_t val, uint32_t cells); ++int dtb_add_range_property(char **dtb, off_t *dtb_size, uint64_t start, uint64_t end, ++ const char *node, const char* parent); ++int dtb_get_memory_ranges(char *dtb, struct memory_ranges *mem_ranges, ++ struct memory_ranges *extra_ranges); ++ + #endif +diff --git a/kexec/kexec-iomem.c b/kexec/kexec-iomem.c +index d00b6b6..1ace808 100644 +--- a/kexec/kexec-iomem.c ++++ b/kexec/kexec-iomem.c +@@ -76,7 +76,7 @@ static int kexec_iomem_single_callback(void *data, int nr, + { + struct memory_range *range = data; + +- if (nr == 0) { ++ if (nr >= 0) { + range->start = base; + range->end = base + length - 1; + } +@@ -94,7 +94,7 @@ int parse_iomem_single(char *str, uint64_t *start, uint64_t *end) + ret = kexec_iomem_for_each_line(str, + kexec_iomem_single_callback, &range); + +- if (ret == 1) { ++ if (ret >= 1) { + if (start) + *start = range.start; + if (end) +diff --git a/kexec/kexec-syscall.h b/kexec/kexec-syscall.h +index be6ccd5..2310b98 100644 +--- a/kexec/kexec-syscall.h ++++ b/kexec/kexec-syscall.h +@@ -80,6 +80,9 @@ + #ifdef __hppa__ + #define __NR_kexec_file_load 355 + #endif ++#if defined(__riscv__) || defined(__riscv) ++#define __NR_kexec_file_load 294 ++#endif + + #ifndef __NR_kexec_file_load + /* system call not available for the arch */ +@@ -137,6 +140,7 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd, + #define KEXEC_ARCH_MIPS_LE (10 << 16) + #define KEXEC_ARCH_MIPS ( 8 << 16) + #define KEXEC_ARCH_CRIS (76 << 16) ++#define KEXEC_ARCH_RISCV (243 << 16) + #define KEXEC_ARCH_LOONGARCH (258 << 16) + + #define KEXEC_MAX_SEGMENTS 16 +@@ -184,5 +188,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd, + #if defined(__loongarch__) + #define KEXEC_ARCH_NATIVE KEXEC_ARCH_LOONGARCH + #endif ++#if defined(__riscv__) || defined(__riscv) ++#define KEXEC_ARCH_NATIVE KEXEC_ARCH_RISCV ++#endif + + #endif /* KEXEC_SYSCALL_H */ +diff --git a/purgatory/Makefile b/purgatory/Makefile +index 4d2d071..7d1175f 100644 +--- a/purgatory/Makefile ++++ b/purgatory/Makefile +@@ -25,6 +25,7 @@ include $(srcdir)/purgatory/arch/ia64/Makefile + include $(srcdir)/purgatory/arch/mips/Makefile + include $(srcdir)/purgatory/arch/ppc/Makefile + include $(srcdir)/purgatory/arch/ppc64/Makefile ++include $(srcdir)/purgatory/arch/riscv/Makefile + include $(srcdir)/purgatory/arch/s390/Makefile + include $(srcdir)/purgatory/arch/sh/Makefile + include $(srcdir)/purgatory/arch/x86_64/Makefile +diff --git a/purgatory/arch/riscv/Makefile b/purgatory/arch/riscv/Makefile +new file mode 100644 +index 0000000..49a6fef +--- /dev/null ++++ b/purgatory/arch/riscv/Makefile +@@ -0,0 +1,7 @@ ++# ++# Purgatory riscv ++# ++ ++riscv_PURGATORY_SRCS = ++ ++dist += purgatory/arch/riscv/Makefile $(riscv_PURGATORY_SRCS) +-- +2.34.1 + diff --git a/kexec-tools.spec b/kexec-tools.spec index ea846c9..70c4646 100644 --- a/kexec-tools.spec +++ b/kexec-tools.spec @@ -4,7 +4,7 @@ Name: kexec-tools Version: 2.0.26 -Release: 2 +Release: 4 License: GPLv2 Summary: The kexec/kdump userspace component URL: https://www.kernel.org/ @@ -34,6 +34,7 @@ Source27: early-kdump-howto.txt Source28: kdump-udev-throttler Source29: kdump.sysconfig.aarch64 Source30: kdump.sysconfig.loongarch64 +Source31: kdump.sysconfig.riscv64 Source100: dracut-kdump.sh Source101: dracut-module-setup.sh @@ -78,6 +79,11 @@ Patch0004: sw_64.patch Patch0005: makedumpfile-1.7.2-sw.patch %endif +%ifarch riscv64 +Patch0006: kexec-tools-Add-riscv-support.patch +Patch0007: riscv-makedumpfile-1.7.3.patch +%endif + %description kexec-tools provides /sbin/kexec binary that facilitates a new kernel to boot using the kernel's kexec feature either on a @@ -110,7 +116,7 @@ rm -f kexec-tools.spec.in cp %{SOURCE21} %{SOURCE26} %{SOURCE27} . make -%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le +%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le riscv64 make -C eppic-%{eppic_ver}/libeppic make -C makedumpfile-%{mkdf_ver} LINKTYPE=dynamic USELZO=on USESNAPPY=on make -C makedumpfile-%{mkdf_ver} LDFLAGS="$LDFLAGS -I../eppic-%{eppic_ver}/libeppic -L../eppic-%{eppic_ver}/libeppic" eppic_makedumpfile.so @@ -129,6 +135,11 @@ mkdir -p %{buildroot}%{_unitdir} mkdir -p -m755 %{buildroot}%{_bindir} mkdir -p -m755 %{buildroot}%{_libdir} mkdir -p -m755 %{buildroot}%{_prefix}/lib/kdump + +%ifarch riscv64 +sed -i 's/--initrd=$TARGET_INITRD //g' %{SOURCE1} +%endif + install -m 755 %{SOURCE1} %{buildroot}%{_bindir}/kdumpctl install -m 755 build/sbin/kexec %{buildroot}/usr/sbin/kexec @@ -154,7 +165,7 @@ install -m 644 %{SOURCE16} %{buildroot}%{_unitdir}/kdump.service install -m 755 -D %{SOURCE22} %{buildroot}%{_prefix}/lib/systemd/system-generators/kdump-dep-generator.sh install -m 644 %{SOURCE13} $RPM_BUILD_ROOT%{_udevrulesdir}/98-kexec.rules -%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le +%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le riscv64 install -m 755 makedumpfile-%{mkdf_ver}/makedumpfile $RPM_BUILD_ROOT/usr/sbin/makedumpfile install -m 644 makedumpfile-%{mkdf_ver}/makedumpfile.8 $RPM_BUILD_ROOT/%{_mandir}/man8/makedumpfile.8 install -m 644 makedumpfile-%{mkdf_ver}/makedumpfile.conf.5 $RPM_BUILD_ROOT/%{_mandir}/man5/makedumpfile.conf.5 @@ -259,14 +270,14 @@ done %{dracutlibdir}/modules.d/* %{_unitdir}/kdump.service %{_prefix}/lib/systemd/system-generators/kdump-dep-generator.sh -%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le +%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le riscv64 %{_libdir}/eppic_makedumpfile.so /usr/share/makedumpfile/ %endif -%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le +%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le riscv64 %{_sysconfdir}/makedumpfile.conf.sample %endif -%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le +%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le riscv64 /usr/sbin/makedumpfile %endif @@ -279,11 +290,14 @@ done %{_mandir}/man8/mkdumprd.8.gz %{_mandir}/man8/vmcore-dmesg.8.gz %{_mandir}/man5/* -%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le +%ifarch %{ix86} x86_64 aarch64 sw_64 loongarch64 ppc64le riscv64 %{_mandir}/man8/makedumpfile.8.gz %endif %changelog +* Thu Apr 18 2024 Mingzheng Xing - 2.0.26-4 +- Add riscv64 support + * Mon Jan 30 2023 chenhaixiang - 2.0.26-3 - update makedumpfile to makedumpfile-1.7.3 diff --git a/riscv-makedumpfile-1.7.3.patch b/riscv-makedumpfile-1.7.3.patch new file mode 100644 index 0000000..18ae058 --- /dev/null +++ b/riscv-makedumpfile-1.7.3.patch @@ -0,0 +1,910 @@ +From a88a07451eee09aac9b0c3f6ce54f959b2d08432 Mon Sep 17 00:00:00 2001 +From: Mingzheng Xing +Date: Wed, 17 Apr 2024 17:13:33 +0800 +Subject: [PATCH] riscv: makedumpfile 1.7.3 + +Upgrade to upstream version 1.7.4, which provides support for the riscv +architecture. + +This patch will be maintained until the openEuler community upgrade. + +Signed-off-by: Mingzheng Xing +--- + makedumpfile-1.7.3/Makefile | 6 +- + makedumpfile-1.7.3/README | 3 + + makedumpfile-1.7.3/arch/ppc64.c | 111 ++++++++++---- + makedumpfile-1.7.3/arch/riscv64.c | 219 +++++++++++++++++++++++++++ + makedumpfile-1.7.3/dwarf_info.c | 29 +++- + makedumpfile-1.7.3/makedumpfile.c | 74 ++++++++- + makedumpfile-1.7.3/makedumpfile.h | 122 ++++++++++++++- + makedumpfile-1.7.3/makedumpfile.spec | 2 +- + 8 files changed, 519 insertions(+), 47 deletions(-) + create mode 100644 makedumpfile-1.7.3/arch/riscv64.c + +diff --git a/makedumpfile-1.7.3/Makefile b/makedumpfile-1.7.3/Makefile +index 9436f2a..0934f14 100644 +--- a/makedumpfile-1.7.3/Makefile ++++ b/makedumpfile-1.7.3/Makefile +@@ -1,7 +1,7 @@ + # makedumpfile + +-VERSION=1.7.3 +-DATE=25 Apr 2023 ++VERSION=1.7.4 ++DATE=6 Nov 2023 + + # Honour the environment variable CC + ifeq ($(strip $CC),) +@@ -47,7 +47,7 @@ endif + SRC_BASE = makedumpfile.c makedumpfile.h diskdump_mod.h sadump_mod.h sadump_info.h + SRC_PART = print_info.c dwarf_info.c elf_info.c erase_info.c sadump_info.c cache.c tools.c printk.c detect_cycle.c + OBJ_PART=$(patsubst %.c,%.o,$(SRC_PART)) +-SRC_ARCH = arch/arm.c arch/arm64.c arch/x86.c arch/x86_64.c arch/ia64.c arch/ppc64.c arch/s390x.c arch/ppc.c arch/sparc64.c arch/mips64.c arch/loongarch64.c ++SRC_ARCH = arch/arm.c arch/arm64.c arch/x86.c arch/x86_64.c arch/ia64.c arch/ppc64.c arch/s390x.c arch/ppc.c arch/sparc64.c arch/mips64.c arch/loongarch64.c arch/riscv64.c + OBJ_ARCH=$(patsubst %.c,%.o,$(SRC_ARCH)) + + LIBS = -ldw -lbz2 -ldl -lelf -lz +diff --git a/makedumpfile-1.7.3/README b/makedumpfile-1.7.3/README +index 4a9d0c0..858ae30 100644 +--- a/makedumpfile-1.7.3/README ++++ b/makedumpfile-1.7.3/README +@@ -169,6 +169,9 @@ + 6.1 | | ** | | | | ** | | -- | | OK | | | + 6.2 | | ** | | | | ** | | -- | | OK | | | + 6.3 | | ** | | | | ** | | -- | | OK | | | ++ 6.4 | | ** | | | | ** | | -- | | OK | | | ++ 6.5 | | ** | | | | ** | | -- | | OK | | | ++ 6.6 | | ** | | | | ** | | -- | | OK | | | + + OK : Support. + -- : Not support. +diff --git a/makedumpfile-1.7.3/arch/ppc64.c b/makedumpfile-1.7.3/arch/ppc64.c +index 5e70acb..96c357c 100644 +--- a/makedumpfile-1.7.3/arch/ppc64.c ++++ b/makedumpfile-1.7.3/arch/ppc64.c +@@ -196,6 +196,10 @@ ppc64_vmemmap_init(void) + int psize, shift; + ulong head; + ++ /* initialise vmemmap_list in case SYMBOL(vmemmap_list) is not found */ ++ info->vmemmap_list = NULL; ++ info->vmemmap_cnt = 0; ++ + if ((SYMBOL(vmemmap_list) == NOT_FOUND_SYMBOL) + || (SYMBOL(mmu_psize_defs) == NOT_FOUND_SYMBOL) + || (SYMBOL(mmu_vmemmap_psize) == NOT_FOUND_SYMBOL) +@@ -216,15 +220,24 @@ ppc64_vmemmap_init(void) + return FALSE; + info->vmemmap_psize = 1 << shift; + +- if (!readmem(VADDR, SYMBOL(vmemmap_list), &head, sizeof(unsigned long))) +- return FALSE; +- + /* +- * Get vmemmap list count and populate vmemmap regions info +- */ +- info->vmemmap_cnt = get_vmemmap_list_info(head); +- if (info->vmemmap_cnt == 0) +- return FALSE; ++ * vmemmap_list symbol can be missing or set to 0 in the kernel. ++ * This would imply vmemmap region is mapped in the kernel pagetable. ++ * ++ * So, read vmemmap_list anyway, and use 'vmemmap_list' if it's not empty ++ * (head != NULL), or we will do a kernel pagetable walk for vmemmap address ++ * translation later ++ **/ ++ readmem(VADDR, SYMBOL(vmemmap_list), &head, sizeof(unsigned long)); ++ ++ if (head) { ++ /* ++ * Get vmemmap list count and populate vmemmap regions info ++ */ ++ info->vmemmap_cnt = get_vmemmap_list_info(head); ++ if (info->vmemmap_cnt == 0) ++ return FALSE; ++ } + + info->flag_vmemmap = TRUE; + return TRUE; +@@ -347,29 +360,6 @@ ppc64_vmalloc_init(void) + return TRUE; + } + +-/* +- * If the vmemmap address translation information is stored in the kernel, +- * make the translation. +- */ +-static unsigned long long +-ppc64_vmemmap_to_phys(unsigned long vaddr) +-{ +- int i; +- ulong offset; +- unsigned long long paddr = NOT_PADDR; +- +- for (i = 0; i < info->vmemmap_cnt; i++) { +- if ((vaddr >= info->vmemmap_list[i].virt) && (vaddr < +- (info->vmemmap_list[i].virt + info->vmemmap_psize))) { +- offset = vaddr - info->vmemmap_list[i].virt; +- paddr = info->vmemmap_list[i].phys + offset; +- break; +- } +- } +- +- return paddr; +-} +- + static unsigned long long + ppc64_vtop_level4(unsigned long vaddr) + { +@@ -379,6 +369,8 @@ ppc64_vtop_level4(unsigned long vaddr) + unsigned long long pgd_pte, pud_pte; + unsigned long long pmd_pte, pte; + unsigned long long paddr = NOT_PADDR; ++ uint is_hugepage = 0; ++ uint pdshift; + uint swap = 0; + + if (info->page_buf == NULL) { +@@ -413,6 +405,13 @@ ppc64_vtop_level4(unsigned long vaddr) + if (!pgd_pte) + return NOT_PADDR; + ++ if (IS_HUGEPAGE(pgd_pte)) { ++ is_hugepage = 1; ++ pte = pgd_pte; ++ pdshift = info->l4_shift; ++ goto out; ++ } ++ + /* + * Sometimes we don't have level3 pagetable entries + */ +@@ -426,6 +425,13 @@ ppc64_vtop_level4(unsigned long vaddr) + pud_pte = swap64(ULONG((info->page_buf + PAGEOFFSET(page_upper))), swap); + if (!pud_pte) + return NOT_PADDR; ++ ++ if (IS_HUGEPAGE(pud_pte)) { ++ is_hugepage = 1; ++ pte = pud_pte; ++ pdshift = info->l3_shift; ++ goto out; ++ } + } else { + pud_pte = pgd_pte; + } +@@ -440,6 +446,13 @@ ppc64_vtop_level4(unsigned long vaddr) + if (!(pmd_pte)) + return NOT_PADDR; + ++ if (IS_HUGEPAGE(pmd_pte)) { ++ is_hugepage = 1; ++ pte = pmd_pte; ++ pdshift = info->l2_shift; ++ goto out; ++ } ++ + pmd_pte = pmd_page_vaddr_l4(pmd_pte); + page_table = (ulong *)(pmd_pte) + + (BTOP(vaddr) & (info->ptrs_per_l1 - 1)); +@@ -456,8 +469,40 @@ ppc64_vtop_level4(unsigned long vaddr) + if (!pte) + return NOT_PADDR; + +- paddr = PAGEBASE(PTOB((pte & info->pte_rpn_mask) >> info->pte_rpn_shift)) ++out: ++ if (is_hugepage) { ++ paddr = PAGEBASE(PTOB((pte & info->pte_rpn_mask) >> info->pte_rpn_shift)) ++ + (vaddr & ((1UL << pdshift) - 1)); ++ } else { ++ paddr = PAGEBASE(PTOB((pte & info->pte_rpn_mask) >> info->pte_rpn_shift)) + + PAGEOFFSET(vaddr); ++ } ++ ++ return paddr; ++} ++ ++/* ++ * If the vmemmap address translation information is stored in the kernel, ++ * make the translation. ++ */ ++static unsigned long long ++ppc64_vmemmap_to_phys(unsigned long vaddr) ++{ ++ int i; ++ ulong offset; ++ unsigned long long paddr = NOT_PADDR; ++ ++ if (!info->vmemmap_list) ++ return ppc64_vtop_level4(vaddr); ++ ++ for (i = 0; i < info->vmemmap_cnt; i++) { ++ if ((vaddr >= info->vmemmap_list[i].virt) && (vaddr < ++ (info->vmemmap_list[i].virt + info->vmemmap_psize))) { ++ offset = vaddr - info->vmemmap_list[i].virt; ++ paddr = info->vmemmap_list[i].phys + offset; ++ break; ++ } ++ } + + return paddr; + } +@@ -567,8 +612,8 @@ get_machdep_info_ppc64(void) + return FALSE; + } + ++ info->vmemmap_start = VMEMMAP_REGION_ID << REGION_SHIFT; + if (SYMBOL(vmemmap_list) != NOT_FOUND_SYMBOL) { +- info->vmemmap_start = VMEMMAP_REGION_ID << REGION_SHIFT; + info->vmemmap_end = info->vmemmap_start; + if (ppc64_vmemmap_init() == FALSE) { + ERRMSG("Can't get vmemmap list info.\n"); +diff --git a/makedumpfile-1.7.3/arch/riscv64.c b/makedumpfile-1.7.3/arch/riscv64.c +new file mode 100644 +index 0000000..1b43ec4 +--- /dev/null ++++ b/makedumpfile-1.7.3/arch/riscv64.c +@@ -0,0 +1,219 @@ ++/* ++ * riscv64.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#ifdef __riscv64__ ++ ++#include "../print_info.h" ++#include "../elf_info.h" ++#include "../makedumpfile.h" ++ ++int ++get_phys_base_riscv64(void) ++{ ++ if (NUMBER(phys_ram_base) != NOT_FOUND_NUMBER) ++ info->phys_base = NUMBER(phys_ram_base); ++ else ++ /* In case that you are using qemu rv64 env */ ++ info->phys_base = 0x80200000; ++ ++ DEBUG_MSG("phys_base : %lx\n", info->phys_base); ++ return TRUE; ++} ++ ++int ++get_machdep_info_riscv64(void) ++{ ++ ++ if(NUMBER(va_bits) == NOT_FOUND_NUMBER || NUMBER(page_offset) == NOT_FOUND_NUMBER || ++ NUMBER(vmalloc_start) == NOT_FOUND_NUMBER || NUMBER(vmalloc_end) == NOT_FOUND_NUMBER || ++ NUMBER(vmemmap_start) == NOT_FOUND_NUMBER || NUMBER(vmemmap_end) == NOT_FOUND_NUMBER || ++ NUMBER(modules_vaddr) == NOT_FOUND_NUMBER || NUMBER(modules_end) == NOT_FOUND_NUMBER || ++ NUMBER(kernel_link_addr) == NOT_FOUND_NUMBER || NUMBER(va_kernel_pa_offset) == NOT_FOUND_NUMBER) ++ return FALSE; ++ ++ if (NUMBER(MAX_PHYSMEM_BITS) != NOT_FOUND_NUMBER) ++ info->max_physmem_bits = NUMBER(MAX_PHYSMEM_BITS); ++ else ++ info->max_physmem_bits = _MAX_PHYSMEM_BITS; ++ ++ if (NUMBER(SECTION_SIZE_BITS) != NOT_FOUND_NUMBER) ++ info->section_size_bits = NUMBER(SECTION_SIZE_BITS); ++ else ++ info->section_size_bits = _SECTION_SIZE_BITS; ++ ++ info->page_offset = NUMBER(page_offset); ++ ++ DEBUG_MSG("va_bits : %ld\n", NUMBER(va_bits)); ++ DEBUG_MSG("page_offset : %lx\n", NUMBER(page_offset)); ++ DEBUG_MSG("vmalloc_start : %lx\n", NUMBER(vmalloc_start)); ++ DEBUG_MSG("vmalloc_end : %lx\n", NUMBER(vmalloc_end)); ++ DEBUG_MSG("vmemmap_start : %lx\n", NUMBER(vmemmap_start)); ++ DEBUG_MSG("vmemmap_end : %lx\n", NUMBER(vmemmap_end)); ++ DEBUG_MSG("modules_vaddr : %lx\n", NUMBER(modules_vaddr)); ++ DEBUG_MSG("modules_end : %lx\n", NUMBER(modules_end)); ++ DEBUG_MSG("kernel_link_addr : %lx\n", NUMBER(kernel_link_addr)); ++ DEBUG_MSG("va_kernel_pa_offset : %lx\n", NUMBER(va_kernel_pa_offset)); ++ ++ return TRUE; ++} ++ ++/* ++ * For direct memory mapping ++ */ ++ ++#define VTOP(X) ({ \ ++ ulong _X = X; \ ++ (_X) >= NUMBER(kernel_link_addr) ? ((_X) - (NUMBER(va_kernel_pa_offset))): \ ++ ((_X) - PAGE_OFFSET + (info->phys_base)); \ ++ }) ++ ++static unsigned long long ++vtop_riscv64(pgd_t * pgd, unsigned long vaddr, long va_bits) ++{ ++ unsigned long long paddr = NOT_PADDR; ++ pgd_t *pgda; ++ p4d_t *p4da; ++ pud_t *puda; ++ pmd_t *pmda; ++ pte_t *ptea; ++ ulong pt_val, pt_phys; ++ ++#define pgd_index(X) ((va_bits == VA_BITS_SV57) ? pgd_index_l5(X) : \ ++ ((va_bits == VA_BITS_SV48) ? pgd_index_l4(X) : pgd_index_l3(X))) ++ ++ /* PGD */ ++ pgda = (pgd_t *)(pgd) + pgd_index(vaddr); ++ if (!readmem(PADDR, (unsigned long long)pgda, &pt_val, sizeof(pt_val))) { ++ ERRMSG("Can't read pgd\n"); ++ goto invalid; ++ } ++ ++ pt_val &= PTE_PFN_PROT_MASK; ++ ++ if (!(pt_val & _PAGE_PRESENT)) { ++ ERRMSG("Can't get a valid pgd.\n"); ++ goto invalid; ++ } ++ ++ pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT(); ++ ++ if (pt_val & _PAGE_LEAF) ++ goto out; ++ ++ if (va_bits == VA_BITS_SV57) ++ goto p4d; ++ else if (va_bits == VA_BITS_SV48) ++ goto pud; ++ else ++ goto pmd; ++p4d: ++ /* P4D */ ++ p4da = (p4d_t *)(pt_phys) + p4d_index(vaddr); ++ if (!readmem(PADDR, (unsigned long long)p4da, &pt_val, sizeof(pt_val))) { ++ ERRMSG("Can't read p4d\n"); ++ goto invalid; ++ } ++ ++ pt_val &= PTE_PFN_PROT_MASK; ++ ++ if (!(pt_val & _PAGE_PRESENT)) { ++ ERRMSG("Can't get a valid p4d.\n"); ++ goto invalid; ++ } ++ ++ pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT(); ++ ++ if (pt_val & _PAGE_LEAF) ++ goto out; ++pud: ++ /* PUD */ ++ puda = (pud_t *)(pt_phys) + pud_index(vaddr); ++ if (!readmem(PADDR, (unsigned long long)puda, &pt_val, sizeof(pt_val))) { ++ ERRMSG("Can't read pud\n"); ++ goto invalid; ++ } ++ ++ pt_val &= PTE_PFN_PROT_MASK; ++ ++ if (!(pt_val & _PAGE_PRESENT)) { ++ ERRMSG("Can't get a valid pud.\n"); ++ goto invalid; ++ } ++ ++ pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT(); ++ ++ if(pt_val & _PAGE_LEAF) ++ goto out; ++pmd: ++ /* PMD */ ++ pmda = (pmd_t *)(pt_phys) + pmd_index(vaddr); ++ if (!readmem(PADDR, (unsigned long long)pmda, &pt_val, sizeof(pt_val))) { ++ ERRMSG("Can't read pmd\n"); ++ goto invalid; ++ } ++ ++ pt_val &= PTE_PFN_PROT_MASK; ++ ++ if (!(pt_val & _PAGE_PRESENT)) { ++ ERRMSG("Can't get a valid pmd.\n"); ++ goto invalid; ++ } ++ ++ pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT(); ++ ++ if (pt_val & _PAGE_LEAF) ++ goto out; ++ ++ /* PTE */ ++ ptea = (pte_t *)(pt_phys) + pte_index(vaddr); ++ if (!readmem(PADDR, (unsigned long long)ptea, &pt_val, sizeof(pt_val))) { ++ ERRMSG("Can't read pte\n"); ++ goto invalid; ++ } ++ ++ pt_val &= PTE_PFN_PROT_MASK; ++ ++ if (!(pt_val & _PAGE_PRESENT)) { ++ ERRMSG("Can't get a valid pte.\n"); ++ goto invalid; ++ } ++ ++ pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT(); ++ ++out: ++ paddr = pt_phys + PAGEOFFSET(vaddr); ++invalid: ++ return paddr; ++} ++ ++unsigned long long ++vaddr_to_paddr_riscv64(unsigned long vaddr) ++{ ++ unsigned long long swapper_phys; ++ ++ if (vaddr >= PAGE_OFFSET && ++ !(vaddr >= NUMBER(modules_vaddr) && vaddr <= NUMBER(modules_end))){ ++ return VTOP(vaddr); ++ } ++ ++ if (SYMBOL(swapper_pg_dir) == NOT_FOUND_SYMBOL) { ++ ERRMSG("Can't get the symbol of swapper_pg_dir.\n"); ++ return NOT_PADDR; ++ } ++ ++ swapper_phys = VTOP(SYMBOL(swapper_pg_dir)); ++ ++ return vtop_riscv64((pgd_t *)swapper_phys, vaddr, NUMBER(va_bits)); ++} ++ ++#endif /* __riscv64__ */ +diff --git a/makedumpfile-1.7.3/dwarf_info.c b/makedumpfile-1.7.3/dwarf_info.c +index 543588b..a3a2fd6 100644 +--- a/makedumpfile-1.7.3/dwarf_info.c ++++ b/makedumpfile-1.7.3/dwarf_info.c +@@ -1091,6 +1091,8 @@ get_symbol_addr(char *symname) + out: + clean_dwfl_info(); + ++ DEBUG_MSG("%s : %s %llx\n", __func__, symname, symbol); ++ + return symbol; + } + +@@ -1175,6 +1177,8 @@ get_next_symbol_addr(char *symname) + out: + clean_dwfl_info(); + ++ DEBUG_MSG("%s: %s %lx\n", __func__, symname, next_symbol); ++ + return next_symbol; + } + +@@ -1192,8 +1196,12 @@ get_structure_size(char *structname, int flag_typedef) + dwarf_info.struct_name = structname; + dwarf_info.struct_size = NOT_FOUND_STRUCTURE; + +- if (!get_debug_info()) ++ if (!get_debug_info()) { ++ DEBUG_MSG("%s: %s failed\n", __func__, structname); + return FAILED_DWARFINFO; ++ } ++ ++ DEBUG_MSG("%s: %s %ld\n", __func__, structname, dwarf_info.struct_size); + + return dwarf_info.struct_size; + } +@@ -1251,8 +1259,13 @@ get_member_offset(char *structname, char *membername, int cmd) + else + dwarf_info.member_name = membername; + +- if (!get_debug_info()) ++ if (!get_debug_info()) { ++ DEBUG_MSG("%s : %s.%s failed\n", __func__, structname, membername); + return FAILED_DWARFINFO; ++ } ++ ++ DEBUG_MSG("%s : %s.%s %ld\n", __func__, structname, membername, ++ dwarf_info.member_offset); + + return dwarf_info.member_offset; + } +@@ -1309,8 +1322,12 @@ get_array_length(char *name01, char *name02, unsigned int cmd) + dwarf_info.member_offset = NOT_FOUND_STRUCTURE; + dwarf_info.array_length = NOT_FOUND_STRUCTURE; + +- if (!get_debug_info()) ++ if (!get_debug_info()) { ++ DEBUG_MSG("%s : %s.%s failed\n", __func__, name01, name02); + return FAILED_DWARFINFO; ++ } ++ ++ DEBUG_MSG("%s : %s.%s %ld\n", __func__, name01, name02, dwarf_info.array_length); + + return dwarf_info.array_length; + } +@@ -1322,8 +1339,12 @@ get_enum_number(char *enum_name) + dwarf_info.enum_name = enum_name; + dwarf_info.enum_number = NOT_FOUND_NUMBER; + +- if (!get_debug_info()) ++ if (!get_debug_info()) { ++ DEBUG_MSG("%s : %s failed\n", __func__, enum_name); + return FAILED_DWARFINFO; ++ } ++ ++ DEBUG_MSG("%s : %s %ld\n", __func__, enum_name, dwarf_info.enum_number); + + return dwarf_info.enum_number; + } +diff --git a/makedumpfile-1.7.3/makedumpfile.c b/makedumpfile-1.7.3/makedumpfile.c +index ba2bb46..a6ec9d4 100644 +--- a/makedumpfile-1.7.3/makedumpfile.c ++++ b/makedumpfile-1.7.3/makedumpfile.c +@@ -262,13 +262,17 @@ is_in_same_page(unsigned long vaddr1, unsigned long vaddr2) + return FALSE; + } + ++/* For Linux 6.6 and later */ ++#define IS_HUGETLB ((unsigned long)-1) ++ + static inline int + isHugetlb(unsigned long dtor) + { +- return ((NUMBER(HUGETLB_PAGE_DTOR) != NOT_FOUND_NUMBER) +- && (NUMBER(HUGETLB_PAGE_DTOR) == dtor)) +- || ((SYMBOL(free_huge_page) != NOT_FOUND_SYMBOL) +- && (SYMBOL(free_huge_page) == dtor)); ++ return (dtor == IS_HUGETLB) ++ || ((NUMBER(HUGETLB_PAGE_DTOR) != NOT_FOUND_NUMBER) ++ && (NUMBER(HUGETLB_PAGE_DTOR) == dtor)) ++ || ((SYMBOL(free_huge_page) != NOT_FOUND_SYMBOL) ++ && (SYMBOL(free_huge_page) == dtor)); + } + + static int +@@ -1710,6 +1714,9 @@ get_symbol_info(void) + return TRUE; + } + ++#define MOD_DATA 1 ++#define MOD_INIT_DATA 5 ++ + int + get_structure_info(void) + { +@@ -1817,6 +1824,26 @@ get_structure_info(void) + OFFSET_INIT(module.num_symtab, "module", "num_symtab"); + OFFSET_INIT(module.list, "module", "list"); + OFFSET_INIT(module.name, "module", "name"); ++ ++ /* kernel >= 6.4 */ ++ SIZE_INIT(module_memory, "module_memory"); ++ if (SIZE(module_memory) != NOT_FOUND_STRUCTURE) { ++ OFFSET_INIT(module.mem, "module", "mem"); ++ OFFSET_INIT(module_memory.base, "module_memory", "base"); ++ OFFSET_INIT(module_memory.size, "module_memory", "size"); ++ ++ OFFSET(module.module_core) = OFFSET(module.mem) + ++ SIZE(module_memory) * MOD_DATA + OFFSET(module_memory.base); ++ OFFSET(module.core_size) = OFFSET(module.mem) + ++ SIZE(module_memory) * MOD_DATA + OFFSET(module_memory.size); ++ OFFSET(module.module_init) = OFFSET(module.mem) + ++ SIZE(module_memory) * MOD_INIT_DATA + OFFSET(module_memory.base); ++ OFFSET(module.init_size) = OFFSET(module.mem) + ++ SIZE(module_memory) * MOD_INIT_DATA + OFFSET(module_memory.size); ++ ++ goto module_end; ++ } ++ + OFFSET_INIT(module.module_core, "module", "module_core"); + if (OFFSET(module.module_core) == NOT_FOUND_STRUCTURE) { + /* for kernel version 4.5 and above */ +@@ -1858,6 +1885,7 @@ get_structure_info(void) + OFFSET(module.init_size) += init_layout; + } + ++module_end: + ENUM_NUMBER_INIT(NR_FREE_PAGES, "NR_FREE_PAGES"); + ENUM_NUMBER_INIT(N_ONLINE, "N_ONLINE"); + ENUM_NUMBER_INIT(pgtable_l5_enabled, "pgtable_l5_enabled"); +@@ -1869,6 +1897,7 @@ get_structure_info(void) + ENUM_NUMBER_INIT(PG_buddy, "PG_buddy"); + ENUM_NUMBER_INIT(PG_slab, "PG_slab"); + ENUM_NUMBER_INIT(PG_hwpoison, "PG_hwpoison"); ++ ENUM_NUMBER_INIT(PG_hugetlb, "PG_hugetlb"); + + ENUM_NUMBER_INIT(PG_head_mask, "PG_head_mask"); + if (NUMBER(PG_head_mask) == NOT_FOUND_NUMBER) { +@@ -2483,6 +2512,7 @@ write_vmcoreinfo_data(void) + WRITE_NUMBER("PG_buddy", PG_buddy); + WRITE_NUMBER("PG_slab", PG_slab); + WRITE_NUMBER("PG_hwpoison", PG_hwpoison); ++ WRITE_NUMBER("PG_hugetlb", PG_hugetlb); + + WRITE_NUMBER("PAGE_BUDDY_MAPCOUNT_VALUE", PAGE_BUDDY_MAPCOUNT_VALUE); + WRITE_NUMBER("PAGE_OFFLINE_MAPCOUNT_VALUE", +@@ -2932,6 +2962,7 @@ read_vmcoreinfo(void) + READ_NUMBER("PG_slab", PG_slab); + READ_NUMBER("PG_buddy", PG_buddy); + READ_NUMBER("PG_hwpoison", PG_hwpoison); ++ READ_NUMBER("PG_hugetlb", PG_hugetlb); + READ_NUMBER("SECTION_SIZE_BITS", SECTION_SIZE_BITS); + READ_NUMBER("MAX_PHYSMEM_BITS", MAX_PHYSMEM_BITS); + +@@ -2948,6 +2979,20 @@ read_vmcoreinfo(void) + READ_NUMBER_UNSIGNED("kimage_voffset", kimage_voffset); + #endif + ++#ifdef __riscv64__ ++ READ_NUMBER("VA_BITS", va_bits); ++ READ_NUMBER_UNSIGNED("phys_ram_base", phys_ram_base); ++ READ_NUMBER_UNSIGNED("PAGE_OFFSET", page_offset); ++ READ_NUMBER_UNSIGNED("VMALLOC_START", vmalloc_start); ++ READ_NUMBER_UNSIGNED("VMALLOC_END", vmalloc_end); ++ READ_NUMBER_UNSIGNED("VMEMMAP_START", vmemmap_start); ++ READ_NUMBER_UNSIGNED("VMEMMAP_END", vmemmap_end); ++ READ_NUMBER_UNSIGNED("MODULES_VADDR", modules_vaddr); ++ READ_NUMBER_UNSIGNED("MODULES_END", modules_end); ++ READ_NUMBER_UNSIGNED("KERNEL_LINK_ADDR", kernel_link_addr); ++ READ_NUMBER_UNSIGNED("va_kernel_pa_offset", va_kernel_pa_offset); ++#endif ++ + READ_NUMBER("HUGETLB_PAGE_DTOR", HUGETLB_PAGE_DTOR); + + return TRUE; +@@ -3264,7 +3309,11 @@ get_mm_flatmem(void) + if (is_xen_memory()) + dump_mem_map(0, info->dom0_mapnr, mem_map, 0); + else ++#ifdef __riscv64__ ++ dump_mem_map((info->phys_base >> PAGESHIFT()), info->max_mapnr, mem_map, 0); ++#else + dump_mem_map(0, info->max_mapnr, mem_map, 0); ++#endif + + return TRUE; + } +@@ -6451,6 +6500,21 @@ __exclude_unnecessary_pages(unsigned long mem_map, + if ((index_pg < PGMM_CACHED - 1) && isCompoundHead(flags)) { + unsigned char *addr = pcache + SIZE(page); + ++ /* ++ * Linux 6.6 and later. Kernels that have PG_hugetlb should also ++ * have the compound order in the low byte of folio._flags_1. ++ */ ++ if (NUMBER(PG_hugetlb) != NOT_FOUND_NUMBER) { ++ unsigned long _flags_1 = ULONG(addr + OFFSET(page.flags)); ++ ++ compound_order = _flags_1 & 0xff; ++ ++ if (_flags_1 & (1UL << NUMBER(PG_hugetlb))) ++ compound_dtor = IS_HUGETLB; ++ ++ goto check_order; ++ } ++ + if (order_offset) { + if (info->kernel_version >= KERNEL_VERSION(4, 16, 0)) + compound_order = UCHAR(addr + order_offset); +@@ -6470,7 +6534,7 @@ __exclude_unnecessary_pages(unsigned long mem_map, + else + compound_dtor = ULONG(addr + dtor_offset); + } +- ++check_order: + if ((compound_order >= sizeof(unsigned long) * 8) + || ((pfn & ((1UL << compound_order) - 1)) != 0)) { + /* Invalid order */ +diff --git a/makedumpfile-1.7.3/makedumpfile.h b/makedumpfile-1.7.3/makedumpfile.h +index 99ccf0a..59a809c 100644 +--- a/makedumpfile-1.7.3/makedumpfile.h ++++ b/makedumpfile-1.7.3/makedumpfile.h +@@ -498,7 +498,7 @@ do { \ + #define KVER_MIN_SHIFT 16 + #define KERNEL_VERSION(x,y,z) (((x) << KVER_MAJ_SHIFT) | ((y) << KVER_MIN_SHIFT) | (z)) + #define OLDEST_VERSION KERNEL_VERSION(2, 6, 15) /* linux-2.6.15 */ +-#define LATEST_VERSION KERNEL_VERSION(6, 3, 0) /* linux-6.3.0 */ ++#define LATEST_VERSION KERNEL_VERSION(6, 6, 0) /* linux-6.6.0 */ + + /* + * vmcoreinfo in /proc/vmcore +@@ -678,6 +678,12 @@ unsigned long get_kvbase_arm64(void); + #define REGION_SHIFT (60UL) + #define VMEMMAP_REGION_ID (0xfUL) + ++/* ++ * If PAGE_PTE is set, then it's a leaf PTE for hugepage ++ */ ++#define PAGE_PTE (1UL << 62) ++#define IS_HUGEPAGE(pte) (!!((pte) & PAGE_PTE)) ++ + /* 4-level page table support */ + + /* 4K pagesize */ +@@ -1046,6 +1052,77 @@ typedef unsigned long pgd_t; + + #endif /* loongarch64 */ + ++#ifdef __riscv64__ ++/* ++ * Referencing the riscv64_is_kvaddr() in Crash-utility, ++ * set the vmemmap start address as the lowest kernel virtual base. ++ */ ++#define KVBASE (NUMBER(vmemmap_start)) ++#define _SECTION_SIZE_BITS (27) ++#define _MAX_PHYSMEM_BITS (56) ++ ++typedef ulong pgd_t; ++typedef ulong p4d_t; ++typedef ulong pud_t; ++typedef ulong pmd_t; ++typedef ulong pte_t; ++ ++/* arch/riscv/include/asm/pgtable-64.h */ ++ ++#define PGD_SHIFT_L3 (30) ++#define PGD_SHIFT_L4 (39) ++#define PGD_SHIFT_L5 (48) ++ ++#define P4D_SHIFT (39) ++#define PUD_SHIFT (30) ++#define PMD_SHIFT (21) ++ ++#define PTRS_PER_PGD (512) ++#define PTRS_PER_P4D (512) ++#define PTRS_PER_PUD (512) ++#define PTRS_PER_PMD (512) ++#define PTRS_PER_PTE (512) ++ ++/* ++ * 3/4/5-levels pg indexs ++ */ ++#define pgd_index_l3(addr) (((addr) >> PGD_SHIFT_L3) & (PTRS_PER_PGD - 1)) ++#define pgd_index_l4(addr) (((addr) >> PGD_SHIFT_L4) & (PTRS_PER_PGD - 1)) ++#define pgd_index_l5(addr) (((addr) >> PGD_SHIFT_L5) & (PTRS_PER_PGD - 1)) ++#define p4d_index(addr) (((addr) >> P4D_SHIFT) & (PTRS_PER_P4D - 1)) ++#define pud_index(addr) (((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1)) ++#define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) ++#define pte_index(addr) (((addr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1)) ++ ++/* arch/riscv/include/asm/pgtable-bits.h */ ++ ++#define _PAGE_PRESENT (1 << 0) ++#define _PAGE_READ (1 << 1) /* Readable */ ++#define _PAGE_WRITE (1 << 2) /* Writable */ ++#define _PAGE_EXEC (1 << 3) /* Executable */ ++#define _PAGE_USER (1 << 4) /* User */ ++#define _PAGE_GLOBAL (1 << 5) /* Global */ ++#define _PAGE_ACCESSED (1 << 6) /* Set by hardware on any access */ ++#define _PAGE_DIRTY (1 << 7) /* Set by hardware on any write */ ++#define _PAGE_SOFT (1 << 8) /* Reserved for software */ ++ ++#define _PAGE_PFN_SHIFT (10) ++#define _PAGE_LEAF (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC) ++ ++/* ++ * Mask for bit 0~53(PROT and PPN) of PTE ++ * 63 6261 60 54 53 10 9 8 7 6 5 4 3 2 1 0 ++ * N PBMT Reserved P P N RSW D A G U X W R V ++ */ ++#define PTE_PFN_PROT_MASK 0x3FFFFFFFFFFFFF ++ ++#define VA_BITS_SV39 (39) ++#define VA_BITS_SV48 (48) ++#define VA_BITS_SV57 (57) ++ ++#endif /* riscv64 */ ++ ++ + /* + * The function of dependence on machine + */ +@@ -1233,6 +1310,22 @@ unsigned long long vaddr_to_paddr_loongarch64(unsigned long vaddr); + #define arch_crashkernel_mem_size() stub_false() + #endif /* loongarch64 */ + ++#ifdef __riscv64__ ++int get_phys_base_riscv64(void); ++int get_machdep_info_riscv64(void); ++unsigned long long vaddr_to_paddr_riscv64(unsigned long vaddr); ++#define paddr_to_vaddr_riscv64(X) ((X) + PAGE_OFFSET - info->phys_base) ++#define find_vmemmap() stub_false() ++#define get_phys_base() get_phys_base_riscv64() ++#define get_machdep_info() get_machdep_info_riscv64() ++#define get_versiondep_info() stub_true() ++#define get_kaslr_offset(X) stub_false() ++#define vaddr_to_paddr(X) vaddr_to_paddr_riscv64(X) ++#define paddr_to_vaddr(X) paddr_to_vaddr_riscv64(X) ++#define is_phys_addr(X) stub_true_ul(X) ++#define arch_crashkernel_mem_size() stub_false() ++#endif /* riscv64 */ ++ + typedef unsigned long long mdf_pfn_t; + + #ifndef ARCH_PFN_OFFSET +@@ -1842,6 +1935,7 @@ struct size_table { + * for loading module symbol data + */ + long module; ++ long module_memory; + + /* + * for sadump +@@ -1944,8 +2038,14 @@ struct offset_table { + long num_symtab; + long symtab; + long strtab; ++ long mem; + } module; + ++ struct module_memory { ++ long base; ++ long size; ++ } module_memory; ++ + /* + * for loading elf_prstaus symbol data + */ +@@ -2131,6 +2231,7 @@ struct number_table { + long PG_buddy; + long PG_slab; + long PG_hwpoison; ++ long PG_hugetlb; + + long PAGE_BUDDY_MAPCOUNT_VALUE; + long PAGE_OFFLINE_MAPCOUNT_VALUE; +@@ -2145,6 +2246,19 @@ struct number_table { + unsigned long PHYS_OFFSET; + unsigned long kimage_voffset; + #endif ++#ifdef __riscv64__ ++ long va_bits; ++ unsigned long phys_ram_base; ++ unsigned long page_offset; ++ unsigned long vmalloc_start; ++ unsigned long vmalloc_end; ++ unsigned long vmemmap_start; ++ unsigned long vmemmap_end; ++ unsigned long modules_vaddr; ++ unsigned long modules_end; ++ unsigned long kernel_link_addr; ++ unsigned long va_kernel_pa_offset; ++#endif + }; + + struct srcfile_table { +@@ -2383,6 +2497,12 @@ int get_xen_info_ia64(void); + #define get_xen_info_arch(X) FALSE + #endif /* loongarch64 */ + ++#ifdef __riscv64__ /* riscv64 */ ++#define kvtop_xen(X) FALSE ++#define get_xen_basic_info_arch(X) FALSE ++#define get_xen_info_arch(X) FALSE ++#endif /* riscv64 */ ++ + struct cycle { + mdf_pfn_t start_pfn; + mdf_pfn_t end_pfn; +diff --git a/makedumpfile-1.7.3/makedumpfile.spec b/makedumpfile-1.7.3/makedumpfile.spec +index 182db54..3b53847 100644 +--- a/makedumpfile-1.7.3/makedumpfile.spec ++++ b/makedumpfile-1.7.3/makedumpfile.spec +@@ -1,6 +1,6 @@ + Name: makedumpfile + Summary: makedumpfile package +-Version: 1.7.3 ++Version: 1.7.4 + Release: 1 + Group: Applications/Text + License: GPL +-- +2.34.1 + -- Gitee