This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / dump / dump_i386.c
1 /*
2  * Architecture specific (i386) functions for Linux crash dumps.
3  *
4  * Created by: Matt Robinson (yakker@sgi.com)
5  *
6  * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
7  *
8  * 2.3 kernel modifications by: Matt D. Robinson (yakker@turbolinux.com)
9  * Copyright 2000 TurboLinux, Inc.  All rights reserved.
10  * 
11  * This code is released under version 2 of the GNU GPL.
12  */
13
14 /*
15  * The hooks for dumping the kernel virtual memory to disk are in this
16  * file.  Any time a modification is made to the virtual memory mechanism,
17  * these routines must be changed to use the new mechanisms.
18  */
19 #include <linux/init.h>
20 #include <linux/types.h>
21 #include <linux/kernel.h>
22 #include <linux/smp.h>
23 #include <linux/fs.h>
24 #include <linux/vmalloc.h>
25 #include <linux/mm.h>
26 #include <linux/dump.h>
27 #include "dump_methods.h"
28 #include <linux/irq.h>
29
30 #include <asm/processor.h>
31 #include <asm/e820.h>
32 #include <asm/hardirq.h>
33 #include <asm/nmi.h>
34
35 static __s32         saved_irq_count;   /* saved preempt_count() flags */
36
37 static int
38 alloc_dha_stack(void)
39 {
40         int i;
41         void *ptr;
42         
43         if (dump_header_asm.dha_stack[0])
44                 return 0;
45
46         ptr = vmalloc(THREAD_SIZE * num_online_cpus());
47         if (!ptr) {
48                 printk("vmalloc for dha_stacks failed\n");
49                 return -ENOMEM;
50         }
51
52         for (i = 0; i < num_online_cpus(); i++) {
53                 dump_header_asm.dha_stack[i] = (u32)((unsigned long)ptr +
54                                 (i * THREAD_SIZE));
55         }
56         return 0;
57 }
58
59 static int
60 free_dha_stack(void) 
61 {
62         if (dump_header_asm.dha_stack[0]) {
63                 vfree((void *)dump_header_asm.dha_stack[0]);    
64                 dump_header_asm.dha_stack[0] = 0;
65         }
66         return 0;
67 }
68
69
70 void 
71 __dump_save_regs(struct pt_regs *dest_regs, const struct pt_regs *regs)
72 {
73         *dest_regs = *regs;
74
75         /* In case of panic dumps, we collects regs on entry to panic.
76          * so, we shouldn't 'fix' ssesp here again. But it is hard to
77          * tell just looking at regs whether ssesp need fixing. We make
78          * this decision by looking at xss in regs. If we have better
79          * means to determine that ssesp are valid (by some flag which
80          * tells that we are here due to panic dump), then we can use
81          * that instead of this kludge.
82          */
83         if (!user_mode(regs)) {
84                 if ((0xffff & regs->xss) == __KERNEL_DS) 
85                         /* already fixed up */
86                         return;
87                 dest_regs->esp = (unsigned long)&(regs->esp);
88                 __asm__ __volatile__ ("movw %%ss, %%ax;"
89                         :"=a"(dest_regs->xss));
90         }
91 }
92
93
94 #ifdef CONFIG_SMP
95 extern cpumask_t irq_affinity[];
96 extern irq_desc_t irq_desc[];
97 extern void dump_send_ipi(void);
98
99 static int dump_expect_ipi[NR_CPUS];
100 static atomic_t waiting_for_dump_ipi;
101 static cpumask_t saved_affinity[NR_IRQS];
102
103 extern void stop_this_cpu(void *); /* exported by i386 kernel */
104
105 static int
106 dump_nmi_callback(struct pt_regs *regs, int cpu) 
107 {
108         if (!dump_expect_ipi[cpu])
109                 return 0;
110
111         dump_expect_ipi[cpu] = 0;
112         
113         dump_save_this_cpu(regs);
114         atomic_dec(&waiting_for_dump_ipi);
115
116  level_changed:
117         switch (dump_silence_level) {
118         case DUMP_HARD_SPIN_CPUS:       /* Spin until dump is complete */
119                 while (dump_oncpu) {
120                         barrier();      /* paranoia */
121                         if (dump_silence_level != DUMP_HARD_SPIN_CPUS)
122                                 goto level_changed;
123
124                         cpu_relax();    /* kill time nicely */
125                 }
126                 break;
127
128         case DUMP_HALT_CPUS:            /* Execute halt */
129                 stop_this_cpu(NULL);
130                 break;
131                 
132         case DUMP_SOFT_SPIN_CPUS:
133                 /* Mark the task so it spins in schedule */
134                 set_tsk_thread_flag(current, TIF_NEED_RESCHED);
135                 break;
136         }
137
138         return 1;
139 }
140
141 /* save registers on other processors */
142 void 
143 __dump_save_other_cpus(void)
144 {
145         int i, cpu = smp_processor_id();
146         int other_cpus = num_online_cpus()-1;
147         
148         if (other_cpus > 0) {
149                 atomic_set(&waiting_for_dump_ipi, other_cpus);
150
151                 for (i = 0; i < NR_CPUS; i++) {
152                         dump_expect_ipi[i] = (i != cpu && cpu_online(i));
153                 }
154
155                 /* short circuit normal NMI handling temporarily */
156                 set_nmi_callback(dump_nmi_callback);
157                 wmb();
158
159                 dump_send_ipi();
160                 /* may be we dont need to wait for NMI to be processed. 
161                    just write out the header at the end of dumping, if
162                    this IPI is not processed until then, there probably
163                    is a problem and we just fail to capture state of 
164                    other cpus. */
165                 while(atomic_read(&waiting_for_dump_ipi) > 0) {
166                         cpu_relax();
167                 }
168
169                 unset_nmi_callback();
170         }
171 }
172
173 /*
174  * Routine to save the old irq affinities and change affinities of all irqs to
175  * the dumping cpu.
176  */
177 static void 
178 set_irq_affinity(void)
179 {
180         int i;
181         cpumask_t cpu = CPU_MASK_NONE;
182
183         cpu_set(smp_processor_id(), cpu);
184         memcpy(saved_affinity, irq_affinity, NR_IRQS * sizeof(unsigned long));
185         for (i = 0; i < NR_IRQS; i++) {
186                 if (irq_desc[i].handler == NULL)
187                         continue;
188                 irq_affinity[i] = cpu;
189                 if (irq_desc[i].handler->set_affinity != NULL)
190                         irq_desc[i].handler->set_affinity(i, irq_affinity[i]);
191         }
192 }
193
194 /*
195  * Restore old irq affinities.
196  */
197 static void 
198 reset_irq_affinity(void)
199 {
200         int i;
201
202         memcpy(irq_affinity, saved_affinity, NR_IRQS * sizeof(unsigned long));
203         for (i = 0; i < NR_IRQS; i++) {
204                 if (irq_desc[i].handler == NULL)
205                         continue;
206                 if (irq_desc[i].handler->set_affinity != NULL)
207                         irq_desc[i].handler->set_affinity(i, saved_affinity[i]);
208         }
209 }
210
211 #else /* !CONFIG_SMP */
212 #define set_irq_affinity()      do { } while (0)
213 #define reset_irq_affinity()    do { } while (0)
214 #define save_other_cpu_states() do { } while (0)
215 #endif /* !CONFIG_SMP */
216
217 /* 
218  * Kludge - dump from interrupt context is unreliable (Fixme)
219  *
220  * We do this so that softirqs initiated for dump i/o 
221  * get processed and we don't hang while waiting for i/o
222  * to complete or in any irq synchronization attempt.
223  *
224  * This is not quite legal of course, as it has the side 
225  * effect of making all interrupts & softirqs triggered 
226  * while dump is in progress complete before currently 
227  * pending softirqs and the currently executing interrupt 
228  * code. 
229  */
230 static inline void
231 irq_bh_save(void)
232 {
233         saved_irq_count = irq_count();
234         preempt_count() &= ~(HARDIRQ_MASK|SOFTIRQ_MASK);
235 }
236
237 static inline void
238 irq_bh_restore(void)
239 {
240         preempt_count() |= saved_irq_count;
241 }
242
243 /*
244  * Name: __dump_irq_enable
245  * Func: Reset system so interrupts are enabled.
246  *       This is used for dump methods that require interrupts
247  *       Eventually, all methods will have interrupts disabled
248  *       and this code can be removed.
249  *
250  *     Change irq affinities
251  *     Re-enable interrupts
252  */
253 int
254 __dump_irq_enable(void)
255 {
256         set_irq_affinity();
257         irq_bh_save();
258         local_irq_enable();
259         return 0;
260 }
261
262 /*
263  * Name: __dump_irq_restore
264  * Func: Resume the system state in an architecture-specific way.
265
266  */
267 void 
268 __dump_irq_restore(void)
269 {
270         local_irq_disable();
271         reset_irq_affinity();
272         irq_bh_restore();
273 }
274
275 /*
276  * Name: __dump_configure_header()
277  * Func: Meant to fill in arch specific header fields except per-cpu state
278  * already captured via __dump_save_context for all CPUs.
279  */
280 int
281 __dump_configure_header(const struct pt_regs *regs)
282 {
283         return (0);
284 }
285
286 /*
287  * Name: __dump_init()
288  * Func: Initialize the dumping routine process.
289  */
290 void
291 __dump_init(uint64_t local_memory_start)
292 {
293         return;
294 }
295
296 /*
297  * Name: __dump_open()
298  * Func: Open the dump device (architecture specific).
299  */
300 void
301 __dump_open(void)
302 {
303         alloc_dha_stack();
304 }
305
306 /*
307  * Name: __dump_cleanup()
308  * Func: Free any architecture specific data structures. This is called
309  *       when the dump module is being removed.
310  */
311 void
312 __dump_cleanup(void)
313 {
314         free_dha_stack();
315 }
316
317 extern int pfn_is_ram(unsigned long);
318
319 /*
320  * Name: __dump_page_valid()
321  * Func: Check if page is valid to dump.
322  */ 
323 int 
324 __dump_page_valid(unsigned long index)
325 {
326         if (!pfn_valid(index))
327                 return 0;
328
329         return pfn_is_ram(index);
330 }
331
332 /* 
333  * Name: manual_handle_crashdump()
334  * Func: Interface for the lkcd dump command. Calls dump_execute()
335  */
336 int
337 manual_handle_crashdump(void) {
338
339         struct pt_regs regs;
340         
341         get_current_regs(&regs);
342         dump_execute("manual", &regs);
343         return 0;
344 }