summaryrefslogtreecommitdiffstats
path: root/common/bootm.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2013-06-10 12:13:44 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2013-06-19 22:57:39 +0200
commit7d58f3f346a29c7a0026864cac7c4d3ad0d89d88 (patch)
tree4aeb18b0e972431bd461f7316edb3807fce6540b /common/bootm.c
parent40c4e61554adc11822e68e0dc45f73e17a2a3094 (diff)
downloadbarebox-7d58f3f346a29c7a0026864cac7c4d3ad0d89d88.tar.gz
barebox-7d58f3f346a29c7a0026864cac7c4d3ad0d89d88.tar.xz
bootm: factor out code to make it usable from C
Much of the bootm code is implemented in the command itself. Move it to a common place to be able to call it from C aswell. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common/bootm.c')
-rw-r--r--common/bootm.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/common/bootm.c b/common/bootm.c
new file mode 100644
index 0000000000..1987a09352
--- /dev/null
+++ b/common/bootm.c
@@ -0,0 +1,303 @@
+/*
+ * 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; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#include <common.h>
+#include <boot.h>
+#include <fs.h>
+#include <malloc.h>
+#include <memory.h>
+
+static LIST_HEAD(handler_list);
+
+int register_image_handler(struct image_handler *handler)
+{
+ list_add_tail(&handler->list, &handler_list);
+
+ return 0;
+}
+
+static struct image_handler *bootm_find_handler(enum filetype filetype,
+ struct image_data *data)
+{
+ struct image_handler *handler;
+
+ list_for_each_entry(handler, &handler_list, list) {
+ if (filetype != filetype_uimage &&
+ handler->filetype == filetype)
+ return handler;
+ if (filetype == filetype_uimage &&
+ handler->ih_os == data->os->header.ih_os)
+ return handler;
+ }
+
+ return NULL;
+}
+
+static int bootm_open_os_uimage(struct image_data *data)
+{
+ int ret;
+
+ data->os = uimage_open(data->os_file);
+ if (!data->os)
+ return -EINVAL;
+
+ if (data->verify) {
+ ret = uimage_verify(data->os);
+ if (ret) {
+ printf("Checking data crc failed with %s\n",
+ strerror(-ret));
+ uimage_close(data->os);
+ return ret;
+ }
+ }
+
+ uimage_print_contents(data->os);
+
+ if (data->os->header.ih_arch != IH_ARCH) {
+ printf("Unsupported Architecture 0x%x\n",
+ data->os->header.ih_arch);
+ uimage_close(data->os);
+ return -EINVAL;
+ }
+
+ if (data->os_address == UIMAGE_SOME_ADDRESS)
+ data->os_address = data->os->header.ih_load;
+
+ if (data->os_address != UIMAGE_INVALID_ADDRESS) {
+ data->os_res = uimage_load_to_sdram(data->os, 0,
+ data->os_address);
+ if (!data->os_res) {
+ uimage_close(data->os);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static int bootm_open_initrd_uimage(struct image_data *data)
+{
+ int ret;
+
+ if (!data->initrd_file)
+ return 0;
+
+ if (strcmp(data->os_file, data->initrd_file)) {
+ data->initrd = uimage_open(data->initrd_file);
+ if (!data->initrd)
+ return -EINVAL;
+
+ if (data->verify) {
+ ret = uimage_verify(data->initrd);
+ if (ret) {
+ printf("Checking data crc failed with %s\n",
+ strerror(-ret));
+ }
+ }
+ uimage_print_contents(data->initrd);
+ } else {
+ data->initrd = data->os;
+ }
+
+ return 0;
+}
+
+static int bootm_open_oftree(struct image_data *data, const char *oftree, int num)
+{
+ enum filetype ft;
+ struct fdt_header *fdt;
+ size_t size;
+
+ printf("Loading devicetree from '%s'\n", oftree);
+
+ ft = file_name_detect_type(oftree);
+ if ((int)ft < 0) {
+ printf("failed to open %s: %s\n", oftree, strerror(-(int)ft));
+ return ft;
+ }
+
+ if (ft == filetype_uimage) {
+#ifdef CONFIG_CMD_BOOTM_OFTREE_UIMAGE
+ struct uimage_handle *of_handle;
+ int release = 0;
+
+ if (!strcmp(data->os_file, oftree)) {
+ of_handle = data->os;
+ } else if (!strcmp(data->initrd_file, oftree)) {
+ of_handle = data->initrd;
+ } else {
+ of_handle = uimage_open(oftree);
+ if (!of_handle)
+ return -ENODEV;
+ uimage_print_contents(of_handle);
+ release = 1;
+ }
+
+ fdt = uimage_load_to_buf(of_handle, num, &size);
+
+ if (release)
+ uimage_close(of_handle);
+#else
+ return -EINVAL;
+#endif
+ } else {
+ fdt = read_file(oftree, &size);
+ if (!fdt) {
+ perror("open");
+ return -ENODEV;
+ }
+ }
+
+ ft = file_detect_type(fdt, size);
+ if (ft != filetype_oftree) {
+ printf("%s is not an oftree but %s\n", oftree,
+ file_type_to_string(ft));
+ }
+
+ data->of_root_node = of_unflatten_dtb(NULL, fdt);
+ if (!data->of_root_node) {
+ pr_err("unable to unflatten devicetree\n");
+ return -EINVAL;
+ }
+
+ free(fdt);
+
+ return 0;
+}
+
+static void bootm_print_info(struct image_data *data)
+{
+ if (data->os_res)
+ printf("OS image is at 0x%08x-0x%08x\n",
+ data->os_res->start,
+ data->os_res->end);
+ else
+ printf("OS image not yet relocated\n");
+
+ if (data->initrd_file) {
+ enum filetype initrd_type = file_name_detect_type(data->initrd_file);
+
+ printf("Loading initrd %s '%s'",
+ file_type_to_string(initrd_type),
+ data->initrd_file);
+ if (initrd_type == filetype_uimage &&
+ data->initrd->header.ih_type == IH_TYPE_MULTI)
+ printf(", multifile image %d", data->initrd_num);
+ printf("\n");
+ if (data->initrd_res)
+ printf("initrd is at 0x%08x-0x%08x\n",
+ data->initrd_res->start,
+ data->initrd_res->end);
+ else
+ printf("initrd image not yet relocated\n");
+ }
+}
+
+int bootm_boot(struct image_data *data)
+{
+ struct image_handler *handler;
+ int ret;
+ enum filetype os_type, initrd_type = filetype_unknown;
+
+ os_type = file_name_detect_type(data->os_file);
+ if ((int)os_type < 0) {
+ printf("could not open %s: %s\n", data->os_file,
+ strerror(-os_type));
+ ret = (int)os_type;
+ goto err_out;
+ }
+
+ if (!data->force && os_type == filetype_unknown) {
+ printf("Unknown OS filetype (try -f)\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if (os_type == filetype_uimage) {
+ ret = bootm_open_os_uimage(data);
+ if (ret) {
+ printf("loading os image failed with %s\n",
+ strerror(-ret));
+ goto err_out;
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD) && data->initrd_file) {
+
+ initrd_type = file_name_detect_type(data->initrd_file);
+ if ((int)initrd_type < 0) {
+ printf("could not open %s: %s\n", data->initrd_file,
+ strerror(-initrd_type));
+ ret = (int)initrd_type;
+ goto err_out;
+ }
+ if (initrd_type == filetype_uimage) {
+ ret = bootm_open_initrd_uimage(data);
+ if (ret) {
+ printf("loading initrd failed with %s\n",
+ strerror(-ret));
+ goto err_out;
+ }
+ }
+ }
+
+ printf("\nLoading OS %s '%s'", file_type_to_string(os_type),
+ data->os_file);
+ if (os_type == filetype_uimage &&
+ data->os->header.ih_type == IH_TYPE_MULTI)
+ printf(", multifile image %d", data->os_num);
+ printf("\n");
+
+ if (IS_ENABLED(CONFIG_OFTREE)) {
+ if (data->oftree_file) {
+ ret = bootm_open_oftree(data, data->oftree_file, data->oftree_num);
+ if (ret)
+ goto err_out;
+ } else {
+ data->of_root_node = of_get_root_node();
+ if (data->of_root_node)
+ printf("using internal devicetree\n");
+ }
+ }
+
+ if (data->os_address == UIMAGE_SOME_ADDRESS)
+ data->os_address = UIMAGE_INVALID_ADDRESS;
+
+ handler = bootm_find_handler(os_type, data);
+ if (!handler) {
+ printf("no image handler found for image type %s\n",
+ file_type_to_string(os_type));
+ if (os_type == filetype_uimage)
+ printf("and os type: %d\n", data->os->header.ih_os);
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ if (bootm_verbose(data)) {
+ bootm_print_info(data);
+ printf("Passing control to %s handler\n", handler->name);
+ }
+
+ ret = handler->bootm(data);
+err_out:
+ if (data->os_res)
+ release_sdram_region(data->os_res);
+ if (data->initrd_res)
+ release_sdram_region(data->initrd_res);
+ if (data->initrd && data->initrd != data->os)
+ uimage_close(data->initrd);
+ if (data->os)
+ uimage_close(data->os);
+
+ return ret;
+}