xref: /openbmc/linux/arch/mips/kernel/machine_kexec.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*40b0b3f8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2583bb86fSNicolas Schichan /*
3583bb86fSNicolas Schichan  * machine_kexec.c for kexec
4583bb86fSNicolas Schichan  * Created by <nschichan@corp.free.fr> on Thu Oct 12 15:15:06 2006
5583bb86fSNicolas Schichan  */
67aa1c8f4SRalf Baechle #include <linux/compiler.h>
7583bb86fSNicolas Schichan #include <linux/kexec.h>
8583bb86fSNicolas Schichan #include <linux/mm.h>
9583bb86fSNicolas Schichan #include <linux/delay.h>
102fe8ea39SDengcheng Zhu #include <linux/libfdt.h>
11583bb86fSNicolas Schichan 
12583bb86fSNicolas Schichan #include <asm/cacheflush.h>
13583bb86fSNicolas Schichan #include <asm/page.h>
14583bb86fSNicolas Schichan 
15c5a69d57STobias Klauser extern const unsigned char relocate_new_kernel[];
161065932fSRalf Baechle extern const size_t relocate_new_kernel_size;
17583bb86fSNicolas Schichan 
18583bb86fSNicolas Schichan extern unsigned long kexec_start_address;
19583bb86fSNicolas Schichan extern unsigned long kexec_indirection_page;
20583bb86fSNicolas Schichan 
2162cac480SDengcheng Zhu static unsigned long reboot_code_buffer;
2262cac480SDengcheng Zhu 
237aa1c8f4SRalf Baechle #ifdef CONFIG_SMP
2462cac480SDengcheng Zhu static void (*relocated_kexec_smp_wait)(void *);
2562cac480SDengcheng Zhu 
267aa1c8f4SRalf Baechle atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
2754c721b8SHidehiro Kawai void (*_crash_smp_send_stop)(void) = NULL;
287aa1c8f4SRalf Baechle #endif
297aa1c8f4SRalf Baechle 
3062cac480SDengcheng Zhu void (*_machine_kexec_shutdown)(void) = NULL;
3162cac480SDengcheng Zhu void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
3262cac480SDengcheng Zhu 
kexec_image_info(const struct kimage * kimage)33856b0f59SMarcin Nowakowski static void kexec_image_info(const struct kimage *kimage)
34856b0f59SMarcin Nowakowski {
35856b0f59SMarcin Nowakowski 	unsigned long i;
36856b0f59SMarcin Nowakowski 
37856b0f59SMarcin Nowakowski 	pr_debug("kexec kimage info:\n");
38856b0f59SMarcin Nowakowski 	pr_debug("  type:        %d\n", kimage->type);
39856b0f59SMarcin Nowakowski 	pr_debug("  start:       %lx\n", kimage->start);
40856b0f59SMarcin Nowakowski 	pr_debug("  head:        %lx\n", kimage->head);
41856b0f59SMarcin Nowakowski 	pr_debug("  nr_segments: %lu\n", kimage->nr_segments);
42856b0f59SMarcin Nowakowski 
43856b0f59SMarcin Nowakowski 	for (i = 0; i < kimage->nr_segments; i++) {
44856b0f59SMarcin Nowakowski 		pr_debug("    segment[%lu]: %016lx - %016lx, 0x%lx bytes, %lu pages\n",
45856b0f59SMarcin Nowakowski 			i,
46856b0f59SMarcin Nowakowski 			kimage->segment[i].mem,
47856b0f59SMarcin Nowakowski 			kimage->segment[i].mem + kimage->segment[i].memsz,
48856b0f59SMarcin Nowakowski 			(unsigned long)kimage->segment[i].memsz,
49856b0f59SMarcin Nowakowski 			(unsigned long)kimage->segment[i].memsz /  PAGE_SIZE);
50856b0f59SMarcin Nowakowski 	}
51856b0f59SMarcin Nowakowski }
52856b0f59SMarcin Nowakowski 
532fe8ea39SDengcheng Zhu #ifdef CONFIG_UHI_BOOT
542fe8ea39SDengcheng Zhu 
uhi_machine_kexec_prepare(struct kimage * kimage)552fe8ea39SDengcheng Zhu static int uhi_machine_kexec_prepare(struct kimage *kimage)
562fe8ea39SDengcheng Zhu {
572fe8ea39SDengcheng Zhu 	int i;
582fe8ea39SDengcheng Zhu 
592fe8ea39SDengcheng Zhu 	/*
602fe8ea39SDengcheng Zhu 	 * In case DTB file is not passed to the new kernel, a flat device
612fe8ea39SDengcheng Zhu 	 * tree will be created by kexec tool. It holds modified command
622fe8ea39SDengcheng Zhu 	 * line for the new kernel.
632fe8ea39SDengcheng Zhu 	 */
642fe8ea39SDengcheng Zhu 	for (i = 0; i < kimage->nr_segments; i++) {
652fe8ea39SDengcheng Zhu 		struct fdt_header fdt;
662fe8ea39SDengcheng Zhu 
672fe8ea39SDengcheng Zhu 		if (kimage->segment[i].memsz <= sizeof(fdt))
682fe8ea39SDengcheng Zhu 			continue;
692fe8ea39SDengcheng Zhu 
702fe8ea39SDengcheng Zhu 		if (copy_from_user(&fdt, kimage->segment[i].buf, sizeof(fdt)))
712fe8ea39SDengcheng Zhu 			continue;
722fe8ea39SDengcheng Zhu 
732fe8ea39SDengcheng Zhu 		if (fdt_check_header(&fdt))
742fe8ea39SDengcheng Zhu 			continue;
752fe8ea39SDengcheng Zhu 
762fe8ea39SDengcheng Zhu 		kexec_args[0] = -2;
772fe8ea39SDengcheng Zhu 		kexec_args[1] = (unsigned long)
782fe8ea39SDengcheng Zhu 			phys_to_virt((unsigned long)kimage->segment[i].mem);
792fe8ea39SDengcheng Zhu 		break;
802fe8ea39SDengcheng Zhu 	}
812fe8ea39SDengcheng Zhu 
822fe8ea39SDengcheng Zhu 	return 0;
832fe8ea39SDengcheng Zhu }
842fe8ea39SDengcheng Zhu 
852fe8ea39SDengcheng Zhu int (*_machine_kexec_prepare)(struct kimage *) = uhi_machine_kexec_prepare;
862fe8ea39SDengcheng Zhu 
872fe8ea39SDengcheng Zhu #else
882fe8ea39SDengcheng Zhu 
892fe8ea39SDengcheng Zhu int (*_machine_kexec_prepare)(struct kimage *) = NULL;
902fe8ea39SDengcheng Zhu 
912fe8ea39SDengcheng Zhu #endif /* CONFIG_UHI_BOOT */
922fe8ea39SDengcheng Zhu 
93583bb86fSNicolas Schichan int
machine_kexec_prepare(struct kimage * kimage)94583bb86fSNicolas Schichan machine_kexec_prepare(struct kimage *kimage)
95583bb86fSNicolas Schichan {
9662cac480SDengcheng Zhu #ifdef CONFIG_SMP
9762cac480SDengcheng Zhu 	if (!kexec_nonboot_cpu_func())
9862cac480SDengcheng Zhu 		return -EINVAL;
9962cac480SDengcheng Zhu #endif
10062cac480SDengcheng Zhu 
101856b0f59SMarcin Nowakowski 	kexec_image_info(kimage);
102856b0f59SMarcin Nowakowski 
1037aa1c8f4SRalf Baechle 	if (_machine_kexec_prepare)
1047aa1c8f4SRalf Baechle 		return _machine_kexec_prepare(kimage);
10562cac480SDengcheng Zhu 
106583bb86fSNicolas Schichan 	return 0;
107583bb86fSNicolas Schichan }
108583bb86fSNicolas Schichan 
109583bb86fSNicolas Schichan void
machine_kexec_cleanup(struct kimage * kimage)110583bb86fSNicolas Schichan machine_kexec_cleanup(struct kimage *kimage)
111583bb86fSNicolas Schichan {
112583bb86fSNicolas Schichan }
113583bb86fSNicolas Schichan 
11462cac480SDengcheng Zhu #ifdef CONFIG_SMP
kexec_shutdown_secondary(void * param)11562cac480SDengcheng Zhu static void kexec_shutdown_secondary(void *param)
11662cac480SDengcheng Zhu {
11762cac480SDengcheng Zhu 	int cpu = smp_processor_id();
11862cac480SDengcheng Zhu 
11962cac480SDengcheng Zhu 	if (!cpu_online(cpu))
12062cac480SDengcheng Zhu 		return;
12162cac480SDengcheng Zhu 
12262cac480SDengcheng Zhu 	/* We won't be sent IPIs any more. */
12362cac480SDengcheng Zhu 	set_cpu_online(cpu, false);
12462cac480SDengcheng Zhu 
12562cac480SDengcheng Zhu 	local_irq_disable();
12662cac480SDengcheng Zhu 	while (!atomic_read(&kexec_ready_to_reboot))
12762cac480SDengcheng Zhu 		cpu_relax();
12862cac480SDengcheng Zhu 
12962cac480SDengcheng Zhu 	kexec_reboot();
13062cac480SDengcheng Zhu 
13162cac480SDengcheng Zhu 	/* NOTREACHED */
13262cac480SDengcheng Zhu }
13362cac480SDengcheng Zhu #endif
13462cac480SDengcheng Zhu 
135583bb86fSNicolas Schichan void
machine_shutdown(void)136583bb86fSNicolas Schichan machine_shutdown(void)
137583bb86fSNicolas Schichan {
1387aa1c8f4SRalf Baechle 	if (_machine_kexec_shutdown)
1397aa1c8f4SRalf Baechle 		_machine_kexec_shutdown();
14062cac480SDengcheng Zhu 
14162cac480SDengcheng Zhu #ifdef CONFIG_SMP
14262cac480SDengcheng Zhu 	smp_call_function(kexec_shutdown_secondary, NULL, 0);
14362cac480SDengcheng Zhu 
14462cac480SDengcheng Zhu 	while (num_online_cpus() > 1) {
14562cac480SDengcheng Zhu 		cpu_relax();
14662cac480SDengcheng Zhu 		mdelay(1);
14762cac480SDengcheng Zhu 	}
14862cac480SDengcheng Zhu #endif
149583bb86fSNicolas Schichan }
150583bb86fSNicolas Schichan 
151583bb86fSNicolas Schichan void
machine_crash_shutdown(struct pt_regs * regs)152583bb86fSNicolas Schichan machine_crash_shutdown(struct pt_regs *regs)
153583bb86fSNicolas Schichan {
1547aa1c8f4SRalf Baechle 	if (_machine_crash_shutdown)
1557aa1c8f4SRalf Baechle 		_machine_crash_shutdown(regs);
1567aa1c8f4SRalf Baechle 	else
1577aa1c8f4SRalf Baechle 		default_machine_crash_shutdown(regs);
158583bb86fSNicolas Schichan }
159583bb86fSNicolas Schichan 
16062cac480SDengcheng Zhu #ifdef CONFIG_SMP
kexec_nonboot_cpu_jump(void)16162cac480SDengcheng Zhu void kexec_nonboot_cpu_jump(void)
16262cac480SDengcheng Zhu {
16362cac480SDengcheng Zhu 	local_flush_icache_range((unsigned long)relocated_kexec_smp_wait,
16462cac480SDengcheng Zhu 				 reboot_code_buffer + relocate_new_kernel_size);
16562cac480SDengcheng Zhu 
16662cac480SDengcheng Zhu 	relocated_kexec_smp_wait(NULL);
16762cac480SDengcheng Zhu }
16862cac480SDengcheng Zhu #endif
16962cac480SDengcheng Zhu 
kexec_reboot(void)17062cac480SDengcheng Zhu void kexec_reboot(void)
17162cac480SDengcheng Zhu {
17262cac480SDengcheng Zhu 	void (*do_kexec)(void) __noreturn;
17362cac480SDengcheng Zhu 
17482689ac6SDengcheng Zhu 	/*
17582689ac6SDengcheng Zhu 	 * We know we were online, and there will be no incoming IPIs at
17682689ac6SDengcheng Zhu 	 * this point. Mark online again before rebooting so that the crash
17782689ac6SDengcheng Zhu 	 * analysis tool will see us correctly.
17882689ac6SDengcheng Zhu 	 */
17982689ac6SDengcheng Zhu 	set_cpu_online(smp_processor_id(), true);
18082689ac6SDengcheng Zhu 
18182689ac6SDengcheng Zhu 	/* Ensure remote CPUs observe that we're online before rebooting. */
18282689ac6SDengcheng Zhu 	smp_mb__after_atomic();
18382689ac6SDengcheng Zhu 
18462cac480SDengcheng Zhu #ifdef CONFIG_SMP
18562cac480SDengcheng Zhu 	if (smp_processor_id() > 0) {
18662cac480SDengcheng Zhu 		/*
18762cac480SDengcheng Zhu 		 * Instead of cpu_relax() or wait, this is needed for kexec
18862cac480SDengcheng Zhu 		 * smp reboot. Kdump usually doesn't require an smp new
18962cac480SDengcheng Zhu 		 * kernel, but kexec may do.
19062cac480SDengcheng Zhu 		 */
19162cac480SDengcheng Zhu 		kexec_nonboot_cpu();
19262cac480SDengcheng Zhu 
19362cac480SDengcheng Zhu 		/* NOTREACHED */
19462cac480SDengcheng Zhu 	}
19562cac480SDengcheng Zhu #endif
19662cac480SDengcheng Zhu 
19762cac480SDengcheng Zhu 	/*
19862cac480SDengcheng Zhu 	 * Make sure we get correct instructions written by the
19962cac480SDengcheng Zhu 	 * machine_kexec() CPU.
20062cac480SDengcheng Zhu 	 */
20162cac480SDengcheng Zhu 	local_flush_icache_range(reboot_code_buffer,
20262cac480SDengcheng Zhu 				 reboot_code_buffer + relocate_new_kernel_size);
20362cac480SDengcheng Zhu 
20462cac480SDengcheng Zhu 	do_kexec = (void *)reboot_code_buffer;
20562cac480SDengcheng Zhu 	do_kexec();
20662cac480SDengcheng Zhu }
2071065932fSRalf Baechle 
208583bb86fSNicolas Schichan void
machine_kexec(struct kimage * image)209583bb86fSNicolas Schichan machine_kexec(struct kimage *image)
210583bb86fSNicolas Schichan {
211583bb86fSNicolas Schichan 	unsigned long entry;
212583bb86fSNicolas Schichan 	unsigned long *ptr;
213583bb86fSNicolas Schichan 
214583bb86fSNicolas Schichan 	reboot_code_buffer =
215583bb86fSNicolas Schichan 	  (unsigned long)page_address(image->control_code_page);
216583bb86fSNicolas Schichan 
2177aa1c8f4SRalf Baechle 	kexec_start_address =
2187aa1c8f4SRalf Baechle 		(unsigned long) phys_to_virt(image->start);
2197aa1c8f4SRalf Baechle 
22091ffaa27SYang Wei 	if (image->type == KEXEC_TYPE_DEFAULT) {
2211065932fSRalf Baechle 		kexec_indirection_page =
2221065932fSRalf Baechle 			(unsigned long) phys_to_virt(image->head & PAGE_MASK);
22391ffaa27SYang Wei 	} else {
22491ffaa27SYang Wei 		kexec_indirection_page = (unsigned long)&image->head;
22591ffaa27SYang Wei 	}
226583bb86fSNicolas Schichan 
227583bb86fSNicolas Schichan 	memcpy((void*)reboot_code_buffer, relocate_new_kernel,
228583bb86fSNicolas Schichan 	       relocate_new_kernel_size);
229583bb86fSNicolas Schichan 
230583bb86fSNicolas Schichan 	/*
231583bb86fSNicolas Schichan 	 * The generic kexec code builds a page list with physical
232583bb86fSNicolas Schichan 	 * addresses. they are directly accessible through KSEG0 (or
233583bb86fSNicolas Schichan 	 * CKSEG0 or XPHYS if on 64bit system), hence the
2347aa1c8f4SRalf Baechle 	 * phys_to_virt() call.
235583bb86fSNicolas Schichan 	 */
236583bb86fSNicolas Schichan 	for (ptr = &image->head; (entry = *ptr) && !(entry &IND_DONE);
237583bb86fSNicolas Schichan 	     ptr = (entry & IND_INDIRECTION) ?
238583bb86fSNicolas Schichan 	       phys_to_virt(entry & PAGE_MASK) : ptr + 1) {
239583bb86fSNicolas Schichan 		if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION ||
240583bb86fSNicolas Schichan 		    *ptr & IND_DESTINATION)
2411065932fSRalf Baechle 			*ptr = (unsigned long) phys_to_virt(*ptr);
242583bb86fSNicolas Schichan 	}
243583bb86fSNicolas Schichan 
244dc57aaf9SDengcheng Zhu 	/* Mark offline BEFORE disabling local irq. */
245dc57aaf9SDengcheng Zhu 	set_cpu_online(smp_processor_id(), false);
246dc57aaf9SDengcheng Zhu 
247583bb86fSNicolas Schichan 	/*
248583bb86fSNicolas Schichan 	 * we do not want to be bothered.
249583bb86fSNicolas Schichan 	 */
250583bb86fSNicolas Schichan 	local_irq_disable();
251583bb86fSNicolas Schichan 
2521065932fSRalf Baechle 	printk("Will call new kernel at %08lx\n", image->start);
253583bb86fSNicolas Schichan 	printk("Bye ...\n");
25462cac480SDengcheng Zhu 	/* Make reboot code buffer available to the boot CPU. */
25597ce9a8dSNicolas Schichan 	__flush_cache_all();
2567aa1c8f4SRalf Baechle #ifdef CONFIG_SMP
2577aa1c8f4SRalf Baechle 	/* All secondary cpus now may jump to kexec_wait cycle */
2587aa1c8f4SRalf Baechle 	relocated_kexec_smp_wait = reboot_code_buffer +
2597aa1c8f4SRalf Baechle 		(void *)(kexec_smp_wait - relocate_new_kernel);
2607aa1c8f4SRalf Baechle 	smp_wmb();
2617aa1c8f4SRalf Baechle 	atomic_set(&kexec_ready_to_reboot, 1);
2627aa1c8f4SRalf Baechle #endif
26362cac480SDengcheng Zhu 	kexec_reboot();
264583bb86fSNicolas Schichan }
265