summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2018-07-09 08:21:04 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2018-07-09 08:21:04 +0200
commit0e8bd85d9b2f981ad24847badda70c73397d8385 (patch)
treead0e96ae3da29ab5c5797d99555d280e9b4d5d14 /common
parentb5ba232df1cf1fd68bd2d7dfb0f3f70f11903c99 (diff)
parenta54a47ff7710d12e5a6c88953b3da85cc162c1ea (diff)
downloadbarebox-0e8bd85d9b2f981ad24847badda70c73397d8385.tar.gz
barebox-0e8bd85d9b2f981ad24847badda70c73397d8385.tar.xz
Merge branch 'for-next/bootm'
Diffstat (limited to 'common')
-rw-r--r--common/Kconfig4
-rw-r--r--common/Makefile1
-rw-r--r--common/bootm.c75
-rw-r--r--common/elf.c145
-rw-r--r--common/filetype.c5
5 files changed, 198 insertions, 32 deletions
diff --git a/common/Kconfig b/common/Kconfig
index 75aea460a3..4909c82322 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -46,6 +46,10 @@ config BLOCK
config BLOCK_WRITE
bool
+config ELF
+ bool
+ depends on MIPS
+
config FILETYPE
bool
diff --git a/common/Makefile b/common/Makefile
index 1ff7d2370b..b6284c2552 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -8,6 +8,7 @@ obj-y += misc.o
obj-pbl-y += memsize.o
obj-y += resource.o
obj-y += bootsource.o
+obj-$(CONFIG_ELF) += elf.o
obj-y += restart.o
obj-y += poweroff.o
obj-$(CONFIG_AUTO_COMPLETE) += complete.o
diff --git a/common/bootm.c b/common/bootm.c
index 5ff6683fe7..169000cccb 100644
--- a/common/bootm.c
+++ b/common/bootm.c
@@ -320,28 +320,24 @@ static int bootm_open_oftree_uimage(struct image_data *data, size_t *size,
}
/*
- * bootm_load_devicetree() - load devicetree
+ * bootm_get_devicetree() - get devicetree
*
* @data: image data context
- * @load_address: The address where the devicetree should be loaded to
*
- * This loads the devicetree to a RAM location. load_address must be a valid
- * address. The resulting devicetree will be found at data->oftree.
+ * This gets the fixed devicetree from the various image sources or the internal
+ * devicetree. It returns a pointer to the allocated devicetree which must be
+ * freed after use.
*
- * Return: 0 on success, negative error code otherwise
+ * Return: pointer to the fixed devicetree or a ERR_PTR() on failure.
*/
-int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
+void *bootm_get_devicetree(struct image_data *data)
{
enum filetype type;
- int fdt_size;
struct fdt_header *oftree;
int ret;
- if (data->oftree)
- return 0;
-
if (!IS_ENABLED(CONFIG_OFTREE))
- return 0;
+ return ERR_PTR(-ENOSYS);
if (IS_ENABLED(CONFIG_FITIMAGE) && data->os_fit &&
fit_has_image(data->os_fit, data->fit_config, "fdt")) {
@@ -351,7 +347,7 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
ret = fit_open_image(data->os_fit, data->fit_config, "fdt",
&of_tree, &of_size);
if (ret)
- return ret;
+ return ERR_PTR(ret);
data->of_root_node = of_unflatten_dtb(of_tree);
} else if (data->oftree_file) {
@@ -362,7 +358,7 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
if ((int)type < 0) {
printf("could not open %s: %s\n", data->oftree_file,
strerror(-type));
- return (int)type;
+ return ERR_PTR((int)type);
}
switch (type) {
@@ -375,11 +371,11 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
FILESIZE_MAX);
break;
default:
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
if (ret)
- return ret;
+ return ERR_PTR(ret);
data->of_root_node = of_unflatten_dtb(oftree);
@@ -388,13 +384,13 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
if (IS_ERR(data->of_root_node)) {
data->of_root_node = NULL;
pr_err("unable to unflatten devicetree\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
} else {
data->of_root_node = of_get_root_node();
if (!data->of_root_node)
- return 0;
+ return NULL;
if (bootm_verbose(data) > 1 && data->of_root_node)
printf("using internal devicetree\n");
@@ -408,31 +404,47 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
oftree = of_get_fixed_tree(data->of_root_node);
if (!oftree)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- fdt_size = be32_to_cpu(oftree->totalsize);
+ fdt_add_reserve_map(oftree);
- data->oftree_res = request_sdram_region("oftree", load_address,
- fdt_size);
- if (!data->oftree_res) {
- free(oftree);
- return -ENOMEM;
- }
+ return oftree;
+}
- memcpy((void *)data->oftree_res->start, oftree, fdt_size);
+/*
+ * bootm_load_devicetree() - load devicetree
+ *
+ * @data: image data context
+ * @fdt: The flat device tree to load
+ * @load_address: The address where the devicetree should be loaded to
+ *
+ * This loads the devicetree to a RAM location. load_address must be a valid
+ * address which is requested with request_sdram_region. The associated region
+ * is released automatically in the bootm error path.
+ *
+ * Return: 0 on success, negative error code otherwise
+ */
+int bootm_load_devicetree(struct image_data *data, void *fdt,
+ unsigned long load_address)
+{
+ int fdt_size;
- free(oftree);
+ if (!IS_ENABLED(CONFIG_OFTREE))
+ return -ENOSYS;
- oftree = (void *)data->oftree_res->start;
+ fdt_size = be32_to_cpu(((struct fdt_header *)fdt)->totalsize);
- fdt_add_reserve_map(oftree);
+ data->oftree_res = request_sdram_region("oftree", load_address,
+ fdt_size);
+ if (!data->oftree_res)
+ return -ENOMEM;
+
+ memcpy((void *)data->oftree_res->start, fdt, fdt_size);
of_print_cmdline(data->of_root_node);
if (bootm_verbose(data) > 1)
of_print_nodes(data->of_root_node, 0);
- data->oftree = oftree;
-
return 0;
}
@@ -576,7 +588,6 @@ int bootm_boot(struct bootm_data *bootm_data)
* When we only allow booting signed images make sure everything
* we boot is in the OS image and not given separately.
*/
- data->oftree = NULL;
data->oftree_file = NULL;
data->initrd_file = NULL;
if (os_type != filetype_oftree) {
diff --git a/common/elf.c b/common/elf.c
new file mode 100644
index 0000000000..8edf388564
--- /dev/null
+++ b/common/elf.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
+ */
+
+#include <common.h>
+#include <elf.h>
+#include <memory.h>
+
+struct elf_section {
+ struct list_head list;
+ struct resource *r;
+};
+
+static int elf_request_region(struct elf_image *elf, resource_size_t start,
+ resource_size_t size)
+{
+ struct list_head *list = &elf->list;
+ struct resource *r_new;
+ struct elf_section *r;
+
+ r = xzalloc(sizeof(*r));
+ r_new = request_sdram_region("elf_section", start, size);
+ if (!r_new) {
+ pr_err("Failed to request region: %pa %pa\n", &start, &size);
+ return -EINVAL;
+ }
+
+ r->r = r_new;
+ list_add_tail(&r->list, list);
+
+ return 0;
+}
+
+static void elf_release_regions(struct elf_image *elf)
+{
+ struct list_head *list = &elf->list;
+ struct elf_section *r, *r_tmp;
+
+ list_for_each_entry_safe(r, r_tmp, list, list) {
+ release_sdram_region(r->r);
+ free(r);
+ }
+}
+
+
+static int load_elf_phdr_segment(struct elf_image *elf, void *src,
+ Elf32_Phdr *phdr)
+{
+ void *dst = (void *)phdr->p_paddr;
+ int ret;
+
+ /* we care only about PT_LOAD segments */
+ if (phdr->p_type != PT_LOAD)
+ return 0;
+
+ if (!phdr->p_filesz)
+ return 0;
+
+ pr_debug("Loading phdr to 0x%p (%i bytes)\n", dst, phdr->p_filesz);
+
+ ret = elf_request_region(elf, (resource_size_t)dst, phdr->p_filesz);
+ if (ret)
+ return ret;
+
+ 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);
+
+ return 0;
+}
+
+static int load_elf_image_phdr(struct elf_image *elf)
+{
+ void *buf = elf->buf;
+ Elf32_Ehdr *ehdr = buf;
+ Elf32_Phdr *phdr = (Elf32_Phdr *)(buf + ehdr->e_phoff);
+ int i, ret;
+
+ elf->entry = ehdr->e_entry;
+
+ for (i = 0; i < ehdr->e_phnum; ++i) {
+ void *src = buf + phdr->p_offset;
+
+ ret = load_elf_phdr_segment(elf, src, phdr);
+ /* in case of error elf_load_image() caller should clean up and
+ * call elf_release_image() */
+ if (ret)
+ return ret;
+
+ ++phdr;
+ }
+
+ return 0;
+}
+
+static int elf_check_image(void *buf)
+{
+ Elf32_Ehdr *ehdr = (Elf32_Ehdr *)buf;
+
+ if (strncmp(buf, ELFMAG, SELFMAG)) {
+ pr_err("ELF magic not found.\n");
+ return -EINVAL;
+ }
+
+ if (ehdr->e_type != ET_EXEC) {
+ pr_err("Non EXEC ELF image.\n");
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+struct elf_image *elf_load_image(void *buf)
+{
+ struct elf_image *elf;
+ int ret;
+
+ elf = xzalloc(sizeof(*elf));
+
+ INIT_LIST_HEAD(&elf->list);
+
+ elf->buf = buf;
+
+ ret = elf_check_image(buf);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = load_elf_image_phdr(elf);
+ if (ret) {
+ elf_release_image(elf);
+ return ERR_PTR(ret);
+ }
+
+ return elf;
+}
+
+void elf_release_image(struct elf_image *elf)
+{
+ elf_release_regions(elf);
+
+ free(elf);
+}
diff --git a/common/filetype.c b/common/filetype.c
index bb807df721..c5f2384a6c 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -27,6 +27,7 @@
#include <envfs.h>
#include <disks.h>
#include <image-sparse.h>
+#include <elf.h>
struct filetype_str {
const char *name; /* human readable filetype */
@@ -69,6 +70,7 @@ static const struct filetype_str filetype_str[] = {
[filetype_kwbimage_v1] = { "MVEBU kwbimage (v1)", "kwb1" },
[filetype_android_sparse] = { "Android sparse image", "sparse" },
[filetype_arm64_linux_image] = { "ARM aarch64 Linux image", "aarch64-linux" },
+ [filetype_elf] = { "ELF", "elf" },
};
const char *file_type_to_string(enum filetype f)
@@ -356,6 +358,9 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
buf[7] == 0x47530000)
return filetype_ch_image_be;
+ if (strncmp(buf8, ELFMAG, 4) == 0)
+ return filetype_elf;
+
return filetype_unknown;
}