pinctrl: at91-pio4: add missing of_node_put
[linux-2.6-block.git] / kernel / kexec_file.c
index 9bd1ec1dd8753ffb712fbaffc8de410310b91497..75d8e7cf040e69bfcde16fb555c09054cfe9ebad 100644 (file)
@@ -110,19 +110,35 @@ int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
 }
 #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;
@@ -694,87 +710,28 @@ out:
 }
 
 #ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY
-/* 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)
+/*
+ * 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))
@@ -782,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;
 
-               if (sechdrs[i].sh_type != SHT_RELA &&
-                   sechdrs[i].sh_type != SHT_REL)
+               relsec = sechdrs + i;
+
+               if (relsec->sh_type != SHT_RELA &&
+                   relsec->sh_type != SHT_REL)
                        continue;
 
                /*
@@ -895,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;
@@ -917,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;
        }
@@ -930,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;
@@ -943,20 +921,23 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min,
 
        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;
@@ -1066,7 +1047,7 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
                return -EINVAL;
        }
 
-       sym_buf = (char *)sec->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);