xref: /openbmc/linux/arch/arm/kernel/machine_kexec.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2c587e4a6SRichard Purdie /*
3c587e4a6SRichard Purdie  * machine_kexec.c - handle transition of Linux booting another kernel
4c587e4a6SRichard Purdie  */
5c587e4a6SRichard Purdie 
6c587e4a6SRichard Purdie #include <linux/mm.h>
7c587e4a6SRichard Purdie #include <linux/kexec.h>
8c587e4a6SRichard Purdie #include <linux/delay.h>
9c587e4a6SRichard Purdie #include <linux/reboot.h>
10fced80c7SRussell King #include <linux/io.h>
119141a003SWill Deacon #include <linux/irq.h>
12c564df4dSMatthew Leach #include <linux/memblock.h>
134cabd1d9SMatthew Leach #include <linux/of_fdt.h>
14c587e4a6SRichard Purdie #include <asm/mmu_context.h>
15c587e4a6SRichard Purdie #include <asm/cacheflush.h>
164d62e81bSRussell King #include <asm/kexec-internal.h>
17e2ccba49SDave Martin #include <asm/fncpy.h>
18c587e4a6SRichard Purdie #include <asm/mach-types.h>
192103f6cbSStephen Warren #include <asm/smp_plat.h>
209f97da78SDavid Howells #include <asm/system_misc.h>
2174d86a70SLaura Abbott #include <asm/set_memory.h>
22c587e4a6SRichard Purdie 
23e2ccba49SDave Martin extern void relocate_new_kernel(void);
24e0fc4f97STobias Klauser extern const unsigned int relocate_new_kernel_size;
25c587e4a6SRichard Purdie 
26b2306531SPer Fransson static atomic_t waiting_for_crash_ipi;
27b2306531SPer Fransson 
28c587e4a6SRichard Purdie /*
29c587e4a6SRichard Purdie  * Provide a dummy crash_notes definition while crash dump arrives to arm.
30c587e4a6SRichard Purdie  * This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
31c587e4a6SRichard Purdie  */
32c587e4a6SRichard Purdie 
machine_kexec_prepare(struct kimage * image)33c587e4a6SRichard Purdie int machine_kexec_prepare(struct kimage *image)
34c587e4a6SRichard Purdie {
354cabd1d9SMatthew Leach 	struct kexec_segment *current_segment;
364cabd1d9SMatthew Leach 	__be32 header;
374cabd1d9SMatthew Leach 	int i, err;
384cabd1d9SMatthew Leach 
390d70262aSRussell King 	image->arch.kernel_r2 = image->start - KEXEC_ARM_ZIMAGE_OFFSET
400d70262aSRussell King 				     + KEXEC_ARM_ATAGS_OFFSET;
410d70262aSRussell King 
424cabd1d9SMatthew Leach 	/*
432103f6cbSStephen Warren 	 * Validate that if the current HW supports SMP, then the SW supports
442103f6cbSStephen Warren 	 * and implements CPU hotplug for the current HW. If not, we won't be
452103f6cbSStephen Warren 	 * able to kexec reliably, so fail the prepare operation.
462103f6cbSStephen Warren 	 */
47fee3fd4fSGeert Uytterhoeven 	if (num_possible_cpus() > 1 && platform_can_secondary_boot() &&
48fee3fd4fSGeert Uytterhoeven 	    !platform_can_cpu_hotplug())
492103f6cbSStephen Warren 		return -EINVAL;
502103f6cbSStephen Warren 
512103f6cbSStephen Warren 	/*
524cabd1d9SMatthew Leach 	 * No segment at default ATAGs address. try to locate
534cabd1d9SMatthew Leach 	 * a dtb using magic.
544cabd1d9SMatthew Leach 	 */
554cabd1d9SMatthew Leach 	for (i = 0; i < image->nr_segments; i++) {
564cabd1d9SMatthew Leach 		current_segment = &image->segment[i];
574cabd1d9SMatthew Leach 
580719392aSRussell King 		if (!memblock_is_region_memory(idmap_to_phys(current_segment->mem),
592456f44dSAaro Koskinen 					       current_segment->memsz))
60c564df4dSMatthew Leach 			return -EINVAL;
61c564df4dSMatthew Leach 
624cabd1d9SMatthew Leach 		err = get_user(header, (__be32*)current_segment->buf);
634cabd1d9SMatthew Leach 		if (err)
644cabd1d9SMatthew Leach 			return err;
654cabd1d9SMatthew Leach 
660d70262aSRussell King 		if (header == cpu_to_be32(OF_DT_HEADER))
670d70262aSRussell King 			image->arch.kernel_r2 = current_segment->mem;
684cabd1d9SMatthew Leach 	}
69c587e4a6SRichard Purdie 	return 0;
70c587e4a6SRichard Purdie }
71c587e4a6SRichard Purdie 
machine_kexec_cleanup(struct kimage * image)72c587e4a6SRichard Purdie void machine_kexec_cleanup(struct kimage *image)
73c587e4a6SRichard Purdie {
74c587e4a6SRichard Purdie }
75c587e4a6SRichard Purdie 
machine_crash_nonpanic_core(void * unused)76f07c647cSChen Lifu static void machine_crash_nonpanic_core(void *unused)
77b2306531SPer Fransson {
78b2306531SPer Fransson 	struct pt_regs regs;
79b2306531SPer Fransson 
808fc0b333SGuilherme G. Piccoli 	local_fiq_disable();
818fc0b333SGuilherme G. Piccoli 
821c37963bSRussell King 	crash_setup_regs(&regs, get_irq_regs());
83b2306531SPer Fransson 	printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n",
84b2306531SPer Fransson 	       smp_processor_id());
85b2306531SPer Fransson 	crash_save_cpu(&regs, smp_processor_id());
86b2306531SPer Fransson 	flush_cache_all();
87b2306531SPer Fransson 
884f9b4fb7SVijaya Kumar K 	set_cpu_online(smp_processor_id(), false);
89b2306531SPer Fransson 	atomic_dec(&waiting_for_crash_ipi);
905388a5b8SRussell King 
915388a5b8SRussell King 	while (1) {
92b2306531SPer Fransson 		cpu_relax();
935388a5b8SRussell King 		wfe();
945388a5b8SRussell King 	}
95b2306531SPer Fransson }
96b2306531SPer Fransson 
97*8922ba71SMårten Lindahl static DEFINE_PER_CPU(call_single_data_t, cpu_stop_csd) =
98*8922ba71SMårten Lindahl 	CSD_INIT(machine_crash_nonpanic_core, NULL);
99*8922ba71SMårten Lindahl 
crash_smp_send_stop(void)1002d7b3c64SRussell King void crash_smp_send_stop(void)
1012d7b3c64SRussell King {
1022d7b3c64SRussell King 	static int cpus_stopped;
1032d7b3c64SRussell King 	unsigned long msecs;
104*8922ba71SMårten Lindahl 	call_single_data_t *csd;
105*8922ba71SMårten Lindahl 	int cpu, this_cpu = raw_smp_processor_id();
1062d7b3c64SRussell King 
1072d7b3c64SRussell King 	if (cpus_stopped)
1082d7b3c64SRussell King 		return;
1092d7b3c64SRussell King 
1102d7b3c64SRussell King 	atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
111*8922ba71SMårten Lindahl 	for_each_online_cpu(cpu) {
112*8922ba71SMårten Lindahl 		if (cpu == this_cpu)
113*8922ba71SMårten Lindahl 			continue;
114*8922ba71SMårten Lindahl 
115*8922ba71SMårten Lindahl 		csd = &per_cpu(cpu_stop_csd, cpu);
116*8922ba71SMårten Lindahl 		smp_call_function_single_async(cpu, csd);
117*8922ba71SMårten Lindahl 	}
118*8922ba71SMårten Lindahl 
1192d7b3c64SRussell King 	msecs = 1000; /* Wait at most a second for the other cpus to stop */
1202d7b3c64SRussell King 	while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
1212d7b3c64SRussell King 		mdelay(1);
1222d7b3c64SRussell King 		msecs--;
1232d7b3c64SRussell King 	}
1242d7b3c64SRussell King 	if (atomic_read(&waiting_for_crash_ipi) > 0)
1252d7b3c64SRussell King 		pr_warn("Non-crashing CPUs did not react to IPI\n");
1262d7b3c64SRussell King 
1272d7b3c64SRussell King 	cpus_stopped = 1;
1282d7b3c64SRussell King }
1292d7b3c64SRussell King 
machine_kexec_mask_interrupts(void)1309141a003SWill Deacon static void machine_kexec_mask_interrupts(void)
1319141a003SWill Deacon {
1329141a003SWill Deacon 	unsigned int i;
1339141a003SWill Deacon 	struct irq_desc *desc;
1349141a003SWill Deacon 
1359141a003SWill Deacon 	for_each_irq_desc(i, desc) {
1369141a003SWill Deacon 		struct irq_chip *chip;
1379141a003SWill Deacon 
1389141a003SWill Deacon 		chip = irq_desc_get_chip(desc);
1399141a003SWill Deacon 		if (!chip)
1409141a003SWill Deacon 			continue;
1419141a003SWill Deacon 
1429141a003SWill Deacon 		if (chip->irq_eoi && irqd_irq_inprogress(&desc->irq_data))
1439141a003SWill Deacon 			chip->irq_eoi(&desc->irq_data);
1449141a003SWill Deacon 
1459141a003SWill Deacon 		if (chip->irq_mask)
1469141a003SWill Deacon 			chip->irq_mask(&desc->irq_data);
1479141a003SWill Deacon 
1489141a003SWill Deacon 		if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data))
1499141a003SWill Deacon 			chip->irq_disable(&desc->irq_data);
1509141a003SWill Deacon 	}
1519141a003SWill Deacon }
1529141a003SWill Deacon 
machine_crash_shutdown(struct pt_regs * regs)153c587e4a6SRichard Purdie void machine_crash_shutdown(struct pt_regs *regs)
154c587e4a6SRichard Purdie {
155c6383620SMika Westerberg 	local_irq_disable();
1562d7b3c64SRussell King 	crash_smp_send_stop();
157b2306531SPer Fransson 
158c6383620SMika Westerberg 	crash_save_cpu(regs, smp_processor_id());
1599141a003SWill Deacon 	machine_kexec_mask_interrupts();
160c6383620SMika Westerberg 
1614ed89f22SRussell King 	pr_info("Loading crashdump kernel...\n");
162c587e4a6SRichard Purdie }
163c587e4a6SRichard Purdie 
machine_kexec(struct kimage * image)164c587e4a6SRichard Purdie void machine_kexec(struct kimage *image)
165c587e4a6SRichard Purdie {
1664138323eSRussell King 	unsigned long page_list, reboot_entry_phys;
1674d62e81bSRussell King 	struct kexec_relocate_data *data;
1684138323eSRussell King 	void (*reboot_entry)(void);
169c587e4a6SRichard Purdie 	void *reboot_code_buffer;
170c587e4a6SRichard Purdie 
1712103f6cbSStephen Warren 	/*
1722103f6cbSStephen Warren 	 * This can only happen if machine_shutdown() failed to disable some
1732103f6cbSStephen Warren 	 * CPU, and that can only happen if the checks in
1742103f6cbSStephen Warren 	 * machine_kexec_prepare() were not correct. If this fails, we can't
1752103f6cbSStephen Warren 	 * reliably kexec anyway, so BUG_ON is appropriate.
1762103f6cbSStephen Warren 	 */
1772103f6cbSStephen Warren 	BUG_ON(num_online_cpus() > 1);
178abf015f0SRussell King 
179abf015f0SRussell King 	page_list = image->head & PAGE_MASK;
180abf015f0SRussell King 
181c587e4a6SRichard Purdie 	reboot_code_buffer = page_address(image->control_code_page);
182c587e4a6SRichard Purdie 
183abf015f0SRussell King 	/* copy our kernel relocation code to the control code page */
184e2ccba49SDave Martin 	reboot_entry = fncpy(reboot_code_buffer,
1854138323eSRussell King 			     &relocate_new_kernel,
186e2ccba49SDave Martin 			     relocate_new_kernel_size);
1874138323eSRussell King 
1884d62e81bSRussell King 	data = reboot_code_buffer + relocate_new_kernel_size;
1894d62e81bSRussell King 	data->kexec_start_address = image->start;
1904d62e81bSRussell King 	data->kexec_indirection_page = page_list;
1914d62e81bSRussell King 	data->kexec_mach_type = machine_arch_type;
1924d62e81bSRussell King 	data->kexec_r2 = image->arch.kernel_r2;
1934d62e81bSRussell King 
1944138323eSRussell King 	/* get the identity mapping physical address for the reboot code */
1954138323eSRussell King 	reboot_entry_phys = virt_to_idmap(reboot_entry);
196abf015f0SRussell King 
1974ed89f22SRussell King 	pr_info("Bye!\n");
198c587e4a6SRichard Purdie 
199e2ccba49SDave Martin 	soft_restart(reboot_entry_phys);
200c587e4a6SRichard Purdie }
20156b700fdSLiu Hua 
arch_crash_save_vmcoreinfo(void)20256b700fdSLiu Hua void arch_crash_save_vmcoreinfo(void)
20356b700fdSLiu Hua {
20456b700fdSLiu Hua #ifdef CONFIG_ARM_LPAE
20556b700fdSLiu Hua 	VMCOREINFO_CONFIG(ARM_LPAE);
20656b700fdSLiu Hua #endif
20756b700fdSLiu Hua }
208