| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2023 Linus Walleij <linus.walleij@linaro.org> |
| * Support for the "SEAttle iMAge" SEAMA NAND image format |
| */ |
| |
| #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" |
| ); |