]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - arch/ia64/kernel/crash.c
[IA64] IA64 Kexec/kdump
[mirror_ubuntu-hirsute-kernel.git] / arch / ia64 / kernel / crash.c
CommitLineData
a7956113
ZN
1/*
2 * arch/ia64/kernel/crash.c
3 *
4 * Architecture specific (ia64) functions for kexec based crash dumps.
5 *
6 * Created by: Khalid Aziz <khalid.aziz@hp.com>
7 * Copyright (C) 2005 Hewlett-Packard Development Company, L.P.
8 * Copyright (C) 2005 Intel Corp Zou Nan hai <nanhai.zou@intel.com>
9 *
10 */
11#include <linux/smp.h>
12#include <linux/delay.h>
13#include <linux/crash_dump.h>
14#include <linux/bootmem.h>
15#include <linux/kexec.h>
16#include <linux/elfcore.h>
17#include <linux/sysctl.h>
18#include <linux/init.h>
19
20#include <asm/kdebug.h>
21#include <asm/mca.h>
22#include <asm/uaccess.h>
23
24int kdump_status[NR_CPUS];
25atomic_t kdump_cpu_freezed;
26atomic_t kdump_in_progress;
27int kdump_on_init = 1;
28ssize_t
29copy_oldmem_page(unsigned long pfn, char *buf,
30 size_t csize, unsigned long offset, int userbuf)
31{
32 void *vaddr;
33
34 if (!csize)
35 return 0;
36 vaddr = __va(pfn<<PAGE_SHIFT);
37 if (userbuf) {
38 if (copy_to_user(buf, (vaddr + offset), csize)) {
39 return -EFAULT;
40 }
41 } else
42 memcpy(buf, (vaddr + offset), csize);
43 return csize;
44}
45
46static inline Elf64_Word
47*append_elf_note(Elf64_Word *buf, char *name, unsigned type, void *data,
48 size_t data_len)
49{
50 struct elf_note *note = (struct elf_note *)buf;
51 note->n_namesz = strlen(name) + 1;
52 note->n_descsz = data_len;
53 note->n_type = type;
54 buf += (sizeof(*note) + 3)/4;
55 memcpy(buf, name, note->n_namesz);
56 buf += (note->n_namesz + 3)/4;
57 memcpy(buf, data, data_len);
58 buf += (data_len + 3)/4;
59 return buf;
60}
61
62static void
63final_note(void *buf)
64{
65 memset(buf, 0, sizeof(struct elf_note));
66}
67
68extern void ia64_dump_cpu_regs(void *);
69
70static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus);
71
72void
73crash_save_this_cpu()
74{
75 void *buf;
76 unsigned long cfm, sof, sol;
77
78 int cpu = smp_processor_id();
79 struct elf_prstatus *prstatus = &per_cpu(elf_prstatus, cpu);
80
81 elf_greg_t *dst = (elf_greg_t *)&(prstatus->pr_reg);
82 memset(prstatus, 0, sizeof(*prstatus));
83 prstatus->pr_pid = current->pid;
84
85 ia64_dump_cpu_regs(dst);
86 cfm = dst[43];
87 sol = (cfm >> 7) & 0x7f;
88 sof = cfm & 0x7f;
89 dst[46] = (unsigned long)ia64_rse_skip_regs((unsigned long *)dst[46],
90 sof - sol);
91
92 buf = (u64 *) per_cpu_ptr(crash_notes, cpu);
93 if (!buf)
94 return;
95 buf = append_elf_note(buf, "CORE", NT_PRSTATUS, prstatus,
96 sizeof(*prstatus));
97 final_note(buf);
98}
99
100static int
101kdump_wait_cpu_freeze(void)
102{
103 int cpu_num = num_online_cpus() - 1;
104 int timeout = 1000;
105 while(timeout-- > 0) {
106 if (atomic_read(&kdump_cpu_freezed) == cpu_num)
107 return 0;
108 udelay(1000);
109 }
110 return 1;
111}
112
113void
114machine_crash_shutdown(struct pt_regs *pt)
115{
116 /* This function is only called after the system
117 * has paniced or is otherwise in a critical state.
118 * The minimum amount of code to allow a kexec'd kernel
119 * to run successfully needs to happen here.
120 *
121 * In practice this means shooting down the other cpus in
122 * an SMP system.
123 */
124 kexec_disable_iosapic();
125#ifdef CONFIG_SMP
126 kdump_smp_send_stop();
127 if (kdump_wait_cpu_freeze() && kdump_on_init) {
128 //not all cpu response to IPI, send INIT to freeze them
129 kdump_smp_send_init();
130 }
131#endif
132}
133
134static void
135machine_kdump_on_init(void)
136{
137 local_irq_disable();
138 kexec_disable_iosapic();
139 machine_kexec(ia64_kimage);
140}
141
142void
143kdump_cpu_freeze(struct unw_frame_info *info, void *arg)
144{
145 int cpuid;
146 local_irq_disable();
147 cpuid = smp_processor_id();
148 crash_save_this_cpu();
149 current->thread.ksp = (__u64)info->sw - 16;
150 atomic_inc(&kdump_cpu_freezed);
151 kdump_status[cpuid] = 1;
152 mb();
153 if (cpuid == 0) {
154 for (;;)
155 cpu_relax();
156 } else
157 ia64_jump_to_sal(&sal_boot_rendez_state[cpuid]);
158}
159
160static int
161kdump_init_notifier(struct notifier_block *self, unsigned long val, void *data)
162{
163 struct ia64_mca_notify_die *nd;
164 struct die_args *args = data;
165
166 if (!kdump_on_init)
167 return NOTIFY_DONE;
168
169 if (val != DIE_INIT_MONARCH_ENTER &&
170 val != DIE_INIT_SLAVE_ENTER &&
171 val != DIE_MCA_RENDZVOUS_LEAVE &&
172 val != DIE_MCA_MONARCH_LEAVE)
173 return NOTIFY_DONE;
174
175 nd = (struct ia64_mca_notify_die *)args->err;
176 /* Reason code 1 means machine check rendezous*/
177 if ((val == DIE_INIT_MONARCH_ENTER || DIE_INIT_SLAVE_ENTER) &&
178 nd->sos->rv_rc == 1)
179 return NOTIFY_DONE;
180
181 switch (val) {
182 case DIE_INIT_MONARCH_ENTER:
183 machine_kdump_on_init();
184 break;
185 case DIE_INIT_SLAVE_ENTER:
186 unw_init_running(kdump_cpu_freeze, NULL);
187 break;
188 case DIE_MCA_RENDZVOUS_LEAVE:
189 if (atomic_read(&kdump_in_progress))
190 unw_init_running(kdump_cpu_freeze, NULL);
191 break;
192 case DIE_MCA_MONARCH_LEAVE:
193 /* die_register->signr indicate if MCA is recoverable */
194 if (!args->signr)
195 machine_kdump_on_init();
196 break;
197 }
198 return NOTIFY_DONE;
199}
200
201#ifdef CONFIG_SYSCTL
202static ctl_table kdump_on_init_table[] = {
203 {
204 .ctl_name = CTL_UNNUMBERED,
205 .procname = "kdump_on_init",
206 .data = &kdump_on_init,
207 .maxlen = sizeof(int),
208 .mode = 0644,
209 .proc_handler = &proc_dointvec,
210 },
211 { .ctl_name = 0 }
212};
213
214static ctl_table sys_table[] = {
215 {
216 .ctl_name = CTL_KERN,
217 .procname = "kernel",
218 .mode = 0555,
219 .child = kdump_on_init_table,
220 },
221 { .ctl_name = 0 }
222};
223#endif
224
225static int
226machine_crash_setup(void)
227{
228 char *from = strstr(saved_command_line, "elfcorehdr=");
229 static struct notifier_block kdump_init_notifier_nb = {
230 .notifier_call = kdump_init_notifier,
231 };
232 int ret;
233 if (from)
234 elfcorehdr_addr = memparse(from+11, &from);
235 saved_max_pfn = (unsigned long)-1;
236 if((ret = register_die_notifier(&kdump_init_notifier_nb)) != 0)
237 return ret;
238#ifdef CONFIG_SYSCTL
239 register_sysctl_table(sys_table, 0);
240#endif
241 return 0;
242}
243
244__initcall(machine_crash_setup);
245