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