[U-Boot] [PATCH v2 4/7] remoteproc: add elf file load support

Fabien Dessenne fabien.dessenne at st.com
Mon May 27 12:23:14 UTC 2019


The current implementation supports only binary file load.
Add helpers to support ELF format (sanity check, and load).
Note that since an ELF image is built for the remote processor, the load
function uses the device_to_virt ops to translate the addresses.
Implement a basic translation for sandbox_testproc.

Add related tests. Test result:
=> ut dm remoteproc_elf
Test: dm_test_remoteproc_elf: remoteproc.c
Test: dm_test_remoteproc_elf: remoteproc.c (flat tree)
Failures: 0

Signed-off-by: Loic Pallardy <loic.pallardy at st.com>
Signed-off-by: Fabien Dessenne <fabien.dessenne at st.com>
---
 drivers/remoteproc/rproc-uclass.c     |  99 +++++++++++++++++++++++++++
 drivers/remoteproc/sandbox_testproc.c |  19 ++++++
 include/remoteproc.h                  |  30 ++++++++-
 test/dm/remoteproc.c                  | 122 ++++++++++++++++++++++++++++++++++
 4 files changed, 267 insertions(+), 3 deletions(-)

diff --git a/drivers/remoteproc/rproc-uclass.c b/drivers/remoteproc/rproc-uclass.c
index c8a41a6..4d85732 100644
--- a/drivers/remoteproc/rproc-uclass.c
+++ b/drivers/remoteproc/rproc-uclass.c
@@ -5,6 +5,7 @@
  */
 #define pr_fmt(fmt) "%s: " fmt, __func__
 #include <common.h>
+#include <elf.h>
 #include <errno.h>
 #include <fdtdec.h>
 #include <malloc.h>
@@ -291,6 +292,104 @@ int rproc_dev_init(int id)
 	return ret;
 }
 
+/* Basic function to verify ELF image format */
+int rproc_elf_sanity_check(ulong addr, ulong size)
+{
+	Elf32_Ehdr *ehdr;
+	char class;
+
+	if (!addr) {
+		pr_debug("Invalid fw address?\n");
+		return -EFAULT;
+	}
+
+	if (size < sizeof(Elf32_Ehdr)) {
+		pr_debug("Image is too small\n");
+		return -ENOSPC;
+	}
+
+	ehdr = (Elf32_Ehdr *)addr;
+	class = ehdr->e_ident[EI_CLASS];
+
+	if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS32) {
+		pr_debug("Not an executable ELF32 image\n");
+		return -EPROTONOSUPPORT;
+	}
+
+	/* We assume the firmware has the same endianness as the host */
+# ifdef __LITTLE_ENDIAN
+	if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+# else /* BIG ENDIAN */
+	if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+# endif
+		pr_debug("Unsupported firmware endianness\n");
+		return -EILSEQ;
+	}
+
+	if (size < ehdr->e_shoff + sizeof(Elf32_Shdr)) {
+		pr_debug("Image is too small\n");
+		return -ENOSPC;
+	}
+
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+		pr_debug("Image is corrupted (bad magic)\n");
+		return -EBADF;
+	}
+
+	if (ehdr->e_phnum == 0) {
+		pr_debug("No loadable segments\n");
+		return -ENOEXEC;
+	}
+
+	if (ehdr->e_phoff > size) {
+		pr_debug("Firmware size is too small\n");
+		return -ENOSPC;
+	}
+
+	return 0;
+}
+
+/* A very simple elf loader, assumes the image is valid */
+int rproc_elf_load_image(struct udevice *dev, unsigned long addr)
+{
+	Elf32_Ehdr *ehdr; /* Elf header structure pointer */
+	Elf32_Phdr *phdr; /* Program header structure pointer */
+	const struct dm_rproc_ops *ops;
+	unsigned int i;
+
+	ehdr = (Elf32_Ehdr *)addr;
+	phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
+
+	ops = rproc_get_ops(dev);
+
+	/* Load each program header */
+	for (i = 0; i < ehdr->e_phnum; ++i) {
+		void *dst = (void *)(uintptr_t)phdr->p_paddr;
+		void *src = (void *)addr + phdr->p_offset;
+
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		if (ops->device_to_virt)
+			dst = ops->device_to_virt(dev, (ulong)dst);
+
+		dev_dbg(dev, "Loading phdr %i to 0x%p (%i bytes)\n",
+			i, dst, phdr->p_filesz);
+		if (phdr->p_filesz)
+			memcpy(dst, src, phdr->p_filesz);
+		if (phdr->p_filesz != phdr->p_memsz)
+			memset(dst + phdr->p_filesz, 0x00,
+			       phdr->p_memsz - phdr->p_filesz);
+		flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
+			    roundup((unsigned long)dst + phdr->p_filesz,
+				    ARCH_DMA_MINALIGN) -
+			    rounddown((unsigned long)dst, ARCH_DMA_MINALIGN));
+		++phdr;
+	}
+
+	return 0;
+}
+
 int rproc_load(int id, ulong addr, ulong size)
 {
 	struct udevice *dev = NULL;
diff --git a/drivers/remoteproc/sandbox_testproc.c b/drivers/remoteproc/sandbox_testproc.c
index 51a67e6..5f35119 100644
--- a/drivers/remoteproc/sandbox_testproc.c
+++ b/drivers/remoteproc/sandbox_testproc.c
@@ -8,6 +8,7 @@
 #include <dm.h>
 #include <errno.h>
 #include <remoteproc.h>
+#include <asm/io.h>
 
 /**
  * enum sandbox_state - different device states
@@ -300,6 +301,23 @@ static int sandbox_testproc_ping(struct udevice *dev)
 	return ret;
 }
 
+#define SANDBOX_RPROC_DEV_TO_PHY_OFFSET	0x1000
+/**
+ * sandbox_testproc_device_to_virt() - Convert device address to virtual address
+ * @dev:	device to operate upon
+ * @da:		device address
+ * @return converted virtual address
+ */
+static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da)
+{
+	u64 paddr;
+
+	/* Use a simple offset conversion */
+	paddr = da + SANDBOX_RPROC_DEV_TO_PHY_OFFSET;
+
+	return phys_to_virt(paddr);
+}
+
 static const struct dm_rproc_ops sandbox_testproc_ops = {
 	.init = sandbox_testproc_init,
 	.reset = sandbox_testproc_reset,
@@ -308,6 +326,7 @@ static const struct dm_rproc_ops sandbox_testproc_ops = {
 	.stop = sandbox_testproc_stop,
 	.is_running = sandbox_testproc_is_running,
 	.ping = sandbox_testproc_ping,
+	.device_to_virt = sandbox_testproc_device_to_virt,
 };
 
 static const struct udevice_id sandbox_ids[] = {
diff --git a/include/remoteproc.h b/include/remoteproc.h
index aef6ff2..f74ccc2 100644
--- a/include/remoteproc.h
+++ b/include/remoteproc.h
@@ -151,10 +151,10 @@ int rproc_dev_init(int id);
 bool rproc_is_initialized(void);
 
 /**
- * rproc_load() - load binary to a remote processor
+ * rproc_load() - load binary or elf to a remote processor
  * @id:		id of the remote processor
- * @addr:	address in memory where the binary image is located
- * @size:	size of the binary image
+ * @addr:	address in memory where the image is located
+ * @size:	size of the image
  * @return 0 if all ok, else appropriate error value.
  */
 int rproc_load(int id, ulong addr, ulong size);
@@ -200,6 +200,26 @@ int rproc_ping(int id);
  * processor, but just ensures that it is out of reset and executing code.
  */
 int rproc_is_running(int id);
+
+/**
+ * rproc_elf_sanity_check() - Verify if an image is a valid ELF one
+ *
+ * Check if a valid ELF image exists at the given memory location. Verify
+ * basic ELF format requirements like magic number and sections size.
+ *
+ * @addr:	address of the image to verify
+ * @size:	size of the image
+ * @return 0 if the image looks good, else appropriate error value.
+ */
+int rproc_elf_sanity_check(ulong addr, ulong size);
+
+/**
+ * rproc_elf_load_image() - load an ELF image
+ * @dev:	device loading the ELF image
+ * @addr:	valid ELF image address
+ * @return 0 if the image is successfully loaded, else appropriate error value.
+ */
+int rproc_elf_load_image(struct udevice *dev, unsigned long addr);
 #else
 static inline int rproc_init(void) { return -ENOSYS; }
 static inline int rproc_dev_init(int id) { return -ENOSYS; }
@@ -210,6 +230,10 @@ static inline int rproc_stop(int id) { return -ENOSYS; }
 static inline int rproc_reset(int id) { return -ENOSYS; }
 static inline int rproc_ping(int id) { return -ENOSYS; }
 static inline int rproc_is_running(int id) { return -ENOSYS; }
+static inline int rproc_elf_sanity_check(ulong addr,
+					 ulong size) { return -ENOSYS; }
+static inline int rproc_elf_load_image(struct udevice *dev,
+				       unsigned long addr)  { return -ENOSYS; }
 #endif
 
 #endif	/* _RPROC_H_ */
diff --git a/test/dm/remoteproc.c b/test/dm/remoteproc.c
index 3975c67..8d444fc 100644
--- a/test/dm/remoteproc.c
+++ b/test/dm/remoteproc.c
@@ -5,8 +5,10 @@
  */
 #include <common.h>
 #include <dm.h>
+#include <elf.h>
 #include <errno.h>
 #include <remoteproc.h>
+#include <asm/io.h>
 #include <dm/test.h>
 #include <test/ut.h>
 /**
@@ -65,3 +67,123 @@ static int dm_test_remoteproc_base(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_remoteproc_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+#define DEVICE_TO_PHYSICAL_OFFSET	0x1000
+/**
+ * dm_test_remoteproc_elf() - test the ELF operations
+ * @uts:	unit test state
+ *
+ * Return:	0 if test passed, else error
+ */
+static int dm_test_remoteproc_elf(struct unit_test_state *uts)
+{
+	u8 valid_elf32[] = {
+		/* @0x00 - ELF HEADER - */
+		/* ELF magic */
+		0x7f, 0x45, 0x4c, 0x46,
+		/* 32 Bits */
+		0x01,
+		/* Endianness */
+#ifdef __LITTLE_ENDIAN
+		0x01,
+#else
+		0x02,
+#endif
+		/* Version */
+		0x01,
+		/* Padding */
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		/* Type : executable */
+		0x02, 0x00,
+		/* Machine: ARM */
+		0x28, 0x00,
+		/* Version */
+		0x01, 0x00, 0x00, 0x00,
+		/* Entry */
+		0x00, 0x00, 0x00, 0x08,
+		/* phoff (program header offset @ 0x40)*/
+		0x40, 0x00, 0x00, 0x00,
+		/* shoff (section header offset : none) */
+		0x00, 0x00, 0x00, 0x00,
+		/* flags */
+		0x00, 0x00, 0x00, 0x00,
+		/* ehsize (elf header size = 0x34) */
+		0x34, 0x00,
+		/* phentsize (program header size = 0x20) */
+		0x20, 0x00,
+		/* phnum (program header number : 1) */
+		0x01, 0x00,
+		/* shentsize (section heade size : none) */
+		0x00, 0x00,
+		/* shnum (section header number: none) */
+		0x00, 0x00,
+		/* shstrndx (section header name section index: none) */
+		0x00, 0x00,
+		/* padding */
+		0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00,
+		/* @0x40 - PROGRAM HEADER TABLE - */
+		/* type : PT_LOAD */
+		0x01, 0x00, 0x00, 0x00,
+		/* offset */
+		0x00, 0x00, 0x00, 0x00,
+		/* vaddr */
+		0x00, 0x00, 0x00, 0x00,
+		/* paddr : physical address */
+		0x00, 0x00, 0x00, 0x00,
+		/* filesz : 0x20 bytes (program header size) */
+		0x20, 0x00, 0x00, 0x00,
+		/* memsz = filesz */
+		0x20, 0x00, 0x00, 0x00,
+		/* flags : readable and exectuable */
+		0x05, 0x00, 0x00, 0x00,
+		/* padding */
+		0x00, 0x00, 0x00, 0x00,
+	};
+	unsigned int size = ARRAY_SIZE(valid_elf32);
+	struct udevice *dev;
+	phys_addr_t loaded_firmware_paddr;
+	void *loaded_firmware;
+	u32 loaded_firmware_size;
+	Elf32_Ehdr *ehdr = (Elf32_Ehdr *)valid_elf32;
+	Elf32_Phdr *phdr = (Elf32_Phdr *)(valid_elf32 + ehdr->e_phoff);
+
+	ut_assertok(uclass_get_device(UCLASS_REMOTEPROC, 0, &dev));
+
+	/*
+	 * In its Program Header Table, let the firmware specifies to be loaded
+	 * at SDRAM_BASE *device* address (p_paddr field).
+	 * Its size is defined by the p_filesz field.
+	 */
+	phdr->p_paddr = CONFIG_SYS_SDRAM_BASE;
+	loaded_firmware_size = phdr->p_filesz;
+
+	/*
+	 * This *device* address is converted to a *physical* address by the
+	 * device_to_virt() operation of sandbox_test_rproc which returns
+	 * DeviceAddress + DEVICE_TO_PHYSICAL_OFFSET.
+	 * This is where we expect to get the firmware loaded.
+	 */
+	loaded_firmware_paddr = phdr->p_paddr + DEVICE_TO_PHYSICAL_OFFSET;
+	loaded_firmware = map_physmem(loaded_firmware_paddr,
+				      loaded_firmware_size, MAP_NOCACHE);
+	ut_assertnonnull(loaded_firmware);
+	memset(loaded_firmware, 0, loaded_firmware_size);
+
+	/* Verify valid ELF format */
+	ut_assertok(rproc_elf_sanity_check((ulong)valid_elf32, size));
+
+	/* Load firmware in loaded_firmware, and verify it */
+	ut_assertok(rproc_elf_load_image(dev, (unsigned long)valid_elf32));
+	ut_assertok(memcmp(loaded_firmware, valid_elf32, loaded_firmware_size));
+	unmap_physmem(loaded_firmware, MAP_NOCACHE);
+
+	/* Invalid ELF Magic */
+	valid_elf32[0] = 0;
+	ut_asserteq(-EPROTONOSUPPORT,
+		    rproc_elf_sanity_check((ulong)valid_elf32, size));
+
+	return 0;
+}
+DM_TEST(dm_test_remoteproc_elf, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-- 
2.7.4



More information about the U-Boot mailing list