Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,9 @@ config ARCH_DEFAULT_CRASH_DUMP
config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
def_bool CRASH_RESERVE

config ARCH_SUPPORTS_CRASH_HOTPLUG
def_bool y

config COMPAT
bool "Kernel support for 32-bit U-mode"
default 64BIT
Expand Down
13 changes: 13 additions & 0 deletions arch/riscv/include/asm/kexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,17 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start,
unsigned long cmdline_len);
#endif

#ifdef CONFIG_CRASH_HOTPLUG
struct kimage;

void arch_crash_handle_hotplug_event(struct kimage *image, void *arg);
#define arch_crash_handle_hotplug_event arch_crash_handle_hotplug_event

int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags);
#define arch_crash_hotplug_support arch_crash_hotplug_support

unsigned int arch_crash_get_elfcorehdr_size(void);
#define crash_get_elfcorehdr_size arch_crash_get_elfcorehdr_size
#endif

#endif
1 change: 1 addition & 0 deletions arch/riscv/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KEXEC_CORE) += kexec_relocate.o crash_save_regs.o machine_kexec.o
obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o kexec_image.o machine_kexec_file.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_CRASH_HOTPLUG) += crash.o
obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o

obj-$(CONFIG_JUMP_LABEL) += jump_label.o
Expand Down
113 changes: 113 additions & 0 deletions arch/riscv/kernel/crash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* RISC-V crash hotplug support
*
* Copyright (C) 2025 Bytedance Ltd.
*/

#include <linux/kexec.h>
#include <linux/crash_core.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>

#ifdef CONFIG_CRASH_HOTPLUG

#undef pr_fmt
#define pr_fmt(fmt) "crash hp: " fmt

int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags)
{
#ifdef CONFIG_KEXEC_FILE
if (image->file_mode)
return 1;
#endif
/*
* For the kexec_load() syscall path, the user space kexec tool
* needs to indicate that the elfcorehdr segment is excluded from
* SHA verification by setting the appropriate flags.
*/
return (kexec_flags & KEXEC_UPDATE_ELFCOREHDR ||
kexec_flags & KEXEC_CRASH_HOTPLUG_SUPPORT);
}

unsigned int arch_crash_get_elfcorehdr_size(void)
{
unsigned int sz;

/* kernel_map, VMCOREINFO and maximum CPUs */
sz = 2 + CONFIG_NR_CPUS;
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
sz += CONFIG_CRASH_MAX_MEMORY_RANGES;
sz *= sizeof(Elf64_Phdr);
return sz;
}

/**
* arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
* @image: a pointer to kexec_crash_image
* @arg: struct memory_notify handler for memory hotplug case and
* NULL for CPU hotplug case.
*
* Prepare the new elfcorehdr and replace the existing elfcorehdr.
*/
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
{
void *elfbuf = NULL, *old_elfcorehdr;
unsigned long mem, memsz;
unsigned long elfsz = 0;

/*
* As crash_prepare_elf64_headers() has already described all
* possible CPUs, there is no need to update the elfcorehdr
* for additional CPU changes.
*/
if ((image->file_mode || image->elfcorehdr_updated) &&
((image->hp_action == KEXEC_CRASH_HP_ADD_CPU) ||
(image->hp_action == KEXEC_CRASH_HP_REMOVE_CPU)))
return;

/*
* Create the new elfcorehdr reflecting the changes to CPU and/or
* memory resources.
*/
if (crash_prepare_headers(true, &elfbuf, &elfsz, NULL)) {
pr_err("unable to create new elfcorehdr");
goto out;
}

/*
* Obtain address and size of the elfcorehdr segment, and
* check it against the new elfcorehdr buffer.
*/
mem = image->segment[image->elfcorehdr_index].mem;
memsz = image->segment[image->elfcorehdr_index].memsz;
if (elfsz > memsz) {
pr_err("update elfcorehdr elfsz %lu > memsz %lu",
elfsz, memsz);
goto out;
}

/*
* Copy new elfcorehdr over the old elfcorehdr at destination.
*/
old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
if (!old_elfcorehdr) {
pr_err("mapping elfcorehdr segment failed\n");
goto out;
}

/*
* Temporarily invalidate the crash image while the
* elfcorehdr is updated.
*/
xchg(&kexec_crash_image, NULL);
memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
xchg(&kexec_crash_image, image);
kunmap_local(old_elfcorehdr);
pr_debug("updated elfcorehdr\n");

out:
vfree(elfbuf);
}

#endif /* CONFIG_CRASH_HOTPLUG */
Loading