riscv: kexec: add kexec_file_load() support

This patch set implements kexec_file_load() for RISC-V, which is
currently only allowed on rv64 due to some minor build issues on 32-bit
platforms in the generic code.  This allows users to kexec() using an FD
as opposed to a buffer.

Link: https://lore.kernel.org/all/20220408100914.150110-1-lizhengyu3@huawei.com/

* palmer/riscv-kexec_file:
  RISC-V: Load purgatory in kexec_file
  RISC-V: Add purgatory
  RISC-V: Support for kexec_file on panic
  RISC-V: Add kexec_file support
  RISC-V: use memcpy for kexec_file mode
  kexec_file: Fix kexec_file.c build error for riscv platform
This commit is contained in:
Palmer Dabbelt 2022-05-19 15:18:47 -07:00
commit 83a7a614ce
No known key found for this signature in database
GPG Key ID: EF4CA1502CCBAB41
13 changed files with 686 additions and 4 deletions

View File

@ -3,5 +3,7 @@
obj-y += kernel/ mm/ net/
obj-$(CONFIG_BUILTIN_DTB) += boot/dts/
obj-$(CONFIG_ARCH_HAS_KEXEC_PURGATORY) += purgatory/
# for cleaning
subdir- += boot

View File

@ -418,6 +418,26 @@ config KEXEC
The name comes from the similarity to the exec system call.
config KEXEC_FILE
bool "kexec file based systmem call"
select KEXEC_CORE
select KEXEC_ELF
select HAVE_IMA_KEXEC if IMA
depends on 64BIT
help
This is new version of kexec system call. This system call is
file based and takes file descriptors as system call argument
for kernel and initramfs as opposed to list of segments as
accepted by previous system call.
If you don't know what to do here, say Y.
config ARCH_HAS_KEXEC_PURGATORY
def_bool KEXEC_FILE
select BUILD_BIN2C
depends on CRYPTO=y
depends on CRYPTO_SHA256=y
config CRASH_DUMP
bool "Build kdump crash kernel"
help

View File

@ -53,4 +53,8 @@ typedef void (*riscv_kexec_method)(unsigned long first_ind_entry,
extern riscv_kexec_method riscv_kexec_norelocate;
#ifdef CONFIG_KEXEC_FILE
extern const struct kexec_file_ops elf_kexec_ops;
#endif
#endif

View File

@ -79,6 +79,7 @@ endif
obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o
obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KEXEC) += kexec_relocate.o crash_save_regs.o machine_kexec.o
obj-$(CONFIG_KEXEC_FILE) += elf_kexec.o machine_kexec_file.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o

View File

@ -0,0 +1,448 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Load ELF vmlinux file for the kexec_file_load syscall.
*
* Copyright (C) 2021 Huawei Technologies Co, Ltd.
*
* Author: Liao Chang (liaochang1@huawei.com)
*
* Based on kexec-tools' kexec-elf-riscv.c, heavily modified
* for kernel.
*/
#define pr_fmt(fmt) "kexec_image: " fmt
#include <linux/elf.h>
#include <linux/kexec.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/libfdt.h>
#include <linux/types.h>
#include <linux/memblock.h>
#include <asm/setup.h>
static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
struct kexec_elf_info *elf_info, unsigned long old_pbase,
unsigned long new_pbase)
{
int i;
int ret = 0;
size_t size;
struct kexec_buf kbuf;
const struct elf_phdr *phdr;
kbuf.image = image;
for (i = 0; i < ehdr->e_phnum; i++) {
phdr = &elf_info->proghdrs[i];
if (phdr->p_type != PT_LOAD)
continue;
size = phdr->p_filesz;
if (size > phdr->p_memsz)
size = phdr->p_memsz;
kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
kbuf.bufsz = size;
kbuf.buf_align = phdr->p_align;
kbuf.mem = phdr->p_paddr - old_pbase + new_pbase;
kbuf.memsz = phdr->p_memsz;
kbuf.top_down = false;
ret = kexec_add_buffer(&kbuf);
if (ret)
break;
}
return ret;
}
/*
* Go through the available phsyical memory regions and find one that hold
* an image of the specified size.
*/
static int elf_find_pbase(struct kimage *image, unsigned long kernel_len,
struct elfhdr *ehdr, struct kexec_elf_info *elf_info,
unsigned long *old_pbase, unsigned long *new_pbase)
{
int i;
int ret;
struct kexec_buf kbuf;
const struct elf_phdr *phdr;
unsigned long lowest_paddr = ULONG_MAX;
unsigned long lowest_vaddr = ULONG_MAX;
for (i = 0; i < ehdr->e_phnum; i++) {
phdr = &elf_info->proghdrs[i];
if (phdr->p_type != PT_LOAD)
continue;
if (lowest_paddr > phdr->p_paddr)
lowest_paddr = phdr->p_paddr;
if (lowest_vaddr > phdr->p_vaddr)
lowest_vaddr = phdr->p_vaddr;
}
kbuf.image = image;
kbuf.buf_min = lowest_paddr;
kbuf.buf_max = ULONG_MAX;
kbuf.buf_align = PAGE_SIZE;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE);
kbuf.top_down = false;
ret = arch_kexec_locate_mem_hole(&kbuf);
if (!ret) {
*old_pbase = lowest_paddr;
*new_pbase = kbuf.mem;
image->start = ehdr->e_entry - lowest_vaddr + kbuf.mem;
}
return ret;
}
static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
{
unsigned int *nr_ranges = arg;
(*nr_ranges)++;
return 0;
}
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
{
struct crash_mem *cmem = arg;
cmem->ranges[cmem->nr_ranges].start = res->start;
cmem->ranges[cmem->nr_ranges].end = res->end;
cmem->nr_ranges++;
return 0;
}
static int prepare_elf_headers(void **addr, unsigned long *sz)
{
struct crash_mem *cmem;
unsigned int nr_ranges;
int ret;
nr_ranges = 1; /* For exclusion of crashkernel region */
walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
if (!cmem)
return -ENOMEM;
cmem->max_nr_ranges = nr_ranges;
cmem->nr_ranges = 0;
ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
if (ret)
goto out;
/* Exclude crashkernel region */
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
if (!ret)
ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
out:
kfree(cmem);
return ret;
}
static char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
unsigned long cmdline_len)
{
int elfcorehdr_strlen;
char *cmdline_ptr;
cmdline_ptr = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);
if (!cmdline_ptr)
return NULL;
elfcorehdr_strlen = sprintf(cmdline_ptr, "elfcorehdr=0x%lx ",
image->elf_load_addr);
if (elfcorehdr_strlen + cmdline_len > COMMAND_LINE_SIZE) {
pr_err("Appending elfcorehdr=<addr> exceeds cmdline size\n");
kfree(cmdline_ptr);
return NULL;
}
memcpy(cmdline_ptr + elfcorehdr_strlen, cmdline, cmdline_len);
/* Ensure it's nul terminated */
cmdline_ptr[COMMAND_LINE_SIZE - 1] = '\0';
return cmdline_ptr;
}
static void *elf_kexec_load(struct kimage *image, char *kernel_buf,
unsigned long kernel_len, char *initrd,
unsigned long initrd_len, char *cmdline,
unsigned long cmdline_len)
{
int ret;
unsigned long old_kernel_pbase = ULONG_MAX;
unsigned long new_kernel_pbase = 0UL;
unsigned long initrd_pbase = 0UL;
unsigned long headers_sz;
unsigned long kernel_start;
void *fdt, *headers;
struct elfhdr ehdr;
struct kexec_buf kbuf;
struct kexec_elf_info elf_info;
char *modified_cmdline = NULL;
ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
if (ret)
return ERR_PTR(ret);
ret = elf_find_pbase(image, kernel_len, &ehdr, &elf_info,
&old_kernel_pbase, &new_kernel_pbase);
if (ret)
goto out;
kernel_start = image->start;
pr_notice("The entry point of kernel at 0x%lx\n", image->start);
/* Add the kernel binary to the image */
ret = riscv_kexec_elf_load(image, &ehdr, &elf_info,
old_kernel_pbase, new_kernel_pbase);
if (ret)
goto out;
kbuf.image = image;
kbuf.buf_min = new_kernel_pbase + kernel_len;
kbuf.buf_max = ULONG_MAX;
/* Add elfcorehdr */
if (image->type == KEXEC_TYPE_CRASH) {
ret = prepare_elf_headers(&headers, &headers_sz);
if (ret) {
pr_err("Preparing elf core header failed\n");
goto out;
}
kbuf.buffer = headers;
kbuf.bufsz = headers_sz;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
kbuf.memsz = headers_sz;
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
kbuf.top_down = true;
ret = kexec_add_buffer(&kbuf);
if (ret) {
vfree(headers);
goto out;
}
image->elf_headers = headers;
image->elf_load_addr = kbuf.mem;
image->elf_headers_sz = headers_sz;
pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
/* Setup cmdline for kdump kernel case */
modified_cmdline = setup_kdump_cmdline(image, cmdline,
cmdline_len);
if (!modified_cmdline) {
pr_err("Setting up cmdline for kdump kernel failed\n");
ret = -EINVAL;
goto out;
}
cmdline = modified_cmdline;
}
#ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY
/* Add purgatory to the image */
kbuf.top_down = true;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
ret = kexec_load_purgatory(image, &kbuf);
if (ret) {
pr_err("Error loading purgatory ret=%d\n", ret);
goto out;
}
ret = kexec_purgatory_get_set_symbol(image, "riscv_kernel_entry",
&kernel_start,
sizeof(kernel_start), 0);
if (ret)
pr_err("Error update purgatory ret=%d\n", ret);
#endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */
/* Add the initrd to the image */
if (initrd != NULL) {
kbuf.buffer = initrd;
kbuf.bufsz = kbuf.memsz = initrd_len;
kbuf.buf_align = PAGE_SIZE;
kbuf.top_down = false;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out;
initrd_pbase = kbuf.mem;
pr_notice("Loaded initrd at 0x%lx\n", initrd_pbase);
}
/* Add the DTB to the image */
fdt = of_kexec_alloc_and_setup_fdt(image, initrd_pbase,
initrd_len, cmdline, 0);
if (!fdt) {
pr_err("Error setting up the new device tree.\n");
ret = -EINVAL;
goto out;
}
fdt_pack(fdt);
kbuf.buffer = fdt;
kbuf.bufsz = kbuf.memsz = fdt_totalsize(fdt);
kbuf.buf_align = PAGE_SIZE;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
kbuf.top_down = true;
ret = kexec_add_buffer(&kbuf);
if (ret) {
pr_err("Error add DTB kbuf ret=%d\n", ret);
goto out_free_fdt;
}
pr_notice("Loaded device tree at 0x%lx\n", kbuf.mem);
goto out;
out_free_fdt:
kvfree(fdt);
out:
kfree(modified_cmdline);
kexec_free_elf_info(&elf_info);
return ret ? ERR_PTR(ret) : NULL;
}
#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1))
#define RISCV_IMM_BITS 12
#define RISCV_IMM_REACH (1LL << RISCV_IMM_BITS)
#define RISCV_CONST_HIGH_PART(x) \
(((x) + (RISCV_IMM_REACH >> 1)) & ~(RISCV_IMM_REACH - 1))
#define RISCV_CONST_LOW_PART(x) ((x) - RISCV_CONST_HIGH_PART(x))
#define ENCODE_ITYPE_IMM(x) \
(RV_X(x, 0, 12) << 20)
#define ENCODE_BTYPE_IMM(x) \
((RV_X(x, 1, 4) << 8) | (RV_X(x, 5, 6) << 25) | \
(RV_X(x, 11, 1) << 7) | (RV_X(x, 12, 1) << 31))
#define ENCODE_UTYPE_IMM(x) \
(RV_X(x, 12, 20) << 12)
#define ENCODE_JTYPE_IMM(x) \
((RV_X(x, 1, 10) << 21) | (RV_X(x, 11, 1) << 20) | \
(RV_X(x, 12, 8) << 12) | (RV_X(x, 20, 1) << 31))
#define ENCODE_CBTYPE_IMM(x) \
((RV_X(x, 1, 2) << 3) | (RV_X(x, 3, 2) << 10) | (RV_X(x, 5, 1) << 2) | \
(RV_X(x, 6, 2) << 5) | (RV_X(x, 8, 1) << 12))
#define ENCODE_CJTYPE_IMM(x) \
((RV_X(x, 1, 3) << 3) | (RV_X(x, 4, 1) << 11) | (RV_X(x, 5, 1) << 2) | \
(RV_X(x, 6, 1) << 7) | (RV_X(x, 7, 1) << 6) | (RV_X(x, 8, 2) << 9) | \
(RV_X(x, 10, 1) << 8) | (RV_X(x, 11, 1) << 12))
#define ENCODE_UJTYPE_IMM(x) \
(ENCODE_UTYPE_IMM(RISCV_CONST_HIGH_PART(x)) | \
(ENCODE_ITYPE_IMM(RISCV_CONST_LOW_PART(x)) << 32))
#define ENCODE_UITYPE_IMM(x) \
(ENCODE_UTYPE_IMM(x) | (ENCODE_ITYPE_IMM(x) << 32))
#define CLEAN_IMM(type, x) \
((~ENCODE_##type##_IMM((uint64_t)(-1))) & (x))
int arch_kexec_apply_relocations_add(struct purgatory_info *pi,
Elf_Shdr *section,
const Elf_Shdr *relsec,
const Elf_Shdr *symtab)
{
const char *strtab, *name, *shstrtab;
const Elf_Shdr *sechdrs;
Elf_Rela *relas;
int i, r_type;
/* String & section header string table */
sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
strtab = (char *)pi->ehdr + sechdrs[symtab->sh_link].sh_offset;
shstrtab = (char *)pi->ehdr + sechdrs[pi->ehdr->e_shstrndx].sh_offset;
relas = (void *)pi->ehdr + relsec->sh_offset;
for (i = 0; i < relsec->sh_size / sizeof(*relas); i++) {
const Elf_Sym *sym; /* symbol to relocate */
unsigned long addr; /* final location after relocation */
unsigned long val; /* relocated symbol value */
unsigned long sec_base; /* relocated symbol value */
void *loc; /* tmp location to modify */
sym = (void *)pi->ehdr + symtab->sh_offset;
sym += ELF64_R_SYM(relas[i].r_info);
if (sym->st_name)
name = strtab + sym->st_name;
else
name = shstrtab + sechdrs[sym->st_shndx].sh_name;
loc = pi->purgatory_buf;
loc += section->sh_offset;
loc += relas[i].r_offset;
if (sym->st_shndx == SHN_ABS)
sec_base = 0;
else if (sym->st_shndx >= pi->ehdr->e_shnum) {
pr_err("Invalid section %d for symbol %s\n",
sym->st_shndx, name);
return -ENOEXEC;
} else
sec_base = pi->sechdrs[sym->st_shndx].sh_addr;
val = sym->st_value;
val += sec_base;
val += relas[i].r_addend;
addr = section->sh_addr + relas[i].r_offset;
r_type = ELF64_R_TYPE(relas[i].r_info);
switch (r_type) {
case R_RISCV_BRANCH:
*(u32 *)loc = CLEAN_IMM(BTYPE, *(u32 *)loc) |
ENCODE_BTYPE_IMM(val - addr);
break;
case R_RISCV_JAL:
*(u32 *)loc = CLEAN_IMM(JTYPE, *(u32 *)loc) |
ENCODE_JTYPE_IMM(val - addr);
break;
/*
* With no R_RISCV_PCREL_LO12_S, R_RISCV_PCREL_LO12_I
* sym is expected to be next to R_RISCV_PCREL_HI20
* in purgatory relsec. Handle it like R_RISCV_CALL
* sym, instead of searching the whole relsec.
*/
case R_RISCV_PCREL_HI20:
case R_RISCV_CALL:
*(u64 *)loc = CLEAN_IMM(UITYPE, *(u64 *)loc) |
ENCODE_UJTYPE_IMM(val - addr);
break;
case R_RISCV_RVC_BRANCH:
*(u32 *)loc = CLEAN_IMM(CBTYPE, *(u32 *)loc) |
ENCODE_CBTYPE_IMM(val - addr);
break;
case R_RISCV_RVC_JUMP:
*(u32 *)loc = CLEAN_IMM(CJTYPE, *(u32 *)loc) |
ENCODE_CJTYPE_IMM(val - addr);
break;
case R_RISCV_ADD32:
*(u32 *)loc += val;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= val;
break;
/* It has been applied by R_RISCV_PCREL_HI20 sym */
case R_RISCV_PCREL_LO12_I:
case R_RISCV_ALIGN:
case R_RISCV_RELAX:
break;
default:
pr_err("Unknown rela relocation: %d\n", r_type);
return -ENOEXEC;
}
}
return 0;
}
const struct kexec_file_ops elf_kexec_ops = {
.probe = kexec_elf_probe,
.load = elf_kexec_load,
};

View File

@ -65,7 +65,9 @@ machine_kexec_prepare(struct kimage *image)
if (image->segment[i].memsz <= sizeof(fdt))
continue;
if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt)))
if (image->file_mode)
memcpy(&fdt, image->segment[i].buf, sizeof(fdt));
else if (copy_from_user(&fdt, image->segment[i].buf, sizeof(fdt)))
continue;
if (fdt_check_header(&fdt))

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* kexec_file for riscv, use vmlinux as the dump-capture kernel image.
*
* Copyright (C) 2021 Huawei Technologies Co, Ltd.
*
* Author: Liao Chang (liaochang1@huawei.com)
*/
#include <linux/kexec.h>
const struct kexec_file_ops * const kexec_file_loaders[] = {
&elf_kexec_ops,
NULL
};

4
arch/riscv/purgatory/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
purgatory.chk
purgatory.ro
kexec-purgatory.c

View File

@ -0,0 +1,95 @@
# SPDX-License-Identifier: GPL-2.0
OBJECT_FILES_NON_STANDARD := y
purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
targets += $(purgatory-y)
PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
$(obj)/string.o: $(srctree)/lib/string.c FORCE
$(call if_changed_rule,cc_o_c)
$(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE
$(call if_changed_rule,cc_o_c)
$(obj)/memcpy.o: $(srctree)/arch/riscv/lib/memcpy.S FORCE
$(call if_changed_rule,as_o_S)
$(obj)/memset.o: $(srctree)/arch/riscv/lib/memset.S FORCE
$(call if_changed_rule,as_o_S)
$(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE
$(call if_changed_rule,cc_o_c)
CFLAGS_sha256.o := -D__DISABLE_EXPORTS
CFLAGS_string.o := -D__DISABLE_EXPORTS
CFLAGS_ctype.o := -D__DISABLE_EXPORTS
# When linking purgatory.ro with -r unresolved symbols are not checked,
# also link a purgatory.chk binary without -r to check for unresolved symbols.
PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib
LDFLAGS_purgatory.ro := -r $(PURGATORY_LDFLAGS)
LDFLAGS_purgatory.chk := $(PURGATORY_LDFLAGS)
targets += purgatory.ro purgatory.chk
# Sanitizer, etc. runtimes are unavailable and cannot be linked here.
GCOV_PROFILE := n
KASAN_SANITIZE := n
UBSAN_SANITIZE := n
KCSAN_SANITIZE := n
KCOV_INSTRUMENT := n
# These are adjustments to the compiler flags used for objects that
# make up the standalone purgatory.ro
PURGATORY_CFLAGS_REMOVE := -mcmodel=kernel
PURGATORY_CFLAGS := -mcmodel=medany -ffreestanding -fno-zero-initialized-in-bss
PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN) -DDISABLE_BRANCH_PROFILING
PURGATORY_CFLAGS += -fno-stack-protector -g0
# Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
# in turn leaves some undefined symbols like __fentry__ in purgatory and not
# sure how to relocate those.
ifdef CONFIG_FUNCTION_TRACER
PURGATORY_CFLAGS_REMOVE += $(CC_FLAGS_FTRACE)
endif
ifdef CONFIG_STACKPROTECTOR
PURGATORY_CFLAGS_REMOVE += -fstack-protector
endif
ifdef CONFIG_STACKPROTECTOR_STRONG
PURGATORY_CFLAGS_REMOVE += -fstack-protector-strong
endif
CFLAGS_REMOVE_purgatory.o += $(PURGATORY_CFLAGS_REMOVE)
CFLAGS_purgatory.o += $(PURGATORY_CFLAGS)
CFLAGS_REMOVE_sha256.o += $(PURGATORY_CFLAGS_REMOVE)
CFLAGS_sha256.o += $(PURGATORY_CFLAGS)
CFLAGS_REMOVE_string.o += $(PURGATORY_CFLAGS_REMOVE)
CFLAGS_string.o += $(PURGATORY_CFLAGS)
CFLAGS_REMOVE_ctype.o += $(PURGATORY_CFLAGS_REMOVE)
CFLAGS_ctype.o += $(PURGATORY_CFLAGS)
AFLAGS_REMOVE_entry.o += -Wa,-gdwarf-2
AFLAGS_REMOVE_memcpy.o += -Wa,-gdwarf-2
AFLAGS_REMOVE_memset.o += -Wa,-gdwarf-2
$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
$(call if_changed,ld)
$(obj)/purgatory.chk: $(obj)/purgatory.ro FORCE
$(call if_changed,ld)
targets += kexec-purgatory.c
quiet_cmd_bin2c = BIN2C $@
cmd_bin2c = $(objtree)/scripts/bin2c kexec_purgatory < $< > $@
$(obj)/kexec-purgatory.c: $(obj)/purgatory.ro $(obj)/purgatory.chk FORCE
$(call if_changed,bin2c)
obj-$(CONFIG_ARCH_HAS_KEXEC_PURGATORY) += kexec-purgatory.o

View File

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* purgatory: Runs between two kernels
*
* Copyright (C) 2022 Huawei Technologies Co, Ltd.
*
* Author: Li Zhengyu (lizhengyu3@huawei.com)
*
*/
.macro size, sym:req
.size \sym, . - \sym
.endm
.text
.globl purgatory_start
purgatory_start:
lla sp, .Lstack
mv s0, a0 /* The hartid of the current hart */
mv s1, a1 /* Phys address of the FDT image */
jal purgatory
/* Start new image. */
mv a0, s0
mv a1, s1
ld a2, riscv_kernel_entry
jr a2
size purgatory_start
.align 4
.rept 256
.quad 0
.endr
.Lstack:
.data
.globl riscv_kernel_entry
riscv_kernel_entry:
.quad 0
size riscv_kernel_entry
.end

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* purgatory: Runs between two kernels
*
* Copyright (C) 2022 Huawei Technologies Co, Ltd.
*
* Author: Li Zhengyu (lizhengyu3@huawei.com)
*
*/
#include <linux/purgatory.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/string.h>
u8 purgatory_sha256_digest[SHA256_DIGEST_SIZE] __section(".kexec-purgatory");
struct kexec_sha_region purgatory_sha_regions[KEXEC_SEGMENT_MAX] __section(".kexec-purgatory");
static int verify_sha256_digest(void)
{
struct kexec_sha_region *ptr, *end;
struct sha256_state ss;
u8 digest[SHA256_DIGEST_SIZE];
sha256_init(&ss);
end = purgatory_sha_regions + ARRAY_SIZE(purgatory_sha_regions);
for (ptr = purgatory_sha_regions; ptr < end; ptr++)
sha256_update(&ss, (uint8_t *)(ptr->start), ptr->len);
sha256_final(&ss, digest);
if (memcmp(digest, purgatory_sha256_digest, sizeof(digest)) != 0)
return 1;
return 0;
}
/* workaround for a warning with -Wmissing-prototypes */
void purgatory(void);
void purgatory(void)
{
if (verify_sha256_digest())
for (;;)
/* loop forever */
;
}

View File

@ -227,7 +227,7 @@ struct crash_mem {
extern int crash_exclude_mem_range(struct crash_mem *mem,
unsigned long long mstart,
unsigned long long mend);
extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
extern int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
void **addr, unsigned long *sz);
#endif /* CONFIG_KEXEC_FILE */

View File

@ -1260,7 +1260,7 @@ int crash_exclude_mem_range(struct crash_mem *mem,
return 0;
}
int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
void **addr, unsigned long *sz)
{
Elf64_Ehdr *ehdr;
@ -1324,7 +1324,7 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
phdr++;
/* Prepare PT_LOAD type program header for kernel text region */
if (kernel_map) {
if (need_kernel_map) {
phdr->p_type = PT_LOAD;
phdr->p_flags = PF_R|PF_W|PF_X;
phdr->p_vaddr = (unsigned long) _text;