mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
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:
commit
ba24659b1d
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user