Merge tag 'ecryptfs-4.17-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / kernel / kexec_file.c
index e5bcd94..75d8e7c 100644 (file)
 #include <linux/ima.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/kernel.h>
+#include <linux/kexec.h>
+#include <linux/slab.h>
 #include <linux/syscalls.h>
 #include <linux/vmalloc.h>
 #include "kexec_internal.h"
 
 static int kexec_calculate_store_digests(struct kimage *image);
 
+/*
+ * Currently this is the only default function that is exported as some
+ * architectures need it to do additional handlings.
+ * In the future, other default functions may be exported too if required.
+ */
+int kexec_image_probe_default(struct kimage *image, void *buf,
+                             unsigned long buf_len)
+{
+       const struct kexec_file_ops * const *fops;
+       int ret = -ENOEXEC;
+
+       for (fops = &kexec_file_loaders[0]; *fops && (*fops)->probe; ++fops) {
+               ret = (*fops)->probe(buf, buf_len);
+               if (!ret) {
+                       image->fops = *fops;
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
 /* Architectures can provide this probe function */
 int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
                                         unsigned long buf_len)
 {
-       return -ENOEXEC;
+       return kexec_image_probe_default(image, buf, buf_len);
+}
+
+static void *kexec_image_load_default(struct kimage *image)
+{
+       if (!image->fops || !image->fops->load)
+               return ERR_PTR(-ENOEXEC);
+
+       return image->fops->load(image, image->kernel_buf,
+                                image->kernel_buf_len, image->initrd_buf,
+                                image->initrd_buf_len, image->cmdline_buf,
+                                image->cmdline_buf_len);
 }
 
 void * __weak arch_kexec_kernel_image_load(struct kimage *image)
 {
-       return ERR_PTR(-ENOEXEC);
+       return kexec_image_load_default(image);
+}
+
+static int kexec_image_post_load_cleanup_default(struct kimage *image)
+{
+       if (!image->fops || !image->fops->cleanup)
+               return 0;
+
+       return image->fops->cleanup(image->image_loader_data);
 }
 
 int __weak arch_kimage_file_post_load_cleanup(struct kimage *image)
 {
-       return -EINVAL;
+       return kexec_image_post_load_cleanup_default(image);
 }
 
 #ifdef CONFIG_KEXEC_VERIFY_SIG
+static int kexec_image_verify_sig_default(struct kimage *image, void *buf,
+                                         unsigned long buf_len)
+{
+       if (!image->fops || !image->fops->verify_sig) {
+               pr_debug("kernel loader does not support signature verification.\n");
+               return -EKEYREJECTED;
+       }
+
+       return image->fops->verify_sig(buf, buf_len);
+}
+
 int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
                                        unsigned long buf_len)
 {
-       return -EKEYREJECTED;
+       return kexec_image_verify_sig_default(image, buf, buf_len);
 }
 #endif
 
-/* Apply relocations of type RELA */
+/*
+ * arch_kexec_apply_relocations_add - apply relocations of type RELA
+ * @pi:                Purgatory to be relocated.
+ * @section:   Section relocations applying to.
+ * @relsec:    Section containing RELAs.
+ * @symtab:    Corresponding symtab.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
 int __weak
-arch_kexec_apply_relocations_add(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
-                                unsigned int relsec)
+arch_kexec_apply_relocations_add(struct purgatory_info *pi, Elf_Shdr *section,
+                                const Elf_Shdr *relsec, const Elf_Shdr *symtab)
 {
        pr_err("RELA relocation unsupported.\n");
        return -ENOEXEC;
 }
 
-/* Apply relocations of type REL */
+/*
+ * arch_kexec_apply_relocations - apply relocations of type REL
+ * @pi:                Purgatory to be relocated.
+ * @section:   Section relocations applying to.
+ * @relsec:    Section containing RELs.
+ * @symtab:    Corresponding symtab.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
 int __weak
-arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
-                            unsigned int relsec)
+arch_kexec_apply_relocations(struct purgatory_info *pi, Elf_Shdr *section,
+                            const Elf_Shdr *relsec, const Elf_Shdr *symtab)
 {
        pr_err("REL relocation unsupported.\n");
        return -ENOEXEC;
@@ -532,6 +605,9 @@ static int kexec_calculate_store_digests(struct kimage *image)
        struct kexec_sha_region *sha_regions;
        struct purgatory_info *pi = &image->purgatory_info;
 
+       if (!IS_ENABLED(CONFIG_ARCH_HAS_KEXEC_PURGATORY))
+               return 0;
+
        zero_buf = __va(page_to_pfn(ZERO_PAGE(0)) << PAGE_SHIFT);
        zero_buf_sz = PAGE_SIZE;
 
@@ -633,87 +709,29 @@ out:
        return ret;
 }
 
-/* Actually load purgatory. Lot of code taken from kexec-tools */
-static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
-                                 unsigned long max, int top_down)
+#ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY
+/*
+ * kexec_purgatory_setup_kbuf - prepare buffer to load purgatory.
+ * @pi:                Purgatory to be loaded.
+ * @kbuf:      Buffer to setup.
+ *
+ * Allocates the memory needed for the buffer. Caller is responsible to free
+ * the memory after use.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int kexec_purgatory_setup_kbuf(struct purgatory_info *pi,
+                                     struct kexec_buf *kbuf)
 {
-       struct purgatory_info *pi = &image->purgatory_info;
-       unsigned long align, bss_align, bss_sz, bss_pad;
-       unsigned long entry, load_addr, curr_load_addr, bss_addr, offset;
-       unsigned char *buf_addr, *src;
-       int i, ret = 0, entry_sidx = -1;
-       const Elf_Shdr *sechdrs_c;
-       Elf_Shdr *sechdrs = NULL;
-       struct kexec_buf kbuf = { .image = image, .bufsz = 0, .buf_align = 1,
-                                 .buf_min = min, .buf_max = max,
-                                 .top_down = top_down };
-
-       /*
-        * sechdrs_c points to section headers in purgatory and are read
-        * only. No modifications allowed.
-        */
-       sechdrs_c = (void *)pi->ehdr + pi->ehdr->e_shoff;
-
-       /*
-        * We can not modify sechdrs_c[] and its fields. It is read only.
-        * Copy it over to a local copy where one can store some temporary
-        * data and free it at the end. We need to modify ->sh_addr and
-        * ->sh_offset fields to keep track of permanent and temporary
-        * locations of sections.
-        */
-       sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr));
-       if (!sechdrs)
-               return -ENOMEM;
-
-       memcpy(sechdrs, sechdrs_c, pi->ehdr->e_shnum * sizeof(Elf_Shdr));
-
-       /*
-        * We seem to have multiple copies of sections. First copy is which
-        * is embedded in kernel in read only section. Some of these sections
-        * will be copied to a temporary buffer and relocated. And these
-        * sections will finally be copied to their final destination at
-        * segment load time.
-        *
-        * Use ->sh_offset to reflect section address in memory. It will
-        * point to original read only copy if section is not allocatable.
-        * Otherwise it will point to temporary copy which will be relocated.
-        *
-        * Use ->sh_addr to contain final address of the section where it
-        * will go during execution time.
-        */
-       for (i = 0; i < pi->ehdr->e_shnum; i++) {
-               if (sechdrs[i].sh_type == SHT_NOBITS)
-                       continue;
-
-               sechdrs[i].sh_offset = (unsigned long)pi->ehdr +
-                                               sechdrs[i].sh_offset;
-       }
-
-       /*
-        * Identify entry point section and make entry relative to section
-        * start.
-        */
-       entry = pi->ehdr->e_entry;
-       for (i = 0; i < pi->ehdr->e_shnum; i++) {
-               if (!(sechdrs[i].sh_flags & SHF_ALLOC))
-                       continue;
-
-               if (!(sechdrs[i].sh_flags & SHF_EXECINSTR))
-                       continue;
-
-               /* Make entry section relative */
-               if (sechdrs[i].sh_addr <= pi->ehdr->e_entry &&
-                   ((sechdrs[i].sh_addr + sechdrs[i].sh_size) >
-                    pi->ehdr->e_entry)) {
-                       entry_sidx = i;
-                       entry -= sechdrs[i].sh_addr;
-                       break;
-               }
-       }
+       const Elf_Shdr *sechdrs;
+       unsigned long bss_align;
+       unsigned long bss_sz;
+       unsigned long align;
+       int i, ret;
 
-       /* Determine how much memory is needed to load relocatable object. */
-       bss_align = 1;
-       bss_sz = 0;
+       sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
+       kbuf->buf_align = bss_align = 1;
+       kbuf->bufsz = bss_sz = 0;
 
        for (i = 0; i < pi->ehdr->e_shnum; i++) {
                if (!(sechdrs[i].sh_flags & SHF_ALLOC))
@@ -721,111 +739,124 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
 
                align = sechdrs[i].sh_addralign;
                if (sechdrs[i].sh_type != SHT_NOBITS) {
-                       if (kbuf.buf_align < align)
-                               kbuf.buf_align = align;
-                       kbuf.bufsz = ALIGN(kbuf.bufsz, align);
-                       kbuf.bufsz += sechdrs[i].sh_size;
+                       if (kbuf->buf_align < align)
+                               kbuf->buf_align = align;
+                       kbuf->bufsz = ALIGN(kbuf->bufsz, align);
+                       kbuf->bufsz += sechdrs[i].sh_size;
                } else {
-                       /* bss section */
                        if (bss_align < align)
                                bss_align = align;
                        bss_sz = ALIGN(bss_sz, align);
                        bss_sz += sechdrs[i].sh_size;
                }
        }
+       kbuf->bufsz = ALIGN(kbuf->bufsz, bss_align);
+       kbuf->memsz = kbuf->bufsz + bss_sz;
+       if (kbuf->buf_align < bss_align)
+               kbuf->buf_align = bss_align;
 
-       /* Determine the bss padding required to align bss properly */
-       bss_pad = 0;
-       if (kbuf.bufsz & (bss_align - 1))
-               bss_pad = bss_align - (kbuf.bufsz & (bss_align - 1));
-
-       kbuf.memsz = kbuf.bufsz + bss_pad + bss_sz;
+       kbuf->buffer = vzalloc(kbuf->bufsz);
+       if (!kbuf->buffer)
+               return -ENOMEM;
+       pi->purgatory_buf = kbuf->buffer;
 
-       /* Allocate buffer for purgatory */
-       kbuf.buffer = vzalloc(kbuf.bufsz);
-       if (!kbuf.buffer) {
-               ret = -ENOMEM;
+       ret = kexec_add_buffer(kbuf);
+       if (ret)
                goto out;
-       }
 
-       if (kbuf.buf_align < bss_align)
-               kbuf.buf_align = bss_align;
+       return 0;
+out:
+       vfree(pi->purgatory_buf);
+       pi->purgatory_buf = NULL;
+       return ret;
+}
 
-       /* Add buffer to segment list */
-       ret = kexec_add_buffer(&kbuf);
-       if (ret)
-               goto out;
-       pi->purgatory_load_addr = kbuf.mem;
+/*
+ * kexec_purgatory_setup_sechdrs - prepares the pi->sechdrs buffer.
+ * @pi:                Purgatory to be loaded.
+ * @kbuf:      Buffer prepared to store purgatory.
+ *
+ * Allocates the memory needed for the buffer. Caller is responsible to free
+ * the memory after use.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
+                                        struct kexec_buf *kbuf)
+{
+       unsigned long bss_addr;
+       unsigned long offset;
+       Elf_Shdr *sechdrs;
+       int i;
+
+       /*
+        * The section headers in kexec_purgatory are read-only. In order to
+        * have them modifiable make a temporary copy.
+        */
+       sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr));
+       if (!sechdrs)
+               return -ENOMEM;
+       memcpy(sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff,
+              pi->ehdr->e_shnum * sizeof(Elf_Shdr));
+       pi->sechdrs = sechdrs;
 
-       /* Load SHF_ALLOC sections */
-       buf_addr = kbuf.buffer;
-       load_addr = curr_load_addr = pi->purgatory_load_addr;
-       bss_addr = load_addr + kbuf.bufsz + bss_pad;
+       offset = 0;
+       bss_addr = kbuf->mem + kbuf->bufsz;
+       kbuf->image->start = pi->ehdr->e_entry;
 
        for (i = 0; i < pi->ehdr->e_shnum; i++) {
+               unsigned long align;
+               void *src, *dst;
+
                if (!(sechdrs[i].sh_flags & SHF_ALLOC))
                        continue;
 
                align = sechdrs[i].sh_addralign;
-               if (sechdrs[i].sh_type != SHT_NOBITS) {
-                       curr_load_addr = ALIGN(curr_load_addr, align);
-                       offset = curr_load_addr - load_addr;
-                       /* We already modifed ->sh_offset to keep src addr */
-                       src = (char *) sechdrs[i].sh_offset;
-                       memcpy(buf_addr + offset, src, sechdrs[i].sh_size);
-
-                       /* Store load address and source address of section */
-                       sechdrs[i].sh_addr = curr_load_addr;
-
-                       /*
-                        * This section got copied to temporary buffer. Update
-                        * ->sh_offset accordingly.
-                        */
-                       sechdrs[i].sh_offset = (unsigned long)(buf_addr + offset);
-
-                       /* Advance to the next address */
-                       curr_load_addr += sechdrs[i].sh_size;
-               } else {
+               if (sechdrs[i].sh_type == SHT_NOBITS) {
                        bss_addr = ALIGN(bss_addr, align);
                        sechdrs[i].sh_addr = bss_addr;
                        bss_addr += sechdrs[i].sh_size;
+                       continue;
                }
-       }
 
-       /* Update entry point based on load address of text section */
-       if (entry_sidx >= 0)
-               entry += sechdrs[entry_sidx].sh_addr;
+               offset = ALIGN(offset, align);
+               if (sechdrs[i].sh_flags & SHF_EXECINSTR &&
+                   pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
+                   pi->ehdr->e_entry < (sechdrs[i].sh_addr
+                                        + sechdrs[i].sh_size)) {
+                       kbuf->image->start -= sechdrs[i].sh_addr;
+                       kbuf->image->start += kbuf->mem + offset;
+               }
 
-       /* Make kernel jump to purgatory after shutdown */
-       image->start = entry;
+               src = (void *)pi->ehdr + sechdrs[i].sh_offset;
+               dst = pi->purgatory_buf + offset;
+               memcpy(dst, src, sechdrs[i].sh_size);
 
-       /* Used later to get/set symbol values */
-       pi->sechdrs = sechdrs;
+               sechdrs[i].sh_addr = kbuf->mem + offset;
+               sechdrs[i].sh_offset = offset;
+               offset += sechdrs[i].sh_size;
+       }
 
-       /*
-        * Used later to identify which section is purgatory and skip it
-        * from checksumming.
-        */
-       pi->purgatory_buf = kbuf.buffer;
-       return ret;
-out:
-       vfree(sechdrs);
-       vfree(kbuf.buffer);
-       return ret;
+       return 0;
 }
 
 static int kexec_apply_relocations(struct kimage *image)
 {
        int i, ret;
        struct purgatory_info *pi = &image->purgatory_info;
-       Elf_Shdr *sechdrs = pi->sechdrs;
+       const Elf_Shdr *sechdrs;
+
+       sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
 
-       /* Apply relocations */
        for (i = 0; i < pi->ehdr->e_shnum; i++) {
-               Elf_Shdr *section, *symtab;
+               const Elf_Shdr *relsec;
+               const Elf_Shdr *symtab;
+               Elf_Shdr *section;
+
+               relsec = sechdrs + i;
 
-               if (sechdrs[i].sh_type != SHT_RELA &&
-                   sechdrs[i].sh_type != SHT_REL)
+               if (relsec->sh_type != SHT_RELA &&
+                   relsec->sh_type != SHT_REL)
                        continue;
 
                /*
@@ -834,12 +865,12 @@ static int kexec_apply_relocations(struct kimage *image)
                 * symbol table. And ->sh_info contains section header
                 * index of section to which relocations apply.
                 */
-               if (sechdrs[i].sh_info >= pi->ehdr->e_shnum ||
-                   sechdrs[i].sh_link >= pi->ehdr->e_shnum)
+               if (relsec->sh_info >= pi->ehdr->e_shnum ||
+                   relsec->sh_link >= pi->ehdr->e_shnum)
                        return -ENOEXEC;
 
-               section = &sechdrs[sechdrs[i].sh_info];
-               symtab = &sechdrs[sechdrs[i].sh_link];
+               section = pi->sechdrs + relsec->sh_info;
+               symtab = sechdrs + relsec->sh_link;
 
                if (!(section->sh_flags & SHF_ALLOC))
                        continue;
@@ -856,12 +887,12 @@ static int kexec_apply_relocations(struct kimage *image)
                 * Respective architecture needs to provide support for applying
                 * relocations of type SHT_RELA/SHT_REL.
                 */
-               if (sechdrs[i].sh_type == SHT_RELA)
-                       ret = arch_kexec_apply_relocations_add(pi->ehdr,
-                                                              sechdrs, i);
-               else if (sechdrs[i].sh_type == SHT_REL)
-                       ret = arch_kexec_apply_relocations(pi->ehdr,
-                                                          sechdrs, i);
+               if (relsec->sh_type == SHT_RELA)
+                       ret = arch_kexec_apply_relocations_add(pi, section,
+                                                              relsec, symtab);
+               else if (relsec->sh_type == SHT_REL)
+                       ret = arch_kexec_apply_relocations(pi, section,
+                                                          relsec, symtab);
                if (ret)
                        return ret;
        }
@@ -869,10 +900,18 @@ static int kexec_apply_relocations(struct kimage *image)
        return 0;
 }
 
-/* Load relocatable purgatory object and relocate it appropriately */
-int kexec_load_purgatory(struct kimage *image, unsigned long min,
-                        unsigned long max, int top_down,
-                        unsigned long *load_addr)
+/*
+ * kexec_load_purgatory - Load and relocate the purgatory object.
+ * @image:     Image to add the purgatory to.
+ * @kbuf:      Memory parameters to use.
+ *
+ * Allocates the memory needed for image->purgatory_info.sechdrs and
+ * image->purgatory_info.purgatory_buf/kbuf->buffer. Caller is responsible
+ * to free the memory after use.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf)
 {
        struct purgatory_info *pi = &image->purgatory_info;
        int ret;
@@ -880,55 +919,51 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min,
        if (kexec_purgatory_size <= 0)
                return -EINVAL;
 
-       if (kexec_purgatory_size < sizeof(Elf_Ehdr))
-               return -ENOEXEC;
-
-       pi->ehdr = (Elf_Ehdr *)kexec_purgatory;
-
-       if (memcmp(pi->ehdr->e_ident, ELFMAG, SELFMAG) != 0
-           || pi->ehdr->e_type != ET_REL
-           || !elf_check_arch(pi->ehdr)
-           || pi->ehdr->e_shentsize != sizeof(Elf_Shdr))
-               return -ENOEXEC;
-
-       if (pi->ehdr->e_shoff >= kexec_purgatory_size
-           || (pi->ehdr->e_shnum * sizeof(Elf_Shdr) >
-           kexec_purgatory_size - pi->ehdr->e_shoff))
-               return -ENOEXEC;
+       pi->ehdr = (const Elf_Ehdr *)kexec_purgatory;
 
-       ret = __kexec_load_purgatory(image, min, max, top_down);
+       ret = kexec_purgatory_setup_kbuf(pi, kbuf);
        if (ret)
                return ret;
 
+       ret = kexec_purgatory_setup_sechdrs(pi, kbuf);
+       if (ret)
+               goto out_free_kbuf;
+
        ret = kexec_apply_relocations(image);
        if (ret)
                goto out;
 
-       *load_addr = pi->purgatory_load_addr;
        return 0;
 out:
        vfree(pi->sechdrs);
        pi->sechdrs = NULL;
-
+out_free_kbuf:
        vfree(pi->purgatory_buf);
        pi->purgatory_buf = NULL;
        return ret;
 }
 
-static Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
-                                           const char *name)
+/*
+ * kexec_purgatory_find_symbol - find a symbol in the purgatory
+ * @pi:                Purgatory to search in.
+ * @name:      Name of the symbol.
+ *
+ * Return: pointer to symbol in read-only symtab on success, NULL on error.
+ */
+static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
+                                                 const char *name)
 {
-       Elf_Sym *syms;
-       Elf_Shdr *sechdrs;
-       Elf_Ehdr *ehdr;
-       int i, k;
+       const Elf_Shdr *sechdrs;
+       const Elf_Ehdr *ehdr;
+       const Elf_Sym *syms;
        const char *strtab;
+       int i, k;
 
-       if (!pi->sechdrs || !pi->ehdr)
+       if (!pi->ehdr)
                return NULL;
 
-       sechdrs = pi->sechdrs;
        ehdr = pi->ehdr;
+       sechdrs = (void *)ehdr + ehdr->e_shoff;
 
        for (i = 0; i < ehdr->e_shnum; i++) {
                if (sechdrs[i].sh_type != SHT_SYMTAB)
@@ -937,8 +972,8 @@ static Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
                if (sechdrs[i].sh_link >= ehdr->e_shnum)
                        /* Invalid strtab section number */
                        continue;
-               strtab = (char *)sechdrs[sechdrs[i].sh_link].sh_offset;
-               syms = (Elf_Sym *)sechdrs[i].sh_offset;
+               strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
+               syms = (void *)ehdr + sechdrs[i].sh_offset;
 
                /* Go through symbols for a match */
                for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
@@ -966,7 +1001,7 @@ static Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
 void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
 {
        struct purgatory_info *pi = &image->purgatory_info;
-       Elf_Sym *sym;
+       const Elf_Sym *sym;
        Elf_Shdr *sechdr;
 
        sym = kexec_purgatory_find_symbol(pi, name);
@@ -989,9 +1024,9 @@ void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
 int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
                                   void *buf, unsigned int size, bool get_value)
 {
-       Elf_Sym *sym;
-       Elf_Shdr *sechdrs;
        struct purgatory_info *pi = &image->purgatory_info;
+       const Elf_Sym *sym;
+       Elf_Shdr *sec;
        char *sym_buf;
 
        sym = kexec_purgatory_find_symbol(pi, name);
@@ -1004,16 +1039,15 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
                return -EINVAL;
        }
 
-       sechdrs = pi->sechdrs;
+       sec = pi->sechdrs + sym->st_shndx;
 
-       if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) {
+       if (sec->sh_type == SHT_NOBITS) {
                pr_err("symbol %s is in a bss section. Cannot %s\n", name,
                       get_value ? "get" : "set");
                return -EINVAL;
        }
 
-       sym_buf = (unsigned char *)sechdrs[sym->st_shndx].sh_offset +
-                                       sym->st_value;
+       sym_buf = (char *)pi->purgatory_buf + sec->sh_offset + sym->st_value;
 
        if (get_value)
                memcpy((void *)buf, sym_buf, size);
@@ -1022,3 +1056,174 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
 
        return 0;
 }
+#endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */
+
+int crash_exclude_mem_range(struct crash_mem *mem,
+                           unsigned long long mstart, unsigned long long mend)
+{
+       int i, j;
+       unsigned long long start, end;
+       struct crash_mem_range temp_range = {0, 0};
+
+       for (i = 0; i < mem->nr_ranges; i++) {
+               start = mem->ranges[i].start;
+               end = mem->ranges[i].end;
+
+               if (mstart > end || mend < start)
+                       continue;
+
+               /* Truncate any area outside of range */
+               if (mstart < start)
+                       mstart = start;
+               if (mend > end)
+                       mend = end;
+
+               /* Found completely overlapping range */
+               if (mstart == start && mend == end) {
+                       mem->ranges[i].start = 0;
+                       mem->ranges[i].end = 0;
+                       if (i < mem->nr_ranges - 1) {
+                               /* Shift rest of the ranges to left */
+                               for (j = i; j < mem->nr_ranges - 1; j++) {
+                                       mem->ranges[j].start =
+                                               mem->ranges[j+1].start;
+                                       mem->ranges[j].end =
+                                                       mem->ranges[j+1].end;
+                               }
+                       }
+                       mem->nr_ranges--;
+                       return 0;
+               }
+
+               if (mstart > start && mend < end) {
+                       /* Split original range */
+                       mem->ranges[i].end = mstart - 1;
+                       temp_range.start = mend + 1;
+                       temp_range.end = end;
+               } else if (mstart != start)
+                       mem->ranges[i].end = mstart - 1;
+               else
+                       mem->ranges[i].start = mend + 1;
+               break;
+       }
+
+       /* If a split happened, add the split to array */
+       if (!temp_range.end)
+               return 0;
+
+       /* Split happened */
+       if (i == mem->max_nr_ranges - 1)
+               return -ENOMEM;
+
+       /* Location where new range should go */
+       j = i + 1;
+       if (j < mem->nr_ranges) {
+               /* Move over all ranges one slot towards the end */
+               for (i = mem->nr_ranges - 1; i >= j; i--)
+                       mem->ranges[i + 1] = mem->ranges[i];
+       }
+
+       mem->ranges[j].start = temp_range.start;
+       mem->ranges[j].end = temp_range.end;
+       mem->nr_ranges++;
+       return 0;
+}
+
+int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
+                         void **addr, unsigned long *sz)
+{
+       Elf64_Ehdr *ehdr;
+       Elf64_Phdr *phdr;
+       unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz;
+       unsigned char *buf;
+       unsigned int cpu, i;
+       unsigned long long notes_addr;
+       unsigned long mstart, mend;
+
+       /* extra phdr for vmcoreinfo elf note */
+       nr_phdr = nr_cpus + 1;
+       nr_phdr += mem->nr_ranges;
+
+       /*
+        * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping
+        * area (for example, ffffffff80000000 - ffffffffa0000000 on x86_64).
+        * I think this is required by tools like gdb. So same physical
+        * memory will be mapped in two elf headers. One will contain kernel
+        * text virtual addresses and other will have __va(physical) addresses.
+        */
+
+       nr_phdr++;
+       elf_sz = sizeof(Elf64_Ehdr) + nr_phdr * sizeof(Elf64_Phdr);
+       elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN);
+
+       buf = vzalloc(elf_sz);
+       if (!buf)
+               return -ENOMEM;
+
+       ehdr = (Elf64_Ehdr *)buf;
+       phdr = (Elf64_Phdr *)(ehdr + 1);
+       memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+       ehdr->e_ident[EI_CLASS] = ELFCLASS64;
+       ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+       ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+       ehdr->e_ident[EI_OSABI] = ELF_OSABI;
+       memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD);
+       ehdr->e_type = ET_CORE;
+       ehdr->e_machine = ELF_ARCH;
+       ehdr->e_version = EV_CURRENT;
+       ehdr->e_phoff = sizeof(Elf64_Ehdr);
+       ehdr->e_ehsize = sizeof(Elf64_Ehdr);
+       ehdr->e_phentsize = sizeof(Elf64_Phdr);
+
+       /* Prepare one phdr of type PT_NOTE for each present cpu */
+       for_each_present_cpu(cpu) {
+               phdr->p_type = PT_NOTE;
+               notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu));
+               phdr->p_offset = phdr->p_paddr = notes_addr;
+               phdr->p_filesz = phdr->p_memsz = sizeof(note_buf_t);
+               (ehdr->e_phnum)++;
+               phdr++;
+       }
+
+       /* Prepare one PT_NOTE header for vmcoreinfo */
+       phdr->p_type = PT_NOTE;
+       phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note();
+       phdr->p_filesz = phdr->p_memsz = VMCOREINFO_NOTE_SIZE;
+       (ehdr->e_phnum)++;
+       phdr++;
+
+       /* Prepare PT_LOAD type program header for kernel text region */
+       if (kernel_map) {
+               phdr->p_type = PT_LOAD;
+               phdr->p_flags = PF_R|PF_W|PF_X;
+               phdr->p_vaddr = (Elf64_Addr)_text;
+               phdr->p_filesz = phdr->p_memsz = _end - _text;
+               phdr->p_offset = phdr->p_paddr = __pa_symbol(_text);
+               ehdr->e_phnum++;
+               phdr++;
+       }
+
+       /* Go through all the ranges in mem->ranges[] and prepare phdr */
+       for (i = 0; i < mem->nr_ranges; i++) {
+               mstart = mem->ranges[i].start;
+               mend = mem->ranges[i].end;
+
+               phdr->p_type = PT_LOAD;
+               phdr->p_flags = PF_R|PF_W|PF_X;
+               phdr->p_offset  = mstart;
+
+               phdr->p_paddr = mstart;
+               phdr->p_vaddr = (unsigned long long) __va(mstart);
+               phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
+               phdr->p_align = 0;
+               ehdr->e_phnum++;
+               phdr++;
+               pr_debug("Crash PT_LOAD elf header. phdr=%p vaddr=0x%llx, paddr=0x%llx, sz=0x%llx e_phnum=%d p_offset=0x%llx\n",
+                       phdr, phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz,
+                       ehdr->e_phnum, phdr->p_offset);
+       }
+
+       *addr = buf;
+       *sz = elf_sz;
+       return 0;
+}