linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC] generic ELF support for kexec
@ 2019-06-25 18:54 Sven Schnelle
  2019-06-26 16:09 ` Christophe Leroy
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Sven Schnelle @ 2019-06-25 18:54 UTC (permalink / raw)
  To: kexec; +Cc: linux-s390, deller, linuxppc-dev

Hi List,

i recently started working on kexec for PA-RISC. While doing so, i figured
that powerpc already has support for reading ELF images inside of the Kernel.
My first attempt was to steal the source code and modify it for PA-RISC, but
it turned out that i didn't had to change much. Only ARM specific stuff like
fdt blob fetching had to be removed.

So instead of duplicating the code, i thought about moving the ELF stuff to
the core kexec code, and exposing several function to use that code from the
arch specific code.

I'm attaching the patch to this Mail. What do you think about that change?
s390 also uses ELF files, and (maybe?) could also switch to this implementation.
But i don't know anything about S/390 and don't have one in my basement. So
i'll leave s390 to the IBM folks.

I haven't really tested PowerPC yet. Can anyone give me a helping hand what
would be a good target to test this code in QEMU? Or even better, test this
code on real Hardware?

If that change is acceptable i would finish the patch and submit it. I think
best would be to push this change through Helge's parisc tree, so we don't
have any dependencies to sort out.

Regards,
Sven

[PATCH] kexec: add generic support for elf kernel images

Signed-off-by: Sven Schnelle <svens@stackframe.org>
---
 arch/Kconfig                       |   3 +
 arch/powerpc/Kconfig               |   1 +
 arch/powerpc/kernel/kexec_elf_64.c | 547 +--------------------------
 include/linux/kexec.h              |  35 ++
 kernel/Makefile                    |   1 +
 kernel/kexec_file_elf.c            | 574 +++++++++++++++++++++++++++++
 6 files changed, 619 insertions(+), 542 deletions(-)
 create mode 100644 kernel/kexec_file_elf.c

diff --git a/arch/Kconfig b/arch/Kconfig
index c47b328eada0..de7520100136 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -18,6 +18,9 @@ config KEXEC_CORE
 	select CRASH_CORE
 	bool
 
+config KEXEC_FILE_ELF
+	bool
+
 config HAVE_IMA_KEXEC
 	bool
 
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 8c1c636308c8..48241260b6ae 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -502,6 +502,7 @@ config KEXEC_FILE
 	select KEXEC_CORE
 	select HAVE_IMA_KEXEC
 	select BUILD_BIN2C
+	select KEXEC_FILE_ELF
 	depends on PPC64
 	depends on CRYPTO=y
 	depends on CRYPTO_SHA256=y
diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
index ba4f18a43ee8..0059e36913e9 100644
--- a/arch/powerpc/kernel/kexec_elf_64.c
+++ b/arch/powerpc/kernel/kexec_elf_64.c
@@ -21,8 +21,6 @@
  * GNU General Public License for more details.
  */
 
-#define pr_fmt(fmt)	"kexec_elf: " fmt
-
 #include <linux/elf.h>
 #include <linux/kexec.h>
 #include <linux/libfdt.h>
@@ -31,540 +29,6 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 
-#define PURGATORY_STACK_SIZE	(16 * 1024)
-
-#define elf_addr_to_cpu	elf64_to_cpu
-
-#ifndef Elf_Rel
-#define Elf_Rel		Elf64_Rel
-#endif /* Elf_Rel */
-
-struct elf_info {
-	/*
-	 * Where the ELF binary contents are kept.
-	 * Memory managed by the user of the struct.
-	 */
-	const char *buffer;
-
-	const struct elfhdr *ehdr;
-	const struct elf_phdr *proghdrs;
-	struct elf_shdr *sechdrs;
-};
-
-static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
-{
-       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
-}
-
-static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
-{
-	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le64_to_cpu(value);
-	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be64_to_cpu(value);
-
-	return value;
-}
-
-static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
-{
-	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le16_to_cpu(value);
-	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be16_to_cpu(value);
-
-	return value;
-}
-
-static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
-{
-	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-		value = le32_to_cpu(value);
-	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-		value = be32_to_cpu(value);
-
-	return value;
-}
-
-/**
- * elf_is_ehdr_sane - check that it is safe to use the ELF header
- * @buf_len:	size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
-{
-	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
-		pr_debug("Bad program header size.\n");
-		return false;
-	} else if (ehdr->e_shnum > 0 &&
-		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
-		pr_debug("Bad section header size.\n");
-		return false;
-	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
-		   ehdr->e_version != EV_CURRENT) {
-		pr_debug("Unknown ELF version.\n");
-		return false;
-	}
-
-	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
-		size_t phdr_size;
-
-		/*
-		 * e_phnum is at most 65535 so calculating the size of the
-		 * program header cannot overflow.
-		 */
-		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
-
-		/* Sanity check the program header table location. */
-		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
-			pr_debug("Program headers at invalid location.\n");
-			return false;
-		} else if (ehdr->e_phoff + phdr_size > buf_len) {
-			pr_debug("Program headers truncated.\n");
-			return false;
-		}
-	}
-
-	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
-		size_t shdr_size;
-
-		/*
-		 * e_shnum is at most 65536 so calculating
-		 * the size of the section header cannot overflow.
-		 */
-		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
-
-		/* Sanity check the section header table location. */
-		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
-			pr_debug("Section headers at invalid location.\n");
-			return false;
-		} else if (ehdr->e_shoff + shdr_size > buf_len) {
-			pr_debug("Section headers truncated.\n");
-			return false;
-		}
-	}
-
-	return true;
-}
-
-static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
-{
-	struct elfhdr *buf_ehdr;
-
-	if (len < sizeof(*buf_ehdr)) {
-		pr_debug("Buffer is too small to hold ELF header.\n");
-		return -ENOEXEC;
-	}
-
-	memset(ehdr, 0, sizeof(*ehdr));
-	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
-	if (!elf_is_elf_file(ehdr)) {
-		pr_debug("No ELF header magic.\n");
-		return -ENOEXEC;
-	}
-
-	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
-		pr_debug("Not a supported ELF class.\n");
-		return -ENOEXEC;
-	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
-		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
-		pr_debug("Not a supported ELF data format.\n");
-		return -ENOEXEC;
-	}
-
-	buf_ehdr = (struct elfhdr *) buf;
-	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
-		pr_debug("Bad ELF header size.\n");
-		return -ENOEXEC;
-	}
-
-	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
-	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
-	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
-	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
-	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
-	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
-	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
-	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
-	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
-	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
-	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
-	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
-
-	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_is_phdr_sane - check that it is safe to use the program header
- * @buf_len:	size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
-{
-
-	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
-		pr_debug("ELF segment location wraps around.\n");
-		return false;
-	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
-		pr_debug("ELF segment not in file.\n");
-		return false;
-	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
-		pr_debug("ELF segment address wraps around.\n");
-		return false;
-	}
-
-	return true;
-}
-
-static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
-			 int idx)
-{
-	/* Override the const in proghdrs, we are the ones doing the loading. */
-	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
-	const char *pbuf;
-	struct elf_phdr *buf_phdr;
-
-	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
-	buf_phdr = (struct elf_phdr *) pbuf;
-
-	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
-	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
-	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
-	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
-	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
-
-	/*
-	 * The following fields have a type equivalent to Elf_Addr
-	 * both in 32 bit and 64 bit ELF.
-	 */
-	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
-	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
-	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
-
-	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_read_phdrs - read the program headers from the buffer
- *
- * This function assumes that the program header table was checked for sanity.
- * Use elf_is_ehdr_sane() if it wasn't.
- */
-static int elf_read_phdrs(const char *buf, size_t len,
-			  struct elf_info *elf_info)
-{
-	size_t phdr_size, i;
-	const struct elfhdr *ehdr = elf_info->ehdr;
-
-	/*
-	 * e_phnum is at most 65535 so calculating the size of the
-	 * program header cannot overflow.
-	 */
-	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
-
-	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
-	if (!elf_info->proghdrs)
-		return -ENOMEM;
-
-	for (i = 0; i < ehdr->e_phnum; i++) {
-		int ret;
-
-		ret = elf_read_phdr(buf, len, elf_info, i);
-		if (ret) {
-			kfree(elf_info->proghdrs);
-			elf_info->proghdrs = NULL;
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * elf_is_shdr_sane - check that it is safe to use the section header
- * @buf_len:	size of the buffer in which the ELF file is loaded.
- */
-static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
-{
-	bool size_ok;
-
-	/* SHT_NULL headers have undefined values, so we can't check them. */
-	if (shdr->sh_type == SHT_NULL)
-		return true;
-
-	/* Now verify sh_entsize */
-	switch (shdr->sh_type) {
-	case SHT_SYMTAB:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
-		break;
-	case SHT_RELA:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
-		break;
-	case SHT_DYNAMIC:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
-		break;
-	case SHT_REL:
-		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
-		break;
-	case SHT_NOTE:
-	case SHT_PROGBITS:
-	case SHT_HASH:
-	case SHT_NOBITS:
-	default:
-		/*
-		 * This is a section whose entsize requirements
-		 * I don't care about.  If I don't know about
-		 * the section I can't care about it's entsize
-		 * requirements.
-		 */
-		size_ok = true;
-		break;
-	}
-
-	if (!size_ok) {
-		pr_debug("ELF section with wrong entry size.\n");
-		return false;
-	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
-		pr_debug("ELF section address wraps around.\n");
-		return false;
-	}
-
-	if (shdr->sh_type != SHT_NOBITS) {
-		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
-			pr_debug("ELF section location wraps around.\n");
-			return false;
-		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
-			pr_debug("ELF section not in file.\n");
-			return false;
-		}
-	}
-
-	return true;
-}
-
-static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
-			 int idx)
-{
-	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
-	const struct elfhdr *ehdr = elf_info->ehdr;
-	const char *sbuf;
-	struct elf_shdr *buf_shdr;
-
-	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
-	buf_shdr = (struct elf_shdr *) sbuf;
-
-	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
-	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
-	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
-	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
-	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
-	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
-
-	/*
-	 * The following fields have a type equivalent to Elf_Addr
-	 * both in 32 bit and 64 bit ELF.
-	 */
-	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
-	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
-	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
-	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
-
-	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_read_shdrs - read the section headers from the buffer
- *
- * This function assumes that the section header table was checked for sanity.
- * Use elf_is_ehdr_sane() if it wasn't.
- */
-static int elf_read_shdrs(const char *buf, size_t len,
-			  struct elf_info *elf_info)
-{
-	size_t shdr_size, i;
-
-	/*
-	 * e_shnum is at most 65536 so calculating
-	 * the size of the section header cannot overflow.
-	 */
-	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
-
-	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
-	if (!elf_info->sechdrs)
-		return -ENOMEM;
-
-	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
-		int ret;
-
-		ret = elf_read_shdr(buf, len, elf_info, i);
-		if (ret) {
-			kfree(elf_info->sechdrs);
-			elf_info->sechdrs = NULL;
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
- * @buf:	Buffer to read ELF file from.
- * @len:	Size of @buf.
- * @ehdr:	Pointer to existing struct which will be populated.
- * @elf_info:	Pointer to existing struct which will be populated.
- *
- * This function allows reading ELF files with different byte order than
- * the kernel, byte-swapping the fields as needed.
- *
- * Return:
- * On success returns 0, and the caller should call elf_free_info(elf_info) to
- * free the memory allocated for the section and program headers.
- */
-int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
-			 struct elf_info *elf_info)
-{
-	int ret;
-
-	ret = elf_read_ehdr(buf, len, ehdr);
-	if (ret)
-		return ret;
-
-	elf_info->buffer = buf;
-	elf_info->ehdr = ehdr;
-	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
-		ret = elf_read_phdrs(buf, len, elf_info);
-		if (ret)
-			return ret;
-	}
-	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
-		ret = elf_read_shdrs(buf, len, elf_info);
-		if (ret) {
-			kfree(elf_info->proghdrs);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-/**
- * elf_free_info - free memory allocated by elf_read_from_buffer
- */
-void elf_free_info(struct elf_info *elf_info)
-{
-	kfree(elf_info->proghdrs);
-	kfree(elf_info->sechdrs);
-	memset(elf_info, 0, sizeof(*elf_info));
-}
-/**
- * build_elf_exec_info - read ELF executable and check that we can use it
- */
-static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
-			       struct elf_info *elf_info)
-{
-	int i;
-	int ret;
-
-	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
-	if (ret)
-		return ret;
-
-	/* Big endian vmlinux has type ET_DYN. */
-	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
-		pr_err("Not an ELF executable.\n");
-		goto error;
-	} else if (!elf_info->proghdrs) {
-		pr_err("No ELF program header.\n");
-		goto error;
-	}
-
-	for (i = 0; i < ehdr->e_phnum; i++) {
-		/*
-		 * Kexec does not support loading interpreters.
-		 * In addition this check keeps us from attempting
-		 * to kexec ordinay executables.
-		 */
-		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
-			pr_err("Requires an ELF interpreter.\n");
-			goto error;
-		}
-	}
-
-	return 0;
-error:
-	elf_free_info(elf_info);
-	return -ENOEXEC;
-}
-
-static int elf64_probe(const char *buf, unsigned long len)
-{
-	struct elfhdr ehdr;
-	struct elf_info elf_info;
-	int ret;
-
-	ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
-	if (ret)
-		return ret;
-
-	elf_free_info(&elf_info);
-
-	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
-}
-
-/**
- * elf_exec_load - load ELF executable image
- * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
- *			section will be loaded in memory.
- *
- * Return:
- * 0 on success, negative value on failure.
- */
-static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
-			 struct elf_info *elf_info,
-			 unsigned long *lowest_load_addr)
-{
-	unsigned long base = 0, lowest_addr = UINT_MAX;
-	int ret;
-	size_t i;
-	struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
-				  .top_down = false };
-
-	/* Read in the PT_LOAD segments. */
-	for (i = 0; i < ehdr->e_phnum; i++) {
-		unsigned long load_addr;
-		size_t size;
-		const struct elf_phdr *phdr;
-
-		phdr = &elf_info->proghdrs[i];
-		if (phdr->p_type != PT_LOAD)
-			continue;
-
-		size = phdr->p_filesz;
-		if (size > phdr->p_memsz)
-			size = phdr->p_memsz;
-
-		kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
-		kbuf.bufsz = size;
-		kbuf.memsz = phdr->p_memsz;
-		kbuf.buf_align = phdr->p_align;
-		kbuf.buf_min = phdr->p_paddr + base;
-		ret = kexec_add_buffer(&kbuf);
-		if (ret)
-			goto out;
-		load_addr = kbuf.mem;
-
-		if (load_addr < lowest_addr)
-			lowest_addr = load_addr;
-	}
-
-	/* Update entry point to reflect new load address. */
-	ehdr->e_entry += base;
-
-	*lowest_load_addr = lowest_addr;
-	ret = 0;
- out:
-	return ret;
-}
-
 static void *elf64_load(struct kimage *image, char *kernel_buf,
 			unsigned long kernel_len, char *initrd,
 			unsigned long initrd_len, char *cmdline,
@@ -577,17 +41,17 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
 	void *fdt;
 	const void *slave_code;
 	struct elfhdr ehdr;
-	struct elf_info elf_info;
+	struct kexec_elf_info elf_info;
 	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
 				  .buf_max = ppc64_rma_size };
 	struct kexec_buf pbuf = { .image = image, .buf_min = 0,
 				  .buf_max = ppc64_rma_size, .top_down = true };
 
-	ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
 	if (ret)
 		goto out;
 
-	ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
+	ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
 	if (ret)
 		goto out;
 
@@ -652,13 +116,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
 		pr_err("Error setting up the purgatory.\n");
 
 out:
-	elf_free_info(&elf_info);
-
+	kexec_free_elf_info(&elf_info);
 	/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
 	return ret ? ERR_PTR(ret) : fdt;
 }
 
 const struct kexec_file_ops kexec_elf64_ops = {
-	.probe = elf64_probe,
+	.probe = kexec_elf_probe,
 	.load = elf64_load,
 };
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index b9b1bc5f9669..49b23b425f84 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
 				       void **addr, unsigned long *sz);
 #endif /* CONFIG_KEXEC_FILE */
 
+#ifdef CONFIG_KEXEC_FILE_ELF
+
+struct kexec_elf_info {
+	/*
+	 * Where the ELF binary contents are kept.
+	 * Memory managed by the user of the struct.
+	 */
+	const char *buffer;
+
+	const struct elfhdr *ehdr;
+	const struct elf_phdr *proghdrs;
+	struct elf_shdr *sechdrs;
+};
+
+void kexec_free_elf_info(struct kexec_elf_info *elf_info);
+
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+			  struct kexec_elf_info *elf_info);
+
+int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
+			  char *kernel_buf, unsigned long kernel_len,
+			  unsigned long *kernel_load_addr);
+
+int kexec_elf_probe(const char *buf, unsigned long len);
+
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+			 struct kexec_elf_info *elf_info,
+			 struct kexec_buf *kbuf,
+			 unsigned long *lowest_load_addr);
+
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+			 struct kexec_elf_info *elf_info,
+			 struct kexec_buf *kbuf,
+			 unsigned long *lowest_load_addr);
+#endif
 struct kimage {
 	kimage_entry_t head;
 	kimage_entry_t *entry;
diff --git a/kernel/Makefile b/kernel/Makefile
index 33824f0385b3..fdba91785977 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
 obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
 obj-$(CONFIG_KEXEC) += kexec.o
 obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
+obj-$(CONFIG_KEXEC_FILE_ELF) += kexec_file_elf.o
 obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
 obj-$(CONFIG_COMPAT) += compat.o
 obj-$(CONFIG_CGROUPS) += cgroup/
diff --git a/kernel/kexec_file_elf.c b/kernel/kexec_file_elf.c
new file mode 100644
index 000000000000..bb966c93492c
--- /dev/null
+++ b/kernel/kexec_file_elf.c
@@ -0,0 +1,574 @@
+/*
+ * Load ELF vmlinux file for the kexec_file_load syscall.
+ *
+ * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
+ * Copyright (C) 2004  IBM Corp.
+ * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
+ * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
+ * Copyright (C) 2016  IBM Corporation
+ *
+ * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
+ * Heavily modified for the kernel by
+ * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"kexec_elf: " fmt
+
+#include <linux/elf.h>
+#include <linux/kexec.h>
+#include <linux/libfdt.h>
+#include <linux/module.h>
+#include <linux/of_fdt.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define elf_addr_to_cpu	elf64_to_cpu
+
+#ifndef Elf_Rel
+#define Elf_Rel		Elf64_Rel
+#endif /* Elf_Rel */
+
+static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
+{
+       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
+}
+
+static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
+{
+	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+		value = le64_to_cpu(value);
+	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+		value = be64_to_cpu(value);
+
+	return value;
+}
+
+static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
+{
+	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+		value = le16_to_cpu(value);
+	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+		value = be16_to_cpu(value);
+
+	return value;
+}
+
+static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
+{
+	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
+		value = le32_to_cpu(value);
+	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
+		value = be32_to_cpu(value);
+
+	return value;
+}
+
+/**
+ * elf_is_ehdr_sane - check that it is safe to use the ELF header
+ * @buf_len:	size of the buffer in which the ELF file is loaded.
+ */
+static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
+{
+	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
+		pr_debug("Bad program header size.\n");
+		return false;
+	} else if (ehdr->e_shnum > 0 &&
+		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
+		pr_debug("Bad section header size.\n");
+		return false;
+	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
+		   ehdr->e_version != EV_CURRENT) {
+		pr_debug("Unknown ELF version.\n");
+		return false;
+	}
+
+	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
+		size_t phdr_size;
+
+		/*
+		 * e_phnum is at most 65535 so calculating the size of the
+		 * program header cannot overflow.
+		 */
+		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
+
+		/* Sanity check the program header table location. */
+		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
+			pr_debug("Program headers at invalid location.\n");
+			return false;
+		} else if (ehdr->e_phoff + phdr_size > buf_len) {
+			pr_debug("Program headers truncated.\n");
+			return false;
+		}
+	}
+
+	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
+		size_t shdr_size;
+
+		/*
+		 * e_shnum is at most 65536 so calculating
+		 * the size of the section header cannot overflow.
+		 */
+		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
+
+		/* Sanity check the section header table location. */
+		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
+			pr_debug("Section headers at invalid location.\n");
+			return false;
+		} else if (ehdr->e_shoff + shdr_size > buf_len) {
+			pr_debug("Section headers truncated.\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
+{
+	struct elfhdr *buf_ehdr;
+
+	if (len < sizeof(*buf_ehdr)) {
+		pr_debug("Buffer is too small to hold ELF header.\n");
+		return -ENOEXEC;
+	}
+
+	memset(ehdr, 0, sizeof(*ehdr));
+	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
+	if (!elf_is_elf_file(ehdr)) {
+		pr_debug("No ELF header magic.\n");
+		return -ENOEXEC;
+	}
+
+	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
+		pr_debug("Not a supported ELF class.\n");
+		return -ENOEXEC;
+	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
+		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+		pr_debug("Not a supported ELF data format.\n");
+		return -ENOEXEC;
+	}
+
+	buf_ehdr = (struct elfhdr *) buf;
+	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
+		pr_debug("Bad ELF header size.\n");
+		return -ENOEXEC;
+	}
+
+	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
+	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
+	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
+	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
+	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
+	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
+	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
+	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
+	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
+	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
+	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
+	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
+
+	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
+}
+
+/**
+ * elf_is_phdr_sane - check that it is safe to use the program header
+ * @buf_len:	size of the buffer in which the ELF file is loaded.
+ */
+static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
+{
+
+	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
+		pr_debug("ELF segment location wraps around.\n");
+		return false;
+	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
+		pr_debug("ELF segment not in file.\n");
+		return false;
+	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
+		pr_debug("ELF segment address wraps around.\n");
+		return false;
+	}
+
+	return true;
+}
+
+static int elf_read_phdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
+			 int idx)
+{
+	/* Override the const in proghdrs, we are the ones doing the loading. */
+	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
+	const char *pbuf;
+	struct elf_phdr *buf_phdr;
+
+	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
+	buf_phdr = (struct elf_phdr *) pbuf;
+
+	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
+	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
+	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
+	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
+	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
+
+	/*
+	 * The following fields have a type equivalent to Elf_Addr
+	 * both in 32 bit and 64 bit ELF.
+	 */
+	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
+	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
+	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
+
+	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
+}
+
+/**
+ * elf_read_phdrs - read the program headers from the buffer
+ *
+ * This function assumes that the program header table was checked for sanity.
+ * Use elf_is_ehdr_sane() if it wasn't.
+ */
+static int elf_read_phdrs(const char *buf, size_t len,
+			  struct kexec_elf_info *elf_info)
+{
+	size_t phdr_size, i;
+	const struct elfhdr *ehdr = elf_info->ehdr;
+
+	/*
+	 * e_phnum is at most 65535 so calculating the size of the
+	 * program header cannot overflow.
+	 */
+	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
+
+	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
+	if (!elf_info->proghdrs)
+		return -ENOMEM;
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		int ret;
+
+		ret = elf_read_phdr(buf, len, elf_info, i);
+		if (ret) {
+			kfree(elf_info->proghdrs);
+			elf_info->proghdrs = NULL;
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * elf_is_shdr_sane - check that it is safe to use the section header
+ * @buf_len:	size of the buffer in which the ELF file is loaded.
+ */
+static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
+{
+	bool size_ok;
+
+	/* SHT_NULL headers have undefined values, so we can't check them. */
+	if (shdr->sh_type == SHT_NULL)
+		return true;
+
+	/* Now verify sh_entsize */
+	switch (shdr->sh_type) {
+	case SHT_SYMTAB:
+		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
+		break;
+	case SHT_RELA:
+		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
+		break;
+	case SHT_DYNAMIC:
+		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
+		break;
+	case SHT_REL:
+		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
+		break;
+	case SHT_NOTE:
+	case SHT_PROGBITS:
+	case SHT_HASH:
+	case SHT_NOBITS:
+	default:
+		/*
+		 * This is a section whose entsize requirements
+		 * I don't care about.  If I don't know about
+		 * the section I can't care about it's entsize
+		 * requirements.
+		 */
+		size_ok = true;
+		break;
+	}
+
+	if (!size_ok) {
+		pr_debug("ELF section with wrong entry size.\n");
+		return false;
+	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
+		pr_debug("ELF section address wraps around.\n");
+		return false;
+	}
+
+	if (shdr->sh_type != SHT_NOBITS) {
+		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
+			pr_debug("ELF section location wraps around.\n");
+			return false;
+		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
+			pr_debug("ELF section not in file.\n");
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
+			 int idx)
+{
+	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
+	const struct elfhdr *ehdr = elf_info->ehdr;
+	const char *sbuf;
+	struct elf_shdr *buf_shdr;
+
+	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
+	buf_shdr = (struct elf_shdr *) sbuf;
+
+	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
+	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
+	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
+	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
+	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
+	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
+
+	/*
+	 * The following fields have a type equivalent to Elf_Addr
+	 * both in 32 bit and 64 bit ELF.
+	 */
+	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
+	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
+	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
+	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
+
+	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
+}
+
+/**
+ * elf_read_shdrs - read the section headers from the buffer
+ *
+ * This function assumes that the section header table was checked for sanity.
+ * Use elf_is_ehdr_sane() if it wasn't.
+ */
+static int elf_read_shdrs(const char *buf, size_t len,
+			  struct kexec_elf_info *elf_info)
+{
+	size_t shdr_size, i;
+
+	/*
+	 * e_shnum is at most 65536 so calculating
+	 * the size of the section header cannot overflow.
+	 */
+	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
+
+	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
+	if (!elf_info->sechdrs)
+		return -ENOMEM;
+
+	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
+		int ret;
+
+		ret = elf_read_shdr(buf, len, elf_info, i);
+		if (ret) {
+			kfree(elf_info->sechdrs);
+			elf_info->sechdrs = NULL;
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
+ * @buf:	Buffer to read ELF file from.
+ * @len:	Size of @buf.
+ * @ehdr:	Pointer to existing struct which will be populated.
+ * @elf_info:	Pointer to existing struct which will be populated.
+ *
+ * This function allows reading ELF files with different byte order than
+ * the kernel, byte-swapping the fields as needed.
+ *
+ * Return:
+ * On success returns 0, and the caller should call kexec_free_elf_info(elf_info) to
+ * free the memory allocated for the section and program headers.
+ */
+int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
+			 struct kexec_elf_info *elf_info)
+{
+	int ret;
+
+	ret = elf_read_ehdr(buf, len, ehdr);
+	if (ret)
+		return ret;
+
+	elf_info->buffer = buf;
+	elf_info->ehdr = ehdr;
+	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
+		ret = elf_read_phdrs(buf, len, elf_info);
+		if (ret)
+			return ret;
+	}
+	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
+		ret = elf_read_shdrs(buf, len, elf_info);
+		if (ret) {
+			kfree(elf_info->proghdrs);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
+ */
+void kexec_free_elf_info(struct kexec_elf_info *elf_info)
+{
+	kfree(elf_info->proghdrs);
+	kfree(elf_info->sechdrs);
+	memset(elf_info, 0, sizeof(*elf_info));
+}
+EXPORT_SYMBOL(kexec_free_elf_info);
+
+/**
+ * kexec_build_elf_info - read ELF executable and check that we can use it
+ */
+int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
+			  struct kexec_elf_info *elf_info)
+{
+	int i;
+	int ret;
+
+	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
+	if (ret)
+		return ret;
+
+	/* Big endian vmlinux has type ET_DYN. */
+	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+		pr_err("Not an ELF executable.\n");
+		goto error;
+	} else if (!elf_info->proghdrs) {
+		pr_err("No ELF program header.\n");
+		goto error;
+	}
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		/*
+		 * Kexec does not support loading interpreters.
+		 * In addition this check keeps us from attempting
+		 * to kexec ordinay executables.
+		 */
+		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
+			pr_err("Requires an ELF interpreter.\n");
+			goto error;
+		}
+	}
+
+	return 0;
+error:
+	kexec_free_elf_info(elf_info);
+	return -ENOEXEC;
+}
+EXPORT_SYMBOL(kexec_build_elf_info);
+
+/**
+ * elf_exec_load - load ELF executable image
+ * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
+ *			section will be loaded in memory.
+ *
+ * Return:
+ * 0 on success, negative value on failure.
+ */
+int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
+		   struct kexec_elf_info *elf_info,
+		   struct kexec_buf *kbuf,
+		   unsigned long *lowest_load_addr)
+{
+	unsigned long lowest_addr = UINT_MAX;
+	int ret;
+	size_t i;
+	/* Read in the PT_LOAD segments. */
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		unsigned long load_addr;
+		size_t size;
+		const struct elf_phdr *phdr;
+
+		phdr = &elf_info->proghdrs[i];
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		size = phdr->p_filesz;
+		if (size > phdr->p_memsz)
+			size = phdr->p_memsz;
+
+		kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
+		kbuf->bufsz = size;
+		kbuf->memsz = phdr->p_memsz;
+		kbuf->buf_align = phdr->p_align;
+		kbuf->buf_min = phdr->p_paddr;
+		kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
+		ret = kexec_add_buffer(kbuf);
+		if (ret)
+			goto out;
+		load_addr = kbuf->mem;
+
+		if (load_addr < lowest_addr)
+			lowest_addr = load_addr;
+	}
+
+	image->start = ehdr->e_entry;
+	*lowest_load_addr = lowest_addr;
+	ret = 0;
+ out:
+	return ret;
+}
+EXPORT_SYMBOL(kexec_elf_load);
+
+int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
+			char *kernel_buf, unsigned long kernel_len,
+			unsigned long *kernel_load_addr)
+{
+	int ret;
+	struct elfhdr ehdr;
+	struct kexec_elf_info elf_info;
+
+	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
+	if (ret)
+		goto out;
+
+	ret = kexec_elf_load(image, &ehdr, &elf_info, kbuf, kernel_load_addr);
+out:
+	kexec_free_elf_info(&elf_info);
+	return ret;
+
+}
+EXPORT_SYMBOL(kexec_elf_kernel_load);
+
+int kexec_elf_probe(const char *buf, unsigned long len)
+{
+	struct elfhdr ehdr;
+	struct kexec_elf_info elf_info;
+	int ret;
+
+	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
+	if (ret)
+		return ret;
+
+	kexec_free_elf_info(&elf_info);
+
+	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
+}
+EXPORT_SYMBOL(kexec_elf_probe);
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] generic ELF support for kexec
  2019-06-25 18:54 [PATCH RFC] generic ELF support for kexec Sven Schnelle
@ 2019-06-26 16:09 ` Christophe Leroy
  2019-06-28  2:04 ` Michael Ellerman
  2019-07-01 12:31 ` Philipp Rudo
  2 siblings, 0 replies; 6+ messages in thread
From: Christophe Leroy @ 2019-06-26 16:09 UTC (permalink / raw)
  To: Sven Schnelle, kexec; +Cc: linux-s390, deller, linuxppc-dev

Hi Sven,

On 06/25/2019 06:54 PM, Sven Schnelle wrote:
> Hi List,
> 
> i recently started working on kexec for PA-RISC. While doing so, i figured
> that powerpc already has support for reading ELF images inside of the Kernel.
> My first attempt was to steal the source code and modify it for PA-RISC, but
> it turned out that i didn't had to change much. Only ARM specific stuff like
> fdt blob fetching had to be removed.
> 
> So instead of duplicating the code, i thought about moving the ELF stuff to
> the core kexec code, and exposing several function to use that code from the
> arch specific code.
> 
> I'm attaching the patch to this Mail. What do you think about that change?
> s390 also uses ELF files, and (maybe?) could also switch to this implementation.
> But i don't know anything about S/390 and don't have one in my basement. So
> i'll leave s390 to the IBM folks.
> 
> I haven't really tested PowerPC yet. Can anyone give me a helping hand what
> would be a good target to test this code in QEMU? Or even better, test this
> code on real Hardware?

Where did you start from ? Your patch doesn't apply on latest 
powerpc/merge branch 
(https://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git):

[root@localhost linux-powerpc]# git am -3 
/root/Downloads/RFC-generic-ELF-support-for-kexec.patch
Applying: generic ELF support for kexec
Using index info to reconstruct a base tree...
M	arch/powerpc/kernel/kexec_elf_64.c
M	kernel/Makefile
Falling back to patching base and 3-way merge...
Auto-merging kernel/Makefile
Auto-merging arch/powerpc/kernel/kexec_elf_64.c
CONFLICT (content): Merge conflict in arch/powerpc/kernel/kexec_elf_64.c
error: Failed to merge in the changes.
Patch failed at 0001 generic ELF support for kexec

Neither does it apply on 5.2-rc6

Looks like it cleanly applies on 5.1

Could you generate your patch using 'git format-patch -M -C ' ? It would 
be a lot easier to see the real changes:

  arch/Kconfig                                       |   3 +
  arch/powerpc/Kconfig                               |   1 +
  arch/powerpc/kernel/kexec_elf_64.c                 | 547 
+--------------------
  include/linux/kexec.h                              |  35 ++
  kernel/Makefile                                    |   1 +
  .../kexec_elf_64.c => kernel/kexec_file_elf.c      | 192 ++------
  6 files changed, 96 insertions(+), 683 deletions(-)
  copy arch/powerpc/kernel/kexec_elf_64.c => kernel/kexec_file_elf.c (77%)

Thanks
Christophe

> 
> If that change is acceptable i would finish the patch and submit it. I think
> best would be to push this change through Helge's parisc tree, so we don't
> have any dependencies to sort out.
> 
> Regards,
> Sven
> 
> [PATCH] kexec: add generic support for elf kernel images
> 
> Signed-off-by: Sven Schnelle <svens@stackframe.org>
> ---
>   arch/Kconfig                       |   3 +
>   arch/powerpc/Kconfig               |   1 +
>   arch/powerpc/kernel/kexec_elf_64.c | 547 +--------------------------
>   include/linux/kexec.h              |  35 ++
>   kernel/Makefile                    |   1 +
>   kernel/kexec_file_elf.c            | 574 +++++++++++++++++++++++++++++
>   6 files changed, 619 insertions(+), 542 deletions(-)
>   create mode 100644 kernel/kexec_file_elf.c
> 
> diff --git a/arch/Kconfig b/arch/Kconfig
> index c47b328eada0..de7520100136 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -18,6 +18,9 @@ config KEXEC_CORE
>   	select CRASH_CORE
>   	bool
>   
> +config KEXEC_FILE_ELF
> +	bool
> +
>   config HAVE_IMA_KEXEC
>   	bool
>   
> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index 8c1c636308c8..48241260b6ae 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -502,6 +502,7 @@ config KEXEC_FILE
>   	select KEXEC_CORE
>   	select HAVE_IMA_KEXEC
>   	select BUILD_BIN2C
> +	select KEXEC_FILE_ELF
>   	depends on PPC64
>   	depends on CRYPTO=y
>   	depends on CRYPTO_SHA256=y
> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
> index ba4f18a43ee8..0059e36913e9 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/arch/powerpc/kernel/kexec_elf_64.c
> @@ -21,8 +21,6 @@
>    * GNU General Public License for more details.
>    */
>   
> -#define pr_fmt(fmt)	"kexec_elf: " fmt
> -
>   #include <linux/elf.h>
>   #include <linux/kexec.h>
>   #include <linux/libfdt.h>
> @@ -31,540 +29,6 @@
>   #include <linux/slab.h>
>   #include <linux/types.h>
>   
> -#define PURGATORY_STACK_SIZE	(16 * 1024)
> -
> -#define elf_addr_to_cpu	elf64_to_cpu
> -
> -#ifndef Elf_Rel
> -#define Elf_Rel		Elf64_Rel
> -#endif /* Elf_Rel */
> -
> -struct elf_info {
> -	/*
> -	 * Where the ELF binary contents are kept.
> -	 * Memory managed by the user of the struct.
> -	 */
> -	const char *buffer;
> -
> -	const struct elfhdr *ehdr;
> -	const struct elf_phdr *proghdrs;
> -	struct elf_shdr *sechdrs;
> -};
> -
> -static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> -{
> -       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> -}
> -
> -static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le64_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be64_to_cpu(value);
> -
> -	return value;
> -}
> -
> -static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le16_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be16_to_cpu(value);
> -
> -	return value;
> -}
> -
> -static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> -{
> -	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> -		value = le32_to_cpu(value);
> -	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> -		value = be32_to_cpu(value);
> -
> -	return value;
> -}
> -
> -/**
> - * elf_is_ehdr_sane - check that it is safe to use the ELF header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> -{
> -	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> -		pr_debug("Bad program header size.\n");
> -		return false;
> -	} else if (ehdr->e_shnum > 0 &&
> -		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> -		pr_debug("Bad section header size.\n");
> -		return false;
> -	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> -		   ehdr->e_version != EV_CURRENT) {
> -		pr_debug("Unknown ELF version.\n");
> -		return false;
> -	}
> -
> -	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> -		size_t phdr_size;
> -
> -		/*
> -		 * e_phnum is at most 65535 so calculating the size of the
> -		 * program header cannot overflow.
> -		 */
> -		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> -		/* Sanity check the program header table location. */
> -		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> -			pr_debug("Program headers at invalid location.\n");
> -			return false;
> -		} else if (ehdr->e_phoff + phdr_size > buf_len) {
> -			pr_debug("Program headers truncated.\n");
> -			return false;
> -		}
> -	}
> -
> -	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> -		size_t shdr_size;
> -
> -		/*
> -		 * e_shnum is at most 65536 so calculating
> -		 * the size of the section header cannot overflow.
> -		 */
> -		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> -
> -		/* Sanity check the section header table location. */
> -		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> -			pr_debug("Section headers at invalid location.\n");
> -			return false;
> -		} else if (ehdr->e_shoff + shdr_size > buf_len) {
> -			pr_debug("Section headers truncated.\n");
> -			return false;
> -		}
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> -{
> -	struct elfhdr *buf_ehdr;
> -
> -	if (len < sizeof(*buf_ehdr)) {
> -		pr_debug("Buffer is too small to hold ELF header.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	memset(ehdr, 0, sizeof(*ehdr));
> -	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> -	if (!elf_is_elf_file(ehdr)) {
> -		pr_debug("No ELF header magic.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> -		pr_debug("Not a supported ELF class.\n");
> -		return -ENOEXEC;
> -	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> -		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> -		pr_debug("Not a supported ELF data format.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	buf_ehdr = (struct elfhdr *) buf;
> -	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> -		pr_debug("Bad ELF header size.\n");
> -		return -ENOEXEC;
> -	}
> -
> -	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> -	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> -	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> -	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> -	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> -	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> -	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> -	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> -	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> -	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> -	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> -	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> -
> -	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_is_phdr_sane - check that it is safe to use the program header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> -{
> -
> -	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> -		pr_debug("ELF segment location wraps around.\n");
> -		return false;
> -	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> -		pr_debug("ELF segment not in file.\n");
> -		return false;
> -	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> -		pr_debug("ELF segment address wraps around.\n");
> -		return false;
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_phdr(const char *buf, size_t len, struct elf_info *elf_info,
> -			 int idx)
> -{
> -	/* Override the const in proghdrs, we are the ones doing the loading. */
> -	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> -	const char *pbuf;
> -	struct elf_phdr *buf_phdr;
> -
> -	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> -	buf_phdr = (struct elf_phdr *) pbuf;
> -
> -	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> -	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> -	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> -	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> -	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> -
> -	/*
> -	 * The following fields have a type equivalent to Elf_Addr
> -	 * both in 32 bit and 64 bit ELF.
> -	 */
> -	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> -	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> -	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> -
> -	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_phdrs - read the program headers from the buffer
> - *
> - * This function assumes that the program header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_phdrs(const char *buf, size_t len,
> -			  struct elf_info *elf_info)
> -{
> -	size_t phdr_size, i;
> -	const struct elfhdr *ehdr = elf_info->ehdr;
> -
> -	/*
> -	 * e_phnum is at most 65535 so calculating the size of the
> -	 * program header cannot overflow.
> -	 */
> -	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> -
> -	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> -	if (!elf_info->proghdrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		int ret;
> -
> -		ret = elf_read_phdr(buf, len, elf_info, i);
> -		if (ret) {
> -			kfree(elf_info->proghdrs);
> -			elf_info->proghdrs = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_is_shdr_sane - check that it is safe to use the section header
> - * @buf_len:	size of the buffer in which the ELF file is loaded.
> - */
> -static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> -{
> -	bool size_ok;
> -
> -	/* SHT_NULL headers have undefined values, so we can't check them. */
> -	if (shdr->sh_type == SHT_NULL)
> -		return true;
> -
> -	/* Now verify sh_entsize */
> -	switch (shdr->sh_type) {
> -	case SHT_SYMTAB:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> -		break;
> -	case SHT_RELA:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> -		break;
> -	case SHT_DYNAMIC:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> -		break;
> -	case SHT_REL:
> -		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> -		break;
> -	case SHT_NOTE:
> -	case SHT_PROGBITS:
> -	case SHT_HASH:
> -	case SHT_NOBITS:
> -	default:
> -		/*
> -		 * This is a section whose entsize requirements
> -		 * I don't care about.  If I don't know about
> -		 * the section I can't care about it's entsize
> -		 * requirements.
> -		 */
> -		size_ok = true;
> -		break;
> -	}
> -
> -	if (!size_ok) {
> -		pr_debug("ELF section with wrong entry size.\n");
> -		return false;
> -	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> -		pr_debug("ELF section address wraps around.\n");
> -		return false;
> -	}
> -
> -	if (shdr->sh_type != SHT_NOBITS) {
> -		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> -			pr_debug("ELF section location wraps around.\n");
> -			return false;
> -		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> -			pr_debug("ELF section not in file.\n");
> -			return false;
> -		}
> -	}
> -
> -	return true;
> -}
> -
> -static int elf_read_shdr(const char *buf, size_t len, struct elf_info *elf_info,
> -			 int idx)
> -{
> -	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> -	const struct elfhdr *ehdr = elf_info->ehdr;
> -	const char *sbuf;
> -	struct elf_shdr *buf_shdr;
> -
> -	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> -	buf_shdr = (struct elf_shdr *) sbuf;
> -
> -	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> -	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> -	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> -	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> -	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> -	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> -
> -	/*
> -	 * The following fields have a type equivalent to Elf_Addr
> -	 * both in 32 bit and 64 bit ELF.
> -	 */
> -	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> -	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> -	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> -	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> -
> -	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_read_shdrs - read the section headers from the buffer
> - *
> - * This function assumes that the section header table was checked for sanity.
> - * Use elf_is_ehdr_sane() if it wasn't.
> - */
> -static int elf_read_shdrs(const char *buf, size_t len,
> -			  struct elf_info *elf_info)
> -{
> -	size_t shdr_size, i;
> -
> -	/*
> -	 * e_shnum is at most 65536 so calculating
> -	 * the size of the section header cannot overflow.
> -	 */
> -	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> -
> -	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> -	if (!elf_info->sechdrs)
> -		return -ENOMEM;
> -
> -	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> -		int ret;
> -
> -		ret = elf_read_shdr(buf, len, elf_info, i);
> -		if (ret) {
> -			kfree(elf_info->sechdrs);
> -			elf_info->sechdrs = NULL;
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> - * @buf:	Buffer to read ELF file from.
> - * @len:	Size of @buf.
> - * @ehdr:	Pointer to existing struct which will be populated.
> - * @elf_info:	Pointer to existing struct which will be populated.
> - *
> - * This function allows reading ELF files with different byte order than
> - * the kernel, byte-swapping the fields as needed.
> - *
> - * Return:
> - * On success returns 0, and the caller should call elf_free_info(elf_info) to
> - * free the memory allocated for the section and program headers.
> - */
> -int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> -			 struct elf_info *elf_info)
> -{
> -	int ret;
> -
> -	ret = elf_read_ehdr(buf, len, ehdr);
> -	if (ret)
> -		return ret;
> -
> -	elf_info->buffer = buf;
> -	elf_info->ehdr = ehdr;
> -	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> -		ret = elf_read_phdrs(buf, len, elf_info);
> -		if (ret)
> -			return ret;
> -	}
> -	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> -		ret = elf_read_shdrs(buf, len, elf_info);
> -		if (ret) {
> -			kfree(elf_info->proghdrs);
> -			return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -/**
> - * elf_free_info - free memory allocated by elf_read_from_buffer
> - */
> -void elf_free_info(struct elf_info *elf_info)
> -{
> -	kfree(elf_info->proghdrs);
> -	kfree(elf_info->sechdrs);
> -	memset(elf_info, 0, sizeof(*elf_info));
> -}
> -/**
> - * build_elf_exec_info - read ELF executable and check that we can use it
> - */
> -static int build_elf_exec_info(const char *buf, size_t len, struct elfhdr *ehdr,
> -			       struct elf_info *elf_info)
> -{
> -	int i;
> -	int ret;
> -
> -	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> -	if (ret)
> -		return ret;
> -
> -	/* Big endian vmlinux has type ET_DYN. */
> -	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> -		pr_err("Not an ELF executable.\n");
> -		goto error;
> -	} else if (!elf_info->proghdrs) {
> -		pr_err("No ELF program header.\n");
> -		goto error;
> -	}
> -
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		/*
> -		 * Kexec does not support loading interpreters.
> -		 * In addition this check keeps us from attempting
> -		 * to kexec ordinay executables.
> -		 */
> -		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> -			pr_err("Requires an ELF interpreter.\n");
> -			goto error;
> -		}
> -	}
> -
> -	return 0;
> -error:
> -	elf_free_info(elf_info);
> -	return -ENOEXEC;
> -}
> -
> -static int elf64_probe(const char *buf, unsigned long len)
> -{
> -	struct elfhdr ehdr;
> -	struct elf_info elf_info;
> -	int ret;
> -
> -	ret = build_elf_exec_info(buf, len, &ehdr, &elf_info);
> -	if (ret)
> -		return ret;
> -
> -	elf_free_info(&elf_info);
> -
> -	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> -}
> -
> -/**
> - * elf_exec_load - load ELF executable image
> - * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
> - *			section will be loaded in memory.
> - *
> - * Return:
> - * 0 on success, negative value on failure.
> - */
> -static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr,
> -			 struct elf_info *elf_info,
> -			 unsigned long *lowest_load_addr)
> -{
> -	unsigned long base = 0, lowest_addr = UINT_MAX;
> -	int ret;
> -	size_t i;
> -	struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size,
> -				  .top_down = false };
> -
> -	/* Read in the PT_LOAD segments. */
> -	for (i = 0; i < ehdr->e_phnum; i++) {
> -		unsigned long load_addr;
> -		size_t size;
> -		const struct elf_phdr *phdr;
> -
> -		phdr = &elf_info->proghdrs[i];
> -		if (phdr->p_type != PT_LOAD)
> -			continue;
> -
> -		size = phdr->p_filesz;
> -		if (size > phdr->p_memsz)
> -			size = phdr->p_memsz;
> -
> -		kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset;
> -		kbuf.bufsz = size;
> -		kbuf.memsz = phdr->p_memsz;
> -		kbuf.buf_align = phdr->p_align;
> -		kbuf.buf_min = phdr->p_paddr + base;
> -		ret = kexec_add_buffer(&kbuf);
> -		if (ret)
> -			goto out;
> -		load_addr = kbuf.mem;
> -
> -		if (load_addr < lowest_addr)
> -			lowest_addr = load_addr;
> -	}
> -
> -	/* Update entry point to reflect new load address. */
> -	ehdr->e_entry += base;
> -
> -	*lowest_load_addr = lowest_addr;
> -	ret = 0;
> - out:
> -	return ret;
> -}
> -
>   static void *elf64_load(struct kimage *image, char *kernel_buf,
>   			unsigned long kernel_len, char *initrd,
>   			unsigned long initrd_len, char *cmdline,
> @@ -577,17 +41,17 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   	void *fdt;
>   	const void *slave_code;
>   	struct elfhdr ehdr;
> -	struct elf_info elf_info;
> +	struct kexec_elf_info elf_info;
>   	struct kexec_buf kbuf = { .image = image, .buf_min = 0,
>   				  .buf_max = ppc64_rma_size };
>   	struct kexec_buf pbuf = { .image = image, .buf_min = 0,
>   				  .buf_max = ppc64_rma_size, .top_down = true };
>   
> -	ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> +	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
>   	if (ret)
>   		goto out;
>   
> -	ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr);
> +	ret = kexec_elf_load(image, &ehdr, &elf_info, &kbuf, &kernel_load_addr);
>   	if (ret)
>   		goto out;
>   
> @@ -652,13 +116,12 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
>   		pr_err("Error setting up the purgatory.\n");
>   
>   out:
> -	elf_free_info(&elf_info);
> -
> +	kexec_free_elf_info(&elf_info);
>   	/* Make kimage_file_post_load_cleanup free the fdt buffer for us. */
>   	return ret ? ERR_PTR(ret) : fdt;
>   }
>   
>   const struct kexec_file_ops kexec_elf64_ops = {
> -	.probe = elf64_probe,
> +	.probe = kexec_elf_probe,
>   	.load = elf64_load,
>   };
> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index b9b1bc5f9669..49b23b425f84 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
>   				       void **addr, unsigned long *sz);
>   #endif /* CONFIG_KEXEC_FILE */
>   
> +#ifdef CONFIG_KEXEC_FILE_ELF
> +
> +struct kexec_elf_info {
> +	/*
> +	 * Where the ELF binary contents are kept.
> +	 * Memory managed by the user of the struct.
> +	 */
> +	const char *buffer;
> +
> +	const struct elfhdr *ehdr;
> +	const struct elf_phdr *proghdrs;
> +	struct elf_shdr *sechdrs;
> +};
> +
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> +
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			  struct kexec_elf_info *elf_info);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> +			  char *kernel_buf, unsigned long kernel_len,
> +			  unsigned long *kernel_load_addr);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);
> +#endif
>   struct kimage {
>   	kimage_entry_t head;
>   	kimage_entry_t *entry;
> diff --git a/kernel/Makefile b/kernel/Makefile
> index 33824f0385b3..fdba91785977 100644
> --- a/kernel/Makefile
> +++ b/kernel/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
>   obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
>   obj-$(CONFIG_KEXEC) += kexec.o
>   obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
> +obj-$(CONFIG_KEXEC_FILE_ELF) += kexec_file_elf.o
>   obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
>   obj-$(CONFIG_COMPAT) += compat.o
>   obj-$(CONFIG_CGROUPS) += cgroup/
> diff --git a/kernel/kexec_file_elf.c b/kernel/kexec_file_elf.c
> new file mode 100644
> index 000000000000..bb966c93492c
> --- /dev/null
> +++ b/kernel/kexec_file_elf.c
> @@ -0,0 +1,574 @@
> +/*
> + * Load ELF vmlinux file for the kexec_file_load syscall.
> + *
> + * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
> + * Copyright (C) 2004  IBM Corp.
> + * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
> + * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
> + * Copyright (C) 2016  IBM Corporation
> + *
> + * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
> + * Heavily modified for the kernel by
> + * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation (version 2 of the License).
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt)	"kexec_elf: " fmt
> +
> +#include <linux/elf.h>
> +#include <linux/kexec.h>
> +#include <linux/libfdt.h>
> +#include <linux/module.h>
> +#include <linux/of_fdt.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#define elf_addr_to_cpu	elf64_to_cpu
> +
> +#ifndef Elf_Rel
> +#define Elf_Rel		Elf64_Rel
> +#endif /* Elf_Rel */
> +
> +static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
> +{
> +       return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
> +}
> +
> +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le64_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be64_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le16_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be16_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le32_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be32_to_cpu(value);
> +
> +	return value;
> +}
> +
> +/**
> + * elf_is_ehdr_sane - check that it is safe to use the ELF header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
> +{
> +	if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
> +		pr_debug("Bad program header size.\n");
> +		return false;
> +	} else if (ehdr->e_shnum > 0 &&
> +		   ehdr->e_shentsize != sizeof(struct elf_shdr)) {
> +		pr_debug("Bad section header size.\n");
> +		return false;
> +	} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
> +		   ehdr->e_version != EV_CURRENT) {
> +		pr_debug("Unknown ELF version.\n");
> +		return false;
> +	}
> +
> +	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> +		size_t phdr_size;
> +
> +		/*
> +		 * e_phnum is at most 65535 so calculating the size of the
> +		 * program header cannot overflow.
> +		 */
> +		phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> +		/* Sanity check the program header table location. */
> +		if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
> +			pr_debug("Program headers at invalid location.\n");
> +			return false;
> +		} else if (ehdr->e_phoff + phdr_size > buf_len) {
> +			pr_debug("Program headers truncated.\n");
> +			return false;
> +		}
> +	}
> +
> +	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> +		size_t shdr_size;
> +
> +		/*
> +		 * e_shnum is at most 65536 so calculating
> +		 * the size of the section header cannot overflow.
> +		 */
> +		shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
> +
> +		/* Sanity check the section header table location. */
> +		if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
> +			pr_debug("Section headers at invalid location.\n");
> +			return false;
> +		} else if (ehdr->e_shoff + shdr_size > buf_len) {
> +			pr_debug("Section headers truncated.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
> +{
> +	struct elfhdr *buf_ehdr;
> +
> +	if (len < sizeof(*buf_ehdr)) {
> +		pr_debug("Buffer is too small to hold ELF header.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	memset(ehdr, 0, sizeof(*ehdr));
> +	memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
> +	if (!elf_is_elf_file(ehdr)) {
> +		pr_debug("No ELF header magic.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
> +		pr_debug("Not a supported ELF class.\n");
> +		return -ENOEXEC;
> +	} else  if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
> +		ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
> +		pr_debug("Not a supported ELF data format.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	buf_ehdr = (struct elfhdr *) buf;
> +	if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
> +		pr_debug("Bad ELF header size.\n");
> +		return -ENOEXEC;
> +	}
> +
> +	ehdr->e_type      = elf16_to_cpu(ehdr, buf_ehdr->e_type);
> +	ehdr->e_machine   = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
> +	ehdr->e_version   = elf32_to_cpu(ehdr, buf_ehdr->e_version);
> +	ehdr->e_entry     = elf_addr_to_cpu(ehdr, buf_ehdr->e_entry);
> +	ehdr->e_phoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_phoff);
> +	ehdr->e_shoff     = elf_addr_to_cpu(ehdr, buf_ehdr->e_shoff);
> +	ehdr->e_flags     = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
> +	ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
> +	ehdr->e_phnum     = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
> +	ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
> +	ehdr->e_shnum     = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
> +	ehdr->e_shstrndx  = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
> +
> +	return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_is_phdr_sane - check that it is safe to use the program header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
> +{
> +
> +	if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
> +		pr_debug("ELF segment location wraps around.\n");
> +		return false;
> +	} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
> +		pr_debug("ELF segment not in file.\n");
> +		return false;
> +	} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
> +		pr_debug("ELF segment address wraps around.\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_phdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	/* Override the const in proghdrs, we are the ones doing the loading. */
> +	struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
> +	const char *pbuf;
> +	struct elf_phdr *buf_phdr;
> +
> +	pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
> +	buf_phdr = (struct elf_phdr *) pbuf;
> +
> +	phdr->p_type   = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
> +	phdr->p_offset = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_offset);
> +	phdr->p_paddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_paddr);
> +	phdr->p_vaddr  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_vaddr);
> +	phdr->p_flags  = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	phdr->p_filesz = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_filesz);
> +	phdr->p_memsz  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_memsz);
> +	phdr->p_align  = elf_addr_to_cpu(elf_info->ehdr, buf_phdr->p_align);
> +
> +	return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_phdrs - read the program headers from the buffer
> + *
> + * This function assumes that the program header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_phdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t phdr_size, i;
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +
> +	/*
> +	 * e_phnum is at most 65535 so calculating the size of the
> +	 * program header cannot overflow.
> +	 */
> +	phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
> +
> +	elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
> +	if (!elf_info->proghdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_phdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->proghdrs);
> +			elf_info->proghdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * elf_is_shdr_sane - check that it is safe to use the section header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> +{
> +	bool size_ok;
> +
> +	/* SHT_NULL headers have undefined values, so we can't check them. */
> +	if (shdr->sh_type == SHT_NULL)
> +		return true;
> +
> +	/* Now verify sh_entsize */
> +	switch (shdr->sh_type) {
> +	case SHT_SYMTAB:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> +		break;
> +	case SHT_RELA:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> +		break;
> +	case SHT_DYNAMIC:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> +		break;
> +	case SHT_REL:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> +		break;
> +	case SHT_NOTE:
> +	case SHT_PROGBITS:
> +	case SHT_HASH:
> +	case SHT_NOBITS:
> +	default:
> +		/*
> +		 * This is a section whose entsize requirements
> +		 * I don't care about.  If I don't know about
> +		 * the section I can't care about it's entsize
> +		 * requirements.
> +		 */
> +		size_ok = true;
> +		break;
> +	}
> +
> +	if (!size_ok) {
> +		pr_debug("ELF section with wrong entry size.\n");
> +		return false;
> +	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> +		pr_debug("ELF section address wraps around.\n");
> +		return false;
> +	}
> +
> +	if (shdr->sh_type != SHT_NOBITS) {
> +		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> +			pr_debug("ELF section location wraps around.\n");
> +			return false;
> +		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> +			pr_debug("ELF section not in file.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +	const char *sbuf;
> +	struct elf_shdr *buf_shdr;
> +
> +	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> +	buf_shdr = (struct elf_shdr *) sbuf;
> +
> +	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> +	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> +	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> +	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> +	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> +	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> +	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> +	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> +	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> +
> +	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_shdrs - read the section headers from the buffer
> + *
> + * This function assumes that the section header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_shdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t shdr_size, i;
> +
> +	/*
> +	 * e_shnum is at most 65536 so calculating
> +	 * the size of the section header cannot overflow.
> +	 */
> +	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> +
> +	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> +	if (!elf_info->sechdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_shdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->sechdrs);
> +			elf_info->sechdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
> + * @buf:	Buffer to read ELF file from.
> + * @len:	Size of @buf.
> + * @ehdr:	Pointer to existing struct which will be populated.
> + * @elf_info:	Pointer to existing struct which will be populated.
> + *
> + * This function allows reading ELF files with different byte order than
> + * the kernel, byte-swapping the fields as needed.
> + *
> + * Return:
> + * On success returns 0, and the caller should call kexec_free_elf_info(elf_info) to
> + * free the memory allocated for the section and program headers.
> + */
> +int elf_read_from_buffer(const char *buf, size_t len, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info)
> +{
> +	int ret;
> +
> +	ret = elf_read_ehdr(buf, len, ehdr);
> +	if (ret)
> +		return ret;
> +
> +	elf_info->buffer = buf;
> +	elf_info->ehdr = ehdr;
> +	if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
> +		ret = elf_read_phdrs(buf, len, elf_info);
> +		if (ret)
> +			return ret;
> +	}
> +	if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
> +		ret = elf_read_shdrs(buf, len, elf_info);
> +		if (ret) {
> +			kfree(elf_info->proghdrs);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> + */
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> +{
> +	kfree(elf_info->proghdrs);
> +	kfree(elf_info->sechdrs);
> +	memset(elf_info, 0, sizeof(*elf_info));
> +}
> +EXPORT_SYMBOL(kexec_free_elf_info);
> +
> +/**
> + * kexec_build_elf_info - read ELF executable and check that we can use it
> + */
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	int i;
> +	int ret;
> +
> +	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> +	if (ret)
> +		return ret;
> +
> +	/* Big endian vmlinux has type ET_DYN. */
> +	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> +		pr_err("Not an ELF executable.\n");
> +		goto error;
> +	} else if (!elf_info->proghdrs) {
> +		pr_err("No ELF program header.\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		/*
> +		 * Kexec does not support loading interpreters.
> +		 * In addition this check keeps us from attempting
> +		 * to kexec ordinay executables.
> +		 */
> +		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> +			pr_err("Requires an ELF interpreter.\n");
> +			goto error;
> +		}
> +	}
> +
> +	return 0;
> +error:
> +	kexec_free_elf_info(elf_info);
> +	return -ENOEXEC;
> +}
> +EXPORT_SYMBOL(kexec_build_elf_info);
> +
> +/**
> + * elf_exec_load - load ELF executable image
> + * @lowest_load_addr:	On return, will be the address where the first PT_LOAD
> + *			section will be loaded in memory.
> + *
> + * Return:
> + * 0 on success, negative value on failure.
> + */
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +		   struct kexec_elf_info *elf_info,
> +		   struct kexec_buf *kbuf,
> +		   unsigned long *lowest_load_addr)
> +{
> +	unsigned long lowest_addr = UINT_MAX;
> +	int ret;
> +	size_t i;
> +	/* Read in the PT_LOAD segments. */
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		unsigned long load_addr;
> +		size_t size;
> +		const struct elf_phdr *phdr;
> +
> +		phdr = &elf_info->proghdrs[i];
> +		if (phdr->p_type != PT_LOAD)
> +			continue;
> +
> +		size = phdr->p_filesz;
> +		if (size > phdr->p_memsz)
> +			size = phdr->p_memsz;
> +
> +		kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
> +		kbuf->bufsz = size;
> +		kbuf->memsz = phdr->p_memsz;
> +		kbuf->buf_align = phdr->p_align;
> +		kbuf->buf_min = phdr->p_paddr;
> +		kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
> +		ret = kexec_add_buffer(kbuf);
> +		if (ret)
> +			goto out;
> +		load_addr = kbuf->mem;
> +
> +		if (load_addr < lowest_addr)
> +			lowest_addr = load_addr;
> +	}
> +
> +	image->start = ehdr->e_entry;
> +	*lowest_load_addr = lowest_addr;
> +	ret = 0;
> + out:
> +	return ret;
> +}
> +EXPORT_SYMBOL(kexec_elf_load);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> +			char *kernel_buf, unsigned long kernel_len,
> +			unsigned long *kernel_load_addr)
> +{
> +	int ret;
> +	struct elfhdr ehdr;
> +	struct kexec_elf_info elf_info;
> +
> +	ret = kexec_build_elf_info(kernel_buf, kernel_len, &ehdr, &elf_info);
> +	if (ret)
> +		goto out;
> +
> +	ret = kexec_elf_load(image, &ehdr, &elf_info, kbuf, kernel_load_addr);
> +out:
> +	kexec_free_elf_info(&elf_info);
> +	return ret;
> +
> +}
> +EXPORT_SYMBOL(kexec_elf_kernel_load);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len)
> +{
> +	struct elfhdr ehdr;
> +	struct kexec_elf_info elf_info;
> +	int ret;
> +
> +	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
> +	if (ret)
> +		return ret;
> +
> +	kexec_free_elf_info(&elf_info);
> +
> +	return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
> +}
> +EXPORT_SYMBOL(kexec_elf_probe);
> 



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] generic ELF support for kexec
  2019-06-25 18:54 [PATCH RFC] generic ELF support for kexec Sven Schnelle
  2019-06-26 16:09 ` Christophe Leroy
@ 2019-06-28  2:04 ` Michael Ellerman
  2019-07-01 17:08   ` Sven Schnelle
  2019-07-01 12:31 ` Philipp Rudo
  2 siblings, 1 reply; 6+ messages in thread
From: Michael Ellerman @ 2019-06-28  2:04 UTC (permalink / raw)
  To: Sven Schnelle, kexec; +Cc: linux-s390, deller, linuxppc-dev

Hi Sven,

Sven Schnelle <svens@stackframe.org> writes:
> Hi List,
>
> i recently started working on kexec for PA-RISC. While doing so, i figured
> that powerpc already has support for reading ELF images inside of the Kernel.
> My first attempt was to steal the source code and modify it for PA-RISC, but
> it turned out that i didn't had to change much. Only ARM specific stuff like
> fdt blob fetching had to be removed.
>
> So instead of duplicating the code, i thought about moving the ELF stuff to
> the core kexec code, and exposing several function to use that code from the
> arch specific code.
>
> I'm attaching the patch to this Mail. What do you think about that change?
> s390 also uses ELF files, and (maybe?) could also switch to this implementation.
> But i don't know anything about S/390 and don't have one in my basement. So
> i'll leave s390 to the IBM folks.

It looks mostly OK, a few comments below.

> I haven't really tested PowerPC yet. Can anyone give me a helping hand what
> would be a good target to test this code in QEMU? Or even better, test this
> code on real Hardware?

There's some info on using qemu here:
  https://github.com/linuxppc/wiki/wiki/Booting-with-Qemu

But I'm not sure where you get a version of kexec that uses kexec_file().

We can probably work out those details though.

> If that change is acceptable i would finish the patch and submit it. I think
> best would be to push this change through Helge's parisc tree, so we don't
> have any dependencies to sort out.

That will work for you but could cause us problems if we have any
changes that touch that code.

It's easy enough to create a topic branch with just that patch that both
of us merge.


> diff --git a/arch/Kconfig b/arch/Kconfig
> index c47b328eada0..de7520100136 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -18,6 +18,9 @@ config KEXEC_CORE
>  	select CRASH_CORE
>  	bool
>  
> +config KEXEC_FILE_ELF
> +	bool

We could probably just call it KEXEC_ELF I think? The functions are
called that after all.

> diff --git a/arch/powerpc/kernel/kexec_elf_64.c b/arch/powerpc/kernel/kexec_elf_64.c
> index ba4f18a43ee8..0059e36913e9 100644
> --- a/arch/powerpc/kernel/kexec_elf_64.c
> +++ b/arch/powerpc/kernel/kexec_elf_64.c
> @@ -21,8 +21,6 @@
>   * GNU General Public License for more details.
>   */
>  
> -#define pr_fmt(fmt)	"kexec_elf: " fmt
> -

Please don't remove that, there are still some prints left in this file
that use it.

>  #include <linux/elf.h>
>  #include <linux/kexec.h>
>  #include <linux/libfdt.h>
> @@ -31,540 +29,6 @@
>  #include <linux/slab.h>
>  #include <linux/types.h>
>  
> -#define PURGATORY_STACK_SIZE	(16 * 1024)

This is unused AFAICS. We should probably remove it explicitly rather
than as part of this patch.

> diff --git a/kernel/kexec_file_elf.c b/kernel/kexec_file_elf.c
> new file mode 100644
> index 000000000000..bb966c93492c
> --- /dev/null
> +++ b/kernel/kexec_file_elf.c
> @@ -0,0 +1,574 @@
> +/*
> + * Load ELF vmlinux file for the kexec_file_load syscall.
> + *
> + * Copyright (C) 2004  Adam Litke (agl@us.ibm.com)
> + * Copyright (C) 2004  IBM Corp.
> + * Copyright (C) 2005  R Sharada (sharada@in.ibm.com)
> + * Copyright (C) 2006  Mohan Kumar M (mohan@in.ibm.com)
> + * Copyright (C) 2016  IBM Corporation
> + *
> + * Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
> + * Heavily modified for the kernel by
> + * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation (version 2 of the License).
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.

You shouldn't be adding new license text. Instead remove those two
paragraphs and use a SPDX tag.

If you look at the file in master it already has the tag.

> +
> +#define pr_fmt(fmt)	"kexec_elf: " fmt
> +
> +#include <linux/elf.h>
> +#include <linux/kexec.h>
> +#include <linux/libfdt.h>

You don't need that do you?

> +#include <linux/module.h>
> +#include <linux/of_fdt.h>

Or that.

> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#define elf_addr_to_cpu	elf64_to_cpu

Why are we doing that rather than just using elf64_to_cpu directly?

> +#ifndef Elf_Rel
> +#define Elf_Rel		Elf64_Rel
> +#endif /* Elf_Rel */

And that?



cheers

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] generic ELF support for kexec
  2019-06-25 18:54 [PATCH RFC] generic ELF support for kexec Sven Schnelle
  2019-06-26 16:09 ` Christophe Leroy
  2019-06-28  2:04 ` Michael Ellerman
@ 2019-07-01 12:31 ` Philipp Rudo
  2019-07-01 18:11   ` Sven Schnelle
  2 siblings, 1 reply; 6+ messages in thread
From: Philipp Rudo @ 2019-07-01 12:31 UTC (permalink / raw)
  To: Sven Schnelle; +Cc: linux-s390, deller, kexec, linuxppc-dev

Hi Sven,

On Tue, 25 Jun 2019 20:54:34 +0200
Sven Schnelle <svens@stackframe.org> wrote:

> Hi List,
> 
> i recently started working on kexec for PA-RISC. While doing so, i figured
> that powerpc already has support for reading ELF images inside of the Kernel.
> My first attempt was to steal the source code and modify it for PA-RISC, but
> it turned out that i didn't had to change much. Only ARM specific stuff like
> fdt blob fetching had to be removed.
> 
> So instead of duplicating the code, i thought about moving the ELF stuff to
> the core kexec code, and exposing several function to use that code from the
> arch specific code.

That's always the right approach. Well done.

> I'm attaching the patch to this Mail. What do you think about that change?
> s390 also uses ELF files, and (maybe?) could also switch to this implementation.
> But i don't know anything about S/390 and don't have one in my basement. So
> i'll leave s390 to the IBM folks.

I'm afraid there isn't much code here s390 can reuse. I see multiple problems
in kexec_elf_load:

* while loading the phdrs we also need to setup some data structures to pass
  to the next kernel
* the s390 kernel needs to be loaded to a fixed address
* there is no support to load a crash kernel

Of course that could all be fixed/worked around by introducing some arch hooks.
But when you take into account that the whole elf loader on s390 is ~100 lines
of code, I don't think it is worth it.

More comments below.
 
[...]

> diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> index b9b1bc5f9669..49b23b425f84 100644
> --- a/include/linux/kexec.h
> +++ b/include/linux/kexec.h
> @@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
>  				       void **addr, unsigned long *sz);
>  #endif /* CONFIG_KEXEC_FILE */
>  
> +#ifdef CONFIG_KEXEC_FILE_ELF
> +
> +struct kexec_elf_info {
> +	/*
> +	 * Where the ELF binary contents are kept.
> +	 * Memory managed by the user of the struct.
> +	 */
> +	const char *buffer;
> +
> +	const struct elfhdr *ehdr;
> +	const struct elf_phdr *proghdrs;
> +	struct elf_shdr *sechdrs;
> +};

Do i understand this right? elf_info->buffer contains the full elf file and
elf_info->ehdr is a (endianness translated) copy of the files ehdr?

If so ...

> +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> +
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			  struct kexec_elf_info *elf_info);
> +
> +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> +			  char *kernel_buf, unsigned long kernel_len,
> +			  unsigned long *kernel_load_addr);
> +
> +int kexec_elf_probe(const char *buf, unsigned long len);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);
> +
> +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> +			 struct kexec_elf_info *elf_info,
> +			 struct kexec_buf *kbuf,
> +			 unsigned long *lowest_load_addr);

... you could simplify the arguments by dropping the ehdr argument. The
elf_info should contain all the information needed. Furthermore the kexec_buf
also contains a pointer to its kimage. So you can drop that argument as well.

An other thing is that you kzalloc the memory needed for proghdrs and sechdrs
but expect the user of those functions to provide the memory needed for ehdr.
Wouldn't it be more consistent to also kzalloc the ehdr?

[...]

> diff --git a/kernel/kexec_file_elf.c b/kernel/kexec_file_elf.c
> new file mode 100644
> index 000000000000..bb966c93492c
> --- /dev/null
> +++ b/kernel/kexec_file_elf.c
> @@ -0,0 +1,574 @@

[...]

> +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le64_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be64_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le16_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be16_to_cpu(value);
> +
> +	return value;
> +}
> +
> +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> +{
> +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> +		value = le32_to_cpu(value);
> +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> +		value = be32_to_cpu(value);
> +
> +	return value;
> +}

What are the elf*_to_cpu good for? In general I'd assume that kexec loads a
kernel for the same architecture it is running on. So the new kernel should
also have the same endianness like the one which loads it. Is this a
ppcle/ppcbe issue?

Furthermore the current order is 64->16->32, which my OCPD absolutely hates :)

[...]

> +/**
> + * elf_is_shdr_sane - check that it is safe to use the section header
> + * @buf_len:	size of the buffer in which the ELF file is loaded.
> + */
> +static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> +{
> +	bool size_ok;
> +
> +	/* SHT_NULL headers have undefined values, so we can't check them. */
> +	if (shdr->sh_type == SHT_NULL)
> +		return true;
> +
> +	/* Now verify sh_entsize */
> +	switch (shdr->sh_type) {
> +	case SHT_SYMTAB:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> +		break;
> +	case SHT_RELA:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> +		break;
> +	case SHT_DYNAMIC:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> +		break;
> +	case SHT_REL:
> +		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> +		break;
> +	case SHT_NOTE:
> +	case SHT_PROGBITS:
> +	case SHT_HASH:
> +	case SHT_NOBITS:
> +	default:
> +		/*
> +		 * This is a section whose entsize requirements
> +		 * I don't care about.  If I don't know about
> +		 * the section I can't care about it's entsize
> +		 * requirements.
> +		 */
> +		size_ok = true;
> +		break;
> +	}
> +
> +	if (!size_ok) {
> +		pr_debug("ELF section with wrong entry size.\n");
> +		return false;
> +	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> +		pr_debug("ELF section address wraps around.\n");
> +		return false;
> +	}
> +
> +	if (shdr->sh_type != SHT_NOBITS) {
> +		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> +			pr_debug("ELF section location wraps around.\n");
> +			return false;
> +		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> +			pr_debug("ELF section not in file.\n");
> +			return false;
> +		}
> +	}
> +
> +	return true;
> +}
> +
> +static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> +			 int idx)
> +{
> +	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> +	const struct elfhdr *ehdr = elf_info->ehdr;
> +	const char *sbuf;
> +	struct elf_shdr *buf_shdr;
> +
> +	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> +	buf_shdr = (struct elf_shdr *) sbuf;
> +
> +	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> +	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> +	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> +	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> +	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> +	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> +
> +	/*
> +	 * The following fields have a type equivalent to Elf_Addr
> +	 * both in 32 bit and 64 bit ELF.
> +	 */
> +	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> +	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> +	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> +	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> +
> +	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> +}
> +
> +/**
> + * elf_read_shdrs - read the section headers from the buffer
> + *
> + * This function assumes that the section header table was checked for sanity.
> + * Use elf_is_ehdr_sane() if it wasn't.
> + */
> +static int elf_read_shdrs(const char *buf, size_t len,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	size_t shdr_size, i;
> +
> +	/*
> +	 * e_shnum is at most 65536 so calculating
> +	 * the size of the section header cannot overflow.
> +	 */
> +	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> +
> +	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> +	if (!elf_info->sechdrs)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> +		int ret;
> +
> +		ret = elf_read_shdr(buf, len, elf_info, i);
> +		if (ret) {
> +			kfree(elf_info->sechdrs);
> +			elf_info->sechdrs = NULL;
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}

In the end you only use the phdrs. So in theory you can drop everything shdr
related. Although keeping it would be 'formally more correct'.

[...]

> +/**
> + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> + */
> +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> +{
> +	kfree(elf_info->proghdrs);
> +	kfree(elf_info->sechdrs);
> +	memset(elf_info, 0, sizeof(*elf_info));
> +}
> +EXPORT_SYMBOL(kexec_free_elf_info);

Why are you exporting these functions? Is there any kexec implementation out
there which is put into a module? Do you even want that to be possible?

> +/**
> + * kexec_build_elf_info - read ELF executable and check that we can use it
> + */
> +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> +			  struct kexec_elf_info *elf_info)
> +{
> +	int i;
> +	int ret;
> +
> +	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> +	if (ret)
> +		return ret;
> +
> +	/* Big endian vmlinux has type ET_DYN. */
> +	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {

s390 is big endian and it's vmlinux has type ET_EXEC. So I assume that this is
a ppc issue?

> +		pr_err("Not an ELF executable.\n");
> +		goto error;
> +	} else if (!elf_info->proghdrs) {
> +		pr_err("No ELF program header.\n");
> +		goto error;
> +	}
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		/*
> +		 * Kexec does not support loading interpreters.
> +		 * In addition this check keeps us from attempting
> +		 * to kexec ordinay executables.
> +		 */
> +		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> +			pr_err("Requires an ELF interpreter.\n");
> +			goto error;
> +		}
> +	}
> +
> +	return 0;
> +error:
> +	kexec_free_elf_info(elf_info);
> +	return -ENOEXEC;
> +}
> +EXPORT_SYMBOL(kexec_build_elf_info);

[...]

> +int kexec_elf_probe(const char *buf, unsigned long len)
> +{
> +	struct elfhdr ehdr;
> +	struct kexec_elf_info elf_info;
> +	int ret;
> +
> +	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);

On s390 I only check the elf magic when probing. That's because the image
loader cannot reliably check the image and thus accepts everything that is
given to it. That also means that any elf file the elf probe rejects (e.g.
because it has a phdr with type PT_INTERP) is passed on to the image loader,
which happily takes it.

If you plan to also add an image loader you should keep that in mind.

Thanks
Philipp


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] generic ELF support for kexec
  2019-06-28  2:04 ` Michael Ellerman
@ 2019-07-01 17:08   ` Sven Schnelle
  0 siblings, 0 replies; 6+ messages in thread
From: Sven Schnelle @ 2019-07-01 17:08 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: linux-s390, deller, kexec, linuxppc-dev

Hi Michael,

On Fri, Jun 28, 2019 at 12:04:11PM +1000, Michael Ellerman wrote:
> Sven Schnelle <svens@stackframe.org> writes:
>   https://github.com/linuxppc/wiki/wiki/Booting-with-Qemu
> 
> But I'm not sure where you get a version of kexec that uses kexec_file().

kexec-tools HEAD supports it, so that's not a problem.

> > If that change is acceptable i would finish the patch and submit it. I think
> > best would be to push this change through Helge's parisc tree, so we don't
> > have any dependencies to sort out.
> 
> That will work for you but could cause us problems if we have any
> changes that touch that code.
> 
> It's easy enough to create a topic branch with just that patch that both
> of us merge.

What should be the base branch for that patch? Christophe suggested the
powerpc/merge branch?

> >  #include <linux/elf.h>
> >  #include <linux/kexec.h>
> >  #include <linux/libfdt.h>
> > @@ -31,540 +29,6 @@
> >  #include <linux/slab.h>
> >  #include <linux/types.h>
> >  
> > -#define PURGATORY_STACK_SIZE	(16 * 1024)
> 
> This is unused AFAICS. We should probably remove it explicitly rather
> than as part of this patch.

I have one patch right now. If wanted i can split up all the changes
suggested during the review into smaller pieces, whatever you prefer.

> Or that.
> 
> > +#include <linux/slab.h>
> > +#include <linux/types.h>
> > +
> > +#define elf_addr_to_cpu	elf64_to_cpu
> 
> Why are we doing that rather than just using elf64_to_cpu directly?
> 
> > +#ifndef Elf_Rel
> > +#define Elf_Rel		Elf64_Rel
> > +#endif /* Elf_Rel */
> 
> And that?

Don't know - ask the PPC people :-)

Regards
Sven

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH RFC] generic ELF support for kexec
  2019-07-01 12:31 ` Philipp Rudo
@ 2019-07-01 18:11   ` Sven Schnelle
  0 siblings, 0 replies; 6+ messages in thread
From: Sven Schnelle @ 2019-07-01 18:11 UTC (permalink / raw)
  To: Philipp Rudo; +Cc: linux-s390, deller, kexec, linuxppc-dev

Hi Philipp,

On Mon, Jul 01, 2019 at 02:31:20PM +0200, Philipp Rudo wrote:
> Sven Schnelle <svens@stackframe.org> wrote:
> 
> > I'm attaching the patch to this Mail. What do you think about that change?
> > s390 also uses ELF files, and (maybe?) could also switch to this implementation.
> > But i don't know anything about S/390 and don't have one in my basement. So
> > i'll leave s390 to the IBM folks.
> 
> I'm afraid there isn't much code here s390 can reuse. I see multiple problems
> in kexec_elf_load:
> 
> * while loading the phdrs we also need to setup some data structures to pass
>   to the next kernel
> * the s390 kernel needs to be loaded to a fixed address
> * there is no support to load a crash kernel
> 
> Of course that could all be fixed/worked around by introducing some arch hooks.
> But when you take into account that the whole elf loader on s390 is ~100 lines
> of code, I don't think it is worth it.

That's fine. I didn't really look into the S/390 Loader, and just wanted to let
the IBM people know.

> More comments below.
>  
> [...]
> 
> > diff --git a/include/linux/kexec.h b/include/linux/kexec.h
> > index b9b1bc5f9669..49b23b425f84 100644
> > --- a/include/linux/kexec.h
> > +++ b/include/linux/kexec.h
> > @@ -216,6 +216,41 @@ extern int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
> >  				       void **addr, unsigned long *sz);
> >  #endif /* CONFIG_KEXEC_FILE */
> >  
> > +#ifdef CONFIG_KEXEC_FILE_ELF
> > +
> > +struct kexec_elf_info {
> > +	/*
> > +	 * Where the ELF binary contents are kept.
> > +	 * Memory managed by the user of the struct.
> > +	 */
> > +	const char *buffer;
> > +
> > +	const struct elfhdr *ehdr;
> > +	const struct elf_phdr *proghdrs;
> > +	struct elf_shdr *sechdrs;
> > +};
> 
> Do i understand this right? elf_info->buffer contains the full elf file and
> elf_info->ehdr is a (endianness translated) copy of the files ehdr?
> 
> If so ...
> 
> > +void kexec_free_elf_info(struct kexec_elf_info *elf_info);
> > +
> > +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> > +			  struct kexec_elf_info *elf_info);
> > +
> > +int kexec_elf_kernel_load(struct kimage *image, struct kexec_buf *kbuf,
> > +			  char *kernel_buf, unsigned long kernel_len,
> > +			  unsigned long *kernel_load_addr);
> > +
> > +int kexec_elf_probe(const char *buf, unsigned long len);
> > +
> > +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> > +			 struct kexec_elf_info *elf_info,
> > +			 struct kexec_buf *kbuf,
> > +			 unsigned long *lowest_load_addr);
> > +
> > +int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
> > +			 struct kexec_elf_info *elf_info,
> > +			 struct kexec_buf *kbuf,
> > +			 unsigned long *lowest_load_addr);
> 
> ... you could simplify the arguments by dropping the ehdr argument. The
> elf_info should contain all the information needed. Furthermore the kexec_buf
> also contains a pointer to its kimage. So you can drop that argument as well.
> 
> An other thing is that you kzalloc the memory needed for proghdrs and sechdrs
> but expect the user of those functions to provide the memory needed for ehdr.
> Wouldn't it be more consistent to also kzalloc the ehdr?
> 

Good point. I'll think about it. I would like to do that in an extra patch,
as it is not a small style change.

> 
> > diff --git a/kernel/kexec_file_elf.c b/kernel/kexec_file_elf.c
> > new file mode 100644
> > index 000000000000..bb966c93492c
> > --- /dev/null
> > +++ b/kernel/kexec_file_elf.c
> > @@ -0,0 +1,574 @@
> 
> [...]
> 
> > +static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
> > +{
> > +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> > +		value = le64_to_cpu(value);
> > +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> > +		value = be64_to_cpu(value);
> > +
> > +	return value;
> > +}
> > +
> > +static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
> > +{
> > +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> > +		value = le16_to_cpu(value);
> > +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> > +		value = be16_to_cpu(value);
> > +
> > +	return value;
> > +}
> > +
> > +static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
> > +{
> > +	if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
> > +		value = le32_to_cpu(value);
> > +	else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
> > +		value = be32_to_cpu(value);
> > +
> > +	return value;
> > +}
> 
> What are the elf*_to_cpu good for? In general I'd assume that kexec loads a
> kernel for the same architecture it is running on. So the new kernel should
> also have the same endianness like the one which loads it. Is this a
> ppcle/ppcbe issue?

Don't know. I would agree, but i'm not an powerpc expert.

> Furthermore the current order is 64->16->32, which my OCPD absolutely hates :)

Fixed.

> [...]
> 
> > +/**
> > + * elf_is_shdr_sane - check that it is safe to use the section header
> > + * @buf_len:	size of the buffer in which the ELF file is loaded.
> > + */
> > +static bool elf_is_shdr_sane(const struct elf_shdr *shdr, size_t buf_len)
> > +{
> > +	bool size_ok;
> > +
> > +	/* SHT_NULL headers have undefined values, so we can't check them. */
> > +	if (shdr->sh_type == SHT_NULL)
> > +		return true;
> > +
> > +	/* Now verify sh_entsize */
> > +	switch (shdr->sh_type) {
> > +	case SHT_SYMTAB:
> > +		size_ok = shdr->sh_entsize == sizeof(Elf_Sym);
> > +		break;
> > +	case SHT_RELA:
> > +		size_ok = shdr->sh_entsize == sizeof(Elf_Rela);
> > +		break;
> > +	case SHT_DYNAMIC:
> > +		size_ok = shdr->sh_entsize == sizeof(Elf_Dyn);
> > +		break;
> > +	case SHT_REL:
> > +		size_ok = shdr->sh_entsize == sizeof(Elf_Rel);
> > +		break;
> > +	case SHT_NOTE:
> > +	case SHT_PROGBITS:
> > +	case SHT_HASH:
> > +	case SHT_NOBITS:
> > +	default:
> > +		/*
> > +		 * This is a section whose entsize requirements
> > +		 * I don't care about.  If I don't know about
> > +		 * the section I can't care about it's entsize
> > +		 * requirements.
> > +		 */
> > +		size_ok = true;
> > +		break;
> > +	}
> > +
> > +	if (!size_ok) {
> > +		pr_debug("ELF section with wrong entry size.\n");
> > +		return false;
> > +	} else if (shdr->sh_addr + shdr->sh_size < shdr->sh_addr) {
> > +		pr_debug("ELF section address wraps around.\n");
> > +		return false;
> > +	}
> > +
> > +	if (shdr->sh_type != SHT_NOBITS) {
> > +		if (shdr->sh_offset + shdr->sh_size < shdr->sh_offset) {
> > +			pr_debug("ELF section location wraps around.\n");
> > +			return false;
> > +		} else if (shdr->sh_offset + shdr->sh_size > buf_len) {
> > +			pr_debug("ELF section not in file.\n");
> > +			return false;
> > +		}
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static int elf_read_shdr(const char *buf, size_t len, struct kexec_elf_info *elf_info,
> > +			 int idx)
> > +{
> > +	struct elf_shdr *shdr = &elf_info->sechdrs[idx];
> > +	const struct elfhdr *ehdr = elf_info->ehdr;
> > +	const char *sbuf;
> > +	struct elf_shdr *buf_shdr;
> > +
> > +	sbuf = buf + ehdr->e_shoff + idx * sizeof(*buf_shdr);
> > +	buf_shdr = (struct elf_shdr *) sbuf;
> > +
> > +	shdr->sh_name      = elf32_to_cpu(ehdr, buf_shdr->sh_name);
> > +	shdr->sh_type      = elf32_to_cpu(ehdr, buf_shdr->sh_type);
> > +	shdr->sh_addr      = elf_addr_to_cpu(ehdr, buf_shdr->sh_addr);
> > +	shdr->sh_offset    = elf_addr_to_cpu(ehdr, buf_shdr->sh_offset);
> > +	shdr->sh_link      = elf32_to_cpu(ehdr, buf_shdr->sh_link);
> > +	shdr->sh_info      = elf32_to_cpu(ehdr, buf_shdr->sh_info);
> > +
> > +	/*
> > +	 * The following fields have a type equivalent to Elf_Addr
> > +	 * both in 32 bit and 64 bit ELF.
> > +	 */
> > +	shdr->sh_flags     = elf_addr_to_cpu(ehdr, buf_shdr->sh_flags);
> > +	shdr->sh_size      = elf_addr_to_cpu(ehdr, buf_shdr->sh_size);
> > +	shdr->sh_addralign = elf_addr_to_cpu(ehdr, buf_shdr->sh_addralign);
> > +	shdr->sh_entsize   = elf_addr_to_cpu(ehdr, buf_shdr->sh_entsize);
> > +
> > +	return elf_is_shdr_sane(shdr, len) ? 0 : -ENOEXEC;
> > +}
> > +
> > +/**
> > + * elf_read_shdrs - read the section headers from the buffer
> > + *
> > + * This function assumes that the section header table was checked for sanity.
> > + * Use elf_is_ehdr_sane() if it wasn't.
> > + */
> > +static int elf_read_shdrs(const char *buf, size_t len,
> > +			  struct kexec_elf_info *elf_info)
> > +{
> > +	size_t shdr_size, i;
> > +
> > +	/*
> > +	 * e_shnum is at most 65536 so calculating
> > +	 * the size of the section header cannot overflow.
> > +	 */
> > +	shdr_size = sizeof(struct elf_shdr) * elf_info->ehdr->e_shnum;
> > +
> > +	elf_info->sechdrs = kzalloc(shdr_size, GFP_KERNEL);
> > +	if (!elf_info->sechdrs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < elf_info->ehdr->e_shnum; i++) {
> > +		int ret;
> > +
> > +		ret = elf_read_shdr(buf, len, elf_info, i);
> > +		if (ret) {
> > +			kfree(elf_info->sechdrs);
> > +			elf_info->sechdrs = NULL;
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> 
> In the end you only use the phdrs. So in theory you can drop everything shdr
> related. Although keeping it would be 'formally more correct'.

Correct, done.
 
> [...]
> 
> > +/**
> > + * kexec_free_elf_info - free memory allocated by elf_read_from_buffer
> > + */
> > +void kexec_free_elf_info(struct kexec_elf_info *elf_info)
> > +{
> > +	kfree(elf_info->proghdrs);
> > +	kfree(elf_info->sechdrs);
> > +	memset(elf_info, 0, sizeof(*elf_info));
> > +}
> > +EXPORT_SYMBOL(kexec_free_elf_info);
> 
> Why are you exporting these functions? Is there any kexec implementation out
> there which is put into a module? Do you even want that to be possible?

My fault. Fixed.

> > +/**
> > + * kexec_build_elf_info - read ELF executable and check that we can use it
> > + */
> > +int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
> > +			  struct kexec_elf_info *elf_info)
> > +{
> > +	int i;
> > +	int ret;
> > +
> > +	ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/* Big endian vmlinux has type ET_DYN. */
> > +	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
> 
> s390 is big endian and it's vmlinux has type ET_EXEC. So I assume that this is
> a ppc issue?

Again, don't know. :)

> > +		pr_err("Not an ELF executable.\n");
> > +		goto error;
> > +	} else if (!elf_info->proghdrs) {
> > +		pr_err("No ELF program header.\n");
> > +		goto error;
> > +	}
> > +
> > +	for (i = 0; i < ehdr->e_phnum; i++) {
> > +		/*
> > +		 * Kexec does not support loading interpreters.
> > +		 * In addition this check keeps us from attempting
> > +		 * to kexec ordinay executables.
> > +		 */
> > +		if (elf_info->proghdrs[i].p_type == PT_INTERP) {
> > +			pr_err("Requires an ELF interpreter.\n");
> > +			goto error;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +error:
> > +	kexec_free_elf_info(elf_info);
> > +	return -ENOEXEC;
> > +}
> > +EXPORT_SYMBOL(kexec_build_elf_info);
> 
> [...]
> 
> > +int kexec_elf_probe(const char *buf, unsigned long len)
> > +{
> > +	struct elfhdr ehdr;
> > +	struct kexec_elf_info elf_info;
> > +	int ret;
> > +
> > +	ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
> 
> On s390 I only check the elf magic when probing. That's because the image
> loader cannot reliably check the image and thus accepts everything that is
> given to it. That also means that any elf file the elf probe rejects (e.g.
> because it has a phdr with type PT_INTERP) is passed on to the image loader,
> which happily takes it.
> 
> If you plan to also add an image loader you should keep that in mind.
> 
> Thanks
> Philipp
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-07-01 18:13 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-25 18:54 [PATCH RFC] generic ELF support for kexec Sven Schnelle
2019-06-26 16:09 ` Christophe Leroy
2019-06-28  2:04 ` Michael Ellerman
2019-07-01 17:08   ` Sven Schnelle
2019-07-01 12:31 ` Philipp Rudo
2019-07-01 18:11   ` Sven Schnelle

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).