ia64/xen-unstable

view xen/common/kexec.c @ 12996:de69059a1f0e

[XEN] Kexec: Disable crash keyhandler when no crash kernel is loaded.

Also make the crash dump key capital-C rather than lowercase to make it harder
to hit by mistake.

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
author Ian Campbell <ian.campbell@xensource.com>
date Wed Dec 13 11:29:09 2006 +0000 (2006-12-13)
parents 58284e749407
children 6a28bfc1a940
line source
1 /******************************************************************************
2 * kexec.c - Achitecture independent kexec code for Xen
3 *
4 * Xen port written by:
5 * - Simon 'Horms' Horman <horms@verge.net.au>
6 * - Magnus Damm <magnus@valinux.co.jp>
7 */
9 #include <asm/kexec.h>
10 #include <xen/lib.h>
11 #include <xen/ctype.h>
12 #include <xen/errno.h>
13 #include <xen/guest_access.h>
14 #include <xen/sched.h>
15 #include <xen/types.h>
16 #include <xen/kexec.h>
17 #include <xen/keyhandler.h>
18 #include <public/kexec.h>
19 #include <xen/cpumask.h>
20 #include <asm/atomic.h>
21 #include <xen/spinlock.h>
22 #include <xen/version.h>
23 #include <public/elfnote.h>
25 DEFINE_PER_CPU (crash_note_t, crash_notes);
26 cpumask_t crash_saved_cpus;
27 int crashing_cpu;
29 xen_kexec_image_t kexec_image[KEXEC_IMAGE_NR];
31 #define KEXEC_FLAG_DEFAULT_POS (KEXEC_IMAGE_NR + 0)
32 #define KEXEC_FLAG_CRASH_POS (KEXEC_IMAGE_NR + 1)
33 #define KEXEC_FLAG_IN_PROGRESS (KEXEC_IMAGE_NR + 2)
35 unsigned long kexec_flags = 0; /* the lowest bits are for KEXEC_IMAGE... */
37 spinlock_t kexec_lock = SPIN_LOCK_UNLOCKED;
39 xen_kexec_reserve_t kexec_crash_area;
41 static void __init parse_crashkernel(char *str)
42 {
43 unsigned long start, size;
45 size = parse_size_and_unit(str, &str);
46 if ( *str == '@' )
47 start = parse_size_and_unit(str+1, NULL);
48 else
49 start = 0;
51 if ( start && size )
52 {
53 kexec_crash_area.start = start;
54 kexec_crash_area.size = size;
55 }
56 }
57 custom_param("crashkernel", parse_crashkernel);
59 static void one_cpu_only(void)
60 {
61 /* Only allow the first cpu to continue - force other cpus to spin */
62 if ( test_and_set_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) )
63 {
64 while (1);
65 }
66 }
68 /* Save the registers in the per-cpu crash note buffer */
70 void machine_crash_save_cpu(void)
71 {
72 int cpu = smp_processor_id();
73 crash_note_t *cntp;
75 if ( !cpu_test_and_set(cpu, crash_saved_cpus) )
76 {
77 cntp = &per_cpu(crash_notes, cpu);
78 elf_core_save_regs(&cntp->core.desc.desc.pr_reg,
79 &cntp->xen_regs.desc.desc);
81 /* setup crash "CORE" note */
82 setup_crash_note(cntp, core, CORE_STR, CORE_STR_LEN, NT_PRSTATUS);
84 /* setup crash note "Xen", XEN_ELFNOTE_CRASH_REGS */
85 setup_crash_note(cntp, xen_regs, XEN_STR, XEN_STR_LEN,
86 XEN_ELFNOTE_CRASH_REGS);
87 }
88 }
90 /* Setup the single Xen specific info crash note */
92 crash_xen_info_t *machine_crash_save_info(void)
93 {
94 int cpu = smp_processor_id();
95 crash_note_t *cntp;
96 crash_xen_info_t *info;
98 BUG_ON(!cpu_test_and_set(cpu, crash_saved_cpus));
100 cntp = &per_cpu(crash_notes, cpu);
102 /* setup crash note "Xen", XEN_ELFNOTE_CRASH_INFO */
103 setup_crash_note(cntp, xen_info, XEN_STR, XEN_STR_LEN,
104 XEN_ELFNOTE_CRASH_INFO);
106 info = &cntp->xen_info.desc.desc;
108 info->xen_major_version = xen_major_version();
109 info->xen_minor_version = xen_minor_version();
110 info->xen_extra_version = __pa(xen_extra_version());
111 info->xen_changeset = __pa(xen_changeset());
112 info->xen_compiler = __pa(xen_compiler());
113 info->xen_compile_date = __pa(xen_compile_date());
114 info->xen_compile_time = __pa(xen_compile_time());
115 info->tainted = tainted;
117 return info;
118 }
120 void machine_crash_kexec(void)
121 {
122 int pos;
123 xen_kexec_image_t *image;
125 one_cpu_only();
127 machine_crash_save_cpu();
128 crashing_cpu = smp_processor_id();
130 machine_crash_shutdown();
132 pos = (test_bit(KEXEC_FLAG_CRASH_POS, &kexec_flags) != 0);
134 if ( test_bit(KEXEC_IMAGE_CRASH_BASE + pos, &kexec_flags) )
135 {
136 image = &kexec_image[KEXEC_IMAGE_CRASH_BASE + pos];
137 machine_kexec(image); /* Does not return */
138 }
139 }
141 static void do_crashdump_trigger(unsigned char key)
142 {
143 int pos = (test_bit(KEXEC_FLAG_CRASH_POS, &kexec_flags) != 0);
144 if ( test_bit(KEXEC_IMAGE_CRASH_BASE + pos, &kexec_flags) )
145 {
146 printk("'%c' pressed -> triggering crashdump\n", key);
147 machine_crash_kexec();
148 }
149 else
150 {
151 printk("'%c' pressed -> no crash kernel loaded -- not triggering crashdump\n", key);
152 }
153 }
155 static __init int register_crashdump_trigger(void)
156 {
157 register_keyhandler('C', do_crashdump_trigger, "trigger a crashdump");
158 return 0;
159 }
160 __initcall(register_crashdump_trigger);
162 static int kexec_get_reserve(xen_kexec_range_t *range)
163 {
164 range->start = kexec_crash_area.start;
165 range->size = kexec_crash_area.size;
166 return 0;
167 }
169 extern unsigned long _text;
171 static int kexec_get_xen(xen_kexec_range_t *range)
172 {
173 range->start = virt_to_maddr(&_text);
174 range->size = (unsigned long)&_end - (unsigned long)&_text;
175 return 0;
176 }
178 static int kexec_get_cpu(xen_kexec_range_t *range)
179 {
180 if ( range->nr < 0 || range->nr >= num_present_cpus() )
181 return -EINVAL;
183 range->start = __pa((unsigned long)&per_cpu(crash_notes, range->nr));
184 range->size = sizeof(crash_note_t);
185 return 0;
186 }
188 static int kexec_get_range(XEN_GUEST_HANDLE(void) uarg)
189 {
190 xen_kexec_range_t range;
191 int ret = -EINVAL;
193 if ( unlikely(copy_from_guest(&range, uarg, 1)) )
194 return -EFAULT;
196 switch ( range.range )
197 {
198 case KEXEC_RANGE_MA_CRASH:
199 ret = kexec_get_reserve(&range);
200 break;
201 case KEXEC_RANGE_MA_XEN:
202 ret = kexec_get_xen(&range);
203 break;
204 case KEXEC_RANGE_MA_CPU:
205 ret = kexec_get_cpu(&range);
206 break;
207 }
209 if ( ret == 0 && unlikely(copy_to_guest(uarg, &range, 1)) )
210 return -EFAULT;
212 return ret;
213 }
215 static int kexec_load_get_bits(int type, int *base, int *bit)
216 {
217 switch ( type )
218 {
219 case KEXEC_TYPE_DEFAULT:
220 *base = KEXEC_IMAGE_DEFAULT_BASE;
221 *bit = KEXEC_FLAG_DEFAULT_POS;
222 break;
223 case KEXEC_TYPE_CRASH:
224 *base = KEXEC_IMAGE_CRASH_BASE;
225 *bit = KEXEC_FLAG_CRASH_POS;
226 break;
227 default:
228 return -1;
229 }
230 return 0;
231 }
233 static int kexec_load_unload(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
234 {
235 xen_kexec_load_t load;
236 xen_kexec_image_t *image;
237 int base, bit, pos;
238 int ret = 0;
240 if ( unlikely(copy_from_guest(&load, uarg, 1)) )
241 return -EFAULT;
243 if ( kexec_load_get_bits(load.type, &base, &bit) )
244 return -EINVAL;
246 pos = (test_bit(bit, &kexec_flags) != 0);
248 /* Load the user data into an unused image */
249 if ( op == KEXEC_CMD_kexec_load )
250 {
251 image = &kexec_image[base + !pos];
253 BUG_ON(test_bit((base + !pos), &kexec_flags)); /* must be free */
255 memcpy(image, &load.image, sizeof(*image));
257 if ( !(ret = machine_kexec_load(load.type, base + !pos, image)) )
258 {
259 /* Set image present bit */
260 set_bit((base + !pos), &kexec_flags);
262 /* Make new image the active one */
263 change_bit(bit, &kexec_flags);
264 }
265 }
267 /* Unload the old image if present and load successful */
268 if ( ret == 0 && !test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) )
269 {
270 if ( test_and_clear_bit((base + pos), &kexec_flags) )
271 {
272 image = &kexec_image[base + pos];
273 machine_kexec_unload(load.type, base + pos, image);
274 }
275 }
277 return ret;
278 }
280 static int kexec_exec(XEN_GUEST_HANDLE(void) uarg)
281 {
282 xen_kexec_exec_t exec;
283 xen_kexec_image_t *image;
284 int base, bit, pos;
286 if ( unlikely(copy_from_guest(&exec, uarg, 1)) )
287 return -EFAULT;
289 if ( kexec_load_get_bits(exec.type, &base, &bit) )
290 return -EINVAL;
292 pos = (test_bit(bit, &kexec_flags) != 0);
294 /* Only allow kexec/kdump into loaded images */
295 if ( !test_bit(base + pos, &kexec_flags) )
296 return -ENOENT;
298 switch (exec.type)
299 {
300 case KEXEC_TYPE_DEFAULT:
301 image = &kexec_image[base + pos];
302 one_cpu_only();
303 machine_reboot_kexec(image); /* Does not return */
304 break;
305 case KEXEC_TYPE_CRASH:
306 machine_crash_kexec(); /* Does not return */
307 break;
308 }
310 return -EINVAL; /* never reached */
311 }
313 long do_kexec_op(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
314 {
315 unsigned long flags;
316 int ret = -EINVAL;
318 if ( !IS_PRIV(current->domain) )
319 return -EPERM;
321 switch ( op )
322 {
323 case KEXEC_CMD_kexec_get_range:
324 ret = kexec_get_range(uarg);
325 break;
326 case KEXEC_CMD_kexec_load:
327 case KEXEC_CMD_kexec_unload:
328 spin_lock_irqsave(&kexec_lock, flags);
329 if (!test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags))
330 {
331 ret = kexec_load_unload(op, uarg);
332 }
333 spin_unlock_irqrestore(&kexec_lock, flags);
334 break;
335 case KEXEC_CMD_kexec:
336 ret = kexec_exec(uarg);
337 break;
338 }
340 return ret;
341 }
343 /*
344 * Local variables:
345 * mode: C
346 * c-set-style: "BSD"
347 * c-basic-offset: 4
348 * tab-width: 4
349 * indent-tabs-mode: nil
350 * End:
351 */