2 * arch/s390/kernel/machine_kexec.c
4 * Copyright IBM Corp. 2005,2006
6 * Author(s): Rolf Adelsberger,
7 * Heiko Carstens <heiko.carstens@de.ibm.com>
10 #include <linux/device.h>
12 #include <linux/kexec.h>
13 #include <linux/delay.h>
14 #include <linux/reboot.h>
15 #include <linux/ftrace.h>
17 #include <asm/setup.h>
18 #include <asm/pgtable.h>
19 #include <asm/pgalloc.h>
20 #include <asm/system.h>
22 #include <asm/reset.h>
26 typedef void (*relocate_kernel_t
)(kimage_entry_t
*, unsigned long);
28 extern const unsigned char relocate_kernel
[];
29 extern const unsigned long long relocate_kernel_len
;
31 #ifdef CONFIG_CRASH_DUMP
33 #define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
34 #define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
43 struct nt_fpregset_64
{
52 static void *nt_init(void *buf
, Elf64_Word type
, void *desc
, int d_len
,
58 note
= (Elf64_Nhdr
*)buf
;
59 note
->n_namesz
= strlen(name
) + 1;
60 note
->n_descsz
= d_len
;
62 len
= sizeof(Elf64_Nhdr
);
64 memcpy(buf
+ len
, name
, note
->n_namesz
);
65 len
= ROUNDUP(len
+ note
->n_namesz
, 4);
67 memcpy(buf
+ len
, desc
, note
->n_descsz
);
68 len
= ROUNDUP(len
+ note
->n_descsz
, 4);
70 return PTR_ADD(buf
, len
);
74 * Initialize prstatus note
76 static void *nt_prstatus(void *ptr
, struct save_area
*sa
)
78 struct elf_prstatus nt_prstatus
;
79 static int cpu_nr
= 1;
81 memset(&nt_prstatus
, 0, sizeof(nt_prstatus
));
82 memcpy(&nt_prstatus
.pr_reg
.gprs
, sa
->gp_regs
, sizeof(sa
->gp_regs
));
83 memcpy(&nt_prstatus
.pr_reg
.psw
, sa
->psw
, sizeof(sa
->psw
));
84 memcpy(&nt_prstatus
.pr_reg
.acrs
, sa
->acc_regs
, sizeof(sa
->acc_regs
));
85 nt_prstatus
.pr_pid
= cpu_nr
;
88 return nt_init(ptr
, NT_PRSTATUS
, &nt_prstatus
, sizeof(nt_prstatus
),
93 * Initialize fpregset (floating point) note
95 static void *nt_fpregset(void *ptr
, struct save_area
*sa
)
97 struct nt_fpregset_64 nt_fpregset
;
99 memset(&nt_fpregset
, 0, sizeof(nt_fpregset
));
100 memcpy(&nt_fpregset
.fpc
, &sa
->fp_ctrl_reg
, sizeof(sa
->fp_ctrl_reg
));
101 memcpy(&nt_fpregset
.fprs
, &sa
->fp_regs
, sizeof(sa
->fp_regs
));
103 return nt_init(ptr
, NT_FPREGSET
, &nt_fpregset
, sizeof(nt_fpregset
),
108 * Initialize timer note
110 static void *nt_s390_timer(void *ptr
, struct save_area
*sa
)
112 return nt_init(ptr
, NT_S390_TIMER
, &sa
->timer
, sizeof(sa
->timer
),
113 KEXEC_CORE_NOTE_NAME
);
117 * Initialize TOD clock comparator note
119 static void *nt_s390_tod_cmp(void *ptr
, struct save_area
*sa
)
121 return nt_init(ptr
, NT_S390_TODCMP
, &sa
->clk_cmp
,
122 sizeof(sa
->clk_cmp
), KEXEC_CORE_NOTE_NAME
);
126 * Initialize TOD programmable register note
128 static void *nt_s390_tod_preg(void *ptr
, struct save_area
*sa
)
130 return nt_init(ptr
, NT_S390_TODPREG
, &sa
->tod_reg
,
131 sizeof(sa
->tod_reg
), KEXEC_CORE_NOTE_NAME
);
135 * Initialize control register note
137 static void *nt_s390_ctrs(void *ptr
, struct save_area
*sa
)
139 return nt_init(ptr
, NT_S390_CTRS
, &sa
->ctrl_regs
,
140 sizeof(sa
->ctrl_regs
), KEXEC_CORE_NOTE_NAME
);
144 * Initialize prefix register note
146 static void *nt_s390_prefix(void *ptr
, struct save_area
*sa
)
148 return nt_init(ptr
, NT_S390_PREFIX
, &sa
->pref_reg
,
149 sizeof(sa
->pref_reg
), KEXEC_CORE_NOTE_NAME
);
155 static void nt_final(void *ptr
)
157 memset(ptr
, 0, sizeof(struct elf_note
));
161 * Add create ELF notes for CPU
163 static void add_elf_notes(int cpu
)
165 struct save_area
*sa
= (void *) 4608 + store_prefix();
168 memcpy((void *) (4608UL + sa
->pref_reg
), sa
, sizeof(*sa
));
169 ptr
= (u64
*) per_cpu_ptr(crash_notes
, cpu
);
170 ptr
= nt_prstatus(ptr
, sa
);
171 ptr
= nt_fpregset(ptr
, sa
);
172 ptr
= nt_s390_timer(ptr
, sa
);
173 ptr
= nt_s390_tod_cmp(ptr
, sa
);
174 ptr
= nt_s390_tod_preg(ptr
, sa
);
175 ptr
= nt_s390_ctrs(ptr
, sa
);
176 ptr
= nt_s390_prefix(ptr
, sa
);
181 * Store status of next available physical CPU
183 static int store_status_next(int start_cpu
, int this_cpu
)
185 struct save_area
*sa
= (void *) 4608 + store_prefix();
188 for (cpu
= start_cpu
; cpu
< 65536; cpu
++) {
192 rc
= raw_sigp(cpu
, sigp_stop_and_store_status
);
193 } while (rc
== sigp_busy
);
194 if (rc
!= sigp_order_code_accepted
)
203 * Initialize CPU ELF notes
205 void setup_regs(void)
207 int cpu
, this_cpu
, phys_cpu
= 0, first
= 1;
212 if (!S390_lowcore
.prefixreg_save_area
)
214 for_each_online_cpu(cpu
) {
220 phys_cpu
= store_status_next(phys_cpu
, this_cpu
);
231 static void __machine_kdump(void *image
)
233 int (*start_kdump
)(int) = (void *)((struct kimage
*) image
)->start
;
237 __load_psw_mask(PSW_BASE_BITS
| PSW_DEFAULT_KEY
);
240 disabled_wait((unsigned long) __builtin_return_address(0));
246 * Give back memory to hypervisor before new kdump is loaded
248 static int machine_kexec_prepare_kdump(void)
250 #ifdef CONFIG_CRASH_DUMP
252 diag10_range(PFN_DOWN(crashk_res
.start
),
253 PFN_DOWN(crashk_res
.end
- crashk_res
.start
+ 1));
260 int machine_kexec_prepare(struct kimage
*image
)
262 void *reboot_code_buffer
;
264 /* Can't replace kernel image since it is read-only. */
265 if (ipl_flags
& IPL_NSS_VALID
)
268 if (image
->type
== KEXEC_TYPE_CRASH
)
269 return machine_kexec_prepare_kdump();
271 /* We don't support anything but the default image type for now. */
272 if (image
->type
!= KEXEC_TYPE_DEFAULT
)
275 /* Get the destination where the assembler code should be copied to.*/
276 reboot_code_buffer
= (void *) page_to_phys(image
->control_code_page
);
279 memcpy(reboot_code_buffer
, relocate_kernel
, relocate_kernel_len
);
283 void machine_kexec_cleanup(struct kimage
*image
)
287 void machine_shutdown(void)
291 static void __machine_kexec(void *data
)
293 relocate_kernel_t data_mover
;
294 struct kimage
*image
= data
;
299 data_mover
= (relocate_kernel_t
) page_to_phys(image
->control_code_page
);
301 /* Call the moving routine */
302 (*data_mover
)(&image
->head
, image
->start
);
306 void machine_kexec(struct kimage
*image
)
309 #ifdef CONFIG_CRASH_DUMP
310 if (image
->type
== KEXEC_TYPE_CRASH
) {
311 int (*start_kdump
)(int) = (void *)image
->start
;
313 __arch_local_irq_stnsm(0xfb); /* disable DAT */
315 __arch_local_irq_stosm(0x04); /* enable DAT */
318 smp_switch_to_ipl_cpu(__machine_kdump
, image
);
322 smp_switch_to_ipl_cpu(__machine_kexec
, image
);