Merge uncontroversial parts of branch 'readlink' of git://git.kernel.org/pub/scm...
[sfrench/cifs-2.6.git] / kernel / kexec_file.c
index 037c321c56188f9fbb35193072f08052d6e4771f..0c2df7f737925b6d0bd5bf624ceb12e8a6d53d86 100644 (file)
@@ -428,25 +428,65 @@ static int locate_mem_hole_callback(u64 start, u64 end, void *arg)
        return locate_mem_hole_bottom_up(start, end, kbuf);
 }
 
-/*
- * Helper function for placing a buffer in a kexec segment. This assumes
- * that kexec_mutex is held.
+/**
+ * arch_kexec_walk_mem - call func(data) on free memory regions
+ * @kbuf:      Context info for the search. Also passed to @func.
+ * @func:      Function to call for each memory region.
+ *
+ * Return: The memory walk will stop when func returns a non-zero value
+ * and that value will be returned. If all free regions are visited without
+ * func returning non-zero, then zero will be returned.
+ */
+int __weak arch_kexec_walk_mem(struct kexec_buf *kbuf,
+                              int (*func)(u64, u64, void *))
+{
+       if (kbuf->image->type == KEXEC_TYPE_CRASH)
+               return walk_iomem_res_desc(crashk_res.desc,
+                                          IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY,
+                                          crashk_res.start, crashk_res.end,
+                                          kbuf, func);
+       else
+               return walk_system_ram_res(0, ULONG_MAX, kbuf, func);
+}
+
+/**
+ * kexec_locate_mem_hole - find free memory for the purgatory or the next kernel
+ * @kbuf:      Parameters for the memory search.
+ *
+ * On success, kbuf->mem will have the start address of the memory region found.
+ *
+ * Return: 0 on success, negative errno on error.
  */
-int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
-                    unsigned long memsz, unsigned long buf_align,
-                    unsigned long buf_min, unsigned long buf_max,
-                    bool top_down, unsigned long *load_addr)
+int kexec_locate_mem_hole(struct kexec_buf *kbuf)
+{
+       int ret;
+
+       ret = arch_kexec_walk_mem(kbuf, locate_mem_hole_callback);
+
+       return ret == 1 ? 0 : -EADDRNOTAVAIL;
+}
+
+/**
+ * kexec_add_buffer - place a buffer in a kexec segment
+ * @kbuf:      Buffer contents and memory parameters.
+ *
+ * This function assumes that kexec_mutex is held.
+ * On successful return, @kbuf->mem will have the physical address of
+ * the buffer in memory.
+ *
+ * Return: 0 on success, negative errno on error.
+ */
+int kexec_add_buffer(struct kexec_buf *kbuf)
 {
 
        struct kexec_segment *ksegment;
-       struct kexec_buf buf, *kbuf;
        int ret;
 
        /* Currently adding segment this way is allowed only in file mode */
-       if (!image->file_mode)
+       if (!kbuf->image->file_mode)
                return -EINVAL;
 
-       if (image->nr_segments >= KEXEC_SEGMENT_MAX)
+       if (kbuf->image->nr_segments >= KEXEC_SEGMENT_MAX)
                return -EINVAL;
 
        /*
@@ -456,45 +496,27 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz,
         * logic goes through list of segments to make sure there are
         * no destination overlaps.
         */
-       if (!list_empty(&image->control_pages)) {
+       if (!list_empty(&kbuf->image->control_pages)) {
                WARN_ON(1);
                return -EINVAL;
        }
 
-       memset(&buf, 0, sizeof(struct kexec_buf));
-       kbuf = &buf;
-       kbuf->image = image;
-       kbuf->buffer = buffer;
-       kbuf->bufsz = bufsz;
-
-       kbuf->memsz = ALIGN(memsz, PAGE_SIZE);
-       kbuf->buf_align = max(buf_align, PAGE_SIZE);
-       kbuf->buf_min = buf_min;
-       kbuf->buf_max = buf_max;
-       kbuf->top_down = top_down;
+       /* Ensure minimum alignment needed for segments. */
+       kbuf->memsz = ALIGN(kbuf->memsz, PAGE_SIZE);
+       kbuf->buf_align = max(kbuf->buf_align, PAGE_SIZE);
 
        /* Walk the RAM ranges and allocate a suitable range for the buffer */
-       if (image->type == KEXEC_TYPE_CRASH)
-               ret = walk_iomem_res_desc(crashk_res.desc,
-                               IORESOURCE_SYSTEM_RAM | 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;
-       }
+       ret = kexec_locate_mem_hole(kbuf);
+       if (ret)
+               return ret;
 
        /* Found a suitable memory range */
-       ksegment = &image->segment[image->nr_segments];
+       ksegment = &kbuf->image->segment[kbuf->image->nr_segments];
        ksegment->kbuf = kbuf->buffer;
        ksegment->bufsz = kbuf->bufsz;
        ksegment->mem = kbuf->mem;
        ksegment->memsz = kbuf->memsz;
-       image->nr_segments++;
-       *load_addr = ksegment->mem;
+       kbuf->image->nr_segments++;
        return 0;
 }
 
@@ -616,13 +638,15 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
                                  unsigned long max, int top_down)
 {
        struct purgatory_info *pi = &image->purgatory_info;
-       unsigned long align, buf_align, bss_align, buf_sz, bss_sz, bss_pad;
-       unsigned long memsz, entry, load_addr, curr_load_addr, bss_addr, offset;
+       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;
-       void *purgatory_buf = 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
@@ -688,9 +712,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
        }
 
        /* Determine how much memory is needed to load relocatable object. */
-       buf_align = 1;
        bss_align = 1;
-       buf_sz = 0;
        bss_sz = 0;
 
        for (i = 0; i < pi->ehdr->e_shnum; i++) {
@@ -699,10 +721,10 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
 
                align = sechdrs[i].sh_addralign;
                if (sechdrs[i].sh_type != SHT_NOBITS) {
-                       if (buf_align < align)
-                               buf_align = align;
-                       buf_sz = ALIGN(buf_sz, align);
-                       buf_sz += 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)
@@ -714,32 +736,31 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
 
        /* Determine the bss padding required to align bss properly */
        bss_pad = 0;
-       if (buf_sz & (bss_align - 1))
-               bss_pad = bss_align - (buf_sz & (bss_align - 1));
+       if (kbuf.bufsz & (bss_align - 1))
+               bss_pad = bss_align - (kbuf.bufsz & (bss_align - 1));
 
-       memsz = buf_sz + bss_pad + bss_sz;
+       kbuf.memsz = kbuf.bufsz + bss_pad + bss_sz;
 
        /* Allocate buffer for purgatory */
-       purgatory_buf = vzalloc(buf_sz);
-       if (!purgatory_buf) {
+       kbuf.buffer = vzalloc(kbuf.bufsz);
+       if (!kbuf.buffer) {
                ret = -ENOMEM;
                goto out;
        }
 
-       if (buf_align < bss_align)
-               buf_align = bss_align;
+       if (kbuf.buf_align < bss_align)
+               kbuf.buf_align = bss_align;
 
        /* Add buffer to segment list */
-       ret = kexec_add_buffer(image, purgatory_buf, buf_sz, memsz,
-                               buf_align, min, max, top_down,
-                               &pi->purgatory_load_addr);
+       ret = kexec_add_buffer(&kbuf);
        if (ret)
                goto out;
+       pi->purgatory_load_addr = kbuf.mem;
 
        /* Load SHF_ALLOC sections */
-       buf_addr = purgatory_buf;
+       buf_addr = kbuf.buffer;
        load_addr = curr_load_addr = pi->purgatory_load_addr;
-       bss_addr = load_addr + buf_sz + bss_pad;
+       bss_addr = load_addr + kbuf.bufsz + bss_pad;
 
        for (i = 0; i < pi->ehdr->e_shnum; i++) {
                if (!(sechdrs[i].sh_flags & SHF_ALLOC))
@@ -785,11 +806,11 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
         * Used later to identify which section is purgatory and skip it
         * from checksumming.
         */
-       pi->purgatory_buf = purgatory_buf;
+       pi->purgatory_buf = kbuf.buffer;
        return ret;
 out:
        vfree(sechdrs);
-       vfree(purgatory_buf);
+       vfree(kbuf.buffer);
        return ret;
 }