pinctrl: at91-pio4: add missing of_node_put
[linux-2.6-block.git] / kernel / kexec_file.c
index 6f0ec9f2e5c149ff622423e13f0203a763a83bad..75d8e7cf040e69bfcde16fb555c09054cfe9ebad 100644 (file)
@@ -730,8 +730,8 @@ static int kexec_purgatory_setup_kbuf(struct purgatory_info *pi,
        int i, ret;
 
        sechdrs = (void *)pi->ehdr + pi->ehdr->e_shoff;
-       bss_align = 1;
-       bss_sz = 0;
+       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))
@@ -763,7 +763,6 @@ static int kexec_purgatory_setup_kbuf(struct purgatory_info *pi,
        ret = kexec_add_buffer(kbuf);
        if (ret)
                goto out;
-       pi->purgatory_load_addr = kbuf->mem;
 
        return 0;
 out:
@@ -785,15 +784,15 @@ out:
 static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
                                         struct kexec_buf *kbuf)
 {
-       unsigned long curr_load_addr;
-       unsigned long load_addr;
        unsigned long bss_addr;
        unsigned long offset;
-       unsigned char *buf_addr;
-       unsigned char *src;
        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;
@@ -801,42 +800,18 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
               pi->ehdr->e_shnum * sizeof(Elf_Shdr));
        pi->sechdrs = sechdrs;
 
-       /*
-        * 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;
-       }
-
-       /* Load SHF_ALLOC sections */
-       buf_addr = kbuf->buffer;
-       load_addr = curr_load_addr = kbuf->mem;
-       bss_addr = load_addr + kbuf->bufsz;
+       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) {
                        bss_addr = ALIGN(bss_addr, align);
                        sechdrs[i].sh_addr = bss_addr;
@@ -844,31 +819,22 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
                        continue;
                }
 
-               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);
-
+               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 += curr_load_addr;
+                       kbuf->image->start += kbuf->mem + offset;
                }
 
-               /* Store load address and source address of section */
-               sechdrs[i].sh_addr = curr_load_addr;
+               src = (void *)pi->ehdr + sechdrs[i].sh_offset;
+               dst = pi->purgatory_buf + offset;
+               memcpy(dst, src, sechdrs[i].sh_size);
 
-               /*
-                * 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;
+               sechdrs[i].sh_addr = kbuf->mem + offset;
+               sechdrs[i].sh_offset = offset;
+               offset += sechdrs[i].sh_size;
        }
 
        return 0;
@@ -934,27 +900,32 @@ 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;
-       struct kexec_buf kbuf = { .image = image, .bufsz = 0, .buf_align = 1,
-                                 .buf_min = min, .buf_max = max,
-                                 .top_down = top_down };
 
        if (kexec_purgatory_size <= 0)
                return -EINVAL;
 
        pi->ehdr = (const Elf_Ehdr *)kexec_purgatory;
 
-       ret = kexec_purgatory_setup_kbuf(pi, &kbuf);
+       ret = kexec_purgatory_setup_kbuf(pi, kbuf);
        if (ret)
                return ret;
 
-       ret = kexec_purgatory_setup_sechdrs(pi, &kbuf);
+       ret = kexec_purgatory_setup_sechdrs(pi, kbuf);
        if (ret)
                goto out_free_kbuf;
 
@@ -962,7 +933,6 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min,
        if (ret)
                goto out;
 
-       *load_addr = pi->purgatory_load_addr;
        return 0;
 out:
        vfree(pi->sechdrs);
@@ -1077,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);