1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2014 4 * Andreas Bießmann <andreas@biessmann.org> 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++) { 133 debug("atmelimage: interrupt vector #%zu is 0x%08X\n", pos+1, 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 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 ); 340