Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Load ELF vmlinux file for the kexec_file_load syscall.
0004  *
0005  * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
0006  * Copyright (C) 2004  IBM Corp.
0007  * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
0008  * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
0009  * Copyright (C) 2016  IBM Corporation
0010  *
0011  * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
0012  * Heavily modified for the kernel by
0013  * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
0014  */
0015 
0016 #define pr_fmt(fmt) "kexec_elf: " fmt
0017 
0018 #include <linux/elf.h>
0019 #include <linux/kexec.h>
0020 #include <linux/libfdt.h>
0021 #include <linux/module.h>
0022 #include <linux/of.h>
0023 #include <linux/of_fdt.h>
0024 #include <linux/slab.h>
0025 #include <linux/types.h>
0026 
0027 static void *elf64_load(struct kimage *image, char *kernel_buf,
0028             unsigned long kernel_len, char *initrd,
0029             unsigned long initrd_len, char *cmdline,
0030             unsigned long cmdline_len)
0031 {
0032     int ret;
0033     unsigned long kernel_load_addr;
0034     unsigned long initrd_load_addr = 0, fdt_load_addr;
0035     void *fdt;
0036     const void *slave_code;
0037     struct elfhdr ehdr;
0038     char *modified_cmdline = NULL;
0039     struct kexec_elf_info elf_info;
0040     struct kexec_buf kbuf = { .image = image, .buf_min = 0,
0041                   .buf_max = ppc64_rma_size };
0042     struct kexec_buf pbuf = { .image = image, .buf_min = 0,
0043                   .buf_max = ppc64_rma_size, .top_down = true,
0044                   .mem = KEXEC_BUF_MEM_UNKNOWN };
0045 
0046     ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
0047     if (ret)
0048         return ERR_PTR(ret);
0049 
0050     if (image->type == KEXEC_TYPE_CRASH) {
0051         /* min & max buffer values for kdump case */
0052         kbuf.buf_min = pbuf.buf_min = crashk_res.start;
0053         kbuf.buf_max = pbuf.buf_max =
0054                 ((crashk_res.end < ppc64_rma_size) ?
0055                  crashk_res.end : (ppc64_rma_size - 1));
0056     }
0057 
0058     ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
0059     if (ret)
0060         goto out;
0061 
0062     pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr);
0063 
0064     ret = kexec_load_purgatory(image, &pbuf);
0065     if (ret) {
0066         pr_err("Loading purgatory failed.\n");
0067         goto out;
0068     }
0069 
0070     pr_debug("Loaded purgatory at 0x%lx\n", pbuf.mem);
0071 
0072     /* Load additional segments needed for panic kernel */
0073     if (image->type == KEXEC_TYPE_CRASH) {
0074         ret = load_crashdump_segments_ppc64(image, &kbuf);
0075         if (ret) {
0076             pr_err("Failed to load kdump kernel segments\n");
0077             goto out;
0078         }
0079 
0080         /* Setup cmdline for kdump kernel case */
0081         modified_cmdline = setup_kdump_cmdline(image, cmdline,
0082                                cmdline_len);
0083         if (!modified_cmdline) {
0084             pr_err("Setting up cmdline for kdump kernel failed\n");
0085             ret = -EINVAL;
0086             goto out;
0087         }
0088         cmdline = modified_cmdline;
0089     }
0090 
0091     if (initrd != NULL) {
0092         kbuf.buffer = initrd;
0093         kbuf.bufsz = kbuf.memsz = initrd_len;
0094         kbuf.buf_align = PAGE_SIZE;
0095         kbuf.top_down = false;
0096         kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
0097         ret = kexec_add_buffer(&kbuf);
0098         if (ret)
0099             goto out;
0100         initrd_load_addr = kbuf.mem;
0101 
0102         pr_debug("Loaded initrd at 0x%lx\n", initrd_load_addr);
0103     }
0104 
0105     fdt = of_kexec_alloc_and_setup_fdt(image, initrd_load_addr,
0106                        initrd_len, cmdline,
0107                        kexec_extra_fdt_size_ppc64(image));
0108     if (!fdt) {
0109         pr_err("Error setting up the new device tree.\n");
0110         ret = -EINVAL;
0111         goto out;
0112     }
0113 
0114     ret = setup_new_fdt_ppc64(image, fdt, initrd_load_addr,
0115                   initrd_len, cmdline);
0116     if (ret)
0117         goto out_free_fdt;
0118 
0119     fdt_pack(fdt);
0120 
0121     kbuf.buffer = fdt;
0122     kbuf.bufsz = kbuf.memsz = fdt_totalsize(fdt);
0123     kbuf.buf_align = PAGE_SIZE;
0124     kbuf.top_down = true;
0125     kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
0126     ret = kexec_add_buffer(&kbuf);
0127     if (ret)
0128         goto out_free_fdt;
0129 
0130     /* FDT will be freed in arch_kimage_file_post_load_cleanup */
0131     image->arch.fdt = fdt;
0132 
0133     fdt_load_addr = kbuf.mem;
0134 
0135     pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr);
0136 
0137     slave_code = elf_info.buffer + elf_info.proghdrs[0].p_offset;
0138     ret = setup_purgatory_ppc64(image, slave_code, fdt, kernel_load_addr,
0139                     fdt_load_addr);
0140     if (ret)
0141         pr_err("Error setting up the purgatory.\n");
0142 
0143     goto out;
0144 
0145 out_free_fdt:
0146     kvfree(fdt);
0147 out:
0148     kfree(modified_cmdline);
0149     kexec_free_elf_info(&elf_info);
0150 
0151     return ret ? ERR_PTR(ret) : NULL;
0152 }
0153 
0154 const struct kexec_file_ops kexec_elf64_ops = {
0155     .probe = kexec_elf_probe,
0156     .load = elf64_load,
0157 };