blob: 9b3600d62daba3f1042f74bffb808072e3dd6bfe [file] [log] [blame]
Andreas Bießmann7c75d3e2014-05-19 14:23:39 +02001/*
2 * (C) Copyright 2014
Andreas Bießmann07dafdb2016-05-01 03:46:16 +02003 * Andreas Bießmann <andreas@biessmann.org>
Andreas Bießmann7c75d3e2014-05-19 14:23:39 +02004 *
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
15static 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
23static 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 */
30static 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 */
47static const char * const configs[] = {
48 "usePmecc",
49 "sectorPerPage",
50 "spareSize",
51 "eccBits",
52 "sectorSize",
53 "eccOffset"
54};
55
56static 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
95err:
96 pr_err("Could not find parameter in token '%s'\n", token);
97 return EXIT_FAILURE;
98}
99
100static 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
115static 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++) {
Tom Rini79a6d5a2015-08-26 15:18:30 -0400134 debug("atmelimage: interrupt vector #%zu is 0x%08X\n", pos+1,
Andreas Bießmann7c75d3e2014-05-19 14:23:39 +0200135 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
156static 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
186static 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
207static 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
228static 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
241static 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
Guilherme Maciel Ferreira28be1cf2015-01-15 02:48:07 -0200327U_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);