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