Merge branch 'akpm' (second patchbomb from Andrew Morton)
[pandora-kernel.git] / kernel / kexec.c
index 669e331..0b49a0a 100644 (file)
@@ -416,6 +416,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,
@@ -460,6 +466,14 @@ static void kimage_file_post_load_cleanup(struct kimage *image)
 
        /* See if architecture has anything to cleanup post load */
        arch_kimage_file_post_load_cleanup(image);
+
+       /*
+        * Above call should have called into bootloader to free up
+        * any data stored in kimage->image_loader_data. It should
+        * be ok now to free it up.
+        */
+       kfree(image->image_loader_data);
+       image->image_loader_data = NULL;
 }
 
 /*
@@ -486,6 +500,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,
@@ -540,6 +563,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)
@@ -547,6 +571,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)
@@ -564,10 +594,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;
@@ -576,7 +608,6 @@ out_free_control_pages:
        kimage_free_page_list(&image->control_pages);
 out_free_post_load_bufs:
        kimage_file_post_load_cleanup(image);
-       kfree(image->image_loader_data);
 out_free_image:
        kfree(image);
        return ret;
@@ -900,8 +931,6 @@ static void kimage_free(struct kimage *image)
        /* Free the kexec control pages... */
        kimage_free_page_list(&image->control_pages);
 
-       kfree(image->image_loader_data);
-
        /*
         * Free up any temporary buffers allocated. This might hit if
         * error occurred much later after buffer allocation.
@@ -1108,10 +1137,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;
@@ -1134,7 +1167,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) {
@@ -1143,7 +1181,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:
@@ -2122,7 +2163,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;