xref: /openbmc/u-boot/tools/atmelimage.c (revision 344c8376)
1 /*
2  * (C) Copyright 2014
3  * Andreas Bießmann <andreas.devel@googlemail.com>
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include "imagetool.h"
9 #include "mkimage.h"
10 
11 #include <image.h>
12 
13 #define pr_err(fmt, args...) fprintf(stderr, "atmelimage Error: " fmt, ##args)
14 
15 static int atmel_check_image_type(uint8_t type)
16 {
17 	if (type == IH_TYPE_ATMELIMAGE)
18 		return EXIT_SUCCESS;
19 	else
20 		return EXIT_FAILURE;
21 }
22 
23 static uint32_t nand_pmecc_header[52];
24 
25 /*
26  * A helper struct for parsing the mkimage -n parameter
27  *
28  * Keep in same order as the configs array!
29  */
30 static struct pmecc_config {
31 	int use_pmecc;
32 	int sector_per_page;
33 	int spare_size;
34 	int ecc_bits;
35 	int sector_size;
36 	int ecc_offset;
37 } pmecc;
38 
39 /*
40  * Strings used for configure the PMECC header via -n mkimage switch
41  *
42  * We estimate a coma separated list of key=value pairs. The mkimage -n
43  * parameter argument should not contain any whitespace.
44  *
45  * Keep in same order as struct pmecc_config!
46  */
47 static const char * const configs[] = {
48 	"usePmecc",
49 	"sectorPerPage",
50 	"spareSize",
51 	"eccBits",
52 	"sectorSize",
53 	"eccOffset"
54 };
55 
56 static int atmel_find_pmecc_parameter_in_token(const char *token)
57 {
58 	size_t pos;
59 	char *param;
60 
61 	debug("token: '%s'\n", token);
62 
63 	for (pos = 0; pos < ARRAY_SIZE(configs); pos++) {
64 		if (strncmp(token, configs[pos], strlen(configs[pos])) == 0) {
65 			param = strstr(token, "=");
66 			if (!param)
67 				goto err;
68 
69 			param++;
70 			debug("\t%s parameter: '%s'\n", configs[pos], param);
71 
72 			switch (pos) {
73 			case 0:
74 				pmecc.use_pmecc = strtol(param, NULL, 10);
75 				return EXIT_SUCCESS;
76 			case 1:
77 				pmecc.sector_per_page = strtol(param, NULL, 10);
78 				return EXIT_SUCCESS;
79 			case 2:
80 				pmecc.spare_size = strtol(param, NULL, 10);
81 				return EXIT_SUCCESS;
82 			case 3:
83 				pmecc.ecc_bits = strtol(param, NULL, 10);
84 				return EXIT_SUCCESS;
85 			case 4:
86 				pmecc.sector_size = strtol(param, NULL, 10);
87 				return EXIT_SUCCESS;
88 			case 5:
89 				pmecc.ecc_offset = strtol(param, NULL, 10);
90 				return EXIT_SUCCESS;
91 			}
92 		}
93 	}
94 
95 err:
96 	pr_err("Could not find parameter in token '%s'\n", token);
97 	return EXIT_FAILURE;
98 }
99 
100 static int atmel_parse_pmecc_params(char *txt)
101 {
102 	char *token;
103 
104 	token = strtok(txt, ",");
105 	while (token != NULL) {
106 		if (atmel_find_pmecc_parameter_in_token(token))
107 			return EXIT_FAILURE;
108 
109 		token = strtok(NULL, ",");
110 	}
111 
112 	return EXIT_SUCCESS;
113 }
114 
115 static int atmel_verify_header(unsigned char *ptr, int image_size,
116 			struct image_tool_params *params)
117 {
118 	uint32_t *ints = (uint32_t *)ptr;
119 	size_t pos;
120 	size_t size = image_size;
121 
122 	/* check if we have an PMECC header attached */
123 	for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
124 		if (ints[pos] >> 28 != 0xC)
125 			break;
126 
127 	if (pos == ARRAY_SIZE(nand_pmecc_header)) {
128 		ints += ARRAY_SIZE(nand_pmecc_header);
129 		size -= sizeof(nand_pmecc_header);
130 	}
131 
132 	/* check the seven interrupt vectors of binary */
133 	for (pos = 0; pos < 7; pos++) {
134 		debug("atmelimage: interrupt vector #%zu is 0x%08X\n", pos+1,
135 		      ints[pos]);
136 		/*
137 		 * all vectors except the 6'th one must contain valid
138 		 * LDR or B Opcode
139 		 */
140 		if (pos == 5)
141 			/* 6'th vector has image size set, check later */
142 			continue;
143 		if ((ints[pos] & 0xff000000) == 0xea000000)
144 			/* valid B Opcode */
145 			continue;
146 		if ((ints[pos] & 0xfffff000) == 0xe59ff000)
147 			/* valid LDR (I=0, P=1, U=1, B=0, W=0, L=1) */
148 			continue;
149 		/* ouch, one of the checks has missed ... */
150 		return 1;
151 	}
152 
153 	return ints[5] != cpu_to_le32(size);
154 }
155 
156 static void atmel_print_pmecc_header(const uint32_t word)
157 {
158 	int val;
159 
160 	printf("\t\tPMECC header\n");
161 
162 	printf("\t\t====================\n");
163 
164 	val = (word >> 18) & 0x1ff;
165 	printf("\t\teccOffset: %9i\n", val);
166 
167 	val = (((word >> 16) & 0x3) == 0) ? 512 : 1024;
168 	printf("\t\tsectorSize: %8i\n", val);
169 
170 	if (((word >> 13) & 0x7) <= 2)
171 		val = (2 << ((word >> 13) & 0x7));
172 	else
173 		val = (12 << (((word >> 13) & 0x7) - 3));
174 	printf("\t\teccBitReq: %9i\n", val);
175 
176 	val = (word >> 4) & 0x1ff;
177 	printf("\t\tspareSize: %9i\n", val);
178 
179 	val = (1 << ((word >> 1) & 0x3));
180 	printf("\t\tnbSectorPerPage: %3i\n", val);
181 
182 	printf("\t\tusePmecc: %10i\n", word & 0x1);
183 	printf("\t\t====================\n");
184 }
185 
186 static void atmel_print_header(const void *ptr)
187 {
188 	uint32_t *ints = (uint32_t *)ptr;
189 	size_t pos;
190 
191 	/* check if we have an PMECC header attached */
192 	for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
193 		if (ints[pos] >> 28 != 0xC)
194 			break;
195 
196 	if (pos == ARRAY_SIZE(nand_pmecc_header)) {
197 		printf("Image Type:\tATMEL ROM-Boot Image with PMECC Header\n");
198 		atmel_print_pmecc_header(ints[0]);
199 		pos += 5;
200 	} else {
201 		printf("Image Type:\tATMEL ROM-Boot Image without PMECC Header\n");
202 		pos = 5;
203 	}
204 	printf("\t\t6'th vector has %u set\n", le32_to_cpu(ints[pos]));
205 }
206 
207 static void atmel_set_header(void *ptr, struct stat *sbuf, int ifd,
208 				struct image_tool_params *params)
209 {
210 	/* just save the image size into 6'th interrupt vector */
211 	uint32_t *ints = (uint32_t *)ptr;
212 	size_t cnt;
213 	size_t pos = 5;
214 	size_t size = sbuf->st_size;
215 
216 	for (cnt = 0; cnt < ARRAY_SIZE(nand_pmecc_header); cnt++)
217 		if (ints[cnt] >> 28 != 0xC)
218 			break;
219 
220 	if (cnt == ARRAY_SIZE(nand_pmecc_header)) {
221 		pos += ARRAY_SIZE(nand_pmecc_header);
222 		size -= sizeof(nand_pmecc_header);
223 	}
224 
225 	ints[pos] = cpu_to_le32(size);
226 }
227 
228 static int atmel_check_params(struct image_tool_params *params)
229 {
230 	if (strlen(params->imagename) > 0)
231 		if (atmel_parse_pmecc_params(params->imagename))
232 			return EXIT_FAILURE;
233 
234 	return !(!params->eflag &&
235 		!params->fflag &&
236 		!params->xflag &&
237 		((params->dflag && !params->lflag) ||
238 		 (params->lflag && !params->dflag)));
239 }
240 
241 static int atmel_vrec_header(struct image_tool_params *params,
242 				struct image_type_params *tparams)
243 {
244 	uint32_t tmp;
245 	size_t pos;
246 
247 	if (strlen(params->imagename) == 0)
248 		return EXIT_SUCCESS;
249 
250 	tmp = 0xC << 28;
251 
252 	tmp |= (pmecc.ecc_offset & 0x1ff) << 18;
253 
254 	switch (pmecc.sector_size) {
255 	case 512:
256 		tmp |= 0 << 16;
257 		break;
258 	case 1024:
259 		tmp |= 1 << 16;
260 		break;
261 
262 	default:
263 		pr_err("Wrong sectorSize (%i) for PMECC header\n",
264 		       pmecc.sector_size);
265 		return EXIT_FAILURE;
266 	}
267 
268 	switch (pmecc.ecc_bits) {
269 	case 2:
270 		tmp |= 0 << 13;
271 		break;
272 	case 4:
273 		tmp |= 1 << 13;
274 		break;
275 	case 8:
276 		tmp |= 2 << 13;
277 		break;
278 	case 12:
279 		tmp |= 3 << 13;
280 		break;
281 	case 24:
282 		tmp |= 4 << 13;
283 		break;
284 
285 	default:
286 		pr_err("Wrong eccBits (%i) for PMECC header\n",
287 		       pmecc.ecc_bits);
288 		 return EXIT_FAILURE;
289 	}
290 
291 	tmp |= (pmecc.spare_size & 0x1ff) << 4;
292 
293 	switch (pmecc.sector_per_page) {
294 	case 1:
295 		tmp |= 0 << 1;
296 		break;
297 	case 2:
298 		tmp |= 1 << 1;
299 		break;
300 	case 4:
301 		tmp |= 2 << 1;
302 		break;
303 	case 8:
304 		tmp |= 3 << 1;
305 		break;
306 
307 	default:
308 		pr_err("Wrong sectorPerPage (%i) for PMECC header\n",
309 		       pmecc.sector_per_page);
310 		return EXIT_FAILURE;
311 	}
312 
313 	if (pmecc.use_pmecc)
314 		tmp |= 1;
315 
316 	for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
317 		nand_pmecc_header[pos] = tmp;
318 
319 	debug("PMECC header filled 52 times with 0x%08X\n", tmp);
320 
321 	tparams->header_size = sizeof(nand_pmecc_header);
322 	tparams->hdr = nand_pmecc_header;
323 
324 	return EXIT_SUCCESS;
325 }
326 
327 U_BOOT_IMAGE_TYPE(
328 	atmelimage,
329 	"ATMEL ROM-Boot Image support",
330 	0,
331 	NULL,
332 	atmel_check_params,
333 	atmel_verify_header,
334 	atmel_print_header,
335 	atmel_set_header,
336 	NULL,
337 	atmel_check_image_type,
338 	NULL,
339 	atmel_vrec_header
340 );
341