Commit 93045705 authored by Philipp Rudo's avatar Philipp Rudo Committed by Linus Torvalds

kernel/kexec_file.c: split up __kexec_load_puragory

When inspecting __kexec_load_purgatory you find that it has two tasks

	1) setting up the kexec_buffer for the new kernel and,
	2) setting up pi->sechdrs for the final load address.

The two tasks are independent of each other.  To improve readability
split up __kexec_load_purgatory into two functions, one for each task,
and call them directly from kexec_load_purgatory.

Link: http://lkml.kernel.org/r/20180321112751.22196-7-prudo@linux.vnet.ibm.comSigned-off-by: default avatarPhilipp Rudo <prudo@linux.vnet.ibm.com>
Acked-by: default avatarDave Young <dyoung@redhat.com>
Cc: AKASHI Takahiro <takahiro.akashi@linaro.org>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8aec395b
...@@ -710,39 +710,97 @@ static int kexec_calculate_store_digests(struct kimage *image) ...@@ -710,39 +710,97 @@ static int kexec_calculate_store_digests(struct kimage *image)
} }
#ifdef CONFIG_ARCH_HAS_KEXEC_PURGATORY #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, * kexec_purgatory_setup_kbuf - prepare buffer to load purgatory.
unsigned long max, int top_down) * @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; const Elf_Shdr *sechdrs;
unsigned long align, bss_align, bss_sz, bss_pad; unsigned long bss_align;
unsigned long entry, load_addr, curr_load_addr, bss_addr, offset; unsigned long bss_sz;
unsigned char *buf_addr, *src; unsigned long align;
int i, ret = 0, entry_sidx = -1; int i, ret;
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 = (void *)pi->ehdr + pi->ehdr->e_shoff;
* sechdrs_c points to section headers in purgatory and are read bss_align = 1;
* only. No modifications allowed. bss_sz = 0;
*/
sechdrs_c = (void *)pi->ehdr + pi->ehdr->e_shoff;
/* for (i = 0; i < pi->ehdr->e_shnum; i++) {
* We can not modify sechdrs_c[] and its fields. It is read only. if (!(sechdrs[i].sh_flags & SHF_ALLOC))
* Copy it over to a local copy where one can store some temporary continue;
* data and free it at the end. We need to modify ->sh_addr and
* ->sh_offset fields to keep track of permanent and temporary align = sechdrs[i].sh_addralign;
* locations of sections. 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;
} else {
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;
kbuf->buffer = vzalloc(kbuf->bufsz);
if (!kbuf->buffer)
return -ENOMEM;
pi->purgatory_buf = kbuf->buffer;
ret = kexec_add_buffer(kbuf);
if (ret)
goto out;
pi->purgatory_load_addr = kbuf->mem;
return 0;
out:
vfree(pi->purgatory_buf);
pi->purgatory_buf = NULL;
return ret;
}
/*
* 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 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 entry_sidx = -1;
int i;
sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr)); sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr));
if (!sechdrs) if (!sechdrs)
return -ENOMEM; return -ENOMEM;
memcpy(sechdrs, (void *)pi->ehdr + pi->ehdr->e_shoff,
memcpy(sechdrs, sechdrs_c, pi->ehdr->e_shnum * sizeof(Elf_Shdr)); pi->ehdr->e_shnum * sizeof(Elf_Shdr));
pi->sechdrs = sechdrs;
/* /*
* We seem to have multiple copies of sections. First copy is which * We seem to have multiple copies of sections. First copy is which
...@@ -770,7 +828,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min, ...@@ -770,7 +828,7 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
* Identify entry point section and make entry relative to section * Identify entry point section and make entry relative to section
* start. * start.
*/ */
entry = pi->ehdr->e_entry; kbuf->image->start = pi->ehdr->e_entry;
for (i = 0; i < pi->ehdr->e_shnum; i++) { for (i = 0; i < pi->ehdr->e_shnum; i++) {
if (!(sechdrs[i].sh_flags & SHF_ALLOC)) if (!(sechdrs[i].sh_flags & SHF_ALLOC))
continue; continue;
...@@ -783,63 +841,19 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min, ...@@ -783,63 +841,19 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
((sechdrs[i].sh_addr + sechdrs[i].sh_size) > ((sechdrs[i].sh_addr + sechdrs[i].sh_size) >
pi->ehdr->e_entry)) { pi->ehdr->e_entry)) {
entry_sidx = i; entry_sidx = i;
entry -= sechdrs[i].sh_addr; kbuf->image->start -= sechdrs[i].sh_addr;
break; break;
} }
} }
/* Determine how much memory is needed to load relocatable object. */
bss_align = 1;
bss_sz = 0;
for (i = 0; i < pi->ehdr->e_shnum; i++) {
if (!(sechdrs[i].sh_flags & SHF_ALLOC))
continue;
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;
} else {
/* bss section */
if (bss_align < align)
bss_align = align;
bss_sz = ALIGN(bss_sz, align);
bss_sz += sechdrs[i].sh_size;
}
}
/* 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;
/* Allocate buffer for purgatory */
kbuf.buffer = vzalloc(kbuf.bufsz);
if (!kbuf.buffer) {
ret = -ENOMEM;
goto out;
}
if (kbuf.buf_align < bss_align)
kbuf.buf_align = bss_align;
/* Add buffer to segment list */
ret = kexec_add_buffer(&kbuf);
if (ret)
goto out;
pi->purgatory_load_addr = kbuf.mem;
/* Load SHF_ALLOC sections */ /* Load SHF_ALLOC sections */
buf_addr = kbuf.buffer; buf_addr = kbuf->buffer;
load_addr = curr_load_addr = pi->purgatory_load_addr; load_addr = curr_load_addr = kbuf->mem;
bss_addr = load_addr + kbuf.bufsz + bss_pad; bss_addr = load_addr + kbuf->bufsz;
for (i = 0; i < pi->ehdr->e_shnum; i++) { for (i = 0; i < pi->ehdr->e_shnum; i++) {
unsigned long align;
if (!(sechdrs[i].sh_flags & SHF_ALLOC)) if (!(sechdrs[i].sh_flags & SHF_ALLOC))
continue; continue;
...@@ -871,24 +885,9 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min, ...@@ -871,24 +885,9 @@ static int __kexec_load_purgatory(struct kimage *image, unsigned long min,
/* Update entry point based on load address of text section */ /* Update entry point based on load address of text section */
if (entry_sidx >= 0) if (entry_sidx >= 0)
entry += sechdrs[entry_sidx].sh_addr; kbuf->image->start += sechdrs[entry_sidx].sh_addr;
/* Make kernel jump to purgatory after shutdown */
image->start = entry;
/* Used later to get/set symbol values */
pi->sechdrs = sechdrs;
/* return 0;
* 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;
} }
static int kexec_apply_relocations(struct kimage *image) static int kexec_apply_relocations(struct kimage *image)
...@@ -958,16 +957,23 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min, ...@@ -958,16 +957,23 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min,
{ {
struct purgatory_info *pi = &image->purgatory_info; struct purgatory_info *pi = &image->purgatory_info;
int ret; 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) if (kexec_purgatory_size <= 0)
return -EINVAL; return -EINVAL;
pi->ehdr = (const Elf_Ehdr *)kexec_purgatory; 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) if (ret)
return ret; return ret;
ret = kexec_purgatory_setup_sechdrs(pi, &kbuf);
if (ret)
goto out_free_kbuf;
ret = kexec_apply_relocations(image); ret = kexec_apply_relocations(image);
if (ret) if (ret)
goto out; goto out;
...@@ -977,7 +983,7 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min, ...@@ -977,7 +983,7 @@ int kexec_load_purgatory(struct kimage *image, unsigned long min,
out: out:
vfree(pi->sechdrs); vfree(pi->sechdrs);
pi->sechdrs = NULL; pi->sechdrs = NULL;
out_free_kbuf:
vfree(pi->purgatory_buf); vfree(pi->purgatory_buf);
pi->purgatory_buf = NULL; pi->purgatory_buf = NULL;
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment