Merge tag 'mvebu-fixes-3.17' of git://git.infradead.org/linux-mvebu into next/fixes...
[cascardo/linux.git] / kernel / kexec.c
index 0926f2a..2bee072 100644 (file)
@@ -64,7 +64,9 @@ bool kexec_in_progress = false;
 char __weak kexec_purgatory[0];
 size_t __weak kexec_purgatory_size = 0;
 
+#ifdef CONFIG_KEXEC_FILE
 static int kexec_calculate_store_digests(struct kimage *image);
+#endif
 
 /* Location of the reserved area for the crash kernel */
 struct resource crashk_res = {
@@ -341,6 +343,7 @@ out_free_image:
        return ret;
 }
 
+#ifdef CONFIG_KEXEC_FILE
 static int copy_file_from_fd(int fd, void **buf, unsigned long *buf_len)
 {
        struct fd f = fdget(fd);
@@ -416,6 +419,12 @@ void __weak arch_kimage_file_post_load_cleanup(struct kimage *image)
 {
 }
 
+int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
+                                       unsigned long buf_len)
+{
+       return -EKEYREJECTED;
+}
+
 /* Apply relocations of type RELA */
 int __weak
 arch_kexec_apply_relocations_add(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
@@ -494,6 +503,15 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
        if (ret)
                goto out;
 
+#ifdef CONFIG_KEXEC_VERIFY_SIG
+       ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
+                                          image->kernel_buf_len);
+       if (ret) {
+               pr_debug("kernel signature verification failed.\n");
+               goto out;
+       }
+       pr_debug("kernel signature verification successful.\n");
+#endif
        /* It is possible that there no initramfs is being loaded */
        if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
                ret = copy_file_from_fd(initrd_fd, &image->initrd_buf,
@@ -548,6 +566,7 @@ kimage_file_alloc_init(struct kimage **rimage, int kernel_fd,
 {
        int ret;
        struct kimage *image;
+       bool kexec_on_panic = flags & KEXEC_FILE_ON_CRASH;
 
        image = do_kimage_alloc_init();
        if (!image)
@@ -555,6 +574,12 @@ kimage_file_alloc_init(struct kimage **rimage, int kernel_fd,
 
        image->file_mode = 1;
 
+       if (kexec_on_panic) {
+               /* Enable special crash kernel control page alloc policy. */
+               image->control_page = crashk_res.start;
+               image->type = KEXEC_TYPE_CRASH;
+       }
+
        ret = kimage_file_prepare_segments(image, kernel_fd, initrd_fd,
                                           cmdline_ptr, cmdline_len, flags);
        if (ret)
@@ -572,10 +597,12 @@ kimage_file_alloc_init(struct kimage **rimage, int kernel_fd,
                goto out_free_post_load_bufs;
        }
 
-       image->swap_page = kimage_alloc_control_pages(image, 0);
-       if (!image->swap_page) {
-               pr_err(KERN_ERR "Could not allocate swap buffer\n");
-               goto out_free_control_pages;
+       if (!kexec_on_panic) {
+               image->swap_page = kimage_alloc_control_pages(image, 0);
+               if (!image->swap_page) {
+                       pr_err(KERN_ERR "Could not allocate swap buffer\n");
+                       goto out_free_control_pages;
+               }
        }
 
        *rimage = image;
@@ -588,6 +615,9 @@ out_free_image:
        kfree(image);
        return ret;
 }
+#else /* CONFIG_KEXEC_FILE */
+static inline void kimage_file_post_load_cleanup(struct kimage *image) { }
+#endif /* CONFIG_KEXEC_FILE */
 
 static int kimage_is_destination_range(struct kimage *image,
                                        unsigned long start,
@@ -1113,10 +1143,14 @@ static int kimage_load_crash_segment(struct kimage *image,
        unsigned long maddr;
        size_t ubytes, mbytes;
        int result;
-       unsigned char __user *buf;
+       unsigned char __user *buf = NULL;
+       unsigned char *kbuf = NULL;
 
        result = 0;
-       buf = segment->buf;
+       if (image->file_mode)
+               kbuf = segment->kbuf;
+       else
+               buf = segment->buf;
        ubytes = segment->bufsz;
        mbytes = segment->memsz;
        maddr = segment->mem;
@@ -1139,7 +1173,12 @@ static int kimage_load_crash_segment(struct kimage *image,
                        /* Zero the trailing part of the page */
                        memset(ptr + uchunk, 0, mchunk - uchunk);
                }
-               result = copy_from_user(ptr, buf, uchunk);
+
+               /* For file based kexec, source pages are in kernel memory */
+               if (image->file_mode)
+                       memcpy(ptr, kbuf, uchunk);
+               else
+                       result = copy_from_user(ptr, buf, uchunk);
                kexec_flush_icache_page(page);
                kunmap(page);
                if (result) {
@@ -1148,7 +1187,10 @@ static int kimage_load_crash_segment(struct kimage *image,
                }
                ubytes -= uchunk;
                maddr  += mchunk;
-               buf += mchunk;
+               if (image->file_mode)
+                       kbuf += mchunk;
+               else
+                       buf += mchunk;
                mbytes -= mchunk;
        }
 out:
@@ -1339,6 +1381,7 @@ COMPAT_SYSCALL_DEFINE4(kexec_load, compat_ulong_t, entry,
 }
 #endif
 
+#ifdef CONFIG_KEXEC_FILE
 SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
                unsigned long, cmdline_len, const char __user *, cmdline_ptr,
                unsigned long, flags)
@@ -1415,6 +1458,8 @@ out:
        return ret;
 }
 
+#endif /* CONFIG_KEXEC_FILE */
+
 void crash_kexec(struct pt_regs *regs)
 {
        /* Take the kexec_mutex here to prevent sys_kexec_load
@@ -1970,6 +2015,7 @@ static int __init crash_save_vmcoreinfo_init(void)
 
 subsys_initcall(crash_save_vmcoreinfo_init);
 
+#ifdef CONFIG_KEXEC_FILE
 static int __kexec_add_segment(struct kimage *image, char *buf,
                               unsigned long bufsz, unsigned long mem,
                               unsigned long memsz)
@@ -2127,7 +2173,14 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
        kbuf->top_down = top_down;
 
        /* Walk the RAM ranges and allocate a suitable range for the buffer */
-       ret = walk_system_ram_res(0, -1, kbuf, locate_mem_hole_callback);
+       if (image->type == KEXEC_TYPE_CRASH)
+               ret = walk_iomem_res("Crash kernel",
+                                    IORESOURCE_MEM | IORESOURCE_BUSY,
+                                    crashk_res.start, crashk_res.end, kbuf,
+                                    locate_mem_hole_callback);
+       else
+               ret = walk_system_ram_res(0, -1, kbuf,
+                                         locate_mem_hole_callback);
        if (ret != 1) {
                /* A suitable memory range could not be found for buffer */
                return -EADDRNOTAVAIL;
@@ -2639,6 +2692,7 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
 
        return 0;
 }
+#endif /* CONFIG_KEXEC_FILE */
 
 /*
  * Move into place and start executing a preloaded standalone