debuggers.hg

annotate xen/common/kexec.c @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents cb756381087c
children
rev   line source
ian@12661 1 /******************************************************************************
ian@12661 2 * kexec.c - Achitecture independent kexec code for Xen
ian@12666 3 *
ian@12661 4 * Xen port written by:
ian@12661 5 * - Simon 'Horms' Horman <horms@verge.net.au>
ian@12661 6 * - Magnus Damm <magnus@valinux.co.jp>
ian@12661 7 */
ian@12661 8
keir@21932 9 #include <xen/init.h>
ian@12661 10 #include <xen/lib.h>
keir@21932 11 #include <xen/acpi.h>
ian@12661 12 #include <xen/ctype.h>
ian@12661 13 #include <xen/errno.h>
ian@12661 14 #include <xen/guest_access.h>
ian@12661 15 #include <xen/sched.h>
ian@12661 16 #include <xen/types.h>
ian@12661 17 #include <xen/kexec.h>
ian@12661 18 #include <xen/keyhandler.h>
ian@12661 19 #include <public/kexec.h>
ian@12661 20 #include <xen/cpumask.h>
ian@12661 21 #include <asm/atomic.h>
ian@12661 22 #include <xen/spinlock.h>
ian@12661 23 #include <xen/version.h>
keir@15328 24 #include <xen/console.h>
keir@17178 25 #include <xen/kexec.h>
ian@12661 26 #include <public/elfnote.h>
kfraser@15846 27 #include <xsm/xsm.h>
keir@17202 28 #ifdef CONFIG_COMPAT
keir@17202 29 #include <compat/kexec.h>
keir@17202 30 #endif
ian@12661 31
keir@19964 32 static DEFINE_PER_CPU_READ_MOSTLY(void *, crash_notes);
ian@13348 33
ian@13349 34 static Elf_Note *xen_crash_note;
ian@13348 35
ian@13349 36 static cpumask_t crash_saved_cpus;
ian@12661 37
ian@13349 38 static xen_kexec_image_t kexec_image[KEXEC_IMAGE_NR];
ian@12661 39
ian@12661 40 #define KEXEC_FLAG_DEFAULT_POS (KEXEC_IMAGE_NR + 0)
ian@12661 41 #define KEXEC_FLAG_CRASH_POS (KEXEC_IMAGE_NR + 1)
ian@12661 42 #define KEXEC_FLAG_IN_PROGRESS (KEXEC_IMAGE_NR + 2)
ian@12661 43
ian@13349 44 static unsigned long kexec_flags = 0; /* the lowest bits are for KEXEC_IMAGE... */
ian@12661 45
ian@13349 46 static spinlock_t kexec_lock = SPIN_LOCK_UNLOCKED;
ian@12661 47
keir@17885 48 static unsigned char vmcoreinfo_data[VMCOREINFO_BYTES];
keir@17885 49 static size_t vmcoreinfo_size = 0;
keir@17885 50
ian@12698 51 xen_kexec_reserve_t kexec_crash_area;
keir@21281 52 static struct {
keir@21281 53 u64 start, end;
keir@21281 54 unsigned long size;
keir@21281 55 } ranges[16] __initdata;
ian@12698 56
keir@21281 57 /*
keir@21281 58 * Parse command lines in the format
keir@21281 59 *
keir@21281 60 * crashkernel=<ramsize-range>:<size>[,...][@<offset>]
keir@21281 61 *
keir@21281 62 * with <ramsize-range> being of form
keir@21281 63 *
keir@21281 64 * <start>-[<end>]
keir@21281 65 *
keir@21281 66 * as well as the legacy ones in the format
keir@21281 67 *
keir@21281 68 * crashkernel=<size>[@<offset>]
keir@21281 69 */
kfraser@13158 70 static void __init parse_crashkernel(const char *str)
ian@12698 71 {
keir@21281 72 const char *cur;
keir@21281 73
keir@21281 74 if ( strchr(str, ':' ) )
keir@21281 75 {
keir@21281 76 unsigned int idx = 0;
keir@21281 77
keir@21281 78 do {
keir@21281 79 if ( idx >= ARRAY_SIZE(ranges) )
keir@21281 80 {
keir@21281 81 printk(XENLOG_WARNING "crashkernel: too many ranges\n");
keir@21281 82 cur = NULL;
keir@21281 83 str = strchr(str, '@');
keir@21281 84 break;
keir@21281 85 }
keir@21281 86
keir@21281 87 ranges[idx].start = parse_size_and_unit(cur = str + !!idx, &str);
keir@21281 88 if ( cur == str )
keir@21281 89 break;
keir@21281 90
keir@21281 91 if ( *str != '-' )
keir@21281 92 {
keir@21281 93 printk(XENLOG_WARNING "crashkernel: '-' expected\n");
keir@21281 94 break;
keir@21281 95 }
keir@21281 96
keir@21281 97 if ( *++str != ':' )
keir@21281 98 {
keir@21281 99 ranges[idx].end = parse_size_and_unit(cur = str, &str);
keir@21281 100 if ( cur == str )
keir@21281 101 break;
keir@21281 102 if ( ranges[idx].end <= ranges[idx].start )
keir@21281 103 {
keir@21281 104 printk(XENLOG_WARNING "crashkernel: end <= start\n");
keir@21281 105 break;
keir@21281 106 }
keir@21281 107 }
keir@21281 108 else
keir@21281 109 ranges[idx].end = -1;
keir@21281 110
keir@21281 111 if ( *str != ':' )
keir@21281 112 {
keir@21281 113 printk(XENLOG_WARNING "crashkernel: ':' expected\n");
keir@21281 114 break;
keir@21281 115 }
keir@21281 116
keir@21281 117 ranges[idx].size = parse_size_and_unit(cur = str + 1, &str);
keir@21281 118 if ( cur == str )
keir@21281 119 break;
keir@21281 120
keir@21281 121 ++idx;
keir@21281 122 } while ( *str == ',' );
keir@21281 123 if ( idx < ARRAY_SIZE(ranges) )
keir@21281 124 ranges[idx].size = 0;
keir@21281 125 }
keir@21281 126 else
keir@21281 127 kexec_crash_area.size = parse_size_and_unit(cur = str, &str);
keir@21281 128 if ( cur != str && *str == '@' )
keir@21281 129 kexec_crash_area.start = parse_size_and_unit(cur = str + 1, &str);
keir@21281 130 if ( cur == str )
keir@21281 131 printk(XENLOG_WARNING "crashkernel: memory value expected\n");
ian@12698 132 }
ian@12698 133 custom_param("crashkernel", parse_crashkernel);
ian@12698 134
keir@21281 135 void __init set_kexec_crash_area_size(u64 system_ram)
keir@21281 136 {
keir@21281 137 unsigned int idx;
keir@21281 138
keir@21281 139 for ( idx = 0; idx < ARRAY_SIZE(ranges) && !kexec_crash_area.size; ++idx )
keir@21281 140 {
keir@21281 141 if ( !ranges[idx].size )
keir@21281 142 break;
keir@21281 143
keir@21281 144 if ( ranges[idx].size >= system_ram )
keir@21281 145 {
keir@21281 146 printk(XENLOG_WARNING "crashkernel: invalid size\n");
keir@21281 147 continue;
keir@21281 148 }
keir@21281 149
keir@21281 150 if ( ranges[idx].start <= system_ram && ranges[idx].end > system_ram )
keir@21281 151 kexec_crash_area.size = ranges[idx].size;
keir@21281 152 }
keir@21281 153 }
keir@21281 154
ian@12661 155 static void one_cpu_only(void)
ian@12661 156 {
kfraser@13062 157 /* Only allow the first cpu to continue - force other cpus to spin */
ian@12666 158 if ( test_and_set_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) )
kfraser@13062 159 for ( ; ; ) ;
ian@12661 160 }
ian@12661 161
kfraser@13062 162 /* Save the registers in the per-cpu crash note buffer. */
kfraser@13062 163 void kexec_crash_save_cpu(void)
ian@12661 164 {
ian@12661 165 int cpu = smp_processor_id();
ian@13348 166 Elf_Note *note = per_cpu(crash_notes, cpu);
ian@13348 167 ELF_Prstatus *prstatus;
ian@13348 168 crash_xen_core_t *xencore;
ian@12661 169
kfraser@13062 170 if ( cpu_test_and_set(cpu, crash_saved_cpus) )
kfraser@13062 171 return;
kfraser@13062 172
ian@13582 173 prstatus = (ELF_Prstatus *)ELFNOTE_DESC(note);
ian@12661 174
ian@13348 175 note = ELFNOTE_NEXT(note);
ian@13582 176 xencore = (crash_xen_core_t *)ELFNOTE_DESC(note);
ian@12661 177
ian@13348 178 elf_core_save_regs(&prstatus->pr_reg, xencore);
ian@12661 179 }
ian@12661 180
kfraser@13062 181 /* Set up the single Xen-specific-info crash note. */
kfraser@13062 182 crash_xen_info_t *kexec_crash_save_info(void)
ian@12661 183 {
ian@12661 184 int cpu = smp_processor_id();
kfraser@14935 185 crash_xen_info_t info;
kfraser@14935 186 crash_xen_info_t *out = (crash_xen_info_t *)ELFNOTE_DESC(xen_crash_note);
ian@12661 187
ian@12661 188 BUG_ON(!cpu_test_and_set(cpu, crash_saved_cpus));
ian@12661 189
kfraser@14935 190 memset(&info, 0, sizeof(info));
kfraser@14935 191 info.xen_major_version = xen_major_version();
kfraser@14935 192 info.xen_minor_version = xen_minor_version();
kfraser@14935 193 info.xen_extra_version = __pa(xen_extra_version());
kfraser@14935 194 info.xen_changeset = __pa(xen_changeset());
kfraser@14935 195 info.xen_compiler = __pa(xen_compiler());
kfraser@14935 196 info.xen_compile_date = __pa(xen_compile_date());
kfraser@14935 197 info.xen_compile_time = __pa(xen_compile_time());
kfraser@14935 198 info.tainted = tainted;
ian@12661 199
kfraser@14935 200 /* Copy from guaranteed-aligned local copy to possibly-unaligned dest. */
kfraser@14935 201 memcpy(out, &info, sizeof(info));
kfraser@14935 202
kfraser@14935 203 return out;
ian@12661 204 }
ian@12661 205
keir@21932 206 static void kexec_common_shutdown(void)
keir@21932 207 {
keir@21932 208 watchdog_disable();
keir@21932 209 console_start_sync();
keir@21932 210 spin_debug_disable();
keir@21932 211 one_cpu_only();
keir@22616 212 acpi_dmar_reinstate();
keir@21932 213 }
keir@21932 214
kfraser@13062 215 void kexec_crash(void)
ian@12661 216 {
ian@12661 217 int pos;
kfraser@13062 218
kfraser@13062 219 pos = (test_bit(KEXEC_FLAG_CRASH_POS, &kexec_flags) != 0);
kfraser@13062 220 if ( !test_bit(KEXEC_IMAGE_CRASH_BASE + pos, &kexec_flags) )
kfraser@13062 221 return;
ian@12661 222
keir@21932 223 kexec_common_shutdown();
kfraser@13062 224 kexec_crash_save_cpu();
ian@12661 225 machine_crash_shutdown();
kfraser@13062 226 machine_kexec(&kexec_image[KEXEC_IMAGE_CRASH_BASE + pos]);
ian@12661 227
kfraser@13062 228 BUG();
ian@12661 229 }
ian@12661 230
keir@21932 231 static long kexec_reboot(void *_image)
keir@21932 232 {
keir@21932 233 xen_kexec_image_t *image = _image;
keir@21932 234
keir@21932 235 kexec_common_shutdown();
keir@21932 236 machine_reboot_kexec(image);
keir@21932 237
keir@21932 238 BUG();
keir@21932 239 return 0;
keir@21932 240 }
keir@21932 241
ian@12661 242 static void do_crashdump_trigger(unsigned char key)
ian@12661 243 {
kfraser@13062 244 printk("'%c' pressed -> triggering crashdump\n", key);
kfraser@13062 245 kexec_crash();
kfraser@13062 246 printk(" * no crash kernel loaded!\n");
ian@12661 247 }
ian@12661 248
keir@20048 249 static struct keyhandler crashdump_trigger_keyhandler = {
keir@20048 250 .u.fn = do_crashdump_trigger,
keir@20048 251 .desc = "trigger a crashdump"
keir@20048 252 };
keir@20048 253
ian@12661 254 static __init int register_crashdump_trigger(void)
ian@12661 255 {
keir@20048 256 register_keyhandler('C', &crashdump_trigger_keyhandler);
kfraser@13062 257 return 0;
ian@12661 258 }
ian@12661 259 __initcall(register_crashdump_trigger);
ian@12661 260
ian@13348 261 static void setup_note(Elf_Note *n, const char *name, int type, int descsz)
ian@13348 262 {
kfraser@13807 263 int l = strlen(name) + 1;
kfraser@13807 264 strlcpy(ELFNOTE_NAME(n), name, l);
kfraser@13807 265 n->namesz = l;
ian@13348 266 n->descsz = descsz;
ian@13348 267 n->type = type;
ian@13348 268 }
ian@13348 269
kfraser@13807 270 static int sizeof_note(const char *name, int descsz)
kfraser@13807 271 {
kfraser@13807 272 return (sizeof(Elf_Note) +
ian@13817 273 ELFNOTE_ALIGN(strlen(name)+1) +
kfraser@13807 274 ELFNOTE_ALIGN(descsz));
kfraser@13807 275 }
kfraser@13807 276
keir@17177 277 static int kexec_get_reserve(xen_kexec_range_t *range)
ian@12661 278 {
ian@13406 279 if ( kexec_crash_area.size > 0 && kexec_crash_area.start > 0) {
ian@13406 280 range->start = kexec_crash_area.start;
ian@13406 281 range->size = kexec_crash_area.size;
ian@13406 282 }
ian@13406 283 else
ian@13406 284 range->start = range->size = 0;
ian@12661 285 return 0;
ian@12661 286 }
ian@12661 287
keir@17177 288 static int kexec_get_cpu(xen_kexec_range_t *range)
ian@12661 289 {
ian@13348 290 int nr = range->nr;
kfraser@13807 291 int nr_bytes = 0;
ian@13348 292
keir@21478 293 if ( nr < 0 || nr >= NR_CPUS || !cpu_online(nr) )
ian@12661 294 return -EINVAL;
ian@12661 295
kfraser@13807 296 nr_bytes += sizeof_note("CORE", sizeof(ELF_Prstatus));
kfraser@13807 297 nr_bytes += sizeof_note("Xen", sizeof(crash_xen_core_t));
kfraser@13807 298
ian@13348 299 /* The Xen info note is included in CPU0's range. */
ian@13348 300 if ( nr == 0 )
kfraser@13807 301 nr_bytes += sizeof_note("Xen", sizeof(crash_xen_info_t));
ian@13348 302
ian@13348 303 if ( per_cpu(crash_notes, nr) == NULL )
ian@13348 304 {
ian@13348 305 Elf_Note *note;
ian@13348 306
ian@13348 307 note = per_cpu(crash_notes, nr) = xmalloc_bytes(nr_bytes);
ian@13348 308
ian@13348 309 if ( note == NULL )
ian@13348 310 return -ENOMEM;
ian@13348 311
ian@13348 312 /* Setup CORE note. */
ian@13348 313 setup_note(note, "CORE", NT_PRSTATUS, sizeof(ELF_Prstatus));
ian@13348 314
ian@13348 315 /* Setup Xen CORE note. */
ian@13348 316 note = ELFNOTE_NEXT(note);
ian@13348 317 setup_note(note, "Xen", XEN_ELFNOTE_CRASH_REGS, sizeof(crash_xen_core_t));
ian@13348 318
ian@13348 319 if (nr == 0)
ian@13348 320 {
ian@13348 321 /* Setup system wide Xen info note. */
ian@13348 322 xen_crash_note = note = ELFNOTE_NEXT(note);
ian@13348 323 setup_note(note, "Xen", XEN_ELFNOTE_CRASH_INFO, sizeof(crash_xen_info_t));
ian@13348 324 }
ian@13348 325 }
ian@13348 326
ian@13348 327 range->start = __pa((unsigned long)per_cpu(crash_notes, nr));
ian@13348 328 range->size = nr_bytes;
ian@12661 329 return 0;
ian@12661 330 }
ian@12661 331
keir@17885 332 static int kexec_get_vmcoreinfo(xen_kexec_range_t *range)
keir@17885 333 {
keir@17885 334 range->start = __pa((unsigned long)vmcoreinfo_data);
keir@17885 335 range->size = VMCOREINFO_BYTES;
keir@17885 336 return 0;
keir@17885 337 }
keir@17885 338
keir@17177 339 static int kexec_get_range_internal(xen_kexec_range_t *range)
keir@17177 340 {
keir@17177 341 int ret = -EINVAL;
keir@17177 342
keir@17177 343 switch ( range->range )
keir@17177 344 {
keir@17177 345 case KEXEC_RANGE_MA_CRASH:
keir@17177 346 ret = kexec_get_reserve(range);
keir@17177 347 break;
keir@17177 348 case KEXEC_RANGE_MA_CPU:
keir@17177 349 ret = kexec_get_cpu(range);
keir@17177 350 break;
keir@17885 351 case KEXEC_RANGE_MA_VMCOREINFO:
keir@17885 352 ret = kexec_get_vmcoreinfo(range);
keir@17885 353 break;
keir@17178 354 default:
keir@17178 355 ret = machine_kexec_get(range);
keir@17178 356 break;
keir@17177 357 }
keir@17177 358
keir@17177 359 return ret;
keir@17177 360 }
keir@17177 361
keir@17177 362 static int kexec_get_range(XEN_GUEST_HANDLE(void) uarg)
ian@12661 363 {
ian@12661 364 xen_kexec_range_t range;
ian@12661 365 int ret = -EINVAL;
ian@12666 366
ian@12666 367 if ( unlikely(copy_from_guest(&range, uarg, 1)) )
ian@12661 368 return -EFAULT;
ian@12661 369
keir@17177 370 ret = kexec_get_range_internal(&range);
ian@12661 371
ian@12666 372 if ( ret == 0 && unlikely(copy_to_guest(uarg, &range, 1)) )
ian@12661 373 return -EFAULT;
ian@12666 374
ian@12661 375 return ret;
ian@12661 376 }
ian@12661 377
keir@17177 378 static int kexec_get_range_compat(XEN_GUEST_HANDLE(void) uarg)
keir@17177 379 {
keir@17204 380 #ifdef CONFIG_COMPAT
keir@17177 381 xen_kexec_range_t range;
keir@17177 382 compat_kexec_range_t compat_range;
keir@17177 383 int ret = -EINVAL;
keir@17177 384
keir@17177 385 if ( unlikely(copy_from_guest(&compat_range, uarg, 1)) )
keir@17177 386 return -EFAULT;
keir@17177 387
keir@17203 388 XLAT_kexec_range(&range, &compat_range);
keir@17177 389
keir@17177 390 ret = kexec_get_range_internal(&range);
keir@17177 391
keir@17177 392 if ( ret == 0 ) {
keir@17203 393 XLAT_kexec_range(&compat_range, &range);
keir@17177 394 if ( unlikely(copy_to_guest(uarg, &compat_range, 1)) )
keir@17177 395 return -EFAULT;
keir@17177 396 }
keir@17177 397
keir@17177 398 return ret;
keir@17204 399 #else /* CONFIG_COMPAT */
keir@17204 400 return 0;
keir@17177 401 #endif /* CONFIG_COMPAT */
keir@17204 402 }
ack@13312 403
ian@12661 404 static int kexec_load_get_bits(int type, int *base, int *bit)
ian@12661 405 {
ian@12666 406 switch ( type )
ian@12661 407 {
ian@12661 408 case KEXEC_TYPE_DEFAULT:
ian@12661 409 *base = KEXEC_IMAGE_DEFAULT_BASE;
ian@12661 410 *bit = KEXEC_FLAG_DEFAULT_POS;
ian@12661 411 break;
ian@12661 412 case KEXEC_TYPE_CRASH:
ian@12661 413 *base = KEXEC_IMAGE_CRASH_BASE;
ian@12661 414 *bit = KEXEC_FLAG_CRASH_POS;
ian@12661 415 break;
ian@12661 416 default:
ian@12661 417 return -1;
ian@12661 418 }
ian@12661 419 return 0;
ian@12661 420 }
ian@12661 421
keir@17885 422 void vmcoreinfo_append_str(const char *fmt, ...)
keir@17885 423 {
keir@17885 424 va_list args;
keir@17885 425 char buf[0x50];
keir@17885 426 int r;
keir@17885 427 size_t note_size = sizeof(Elf_Note) + ELFNOTE_ALIGN(strlen(VMCOREINFO_NOTE_NAME) + 1);
keir@17885 428
keir@17885 429 if (vmcoreinfo_size + note_size + sizeof(buf) > VMCOREINFO_BYTES)
keir@17885 430 return;
keir@17885 431
keir@17885 432 va_start(args, fmt);
keir@17885 433 r = vsnprintf(buf, sizeof(buf), fmt, args);
keir@17885 434 va_end(args);
keir@17885 435
keir@17885 436 memcpy(&vmcoreinfo_data[note_size + vmcoreinfo_size], buf, r);
keir@17885 437
keir@17885 438 vmcoreinfo_size += r;
keir@17885 439 }
keir@17885 440
keir@17885 441 static void crash_save_vmcoreinfo(void)
keir@17885 442 {
keir@17885 443 size_t data_size;
keir@17885 444
keir@17885 445 if (vmcoreinfo_size > 0) /* already saved */
keir@17885 446 return;
keir@17885 447
keir@17885 448 data_size = VMCOREINFO_BYTES - (sizeof(Elf_Note) + ELFNOTE_ALIGN(strlen(VMCOREINFO_NOTE_NAME) + 1));
keir@17885 449 setup_note((Elf_Note *)vmcoreinfo_data, VMCOREINFO_NOTE_NAME, 0, data_size);
keir@17885 450
keir@17885 451 VMCOREINFO_PAGESIZE(PAGE_SIZE);
keir@17885 452
keir@17885 453 VMCOREINFO_SYMBOL(domain_list);
keir@20201 454 #ifndef frame_table
keir@17885 455 VMCOREINFO_SYMBOL(frame_table);
keir@20201 456 #else
keir@20201 457 {
keir@20201 458 static const void *const _frame_table = frame_table;
keir@20201 459 VMCOREINFO_SYMBOL_ALIAS(frame_table, _frame_table);
keir@20201 460 }
keir@20201 461 #endif
keir@17885 462 VMCOREINFO_SYMBOL(max_page);
keir@17885 463
keir@17885 464 VMCOREINFO_STRUCT_SIZE(page_info);
keir@17885 465 VMCOREINFO_STRUCT_SIZE(domain);
keir@17885 466
keir@17885 467 VMCOREINFO_OFFSET(page_info, count_info);
keir@22712 468 #ifdef __ia64__
keir@22712 469 VMCOREINFO_OFFSET_SUB(page_info, u.inuse, _domain);
keir@22712 470 #else
keir@22712 471 VMCOREINFO_OFFSET_SUB(page_info, v.inuse, _domain);
keir@22712 472 #endif
keir@17885 473 VMCOREINFO_OFFSET(domain, domain_id);
keir@17885 474 VMCOREINFO_OFFSET(domain, next_in_list);
keir@17885 475
keir@17885 476 #ifdef ARCH_CRASH_SAVE_VMCOREINFO
keir@17885 477 arch_crash_save_vmcoreinfo();
keir@17885 478 #endif
keir@17885 479 }
keir@17885 480
keir@17204 481 static int kexec_load_unload_internal(unsigned long op, xen_kexec_load_t *load)
ian@12661 482 {
ian@12661 483 xen_kexec_image_t *image;
ian@12661 484 int base, bit, pos;
ian@12661 485 int ret = 0;
ian@12661 486
keir@17204 487 if ( kexec_load_get_bits(load->type, &base, &bit) )
ian@12661 488 return -EINVAL;
ian@12661 489
ian@12661 490 pos = (test_bit(bit, &kexec_flags) != 0);
ian@12661 491
ian@12661 492 /* Load the user data into an unused image */
ian@12666 493 if ( op == KEXEC_CMD_kexec_load )
ian@12661 494 {
ian@12661 495 image = &kexec_image[base + !pos];
ian@12661 496
ian@12661 497 BUG_ON(test_bit((base + !pos), &kexec_flags)); /* must be free */
ian@12661 498
keir@17204 499 memcpy(image, &load->image, sizeof(*image));
ian@12666 500
keir@17204 501 if ( !(ret = machine_kexec_load(load->type, base + !pos, image)) )
ian@12661 502 {
ian@12661 503 /* Set image present bit */
ian@12661 504 set_bit((base + !pos), &kexec_flags);
ian@12661 505
ian@12661 506 /* Make new image the active one */
ian@12661 507 change_bit(bit, &kexec_flags);
ian@12661 508 }
keir@17885 509
keir@17885 510 crash_save_vmcoreinfo();
ian@12661 511 }
ian@12661 512
ian@12661 513 /* Unload the old image if present and load successful */
ian@12666 514 if ( ret == 0 && !test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) )
ian@12661 515 {
ian@12666 516 if ( test_and_clear_bit((base + pos), &kexec_flags) )
ian@12661 517 {
ian@12661 518 image = &kexec_image[base + pos];
keir@17204 519 machine_kexec_unload(load->type, base + pos, image);
ian@12661 520 }
ian@12661 521 }
ian@12661 522
ian@12661 523 return ret;
ian@12661 524 }
ian@12661 525
keir@17204 526 static int kexec_load_unload(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
keir@17204 527 {
keir@17204 528 xen_kexec_load_t load;
keir@17204 529
keir@17204 530 if ( unlikely(copy_from_guest(&load, uarg, 1)) )
keir@17204 531 return -EFAULT;
keir@17204 532
keir@17204 533 return kexec_load_unload_internal(op, &load);
keir@17204 534 }
keir@17204 535
keir@17204 536 static int kexec_load_unload_compat(unsigned long op,
keir@17204 537 XEN_GUEST_HANDLE(void) uarg)
keir@17204 538 {
keir@17204 539 #ifdef CONFIG_COMPAT
keir@17204 540 compat_kexec_load_t compat_load;
keir@17204 541 xen_kexec_load_t load;
keir@17204 542
keir@17204 543 if ( unlikely(copy_from_guest(&compat_load, uarg, 1)) )
keir@17204 544 return -EFAULT;
keir@17204 545
keir@17204 546 /* This is a bit dodgy, load.image is inside load,
keir@17204 547 * but XLAT_kexec_load (which is automatically generated)
keir@17204 548 * doesn't translate load.image (correctly)
keir@17204 549 * Just copy load->type, the only other member, manually instead.
keir@17204 550 *
keir@17204 551 * XLAT_kexec_load(&load, &compat_load);
keir@17204 552 */
keir@17204 553 load.type = compat_load.type;
keir@17204 554 XLAT_kexec_image(&load.image, &compat_load.image);
keir@17204 555
keir@17204 556 return kexec_load_unload_internal(op, &load);
keir@17204 557 #else /* CONFIG_COMPAT */
keir@17204 558 return 0;
keir@17204 559 #endif /* CONFIG_COMPAT */
keir@17204 560 }
ack@13312 561
ian@12661 562 static int kexec_exec(XEN_GUEST_HANDLE(void) uarg)
ian@12661 563 {
ian@12661 564 xen_kexec_exec_t exec;
ian@12661 565 xen_kexec_image_t *image;
keir@21932 566 int base, bit, pos, ret = -EINVAL;
ian@12661 567
ian@12666 568 if ( unlikely(copy_from_guest(&exec, uarg, 1)) )
ian@12661 569 return -EFAULT;
ian@12661 570
ian@12666 571 if ( kexec_load_get_bits(exec.type, &base, &bit) )
ian@12661 572 return -EINVAL;
ian@12661 573
ian@12661 574 pos = (test_bit(bit, &kexec_flags) != 0);
ian@12661 575
ian@12661 576 /* Only allow kexec/kdump into loaded images */
ian@12666 577 if ( !test_bit(base + pos, &kexec_flags) )
ian@12661 578 return -ENOENT;
ian@12661 579
ian@12661 580 switch (exec.type)
ian@12661 581 {
ian@12661 582 case KEXEC_TYPE_DEFAULT:
ian@12661 583 image = &kexec_image[base + pos];
keir@21932 584 ret = continue_hypercall_on_cpu(0, kexec_reboot, image);
ian@12661 585 break;
ian@12661 586 case KEXEC_TYPE_CRASH:
kfraser@13062 587 kexec_crash(); /* Does not return */
ian@12661 588 break;
ian@12661 589 }
ian@12661 590
ian@12661 591 return -EINVAL; /* never reached */
ian@12661 592 }
ian@12661 593
keir@17204 594 int do_kexec_op_internal(unsigned long op, XEN_GUEST_HANDLE(void) uarg,
keir@17204 595 int compat)
ian@12661 596 {
ian@12661 597 unsigned long flags;
ian@12661 598 int ret = -EINVAL;
ian@12661 599
ian@12666 600 if ( !IS_PRIV(current->domain) )
ian@12661 601 return -EPERM;
ian@12661 602
kfraser@15846 603 ret = xsm_kexec();
kfraser@15846 604 if ( ret )
kfraser@15846 605 return ret;
kfraser@15846 606
ian@12666 607 switch ( op )
ian@12661 608 {
ian@12661 609 case KEXEC_CMD_kexec_get_range:
keir@17204 610 if (compat)
keir@17204 611 ret = kexec_get_range_compat(uarg);
keir@17204 612 else
keir@17204 613 ret = kexec_get_range(uarg);
ian@12661 614 break;
ian@12661 615 case KEXEC_CMD_kexec_load:
ian@12661 616 case KEXEC_CMD_kexec_unload:
ian@12661 617 spin_lock_irqsave(&kexec_lock, flags);
ian@12661 618 if (!test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags))
ian@12661 619 {
keir@17204 620 if (compat)
keir@17204 621 ret = kexec_load_unload_compat(op, uarg);
keir@17204 622 else
keir@17204 623 ret = kexec_load_unload(op, uarg);
ian@12661 624 }
ian@12661 625 spin_unlock_irqrestore(&kexec_lock, flags);
ian@12661 626 break;
ian@12661 627 case KEXEC_CMD_kexec:
ian@12661 628 ret = kexec_exec(uarg);
ian@12661 629 break;
ian@12661 630 }
ian@12661 631
ian@12661 632 return ret;
ian@12661 633 }
ian@12661 634
keir@17204 635 long do_kexec_op(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
keir@17204 636 {
keir@17204 637 return do_kexec_op_internal(op, uarg, 0);
keir@17204 638 }
keir@17204 639
keir@17204 640 #ifdef CONFIG_COMPAT
keir@17204 641 int compat_kexec_op(unsigned long op, XEN_GUEST_HANDLE(void) uarg)
keir@17204 642 {
keir@17204 643 return do_kexec_op_internal(op, uarg, 1);
keir@17204 644 }
keir@17204 645 #endif
keir@17204 646
ian@12661 647 /*
ian@12661 648 * Local variables:
ian@12661 649 * mode: C
ian@12661 650 * c-set-style: "BSD"
ian@12661 651 * c-basic-offset: 4
ian@12661 652 * tab-width: 4
ian@12661 653 * indent-tabs-mode: nil
ian@12661 654 * End:
ian@12661 655 */