2019-05-19 20:08:55 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2005-06-26 05:57:56 +08:00
|
|
|
/*
|
2007-10-20 09:24:20 +08:00
|
|
|
* Architecture specific (i386/x86_64) functions for kexec based crash dumps.
|
2005-06-26 05:57:56 +08:00
|
|
|
*
|
|
|
|
* Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
|
|
|
|
*
|
|
|
|
* Copyright (C) IBM Corporation, 2004. All rights reserved.
|
2014-08-09 05:26:09 +08:00
|
|
|
* Copyright (C) Red Hat Inc., 2014. All rights reserved.
|
|
|
|
* Authors:
|
|
|
|
* Vivek Goyal <vgoyal@redhat.com>
|
2005-06-26 05:57:56 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-08-09 05:26:09 +08:00
|
|
|
#define pr_fmt(fmt) "kexec: " fmt
|
|
|
|
|
2005-06-26 05:57:56 +08:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/reboot.h>
|
|
|
|
#include <linux/kexec.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/elf.h>
|
|
|
|
#include <linux/elfcore.h>
|
2016-07-14 08:18:56 +08:00
|
|
|
#include <linux/export.h>
|
2014-08-09 05:26:09 +08:00
|
|
|
#include <linux/slab.h>
|
2015-06-02 17:01:38 +08:00
|
|
|
#include <linux/vmalloc.h>
|
2024-06-11 20:26:44 +08:00
|
|
|
#include <linux/memblock.h>
|
2005-06-26 05:57:56 +08:00
|
|
|
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/hardirq.h>
|
|
|
|
#include <asm/nmi.h>
|
|
|
|
#include <asm/hw_irq.h>
|
2005-11-15 16:09:04 +08:00
|
|
|
#include <asm/apic.h>
|
2017-01-27 18:59:46 +08:00
|
|
|
#include <asm/e820/types.h>
|
2014-10-27 16:12:04 +08:00
|
|
|
#include <asm/io_apic.h>
|
2007-12-04 00:17:10 +08:00
|
|
|
#include <asm/hpet.h>
|
2007-05-08 15:27:03 +08:00
|
|
|
#include <linux/kdebug.h>
|
2009-01-08 00:05:48 +08:00
|
|
|
#include <asm/cpu.h>
|
2008-03-18 03:08:38 +08:00
|
|
|
#include <asm/reboot.h>
|
2015-11-04 13:22:33 +08:00
|
|
|
#include <asm/intel_pt.h>
|
2018-11-22 10:04:09 +08:00
|
|
|
#include <asm/crash.h>
|
2024-06-11 20:26:44 +08:00
|
|
|
#include <asm/cmdline.h>
|
2008-11-12 21:34:40 +08:00
|
|
|
|
2014-08-09 05:26:09 +08:00
|
|
|
/* Used while preparing memory map entries for second kernel */
|
|
|
|
struct crash_memmap_data {
|
|
|
|
struct boot_params *params;
|
|
|
|
/* Type of memory */
|
|
|
|
unsigned int type;
|
|
|
|
};
|
|
|
|
|
2012-12-06 23:40:47 +08:00
|
|
|
/*
|
|
|
|
* This is used to VMCLEAR all VMCSs loaded on the
|
|
|
|
* processor. And when loading kvm_intel module, the
|
|
|
|
* callback function pointer will be assigned.
|
|
|
|
*
|
|
|
|
* protected by rcu.
|
|
|
|
*/
|
2012-12-11 17:11:34 +08:00
|
|
|
crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss = NULL;
|
2012-12-06 23:40:47 +08:00
|
|
|
EXPORT_SYMBOL_GPL(crash_vmclear_loaded_vmcss);
|
|
|
|
|
|
|
|
static inline void cpu_crash_vmclear_loaded_vmcss(void)
|
|
|
|
{
|
2012-12-11 17:11:34 +08:00
|
|
|
crash_vmclear_fn *do_vmclear_operation = NULL;
|
2012-12-06 23:40:47 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
do_vmclear_operation = rcu_dereference(crash_vmclear_loaded_vmcss);
|
|
|
|
if (do_vmclear_operation)
|
|
|
|
do_vmclear_operation();
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2024-06-11 20:26:44 +08:00
|
|
|
/*
|
|
|
|
* When the crashkernel option is specified, only use the low
|
|
|
|
* 1M for the real mode trampoline.
|
|
|
|
*/
|
|
|
|
void __init crash_reserve_low_1M(void)
|
|
|
|
{
|
|
|
|
if (cmdline_find_option(boot_command_line, "crashkernel", NULL, 0) < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
memblock_reserve(0, 1<<20);
|
|
|
|
pr_info("Reserving the low 1M of memory for crashkernel\n");
|
|
|
|
}
|
|
|
|
|
2008-11-12 21:34:38 +08:00
|
|
|
#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
|
|
|
|
|
2011-10-01 03:06:21 +08:00
|
|
|
static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
|
2005-06-26 05:57:58 +08:00
|
|
|
{
|
2008-11-12 21:34:37 +08:00
|
|
|
crash_save_cpu(regs, cpu);
|
|
|
|
|
2012-12-06 23:40:47 +08:00
|
|
|
/*
|
|
|
|
* VMCLEAR VMCSs loaded on all cpus if needed.
|
|
|
|
*/
|
|
|
|
cpu_crash_vmclear_loaded_vmcss();
|
|
|
|
|
2015-11-04 13:22:33 +08:00
|
|
|
/*
|
|
|
|
* Disable Intel PT to stop its logging
|
|
|
|
*/
|
|
|
|
cpu_emergency_stop_pt();
|
|
|
|
|
2008-11-12 21:34:37 +08:00
|
|
|
disable_local_APIC();
|
|
|
|
}
|
|
|
|
|
2016-10-12 04:54:23 +08:00
|
|
|
void kdump_nmi_shootdown_cpus(void)
|
2008-11-12 21:34:39 +08:00
|
|
|
{
|
2008-11-12 21:34:40 +08:00
|
|
|
nmi_shootdown_cpus(kdump_nmi_callback);
|
2008-11-12 21:34:39 +08:00
|
|
|
|
2005-11-15 16:09:04 +08:00
|
|
|
disable_local_APIC();
|
2005-06-26 05:57:58 +08:00
|
|
|
}
|
2008-11-12 21:34:39 +08:00
|
|
|
|
2016-10-12 04:54:23 +08:00
|
|
|
/* Override the weak function in kernel/panic.c */
|
|
|
|
void crash_smp_send_stop(void)
|
|
|
|
{
|
|
|
|
static int cpus_stopped;
|
|
|
|
|
|
|
|
if (cpus_stopped)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (smp_ops.crash_stop_other_cpus)
|
|
|
|
smp_ops.crash_stop_other_cpus();
|
|
|
|
else
|
|
|
|
smp_send_stop();
|
|
|
|
|
|
|
|
cpus_stopped = 1;
|
|
|
|
}
|
|
|
|
|
2005-06-26 05:57:58 +08:00
|
|
|
#else
|
2016-10-12 04:54:23 +08:00
|
|
|
void crash_smp_send_stop(void)
|
2005-06-26 05:57:58 +08:00
|
|
|
{
|
|
|
|
/* There are no cpus to shootdown */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-03-18 03:08:38 +08:00
|
|
|
void native_machine_crash_shutdown(struct pt_regs *regs)
|
2005-06-26 05:57:56 +08:00
|
|
|
{
|
|
|
|
/* This function is only called after the system
|
2006-06-27 00:30:00 +08:00
|
|
|
* has panicked or is otherwise in a critical state.
|
2005-06-26 05:57:56 +08:00
|
|
|
* The minimum amount of code to allow a kexec'd kernel
|
|
|
|
* to run successfully needs to happen here.
|
|
|
|
*
|
|
|
|
* In practice this means shooting down the other cpus in
|
|
|
|
* an SMP system.
|
|
|
|
*/
|
2005-06-26 05:57:58 +08:00
|
|
|
/* The kernel is broken so disable interrupts */
|
|
|
|
local_irq_disable();
|
2005-06-26 05:58:14 +08:00
|
|
|
|
2016-10-12 04:54:23 +08:00
|
|
|
crash_smp_send_stop();
|
2008-11-18 05:03:23 +08:00
|
|
|
|
2012-12-06 23:40:47 +08:00
|
|
|
/*
|
|
|
|
* VMCLEAR VMCSs loaded on this cpu if needed.
|
|
|
|
*/
|
|
|
|
cpu_crash_vmclear_loaded_vmcss();
|
|
|
|
|
2024-06-12 13:13:20 +08:00
|
|
|
cpu_emergency_disable_virtualization();
|
2008-11-18 05:03:23 +08:00
|
|
|
|
2015-11-04 13:22:33 +08:00
|
|
|
/*
|
|
|
|
* Disable Intel PT to stop its logging
|
|
|
|
*/
|
|
|
|
cpu_emergency_stop_pt();
|
|
|
|
|
2013-08-20 15:01:07 +08:00
|
|
|
#ifdef CONFIG_X86_IO_APIC
|
|
|
|
/* Prevent crash_kexec() from deadlocking on ioapic_lock. */
|
|
|
|
ioapic_zap_locks();
|
2018-02-14 13:46:53 +08:00
|
|
|
clear_IO_APIC();
|
2007-12-04 00:17:10 +08:00
|
|
|
#endif
|
2013-10-24 09:30:12 +08:00
|
|
|
lapic_shutdown();
|
2018-02-14 13:46:53 +08:00
|
|
|
restore_boot_irq_mode();
|
2007-12-04 00:17:10 +08:00
|
|
|
#ifdef CONFIG_HPET_TIMER
|
|
|
|
hpet_disable();
|
2005-11-15 16:09:04 +08:00
|
|
|
#endif
|
2006-12-07 12:40:41 +08:00
|
|
|
crash_save_cpu(regs, safe_smp_processor_id());
|
2005-06-26 05:57:56 +08:00
|
|
|
}
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2014-08-30 06:18:46 +08:00
|
|
|
#ifdef CONFIG_KEXEC_FILE
|
2019-06-24 12:41:18 +08:00
|
|
|
|
|
|
|
static unsigned long crash_zero_bytes;
|
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
x86/kexec: Fix kexec crash in syscall kexec_file_load()
The original bug is a page fault crash that sometimes happens
on big machines when preparing ELF headers:
BUG: unable to handle kernel paging request at ffffc90613fc9000
IP: [<ffffffff8103d645>] prepare_elf64_ram_headers_callback+0x165/0x260
The bug is caused by us under-counting the number of memory ranges
and subsequently not allocating enough ELF header space for them.
The bug is typically masked on smaller systems, because the ELF header
allocation is rounded up to the next page.
This patch modifies the code in fill_up_crash_elf_data() by using
walk_system_ram_res() instead of walk_system_ram_range() to correctly
count the max number of crash memory ranges. That's because the
walk_system_ram_range() filters out small memory regions that
reside in the same page, but walk_system_ram_res() does not.
Here's how I found the bug:
After tracing prepare_elf64_headers() and prepare_elf64_ram_headers_callback(),
the code uses walk_system_ram_res() to fill-in crash memory regions information
to the program header, so it counts those small memory regions that
reside in a page area.
But, when the kernel was using walk_system_ram_range() in
fill_up_crash_elf_data() to count the number of crash memory regions,
it filters out small regions.
I printed those small memory regions, for example:
kexec: Get nr_ram ranges. vaddr=0xffff880077592258 paddr=0x77592258, sz=0xdc0
Based on the code in walk_system_ram_range(), this memory region
will be filtered out:
pfn = (0x77592258 + 0x1000 - 1) >> 12 = 0x77593
end_pfn = (0x77592258 + 0xfc0 -1 + 1) >> 12 = 0x77593
end_pfn - pfn = 0x77593 - 0x77593 = 0 <=== if (end_pfn > pfn) is FALSE
So, the max_nr_ranges that's counted by the kernel doesn't include
small memory regions - causing us to under-allocate the required space.
That causes the page fault crash that happens in a later code path
when preparing ELF headers.
This bug is not easy to reproduce on small machines that have few
CPUs, because the allocated page aligned ELF buffer has more free
space to cover those small memory regions' PT_LOAD headers.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: <stable@vger.kernel.org>
Link: http://lkml.kernel.org/r/1443531537-29436-1-git-send-email-jlee@suse.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-09-29 20:58:57 +08:00
|
|
|
unsigned int *nr_ranges = arg;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
|
|
|
(*nr_ranges)++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Gather all the required information to prepare elf headers for ram regions */
|
2018-04-14 06:35:59 +08:00
|
|
|
static struct crash_mem *fill_up_crash_elf_data(void)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
|
|
|
unsigned int nr_ranges = 0;
|
2018-04-14 06:35:59 +08:00
|
|
|
struct crash_mem *cmem;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
x86/kexec: Fix kexec crash in syscall kexec_file_load()
The original bug is a page fault crash that sometimes happens
on big machines when preparing ELF headers:
BUG: unable to handle kernel paging request at ffffc90613fc9000
IP: [<ffffffff8103d645>] prepare_elf64_ram_headers_callback+0x165/0x260
The bug is caused by us under-counting the number of memory ranges
and subsequently not allocating enough ELF header space for them.
The bug is typically masked on smaller systems, because the ELF header
allocation is rounded up to the next page.
This patch modifies the code in fill_up_crash_elf_data() by using
walk_system_ram_res() instead of walk_system_ram_range() to correctly
count the max number of crash memory ranges. That's because the
walk_system_ram_range() filters out small memory regions that
reside in the same page, but walk_system_ram_res() does not.
Here's how I found the bug:
After tracing prepare_elf64_headers() and prepare_elf64_ram_headers_callback(),
the code uses walk_system_ram_res() to fill-in crash memory regions information
to the program header, so it counts those small memory regions that
reside in a page area.
But, when the kernel was using walk_system_ram_range() in
fill_up_crash_elf_data() to count the number of crash memory regions,
it filters out small regions.
I printed those small memory regions, for example:
kexec: Get nr_ram ranges. vaddr=0xffff880077592258 paddr=0x77592258, sz=0xdc0
Based on the code in walk_system_ram_range(), this memory region
will be filtered out:
pfn = (0x77592258 + 0x1000 - 1) >> 12 = 0x77593
end_pfn = (0x77592258 + 0xfc0 -1 + 1) >> 12 = 0x77593
end_pfn - pfn = 0x77593 - 0x77593 = 0 <=== if (end_pfn > pfn) is FALSE
So, the max_nr_ranges that's counted by the kernel doesn't include
small memory regions - causing us to under-allocate the required space.
That causes the page fault crash that happens in a later code path
when preparing ELF headers.
This bug is not easy to reproduce on small machines that have few
CPUs, because the allocated page aligned ELF buffer has more free
space to cover those small memory regions' PT_LOAD headers.
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Baoquan He <bhe@redhat.com>
Cc: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Viresh Kumar <viresh.kumar@linaro.org>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: kexec@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: <stable@vger.kernel.org>
Link: http://lkml.kernel.org/r/1443531537-29436-1-git-send-email-jlee@suse.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2015-09-29 20:58:57 +08:00
|
|
|
walk_system_ram_res(0, -1, &nr_ranges,
|
2014-08-09 05:26:09 +08:00
|
|
|
get_nr_ram_ranges_callback);
|
2018-04-14 06:35:59 +08:00
|
|
|
if (!nr_ranges)
|
|
|
|
return NULL;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2018-04-14 06:35:59 +08:00
|
|
|
/*
|
|
|
|
* Exclusion of crash region and/or crashk_low_res may cause
|
|
|
|
* another range split. So add extra two slots here.
|
|
|
|
*/
|
|
|
|
nr_ranges += 2;
|
2019-04-04 02:42:30 +08:00
|
|
|
cmem = vzalloc(struct_size(cmem, ranges, nr_ranges));
|
2018-04-14 06:35:59 +08:00
|
|
|
if (!cmem)
|
|
|
|
return NULL;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2018-04-14 06:35:59 +08:00
|
|
|
cmem->max_nr_ranges = nr_ranges;
|
|
|
|
cmem->nr_ranges = 0;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2018-04-14 06:35:59 +08:00
|
|
|
return cmem;
|
2014-08-09 05:26:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for any unwanted ranges between mstart, mend and remove them. This
|
2018-04-14 06:35:59 +08:00
|
|
|
* might lead to split and split ranges are put in cmem->ranges[] array
|
2014-08-09 05:26:09 +08:00
|
|
|
*/
|
2018-04-14 06:35:59 +08:00
|
|
|
static int elf_header_exclude_ranges(struct crash_mem *cmem)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Exclude crashkernel region */
|
2018-04-14 06:36:06 +08:00
|
|
|
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
|
2014-08-09 05:26:09 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2014-10-14 06:53:42 +08:00
|
|
|
if (crashk_low_res.end) {
|
2018-04-14 06:36:06 +08:00
|
|
|
ret = crash_exclude_mem_range(cmem, crashk_low_res.start,
|
|
|
|
crashk_low_res.end);
|
2014-10-14 06:53:42 +08:00
|
|
|
}
|
2014-08-09 05:26:09 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
2018-04-14 06:35:59 +08:00
|
|
|
struct crash_mem *cmem = arg;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2018-04-14 06:35:53 +08:00
|
|
|
cmem->ranges[cmem->nr_ranges].start = res->start;
|
|
|
|
cmem->ranges[cmem->nr_ranges].end = res->end;
|
|
|
|
cmem->nr_ranges++;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2018-04-14 06:35:53 +08:00
|
|
|
return 0;
|
2014-08-09 05:26:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare elf headers. Return addr and size */
|
|
|
|
static int prepare_elf_headers(struct kimage *image, void **addr,
|
|
|
|
unsigned long *sz)
|
|
|
|
{
|
2018-04-14 06:35:59 +08:00
|
|
|
struct crash_mem *cmem;
|
2018-04-14 06:35:53 +08:00
|
|
|
Elf64_Ehdr *ehdr;
|
|
|
|
Elf64_Phdr *phdr;
|
|
|
|
int ret, i;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2018-04-14 06:35:59 +08:00
|
|
|
cmem = fill_up_crash_elf_data();
|
|
|
|
if (!cmem)
|
2014-08-09 05:26:09 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-04-14 06:35:59 +08:00
|
|
|
ret = walk_system_ram_res(0, -1, cmem,
|
2018-04-14 06:35:53 +08:00
|
|
|
prepare_elf64_ram_headers_callback);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Exclude unwanted mem ranges */
|
2018-04-14 06:35:59 +08:00
|
|
|
ret = elf_header_exclude_ranges(cmem);
|
2018-04-14 06:35:53 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2014-08-09 05:26:09 +08:00
|
|
|
/* By default prepare 64bit headers */
|
2018-04-14 06:36:06 +08:00
|
|
|
ret = crash_prepare_elf64_headers(cmem,
|
|
|
|
IS_ENABLED(CONFIG_X86_64), addr, sz);
|
2018-04-14 06:35:53 +08:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a range matches backup region, adjust offset to backup
|
|
|
|
* segment.
|
|
|
|
*/
|
|
|
|
ehdr = (Elf64_Ehdr *)*addr;
|
|
|
|
phdr = (Elf64_Phdr *)(ehdr + 1);
|
|
|
|
for (i = 0; i < ehdr->e_phnum; phdr++, i++)
|
|
|
|
if (phdr->p_type == PT_LOAD &&
|
|
|
|
phdr->p_paddr == image->arch.backup_src_start &&
|
|
|
|
phdr->p_memsz == image->arch.backup_src_sz) {
|
|
|
|
phdr->p_offset = image->arch.backup_load_addr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
2018-04-14 06:35:59 +08:00
|
|
|
vfree(cmem);
|
2014-08-09 05:26:09 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-27 19:54:38 +08:00
|
|
|
static int add_e820_entry(struct boot_params *params, struct e820_entry *entry)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
|
|
|
unsigned int nr_e820_entries;
|
|
|
|
|
|
|
|
nr_e820_entries = params->e820_entries;
|
2017-01-29 00:29:08 +08:00
|
|
|
if (nr_e820_entries >= E820_MAX_ENTRIES_ZEROPAGE)
|
2014-08-09 05:26:09 +08:00
|
|
|
return 1;
|
|
|
|
|
2017-01-27 20:54:38 +08:00
|
|
|
memcpy(¶ms->e820_table[nr_e820_entries], entry,
|
2017-01-27 19:54:38 +08:00
|
|
|
sizeof(struct e820_entry));
|
2014-08-09 05:26:09 +08:00
|
|
|
params->e820_entries++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
static int memmap_entry_callback(struct resource *res, void *arg)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
|
|
|
struct crash_memmap_data *cmd = arg;
|
|
|
|
struct boot_params *params = cmd->params;
|
2017-01-27 19:54:38 +08:00
|
|
|
struct e820_entry ei;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
ei.addr = res->start;
|
2017-11-08 03:18:01 +08:00
|
|
|
ei.size = resource_size(res);
|
2014-08-09 05:26:09 +08:00
|
|
|
ei.type = cmd->type;
|
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int memmap_exclude_ranges(struct kimage *image, struct crash_mem *cmem,
|
|
|
|
unsigned long long mstart,
|
|
|
|
unsigned long long mend)
|
|
|
|
{
|
|
|
|
unsigned long start, end;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
cmem->ranges[0].start = mstart;
|
|
|
|
cmem->ranges[0].end = mend;
|
|
|
|
cmem->nr_ranges = 1;
|
|
|
|
|
|
|
|
/* Exclude Backup region */
|
|
|
|
start = image->arch.backup_load_addr;
|
|
|
|
end = start + image->arch.backup_src_sz - 1;
|
2018-04-14 06:36:06 +08:00
|
|
|
ret = crash_exclude_mem_range(cmem, start, end);
|
2014-08-09 05:26:09 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Exclude elf header region */
|
|
|
|
start = image->arch.elf_load_addr;
|
|
|
|
end = start + image->arch.elf_headers_sz - 1;
|
2018-04-14 06:36:06 +08:00
|
|
|
return crash_exclude_mem_range(cmem, start, end);
|
2014-08-09 05:26:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare memory map for crash dump kernel */
|
|
|
|
int crash_setup_memmap_entries(struct kimage *image, struct boot_params *params)
|
|
|
|
{
|
|
|
|
int i, ret = 0;
|
|
|
|
unsigned long flags;
|
2017-01-27 19:54:38 +08:00
|
|
|
struct e820_entry ei;
|
2014-08-09 05:26:09 +08:00
|
|
|
struct crash_memmap_data cmd;
|
|
|
|
struct crash_mem *cmem;
|
|
|
|
|
2024-06-11 20:26:44 +08:00
|
|
|
cmem = vzalloc(struct_size(cmem, ranges, 1));
|
2014-08-09 05:26:09 +08:00
|
|
|
if (!cmem)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct crash_memmap_data));
|
|
|
|
cmd.params = params;
|
|
|
|
|
|
|
|
/* Add first 640K segment */
|
|
|
|
ei.addr = image->arch.backup_src_start;
|
|
|
|
ei.size = image->arch.backup_src_sz;
|
2017-01-29 00:09:33 +08:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-09 05:26:09 +08:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
|
|
|
|
/* Add ACPI tables */
|
2017-01-29 00:09:33 +08:00
|
|
|
cmd.type = E820_TYPE_ACPI;
|
2014-08-09 05:26:09 +08:00
|
|
|
flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
2016-01-27 04:57:30 +08:00
|
|
|
walk_iomem_res_desc(IORES_DESC_ACPI_TABLES, flags, 0, -1, &cmd,
|
2014-08-09 05:26:09 +08:00
|
|
|
memmap_entry_callback);
|
|
|
|
|
|
|
|
/* Add ACPI Non-volatile Storage */
|
2017-01-29 00:09:33 +08:00
|
|
|
cmd.type = E820_TYPE_NVS;
|
2016-01-27 04:57:30 +08:00
|
|
|
walk_iomem_res_desc(IORES_DESC_ACPI_NV_STORAGE, flags, 0, -1, &cmd,
|
2014-08-09 05:26:09 +08:00
|
|
|
memmap_entry_callback);
|
|
|
|
|
x86/crash: Add e820 reserved ranges to kdump kernel's e820 table
At present, when using the kexec_file_load() syscall to load the kernel
image and initramfs, for example:
kexec -s -p xxx
the kernel does not pass the e820 reserved ranges to the second kernel,
which might cause two problems:
1. MMCONFIG: A device in PCI segment 1 cannot be discovered by the
kernel PCI probing without all the e820 I/O reservations being present
in the e820 table. Which is the case currently, because the kdump kernel
does not have those reservations because the kexec command does not pass
the I/O reservation via the "memmap=xxx" command line option.
Further details courtesy of Bjorn Helgaas¹: I think you should regard
correct MCFG/ECAM usage in the kdump kernel as a requirement. MMCONFIG
(aka ECAM) space is described in the ACPI MCFG table. If you don't have
ECAM:
(a) PCI devices won't work at all on non-x86 systems that use only
ECAM for config access,
(b) you won't be able to access devices on non-0 segments (granted,
there aren't very many of these yet, but there will be more in the
future), and
(c) you won't be able to access extended config space (addresses
0x100-0xfff), which means none of the Extended Capabilities will be
available (AER, ACS, ATS, etc).
2. The second issue is that the SME kdump kernel doesn't work without
the e820 reserved ranges. When SME is active in the kdump kernel, those
reserved regions are still decrypted, but because those reserved ranges
are not present at all in kdump kernel's e820 table, they are accessed
as encrypted. Which is obviously wrong.
[1]: https://lkml.kernel.org/r/CABhMZUUscS3jUZUSM5Y6EYJK6weo7Mjj5-EAKGvbw0qEe%2B38zw@mail.gmail.com
[ bp: Heavily massage commit message. ]
Suggested-by: Dave Young <dyoung@redhat.com>
Signed-off-by: Lianbo Jiang <lijiang@redhat.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Baoquan He <bhe@redhat.com>
Cc: Bjorn Helgaas <bjorn.helgaas@gmail.com>
Cc: dave.hansen@linux.intel.com
Cc: Dave Young <dyoung@redhat.com>
Cc: "Gustavo A. R. Silva" <gustavo@embeddedor.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: kexec@lists.infradead.org
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: x86-ml <x86@kernel.org>
Cc: Yi Wang <wang.yi59@zte.com.cn>
Link: https://lkml.kernel.org/r/20190423013007.17838-4-lijiang@redhat.com
2019-04-23 09:30:07 +08:00
|
|
|
/* Add e820 reserved ranges */
|
|
|
|
cmd.type = E820_TYPE_RESERVED;
|
|
|
|
flags = IORESOURCE_MEM;
|
|
|
|
walk_iomem_res_desc(IORES_DESC_RESERVED, flags, 0, -1, &cmd,
|
|
|
|
memmap_entry_callback);
|
|
|
|
|
2014-08-09 05:26:09 +08:00
|
|
|
/* Add crashk_low_res region */
|
|
|
|
if (crashk_low_res.end) {
|
|
|
|
ei.addr = crashk_low_res.start;
|
|
|
|
ei.size = crashk_low_res.end - crashk_low_res.start + 1;
|
2017-01-29 00:09:33 +08:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-09 05:26:09 +08:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exclude some ranges from crashk_res and add rest to memmap */
|
|
|
|
ret = memmap_exclude_ranges(image, cmem, crashk_res.start,
|
|
|
|
crashk_res.end);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (i = 0; i < cmem->nr_ranges; i++) {
|
|
|
|
ei.size = cmem->ranges[i].end - cmem->ranges[i].start + 1;
|
|
|
|
|
|
|
|
/* If entry is less than a page, skip it */
|
|
|
|
if (ei.size < PAGE_SIZE)
|
|
|
|
continue;
|
|
|
|
ei.addr = cmem->ranges[i].start;
|
2017-01-29 00:09:33 +08:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-09 05:26:09 +08:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
vfree(cmem);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
static int determine_backup_region(struct resource *res, void *arg)
|
2014-08-09 05:26:09 +08:00
|
|
|
{
|
|
|
|
struct kimage *image = arg;
|
|
|
|
|
2017-10-20 22:30:51 +08:00
|
|
|
image->arch.backup_src_start = res->start;
|
2017-11-08 03:18:01 +08:00
|
|
|
image->arch.backup_src_sz = resource_size(res);
|
2014-08-09 05:26:09 +08:00
|
|
|
|
|
|
|
/* Expecting only one range for backup region */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int crash_load_segments(struct kimage *image)
|
|
|
|
{
|
|
|
|
int ret;
|
2016-11-29 20:45:48 +08:00
|
|
|
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
|
|
|
|
.buf_max = ULONG_MAX, .top_down = false };
|
2014-08-09 05:26:09 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine and load a segment for backup area. First 640K RAM
|
|
|
|
* region is backup source
|
|
|
|
*/
|
|
|
|
|
|
|
|
ret = walk_system_ram_res(KEXEC_BACKUP_SRC_START, KEXEC_BACKUP_SRC_END,
|
|
|
|
image, determine_backup_region);
|
|
|
|
|
|
|
|
/* Zero or postive return values are ok */
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Add backup segment. */
|
2016-11-29 20:45:48 +08:00
|
|
|
if (image->arch.backup_src_sz) {
|
|
|
|
kbuf.buffer = &crash_zero_bytes;
|
|
|
|
kbuf.bufsz = sizeof(crash_zero_bytes);
|
|
|
|
kbuf.memsz = image->arch.backup_src_sz;
|
|
|
|
kbuf.buf_align = PAGE_SIZE;
|
2014-08-09 05:26:09 +08:00
|
|
|
/*
|
|
|
|
* Ideally there is no source for backup segment. This is
|
|
|
|
* copied in purgatory after crash. Just add a zero filled
|
|
|
|
* segment for now to make sure checksum logic works fine.
|
|
|
|
*/
|
2016-11-29 20:45:48 +08:00
|
|
|
ret = kexec_add_buffer(&kbuf);
|
2014-08-09 05:26:09 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-11-29 20:45:48 +08:00
|
|
|
image->arch.backup_load_addr = kbuf.mem;
|
2014-08-09 05:26:09 +08:00
|
|
|
pr_debug("Loaded backup region at 0x%lx backup_start=0x%lx memsz=0x%lx\n",
|
2016-11-29 20:45:48 +08:00
|
|
|
image->arch.backup_load_addr,
|
|
|
|
image->arch.backup_src_start, kbuf.memsz);
|
2014-08-09 05:26:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare elf headers and add a segment */
|
2016-11-29 20:45:48 +08:00
|
|
|
ret = prepare_elf_headers(image, &kbuf.buffer, &kbuf.bufsz);
|
2014-08-09 05:26:09 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-11-29 20:45:48 +08:00
|
|
|
image->arch.elf_headers = kbuf.buffer;
|
|
|
|
image->arch.elf_headers_sz = kbuf.bufsz;
|
2014-08-09 05:26:09 +08:00
|
|
|
|
2016-11-29 20:45:48 +08:00
|
|
|
kbuf.memsz = kbuf.bufsz;
|
|
|
|
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
|
2018-12-28 09:12:47 +08:00
|
|
|
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
|
2016-11-29 20:45:48 +08:00
|
|
|
ret = kexec_add_buffer(&kbuf);
|
2014-08-09 05:26:09 +08:00
|
|
|
if (ret) {
|
|
|
|
vfree((void *)image->arch.elf_headers);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-11-29 20:45:48 +08:00
|
|
|
image->arch.elf_load_addr = kbuf.mem;
|
2014-08-09 05:26:09 +08:00
|
|
|
pr_debug("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
|
2016-11-29 20:45:48 +08:00
|
|
|
image->arch.elf_load_addr, kbuf.bufsz, kbuf.bufsz);
|
2014-08-09 05:26:09 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2014-08-30 06:18:46 +08:00
|
|
|
#endif /* CONFIG_KEXEC_FILE */
|