Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/linux-sunxi/sunxi-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2021-03-15 15:15:09 +0300
committerGitHub <noreply@github.com>2021-03-15 15:15:09 +0300
commit6c02224448970ee3840e512cc99d5dcbeee9f2a0 (patch)
treeaee5db8390e39ec4b5d48f917a6e333793e7fe59
parent7a6a2221ad887e547fb29e3f8e35d0c5c581911c (diff)
parent14b3492e410ce0771a497219ac7eaeff3d615286 (diff)
Merge pull request #152 from apritzel/fit
sunxi-fel FIT image support
-rw-r--r--Makefile4
-rw-r--r--fel.c49
-rw-r--r--fel_lib.c10
-rw-r--r--fel_lib.h4
-rw-r--r--fit_image.c282
-rw-r--r--fit_image.h32
6 files changed, 371 insertions, 10 deletions
diff --git a/Makefile b/Makefile
index 67d5833..a4fa649 100644
--- a/Makefile
+++ b/Makefile
@@ -137,9 +137,9 @@ SOC_INFO := soc_info.c soc_info.h
FEL_LIB := fel_lib.c fel_lib.h
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
-sunxi-fel: fel.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
+sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \
- $(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS)
+ $(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) -lfdt
sunxi-nand-part: nand-part-main.c nand-part.c nand-part-a10.h nand-part-a20.h
$(CC) $(HOST_CFLAGS) -c -o nand-part-main.o nand-part-main.c
diff --git a/fel.c b/fel.c
index f949ccb..8268a1a 100644
--- a/fel.c
+++ b/fel.c
@@ -19,6 +19,7 @@
#include "portable_endian.h"
#include "fel_lib.h"
#include "fel-spiflash.h"
+#include "fit_image.h"
#include <assert.h>
#include <ctype.h>
@@ -31,9 +32,10 @@
#include <zlib.h>
#include <sys/stat.h>
-static bool verbose = false; /* If set, makes the 'fel' tool more talkative */
+bool verbose = false; /* If set, makes the 'fel' tool more talkative */
static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */
static uint32_t uboot_size = 0; /* size of U-Boot binary */
+static bool enter_in_aarch64 = false;
/* printf-style output, but only if "verbose" flag is active */
#define pr_info(...) \
@@ -45,6 +47,7 @@ static uint32_t uboot_size = 0; /* size of U-Boot binary */
#define IH_TYPE_INVALID 0 /* Invalid Image */
#define IH_TYPE_FIRMWARE 5 /* Firmware Image */
#define IH_TYPE_SCRIPT 6 /* Script file */
+#define IH_TYPE_FLATDT 8 /* DTB or FIT image */
#define IH_NMLEN 32 /* Image Name Length */
/* Additional error codes, newly introduced for get_image_type() */
@@ -90,6 +93,8 @@ int get_image_type(const uint8_t *buf, size_t len)
if (len <= HEADER_SIZE) /* insufficient length/size */
return IH_TYPE_INVALID;
+ if (be32toh(hdr->ih_magic) == 0xd00dfeed)
+ return IH_TYPE_FLATDT;
if (be32toh(hdr->ih_magic) != IH_MAGIC) /* signature mismatch */
return IH_TYPE_INVALID;
/* For sunxi, we always expect ARM architecture here */
@@ -864,7 +869,8 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
* address stored within the image header; and the function preserves the
* U-Boot entry point (offset) and size values.
*/
-void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len)
+static void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf,
+ size_t len, const char *dt_name)
{
if (len <= HEADER_SIZE)
return; /* Insufficient size (no actual data), just bail out */
@@ -887,6 +893,13 @@ void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len)
}
exit(1);
}
+ if (image_type == IH_TYPE_FLATDT) { /* FIT image */
+ uboot_entry = load_fit_images(dev, buf, dt_name,
+ &enter_in_aarch64);
+ uboot_size = 4; /* dummy value to pass check below */
+ return;
+ }
+
if (image_type != IH_TYPE_FIRMWARE)
pr_fatal("U-Boot image type mismatch: "
"expected IH_TYPE_FIRMWARE, got %02X\n", image_type);
@@ -923,6 +936,28 @@ void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len)
uboot_size = data_size;
}
+static const char *spl_get_dtb_name(uint8_t *spl_buf)
+{
+ uint32_t dt_offset;
+
+ if (memcmp(spl_buf + 4, "eGON.BT0", 8))
+ return NULL;
+
+ if (memcmp(spl_buf + 0x14, "SPL", 3))
+ return NULL;
+
+ if (spl_buf[0x17] < 0x2) /* only since v0.2 */
+ return NULL;
+
+ memcpy(&dt_offset, spl_buf + 0x20, 4);
+ dt_offset = le32toh(dt_offset);
+
+ if (verbose)
+ printf("found DT name in SPL header: %s\n", spl_buf + dt_offset);
+
+ return (char *)spl_buf + dt_offset;
+}
+
/*
* This function handles the common part of both "spl" and "uboot" commands.
*/
@@ -932,15 +967,18 @@ void aw_fel_process_spl_and_uboot(feldev_handle *dev, const char *filename)
uint32_t offset;
/* load file into memory buffer */
uint8_t *buf = load_file(filename, &size);
+ const char *dt_name = spl_get_dtb_name(buf);
/* write and execute the SPL from the buffer */
offset = aw_fel_write_and_execute_spl(dev, buf, size);
+
/* check for optional main U-Boot binary (and transfer it, if applicable) */
if (size > offset) {
/* U-Boot pads to at least 32KB */
if (offset < SPL_MIN_OFFSET)
offset = SPL_MIN_OFFSET;
- aw_fel_write_uboot_image(dev, buf + offset, size - offset);
+ aw_fel_write_uboot_image(dev, buf + offset, size - offset,
+ dt_name);
}
free(buf);
}
@@ -1405,7 +1443,10 @@ int main(int argc, char **argv)
/* auto-start U-Boot if requested (by the "uboot" command) */
if (uboot_autostart) {
pr_info("Starting U-Boot (0x%08X).\n", uboot_entry);
- aw_fel_execute(handle, uboot_entry);
+ if (enter_in_aarch64)
+ aw_rmr_request(handle, uboot_entry, true);
+ else
+ aw_fel_execute(handle, uboot_entry);
}
feldev_done(handle);
diff --git a/fel_lib.c b/fel_lib.c
index d482ce8..0852cc8 100644
--- a/fel_lib.c
+++ b/fel_lib.c
@@ -214,8 +214,11 @@ void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len)
}
/* AW_FEL_1_WRITE request */
-void aw_fel_write(feldev_handle *dev, void *buf, uint32_t offset, size_t len)
+void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len)
{
+ if (len == 0)
+ return;
+
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
aw_usb_write(dev, buf, len, false);
aw_read_fel_status(dev);
@@ -233,9 +236,12 @@ void aw_fel_execute(feldev_handle *dev, uint32_t offset)
* Unlike aw_fel_write() above - which is reserved for internal use - this
* routine optionally allows progress callbacks.
*/
-void aw_fel_write_buffer(feldev_handle *dev, void *buf, uint32_t offset,
+void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
size_t len, bool progress)
{
+ if (len == 0)
+ return;
+
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
aw_usb_write(dev, buf, len, progress);
aw_read_fel_status(dev);
diff --git a/fel_lib.h b/fel_lib.h
index 6f0a980..ddbc2f3 100644
--- a/fel_lib.h
+++ b/fel_lib.h
@@ -59,8 +59,8 @@ feldev_list_entry *list_fel_devices(size_t *count);
/* FEL functions */
void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len);
-void aw_fel_write(feldev_handle *dev, void *buf, uint32_t offset, size_t len);
-void aw_fel_write_buffer(feldev_handle *dev, void *buf, uint32_t offset,
+void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len);
+void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
size_t len, bool progress);
void aw_fel_execute(feldev_handle *dev, uint32_t offset);
diff --git a/fit_image.c b/fit_image.c
new file mode 100644
index 0000000..899e868
--- /dev/null
+++ b/fit_image.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2018-2020 Andre Przywara <osp@andrep.de>
+ *
+ * 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; under 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 should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <libfdt.h>
+
+#include "common.h"
+#include "fel_lib.h"
+#include "fit_image.h"
+
+/* defined in fel.c */
+extern bool verbose;
+
+#define IH_ARCH_INVALID 0
+#define IH_ARCH_ARM 2
+#define IH_ARCH_ARM64 22
+
+#define IH_OS_INVALID 0
+#define IH_OS_LINUX 5
+#define IH_OS_U_BOOT 17
+#define IH_OS_ARM_TRUSTED_FIRMWARE 25
+#define IH_OS_EFI 28
+
+struct fit_image_info {
+ const char *description;
+ const char *data;
+ uint32_t data_size;
+ uint32_t load_addr;
+ uint32_t entry_point;
+ uint8_t os;
+ uint8_t arch;
+};
+
+static int fit_parse_os(const char *value)
+{
+ if (!value || !*value)
+ return IH_OS_INVALID;
+
+ if (!strcmp(value, "u-boot"))
+ return IH_OS_U_BOOT;
+ if (!strcmp(value, "linux"))
+ return IH_OS_LINUX;
+ if (!strcmp(value, "arm-trusted-firmware"))
+ return IH_OS_ARM_TRUSTED_FIRMWARE;
+ if (!strcmp(value, "efi"))
+ return IH_OS_EFI;
+
+ return IH_OS_INVALID;
+}
+
+static int fit_parse_arch(const char *value)
+{
+ if (!value || !*value)
+ return IH_ARCH_INVALID;
+
+ if (!strcmp(value, "arm"))
+ return IH_ARCH_ARM;
+ if (!strcmp(value, "arm64"))
+ return IH_ARCH_ARM64;
+
+ return IH_ARCH_INVALID;
+}
+
+static uint32_t fdt_getprop_u32(const void *fdt, int node, const char *name)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property(fdt, node, name, NULL);
+ if (!prop)
+ return ~0U;
+
+ return be32toh(*(uint32_t *)prop->data);
+}
+
+static const char *fdt_getprop_str(const void *fdt, int node, const char *name)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property(fdt, node, name, NULL);
+ if (!prop)
+ return NULL;
+
+ return prop->data;
+}
+
+/*
+ * Find the image with the given name under the /images node, and parse
+ * its information into the fit_image_info struct.
+ * Returns 0 on success, and a negative error value otherwise.
+ */
+static int fit_get_image_info(const void *fit, const char *name,
+ struct fit_image_info *info)
+{
+ int node;
+ const char *str;
+ uint32_t data_offset;
+
+ node = fdt_path_offset(fit, "/images");
+ if (node < 0)
+ return -1;
+ node = fdt_subnode_offset(fit, node, name);
+ if (node < 0)
+ return -1;
+
+ info->load_addr = fdt_getprop_u32(fit, node, "load");
+ info->entry_point = fdt_getprop_u32(fit, node, "entry");
+ info->description = fdt_getprop_str(fit, node, "description");
+ /* properties used for FIT images with external data */
+ info->data_size = fdt_getprop_u32(fit, node, "data-size");
+ data_offset = fdt_getprop_u32(fit, node, "data-offset");
+
+ /* check for embedded data (when invalid external data properties) */
+ if (info->data_size == ~0U || data_offset == ~0U) {
+ const struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property(fit, node, "data", &len);
+ info->data_size = len;
+ info->data = prop->data;
+ } else {
+ /* external data is appended at the end of the FIT DTB blob */
+ info->data = (const char *)fit + ((fdt_totalsize(fit) + 3) & ~3U);
+ info->data += data_offset;
+ }
+
+ info->os = fit_parse_os(fdt_getprop_str(fit, node, "os"));
+ info->arch = fit_parse_arch(fdt_getprop_str(fit, node, "arch"));
+
+ str = fdt_getprop_str(fit, node, "compression");
+ /* The current SPL does not support compression either. */
+ if (str && strcmp(str, "none")) {
+ printf("compression \"%s\" not supported for image \"%s\"\n",
+ str, info->description);
+ return -2;
+ }
+
+ return 0;
+}
+
+static int entry_arch;
+static uint32_t dtb_addr;
+
+/*
+ * Upload the image described by its fit_image_info struct to the board.
+ * Detect if an image contains an entry point and return that.
+ * Set entry_arch to arm or arm64 on the way. Also detect the image
+ * containing U-Boot and record its end address, so that the DTB can be
+ * appended later on.
+ * Returns the entry point if any is specified, or 0 otherwise.
+ */
+static uint32_t fit_load_image(feldev_handle *dev, struct fit_image_info *img)
+{
+ uint32_t ret = 0;
+
+ if (verbose)
+ printf("loading image \"%s\" (%d bytes) to 0x%x\n",
+ img->description, img->data_size, img->load_addr);
+ aw_fel_write_buffer(dev, img->data,
+ img->load_addr, img->data_size, true);
+
+ if (img->entry_point != ~0U) {
+ ret = img->entry_point;
+ entry_arch = img->arch;
+ }
+ /* either explicitly marked as U-Boot, or the first invalid one */
+ if (img->os == IH_OS_U_BOOT ||
+ (!dtb_addr && img->os == IH_OS_INVALID))
+ dtb_addr = img->load_addr + img->data_size;
+
+ return ret;
+}
+
+uint32_t load_fit_images(feldev_handle *dev, const void *fit,
+ const char *dt_name, bool *use_aarch64)
+{
+ const struct fdt_property *prop;
+ struct fit_image_info img;
+ const char *str;
+ int node, len;
+ uint32_t entry_point = 0;
+
+ node = fdt_path_offset(fit, "/configurations");
+ if (node < 0) {
+ pr_error("invalid FIT image, no /configurations node\n");
+ return 0;
+ }
+
+ /*
+ * Find the right configuration node, either by using the provided
+ * DT name as an identifier, falling back to the node titled "default",
+ * or by using just the first node.
+ */
+ if (dt_name) {
+ for (node = fdt_first_subnode(fit, node);
+ node >= 0;
+ node = fdt_next_subnode(fit, node)) {
+ prop = fdt_get_property(fit, node, "description", NULL);
+ if (prop && !strcmp(prop->data, dt_name))
+ break;
+ }
+ if (node < 0) {
+ pr_error("no matching FIT configuration node for \"%s\"\n",
+ dt_name);
+ return 0;
+ }
+ } else {
+ prop = fdt_get_property(fit, node, "default", NULL);
+ if (!prop)
+ node = fdt_first_subnode(fit, node);
+ else
+ node = fdt_subnode_offset(fit, node, prop->data);
+ if (node < 0) {
+ pr_error("no default FIT configuration node\n");
+ return 0;
+ }
+ }
+
+ entry_arch = IH_ARCH_INVALID;
+ dtb_addr = 0;
+
+ /* Load the image described as "firmware". */
+ str = fdt_getprop_str(fit, node, "firmware");
+ if (str && !fit_get_image_info(fit, str, &img)) {
+ uint32_t addr = fit_load_image(dev, &img);
+
+ if (addr != 0)
+ entry_point = addr;
+ } else {
+ printf("WARNING: no valid \"firmware\" image entry in FIT\n");
+ }
+
+ /* load all loadables, at their respective load addresses */
+ prop = fdt_get_property(fit, node, "loadables", &len);
+ for (str = prop ? prop->data : NULL;
+ prop && (str - prop->data) < len && *str;
+ str += strlen(str) + 1) {
+ uint32_t addr;
+
+ if (fit_get_image_info(fit, str, &img)) {
+ printf("Can't load loadable \"%s\", skipping.\n", str);
+ continue;
+ }
+ addr = fit_load_image(dev, &img);
+ if (addr != 0)
+ entry_point = addr;
+ }
+
+ if (use_aarch64)
+ *use_aarch64 = (entry_arch == IH_ARCH_ARM64);
+
+ if (!dtb_addr) {
+ printf("Warning: no U-Boot image found, not loading DTB\n");
+ return entry_point;
+ }
+
+ /* load .dtb right after the U-Boot image (appended DTB) */
+ str = fdt_getprop_str(fit, node, "fdt");
+ if (!str || fit_get_image_info(fit, str, &img)) {
+ printf("Warning: no FDT found in FIT image\n");
+ return entry_point;
+ }
+ if (verbose)
+ printf("loading DTB \"%s\" (%d bytes)\n", img.description,
+ img.data_size);
+ aw_fel_write_buffer(dev, img.data, dtb_addr, img.data_size, false);
+
+ return entry_point;
+}
diff --git a/fit_image.h b/fit_image.h
new file mode 100644
index 0000000..30813a3
--- /dev/null
+++ b/fit_image.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018-2020 Andre Przywara <osp@andrep.de>
+ *
+ * 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; under 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 should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __FIT_IMAGE_H__
+#define __FIT_IMAGE_H__
+
+#include <stdint.h>
+#include "fel_lib.h"
+
+/*
+ * Load all images referenced in the given U-Boot FIT image. @dt_name will
+ * be used to select one of the configurations. @use_aarch64 contains the
+ * target architecture of the entry point.
+ * Returns the entry point address of the image to be started.
+ */
+uint32_t load_fit_images(feldev_handle *dev, const void *fit,
+ const char *dt_name, bool *use_aarch64);
+
+#endif