~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/kernel/machine_kexec.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * machine_kexec.c - handle transition of Linux booting another kernel
  4  */
  5 
  6 #include <linux/mm.h>
  7 #include <linux/kexec.h>
  8 #include <linux/delay.h>
  9 #include <linux/reboot.h>
 10 #include <linux/io.h>
 11 #include <linux/irq.h>
 12 #include <linux/memblock.h>
 13 #include <linux/of_fdt.h>
 14 #include <asm/mmu_context.h>
 15 #include <asm/cacheflush.h>
 16 #include <asm/kexec-internal.h>
 17 #include <asm/fncpy.h>
 18 #include <asm/mach-types.h>
 19 #include <asm/smp_plat.h>
 20 #include <asm/system_misc.h>
 21 #include <asm/set_memory.h>
 22 
 23 extern void relocate_new_kernel(void);
 24 extern const unsigned int relocate_new_kernel_size;
 25 
 26 static atomic_t waiting_for_crash_ipi;
 27 
 28 /*
 29  * Provide a dummy crash_notes definition while crash dump arrives to arm.
 30  * This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
 31  */
 32 
 33 int machine_kexec_prepare(struct kimage *image)
 34 {
 35         struct kexec_segment *current_segment;
 36         __be32 header;
 37         int i, err;
 38 
 39         image->arch.kernel_r2 = image->start - KEXEC_ARM_ZIMAGE_OFFSET
 40                                      + KEXEC_ARM_ATAGS_OFFSET;
 41 
 42         /*
 43          * Validate that if the current HW supports SMP, then the SW supports
 44          * and implements CPU hotplug for the current HW. If not, we won't be
 45          * able to kexec reliably, so fail the prepare operation.
 46          */
 47         if (num_possible_cpus() > 1 && platform_can_secondary_boot() &&
 48             !platform_can_cpu_hotplug())
 49                 return -EINVAL;
 50 
 51         /*
 52          * No segment at default ATAGs address. try to locate
 53          * a dtb using magic.
 54          */
 55         for (i = 0; i < image->nr_segments; i++) {
 56                 current_segment = &image->segment[i];
 57 
 58                 if (!memblock_is_region_memory(idmap_to_phys(current_segment->mem),
 59                                                current_segment->memsz))
 60                         return -EINVAL;
 61 
 62                 err = get_user(header, (__be32*)current_segment->buf);
 63                 if (err)
 64                         return err;
 65 
 66                 if (header == cpu_to_be32(OF_DT_HEADER))
 67                         image->arch.kernel_r2 = current_segment->mem;
 68         }
 69         return 0;
 70 }
 71 
 72 void machine_kexec_cleanup(struct kimage *image)
 73 {
 74 }
 75 
 76 static void machine_crash_nonpanic_core(void *unused)
 77 {
 78         struct pt_regs regs;
 79 
 80         local_fiq_disable();
 81 
 82         crash_setup_regs(&regs, get_irq_regs());
 83         printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n",
 84                smp_processor_id());
 85         crash_save_cpu(&regs, smp_processor_id());
 86         flush_cache_all();
 87 
 88         set_cpu_online(smp_processor_id(), false);
 89         atomic_dec(&waiting_for_crash_ipi);
 90 
 91         while (1) {
 92                 cpu_relax();
 93                 wfe();
 94         }
 95 }
 96 
 97 static DEFINE_PER_CPU(call_single_data_t, cpu_stop_csd) =
 98         CSD_INIT(machine_crash_nonpanic_core, NULL);
 99 
100 void crash_smp_send_stop(void)
101 {
102         static int cpus_stopped;
103         unsigned long msecs;
104         call_single_data_t *csd;
105         int cpu, this_cpu = raw_smp_processor_id();
106 
107         if (cpus_stopped)
108                 return;
109 
110         atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
111         for_each_online_cpu(cpu) {
112                 if (cpu == this_cpu)
113                         continue;
114 
115                 csd = &per_cpu(cpu_stop_csd, cpu);
116                 smp_call_function_single_async(cpu, csd);
117         }
118 
119         msecs = 1000; /* Wait at most a second for the other cpus to stop */
120         while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
121                 mdelay(1);
122                 msecs--;
123         }
124         if (atomic_read(&waiting_for_crash_ipi) > 0)
125                 pr_warn("Non-crashing CPUs did not react to IPI\n");
126 
127         cpus_stopped = 1;
128 }
129 
130 static void machine_kexec_mask_interrupts(void)
131 {
132         unsigned int i;
133         struct irq_desc *desc;
134 
135         for_each_irq_desc(i, desc) {
136                 struct irq_chip *chip;
137 
138                 chip = irq_desc_get_chip(desc);
139                 if (!chip)
140                         continue;
141 
142                 if (chip->irq_eoi && irqd_irq_inprogress(&desc->irq_data))
143                         chip->irq_eoi(&desc->irq_data);
144 
145                 if (chip->irq_mask)
146                         chip->irq_mask(&desc->irq_data);
147 
148                 if (chip->irq_disable && !irqd_irq_disabled(&desc->irq_data))
149                         chip->irq_disable(&desc->irq_data);
150         }
151 }
152 
153 void machine_crash_shutdown(struct pt_regs *regs)
154 {
155         local_irq_disable();
156         crash_smp_send_stop();
157 
158         crash_save_cpu(regs, smp_processor_id());
159         machine_kexec_mask_interrupts();
160 
161         pr_info("Loading crashdump kernel...\n");
162 }
163 
164 void machine_kexec(struct kimage *image)
165 {
166         unsigned long page_list, reboot_entry_phys;
167         struct kexec_relocate_data *data;
168         void (*reboot_entry)(void);
169         void *reboot_code_buffer;
170 
171         /*
172          * This can only happen if machine_shutdown() failed to disable some
173          * CPU, and that can only happen if the checks in
174          * machine_kexec_prepare() were not correct. If this fails, we can't
175          * reliably kexec anyway, so BUG_ON is appropriate.
176          */
177         BUG_ON(num_online_cpus() > 1);
178 
179         page_list = image->head & PAGE_MASK;
180 
181         reboot_code_buffer = page_address(image->control_code_page);
182 
183         /* copy our kernel relocation code to the control code page */
184         reboot_entry = fncpy(reboot_code_buffer,
185                              &relocate_new_kernel,
186                              relocate_new_kernel_size);
187 
188         data = reboot_code_buffer + relocate_new_kernel_size;
189         data->kexec_start_address = image->start;
190         data->kexec_indirection_page = page_list;
191         data->kexec_mach_type = machine_arch_type;
192         data->kexec_r2 = image->arch.kernel_r2;
193 
194         /* get the identity mapping physical address for the reboot code */
195         reboot_entry_phys = virt_to_idmap(reboot_entry);
196 
197         pr_info("Bye!\n");
198 
199         soft_restart(reboot_entry_phys);
200 }
201 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php