]>
Commit | Line | Data |
---|---|---|
cf13f0ea | 1 | /* |
a53c8fab | 2 | * Copyright IBM Corp. 2005, 2011 |
cf13f0ea | 3 | * |
c6b5b847 HC |
4 | * Author(s): Rolf Adelsberger, |
5 | * Heiko Carstens <heiko.carstens@de.ibm.com> | |
60a0c68d | 6 | * Michael Holzheu <holzheu@linux.vnet.ibm.com> |
cf13f0ea HC |
7 | */ |
8 | ||
cf13f0ea HC |
9 | #include <linux/device.h> |
10 | #include <linux/mm.h> | |
11 | #include <linux/kexec.h> | |
12 | #include <linux/delay.h> | |
2b67fc46 | 13 | #include <linux/reboot.h> |
6966727d | 14 | #include <linux/ftrace.h> |
3ab121ab | 15 | #include <linux/debug_locks.h> |
b66ac63e | 16 | #include <linux/suspend.h> |
a386fba2 HC |
17 | #include <asm/cio.h> |
18 | #include <asm/setup.h> | |
cf13f0ea HC |
19 | #include <asm/pgtable.h> |
20 | #include <asm/pgalloc.h> | |
a386fba2 | 21 | #include <asm/smp.h> |
15e9b586 | 22 | #include <asm/reset.h> |
9c9c1761 | 23 | #include <asm/ipl.h> |
60a0c68d | 24 | #include <asm/diag.h> |
6b563d8c | 25 | #include <asm/elf.h> |
60a0c68d | 26 | #include <asm/asm-offsets.h> |
a9fbf1a5 | 27 | #include <asm/os_info.h> |
a62bc073 | 28 | #include <asm/switch_to.h> |
cf13f0ea | 29 | |
c6b5b847 | 30 | typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long); |
cf13f0ea | 31 | |
2efe55a9 TK |
32 | extern const unsigned char relocate_kernel[]; |
33 | extern const unsigned long long relocate_kernel_len; | |
cf13f0ea | 34 | |
60a0c68d MH |
35 | #ifdef CONFIG_CRASH_DUMP |
36 | ||
b66ac63e MH |
37 | /* |
38 | * PM notifier callback for kdump | |
39 | */ | |
40 | static int machine_kdump_pm_cb(struct notifier_block *nb, unsigned long action, | |
41 | void *ptr) | |
42 | { | |
43 | switch (action) { | |
44 | case PM_SUSPEND_PREPARE: | |
45 | case PM_HIBERNATION_PREPARE: | |
7a0058ec XP |
46 | if (kexec_crash_image) |
47 | arch_kexec_unprotect_crashkres(); | |
b66ac63e MH |
48 | break; |
49 | case PM_POST_SUSPEND: | |
50 | case PM_POST_HIBERNATION: | |
7a0058ec XP |
51 | if (kexec_crash_image) |
52 | arch_kexec_protect_crashkres(); | |
b66ac63e MH |
53 | break; |
54 | default: | |
55 | return NOTIFY_DONE; | |
56 | } | |
57 | return NOTIFY_OK; | |
58 | } | |
59 | ||
60 | static int __init machine_kdump_pm_init(void) | |
61 | { | |
62 | pm_notifier(machine_kdump_pm_cb, 0); | |
7a0058ec XP |
63 | /* Create initial mapping for crashkernel memory */ |
64 | arch_kexec_unprotect_crashkres(); | |
b66ac63e MH |
65 | return 0; |
66 | } | |
67 | arch_initcall(machine_kdump_pm_init); | |
60a0c68d MH |
68 | |
69 | /* | |
1a36a39e MS |
70 | * Reset the system, copy boot CPU registers to absolute zero, |
71 | * and jump to the kdump image | |
60a0c68d MH |
72 | */ |
73 | static void __do_machine_kdump(void *image) | |
74 | { | |
1a36a39e MS |
75 | int (*start_kdump)(int); |
76 | unsigned long prefix; | |
77 | ||
78 | /* store_status() saved the prefix register to lowcore */ | |
79 | prefix = (unsigned long) S390_lowcore.prefixreg_save_area; | |
80 | ||
81 | /* Now do the reset */ | |
82 | s390_reset_system(); | |
83 | ||
84 | /* | |
85 | * Copy dump CPU store status info to absolute zero. | |
86 | * This need to be done *after* s390_reset_system set the | |
87 | * prefix register of this CPU to zero | |
88 | */ | |
89 | memcpy((void *) __LC_FPREGS_SAVE_AREA, | |
90 | (void *)(prefix + __LC_FPREGS_SAVE_AREA), 512); | |
60a0c68d | 91 | |
fa7c0043 | 92 | __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA); |
1a36a39e | 93 | start_kdump = (void *)((struct kimage *) image)->start; |
60a0c68d | 94 | start_kdump(1); |
1a36a39e MS |
95 | |
96 | /* Die if start_kdump returns */ | |
97 | disabled_wait((unsigned long) __builtin_return_address(0)); | |
98 | } | |
99 | ||
100 | /* | |
101 | * Start kdump: create a LGR log entry, store status of all CPUs and | |
102 | * branch to __do_machine_kdump. | |
103 | */ | |
104 | static noinline void __machine_kdump(void *image) | |
105 | { | |
106 | int this_cpu, cpu; | |
107 | ||
108 | lgr_info_log(); | |
109 | /* Get status of the other CPUs */ | |
110 | this_cpu = smp_find_processor_id(stap()); | |
111 | for_each_online_cpu(cpu) { | |
112 | if (cpu == this_cpu) | |
113 | continue; | |
114 | if (smp_store_status(cpu)) | |
115 | continue; | |
116 | } | |
117 | /* Store status of the boot CPU */ | |
118 | if (MACHINE_HAS_VX) | |
119 | save_vx_regs((void *) &S390_lowcore.vector_save_area); | |
120 | /* | |
121 | * To create a good backchain for this CPU in the dump store_status | |
122 | * is passed the address of a function. The address is saved into | |
123 | * the PSW save area of the boot CPU and the function is invoked as | |
124 | * a tail call of store_status. The backchain in the dump will look | |
125 | * like this: | |
126 | * restart_int_handler -> __machine_kexec -> __do_machine_kdump | |
127 | * The call to store_status() will not return. | |
128 | */ | |
129 | store_status(__do_machine_kdump, image); | |
60a0c68d | 130 | } |
10ad34bc | 131 | #endif |
60a0c68d MH |
132 | |
133 | /* | |
134 | * Check if kdump checksums are valid: We call purgatory with parameter "0" | |
135 | */ | |
136 | static int kdump_csum_valid(struct kimage *image) | |
137 | { | |
138 | #ifdef CONFIG_CRASH_DUMP | |
139 | int (*start_kdump)(int) = (void *)image->start; | |
140 | int rc; | |
141 | ||
142 | __arch_local_irq_stnsm(0xfb); /* disable DAT */ | |
143 | rc = start_kdump(0); | |
144 | __arch_local_irq_stosm(0x04); /* enable DAT */ | |
145 | return rc ? 0 : -EINVAL; | |
146 | #else | |
147 | return -EINVAL; | |
148 | #endif | |
149 | } | |
150 | ||
7a0058ec XP |
151 | #ifdef CONFIG_CRASH_DUMP |
152 | ||
dab7a7b1 MH |
153 | /* |
154 | * Map or unmap crashkernel memory | |
155 | */ | |
156 | static void crash_map_pages(int enable) | |
157 | { | |
158 | unsigned long size = resource_size(&crashk_res); | |
159 | ||
160 | BUG_ON(crashk_res.start % KEXEC_CRASH_MEM_ALIGN || | |
161 | size % KEXEC_CRASH_MEM_ALIGN); | |
162 | if (enable) | |
163 | vmem_add_mapping(crashk_res.start, size); | |
a9fbf1a5 | 164 | else { |
dab7a7b1 | 165 | vmem_remove_mapping(crashk_res.start, size); |
a9fbf1a5 MH |
166 | if (size) |
167 | os_info_crashkernel_add(crashk_res.start, size); | |
168 | else | |
169 | os_info_crashkernel_add(0, 0); | |
170 | } | |
dab7a7b1 MH |
171 | } |
172 | ||
173 | /* | |
7a0058ec | 174 | * Unmap crashkernel memory |
dab7a7b1 | 175 | */ |
7a0058ec | 176 | void arch_kexec_protect_crashkres(void) |
dab7a7b1 | 177 | { |
7a0058ec XP |
178 | if (crashk_res.end) |
179 | crash_map_pages(0); | |
dab7a7b1 MH |
180 | } |
181 | ||
182 | /* | |
7a0058ec | 183 | * Map crashkernel memory |
dab7a7b1 | 184 | */ |
7a0058ec | 185 | void arch_kexec_unprotect_crashkres(void) |
dab7a7b1 | 186 | { |
7a0058ec XP |
187 | if (crashk_res.end) |
188 | crash_map_pages(1); | |
dab7a7b1 MH |
189 | } |
190 | ||
7a0058ec XP |
191 | #endif |
192 | ||
60a0c68d MH |
193 | /* |
194 | * Give back memory to hypervisor before new kdump is loaded | |
195 | */ | |
196 | static int machine_kexec_prepare_kdump(void) | |
197 | { | |
198 | #ifdef CONFIG_CRASH_DUMP | |
199 | if (MACHINE_IS_VM) | |
200 | diag10_range(PFN_DOWN(crashk_res.start), | |
201 | PFN_DOWN(crashk_res.end - crashk_res.start + 1)); | |
202 | return 0; | |
203 | #else | |
204 | return -EINVAL; | |
205 | #endif | |
206 | } | |
207 | ||
c6b5b847 | 208 | int machine_kexec_prepare(struct kimage *image) |
cf13f0ea | 209 | { |
c6b5b847 | 210 | void *reboot_code_buffer; |
cf13f0ea | 211 | |
9c9c1761 HC |
212 | /* Can't replace kernel image since it is read-only. */ |
213 | if (ipl_flags & IPL_NSS_VALID) | |
d978386e | 214 | return -EOPNOTSUPP; |
9c9c1761 | 215 | |
60a0c68d MH |
216 | if (image->type == KEXEC_TYPE_CRASH) |
217 | return machine_kexec_prepare_kdump(); | |
218 | ||
cf13f0ea HC |
219 | /* We don't support anything but the default image type for now. */ |
220 | if (image->type != KEXEC_TYPE_DEFAULT) | |
221 | return -EINVAL; | |
222 | ||
223 | /* Get the destination where the assembler code should be copied to.*/ | |
c6b5b847 | 224 | reboot_code_buffer = (void *) page_to_phys(image->control_code_page); |
cf13f0ea HC |
225 | |
226 | /* Then copy it */ | |
c6b5b847 | 227 | memcpy(reboot_code_buffer, relocate_kernel, relocate_kernel_len); |
cf13f0ea HC |
228 | return 0; |
229 | } | |
230 | ||
c6b5b847 | 231 | void machine_kexec_cleanup(struct kimage *image) |
cf13f0ea HC |
232 | { |
233 | } | |
234 | ||
60a0c68d MH |
235 | void arch_crash_save_vmcoreinfo(void) |
236 | { | |
237 | VMCOREINFO_SYMBOL(lowcore_ptr); | |
7fe7a18c | 238 | VMCOREINFO_SYMBOL(high_memory); |
60a0c68d MH |
239 | VMCOREINFO_LENGTH(lowcore_ptr, NR_CPUS); |
240 | } | |
241 | ||
c6b5b847 | 242 | void machine_shutdown(void) |
cf13f0ea | 243 | { |
cf13f0ea HC |
244 | } |
245 | ||
48a8ca03 HC |
246 | void machine_crash_shutdown(struct pt_regs *regs) |
247 | { | |
248 | } | |
249 | ||
60a0c68d MH |
250 | /* |
251 | * Do normal kexec | |
252 | */ | |
253 | static void __do_machine_kexec(void *data) | |
cf13f0ea | 254 | { |
cf13f0ea | 255 | relocate_kernel_t data_mover; |
2c2df118 | 256 | struct kimage *image = data; |
cf13f0ea | 257 | |
1a36a39e | 258 | s390_reset_system(); |
c6b5b847 | 259 | data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page); |
cf13f0ea HC |
260 | |
261 | /* Call the moving routine */ | |
c6b5b847 | 262 | (*data_mover)(&image->head, image->start); |
1a36a39e MS |
263 | |
264 | /* Die if kexec returns */ | |
265 | disabled_wait((unsigned long) __builtin_return_address(0)); | |
cf13f0ea | 266 | } |
2c2df118 | 267 | |
60a0c68d MH |
268 | /* |
269 | * Reset system and call either kdump or normal kexec | |
270 | */ | |
271 | static void __machine_kexec(void *data) | |
272 | { | |
fa7c0043 | 273 | __arch_local_irq_stosm(0x04); /* enable DAT */ |
60a0c68d | 274 | pfault_fini(); |
3ab121ab MH |
275 | tracing_off(); |
276 | debug_locks_off(); | |
10ad34bc | 277 | #ifdef CONFIG_CRASH_DUMP |
1a36a39e MS |
278 | if (((struct kimage *) data)->type == KEXEC_TYPE_CRASH) |
279 | __machine_kdump(data); | |
10ad34bc | 280 | #endif |
1a36a39e | 281 | __do_machine_kexec(data); |
60a0c68d MH |
282 | } |
283 | ||
284 | /* | |
285 | * Do either kdump or normal kexec. In case of kdump we first ask | |
286 | * purgatory, if kdump checksums are valid. | |
287 | */ | |
2c2df118 HC |
288 | void machine_kexec(struct kimage *image) |
289 | { | |
60a0c68d MH |
290 | if (image->type == KEXEC_TYPE_CRASH && !kdump_csum_valid(image)) |
291 | return; | |
6966727d | 292 | tracer_disable(); |
2c2df118 | 293 | smp_send_stop(); |
8b646bd7 | 294 | smp_call_ipl_cpu(__machine_kexec, image); |
2c2df118 | 295 | } |