xref: /openbmc/u-boot/tools/atmelimage.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
27b1a4117SAndreas Bießmann /*
37b1a4117SAndreas Bießmann  * (C) Copyright 2014
409c2b8f3SAndreas Bießmann  * Andreas Bießmann <andreas@biessmann.org>
57b1a4117SAndreas Bießmann  */
67b1a4117SAndreas Bießmann 
77b1a4117SAndreas Bießmann #include "imagetool.h"
87b1a4117SAndreas Bießmann #include "mkimage.h"
97b1a4117SAndreas Bießmann 
107b1a4117SAndreas Bießmann #include <image.h>
117b1a4117SAndreas Bießmann 
127b1a4117SAndreas Bießmann #define pr_err(fmt, args...) fprintf(stderr, "atmelimage Error: " fmt, ##args)
137b1a4117SAndreas Bießmann 
atmel_check_image_type(uint8_t type)147b1a4117SAndreas Bießmann static int atmel_check_image_type(uint8_t type)
157b1a4117SAndreas Bießmann {
167b1a4117SAndreas Bießmann 	if (type == IH_TYPE_ATMELIMAGE)
177b1a4117SAndreas Bießmann 		return EXIT_SUCCESS;
187b1a4117SAndreas Bießmann 	else
197b1a4117SAndreas Bießmann 		return EXIT_FAILURE;
207b1a4117SAndreas Bießmann }
217b1a4117SAndreas Bießmann 
227b1a4117SAndreas Bießmann static uint32_t nand_pmecc_header[52];
237b1a4117SAndreas Bießmann 
247b1a4117SAndreas Bießmann /*
257b1a4117SAndreas Bießmann  * A helper struct for parsing the mkimage -n parameter
267b1a4117SAndreas Bießmann  *
277b1a4117SAndreas Bießmann  * Keep in same order as the configs array!
287b1a4117SAndreas Bießmann  */
297b1a4117SAndreas Bießmann static struct pmecc_config {
307b1a4117SAndreas Bießmann 	int use_pmecc;
317b1a4117SAndreas Bießmann 	int sector_per_page;
327b1a4117SAndreas Bießmann 	int spare_size;
337b1a4117SAndreas Bießmann 	int ecc_bits;
347b1a4117SAndreas Bießmann 	int sector_size;
357b1a4117SAndreas Bießmann 	int ecc_offset;
367b1a4117SAndreas Bießmann } pmecc;
377b1a4117SAndreas Bießmann 
387b1a4117SAndreas Bießmann /*
397b1a4117SAndreas Bießmann  * Strings used for configure the PMECC header via -n mkimage switch
407b1a4117SAndreas Bießmann  *
417b1a4117SAndreas Bießmann  * We estimate a coma separated list of key=value pairs. The mkimage -n
427b1a4117SAndreas Bießmann  * parameter argument should not contain any whitespace.
437b1a4117SAndreas Bießmann  *
447b1a4117SAndreas Bießmann  * Keep in same order as struct pmecc_config!
457b1a4117SAndreas Bießmann  */
467b1a4117SAndreas Bießmann static const char * const configs[] = {
477b1a4117SAndreas Bießmann 	"usePmecc",
487b1a4117SAndreas Bießmann 	"sectorPerPage",
497b1a4117SAndreas Bießmann 	"spareSize",
507b1a4117SAndreas Bießmann 	"eccBits",
517b1a4117SAndreas Bießmann 	"sectorSize",
527b1a4117SAndreas Bießmann 	"eccOffset"
537b1a4117SAndreas Bießmann };
547b1a4117SAndreas Bießmann 
atmel_find_pmecc_parameter_in_token(const char * token)557b1a4117SAndreas Bießmann static int atmel_find_pmecc_parameter_in_token(const char *token)
567b1a4117SAndreas Bießmann {
577b1a4117SAndreas Bießmann 	size_t pos;
587b1a4117SAndreas Bießmann 	char *param;
597b1a4117SAndreas Bießmann 
607b1a4117SAndreas Bießmann 	debug("token: '%s'\n", token);
617b1a4117SAndreas Bießmann 
627b1a4117SAndreas Bießmann 	for (pos = 0; pos < ARRAY_SIZE(configs); pos++) {
637b1a4117SAndreas Bießmann 		if (strncmp(token, configs[pos], strlen(configs[pos])) == 0) {
647b1a4117SAndreas Bießmann 			param = strstr(token, "=");
657b1a4117SAndreas Bießmann 			if (!param)
667b1a4117SAndreas Bießmann 				goto err;
677b1a4117SAndreas Bießmann 
687b1a4117SAndreas Bießmann 			param++;
697b1a4117SAndreas Bießmann 			debug("\t%s parameter: '%s'\n", configs[pos], param);
707b1a4117SAndreas Bießmann 
717b1a4117SAndreas Bießmann 			switch (pos) {
727b1a4117SAndreas Bießmann 			case 0:
737b1a4117SAndreas Bießmann 				pmecc.use_pmecc = strtol(param, NULL, 10);
747b1a4117SAndreas Bießmann 				return EXIT_SUCCESS;
757b1a4117SAndreas Bießmann 			case 1:
767b1a4117SAndreas Bießmann 				pmecc.sector_per_page = strtol(param, NULL, 10);
777b1a4117SAndreas Bießmann 				return EXIT_SUCCESS;
787b1a4117SAndreas Bießmann 			case 2:
797b1a4117SAndreas Bießmann 				pmecc.spare_size = strtol(param, NULL, 10);
807b1a4117SAndreas Bießmann 				return EXIT_SUCCESS;
817b1a4117SAndreas Bießmann 			case 3:
827b1a4117SAndreas Bießmann 				pmecc.ecc_bits = strtol(param, NULL, 10);
837b1a4117SAndreas Bießmann 				return EXIT_SUCCESS;
847b1a4117SAndreas Bießmann 			case 4:
857b1a4117SAndreas Bießmann 				pmecc.sector_size = strtol(param, NULL, 10);
867b1a4117SAndreas Bießmann 				return EXIT_SUCCESS;
877b1a4117SAndreas Bießmann 			case 5:
887b1a4117SAndreas Bießmann 				pmecc.ecc_offset = strtol(param, NULL, 10);
897b1a4117SAndreas Bießmann 				return EXIT_SUCCESS;
907b1a4117SAndreas Bießmann 			}
917b1a4117SAndreas Bießmann 		}
927b1a4117SAndreas Bießmann 	}
937b1a4117SAndreas Bießmann 
947b1a4117SAndreas Bießmann err:
957b1a4117SAndreas Bießmann 	pr_err("Could not find parameter in token '%s'\n", token);
967b1a4117SAndreas Bießmann 	return EXIT_FAILURE;
977b1a4117SAndreas Bießmann }
987b1a4117SAndreas Bießmann 
atmel_parse_pmecc_params(char * txt)997b1a4117SAndreas Bießmann static int atmel_parse_pmecc_params(char *txt)
1007b1a4117SAndreas Bießmann {
1017b1a4117SAndreas Bießmann 	char *token;
1027b1a4117SAndreas Bießmann 
1037b1a4117SAndreas Bießmann 	token = strtok(txt, ",");
1047b1a4117SAndreas Bießmann 	while (token != NULL) {
1057b1a4117SAndreas Bießmann 		if (atmel_find_pmecc_parameter_in_token(token))
1067b1a4117SAndreas Bießmann 			return EXIT_FAILURE;
1077b1a4117SAndreas Bießmann 
1087b1a4117SAndreas Bießmann 		token = strtok(NULL, ",");
1097b1a4117SAndreas Bießmann 	}
1107b1a4117SAndreas Bießmann 
1117b1a4117SAndreas Bießmann 	return EXIT_SUCCESS;
1127b1a4117SAndreas Bießmann }
1137b1a4117SAndreas Bießmann 
atmel_verify_header(unsigned char * ptr,int image_size,struct image_tool_params * params)1147b1a4117SAndreas Bießmann static int atmel_verify_header(unsigned char *ptr, int image_size,
1157b1a4117SAndreas Bießmann 			struct image_tool_params *params)
1167b1a4117SAndreas Bießmann {
1177b1a4117SAndreas Bießmann 	uint32_t *ints = (uint32_t *)ptr;
1187b1a4117SAndreas Bießmann 	size_t pos;
1197b1a4117SAndreas Bießmann 	size_t size = image_size;
1207b1a4117SAndreas Bießmann 
1217b1a4117SAndreas Bießmann 	/* check if we have an PMECC header attached */
1227b1a4117SAndreas Bießmann 	for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
1237b1a4117SAndreas Bießmann 		if (ints[pos] >> 28 != 0xC)
1247b1a4117SAndreas Bießmann 			break;
1257b1a4117SAndreas Bießmann 
1267b1a4117SAndreas Bießmann 	if (pos == ARRAY_SIZE(nand_pmecc_header)) {
1277b1a4117SAndreas Bießmann 		ints += ARRAY_SIZE(nand_pmecc_header);
1287b1a4117SAndreas Bießmann 		size -= sizeof(nand_pmecc_header);
1297b1a4117SAndreas Bießmann 	}
1307b1a4117SAndreas Bießmann 
1317b1a4117SAndreas Bießmann 	/* check the seven interrupt vectors of binary */
1327b1a4117SAndreas Bießmann 	for (pos = 0; pos < 7; pos++) {
133a679cc01STom Rini 		debug("atmelimage: interrupt vector #%zu is 0x%08X\n", pos+1,
1347b1a4117SAndreas Bießmann 		      ints[pos]);
1357b1a4117SAndreas Bießmann 		/*
1367b1a4117SAndreas Bießmann 		 * all vectors except the 6'th one must contain valid
1377b1a4117SAndreas Bießmann 		 * LDR or B Opcode
1387b1a4117SAndreas Bießmann 		 */
1397b1a4117SAndreas Bießmann 		if (pos == 5)
1407b1a4117SAndreas Bießmann 			/* 6'th vector has image size set, check later */
1417b1a4117SAndreas Bießmann 			continue;
1427b1a4117SAndreas Bießmann 		if ((ints[pos] & 0xff000000) == 0xea000000)
1437b1a4117SAndreas Bießmann 			/* valid B Opcode */
1447b1a4117SAndreas Bießmann 			continue;
1457b1a4117SAndreas Bießmann 		if ((ints[pos] & 0xfffff000) == 0xe59ff000)
1467b1a4117SAndreas Bießmann 			/* valid LDR (I=0, P=1, U=1, B=0, W=0, L=1) */
1477b1a4117SAndreas Bießmann 			continue;
1487b1a4117SAndreas Bießmann 		/* ouch, one of the checks has missed ... */
1497b1a4117SAndreas Bießmann 		return 1;
1507b1a4117SAndreas Bießmann 	}
1517b1a4117SAndreas Bießmann 
1527b1a4117SAndreas Bießmann 	return ints[5] != cpu_to_le32(size);
1537b1a4117SAndreas Bießmann }
1547b1a4117SAndreas Bießmann 
atmel_print_pmecc_header(const uint32_t word)1557b1a4117SAndreas Bießmann static void atmel_print_pmecc_header(const uint32_t word)
1567b1a4117SAndreas Bießmann {
1577b1a4117SAndreas Bießmann 	int val;
1587b1a4117SAndreas Bießmann 
1597b1a4117SAndreas Bießmann 	printf("\t\tPMECC header\n");
1607b1a4117SAndreas Bießmann 
1617b1a4117SAndreas Bießmann 	printf("\t\t====================\n");
1627b1a4117SAndreas Bießmann 
1637b1a4117SAndreas Bießmann 	val = (word >> 18) & 0x1ff;
1647b1a4117SAndreas Bießmann 	printf("\t\teccOffset: %9i\n", val);
1657b1a4117SAndreas Bießmann 
1667b1a4117SAndreas Bießmann 	val = (((word >> 16) & 0x3) == 0) ? 512 : 1024;
1677b1a4117SAndreas Bießmann 	printf("\t\tsectorSize: %8i\n", val);
1687b1a4117SAndreas Bießmann 
1697b1a4117SAndreas Bießmann 	if (((word >> 13) & 0x7) <= 2)
1707b1a4117SAndreas Bießmann 		val = (2 << ((word >> 13) & 0x7));
1717b1a4117SAndreas Bießmann 	else
1727b1a4117SAndreas Bießmann 		val = (12 << (((word >> 13) & 0x7) - 3));
1737b1a4117SAndreas Bießmann 	printf("\t\teccBitReq: %9i\n", val);
1747b1a4117SAndreas Bießmann 
1757b1a4117SAndreas Bießmann 	val = (word >> 4) & 0x1ff;
1767b1a4117SAndreas Bießmann 	printf("\t\tspareSize: %9i\n", val);
1777b1a4117SAndreas Bießmann 
1787b1a4117SAndreas Bießmann 	val = (1 << ((word >> 1) & 0x3));
1797b1a4117SAndreas Bießmann 	printf("\t\tnbSectorPerPage: %3i\n", val);
1807b1a4117SAndreas Bießmann 
1817b1a4117SAndreas Bießmann 	printf("\t\tusePmecc: %10i\n", word & 0x1);
1827b1a4117SAndreas Bießmann 	printf("\t\t====================\n");
1837b1a4117SAndreas Bießmann }
1847b1a4117SAndreas Bießmann 
atmel_print_header(const void * ptr)1857b1a4117SAndreas Bießmann static void atmel_print_header(const void *ptr)
1867b1a4117SAndreas Bießmann {
1877b1a4117SAndreas Bießmann 	uint32_t *ints = (uint32_t *)ptr;
1887b1a4117SAndreas Bießmann 	size_t pos;
1897b1a4117SAndreas Bießmann 
1907b1a4117SAndreas Bießmann 	/* check if we have an PMECC header attached */
1917b1a4117SAndreas Bießmann 	for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
1927b1a4117SAndreas Bießmann 		if (ints[pos] >> 28 != 0xC)
1937b1a4117SAndreas Bießmann 			break;
1947b1a4117SAndreas Bießmann 
1957b1a4117SAndreas Bießmann 	if (pos == ARRAY_SIZE(nand_pmecc_header)) {
1967b1a4117SAndreas Bießmann 		printf("Image Type:\tATMEL ROM-Boot Image with PMECC Header\n");
1977b1a4117SAndreas Bießmann 		atmel_print_pmecc_header(ints[0]);
1987b1a4117SAndreas Bießmann 		pos += 5;
1997b1a4117SAndreas Bießmann 	} else {
2007b1a4117SAndreas Bießmann 		printf("Image Type:\tATMEL ROM-Boot Image without PMECC Header\n");
2017b1a4117SAndreas Bießmann 		pos = 5;
2027b1a4117SAndreas Bießmann 	}
2037b1a4117SAndreas Bießmann 	printf("\t\t6'th vector has %u set\n", le32_to_cpu(ints[pos]));
2047b1a4117SAndreas Bießmann }
2057b1a4117SAndreas Bießmann 
atmel_set_header(void * ptr,struct stat * sbuf,int ifd,struct image_tool_params * params)2067b1a4117SAndreas Bießmann static void atmel_set_header(void *ptr, struct stat *sbuf, int ifd,
2077b1a4117SAndreas Bießmann 				struct image_tool_params *params)
2087b1a4117SAndreas Bießmann {
2097b1a4117SAndreas Bießmann 	/* just save the image size into 6'th interrupt vector */
2107b1a4117SAndreas Bießmann 	uint32_t *ints = (uint32_t *)ptr;
2117b1a4117SAndreas Bießmann 	size_t cnt;
2127b1a4117SAndreas Bießmann 	size_t pos = 5;
2137b1a4117SAndreas Bießmann 	size_t size = sbuf->st_size;
2147b1a4117SAndreas Bießmann 
2157b1a4117SAndreas Bießmann 	for (cnt = 0; cnt < ARRAY_SIZE(nand_pmecc_header); cnt++)
2167b1a4117SAndreas Bießmann 		if (ints[cnt] >> 28 != 0xC)
2177b1a4117SAndreas Bießmann 			break;
2187b1a4117SAndreas Bießmann 
2197b1a4117SAndreas Bießmann 	if (cnt == ARRAY_SIZE(nand_pmecc_header)) {
2207b1a4117SAndreas Bießmann 		pos += ARRAY_SIZE(nand_pmecc_header);
2217b1a4117SAndreas Bießmann 		size -= sizeof(nand_pmecc_header);
2227b1a4117SAndreas Bießmann 	}
2237b1a4117SAndreas Bießmann 
2247b1a4117SAndreas Bießmann 	ints[pos] = cpu_to_le32(size);
2257b1a4117SAndreas Bießmann }
2267b1a4117SAndreas Bießmann 
atmel_check_params(struct image_tool_params * params)2277b1a4117SAndreas Bießmann static int atmel_check_params(struct image_tool_params *params)
2287b1a4117SAndreas Bießmann {
2297b1a4117SAndreas Bießmann 	if (strlen(params->imagename) > 0)
2307b1a4117SAndreas Bießmann 		if (atmel_parse_pmecc_params(params->imagename))
2317b1a4117SAndreas Bießmann 			return EXIT_FAILURE;
2327b1a4117SAndreas Bießmann 
2337b1a4117SAndreas Bießmann 	return !(!params->eflag &&
2347b1a4117SAndreas Bießmann 		!params->fflag &&
2357b1a4117SAndreas Bießmann 		!params->xflag &&
2367b1a4117SAndreas Bießmann 		((params->dflag && !params->lflag) ||
2377b1a4117SAndreas Bießmann 		 (params->lflag && !params->dflag)));
2387b1a4117SAndreas Bießmann }
2397b1a4117SAndreas Bießmann 
atmel_vrec_header(struct image_tool_params * params,struct image_type_params * tparams)2407b1a4117SAndreas Bießmann static int atmel_vrec_header(struct image_tool_params *params,
2417b1a4117SAndreas Bießmann 				struct image_type_params *tparams)
2427b1a4117SAndreas Bießmann {
2437b1a4117SAndreas Bießmann 	uint32_t tmp;
2447b1a4117SAndreas Bießmann 	size_t pos;
2457b1a4117SAndreas Bießmann 
2467b1a4117SAndreas Bießmann 	if (strlen(params->imagename) == 0)
2477b1a4117SAndreas Bießmann 		return EXIT_SUCCESS;
2487b1a4117SAndreas Bießmann 
2497b1a4117SAndreas Bießmann 	tmp = 0xC << 28;
2507b1a4117SAndreas Bießmann 
2517b1a4117SAndreas Bießmann 	tmp |= (pmecc.ecc_offset & 0x1ff) << 18;
2527b1a4117SAndreas Bießmann 
2537b1a4117SAndreas Bießmann 	switch (pmecc.sector_size) {
2547b1a4117SAndreas Bießmann 	case 512:
2557b1a4117SAndreas Bießmann 		tmp |= 0 << 16;
2567b1a4117SAndreas Bießmann 		break;
2577b1a4117SAndreas Bießmann 	case 1024:
2587b1a4117SAndreas Bießmann 		tmp |= 1 << 16;
2597b1a4117SAndreas Bießmann 		break;
2607b1a4117SAndreas Bießmann 
2617b1a4117SAndreas Bießmann 	default:
2627b1a4117SAndreas Bießmann 		pr_err("Wrong sectorSize (%i) for PMECC header\n",
2637b1a4117SAndreas Bießmann 		       pmecc.sector_size);
2647b1a4117SAndreas Bießmann 		return EXIT_FAILURE;
2657b1a4117SAndreas Bießmann 	}
2667b1a4117SAndreas Bießmann 
2677b1a4117SAndreas Bießmann 	switch (pmecc.ecc_bits) {
2687b1a4117SAndreas Bießmann 	case 2:
2697b1a4117SAndreas Bießmann 		tmp |= 0 << 13;
2707b1a4117SAndreas Bießmann 		break;
2717b1a4117SAndreas Bießmann 	case 4:
2727b1a4117SAndreas Bießmann 		tmp |= 1 << 13;
2737b1a4117SAndreas Bießmann 		break;
2747b1a4117SAndreas Bießmann 	case 8:
2757b1a4117SAndreas Bießmann 		tmp |= 2 << 13;
2767b1a4117SAndreas Bießmann 		break;
2777b1a4117SAndreas Bießmann 	case 12:
2787b1a4117SAndreas Bießmann 		tmp |= 3 << 13;
2797b1a4117SAndreas Bießmann 		break;
2807b1a4117SAndreas Bießmann 	case 24:
2817b1a4117SAndreas Bießmann 		tmp |= 4 << 13;
2827b1a4117SAndreas Bießmann 		break;
2837b1a4117SAndreas Bießmann 
2847b1a4117SAndreas Bießmann 	default:
2857b1a4117SAndreas Bießmann 		pr_err("Wrong eccBits (%i) for PMECC header\n",
2867b1a4117SAndreas Bießmann 		       pmecc.ecc_bits);
2877b1a4117SAndreas Bießmann 		 return EXIT_FAILURE;
2887b1a4117SAndreas Bießmann 	}
2897b1a4117SAndreas Bießmann 
2907b1a4117SAndreas Bießmann 	tmp |= (pmecc.spare_size & 0x1ff) << 4;
2917b1a4117SAndreas Bießmann 
2927b1a4117SAndreas Bießmann 	switch (pmecc.sector_per_page) {
2937b1a4117SAndreas Bießmann 	case 1:
2947b1a4117SAndreas Bießmann 		tmp |= 0 << 1;
2957b1a4117SAndreas Bießmann 		break;
2967b1a4117SAndreas Bießmann 	case 2:
2977b1a4117SAndreas Bießmann 		tmp |= 1 << 1;
2987b1a4117SAndreas Bießmann 		break;
2997b1a4117SAndreas Bießmann 	case 4:
3007b1a4117SAndreas Bießmann 		tmp |= 2 << 1;
3017b1a4117SAndreas Bießmann 		break;
3027b1a4117SAndreas Bießmann 	case 8:
3037b1a4117SAndreas Bießmann 		tmp |= 3 << 1;
3047b1a4117SAndreas Bießmann 		break;
3057b1a4117SAndreas Bießmann 
3067b1a4117SAndreas Bießmann 	default:
3077b1a4117SAndreas Bießmann 		pr_err("Wrong sectorPerPage (%i) for PMECC header\n",
3087b1a4117SAndreas Bießmann 		       pmecc.sector_per_page);
3097b1a4117SAndreas Bießmann 		return EXIT_FAILURE;
3107b1a4117SAndreas Bießmann 	}
3117b1a4117SAndreas Bießmann 
3127b1a4117SAndreas Bießmann 	if (pmecc.use_pmecc)
3137b1a4117SAndreas Bießmann 		tmp |= 1;
3147b1a4117SAndreas Bießmann 
3157b1a4117SAndreas Bießmann 	for (pos = 0; pos < ARRAY_SIZE(nand_pmecc_header); pos++)
3167b1a4117SAndreas Bießmann 		nand_pmecc_header[pos] = tmp;
3177b1a4117SAndreas Bießmann 
3187b1a4117SAndreas Bießmann 	debug("PMECC header filled 52 times with 0x%08X\n", tmp);
3197b1a4117SAndreas Bießmann 
3207b1a4117SAndreas Bießmann 	tparams->header_size = sizeof(nand_pmecc_header);
3217b1a4117SAndreas Bießmann 	tparams->hdr = nand_pmecc_header;
3227b1a4117SAndreas Bießmann 
3237b1a4117SAndreas Bießmann 	return EXIT_SUCCESS;
3247b1a4117SAndreas Bießmann }
3257b1a4117SAndreas Bießmann 
326a93648d1SGuilherme Maciel Ferreira U_BOOT_IMAGE_TYPE(
327a93648d1SGuilherme Maciel Ferreira 	atmelimage,
328a93648d1SGuilherme Maciel Ferreira 	"ATMEL ROM-Boot Image support",
329a93648d1SGuilherme Maciel Ferreira 	0,
330a93648d1SGuilherme Maciel Ferreira 	NULL,
331a93648d1SGuilherme Maciel Ferreira 	atmel_check_params,
332a93648d1SGuilherme Maciel Ferreira 	atmel_verify_header,
333a93648d1SGuilherme Maciel Ferreira 	atmel_print_header,
334a93648d1SGuilherme Maciel Ferreira 	atmel_set_header,
335a93648d1SGuilherme Maciel Ferreira 	NULL,
336a93648d1SGuilherme Maciel Ferreira 	atmel_check_image_type,
337a93648d1SGuilherme Maciel Ferreira 	NULL,
338a93648d1SGuilherme Maciel Ferreira 	atmel_vrec_header
339a93648d1SGuilherme Maciel Ferreira );
340