F.Y.I Nick Kossifidis The first author should be him. On Wed, Sep 14, 2022 at 3:52 PM Xianting Tian <xianting.tian@xxxxxxxxxxxxxxxxx> wrote: > > 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 <yixun.lan@xxxxxxxxx> > Signed-off-by: Xianting Tian <xianting.tian@xxxxxxxxxxxxxxxxx> > Signed-off-by: Yixun Lan <yixun.lan@xxxxxxxxx> > Signed-off-by: Nick Kossifidis <mick@xxxxxxxxxxxx> > --- > configure.ac | 3 + > include/elf.h | 3 +- > kexec/Makefile | 1 + > kexec/arch/riscv/Makefile | 35 ++ > kexec/arch/riscv/crashdump-riscv.c | 140 ++++++++ > kexec/arch/riscv/include/arch/options.h | 43 +++ > kexec/arch/riscv/kexec-elf-riscv.c | 255 ++++++++++++++ > kexec/arch/riscv/kexec-riscv.c | 365 +++++++++++++++++++ > kexec/arch/riscv/kexec-riscv.h | 32 ++ > kexec/dt-ops.c | 442 +++++++++++++++++++++++- > kexec/dt-ops.h | 7 + > kexec/kexec-syscall.h | 4 + > purgatory/Makefile | 1 + > purgatory/arch/riscv/Makefile | 7 + > 14 files changed, 1336 insertions(+), 2 deletions(-) > create mode 100644 kexec/arch/riscv/Makefile > create mode 100644 kexec/arch/riscv/crashdump-riscv.c > create mode 100644 kexec/arch/riscv/include/arch/options.h > create mode 100644 kexec/arch/riscv/kexec-elf-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 0d825ef..7cc55b0 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -58,6 +58,9 @@ case $target_cpu in > hppa*) > ARCH="hppa" > ;; > + riscv32|riscv64 ) > + ARCH="riscv" > + ;; > * ) > AC_MSG_ERROR([unsupported architecture $target_cpu]) > ;; > diff --git a/include/elf.h b/include/elf.h > index b7677a2..123f167 100644 > --- a/include/elf.h > +++ b/include/elf.h > @@ -259,7 +259,8 @@ 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_NUM 184 > +#define EM_RISCV 243 /* RISC-V */ > +#define EM_NUM 244 > > /* If it is necessary to assign new unofficial EM_* values, please > pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the > diff --git a/kexec/Makefile b/kexec/Makefile > index e69e309..ca17831 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..f26cc90 > --- /dev/null > +++ b/kexec/arch/riscv/Makefile > @@ -0,0 +1,35 @@ > +# > +# 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/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/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..3ed4fe3 > --- /dev/null > +++ b/kexec/arch/riscv/crashdump-riscv.c > @@ -0,0 +1,140 @@ > +#include <errno.h> > +#include <linux/elf.h> > +#include <unistd.h> > + > +#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/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/kexec-elf-riscv.c b/kexec/arch/riscv/kexec-elf-riscv.c > new file mode 100644 > index 0000000..f3c011c > --- /dev/null > +++ b/kexec/arch/riscv/kexec-elf-riscv.c > @@ -0,0 +1,255 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2019 FORTH-ICS/CARV > + * Nick Kossifidis <mick@xxxxxxxxxxxx> > + */ > + > +#include "kexec.h" > +#include "dt-ops.h" /* For dtb_set/clear_initrd() */ > +#include <elf.h> /* For ELF header handling */ > +#include <errno.h> /* For EFBIG/EINVAL */ > +#include <unistd.h> /* For getpagesize() */ > +#include "kexec-syscall.h" /* For KEXEC_ON_CRASH */ > +#include "kexec-riscv.h" > + > + > +/*********\ > +* HELPERS * > +\*********/ > + > +/* > + * Go through the available physical memory regions and > + * find one that can hold an image of the specified size. > + * 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. > + */ > +static int elf_riscv_find_pbase(struct kexec_info *info, off_t *addr, > + off_t size) > +{ > + 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) { > + /* > + * Kernel should be aligned to the nearest > + * hugepage (2MB for RV64, 4MB for RV32). > + */ > +#if __riscv_xlen == 64 > + start = _ALIGN_UP(start, 0x200000); > +#else > + start = _ALIGN_UP(start, 0x400000); > +#endif > + 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; > + > +#if __riscv_xlen == 64 > + start = _ALIGN_UP(start, 0x200000); > +#else > + start = _ALIGN_UP(start, 0x400000); > +#endif > + > + if (end > start && ((end - start) >= size)) { > + *addr = start; > + return 0; > + } > + } > + > + return -EFBIG; > +} > + > +/**************\ > +* 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) { > + fprintf(stderr, "kexec_file not supported on this " > + "architecture\n"); > + return -EINVAL; > + } > + > + /* 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 = elf_riscv_find_pbase(info, &new_base_addr, kernel_size); > + 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-riscv.c b/kexec/arch/riscv/kexec-riscv.c > new file mode 100644 > index 0000000..fe5dd2d > --- /dev/null > +++ b/kexec/arch/riscv/kexec-riscv.c > @@ -0,0 +1,365 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2019 FORTH-ICS/CARV > + * Nick Kossifidis <mick@xxxxxxxxxxxx> > + */ > + > +#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 <arch/options.h> /* For KEXEC_ARCH_OPTIONS */ > +#include <getopt.h> /* For struct option */ > +#include <sys/stat.h> /* For stat() and struct stat */ > +#include <stdlib.h> /* For free() */ > +#include <errno.h> /* For EINVAL */ > +#include <libfdt.h> /* For DeviceTree handling */ > +#include "kexec-riscv.h" > + > +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}, > +}; > +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" > +" --cmdline=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}; > +static struct memory_ranges sysmem_ranges = {0}; > + > +/****************\ > +* COMMON HELPERS * > +\****************/ > + > +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, > + "memory", "linux,usable-memory"); > + if (ret) { > + fprintf(stderr, "Couldn't add usable-memory to fdt\n"); > + return ret; > + } > + > + max_usable = end; > + } else { > + /* > + * Make sure we remove elfcorehdr and usable-memory > + * when switching from crash kernel to a normal one. > + */ > + dtb_delete_property(fdt->buf, "chosen", "linux,elfcorehdr"); > + dtb_delete_property(fdt->buf, "memory", "linux,usable-memory"); > + } > + > + /* 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; > + max_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 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) > +{ > + const struct fdt_image *fdt = &provided_fdt; > + struct memory_ranges *extra_ranges = NULL; > + int i = 0; > + int ret = 0; > + > + if (arch_options.initrd_start && arch_options.initrd_end) { > + int initrd_size = arch_options.initrd_end - arch_options.initrd_start; > + dbgprintf("Marking current intird image as reserved\n"); > + ret = mem_regions_alloc_and_add(extra_ranges, > + arch_options.initrd_start, > + initrd_size, > + RANGE_RESERVED); > + if (ret) > + return ret; > + } > + > + ret = dtb_get_memory_ranges(fdt->buf, &sysmem_ranges, extra_ranges); > + if (ret) { > + fprintf(stderr, "Could not get memory ranges from device tree (%i) !\n", ret); > + return ret; > + } > + > + *range = sysmem_ranges.ranges; > + *num_ranges = sysmem_ranges.size; > + > + dbgprintf("Memory regions:\n"); > + for (i = 0; i < sysmem_ranges.size; i++) { > + dbgprintf("\t0x%llx - 0x%llx : %s (%i)\n", > + sysmem_ranges.ranges[i].start, > + sysmem_ranges.ranges[i].end, > + sysmem_ranges.ranges[i].type == RANGE_RESERVED ? > + "RANGE_RESERVED" : "RANGE_RAM", > + sysmem_ranges.ranges[i].type); > + } > + > + 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..c4323a6 > --- /dev/null > +++ b/kexec/arch/riscv/kexec-riscv.h > @@ -0,0 +1,32 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2019 FORTH-ICS/CARV > + * Nick Kossifidis <mick@xxxxxxxxxxxx> > + */ > + > +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 load_extra_segments(struct kexec_info *info, uint64_t kernel_base, > + uint64_t kernel_size, uint64_t max_addr); > + > +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); > 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 <libfdt.h> > #include <stdio.h> > #include <stdlib.h> > +#include <stdbool.h> > > #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 <address><size> 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-syscall.h b/kexec/kexec-syscall.h > index bea29d4..2e99e2b 100644 > --- a/kexec/kexec-syscall.h > +++ b/kexec/kexec-syscall.h > @@ -134,6 +134,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_MAX_SEGMENTS 16 > > @@ -177,5 +178,8 @@ static inline long kexec_file_load(int kernel_fd, int initrd_fd, > #if defined(__arm64__) > #define KEXEC_ARCH_NATIVE KEXEC_ARCH_ARM64 > #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 15adb12..11694e5 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..8bded71 > --- /dev/null > +++ b/purgatory/arch/riscv/Makefile > @@ -0,0 +1,7 @@ > +# > +# Purgatory riscv > +# > + > +riscv_PURGATORY_SRCS = > + > +dist += purgatory/arch/sh/Makefile $(riscv_PURGATORY_SRCS) > -- > 2.17.1 > -- Best Regards Guo Ren _______________________________________________ kexec mailing list kexec@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/kexec