2005-06-26 04:57:56 +07:00
|
|
|
/*
|
2007-10-20 08:24:20 +07:00
|
|
|
* Architecture specific (i386/x86_64) functions for kexec based crash dumps.
|
2005-06-26 04:57:56 +07:00
|
|
|
*
|
|
|
|
* Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
|
|
|
|
*
|
|
|
|
* Copyright (C) IBM Corporation, 2004. All rights reserved.
|
2014-08-09 04:26:09 +07:00
|
|
|
* Copyright (C) Red Hat Inc., 2014. All rights reserved.
|
|
|
|
* Authors:
|
|
|
|
* Vivek Goyal <vgoyal@redhat.com>
|
2005-06-26 04:57:56 +07:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-08-09 04:26:09 +07:00
|
|
|
#define pr_fmt(fmt) "kexec: " fmt
|
|
|
|
|
2005-06-26 04:57:56 +07: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 07:18:56 +07:00
|
|
|
#include <linux/export.h>
|
2014-08-09 04:26:09 +07:00
|
|
|
#include <linux/slab.h>
|
2015-06-02 16:01:38 +07:00
|
|
|
#include <linux/vmalloc.h>
|
2005-06-26 04:57:56 +07:00
|
|
|
|
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/hardirq.h>
|
|
|
|
#include <asm/nmi.h>
|
|
|
|
#include <asm/hw_irq.h>
|
2005-11-15 15:09:04 +07:00
|
|
|
#include <asm/apic.h>
|
2017-01-27 17:59:46 +07:00
|
|
|
#include <asm/e820/types.h>
|
2014-10-27 15:12:04 +07:00
|
|
|
#include <asm/io_apic.h>
|
2007-12-03 23:17:10 +07:00
|
|
|
#include <asm/hpet.h>
|
2007-05-08 14:27:03 +07:00
|
|
|
#include <linux/kdebug.h>
|
2009-01-07 23:05:48 +07:00
|
|
|
#include <asm/cpu.h>
|
2008-03-18 02:08:38 +07:00
|
|
|
#include <asm/reboot.h>
|
2008-11-18 04:03:23 +07:00
|
|
|
#include <asm/virtext.h>
|
2015-11-04 12:22:33 +07:00
|
|
|
#include <asm/intel_pt.h>
|
2008-11-12 20:34:40 +07:00
|
|
|
|
2014-08-09 04:26:09 +07: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 22:40:47 +07: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 16:11:34 +07:00
|
|
|
crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss = NULL;
|
2012-12-06 22:40:47 +07:00
|
|
|
EXPORT_SYMBOL_GPL(crash_vmclear_loaded_vmcss);
|
2014-08-09 04:26:09 +07:00
|
|
|
unsigned long crash_zero_bytes;
|
2012-12-06 22:40:47 +07:00
|
|
|
|
|
|
|
static inline void cpu_crash_vmclear_loaded_vmcss(void)
|
|
|
|
{
|
2012-12-11 16:11:34 +07:00
|
|
|
crash_vmclear_fn *do_vmclear_operation = NULL;
|
2012-12-06 22:40:47 +07:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
do_vmclear_operation = rcu_dereference(crash_vmclear_loaded_vmcss);
|
|
|
|
if (do_vmclear_operation)
|
|
|
|
do_vmclear_operation();
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2008-11-12 20:34:38 +07:00
|
|
|
#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
|
|
|
|
|
2011-10-01 02:06:21 +07:00
|
|
|
static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
|
2005-06-26 04:57:58 +07:00
|
|
|
{
|
2007-10-24 17:58:01 +07:00
|
|
|
#ifdef CONFIG_X86_32
|
2005-06-26 04:58:13 +07:00
|
|
|
struct pt_regs fixed_regs;
|
2008-11-12 20:34:37 +07:00
|
|
|
|
2015-03-19 08:33:33 +07:00
|
|
|
if (!user_mode(regs)) {
|
2008-11-12 20:34:37 +07:00
|
|
|
crash_fixup_ss_esp(&fixed_regs, regs);
|
|
|
|
regs = &fixed_regs;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
crash_save_cpu(regs, cpu);
|
|
|
|
|
2012-12-06 22:40:47 +07:00
|
|
|
/*
|
|
|
|
* VMCLEAR VMCSs loaded on all cpus if needed.
|
|
|
|
*/
|
|
|
|
cpu_crash_vmclear_loaded_vmcss();
|
|
|
|
|
2008-11-18 04:03:23 +07:00
|
|
|
/* Disable VMX or SVM if needed.
|
|
|
|
*
|
|
|
|
* We need to disable virtualization on all CPUs.
|
|
|
|
* Having VMX or SVM enabled on any CPU may break rebooting
|
|
|
|
* after the kdump kernel has finished its task.
|
|
|
|
*/
|
|
|
|
cpu_emergency_vmxoff();
|
|
|
|
cpu_emergency_svm_disable();
|
|
|
|
|
2015-11-04 12:22:33 +07:00
|
|
|
/*
|
|
|
|
* Disable Intel PT to stop its logging
|
|
|
|
*/
|
|
|
|
cpu_emergency_stop_pt();
|
|
|
|
|
2008-11-12 20:34:37 +07:00
|
|
|
disable_local_APIC();
|
|
|
|
}
|
|
|
|
|
2016-10-12 03:54:23 +07:00
|
|
|
void kdump_nmi_shootdown_cpus(void)
|
2008-11-12 20:34:39 +07:00
|
|
|
{
|
2008-11-12 20:34:40 +07:00
|
|
|
nmi_shootdown_cpus(kdump_nmi_callback);
|
2008-11-12 20:34:39 +07:00
|
|
|
|
2005-11-15 15:09:04 +07:00
|
|
|
disable_local_APIC();
|
2005-06-26 04:57:58 +07:00
|
|
|
}
|
2008-11-12 20:34:39 +07:00
|
|
|
|
2016-10-12 03:54:23 +07: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 04:57:58 +07:00
|
|
|
#else
|
2016-10-12 03:54:23 +07:00
|
|
|
void crash_smp_send_stop(void)
|
2005-06-26 04:57:58 +07:00
|
|
|
{
|
|
|
|
/* There are no cpus to shootdown */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-03-18 02:08:38 +07:00
|
|
|
void native_machine_crash_shutdown(struct pt_regs *regs)
|
2005-06-26 04:57:56 +07:00
|
|
|
{
|
|
|
|
/* This function is only called after the system
|
2006-06-26 23:30:00 +07:00
|
|
|
* has panicked or is otherwise in a critical state.
|
2005-06-26 04:57:56 +07: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 04:57:58 +07:00
|
|
|
/* The kernel is broken so disable interrupts */
|
|
|
|
local_irq_disable();
|
2005-06-26 04:58:14 +07:00
|
|
|
|
2016-10-12 03:54:23 +07:00
|
|
|
crash_smp_send_stop();
|
2008-11-18 04:03:23 +07:00
|
|
|
|
2012-12-06 22:40:47 +07:00
|
|
|
/*
|
|
|
|
* VMCLEAR VMCSs loaded on this cpu if needed.
|
|
|
|
*/
|
|
|
|
cpu_crash_vmclear_loaded_vmcss();
|
|
|
|
|
2008-11-18 04:03:23 +07:00
|
|
|
/* Booting kdump kernel with VMX or SVM enabled won't work,
|
|
|
|
* because (among other limitations) we can't disable paging
|
|
|
|
* with the virt flags.
|
|
|
|
*/
|
|
|
|
cpu_emergency_vmxoff();
|
|
|
|
cpu_emergency_svm_disable();
|
|
|
|
|
2015-11-04 12:22:33 +07:00
|
|
|
/*
|
|
|
|
* Disable Intel PT to stop its logging
|
|
|
|
*/
|
|
|
|
cpu_emergency_stop_pt();
|
|
|
|
|
2013-08-20 14:01:07 +07:00
|
|
|
#ifdef CONFIG_X86_IO_APIC
|
|
|
|
/* Prevent crash_kexec() from deadlocking on ioapic_lock. */
|
|
|
|
ioapic_zap_locks();
|
2018-02-14 12:46:53 +07:00
|
|
|
clear_IO_APIC();
|
2007-12-03 23:17:10 +07:00
|
|
|
#endif
|
2013-10-24 08:30:12 +07:00
|
|
|
lapic_shutdown();
|
2018-02-14 12:46:53 +07:00
|
|
|
restore_boot_irq_mode();
|
2007-12-03 23:17:10 +07:00
|
|
|
#ifdef CONFIG_HPET_TIMER
|
|
|
|
hpet_disable();
|
2005-11-15 15:09:04 +07:00
|
|
|
#endif
|
2006-12-07 11:40:41 +07:00
|
|
|
crash_save_cpu(regs, safe_smp_processor_id());
|
2005-06-26 04:57:56 +07:00
|
|
|
}
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2014-08-30 05:18:46 +07:00
|
|
|
#ifdef CONFIG_KEXEC_FILE
|
2017-10-20 21:30:51 +07:00
|
|
|
static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
|
2014-08-09 04:26:09 +07: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 19:58:57 +07:00
|
|
|
unsigned int *nr_ranges = arg;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
|
|
|
(*nr_ranges)++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Gather all the required information to prepare elf headers for ram regions */
|
2018-04-14 05:35:59 +07:00
|
|
|
static struct crash_mem *fill_up_crash_elf_data(void)
|
2014-08-09 04:26:09 +07:00
|
|
|
{
|
|
|
|
unsigned int nr_ranges = 0;
|
2018-04-14 05:35:59 +07:00
|
|
|
struct crash_mem *cmem;
|
2014-08-09 04:26:09 +07: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 19:58:57 +07:00
|
|
|
walk_system_ram_res(0, -1, &nr_ranges,
|
2014-08-09 04:26:09 +07:00
|
|
|
get_nr_ram_ranges_callback);
|
2018-04-14 05:35:59 +07:00
|
|
|
if (!nr_ranges)
|
|
|
|
return NULL;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2018-04-14 05:35:59 +07:00
|
|
|
/*
|
|
|
|
* Exclusion of crash region and/or crashk_low_res may cause
|
|
|
|
* another range split. So add extra two slots here.
|
|
|
|
*/
|
|
|
|
nr_ranges += 2;
|
|
|
|
cmem = vzalloc(sizeof(struct crash_mem) +
|
|
|
|
sizeof(struct crash_mem_range) * nr_ranges);
|
|
|
|
if (!cmem)
|
|
|
|
return NULL;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2018-04-14 05:35:59 +07:00
|
|
|
cmem->max_nr_ranges = nr_ranges;
|
|
|
|
cmem->nr_ranges = 0;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2018-04-14 05:35:59 +07:00
|
|
|
return cmem;
|
2014-08-09 04:26:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for any unwanted ranges between mstart, mend and remove them. This
|
2018-04-14 05:35:59 +07:00
|
|
|
* might lead to split and split ranges are put in cmem->ranges[] array
|
2014-08-09 04:26:09 +07:00
|
|
|
*/
|
2018-04-14 05:35:59 +07:00
|
|
|
static int elf_header_exclude_ranges(struct crash_mem *cmem)
|
2014-08-09 04:26:09 +07:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Exclude crashkernel region */
|
2018-04-14 05:36:06 +07:00
|
|
|
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
|
2014-08-09 04:26:09 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2014-10-14 05:53:42 +07:00
|
|
|
if (crashk_low_res.end) {
|
2018-04-14 05:36:06 +07:00
|
|
|
ret = crash_exclude_mem_range(cmem, crashk_low_res.start,
|
|
|
|
crashk_low_res.end);
|
2014-10-14 05:53:42 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2014-08-09 04:26:09 +07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-20 21:30:51 +07:00
|
|
|
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
|
2014-08-09 04:26:09 +07:00
|
|
|
{
|
2018-04-14 05:35:59 +07:00
|
|
|
struct crash_mem *cmem = arg;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2018-04-14 05:35:53 +07:00
|
|
|
cmem->ranges[cmem->nr_ranges].start = res->start;
|
|
|
|
cmem->ranges[cmem->nr_ranges].end = res->end;
|
|
|
|
cmem->nr_ranges++;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2018-04-14 05:35:53 +07:00
|
|
|
return 0;
|
2014-08-09 04:26:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare elf headers. Return addr and size */
|
|
|
|
static int prepare_elf_headers(struct kimage *image, void **addr,
|
|
|
|
unsigned long *sz)
|
|
|
|
{
|
2018-04-14 05:35:59 +07:00
|
|
|
struct crash_mem *cmem;
|
2018-04-14 05:35:53 +07:00
|
|
|
Elf64_Ehdr *ehdr;
|
|
|
|
Elf64_Phdr *phdr;
|
|
|
|
int ret, i;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2018-04-14 05:35:59 +07:00
|
|
|
cmem = fill_up_crash_elf_data();
|
|
|
|
if (!cmem)
|
2014-08-09 04:26:09 +07:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-04-14 05:35:59 +07:00
|
|
|
ret = walk_system_ram_res(0, -1, cmem,
|
2018-04-14 05:35:53 +07:00
|
|
|
prepare_elf64_ram_headers_callback);
|
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Exclude unwanted mem ranges */
|
2018-04-14 05:35:59 +07:00
|
|
|
ret = elf_header_exclude_ranges(cmem);
|
2018-04-14 05:35:53 +07:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2014-08-09 04:26:09 +07:00
|
|
|
/* By default prepare 64bit headers */
|
2018-04-14 05:36:06 +07:00
|
|
|
ret = crash_prepare_elf64_headers(cmem,
|
|
|
|
IS_ENABLED(CONFIG_X86_64), addr, sz);
|
2018-04-14 05:35:53 +07: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 05:35:59 +07:00
|
|
|
vfree(cmem);
|
2014-08-09 04:26:09 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-01-27 18:54:38 +07:00
|
|
|
static int add_e820_entry(struct boot_params *params, struct e820_entry *entry)
|
2014-08-09 04:26:09 +07:00
|
|
|
{
|
|
|
|
unsigned int nr_e820_entries;
|
|
|
|
|
|
|
|
nr_e820_entries = params->e820_entries;
|
2017-01-28 23:29:08 +07:00
|
|
|
if (nr_e820_entries >= E820_MAX_ENTRIES_ZEROPAGE)
|
2014-08-09 04:26:09 +07:00
|
|
|
return 1;
|
|
|
|
|
2017-01-27 19:54:38 +07:00
|
|
|
memcpy(¶ms->e820_table[nr_e820_entries], entry,
|
2017-01-27 18:54:38 +07:00
|
|
|
sizeof(struct e820_entry));
|
2014-08-09 04:26:09 +07:00
|
|
|
params->e820_entries++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-20 21:30:51 +07:00
|
|
|
static int memmap_entry_callback(struct resource *res, void *arg)
|
2014-08-09 04:26:09 +07:00
|
|
|
{
|
|
|
|
struct crash_memmap_data *cmd = arg;
|
|
|
|
struct boot_params *params = cmd->params;
|
2017-01-27 18:54:38 +07:00
|
|
|
struct e820_entry ei;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2017-10-20 21:30:51 +07:00
|
|
|
ei.addr = res->start;
|
2017-11-08 02:18:01 +07:00
|
|
|
ei.size = resource_size(res);
|
2014-08-09 04:26:09 +07: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 05:36:06 +07:00
|
|
|
ret = crash_exclude_mem_range(cmem, start, end);
|
2014-08-09 04:26:09 +07: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 05:36:06 +07:00
|
|
|
return crash_exclude_mem_range(cmem, start, end);
|
2014-08-09 04:26:09 +07: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 18:54:38 +07:00
|
|
|
struct e820_entry ei;
|
2014-08-09 04:26:09 +07:00
|
|
|
struct crash_memmap_data cmd;
|
|
|
|
struct crash_mem *cmem;
|
|
|
|
|
|
|
|
cmem = vzalloc(sizeof(struct crash_mem));
|
|
|
|
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-28 23:09:33 +07:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-09 04:26:09 +07:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
|
|
|
|
/* Add ACPI tables */
|
2017-01-28 23:09:33 +07:00
|
|
|
cmd.type = E820_TYPE_ACPI;
|
2014-08-09 04:26:09 +07:00
|
|
|
flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
2016-01-27 03:57:30 +07:00
|
|
|
walk_iomem_res_desc(IORES_DESC_ACPI_TABLES, flags, 0, -1, &cmd,
|
2014-08-09 04:26:09 +07:00
|
|
|
memmap_entry_callback);
|
|
|
|
|
|
|
|
/* Add ACPI Non-volatile Storage */
|
2017-01-28 23:09:33 +07:00
|
|
|
cmd.type = E820_TYPE_NVS;
|
2016-01-27 03:57:30 +07:00
|
|
|
walk_iomem_res_desc(IORES_DESC_ACPI_NV_STORAGE, flags, 0, -1, &cmd,
|
2014-08-09 04:26:09 +07:00
|
|
|
memmap_entry_callback);
|
|
|
|
|
|
|
|
/* 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-28 23:09:33 +07:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-09 04:26:09 +07: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-28 23:09:33 +07:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-09 04:26:09 +07:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
vfree(cmem);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-20 21:30:51 +07:00
|
|
|
static int determine_backup_region(struct resource *res, void *arg)
|
2014-08-09 04:26:09 +07:00
|
|
|
{
|
|
|
|
struct kimage *image = arg;
|
|
|
|
|
2017-10-20 21:30:51 +07:00
|
|
|
image->arch.backup_src_start = res->start;
|
2017-11-08 02:18:01 +07:00
|
|
|
image->arch.backup_src_sz = resource_size(res);
|
2014-08-09 04:26:09 +07:00
|
|
|
|
|
|
|
/* Expecting only one range for backup region */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int crash_load_segments(struct kimage *image)
|
|
|
|
{
|
|
|
|
int ret;
|
2016-11-29 19:45:48 +07:00
|
|
|
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
|
|
|
|
.buf_max = ULONG_MAX, .top_down = false };
|
2014-08-09 04:26:09 +07: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 19:45:48 +07: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 04:26:09 +07: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 19:45:48 +07:00
|
|
|
ret = kexec_add_buffer(&kbuf);
|
2014-08-09 04:26:09 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2016-11-29 19:45:48 +07:00
|
|
|
image->arch.backup_load_addr = kbuf.mem;
|
2014-08-09 04:26:09 +07:00
|
|
|
pr_debug("Loaded backup region at 0x%lx backup_start=0x%lx memsz=0x%lx\n",
|
2016-11-29 19:45:48 +07:00
|
|
|
image->arch.backup_load_addr,
|
|
|
|
image->arch.backup_src_start, kbuf.memsz);
|
2014-08-09 04:26:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare elf headers and add a segment */
|
2016-11-29 19:45:48 +07:00
|
|
|
ret = prepare_elf_headers(image, &kbuf.buffer, &kbuf.bufsz);
|
2014-08-09 04:26:09 +07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-11-29 19:45:48 +07:00
|
|
|
image->arch.elf_headers = kbuf.buffer;
|
|
|
|
image->arch.elf_headers_sz = kbuf.bufsz;
|
2014-08-09 04:26:09 +07:00
|
|
|
|
2016-11-29 19:45:48 +07:00
|
|
|
kbuf.memsz = kbuf.bufsz;
|
|
|
|
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
|
|
|
|
ret = kexec_add_buffer(&kbuf);
|
2014-08-09 04:26:09 +07:00
|
|
|
if (ret) {
|
|
|
|
vfree((void *)image->arch.elf_headers);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-11-29 19:45:48 +07:00
|
|
|
image->arch.elf_load_addr = kbuf.mem;
|
2014-08-09 04:26:09 +07:00
|
|
|
pr_debug("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
|
2016-11-29 19:45:48 +07:00
|
|
|
image->arch.elf_load_addr, kbuf.bufsz, kbuf.bufsz);
|
2014-08-09 04:26:09 +07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2014-08-30 05:18:46 +07:00
|
|
|
#endif /* CONFIG_KEXEC_FILE */
|