2019-05-19 13:08:55 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2005-06-25 14:57:56 -07:00
|
|
|
/*
|
2007-10-19 18:24:20 -07:00
|
|
|
* Architecture specific (i386/x86_64) functions for kexec based crash dumps.
|
2005-06-25 14:57:56 -07:00
|
|
|
*
|
|
|
|
* Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
|
|
|
|
*
|
|
|
|
* Copyright (C) IBM Corporation, 2004. All rights reserved.
|
2014-08-08 14:26:09 -07:00
|
|
|
* Copyright (C) Red Hat Inc., 2014. All rights reserved.
|
|
|
|
* Authors:
|
|
|
|
* Vivek Goyal <vgoyal@redhat.com>
|
2005-06-25 14:57:56 -07:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-08-08 14:26:09 -07:00
|
|
|
#define pr_fmt(fmt) "kexec: " fmt
|
|
|
|
|
2005-06-25 14: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-13 20:18:56 -04:00
|
|
|
#include <linux/export.h>
|
2014-08-08 14:26:09 -07:00
|
|
|
#include <linux/slab.h>
|
2015-06-02 19:01:38 +10:00
|
|
|
#include <linux/vmalloc.h>
|
2019-11-08 17:00:25 +08:00
|
|
|
#include <linux/memblock.h>
|
2005-06-25 14:57:56 -07:00
|
|
|
|
2024-01-12 10:44:39 +01:00
|
|
|
#include <asm/bootparam.h>
|
2005-06-25 14:57:56 -07:00
|
|
|
#include <asm/processor.h>
|
|
|
|
#include <asm/hardirq.h>
|
|
|
|
#include <asm/nmi.h>
|
|
|
|
#include <asm/hw_irq.h>
|
2005-11-15 00:09:04 -08:00
|
|
|
#include <asm/apic.h>
|
2017-01-27 11:59:46 +01:00
|
|
|
#include <asm/e820/types.h>
|
2014-10-27 16:12:04 +08:00
|
|
|
#include <asm/io_apic.h>
|
2007-12-03 17:17:10 +01:00
|
|
|
#include <asm/hpet.h>
|
2007-05-08 00:27:03 -07:00
|
|
|
#include <linux/kdebug.h>
|
2009-01-07 21:35:48 +05:30
|
|
|
#include <asm/cpu.h>
|
2008-03-17 16:08:38 -03:00
|
|
|
#include <asm/reboot.h>
|
2015-11-04 14:22:33 +09:00
|
|
|
#include <asm/intel_pt.h>
|
2018-11-22 10:04:09 +08:00
|
|
|
#include <asm/crash.h>
|
2019-11-08 17:00:25 +08:00
|
|
|
#include <asm/cmdline.h>
|
2024-01-25 22:11:20 -06:00
|
|
|
#include <asm/sev.h>
|
2008-11-12 11:34:40 -02:00
|
|
|
|
2014-08-08 14: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;
|
|
|
|
};
|
|
|
|
|
2008-11-12 11:34:38 -02:00
|
|
|
#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
|
|
|
|
|
2011-09-30 15:06:21 -04:00
|
|
|
static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
|
2005-06-25 14:57:58 -07:00
|
|
|
{
|
2008-11-12 11:34:37 -02:00
|
|
|
crash_save_cpu(regs, cpu);
|
|
|
|
|
2015-11-04 14:22:33 +09:00
|
|
|
/*
|
|
|
|
* Disable Intel PT to stop its logging
|
|
|
|
*/
|
|
|
|
cpu_emergency_stop_pt();
|
|
|
|
|
2024-01-25 22:11:20 -06:00
|
|
|
kdump_sev_callback();
|
|
|
|
|
2008-11-12 11:34:37 -02:00
|
|
|
disable_local_APIC();
|
|
|
|
}
|
|
|
|
|
2016-10-11 13:54:23 -07:00
|
|
|
void kdump_nmi_shootdown_cpus(void)
|
2008-11-12 11:34:39 -02:00
|
|
|
{
|
2008-11-12 11:34:40 -02:00
|
|
|
nmi_shootdown_cpus(kdump_nmi_callback);
|
2008-11-12 11:34:39 -02:00
|
|
|
|
2005-11-15 00:09:04 -08:00
|
|
|
disable_local_APIC();
|
2005-06-25 14:57:58 -07:00
|
|
|
}
|
2008-11-12 11:34:39 -02:00
|
|
|
|
2016-10-11 13: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-25 14:57:58 -07:00
|
|
|
#else
|
2016-10-11 13:54:23 -07:00
|
|
|
void crash_smp_send_stop(void)
|
2005-06-25 14:57:58 -07:00
|
|
|
{
|
|
|
|
/* There are no cpus to shootdown */
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-03-17 16:08:38 -03:00
|
|
|
void native_machine_crash_shutdown(struct pt_regs *regs)
|
2005-06-25 14:57:56 -07:00
|
|
|
{
|
|
|
|
/* This function is only called after the system
|
2006-06-26 18:30:00 +02:00
|
|
|
* has panicked or is otherwise in a critical state.
|
2005-06-25 14: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-25 14:57:58 -07:00
|
|
|
/* The kernel is broken so disable interrupts */
|
|
|
|
local_irq_disable();
|
2005-06-25 14:58:14 -07:00
|
|
|
|
2016-10-11 13:54:23 -07:00
|
|
|
crash_smp_send_stop();
|
2008-11-17 19:03:23 -02:00
|
|
|
|
x86/crash: Disable virt in core NMI crash handler to avoid double shootdown
Disable virtualization in crash_nmi_callback() and rework the
emergency_vmx_disable_all() path to do an NMI shootdown if and only if a
shootdown has not already occurred. NMI crash shootdown fundamentally
can't support multiple invocations as responding CPUs are deliberately
put into halt state without unblocking NMIs. But, the emergency reboot
path doesn't have any work of its own, it simply cares about disabling
virtualization, i.e. so long as a shootdown occurred, emergency reboot
doesn't care who initiated the shootdown, or when.
If "crash_kexec_post_notifiers" is specified on the kernel command line,
panic() will invoke crash_smp_send_stop() and result in a second call to
nmi_shootdown_cpus() during native_machine_emergency_restart().
Invoke the callback _before_ disabling virtualization, as the current
VMCS needs to be cleared before doing VMXOFF. Note, this results in a
subtle change in ordering between disabling virtualization and stopping
Intel PT on the responding CPUs. While VMX and Intel PT do interact,
VMXOFF and writes to MSR_IA32_RTIT_CTL do not induce faults between one
another, which is all that matters when panicking.
Harden nmi_shootdown_cpus() against multiple invocations to try and
capture any such kernel bugs via a WARN instead of hanging the system
during a crash/dump, e.g. prior to the recent hardening of
register_nmi_handler(), re-registering the NMI handler would trigger a
double list_add() and hang the system if CONFIG_BUG_ON_DATA_CORRUPTION=y.
list_add double add: new=ffffffff82220800, prev=ffffffff8221cfe8, next=ffffffff82220800.
WARNING: CPU: 2 PID: 1319 at lib/list_debug.c:29 __list_add_valid+0x67/0x70
Call Trace:
__register_nmi_handler+0xcf/0x130
nmi_shootdown_cpus+0x39/0x90
native_machine_emergency_restart+0x1c9/0x1d0
panic+0x237/0x29b
Extract the disabling logic to a common helper to deduplicate code, and
to prepare for doing the shootdown in the emergency reboot path if SVM
is supported.
Note, prior to commit ed72736183c4 ("x86/reboot: Force all cpus to exit
VMX root if VMX is supported"), nmi_shootdown_cpus() was subtly protected
against a second invocation by a cpu_vmx_enabled() check as the kdump
handler would disable VMX if it ran first.
Fixes: ed72736183c4 ("x86/reboot: Force all cpus to exit VMX root if VMX is supported")
Cc: stable@vger.kernel.org
Reported-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Link: https://lore.kernel.org/all/20220427224924.592546-2-gpiccoli@igalia.com
Tested-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20221130233650.1404148-2-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
2022-11-30 23:36:47 +00:00
|
|
|
cpu_emergency_disable_virtualization();
|
2008-11-17 19:03:23 -02:00
|
|
|
|
2015-11-04 14:22:33 +09:00
|
|
|
/*
|
|
|
|
* Disable Intel PT to stop its logging
|
|
|
|
*/
|
|
|
|
cpu_emergency_stop_pt();
|
|
|
|
|
2013-08-20 16:01:07 +09: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-03 17:17:10 +01:00
|
|
|
#endif
|
2013-10-23 18:30:12 -07:00
|
|
|
lapic_shutdown();
|
2018-02-14 13:46:53 +08:00
|
|
|
restore_boot_irq_mode();
|
2007-12-03 17:17:10 +01:00
|
|
|
#ifdef CONFIG_HPET_TIMER
|
|
|
|
hpet_disable();
|
2005-11-15 00:09:04 -08:00
|
|
|
#endif
|
2024-06-14 12:58:55 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Non-crash kexec calls enc_kexec_begin() while scheduling is still
|
|
|
|
* active. This allows the callback to wait until all in-flight
|
|
|
|
* shared<->private conversions are complete. In a crash scenario,
|
|
|
|
* enc_kexec_begin() gets called after all but one CPU have been shut
|
|
|
|
* down and interrupts have been disabled. This allows the callback to
|
|
|
|
* detect a race with the conversion and report it.
|
|
|
|
*/
|
|
|
|
x86_platform.guest.enc_kexec_begin();
|
|
|
|
x86_platform.guest.enc_kexec_finish();
|
|
|
|
|
2006-12-06 20:40:41 -08:00
|
|
|
crash_save_cpu(regs, safe_smp_processor_id());
|
2005-06-25 14:57:56 -07:00
|
|
|
}
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2023-08-14 17:44:43 -04:00
|
|
|
#if defined(CONFIG_KEXEC_FILE) || defined(CONFIG_CRASH_HOTPLUG)
|
2017-10-20 09:30:51 -05:00
|
|
|
static int get_nr_ram_ranges_callback(struct resource *res, void *arg)
|
2014-08-08 14: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 20:58:57 +08:00
|
|
|
unsigned int *nr_ranges = arg;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
|
|
|
(*nr_ranges)++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Gather all the required information to prepare elf headers for ram regions */
|
2018-04-13 15:35:59 -07:00
|
|
|
static struct crash_mem *fill_up_crash_elf_data(void)
|
2014-08-08 14:26:09 -07:00
|
|
|
{
|
|
|
|
unsigned int nr_ranges = 0;
|
2018-04-13 15:35:59 -07:00
|
|
|
struct crash_mem *cmem;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2019-11-14 16:11:50 +01:00
|
|
|
walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
|
2018-04-13 15:35:59 -07:00
|
|
|
if (!nr_ranges)
|
|
|
|
return NULL;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2018-04-13 15: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;
|
2019-04-03 13:42:30 -05:00
|
|
|
cmem = vzalloc(struct_size(cmem, ranges, nr_ranges));
|
2018-04-13 15:35:59 -07:00
|
|
|
if (!cmem)
|
|
|
|
return NULL;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2018-04-13 15:35:59 -07:00
|
|
|
cmem->max_nr_ranges = nr_ranges;
|
|
|
|
cmem->nr_ranges = 0;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2018-04-13 15:35:59 -07:00
|
|
|
return cmem;
|
2014-08-08 14:26:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look for any unwanted ranges between mstart, mend and remove them. This
|
2018-04-13 15:35:59 -07:00
|
|
|
* might lead to split and split ranges are put in cmem->ranges[] array
|
2014-08-08 14:26:09 -07:00
|
|
|
*/
|
2018-04-13 15:35:59 -07:00
|
|
|
static int elf_header_exclude_ranges(struct crash_mem *cmem)
|
2014-08-08 14:26:09 -07:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
2019-11-08 17:00:26 +08:00
|
|
|
/* Exclude the low 1M because it is always reserved */
|
2024-01-02 22:49:04 +08:00
|
|
|
ret = crash_exclude_mem_range(cmem, 0, SZ_1M - 1);
|
2019-11-08 17:00:26 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2014-08-08 14:26:09 -07:00
|
|
|
/* Exclude crashkernel region */
|
2018-04-13 15:36:06 -07:00
|
|
|
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
|
2014-08-08 14:26:09 -07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2019-11-14 16:11:50 +01:00
|
|
|
if (crashk_low_res.end)
|
2018-04-13 15:36:06 -07:00
|
|
|
ret = crash_exclude_mem_range(cmem, crashk_low_res.start,
|
2019-11-14 16:11:50 +01:00
|
|
|
crashk_low_res.end);
|
2014-08-08 14:26:09 -07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-10-20 09:30:51 -05:00
|
|
|
static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
|
2014-08-08 14:26:09 -07:00
|
|
|
{
|
2018-04-13 15:35:59 -07:00
|
|
|
struct crash_mem *cmem = arg;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2018-04-13 15: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-08 14:26:09 -07:00
|
|
|
|
2018-04-13 15:35:53 -07:00
|
|
|
return 0;
|
2014-08-08 14:26:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepare elf headers. Return addr and size */
|
2024-01-02 22:49:03 +08:00
|
|
|
static int prepare_elf_headers(void **addr, unsigned long *sz,
|
|
|
|
unsigned long *nr_mem_ranges)
|
2014-08-08 14:26:09 -07:00
|
|
|
{
|
2018-04-13 15:35:59 -07:00
|
|
|
struct crash_mem *cmem;
|
2019-11-08 17:00:26 +08:00
|
|
|
int ret;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2018-04-13 15:35:59 -07:00
|
|
|
cmem = fill_up_crash_elf_data();
|
|
|
|
if (!cmem)
|
2014-08-08 14:26:09 -07:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-11-14 16:11:50 +01:00
|
|
|
ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
|
2018-04-13 15:35:53 -07:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Exclude unwanted mem ranges */
|
2018-04-13 15:35:59 -07:00
|
|
|
ret = elf_header_exclude_ranges(cmem);
|
2018-04-13 15:35:53 -07:00
|
|
|
if (ret)
|
|
|
|
goto out;
|
|
|
|
|
2023-08-14 17:44:43 -04:00
|
|
|
/* Return the computed number of memory ranges, for hotplug usage */
|
|
|
|
*nr_mem_ranges = cmem->nr_ranges;
|
|
|
|
|
2014-08-08 14:26:09 -07:00
|
|
|
/* By default prepare 64bit headers */
|
2024-01-02 22:49:03 +08:00
|
|
|
ret = crash_prepare_elf64_headers(cmem, IS_ENABLED(CONFIG_X86_64), addr, sz);
|
2018-04-13 15:35:53 -07:00
|
|
|
|
|
|
|
out:
|
2018-04-13 15:35:59 -07:00
|
|
|
vfree(cmem);
|
2014-08-08 14:26:09 -07:00
|
|
|
return ret;
|
|
|
|
}
|
2023-08-14 17:44:43 -04:00
|
|
|
#endif
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2023-08-14 17:44:43 -04:00
|
|
|
#ifdef CONFIG_KEXEC_FILE
|
2017-01-27 12:54:38 +01:00
|
|
|
static int add_e820_entry(struct boot_params *params, struct e820_entry *entry)
|
2014-08-08 14:26:09 -07:00
|
|
|
{
|
|
|
|
unsigned int nr_e820_entries;
|
|
|
|
|
|
|
|
nr_e820_entries = params->e820_entries;
|
2017-01-28 17:29:08 +01:00
|
|
|
if (nr_e820_entries >= E820_MAX_ENTRIES_ZEROPAGE)
|
2014-08-08 14:26:09 -07:00
|
|
|
return 1;
|
|
|
|
|
2019-11-14 16:11:50 +01:00
|
|
|
memcpy(¶ms->e820_table[nr_e820_entries], entry, sizeof(struct e820_entry));
|
2014-08-08 14:26:09 -07:00
|
|
|
params->e820_entries++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-20 09:30:51 -05:00
|
|
|
static int memmap_entry_callback(struct resource *res, void *arg)
|
2014-08-08 14:26:09 -07:00
|
|
|
{
|
|
|
|
struct crash_memmap_data *cmd = arg;
|
|
|
|
struct boot_params *params = cmd->params;
|
2017-01-27 12:54:38 +01:00
|
|
|
struct e820_entry ei;
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2017-10-20 09:30:51 -05:00
|
|
|
ei.addr = res->start;
|
2017-11-08 03:18:01 +08:00
|
|
|
ei.size = resource_size(res);
|
2014-08-08 14: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;
|
|
|
|
|
|
|
|
cmem->ranges[0].start = mstart;
|
|
|
|
cmem->ranges[0].end = mend;
|
|
|
|
cmem->nr_ranges = 1;
|
|
|
|
|
|
|
|
/* Exclude elf header region */
|
2021-02-21 09:49:21 -08:00
|
|
|
start = image->elf_load_addr;
|
|
|
|
end = start + image->elf_headers_sz - 1;
|
2018-04-13 15:36:06 -07:00
|
|
|
return crash_exclude_mem_range(cmem, start, end);
|
2014-08-08 14: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 12:54:38 +01:00
|
|
|
struct e820_entry ei;
|
2014-08-08 14:26:09 -07:00
|
|
|
struct crash_memmap_data cmd;
|
|
|
|
struct crash_mem *cmem;
|
|
|
|
|
2021-04-16 14:02:07 +02:00
|
|
|
cmem = vzalloc(struct_size(cmem, ranges, 1));
|
2014-08-08 14:26:09 -07:00
|
|
|
if (!cmem)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
memset(&cmd, 0, sizeof(struct crash_memmap_data));
|
|
|
|
cmd.params = params;
|
|
|
|
|
2019-11-08 17:00:26 +08:00
|
|
|
/* Add the low 1M */
|
|
|
|
cmd.type = E820_TYPE_RAM;
|
|
|
|
flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
|
|
|
|
walk_iomem_res_desc(IORES_DESC_NONE, flags, 0, (1<<20)-1, &cmd,
|
2019-11-14 16:11:50 +01:00
|
|
|
memmap_entry_callback);
|
2014-08-08 14:26:09 -07:00
|
|
|
|
|
|
|
/* Add ACPI tables */
|
2017-01-28 17:09:33 +01:00
|
|
|
cmd.type = E820_TYPE_ACPI;
|
2014-08-08 14:26:09 -07:00
|
|
|
flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
2016-01-26 21:57:30 +01:00
|
|
|
walk_iomem_res_desc(IORES_DESC_ACPI_TABLES, flags, 0, -1, &cmd,
|
2019-11-14 16:11:50 +01:00
|
|
|
memmap_entry_callback);
|
2014-08-08 14:26:09 -07:00
|
|
|
|
|
|
|
/* Add ACPI Non-volatile Storage */
|
2017-01-28 17:09:33 +01:00
|
|
|
cmd.type = E820_TYPE_NVS;
|
2016-01-26 21:57:30 +01:00
|
|
|
walk_iomem_res_desc(IORES_DESC_ACPI_NV_STORAGE, flags, 0, -1, &cmd,
|
2019-11-14 16:11:50 +01:00
|
|
|
memmap_entry_callback);
|
2014-08-08 14:26:09 -07:00
|
|
|
|
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,
|
2019-11-14 16:11:50 +01: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
|
|
|
|
2014-08-08 14:26:09 -07:00
|
|
|
/* Add crashk_low_res region */
|
|
|
|
if (crashk_low_res.end) {
|
|
|
|
ei.addr = crashk_low_res.start;
|
2020-01-01 18:49:49 +01:00
|
|
|
ei.size = resource_size(&crashk_low_res);
|
2017-01-28 17:09:33 +01:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-08 14:26:09 -07:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Exclude some ranges from crashk_res and add rest to memmap */
|
2019-11-14 16:11:50 +01:00
|
|
|
ret = memmap_exclude_ranges(image, cmem, crashk_res.start, crashk_res.end);
|
2014-08-08 14:26:09 -07:00
|
|
|
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 17:09:33 +01:00
|
|
|
ei.type = E820_TYPE_RAM;
|
2014-08-08 14:26:09 -07:00
|
|
|
add_e820_entry(params, &ei);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
vfree(cmem);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int crash_load_segments(struct kimage *image)
|
|
|
|
{
|
|
|
|
int ret;
|
2023-08-14 17:44:43 -04:00
|
|
|
unsigned long pnum = 0;
|
2016-11-29 23:45:48 +11:00
|
|
|
struct kexec_buf kbuf = { .image = image, .buf_min = 0,
|
|
|
|
.buf_max = ULONG_MAX, .top_down = false };
|
2014-08-08 14:26:09 -07:00
|
|
|
|
|
|
|
/* Prepare elf headers and add a segment */
|
2024-01-02 22:49:03 +08:00
|
|
|
ret = prepare_elf_headers(&kbuf.buffer, &kbuf.bufsz, &pnum);
|
2014-08-08 14:26:09 -07:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2023-08-14 17:44:43 -04:00
|
|
|
image->elf_headers = kbuf.buffer;
|
|
|
|
image->elf_headers_sz = kbuf.bufsz;
|
|
|
|
kbuf.memsz = kbuf.bufsz;
|
|
|
|
|
|
|
|
#ifdef CONFIG_CRASH_HOTPLUG
|
|
|
|
/*
|
|
|
|
* The elfcorehdr segment size accounts for VMCOREINFO, kernel_map,
|
|
|
|
* maximum CPUs and maximum memory ranges.
|
|
|
|
*/
|
|
|
|
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
|
|
|
|
pnum = 2 + CONFIG_NR_CPUS_DEFAULT + CONFIG_CRASH_MAX_MEMORY_RANGES;
|
|
|
|
else
|
|
|
|
pnum += 2 + CONFIG_NR_CPUS_DEFAULT;
|
|
|
|
|
|
|
|
if (pnum < (unsigned long)PN_XNUM) {
|
|
|
|
kbuf.memsz = pnum * sizeof(Elf64_Phdr);
|
|
|
|
kbuf.memsz += sizeof(Elf64_Ehdr);
|
|
|
|
|
|
|
|
image->elfcorehdr_index = image->nr_segments;
|
|
|
|
|
|
|
|
/* Mark as usable to crash kernel, else crash kernel fails on boot */
|
|
|
|
image->elf_headers_sz = kbuf.memsz;
|
|
|
|
} else {
|
|
|
|
pr_err("number of Phdrs %lu exceeds max\n", pnum);
|
|
|
|
}
|
|
|
|
#endif
|
2014-08-08 14:26:09 -07:00
|
|
|
|
2016-11-29 23:45:48 +11:00
|
|
|
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
|
2018-12-28 09:12:47 +08:00
|
|
|
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
|
2016-11-29 23:45:48 +11:00
|
|
|
ret = kexec_add_buffer(&kbuf);
|
2022-11-22 12:51:22 +01:00
|
|
|
if (ret)
|
2014-08-08 14:26:09 -07:00
|
|
|
return ret;
|
2021-02-21 09:49:21 -08:00
|
|
|
image->elf_load_addr = kbuf.mem;
|
2023-12-13 13:57:43 +08:00
|
|
|
kexec_dprintk("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
|
|
|
|
image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
|
2014-08-08 14:26:09 -07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2014-08-29 15:18:46 -07:00
|
|
|
#endif /* CONFIG_KEXEC_FILE */
|
2023-08-14 17:44:43 -04:00
|
|
|
|
|
|
|
#ifdef CONFIG_CRASH_HOTPLUG
|
|
|
|
|
|
|
|
#undef pr_fmt
|
|
|
|
#define pr_fmt(fmt) "crash hp: " fmt
|
|
|
|
|
2024-03-26 11:24:09 +05:30
|
|
|
int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags)
|
crash: hotplug support for kexec_load()
The hotplug support for kexec_load() requires changes to the userspace
kexec-tools and a little extra help from the kernel.
Given a kdump capture kernel loaded via kexec_load(), and a subsequent
hotplug event, the crash hotplug handler finds the elfcorehdr and rewrites
it to reflect the hotplug change. That is the desired outcome, however,
at kernel panic time, the purgatory integrity check fails (because the
elfcorehdr changed), and the capture kernel does not boot and no vmcore is
generated.
Therefore, the userspace kexec-tools/kexec must indicate to the kernel
that the elfcorehdr can be modified (because the kexec excluded the
elfcorehdr from the digest, and sized the elfcorehdr memory buffer
appropriately).
To facilitate hotplug support with kexec_load():
- a new kexec flag KEXEC_UPATE_ELFCOREHDR indicates that it is
safe for the kernel to modify the kexec_load()'d elfcorehdr
- the /sys/kernel/crash_elfcorehdr_size node communicates the
preferred size of the elfcorehdr memory buffer
- The sysfs crash_hotplug nodes (ie.
/sys/devices/system/[cpu|memory]/crash_hotplug) dynamically
take into account kexec_file_load() vs kexec_load() and
KEXEC_UPDATE_ELFCOREHDR.
This is critical so that the udev rule processing of crash_hotplug
is all that is needed to determine if the userspace unload-then-load
of the kdump image is to be skipped, or not. The proposed udev
rule change looks like:
# The kernel updates the crash elfcorehdr for CPU and memory changes
SUBSYSTEM=="cpu", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
SUBSYSTEM=="memory", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
The table below indicates the behavior of kexec_load()'d kdump image
updates (with the new udev crash_hotplug rule in place):
Kernel |Kexec
-------+-----+----
Old |Old |New
| a | a
-------+-----+----
New | a | b
-------+-----+----
where kexec 'old' and 'new' delineate kexec-tools has the needed
modifications for the crash hotplug feature, and kernel 'old' and 'new'
delineate the kernel supports this crash hotplug feature.
Behavior 'a' indicates the unload-then-reload of the entire kdump image.
For the kexec 'old' column, the unload-then-reload occurs due to the
missing flag KEXEC_UPDATE_ELFCOREHDR. An 'old' kernel (with 'new' kexec)
does not present the crash_hotplug sysfs node, which leads to the
unload-then-reload of the kdump image.
Behavior 'b' indicates the desired optimized behavior of the kernel
directly modifying the elfcorehdr and avoiding the unload-then-reload of
the kdump image.
If the udev rule is not updated with crash_hotplug node check, then no
matter any combination of kernel or kexec is new or old, the kdump image
continues to be unload-then-reload on hotplug changes.
To fully support crash hotplug feature, there needs to be a rollout of
kernel, kexec-tools and udev rule changes. However, the order of the
rollout of these pieces does not matter; kexec_load()'d kdump images still
function for hotplug as-is.
Link: https://lkml.kernel.org/r/20230814214446.6659-7-eric.devolder@oracle.com
Signed-off-by: Eric DeVolder <eric.devolder@oracle.com>
Suggested-by: Hari Bathini <hbathini@linux.ibm.com>
Acked-by: Hari Bathini <hbathini@linux.ibm.com>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Akhil Raj <lf32.dev@gmail.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Borislav Petkov (AMD) <bp@alien8.de>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Mimi Zohar <zohar@linux.ibm.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Sourabh Jain <sourabhjain@linux.ibm.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Thomas Weißschuh <linux@weissschuh.net>
Cc: Valentin Schneider <vschneid@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-14 17:44:44 -04:00
|
|
|
{
|
|
|
|
|
2024-03-26 11:24:09 +05:30
|
|
|
#ifdef CONFIG_KEXEC_FILE
|
|
|
|
if (image->file_mode)
|
|
|
|
return 1;
|
crash: hotplug support for kexec_load()
The hotplug support for kexec_load() requires changes to the userspace
kexec-tools and a little extra help from the kernel.
Given a kdump capture kernel loaded via kexec_load(), and a subsequent
hotplug event, the crash hotplug handler finds the elfcorehdr and rewrites
it to reflect the hotplug change. That is the desired outcome, however,
at kernel panic time, the purgatory integrity check fails (because the
elfcorehdr changed), and the capture kernel does not boot and no vmcore is
generated.
Therefore, the userspace kexec-tools/kexec must indicate to the kernel
that the elfcorehdr can be modified (because the kexec excluded the
elfcorehdr from the digest, and sized the elfcorehdr memory buffer
appropriately).
To facilitate hotplug support with kexec_load():
- a new kexec flag KEXEC_UPATE_ELFCOREHDR indicates that it is
safe for the kernel to modify the kexec_load()'d elfcorehdr
- the /sys/kernel/crash_elfcorehdr_size node communicates the
preferred size of the elfcorehdr memory buffer
- The sysfs crash_hotplug nodes (ie.
/sys/devices/system/[cpu|memory]/crash_hotplug) dynamically
take into account kexec_file_load() vs kexec_load() and
KEXEC_UPDATE_ELFCOREHDR.
This is critical so that the udev rule processing of crash_hotplug
is all that is needed to determine if the userspace unload-then-load
of the kdump image is to be skipped, or not. The proposed udev
rule change looks like:
# The kernel updates the crash elfcorehdr for CPU and memory changes
SUBSYSTEM=="cpu", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
SUBSYSTEM=="memory", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
The table below indicates the behavior of kexec_load()'d kdump image
updates (with the new udev crash_hotplug rule in place):
Kernel |Kexec
-------+-----+----
Old |Old |New
| a | a
-------+-----+----
New | a | b
-------+-----+----
where kexec 'old' and 'new' delineate kexec-tools has the needed
modifications for the crash hotplug feature, and kernel 'old' and 'new'
delineate the kernel supports this crash hotplug feature.
Behavior 'a' indicates the unload-then-reload of the entire kdump image.
For the kexec 'old' column, the unload-then-reload occurs due to the
missing flag KEXEC_UPDATE_ELFCOREHDR. An 'old' kernel (with 'new' kexec)
does not present the crash_hotplug sysfs node, which leads to the
unload-then-reload of the kdump image.
Behavior 'b' indicates the desired optimized behavior of the kernel
directly modifying the elfcorehdr and avoiding the unload-then-reload of
the kdump image.
If the udev rule is not updated with crash_hotplug node check, then no
matter any combination of kernel or kexec is new or old, the kdump image
continues to be unload-then-reload on hotplug changes.
To fully support crash hotplug feature, there needs to be a rollout of
kernel, kexec-tools and udev rule changes. However, the order of the
rollout of these pieces does not matter; kexec_load()'d kdump images still
function for hotplug as-is.
Link: https://lkml.kernel.org/r/20230814214446.6659-7-eric.devolder@oracle.com
Signed-off-by: Eric DeVolder <eric.devolder@oracle.com>
Suggested-by: Hari Bathini <hbathini@linux.ibm.com>
Acked-by: Hari Bathini <hbathini@linux.ibm.com>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Akhil Raj <lf32.dev@gmail.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Borislav Petkov (AMD) <bp@alien8.de>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Mimi Zohar <zohar@linux.ibm.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Sourabh Jain <sourabhjain@linux.ibm.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Thomas Weißschuh <linux@weissschuh.net>
Cc: Valentin Schneider <vschneid@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-14 17:44:44 -04:00
|
|
|
#endif
|
2024-03-26 11:24:09 +05:30
|
|
|
/*
|
|
|
|
* Initially, crash hotplug support for kexec_load was added
|
|
|
|
* with the KEXEC_UPDATE_ELFCOREHDR flag. Later, this
|
|
|
|
* functionality was expanded to accommodate multiple kexec
|
|
|
|
* segment updates, leading to the introduction of the
|
|
|
|
* KEXEC_CRASH_HOTPLUG_SUPPORT kexec flag bit. Consequently,
|
|
|
|
* when the kexec tool sends either of these flags, it indicates
|
|
|
|
* that the required kexec segment (elfcorehdr) is excluded from
|
|
|
|
* the SHA calculation.
|
|
|
|
*/
|
|
|
|
return (kexec_flags & KEXEC_UPDATE_ELFCOREHDR ||
|
|
|
|
kexec_flags & KEXEC_CRASH_HOTPLUG_SUPPORT);
|
|
|
|
}
|
crash: hotplug support for kexec_load()
The hotplug support for kexec_load() requires changes to the userspace
kexec-tools and a little extra help from the kernel.
Given a kdump capture kernel loaded via kexec_load(), and a subsequent
hotplug event, the crash hotplug handler finds the elfcorehdr and rewrites
it to reflect the hotplug change. That is the desired outcome, however,
at kernel panic time, the purgatory integrity check fails (because the
elfcorehdr changed), and the capture kernel does not boot and no vmcore is
generated.
Therefore, the userspace kexec-tools/kexec must indicate to the kernel
that the elfcorehdr can be modified (because the kexec excluded the
elfcorehdr from the digest, and sized the elfcorehdr memory buffer
appropriately).
To facilitate hotplug support with kexec_load():
- a new kexec flag KEXEC_UPATE_ELFCOREHDR indicates that it is
safe for the kernel to modify the kexec_load()'d elfcorehdr
- the /sys/kernel/crash_elfcorehdr_size node communicates the
preferred size of the elfcorehdr memory buffer
- The sysfs crash_hotplug nodes (ie.
/sys/devices/system/[cpu|memory]/crash_hotplug) dynamically
take into account kexec_file_load() vs kexec_load() and
KEXEC_UPDATE_ELFCOREHDR.
This is critical so that the udev rule processing of crash_hotplug
is all that is needed to determine if the userspace unload-then-load
of the kdump image is to be skipped, or not. The proposed udev
rule change looks like:
# The kernel updates the crash elfcorehdr for CPU and memory changes
SUBSYSTEM=="cpu", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
SUBSYSTEM=="memory", ATTRS{crash_hotplug}=="1", GOTO="kdump_reload_end"
The table below indicates the behavior of kexec_load()'d kdump image
updates (with the new udev crash_hotplug rule in place):
Kernel |Kexec
-------+-----+----
Old |Old |New
| a | a
-------+-----+----
New | a | b
-------+-----+----
where kexec 'old' and 'new' delineate kexec-tools has the needed
modifications for the crash hotplug feature, and kernel 'old' and 'new'
delineate the kernel supports this crash hotplug feature.
Behavior 'a' indicates the unload-then-reload of the entire kdump image.
For the kexec 'old' column, the unload-then-reload occurs due to the
missing flag KEXEC_UPDATE_ELFCOREHDR. An 'old' kernel (with 'new' kexec)
does not present the crash_hotplug sysfs node, which leads to the
unload-then-reload of the kdump image.
Behavior 'b' indicates the desired optimized behavior of the kernel
directly modifying the elfcorehdr and avoiding the unload-then-reload of
the kdump image.
If the udev rule is not updated with crash_hotplug node check, then no
matter any combination of kernel or kexec is new or old, the kdump image
continues to be unload-then-reload on hotplug changes.
To fully support crash hotplug feature, there needs to be a rollout of
kernel, kexec-tools and udev rule changes. However, the order of the
rollout of these pieces does not matter; kexec_load()'d kdump images still
function for hotplug as-is.
Link: https://lkml.kernel.org/r/20230814214446.6659-7-eric.devolder@oracle.com
Signed-off-by: Eric DeVolder <eric.devolder@oracle.com>
Suggested-by: Hari Bathini <hbathini@linux.ibm.com>
Acked-by: Hari Bathini <hbathini@linux.ibm.com>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Akhil Raj <lf32.dev@gmail.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Borislav Petkov (AMD) <bp@alien8.de>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Mimi Zohar <zohar@linux.ibm.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Sean Christopherson <seanjc@google.com>
Cc: Sourabh Jain <sourabhjain@linux.ibm.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Thomas Weißschuh <linux@weissschuh.net>
Cc: Valentin Schneider <vschneid@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
2023-08-14 17:44:44 -04:00
|
|
|
|
|
|
|
unsigned int arch_crash_get_elfcorehdr_size(void)
|
|
|
|
{
|
|
|
|
unsigned int sz;
|
|
|
|
|
|
|
|
/* kernel_map, VMCOREINFO and maximum CPUs */
|
|
|
|
sz = 2 + CONFIG_NR_CPUS_DEFAULT;
|
|
|
|
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
|
|
|
|
sz += CONFIG_CRASH_MAX_MEMORY_RANGES;
|
|
|
|
sz *= sizeof(Elf64_Phdr);
|
|
|
|
return sz;
|
|
|
|
}
|
|
|
|
|
2023-08-14 17:44:43 -04:00
|
|
|
/**
|
|
|
|
* arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
|
|
|
|
* @image: a pointer to kexec_crash_image
|
2024-03-26 11:24:08 +05:30
|
|
|
* @arg: struct memory_notify handler for memory hotplug case and
|
|
|
|
* NULL for CPU hotplug case.
|
2023-08-14 17:44:43 -04:00
|
|
|
*
|
|
|
|
* Prepare the new elfcorehdr and replace the existing elfcorehdr.
|
|
|
|
*/
|
2024-03-26 11:24:08 +05:30
|
|
|
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
|
2023-08-14 17:44:43 -04:00
|
|
|
{
|
|
|
|
void *elfbuf = NULL, *old_elfcorehdr;
|
|
|
|
unsigned long nr_mem_ranges;
|
|
|
|
unsigned long mem, memsz;
|
|
|
|
unsigned long elfsz = 0;
|
|
|
|
|
2023-08-14 17:44:46 -04:00
|
|
|
/*
|
|
|
|
* As crash_prepare_elf64_headers() has already described all
|
|
|
|
* possible CPUs, there is no need to update the elfcorehdr
|
|
|
|
* for additional CPU changes.
|
|
|
|
*/
|
|
|
|
if ((image->file_mode || image->elfcorehdr_updated) &&
|
|
|
|
((image->hp_action == KEXEC_CRASH_HP_ADD_CPU) ||
|
|
|
|
(image->hp_action == KEXEC_CRASH_HP_REMOVE_CPU)))
|
|
|
|
return;
|
|
|
|
|
2023-08-14 17:44:43 -04:00
|
|
|
/*
|
|
|
|
* Create the new elfcorehdr reflecting the changes to CPU and/or
|
|
|
|
* memory resources.
|
|
|
|
*/
|
2024-01-02 22:49:03 +08:00
|
|
|
if (prepare_elf_headers(&elfbuf, &elfsz, &nr_mem_ranges)) {
|
2023-08-14 17:44:43 -04:00
|
|
|
pr_err("unable to create new elfcorehdr");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Obtain address and size of the elfcorehdr segment, and
|
|
|
|
* check it against the new elfcorehdr buffer.
|
|
|
|
*/
|
|
|
|
mem = image->segment[image->elfcorehdr_index].mem;
|
|
|
|
memsz = image->segment[image->elfcorehdr_index].memsz;
|
|
|
|
if (elfsz > memsz) {
|
|
|
|
pr_err("update elfcorehdr elfsz %lu > memsz %lu",
|
|
|
|
elfsz, memsz);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy new elfcorehdr over the old elfcorehdr at destination.
|
|
|
|
*/
|
|
|
|
old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
|
|
|
|
if (!old_elfcorehdr) {
|
|
|
|
pr_err("mapping elfcorehdr segment failed\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Temporarily invalidate the crash image while the
|
|
|
|
* elfcorehdr is updated.
|
|
|
|
*/
|
|
|
|
xchg(&kexec_crash_image, NULL);
|
|
|
|
memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
|
|
|
|
xchg(&kexec_crash_image, image);
|
|
|
|
kunmap_local(old_elfcorehdr);
|
|
|
|
pr_debug("updated elfcorehdr\n");
|
|
|
|
|
|
|
|
out:
|
|
|
|
vfree(elfbuf);
|
|
|
|
}
|
|
|
|
#endif
|