summaryrefslogtreecommitdiff
path: root/cmd/seama.c
blob: 3c8e819923400c93d3957e99d23ab9e1862fcb84 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// 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"
);