1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2023 Linus Walleij <linus.walleij@linaro.org> 4 * Support for the "SEAttle iMAge" SEAMA NAND image format 5 */ 6 7#include <common.h> 8#include <command.h> 9#include <nand.h> 10 11/* 12 * All SEAMA data is stored in the flash in "network endianness" 13 * i.e. big endian, which means that it needs to be byte-swapped 14 * on all little endian platforms. 15 * 16 * structure for a SEAMA entity in NAND flash: 17 * 18 * 32 bit SEAMA magic 0x5EA3A417 19 * 16 bit reserved 20 * 16 bit metadata size (following the header) 21 * 32 bit image size 22 * 16 bytes MD5 digest of the image 23 * meta data 24 * ... image data ... 25 * 26 * Then if a new SEAMA magic follows, that is the next image. 27 */ 28 29#define SEAMA_MAGIC 0x5EA3A417 30#define SEAMA_HDR_NO_META_SZ 28 31#define SEAMA_MAX_META_SZ (1024 - SEAMA_HDR_NO_META_SZ) 32 33struct seama_header { 34 u32 magic; 35 u32 meta_size; 36 u32 image_size; 37 u8 md5[16]; 38 u8 metadata[SEAMA_MAX_META_SZ]; 39}; 40 41static struct seama_header shdr; 42 43static int env_set_val(const char *varname, ulong val) 44{ 45 int ret; 46 47 ret = env_set_hex(varname, val); 48 if (ret) 49 printf("Failed to %s env var\n", varname); 50 51 return ret; 52} 53 54static int do_seama_load_image(struct cmd_tbl *cmdtp, int flag, int argc, 55 char *const argv[]) 56{ 57 struct mtd_info *mtd; 58 uintptr_t load_addr; 59 unsigned long image_index; 60 u32 len; 61 size_t readsz; 62 int ret; 63 u32 *start; 64 u32 *offset; 65 u32 *end; 66 u32 tmp; 67 68 if (argc < 2 || argc > 3) 69 return CMD_RET_USAGE; 70 71 load_addr = hextoul(argv[1], NULL); 72 if (!load_addr) { 73 printf("Invalid load address\n"); 74 return CMD_RET_USAGE; 75 } 76 77 /* Can be 0 for first image */ 78 image_index = hextoul(argv[2], NULL); 79 80 /* We only support one NAND, the first one */ 81 nand_curr_device = 0; 82 mtd = get_nand_dev_by_index(0); 83 if (!mtd) { 84 printf("NAND Device 0 not available\n"); 85 return CMD_RET_FAILURE; 86 } 87 88#ifdef CONFIG_SYS_NAND_SELECT_DEVICE 89 board_nand_select_device(mtd_to_nand(mtd), 0); 90#endif 91 92 printf("Loading SEAMA image %lu from %s\n", image_index, mtd->name); 93 94 readsz = sizeof(shdr); 95 offset = 0; 96 ret = nand_read_skip_bad(mtd, 0, &readsz, NULL, mtd->size, 97 (u_char *)&shdr); 98 if (ret) { 99 printf("Read error reading SEAMA header\n"); 100 return CMD_RET_FAILURE; 101 } 102 103 if (shdr.magic != SEAMA_MAGIC) { 104 printf("Invalid SEAMA image magic: 0x%08x\n", shdr.magic); 105 return CMD_RET_FAILURE; 106 } 107 108 /* Only the lower 16 bits are valid */ 109 shdr.meta_size &= 0xFFFF; 110 111 if (env_set_val("seama_image_size", 0)) 112 return CMD_RET_FAILURE; 113 114 printf("SEMA IMAGE:\n"); 115 printf(" metadata size %d\n", shdr.meta_size); 116 printf(" image size %d\n", shdr.image_size); 117 printf(" checksum %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 118 shdr.md5[0], shdr.md5[1], shdr.md5[2], shdr.md5[3], 119 shdr.md5[4], shdr.md5[5], shdr.md5[6], shdr.md5[7], 120 shdr.md5[8], shdr.md5[9], shdr.md5[10], shdr.md5[11], 121 shdr.md5[12], shdr.md5[13], shdr.md5[14], shdr.md5[15]); 122 123 /* TODO: handle metadata if needed */ 124 125 len = shdr.image_size; 126 if (env_set_val("seama_image_size", len)) 127 return CMD_RET_FAILURE; 128 129 /* We need to include the header (read full pages) */ 130 readsz = shdr.image_size + SEAMA_HDR_NO_META_SZ + shdr.meta_size; 131 ret = nand_read_skip_bad(mtd, 0, &readsz, NULL, mtd->size, 132 (u_char *)load_addr); 133 if (ret) { 134 printf("Read error reading SEAMA main image\n"); 135 return CMD_RET_FAILURE; 136 } 137 138 /* We use a temporary variable tmp to avoid to hairy casts */ 139 start = (u32 *)load_addr; 140 tmp = (u32)start; 141 tmp += SEAMA_HDR_NO_META_SZ + shdr.meta_size; 142 offset = (u32 *)tmp; 143 tmp += shdr.image_size; 144 end = (u32 *)tmp; 145 146 printf("Decoding SEAMA image 0x%08x..0x%08x to 0x%08x\n", 147 (u32)offset, (u32)end, (u32)start); 148 for (; start < end; start++, offset++) 149 *start = be32_to_cpu(*offset); 150 151 return CMD_RET_SUCCESS; 152} 153 154U_BOOT_CMD 155 (seama, 3, 1, do_seama_load_image, 156 "Load the SEAMA image and sets envs", 157 "seama <addr> <imageindex>\n" 158); 159