Merge branch 'drm-patches' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied...
[pandora-kernel.git] / arch / ia64 / kernel / crash.c
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
24 int kdump_status[NR_CPUS];
25 atomic_t kdump_cpu_freezed;
26 atomic_t kdump_in_progress;
27 int kdump_on_init = 1;
28 ssize_t
29 copy_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
46 static 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
62 static void
63 final_note(void *buf)
64 {
65         memset(buf, 0, sizeof(struct elf_note));
66 }
67
68 extern void ia64_dump_cpu_regs(void *);
69
70 static DEFINE_PER_CPU(struct elf_prstatus, elf_prstatus);
71
72 void
73 crash_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
100 static int
101 kdump_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
113 void
114 machine_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
134 static void
135 machine_kdump_on_init(void)
136 {
137         local_irq_disable();
138         kexec_disable_iosapic();
139         machine_kexec(ia64_kimage);
140 }
141
142 void
143 kdump_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
160 static int
161 kdump_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
202 static 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
214 static 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
225 static int
226 machine_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