|
@@ -0,0 +1,158 @@
|
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
|
+/*
|
|
|
+ * Copyright 2023 Linus Walleij <linus.walleij@linaro.org>
|
|
|
+ * Support for the "SEAttle iMAge" SEAMA NAND image format
|
|
|
+ */
|
|
|
+
|
|
|
+#include <common.h>
|
|
|
+#include <command.h>
|
|
|
+#include <nand.h>
|
|
|
+
|
|
|
+/*
|
|
|
+ * All SEAMA data is stored in the flash in "network endianness"
|
|
|
+ * i.e. big endian, which means that it needs to be byte-swapped
|
|
|
+ * on all little endian platforms.
|
|
|
+ *
|
|
|
+ * structure for a SEAMA entity in NAND flash:
|
|
|
+ *
|
|
|
+ * 32 bit SEAMA magic 0x5EA3A417
|
|
|
+ * 16 bit reserved
|
|
|
+ * 16 bit metadata size (following the header)
|
|
|
+ * 32 bit image size
|
|
|
+ * 16 bytes MD5 digest of the image
|
|
|
+ * meta data
|
|
|
+ * ... image data ...
|
|
|
+ *
|
|
|
+ * Then if a new SEAMA magic follows, that is the next image.
|
|
|
+ */
|
|
|
+
|
|
|
+#define SEAMA_MAGIC 0x5EA3A417
|
|
|
+#define SEAMA_HDR_NO_META_SZ 28
|
|
|
+#define SEAMA_MAX_META_SZ (1024 - SEAMA_HDR_NO_META_SZ)
|
|
|
+
|
|
|
+struct seama_header {
|
|
|
+ u32 magic;
|
|
|
+ u32 meta_size;
|
|
|
+ u32 image_size;
|
|
|
+ u8 md5[16];
|
|
|
+ u8 metadata[SEAMA_MAX_META_SZ];
|
|
|
+};
|
|
|
+
|
|
|
+static struct seama_header shdr;
|
|
|
+
|
|
|
+static int env_set_val(const char *varname, ulong val)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = env_set_hex(varname, val);
|
|
|
+ if (ret)
|
|
|
+ printf("Failed to %s env var\n", varname);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int do_seama_load_image(struct cmd_tbl *cmdtp, int flag, int argc,
|
|
|
+ char *const argv[])
|
|
|
+{
|
|
|
+ struct mtd_info *mtd;
|
|
|
+ uintptr_t load_addr;
|
|
|
+ unsigned long image_index;
|
|
|
+ u32 len;
|
|
|
+ size_t readsz;
|
|
|
+ int ret;
|
|
|
+ u32 *start;
|
|
|
+ u32 *offset;
|
|
|
+ u32 *end;
|
|
|
+ u32 tmp;
|
|
|
+
|
|
|
+ if (argc < 2 || argc > 3)
|
|
|
+ return CMD_RET_USAGE;
|
|
|
+
|
|
|
+ load_addr = hextoul(argv[1], NULL);
|
|
|
+ if (!load_addr) {
|
|
|
+ printf("Invalid load address\n");
|
|
|
+ return CMD_RET_USAGE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Can be 0 for first image */
|
|
|
+ image_index = hextoul(argv[2], NULL);
|
|
|
+
|
|
|
+ /* We only support one NAND, the first one */
|
|
|
+ nand_curr_device = 0;
|
|
|
+ mtd = get_nand_dev_by_index(0);
|
|
|
+ if (!mtd) {
|
|
|
+ printf("NAND Device 0 not available\n");
|
|
|
+ return CMD_RET_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
|
|
|
+ board_nand_select_device(mtd_to_nand(mtd), 0);
|
|
|
+#endif
|
|
|
+
|
|
|
+ printf("Loading SEAMA image %lu from %s\n", image_index, mtd->name);
|
|
|
+
|
|
|
+ readsz = sizeof(shdr);
|
|
|
+ offset = 0;
|
|
|
+ ret = nand_read_skip_bad(mtd, 0, &readsz, NULL, mtd->size,
|
|
|
+ (u_char *)&shdr);
|
|
|
+ if (ret) {
|
|
|
+ printf("Read error reading SEAMA header\n");
|
|
|
+ return CMD_RET_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (shdr.magic != SEAMA_MAGIC) {
|
|
|
+ printf("Invalid SEAMA image magic: 0x%08x\n", shdr.magic);
|
|
|
+ return CMD_RET_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Only the lower 16 bits are valid */
|
|
|
+ shdr.meta_size &= 0xFFFF;
|
|
|
+
|
|
|
+ if (env_set_val("seama_image_size", 0))
|
|
|
+ return CMD_RET_FAILURE;
|
|
|
+
|
|
|
+ printf("SEMA IMAGE:\n");
|
|
|
+ printf(" metadata size %d\n", shdr.meta_size);
|
|
|
+ printf(" image size %d\n", shdr.image_size);
|
|
|
+ printf(" checksum %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
|
|
+ shdr.md5[0], shdr.md5[1], shdr.md5[2], shdr.md5[3],
|
|
|
+ shdr.md5[4], shdr.md5[5], shdr.md5[6], shdr.md5[7],
|
|
|
+ shdr.md5[8], shdr.md5[9], shdr.md5[10], shdr.md5[11],
|
|
|
+ shdr.md5[12], shdr.md5[13], shdr.md5[14], shdr.md5[15]);
|
|
|
+
|
|
|
+ /* TODO: handle metadata if needed */
|
|
|
+
|
|
|
+ len = shdr.image_size;
|
|
|
+ if (env_set_val("seama_image_size", len))
|
|
|
+ return CMD_RET_FAILURE;
|
|
|
+
|
|
|
+ /* We need to include the header (read full pages) */
|
|
|
+ readsz = shdr.image_size + SEAMA_HDR_NO_META_SZ + shdr.meta_size;
|
|
|
+ ret = nand_read_skip_bad(mtd, 0, &readsz, NULL, mtd->size,
|
|
|
+ (u_char *)load_addr);
|
|
|
+ if (ret) {
|
|
|
+ printf("Read error reading SEAMA main image\n");
|
|
|
+ return CMD_RET_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* We use a temporary variable tmp to avoid to hairy casts */
|
|
|
+ start = (u32 *)load_addr;
|
|
|
+ tmp = (u32)start;
|
|
|
+ tmp += SEAMA_HDR_NO_META_SZ + shdr.meta_size;
|
|
|
+ offset = (u32 *)tmp;
|
|
|
+ tmp += shdr.image_size;
|
|
|
+ end = (u32 *)tmp;
|
|
|
+
|
|
|
+ printf("Decoding SEAMA image 0x%08x..0x%08x to 0x%08x\n",
|
|
|
+ (u32)offset, (u32)end, (u32)start);
|
|
|
+ for (; start < end; start++, offset++)
|
|
|
+ *start = be32_to_cpu(*offset);
|
|
|
+
|
|
|
+ return CMD_RET_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+U_BOOT_CMD
|
|
|
+ (seama, 3, 1, do_seama_load_image,
|
|
|
+ "Load the SEAMA image and sets envs",
|
|
|
+ "seama <addr> <imageindex>\n"
|
|
|
+);
|