159 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C
		
	
	
	
// 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"
 | 
						|
);
 |