debuggers.hg

annotate xen/common/kexec.c @ 22855:1d1eec7e1fb4

xl: Perform minimal validation of virtual disk file while parsing config file

This patch performs some very basic validation on the virtual disk
file passed through the config file. This validation ensures that we
don't go too far with the initialization like spawn qemu and more
while there could be some potentially fundamental issues.

[ Patch fixed up to work with PHYSTYPE_EMPTY 22808:6ec61438713a -iwj ]

Signed-off-by: Kamala Narasimhan <kamala.narasimhan@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Kamala Narasimhan <kamala.narasimhan@gmail.com>
date Tue Jan 25 18:09:49 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 */