ia64/xen-unstable

view xen/common/kexec.c @ 15896:42d4313b5fdd

[IA64] update .hgignore for xenitp

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
author Alex Williamson <alex.williamson@hp.com>
date Mon Sep 24 14:21:02 2007 -0600 (2007-09-24)
parents 96f64f4c42f0
children 90ae3dfdd3cc
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 <xen/lib.h>
10 #include <xen/ctype.h>
11 #include <xen/errno.h>
12 #include <xen/guest_access.h>
13 #include <xen/sched.h>
14 #include <xen/types.h>
15 #include <xen/kexec.h>
16 #include <xen/keyhandler.h>
17 #include <public/kexec.h>
18 #include <xen/cpumask.h>
19 #include <asm/atomic.h>
20 #include <xen/spinlock.h>
21 #include <xen/version.h>
22 #include <xen/console.h>
23 #include <public/elfnote.h>
24 #include <xsm/xsm.h>
26 #ifndef COMPAT
28 typedef long ret_t;
30 static DEFINE_PER_CPU(void *, crash_notes);
32 static Elf_Note *xen_crash_note;
34 static cpumask_t crash_saved_cpus;
36 static xen_kexec_image_t kexec_image[KEXEC_IMAGE_NR];
38 #define KEXEC_FLAG_DEFAULT_POS (KEXEC_IMAGE_NR + 0)
39 #define KEXEC_FLAG_CRASH_POS (KEXEC_IMAGE_NR + 1)
40 #define KEXEC_FLAG_IN_PROGRESS (KEXEC_IMAGE_NR + 2)
42 static unsigned long kexec_flags = 0; /* the lowest bits are for KEXEC_IMAGE... */
44 static spinlock_t kexec_lock = SPIN_LOCK_UNLOCKED;
46 xen_kexec_reserve_t kexec_crash_area;
48 static void __init parse_crashkernel(const char *str)
49 {
50 kexec_crash_area.size = parse_size_and_unit(str, &str);
51 if ( *str == '@' )
52 kexec_crash_area.start = parse_size_and_unit(str+1, NULL);
53 }
54 custom_param("crashkernel", parse_crashkernel);
56 static void one_cpu_only(void)
57 {
58 /* Only allow the first cpu to continue - force other cpus to spin */
59 if ( test_and_set_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) )
60 for ( ; ; ) ;
61 }
63 /* Save the registers in the per-cpu crash note buffer. */
64 void kexec_crash_save_cpu(void)
65 {
66 int cpu = smp_processor_id();
67 Elf_Note *note = per_cpu(crash_notes, cpu);
68 ELF_Prstatus *prstatus;
69 crash_xen_core_t *xencore;
71 if ( cpu_test_and_set(cpu, crash_saved_cpus) )
72 return;
74 prstatus = (ELF_Prstatus *)ELFNOTE_DESC(note);
76 note = ELFNOTE_NEXT(note);
77 xencore = (crash_xen_core_t *)ELFNOTE_DESC(note);
79 elf_core_save_regs(&prstatus->pr_reg, xencore);
80 }
82 /* Set up the single Xen-specific-info crash note. */
83 crash_xen_info_t *kexec_crash_save_info(void)
84 {
85 int cpu = smp_processor_id();
86 crash_xen_info_t info;
87 crash_xen_info_t *out = (crash_xen_info_t *)ELFNOTE_DESC(xen_crash_note);
89 BUG_ON(!cpu_test_and_set(cpu, crash_saved_cpus));
91 memset(&info, 0, sizeof(info));
92 info.xen_major_version = xen_major_version();
93 info.xen_minor_version = xen_minor_version();
94 info.xen_extra_version = __pa(xen_extra_version());
95 info.xen_changeset = __pa(xen_changeset());
96 info.xen_compiler = __pa(xen_compiler());
97 info.xen_compile_date = __pa(xen_compile_date());
98 info.xen_compile_time = __pa(xen_compile_time());
99 info.tainted = tainted;
101 /* Copy from guaranteed-aligned local copy to possibly-unaligned dest. */
102 memcpy(out, &info, sizeof(info));
104 return out;
105 }
107 void kexec_crash(void)
108 {
109 int pos;
111 pos = (test_bit(KEXEC_FLAG_CRASH_POS, &kexec_flags) != 0);
112 if ( !test_bit(KEXEC_IMAGE_CRASH_BASE + pos, &kexec_flags) )
113 return;
115 console_start_sync();
117 one_cpu_only();
118 kexec_crash_save_cpu();
119 machine_crash_shutdown();
121 machine_kexec(&kexec_image[KEXEC_IMAGE_CRASH_BASE + pos]);
123 BUG();
124 }
126 static void do_crashdump_trigger(unsigned char key)
127 {
128 printk("'%c' pressed -> triggering crashdump\n", key);
129 kexec_crash();
130 printk(" * no crash kernel loaded!\n");
131 }
133 static __init int register_crashdump_trigger(void)
134 {
135 register_keyhandler('C', do_crashdump_trigger, "trigger a crashdump");
136 return 0;
137 }
138 __initcall(register_crashdump_trigger);
140 static void setup_note(Elf_Note *n, const char *name, int type, int descsz)
141 {
142 int l = strlen(name) + 1;
143 strlcpy(ELFNOTE_NAME(n), name, l);
144 n->namesz = l;
145 n->descsz = descsz;
146 n->type = type;
147 }
149 static int sizeof_note(const char *name, int descsz)
150 {
151 return (sizeof(Elf_Note) +
152 ELFNOTE_ALIGN(strlen(name)+1) +
153 ELFNOTE_ALIGN(descsz));
154 }
156 #define kexec_get(x) kexec_get_##x
158 #endif
160 static int kexec_get(reserve)(xen_kexec_range_t *range)
161 {
162 if ( kexec_crash_area.size > 0 && kexec_crash_area.start > 0) {
163 range->start = kexec_crash_area.start;
164 range->size = kexec_crash_area.size;
165 }
166 else
167 range->start = range->size = 0;
168 return 0;
169 }
171 static int kexec_get(xen)(xen_kexec_range_t *range)
172 {
173 #ifdef CONFIG_X86_64
174 range->start = xenheap_phys_start;
175 #else
176 range->start = virt_to_maddr(_start);
177 #endif
178 range->size = (unsigned long)xenheap_phys_end - (unsigned long)range->start;
179 return 0;
180 }
182 static int kexec_get(cpu)(xen_kexec_range_t *range)
183 {
184 int nr = range->nr;
185 int nr_bytes = 0;
187 if ( nr < 0 || nr >= num_present_cpus() )
188 return -EINVAL;
190 nr_bytes += sizeof_note("CORE", sizeof(ELF_Prstatus));
191 nr_bytes += sizeof_note("Xen", sizeof(crash_xen_core_t));
193 /* The Xen info note is included in CPU0's range. */
194 if ( nr == 0 )
195 nr_bytes += sizeof_note("Xen", sizeof(crash_xen_info_t));
197 if ( per_cpu(crash_notes, nr) == NULL )
198 {
199 Elf_Note *note;
201 note = per_cpu(crash_notes, nr) = xmalloc_bytes(nr_bytes);
203 if ( note == NULL )
204 return -ENOMEM;
206 /* Setup CORE note. */
207 setup_note(note, "CORE", NT_PRSTATUS, sizeof(ELF_Prstatus));
209 /* Setup Xen CORE note. */
210 note = ELFNOTE_NEXT(note);
211 setup_note(note, "Xen", XEN_ELFNOTE_CRASH_REGS, sizeof(crash_xen_core_t));
213 if (nr == 0)
214 {
215 /* Setup system wide Xen info note. */
216 xen_crash_note = note = ELFNOTE_NEXT(note);
217 setup_note(note, "Xen", XEN_ELFNOTE_CRASH_INFO, sizeof(crash_xen_info_t));
218 }
219 }
221 range->start = __pa((unsigned long)per_cpu(crash_notes, nr));
222 range->size = nr_bytes;
223 return 0;
224 }
226 static int kexec_get(range)(XEN_GUEST_HANDLE(void) uarg)
227 {
228 xen_kexec_range_t range;
229 int ret = -EINVAL;
231 if ( unlikely(copy_from_guest(&range, uarg, 1)) )
232 return -EFAULT;
234 switch ( range.range )
235 {
236 case KEXEC_RANGE_MA_CRASH:
237 ret = kexec_get(reserve)(&range);
238 break;
239 case KEXEC_RANGE_MA_XEN:
240 ret = kexec_get(xen)(&range);
241 break;
242 case KEXEC_RANGE_MA_CPU:
243 ret = kexec_get(cpu)(&range);
244 break;
245 }
247 if ( ret == 0 && unlikely(copy_to_guest(uarg, &range, 1)) )
248 return -EFAULT;
250 return ret;
251 }
253 #ifndef COMPAT
255 static int kexec_load_get_bits(int type, int *base, int *bit)
256 {
257 switch ( type )
258 {
259 case KEXEC_TYPE_DEFAULT:
260 *base = KEXEC_IMAGE_DEFAULT_BASE;
261 *bit = KEXEC_FLAG_DEFAULT_POS;
262 break;
263 case KEXEC_TYPE_CRASH:
264 *base = KEXEC_IMAGE_CRASH_BASE;
265 *bit = KEXEC_FLAG_CRASH_POS;
266 break;
267 default:
268 return -1;
269 }
270 return 0;
271 }
273 #endif
275 static int kexec_load_unload(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
276 {
277 xen_kexec_load_t load;
278 xen_kexec_image_t *image;
279 int base, bit, pos;
280 int ret = 0;
282 if ( unlikely(copy_from_guest(&load, uarg, 1)) )
283 return -EFAULT;
285 if ( kexec_load_get_bits(load.type, &base, &bit) )
286 return -EINVAL;
288 pos = (test_bit(bit, &kexec_flags) != 0);
290 /* Load the user data into an unused image */
291 if ( op == KEXEC_CMD_kexec_load )
292 {
293 image = &kexec_image[base + !pos];
295 BUG_ON(test_bit((base + !pos), &kexec_flags)); /* must be free */
297 #ifndef COMPAT
298 memcpy(image, &load.image, sizeof(*image));
299 #else
300 XLAT_kexec_image(image, &load.image);
301 #endif
303 if ( !(ret = machine_kexec_load(load.type, base + !pos, image)) )
304 {
305 /* Set image present bit */
306 set_bit((base + !pos), &kexec_flags);
308 /* Make new image the active one */
309 change_bit(bit, &kexec_flags);
310 }
311 }
313 /* Unload the old image if present and load successful */
314 if ( ret == 0 && !test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) )
315 {
316 if ( test_and_clear_bit((base + pos), &kexec_flags) )
317 {
318 image = &kexec_image[base + pos];
319 machine_kexec_unload(load.type, base + pos, image);
320 }
321 }
323 return ret;
324 }
326 #ifndef COMPAT
328 static int kexec_exec(XEN_GUEST_HANDLE(void) uarg)
329 {
330 xen_kexec_exec_t exec;
331 xen_kexec_image_t *image;
332 int base, bit, pos;
334 if ( unlikely(copy_from_guest(&exec, uarg, 1)) )
335 return -EFAULT;
337 if ( kexec_load_get_bits(exec.type, &base, &bit) )
338 return -EINVAL;
340 pos = (test_bit(bit, &kexec_flags) != 0);
342 /* Only allow kexec/kdump into loaded images */
343 if ( !test_bit(base + pos, &kexec_flags) )
344 return -ENOENT;
346 switch (exec.type)
347 {
348 case KEXEC_TYPE_DEFAULT:
349 image = &kexec_image[base + pos];
350 one_cpu_only();
351 machine_reboot_kexec(image); /* Does not return */
352 break;
353 case KEXEC_TYPE_CRASH:
354 kexec_crash(); /* Does not return */
355 break;
356 }
358 return -EINVAL; /* never reached */
359 }
361 #endif
363 ret_t do_kexec_op(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
364 {
365 unsigned long flags;
366 int ret = -EINVAL;
368 if ( !IS_PRIV(current->domain) )
369 return -EPERM;
371 ret = xsm_kexec();
372 if ( ret )
373 return ret;
375 switch ( op )
376 {
377 case KEXEC_CMD_kexec_get_range:
378 ret = kexec_get(range)(uarg);
379 break;
380 case KEXEC_CMD_kexec_load:
381 case KEXEC_CMD_kexec_unload:
382 spin_lock_irqsave(&kexec_lock, flags);
383 if (!test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags))
384 {
385 ret = kexec_load_unload(op, uarg);
386 }
387 spin_unlock_irqrestore(&kexec_lock, flags);
388 break;
389 case KEXEC_CMD_kexec:
390 ret = kexec_exec(uarg);
391 break;
392 }
394 return ret;
395 }
397 #if defined(CONFIG_COMPAT) && !defined(COMPAT)
398 #include "compat/kexec.c"
399 #endif
401 /*
402 * Local variables:
403 * mode: C
404 * c-set-style: "BSD"
405 * c-basic-offset: 4
406 * tab-width: 4
407 * indent-tabs-mode: nil
408 * End:
409 */