xref: /kernel/linux/linux-6.6/arch/x86/kernel/crash.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Architecture specific (i386/x86_64) functions for kexec based crash dumps.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2004. All rights reserved.
862306a36Sopenharmony_ci * Copyright (C) Red Hat Inc., 2014. All rights reserved.
962306a36Sopenharmony_ci * Authors:
1062306a36Sopenharmony_ci *      Vivek Goyal <vgoyal@redhat.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt)	"kexec: " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/kernel.h>
1862306a36Sopenharmony_ci#include <linux/smp.h>
1962306a36Sopenharmony_ci#include <linux/reboot.h>
2062306a36Sopenharmony_ci#include <linux/kexec.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/elf.h>
2362306a36Sopenharmony_ci#include <linux/elfcore.h>
2462306a36Sopenharmony_ci#include <linux/export.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/vmalloc.h>
2762306a36Sopenharmony_ci#include <linux/memblock.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <asm/processor.h>
3062306a36Sopenharmony_ci#include <asm/hardirq.h>
3162306a36Sopenharmony_ci#include <asm/nmi.h>
3262306a36Sopenharmony_ci#include <asm/hw_irq.h>
3362306a36Sopenharmony_ci#include <asm/apic.h>
3462306a36Sopenharmony_ci#include <asm/e820/types.h>
3562306a36Sopenharmony_ci#include <asm/io_apic.h>
3662306a36Sopenharmony_ci#include <asm/hpet.h>
3762306a36Sopenharmony_ci#include <linux/kdebug.h>
3862306a36Sopenharmony_ci#include <asm/cpu.h>
3962306a36Sopenharmony_ci#include <asm/reboot.h>
4062306a36Sopenharmony_ci#include <asm/intel_pt.h>
4162306a36Sopenharmony_ci#include <asm/crash.h>
4262306a36Sopenharmony_ci#include <asm/cmdline.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Used while preparing memory map entries for second kernel */
4562306a36Sopenharmony_cistruct crash_memmap_data {
4662306a36Sopenharmony_ci	struct boot_params *params;
4762306a36Sopenharmony_ci	/* Type of memory */
4862306a36Sopenharmony_ci	unsigned int type;
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void kdump_nmi_callback(int cpu, struct pt_regs *regs)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	crash_save_cpu(regs, cpu);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/*
5862306a36Sopenharmony_ci	 * Disable Intel PT to stop its logging
5962306a36Sopenharmony_ci	 */
6062306a36Sopenharmony_ci	cpu_emergency_stop_pt();
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	disable_local_APIC();
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_civoid kdump_nmi_shootdown_cpus(void)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	nmi_shootdown_cpus(kdump_nmi_callback);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	disable_local_APIC();
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Override the weak function in kernel/panic.c */
7362306a36Sopenharmony_civoid crash_smp_send_stop(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	static int cpus_stopped;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	if (cpus_stopped)
7862306a36Sopenharmony_ci		return;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (smp_ops.crash_stop_other_cpus)
8162306a36Sopenharmony_ci		smp_ops.crash_stop_other_cpus();
8262306a36Sopenharmony_ci	else
8362306a36Sopenharmony_ci		smp_send_stop();
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	cpus_stopped = 1;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#else
8962306a36Sopenharmony_civoid crash_smp_send_stop(void)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	/* There are no cpus to shootdown */
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci#endif
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_civoid native_machine_crash_shutdown(struct pt_regs *regs)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	/* This function is only called after the system
9862306a36Sopenharmony_ci	 * has panicked or is otherwise in a critical state.
9962306a36Sopenharmony_ci	 * The minimum amount of code to allow a kexec'd kernel
10062306a36Sopenharmony_ci	 * to run successfully needs to happen here.
10162306a36Sopenharmony_ci	 *
10262306a36Sopenharmony_ci	 * In practice this means shooting down the other cpus in
10362306a36Sopenharmony_ci	 * an SMP system.
10462306a36Sopenharmony_ci	 */
10562306a36Sopenharmony_ci	/* The kernel is broken so disable interrupts */
10662306a36Sopenharmony_ci	local_irq_disable();
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	crash_smp_send_stop();
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	cpu_emergency_disable_virtualization();
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 * Disable Intel PT to stop its logging
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	cpu_emergency_stop_pt();
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#ifdef CONFIG_X86_IO_APIC
11862306a36Sopenharmony_ci	/* Prevent crash_kexec() from deadlocking on ioapic_lock. */
11962306a36Sopenharmony_ci	ioapic_zap_locks();
12062306a36Sopenharmony_ci	clear_IO_APIC();
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci	lapic_shutdown();
12362306a36Sopenharmony_ci	restore_boot_irq_mode();
12462306a36Sopenharmony_ci#ifdef CONFIG_HPET_TIMER
12562306a36Sopenharmony_ci	hpet_disable();
12662306a36Sopenharmony_ci#endif
12762306a36Sopenharmony_ci	crash_save_cpu(regs, safe_smp_processor_id());
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#if defined(CONFIG_KEXEC_FILE) || defined(CONFIG_CRASH_HOTPLUG)
13162306a36Sopenharmony_cistatic int get_nr_ram_ranges_callback(struct resource *res, void *arg)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned int *nr_ranges = arg;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	(*nr_ranges)++;
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* Gather all the required information to prepare elf headers for ram regions */
14062306a36Sopenharmony_cistatic struct crash_mem *fill_up_crash_elf_data(void)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	unsigned int nr_ranges = 0;
14362306a36Sopenharmony_ci	struct crash_mem *cmem;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	walk_system_ram_res(0, -1, &nr_ranges, get_nr_ram_ranges_callback);
14662306a36Sopenharmony_ci	if (!nr_ranges)
14762306a36Sopenharmony_ci		return NULL;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * Exclusion of crash region and/or crashk_low_res may cause
15162306a36Sopenharmony_ci	 * another range split. So add extra two slots here.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	nr_ranges += 2;
15462306a36Sopenharmony_ci	cmem = vzalloc(struct_size(cmem, ranges, nr_ranges));
15562306a36Sopenharmony_ci	if (!cmem)
15662306a36Sopenharmony_ci		return NULL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	cmem->max_nr_ranges = nr_ranges;
15962306a36Sopenharmony_ci	cmem->nr_ranges = 0;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return cmem;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/*
16562306a36Sopenharmony_ci * Look for any unwanted ranges between mstart, mend and remove them. This
16662306a36Sopenharmony_ci * might lead to split and split ranges are put in cmem->ranges[] array
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_cistatic int elf_header_exclude_ranges(struct crash_mem *cmem)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	int ret = 0;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Exclude the low 1M because it is always reserved */
17362306a36Sopenharmony_ci	ret = crash_exclude_mem_range(cmem, 0, (1<<20)-1);
17462306a36Sopenharmony_ci	if (ret)
17562306a36Sopenharmony_ci		return ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Exclude crashkernel region */
17862306a36Sopenharmony_ci	ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
17962306a36Sopenharmony_ci	if (ret)
18062306a36Sopenharmony_ci		return ret;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (crashk_low_res.end)
18362306a36Sopenharmony_ci		ret = crash_exclude_mem_range(cmem, crashk_low_res.start,
18462306a36Sopenharmony_ci					      crashk_low_res.end);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return ret;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int prepare_elf64_ram_headers_callback(struct resource *res, void *arg)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct crash_mem *cmem = arg;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	cmem->ranges[cmem->nr_ranges].start = res->start;
19462306a36Sopenharmony_ci	cmem->ranges[cmem->nr_ranges].end = res->end;
19562306a36Sopenharmony_ci	cmem->nr_ranges++;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/* Prepare elf headers. Return addr and size */
20162306a36Sopenharmony_cistatic int prepare_elf_headers(struct kimage *image, void **addr,
20262306a36Sopenharmony_ci					unsigned long *sz, unsigned long *nr_mem_ranges)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct crash_mem *cmem;
20562306a36Sopenharmony_ci	int ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	cmem = fill_up_crash_elf_data();
20862306a36Sopenharmony_ci	if (!cmem)
20962306a36Sopenharmony_ci		return -ENOMEM;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = walk_system_ram_res(0, -1, cmem, prepare_elf64_ram_headers_callback);
21262306a36Sopenharmony_ci	if (ret)
21362306a36Sopenharmony_ci		goto out;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Exclude unwanted mem ranges */
21662306a36Sopenharmony_ci	ret = elf_header_exclude_ranges(cmem);
21762306a36Sopenharmony_ci	if (ret)
21862306a36Sopenharmony_ci		goto out;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Return the computed number of memory ranges, for hotplug usage */
22162306a36Sopenharmony_ci	*nr_mem_ranges = cmem->nr_ranges;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* By default prepare 64bit headers */
22462306a36Sopenharmony_ci	ret =  crash_prepare_elf64_headers(cmem, IS_ENABLED(CONFIG_X86_64), addr, sz);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciout:
22762306a36Sopenharmony_ci	vfree(cmem);
22862306a36Sopenharmony_ci	return ret;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci#endif
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_FILE
23362306a36Sopenharmony_cistatic int add_e820_entry(struct boot_params *params, struct e820_entry *entry)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	unsigned int nr_e820_entries;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	nr_e820_entries = params->e820_entries;
23862306a36Sopenharmony_ci	if (nr_e820_entries >= E820_MAX_ENTRIES_ZEROPAGE)
23962306a36Sopenharmony_ci		return 1;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	memcpy(&params->e820_table[nr_e820_entries], entry, sizeof(struct e820_entry));
24262306a36Sopenharmony_ci	params->e820_entries++;
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int memmap_entry_callback(struct resource *res, void *arg)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct crash_memmap_data *cmd = arg;
24962306a36Sopenharmony_ci	struct boot_params *params = cmd->params;
25062306a36Sopenharmony_ci	struct e820_entry ei;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	ei.addr = res->start;
25362306a36Sopenharmony_ci	ei.size = resource_size(res);
25462306a36Sopenharmony_ci	ei.type = cmd->type;
25562306a36Sopenharmony_ci	add_e820_entry(params, &ei);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int memmap_exclude_ranges(struct kimage *image, struct crash_mem *cmem,
26162306a36Sopenharmony_ci				 unsigned long long mstart,
26262306a36Sopenharmony_ci				 unsigned long long mend)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	unsigned long start, end;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	cmem->ranges[0].start = mstart;
26762306a36Sopenharmony_ci	cmem->ranges[0].end = mend;
26862306a36Sopenharmony_ci	cmem->nr_ranges = 1;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* Exclude elf header region */
27162306a36Sopenharmony_ci	start = image->elf_load_addr;
27262306a36Sopenharmony_ci	end = start + image->elf_headers_sz - 1;
27362306a36Sopenharmony_ci	return crash_exclude_mem_range(cmem, start, end);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/* Prepare memory map for crash dump kernel */
27762306a36Sopenharmony_ciint crash_setup_memmap_entries(struct kimage *image, struct boot_params *params)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int i, ret = 0;
28062306a36Sopenharmony_ci	unsigned long flags;
28162306a36Sopenharmony_ci	struct e820_entry ei;
28262306a36Sopenharmony_ci	struct crash_memmap_data cmd;
28362306a36Sopenharmony_ci	struct crash_mem *cmem;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	cmem = vzalloc(struct_size(cmem, ranges, 1));
28662306a36Sopenharmony_ci	if (!cmem)
28762306a36Sopenharmony_ci		return -ENOMEM;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	memset(&cmd, 0, sizeof(struct crash_memmap_data));
29062306a36Sopenharmony_ci	cmd.params = params;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Add the low 1M */
29362306a36Sopenharmony_ci	cmd.type = E820_TYPE_RAM;
29462306a36Sopenharmony_ci	flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
29562306a36Sopenharmony_ci	walk_iomem_res_desc(IORES_DESC_NONE, flags, 0, (1<<20)-1, &cmd,
29662306a36Sopenharmony_ci			    memmap_entry_callback);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Add ACPI tables */
29962306a36Sopenharmony_ci	cmd.type = E820_TYPE_ACPI;
30062306a36Sopenharmony_ci	flags = IORESOURCE_MEM | IORESOURCE_BUSY;
30162306a36Sopenharmony_ci	walk_iomem_res_desc(IORES_DESC_ACPI_TABLES, flags, 0, -1, &cmd,
30262306a36Sopenharmony_ci			    memmap_entry_callback);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* Add ACPI Non-volatile Storage */
30562306a36Sopenharmony_ci	cmd.type = E820_TYPE_NVS;
30662306a36Sopenharmony_ci	walk_iomem_res_desc(IORES_DESC_ACPI_NV_STORAGE, flags, 0, -1, &cmd,
30762306a36Sopenharmony_ci			    memmap_entry_callback);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* Add e820 reserved ranges */
31062306a36Sopenharmony_ci	cmd.type = E820_TYPE_RESERVED;
31162306a36Sopenharmony_ci	flags = IORESOURCE_MEM;
31262306a36Sopenharmony_ci	walk_iomem_res_desc(IORES_DESC_RESERVED, flags, 0, -1, &cmd,
31362306a36Sopenharmony_ci			    memmap_entry_callback);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Add crashk_low_res region */
31662306a36Sopenharmony_ci	if (crashk_low_res.end) {
31762306a36Sopenharmony_ci		ei.addr = crashk_low_res.start;
31862306a36Sopenharmony_ci		ei.size = resource_size(&crashk_low_res);
31962306a36Sopenharmony_ci		ei.type = E820_TYPE_RAM;
32062306a36Sopenharmony_ci		add_e820_entry(params, &ei);
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/* Exclude some ranges from crashk_res and add rest to memmap */
32462306a36Sopenharmony_ci	ret = memmap_exclude_ranges(image, cmem, crashk_res.start, crashk_res.end);
32562306a36Sopenharmony_ci	if (ret)
32662306a36Sopenharmony_ci		goto out;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	for (i = 0; i < cmem->nr_ranges; i++) {
32962306a36Sopenharmony_ci		ei.size = cmem->ranges[i].end - cmem->ranges[i].start + 1;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		/* If entry is less than a page, skip it */
33262306a36Sopenharmony_ci		if (ei.size < PAGE_SIZE)
33362306a36Sopenharmony_ci			continue;
33462306a36Sopenharmony_ci		ei.addr = cmem->ranges[i].start;
33562306a36Sopenharmony_ci		ei.type = E820_TYPE_RAM;
33662306a36Sopenharmony_ci		add_e820_entry(params, &ei);
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ciout:
34062306a36Sopenharmony_ci	vfree(cmem);
34162306a36Sopenharmony_ci	return ret;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciint crash_load_segments(struct kimage *image)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	int ret;
34762306a36Sopenharmony_ci	unsigned long pnum = 0;
34862306a36Sopenharmony_ci	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
34962306a36Sopenharmony_ci				  .buf_max = ULONG_MAX, .top_down = false };
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Prepare elf headers and add a segment */
35262306a36Sopenharmony_ci	ret = prepare_elf_headers(image, &kbuf.buffer, &kbuf.bufsz, &pnum);
35362306a36Sopenharmony_ci	if (ret)
35462306a36Sopenharmony_ci		return ret;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	image->elf_headers	= kbuf.buffer;
35762306a36Sopenharmony_ci	image->elf_headers_sz	= kbuf.bufsz;
35862306a36Sopenharmony_ci	kbuf.memsz		= kbuf.bufsz;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci#ifdef CONFIG_CRASH_HOTPLUG
36162306a36Sopenharmony_ci	/*
36262306a36Sopenharmony_ci	 * The elfcorehdr segment size accounts for VMCOREINFO, kernel_map,
36362306a36Sopenharmony_ci	 * maximum CPUs and maximum memory ranges.
36462306a36Sopenharmony_ci	 */
36562306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
36662306a36Sopenharmony_ci		pnum = 2 + CONFIG_NR_CPUS_DEFAULT + CONFIG_CRASH_MAX_MEMORY_RANGES;
36762306a36Sopenharmony_ci	else
36862306a36Sopenharmony_ci		pnum += 2 + CONFIG_NR_CPUS_DEFAULT;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (pnum < (unsigned long)PN_XNUM) {
37162306a36Sopenharmony_ci		kbuf.memsz = pnum * sizeof(Elf64_Phdr);
37262306a36Sopenharmony_ci		kbuf.memsz += sizeof(Elf64_Ehdr);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		image->elfcorehdr_index = image->nr_segments;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		/* Mark as usable to crash kernel, else crash kernel fails on boot */
37762306a36Sopenharmony_ci		image->elf_headers_sz = kbuf.memsz;
37862306a36Sopenharmony_ci	} else {
37962306a36Sopenharmony_ci		pr_err("number of Phdrs %lu exceeds max\n", pnum);
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci#endif
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
38462306a36Sopenharmony_ci	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
38562306a36Sopenharmony_ci	ret = kexec_add_buffer(&kbuf);
38662306a36Sopenharmony_ci	if (ret)
38762306a36Sopenharmony_ci		return ret;
38862306a36Sopenharmony_ci	image->elf_load_addr = kbuf.mem;
38962306a36Sopenharmony_ci	pr_debug("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
39062306a36Sopenharmony_ci		 image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return ret;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci#endif /* CONFIG_KEXEC_FILE */
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci#ifdef CONFIG_CRASH_HOTPLUG
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci#undef pr_fmt
39962306a36Sopenharmony_ci#define pr_fmt(fmt) "crash hp: " fmt
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/* These functions provide the value for the sysfs crash_hotplug nodes */
40262306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
40362306a36Sopenharmony_ciint arch_crash_hotplug_cpu_support(void)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	return crash_check_update_elfcorehdr();
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci#endif
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
41062306a36Sopenharmony_ciint arch_crash_hotplug_memory_support(void)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	return crash_check_update_elfcorehdr();
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci#endif
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ciunsigned int arch_crash_get_elfcorehdr_size(void)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	unsigned int sz;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* kernel_map, VMCOREINFO and maximum CPUs */
42162306a36Sopenharmony_ci	sz = 2 + CONFIG_NR_CPUS_DEFAULT;
42262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
42362306a36Sopenharmony_ci		sz += CONFIG_CRASH_MAX_MEMORY_RANGES;
42462306a36Sopenharmony_ci	sz *= sizeof(Elf64_Phdr);
42562306a36Sopenharmony_ci	return sz;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/**
42962306a36Sopenharmony_ci * arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
43062306a36Sopenharmony_ci * @image: a pointer to kexec_crash_image
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * Prepare the new elfcorehdr and replace the existing elfcorehdr.
43362306a36Sopenharmony_ci */
43462306a36Sopenharmony_civoid arch_crash_handle_hotplug_event(struct kimage *image)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	void *elfbuf = NULL, *old_elfcorehdr;
43762306a36Sopenharmony_ci	unsigned long nr_mem_ranges;
43862306a36Sopenharmony_ci	unsigned long mem, memsz;
43962306a36Sopenharmony_ci	unsigned long elfsz = 0;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * As crash_prepare_elf64_headers() has already described all
44362306a36Sopenharmony_ci	 * possible CPUs, there is no need to update the elfcorehdr
44462306a36Sopenharmony_ci	 * for additional CPU changes.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	if ((image->file_mode || image->elfcorehdr_updated) &&
44762306a36Sopenharmony_ci		((image->hp_action == KEXEC_CRASH_HP_ADD_CPU) ||
44862306a36Sopenharmony_ci		(image->hp_action == KEXEC_CRASH_HP_REMOVE_CPU)))
44962306a36Sopenharmony_ci		return;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/*
45262306a36Sopenharmony_ci	 * Create the new elfcorehdr reflecting the changes to CPU and/or
45362306a36Sopenharmony_ci	 * memory resources.
45462306a36Sopenharmony_ci	 */
45562306a36Sopenharmony_ci	if (prepare_elf_headers(image, &elfbuf, &elfsz, &nr_mem_ranges)) {
45662306a36Sopenharmony_ci		pr_err("unable to create new elfcorehdr");
45762306a36Sopenharmony_ci		goto out;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/*
46162306a36Sopenharmony_ci	 * Obtain address and size of the elfcorehdr segment, and
46262306a36Sopenharmony_ci	 * check it against the new elfcorehdr buffer.
46362306a36Sopenharmony_ci	 */
46462306a36Sopenharmony_ci	mem = image->segment[image->elfcorehdr_index].mem;
46562306a36Sopenharmony_ci	memsz = image->segment[image->elfcorehdr_index].memsz;
46662306a36Sopenharmony_ci	if (elfsz > memsz) {
46762306a36Sopenharmony_ci		pr_err("update elfcorehdr elfsz %lu > memsz %lu",
46862306a36Sopenharmony_ci			elfsz, memsz);
46962306a36Sopenharmony_ci		goto out;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/*
47362306a36Sopenharmony_ci	 * Copy new elfcorehdr over the old elfcorehdr at destination.
47462306a36Sopenharmony_ci	 */
47562306a36Sopenharmony_ci	old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
47662306a36Sopenharmony_ci	if (!old_elfcorehdr) {
47762306a36Sopenharmony_ci		pr_err("mapping elfcorehdr segment failed\n");
47862306a36Sopenharmony_ci		goto out;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/*
48262306a36Sopenharmony_ci	 * Temporarily invalidate the crash image while the
48362306a36Sopenharmony_ci	 * elfcorehdr is updated.
48462306a36Sopenharmony_ci	 */
48562306a36Sopenharmony_ci	xchg(&kexec_crash_image, NULL);
48662306a36Sopenharmony_ci	memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
48762306a36Sopenharmony_ci	xchg(&kexec_crash_image, image);
48862306a36Sopenharmony_ci	kunmap_local(old_elfcorehdr);
48962306a36Sopenharmony_ci	pr_debug("updated elfcorehdr\n");
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciout:
49262306a36Sopenharmony_ci	vfree(elfbuf);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci#endif
495