kexec: updates for v7.3-rc1

* Deduplicate crash memory allocation and the exclusion of reserved crash
   kernel regions from architecture specific code into a generic
   crash_prepare_headers() and enable crashkernel CMA reservation on arm64 and
   riscv reservation on arm64 and riscv.
 * Skip purgatory checksum verification when the kexec segments cannot be
   corrupted by DMA, which saves about 250ms on kexec.
 * Replace __ASSEMBLY__ with the compiler provided __ASSEMBLER__ in
   include/linux/kexec.h.
 * Fix a keyring refcount imbalance in the kdump kernel's dm-crypt key restore
   path, which over-dropped the user keyring reference when more than one key
   was restored.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEeOVYVaWZL5900a/pOQOGJssO/ZEFAmp9ouoACgkQOQOGJssO
 /ZEH/Af/b1qmnw8uUe2wzd1zcAEEsk6YwF8lmS5o4k7skiVKlHWQuDzPrwrxFTvg
 d6obJrLllMN2ULyLQ8ghMKbsdo8RCGBIpXF+bPEXmIF8bA/PhtDaxvi3scsxvie6
 zU3geZbExlzg0Ik525I0DyPoEZc2JAg5ietp8bF6ZN5HNkDtBoNvi2ROc4mR9kzO
 agrczVnIEuIE3Tk+IfD6081iS3Wz/PjRC+yPBU1yYFobA6/bL7kFma7kED8PnxvI
 2+oENcd1FtC/WBlmluZE0BC4SjNcsT1geyzAFlGlRvNzbHdtj13j6mTBSsbRpJMW
 WdewVpIpFGUqRbUIIdvOISLOcf62lg==
 =PjFs
 -----END PGP SIGNATURE-----

Merge tag 'kexec-v7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux

Pull kexec updates from Mike Rapoport:

 - Deduplicate crash memory allocation and the exclusion of reserved
   crash kernel regions from architecture specific code into a generic
   crash_prepare_headers() and enable crashkernel CMA reservation on
   arm64 and riscv reservation on arm64 and riscv.

 - Skip purgatory checksum verification when the kexec segments cannot
   be corrupted by DMA, which saves about 250ms on kexec.

 - Replace __ASSEMBLY__ with the compiler provided __ASSEMBLER__ in
   include/linux/kexec.h.

 - Fix a keyring refcount imbalance in the kdump kernel's dm-crypt key
   restore path, which over-dropped the user keyring reference when
   more than one key was restored.

* tag 'kexec-v7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux:
  crash_dump: release keyring reference at the correct time
  kexec: Replace __ASSEMBLY__ with __ASSEMBLER__ in header file
  kexec_file: skip checksum verification when safe
  riscv: kexec_file: Add support for crashkernel CMA reservation
  arm64: kexec_file: Add support for crashkernel CMA reservation
  powerpc/kexec_file: Use crash_exclude_core_ranges() helper
  LoongArch: kexec_file: Use crash_prepare_headers() helper to simplify code
  riscv: kexec_file: Use crash_prepare_headers() helper to simplify code
  x86/crash: Use crash_prepare_headers() helper to simplify code
  arm64: kexec_file: Use crash_prepare_headers() helper to simplify code
  crash: Add crash_prepare_headers() to exclude crash kernel memory
  powerpc/crash: sort crash memory ranges before preparing elfcorehdr
  riscv: kexec_file: Fix crashk_low_res not exclude bug
This commit is contained in:
Linus Torvalds 2026-08-18 10:28:28 -07:00
commit ba24659b1d
18 changed files with 218 additions and 287 deletions

View File

@ -1090,14 +1090,14 @@ Kernel parameters
It will be ignored when crashkernel=X,high is not used
or memory reserved is below 4G.
crashkernel=size[KMG],cma
[KNL, X86, ppc] Reserve additional crash kernel memory from
CMA. This reservation is usable by the first system's
userspace memory and kernel movable allocations (memory
balloon, zswap). Pages allocated from this memory range
will not be included in the vmcore so this should not
be used if dumping of userspace memory is intended and
it has to be expected that some movable kernel pages
may be missing from the dump.
[KNL, X86, ARM64, RISCV, PPC] Reserve additional crash
kernel memory from CMA. This reservation is usable by
the first system's userspace memory and kernel movable
allocations (memory balloon, zswap). Pages allocated
from this memory range will not be included in the vmcore
so this should not be used if dumping of userspace memory
is intended and it has to be expected that some movable
kernel pages may be missing from the dump.
A standard crashkernel reservation, as described above,
is still needed to hold the crash kernel and initrd.

View File

@ -40,46 +40,30 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
}
#ifdef CONFIG_CRASH_DUMP
static int prepare_elf_headers(void **addr, unsigned long *sz)
unsigned int arch_get_system_nr_ranges(void)
{
struct crash_mem *cmem;
unsigned int nr_ranges;
int ret;
u64 i;
unsigned int nr_ranges = 2 + crashk_cma_cnt; /* for exclusion of crashkernel region */
phys_addr_t start, end;
u64 i;
nr_ranges = 2; /* for exclusion of crashkernel region */
for_each_mem_range(i, &start, &end)
nr_ranges++;
cmem = kmalloc_flex(*cmem, ranges, nr_ranges);
if (!cmem)
return -ENOMEM;
return nr_ranges;
}
int arch_crash_populate_cmem(struct crash_mem *cmem)
{
phys_addr_t start, end;
u64 i;
cmem->max_nr_ranges = nr_ranges;
cmem->nr_ranges = 0;
for_each_mem_range(i, &start, &end) {
cmem->ranges[cmem->nr_ranges].start = start;
cmem->ranges[cmem->nr_ranges].end = end - 1;
cmem->nr_ranges++;
}
/* Exclude crashkernel region */
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
if (ret)
goto out;
if (crashk_low_res.end) {
ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
if (ret)
goto out;
}
ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
out:
kfree(cmem);
return ret;
return 0;
}
#endif
@ -109,7 +93,7 @@ int load_other_segments(struct kimage *image,
void *headers;
unsigned long headers_sz;
if (image->type == KEXEC_TYPE_CRASH) {
ret = prepare_elf_headers(&headers, &headers_sz);
ret = crash_prepare_headers(true, &headers, &headers_sz, NULL);
if (ret) {
pr_err("Preparing elf core header failed\n");
goto out_err;

View File

@ -96,8 +96,8 @@ phys_addr_t __ro_after_init arm64_dma_phys_limit;
static void __init arch_reserve_crashkernel(void)
{
unsigned long long crash_base, crash_size, cma_size = 0;
unsigned long long low_size = 0;
unsigned long long crash_base, crash_size;
bool high = false;
int ret;
@ -106,11 +106,12 @@ static void __init arch_reserve_crashkernel(void)
ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
&crash_size, &crash_base,
&low_size, NULL, &high);
&low_size, &cma_size, &high);
if (ret)
return;
reserve_crashkernel_generic(crash_size, crash_base, low_size, high);
reserve_crashkernel_cma(cma_size);
}
static phys_addr_t __init max_zone_phys(phys_addr_t zone_limit)

View File

@ -56,46 +56,30 @@ static void cmdline_add_initrd(struct kimage *image, unsigned long *cmdline_tmpl
}
#ifdef CONFIG_CRASH_DUMP
static int prepare_elf_headers(void **addr, unsigned long *sz)
unsigned int arch_get_system_nr_ranges(void)
{
int ret, nr_ranges;
uint64_t i;
int nr_ranges = 2; /* for exclusion of crashkernel region */
phys_addr_t start, end;
struct crash_mem *cmem;
uint64_t i;
nr_ranges = 2; /* for exclusion of crashkernel region */
for_each_mem_range(i, &start, &end)
nr_ranges++;
cmem = kmalloc_flex(*cmem, ranges, nr_ranges);
if (!cmem)
return -ENOMEM;
return nr_ranges;
}
int arch_crash_populate_cmem(struct crash_mem *cmem)
{
phys_addr_t start, end;
uint64_t i;
cmem->max_nr_ranges = nr_ranges;
cmem->nr_ranges = 0;
for_each_mem_range(i, &start, &end) {
cmem->ranges[cmem->nr_ranges].start = start;
cmem->ranges[cmem->nr_ranges].end = end - 1;
cmem->nr_ranges++;
}
/* Exclude crashkernel region */
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
if (ret < 0)
goto out;
if (crashk_low_res.end) {
ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
if (ret < 0)
goto out;
}
ret = crash_prepare_elf64_headers(cmem, true, addr, sz);
out:
kfree(cmem);
return ret;
return 0;
}
/*
@ -163,7 +147,7 @@ int load_other_segments(struct kimage *image,
void *headers;
unsigned long headers_sz;
ret = prepare_elf_headers(&headers, &headers_sz);
ret = crash_prepare_headers(true, &headers, &headers_sz, NULL);
if (ret < 0) {
pr_err("Preparing elf core header failed\n");
goto out_err;

View File

@ -7,7 +7,6 @@
void sort_memory_ranges(struct crash_mem *mrngs, bool merge);
struct crash_mem *realloc_mem_ranges(struct crash_mem **mem_ranges);
int add_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size);
int remove_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size);
int get_exclude_memory_ranges(struct crash_mem **mem_ranges);
int get_reserved_memory_ranges(struct crash_mem **mem_ranges);
int get_crash_memory_ranges(struct crash_mem **mem_ranges);

View File

@ -493,7 +493,7 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify *
struct crash_mem *cmem = NULL;
struct kexec_segment *ksegment;
void *ptr, *mem, *elfbuf = NULL;
unsigned long elfsz, memsz, base_addr, size;
unsigned long elfsz, memsz, base_addr, size, end;
ksegment = &image->segment[image->elfcorehdr_index];
mem = (void *) ksegment->mem;
@ -512,7 +512,8 @@ static void update_crash_elfcorehdr(struct kimage *image, struct memory_notify *
if (image->hp_action == KEXEC_CRASH_HP_REMOVE_MEMORY) {
base_addr = PFN_PHYS(mn->start_pfn);
size = mn->nr_pages * PAGE_SIZE;
ret = remove_mem_range(&cmem, base_addr, size);
end = base_addr + size - 1;
ret = arch_crash_exclude_mem_range(&cmem, base_addr, end);
if (ret) {
pr_err("Failed to remove hot-unplugged memory from crash memory ranges\n");
goto out;

View File

@ -553,9 +553,9 @@ int get_usable_memory_ranges(struct crash_mem **mem_ranges)
#endif /* CONFIG_KEXEC_FILE */
#ifdef CONFIG_CRASH_DUMP
static int crash_exclude_mem_range_guarded(struct crash_mem **mem_ranges,
unsigned long long mstart,
unsigned long long mend)
int arch_crash_exclude_mem_range(struct crash_mem **mem_ranges,
unsigned long long mstart,
unsigned long long mend)
{
struct crash_mem *tmem = *mem_ranges;
@ -604,18 +604,10 @@ int get_crash_memory_ranges(struct crash_mem **mem_ranges)
sort_memory_ranges(*mem_ranges, true);
}
/* Exclude crashkernel region */
ret = crash_exclude_mem_range_guarded(mem_ranges, crashk_res.start, crashk_res.end);
ret = crash_exclude_core_ranges(mem_ranges);
if (ret)
goto out;
for (i = 0; i < crashk_cma_cnt; ++i) {
ret = crash_exclude_mem_range_guarded(mem_ranges, crashk_cma_ranges[i].start,
crashk_cma_ranges[i].end);
if (ret)
goto out;
}
/*
* FIXME: For now, stay in parity with kexec-tools but if RTAS/OPAL
* regions are exported to save their context at the time of
@ -641,89 +633,4 @@ int get_crash_memory_ranges(struct crash_mem **mem_ranges)
pr_err("Failed to setup crash memory ranges\n");
return ret;
}
/**
* remove_mem_range - Removes the given memory range from the range list.
* @mem_ranges: Range list to remove the memory range to.
* @base: Base address of the range to remove.
* @size: Size of the memory range to remove.
*
* (Re)allocates memory, if needed.
*
* Returns 0 on success, negative errno on error.
*/
int remove_mem_range(struct crash_mem **mem_ranges, u64 base, u64 size)
{
u64 end;
int ret = 0;
unsigned int i;
u64 mstart, mend;
struct crash_mem *mem_rngs = *mem_ranges;
if (!size)
return 0;
/*
* Memory range are stored as start and end address, use
* the same format to do remove operation.
*/
end = base + size - 1;
for (i = 0; i < mem_rngs->nr_ranges; i++) {
mstart = mem_rngs->ranges[i].start;
mend = mem_rngs->ranges[i].end;
/*
* Memory range to remove is not part of this range entry
* in the memory range list
*/
if (!(base >= mstart && end <= mend))
continue;
/*
* Memory range to remove is equivalent to this entry in the
* memory range list. Remove the range entry from the list.
*/
if (base == mstart && end == mend) {
for (; i < mem_rngs->nr_ranges - 1; i++) {
mem_rngs->ranges[i].start = mem_rngs->ranges[i+1].start;
mem_rngs->ranges[i].end = mem_rngs->ranges[i+1].end;
}
mem_rngs->nr_ranges--;
goto out;
}
/*
* Start address of the memory range to remove and the
* current memory range entry in the list is same. Just
* move the start address of the current memory range
* entry in the list to end + 1.
*/
else if (base == mstart) {
mem_rngs->ranges[i].start = end + 1;
goto out;
}
/*
* End address of the memory range to remove and the
* current memory range entry in the list is same.
* Just move the end address of the current memory
* range entry in the list to base - 1.
*/
else if (end == mend) {
mem_rngs->ranges[i].end = base - 1;
goto out;
}
/*
* Memory range to remove is not at the edge of current
* memory range entry. Split the current memory entry into
* two half.
*/
else {
size = mem_rngs->ranges[i].end - end + 1;
mem_rngs->ranges[i].end = base - 1;
ret = add_mem_range(mem_ranges, end + 1, size);
}
}
out:
return ret;
}
#endif /* CONFIG_CRASH_DUMP */

View File

@ -45,6 +45,15 @@ static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
return 0;
}
unsigned int arch_get_system_nr_ranges(void)
{
unsigned int nr_ranges = 2 + crashk_cma_cnt; /* For exclusion of crashkernel region */
walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
return nr_ranges;
}
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
{
struct crash_mem *cmem = arg;
@ -56,33 +65,9 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
return 0;
}
static int prepare_elf_headers(void **addr, unsigned long *sz)
int arch_crash_populate_cmem(struct crash_mem *cmem)
{
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_flex(*cmem, ranges, nr_ranges);
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;
return walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
}
static char *setup_kdump_cmdline(struct kimage *image, char *cmdline,
@ -274,7 +259,7 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start,
if (image->type == KEXEC_TYPE_CRASH) {
void *headers;
unsigned long headers_sz;
ret = prepare_elf_headers(&headers, &headers_sz);
ret = crash_prepare_headers(true, &headers, &headers_sz, NULL);
if (ret) {
pr_err("Preparing elf core header failed\n");
goto out;

View File

@ -1328,7 +1328,7 @@ static inline void setup_vm_final(void)
*/
static void __init arch_reserve_crashkernel(void)
{
unsigned long long low_size = 0;
unsigned long long low_size = 0, cma_size = 0;
unsigned long long crash_base, crash_size;
bool high = false;
int ret;
@ -1338,11 +1338,12 @@ static void __init arch_reserve_crashkernel(void)
ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
&crash_size, &crash_base,
&low_size, NULL, &high);
&low_size, &cma_size, &high);
if (ret)
return;
reserve_crashkernel_generic(crash_size, crash_base, low_size, high);
reserve_crashkernel_cma(cma_size);
}
void __init paging_init(void)

View File

@ -155,16 +155,8 @@ static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
return 0;
}
/* Gather all the required information to prepare elf headers for ram regions */
static struct crash_mem *fill_up_crash_elf_data(void)
unsigned int arch_get_system_nr_ranges(void)
{
unsigned int nr_ranges = 0;
struct crash_mem *cmem;
walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
if (!nr_ranges)
return NULL;
/*
* Exclusion of crash region, crashk_low_res and/or crashk_cma_ranges
* may cause range splits. So add extra slots here.
@ -179,49 +171,16 @@ static struct crash_mem *fill_up_crash_elf_data(void)
* But in order to lest the low 1M could be changed in the future,
* (e.g. [start, 1M]), add a extra slot.
*/
nr_ranges += 3 + crashk_cma_cnt;
cmem = vzalloc(struct_size(cmem, ranges, nr_ranges));
if (!cmem)
return NULL;
unsigned int nr_ranges = 3 + crashk_cma_cnt;
cmem->max_nr_ranges = nr_ranges;
return cmem;
walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
return nr_ranges;
}
/*
* Look for any unwanted ranges between mstart, mend and remove them. This
* might lead to split and split ranges are put in cmem->ranges[] array
*/
static int elf_header_exclude_ranges(struct crash_mem *cmem)
int arch_crash_exclude_ranges(struct crash_mem *cmem)
{
int ret = 0;
int i;
/* Exclude the low 1M because it is always reserved */
ret = crash_exclude_mem_range(cmem, 0, SZ_1M - 1);
if (ret)
return ret;
/* Exclude crashkernel region */
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
if (ret)
return ret;
if (crashk_low_res.end)
ret = crash_exclude_mem_range(cmem, crashk_low_res.start,
crashk_low_res.end);
if (ret)
return ret;
for (i = 0; i < crashk_cma_cnt; ++i) {
ret = crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start,
crashk_cma_ranges[i].end);
if (ret)
return ret;
}
return 0;
return crash_exclude_mem_range(cmem, 0, SZ_1M - 1);
}
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
@ -235,35 +194,9 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
return 0;
}
/* Prepare elf headers. Return addr and size */
static int prepare_elf_headers(void **addr, unsigned long *sz,
unsigned long *nr_mem_ranges)
int arch_crash_populate_cmem(struct crash_mem *cmem)
{
struct crash_mem *cmem;
int ret;
cmem = fill_up_crash_elf_data();
if (!cmem)
return -ENOMEM;
ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
if (ret)
goto out;
/* Exclude unwanted mem ranges */
ret = elf_header_exclude_ranges(cmem);
if (ret)
goto out;
/* Return the computed number of memory ranges, for hotplug usage */
*nr_mem_ranges = cmem->nr_ranges;
/* By default prepare 64bit headers */
ret = crash_prepare_elf64_headers(cmem, IS_ENABLED(CONFIG_X86_64), addr, sz);
out:
vfree(cmem);
return ret;
return walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
}
#endif
@ -421,7 +354,8 @@ int crash_load_segments(struct kimage *image)
.buf_max = ULONG_MAX, .top_down = false };
/* Prepare elf headers and add a segment */
ret = prepare_elf_headers(&kbuf.buffer, &kbuf.bufsz, &pnum);
ret = crash_prepare_headers(IS_ENABLED(CONFIG_X86_64), &kbuf.buffer,
&kbuf.bufsz, &pnum);
if (ret)
return ret;
@ -514,7 +448,6 @@ unsigned int arch_crash_get_elfcorehdr_size(void)
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
{
void *elfbuf = NULL, *old_elfcorehdr;
unsigned long nr_mem_ranges;
unsigned long mem, memsz;
unsigned long elfsz = 0;
@ -532,7 +465,7 @@ void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
* Create the new elfcorehdr reflecting the changes to CPU and/or
* memory resources.
*/
if (prepare_elf_headers(&elfbuf, &elfsz, &nr_mem_ranges)) {
if (crash_prepare_headers(IS_ENABLED(CONFIG_X86_64), &elfbuf, &elfsz, NULL)) {
pr_err("unable to create new elfcorehdr");
goto out;
}

View File

@ -878,11 +878,12 @@ static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND;
/*
* The main usage of linux,usable-memory-range is for crash dump kernel.
* Originally, the number of usable-memory regions is one. Now there may
* be two regions, low region and high region.
* To make compatibility with existing user-space and older kdump, the low
* region is always the last range of linux,usable-memory-range if exist.
* be 2 + CRASHK_CMA_RANGES_MAX regions, low region, high region and cma
* regions. To make compatibility with existing user-space and older kdump,
* the high and low region are always the first two ranges of
* linux,usable-memory-range if exist.
*/
#define MAX_USABLE_RANGES 2
#define MAX_USABLE_RANGES (2 + CRASHK_CMA_RANGES_MAX)
/**
* early_init_dt_check_for_usable_mem_range - Decode usable memory range

View File

@ -458,6 +458,15 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
if (ret)
goto out;
}
for (int i = 0; i < crashk_cma_cnt; i++) {
ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
"linux,usable-memory-range",
crashk_cma_ranges[i].start,
crashk_cma_ranges[i].end - crashk_cma_ranges[i].start + 1);
if (ret)
goto out;
}
#endif
}

View File

@ -59,6 +59,9 @@ extern int crash_exclude_mem_range(struct crash_mem *mem,
unsigned long long mend);
extern int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
void **addr, unsigned long *sz);
extern int crash_prepare_headers(int need_kernel_map, void **addr,
unsigned long *sz, unsigned long *nr_mem_ranges);
extern int crash_exclude_core_ranges(struct crash_mem **cmem);
struct kimage;
struct kexec_segment;
@ -76,6 +79,12 @@ int kexec_should_crash(struct task_struct *p);
int kexec_crash_loaded(void);
void crash_save_cpu(struct pt_regs *regs, int cpu);
extern int kimage_crash_copy_vmcoreinfo(struct kimage *image);
extern unsigned int arch_get_system_nr_ranges(void);
extern int arch_crash_populate_cmem(struct crash_mem *cmem);
extern int arch_crash_exclude_ranges(struct crash_mem *cmem);
extern int arch_crash_exclude_mem_range(struct crash_mem **mem,
unsigned long long mstart,
unsigned long long mend);
#else /* !CONFIG_CRASH_DUMP*/
struct pt_regs;

View File

@ -14,9 +14,11 @@
extern struct resource crashk_res;
extern struct resource crashk_low_res;
extern struct range crashk_cma_ranges[];
#define CRASHK_CMA_RANGES_MAX 4
#if defined(CONFIG_CMA) && defined(CONFIG_ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION)
#define CRASHKERNEL_CMA
#define CRASHKERNEL_CMA_RANGES_MAX 4
#define CRASHKERNEL_CMA_RANGES_MAX (CRASHK_CMA_RANGES_MAX)
extern int crashk_cma_cnt;
#else
#define crashk_cma_cnt 0

View File

@ -13,7 +13,7 @@
#define IND_SOURCE (1 << IND_SOURCE_BIT)
#define IND_FLAGS (IND_DESTINATION | IND_INDIRECTION | IND_DONE | IND_SOURCE)
#if !defined(__ASSEMBLY__)
#if !defined(__ASSEMBLER__)
#include <linux/vmcore_info.h>
#include <linux/crash_reserve.h>

View File

@ -168,9 +168,6 @@ static inline resource_size_t crash_resource_size(const struct resource *res)
return !res->end ? 0 : resource_size(res);
}
int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
void **addr, unsigned long *sz)
{
@ -272,6 +269,92 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
return 0;
}
static struct crash_mem *alloc_cmem(unsigned int nr_ranges)
{
struct crash_mem *cmem;
cmem = kvzalloc_flex(*cmem, ranges, nr_ranges);
if (!cmem)
return NULL;
cmem->max_nr_ranges = nr_ranges;
return cmem;
}
unsigned int __weak arch_get_system_nr_ranges(void) { return 0; }
int __weak arch_crash_populate_cmem(struct crash_mem *cmem) { return -1; }
int __weak arch_crash_exclude_ranges(struct crash_mem *cmem) { return 0; }
int __weak arch_crash_exclude_mem_range(struct crash_mem **mem,
unsigned long long mstart,
unsigned long long mend)
{
return crash_exclude_mem_range(*mem, mstart, mend);
}
int crash_exclude_core_ranges(struct crash_mem **cmem)
{
int ret, i;
/* Exclude crashkernel region */
ret = arch_crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
if (ret)
return ret;
if (crashk_low_res.end) {
ret = arch_crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
if (ret)
return ret;
}
for (i = 0; i < crashk_cma_cnt; ++i) {
ret = arch_crash_exclude_mem_range(cmem, crashk_cma_ranges[i].start,
crashk_cma_ranges[i].end);
if (ret)
return ret;
}
return 0;
}
int crash_prepare_headers(int need_kernel_map, void **addr, unsigned long *sz,
unsigned long *nr_mem_ranges)
{
unsigned int max_nr_ranges;
struct crash_mem *cmem;
int ret;
max_nr_ranges = arch_get_system_nr_ranges();
if (!max_nr_ranges)
return -ENOMEM;
cmem = alloc_cmem(max_nr_ranges);
if (!cmem)
return -ENOMEM;
ret = arch_crash_populate_cmem(cmem);
if (ret)
goto out;
ret = crash_exclude_core_ranges(&cmem);
if (ret)
goto out;
ret = arch_crash_exclude_ranges(cmem);
if (ret)
goto out;
/* Return the computed number of memory ranges, for hotplug usage */
if (nr_mem_ranges)
*nr_mem_ranges = cmem->nr_ranges;
ret = crash_prepare_elf64_headers(cmem, need_kernel_map, addr, sz);
out:
kvfree(cmem);
return ret;
}
/**
* crash_exclude_mem_range - exclude a mem range for existing ranges
* @mem: mem->range contains an array of ranges sorted in ascending order

View File

@ -81,7 +81,6 @@ static int add_key_to_keyring(struct dm_crypt_key *dm_key,
kexec_dprintk("Error when adding key");
}
key_ref_put(keyring_ref);
return r;
}
@ -104,6 +103,7 @@ static int restore_dm_crypt_keys_to_thread_keyring(void)
struct dm_crypt_key *key;
size_t keys_header_size;
key_ref_t keyring_ref;
int ret = 0;
u64 addr;
/* find the target keyring (which must be writable) */
@ -118,7 +118,8 @@ static int restore_dm_crypt_keys_to_thread_keyring(void)
dm_crypt_keys_read((char *)&key_count, sizeof(key_count), &addr);
if (key_count > KEY_NUM_MAX) {
kexec_dprintk("Failed to read the number of dm-crypt keys\n");
return -1;
ret = -1;
goto out;
}
kexec_dprintk("There are %u keys\n", key_count);
@ -126,8 +127,10 @@ static int restore_dm_crypt_keys_to_thread_keyring(void)
keys_header_size = get_keys_header_size(key_count);
keys_header = kzalloc(keys_header_size, GFP_KERNEL);
if (!keys_header)
return -ENOMEM;
if (!keys_header) {
ret = -ENOMEM;
goto out;
}
dm_crypt_keys_read((char *)keys_header, keys_header_size, &addr);
@ -137,7 +140,9 @@ static int restore_dm_crypt_keys_to_thread_keyring(void)
add_key_to_keyring(key, keyring_ref);
}
return 0;
out:
key_ref_put(keyring_ref);
return ret;
}
static int read_key_from_user_keyring(struct dm_crypt_key *dm_key)

View File

@ -27,6 +27,7 @@
#include <linux/syscalls.h>
#include <linux/vmalloc.h>
#include <linux/dma-map-ops.h>
#include <linux/kexec_handover.h>
#include "kexec_internal.h"
#ifdef CONFIG_KEXEC_SIG
@ -798,6 +799,16 @@ int kexec_add_buffer(struct kexec_buf *kbuf)
return 0;
}
static bool kexec_only_cma_segments(struct kimage *image)
{
for (int i = 0; i < image->nr_segments; i++) {
if (!image->segment_cma[i])
return false;
}
return true;
}
/* Calculate and store the digest of segments */
static int kexec_calculate_store_digests(struct kimage *image)
{
@ -822,6 +833,21 @@ static int kexec_calculate_store_digests(struct kimage *image)
sha256_init(&sctx);
/*
* If KHO is enabled, the destinations are located in KHO scratch.
* KHO scratch can only contain early boot allocations and movable
* allocations. That means there is no risk of memory corruption by
* uncancelled DMA.
*
* If all segments were loaded into contiguous memory, there will be no
* relocations at all, so also no risk of corruption.
*/
if (image->type != KEXEC_TYPE_CRASH &&
(kho_is_enabled() || kexec_only_cma_segments(image))) {
pr_debug("disabling checksum verification in purgatory\n");
goto skip_checksum;
}
for (j = i = 0; i < image->nr_segments; i++) {
struct kexec_segment *ksegment;
@ -867,6 +893,7 @@ static int kexec_calculate_store_digests(struct kimage *image)
j++;
}
skip_checksum:
sha256_final(&sctx, digest);
ret = kexec_purgatory_get_set_symbol(image, "purgatory_sha_regions",