xref: /openbmc/u-boot/tools/pblimage.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
25d898a00SShaohui Xie /*
394cb17d0SAlison Wang  * Copyright 2012-2014 Freescale Semiconductor, Inc.
45d898a00SShaohui Xie  */
5f86ed6a8SGuilherme Maciel Ferreira #include "imagetool.h"
65d898a00SShaohui Xie #include <image.h>
75d898a00SShaohui Xie #include "pblimage.h"
8abbc67eeSCharles Manning #include "pbl_crc32.h"
95d898a00SShaohui Xie 
1094cb17d0SAlison Wang #define roundup(x, y)		((((x) + ((y) - 1)) / (y)) * (y))
1194cb17d0SAlison Wang #define PBL_ACS_CONT_CMD	0x81000000
1294cb17d0SAlison Wang #define PBL_ADDR_24BIT_MASK	0x00ffffff
1394cb17d0SAlison Wang 
145d898a00SShaohui Xie /*
150d3efd80SChris Packham  * Initialize to an invalid value.
165d898a00SShaohui Xie  */
170d3efd80SChris Packham static uint32_t next_pbl_cmd = 0x82000000;
185d898a00SShaohui Xie /*
195d898a00SShaohui Xie  * need to store all bytes in memory for calculating crc32, then write the
205d898a00SShaohui Xie  * bytes to image file for PBL boot.
215d898a00SShaohui Xie  */
220d3efd80SChris Packham static unsigned char mem_buf[1000000];
235d898a00SShaohui Xie static unsigned char *pmem_buf = mem_buf;
245d898a00SShaohui Xie static int pbl_size;
255d898a00SShaohui Xie static char *fname = "Unknown";
265d898a00SShaohui Xie static int lineno = -1;
275d898a00SShaohui Xie static struct pbl_header pblimage_header;
2894cb17d0SAlison Wang static int uboot_size;
2994cb17d0SAlison Wang static int arch_flag;
3094cb17d0SAlison Wang 
3194cb17d0SAlison Wang static uint32_t pbl_cmd_initaddr;
3294cb17d0SAlison Wang static uint32_t pbi_crc_cmd1;
3394cb17d0SAlison Wang static uint32_t pbi_crc_cmd2;
3494cb17d0SAlison Wang static uint32_t pbl_end_cmd[4];
355d898a00SShaohui Xie 
365d898a00SShaohui Xie static union
375d898a00SShaohui Xie {
385d898a00SShaohui Xie 	char c[4];
395d898a00SShaohui Xie 	unsigned char l;
405d898a00SShaohui Xie } endian_test = { {'l', '?', '?', 'b'} };
415d898a00SShaohui Xie 
425d898a00SShaohui Xie #define ENDIANNESS ((char)endian_test.l)
435d898a00SShaohui Xie 
440d3efd80SChris Packham /*
450d3efd80SChris Packham  * The PBL can load up to 64 bytes at a time, so we split the U-Boot
460d3efd80SChris Packham  * image into 64 byte chunks. PBL needs a command for each piece, of
470d3efd80SChris Packham  * the form "81xxxxxx", where "xxxxxx" is the offset. Calculate the
480d3efd80SChris Packham  * start offset by subtracting the size of the u-boot image from the
490d3efd80SChris Packham  * top of the allowable 24-bit range.
500d3efd80SChris Packham  */
generate_pbl_cmd(void)515d898a00SShaohui Xie static void generate_pbl_cmd(void)
525d898a00SShaohui Xie {
535d898a00SShaohui Xie 	uint32_t val = next_pbl_cmd;
545d898a00SShaohui Xie 	next_pbl_cmd += 0x40;
555d898a00SShaohui Xie 	int i;
565d898a00SShaohui Xie 
575d898a00SShaohui Xie 	for (i = 3; i >= 0; i--) {
585d898a00SShaohui Xie 		*pmem_buf++ = (val >> (i * 8)) & 0xff;
595d898a00SShaohui Xie 		pbl_size++;
605d898a00SShaohui Xie 	}
615d898a00SShaohui Xie }
625d898a00SShaohui Xie 
pbl_fget(size_t size,FILE * stream)635d898a00SShaohui Xie static void pbl_fget(size_t size, FILE *stream)
645d898a00SShaohui Xie {
6594cb17d0SAlison Wang 	unsigned char c = 0xff;
665d898a00SShaohui Xie 	int c_temp;
675d898a00SShaohui Xie 
6894cb17d0SAlison Wang 	while (size) {
6994cb17d0SAlison Wang 		c_temp = fgetc(stream);
7094cb17d0SAlison Wang 		if (c_temp != EOF)
715d898a00SShaohui Xie 			c = (unsigned char)c_temp;
7294cb17d0SAlison Wang 		else if ((c_temp == EOF) && (arch_flag == IH_ARCH_ARM))
7394cb17d0SAlison Wang 			c = 0xff;
745d898a00SShaohui Xie 		*pmem_buf++ = c;
755d898a00SShaohui Xie 		pbl_size++;
765d898a00SShaohui Xie 		size--;
775d898a00SShaohui Xie 	}
785d898a00SShaohui Xie }
795d898a00SShaohui Xie 
805d898a00SShaohui Xie /* load split u-boot with PBI command 81xxxxxx. */
load_uboot(FILE * fp_uboot)815d898a00SShaohui Xie static void load_uboot(FILE *fp_uboot)
825d898a00SShaohui Xie {
8394cb17d0SAlison Wang 	next_pbl_cmd = pbl_cmd_initaddr - uboot_size;
8494cb17d0SAlison Wang 	while (next_pbl_cmd < pbl_cmd_initaddr) {
855d898a00SShaohui Xie 		generate_pbl_cmd();
865d898a00SShaohui Xie 		pbl_fget(64, fp_uboot);
875d898a00SShaohui Xie 	}
885d898a00SShaohui Xie }
895d898a00SShaohui Xie 
check_get_hexval(char * token)905d898a00SShaohui Xie static void check_get_hexval(char *token)
915d898a00SShaohui Xie {
925d898a00SShaohui Xie 	uint32_t hexval;
935d898a00SShaohui Xie 	int i;
945d898a00SShaohui Xie 
955d898a00SShaohui Xie 	if (!sscanf(token, "%x", &hexval)) {
965d898a00SShaohui Xie 		printf("Error:%s[%d] - Invalid hex data(%s)\n", fname,
975d898a00SShaohui Xie 			lineno, token);
985d898a00SShaohui Xie 		exit(EXIT_FAILURE);
995d898a00SShaohui Xie 	}
1005d898a00SShaohui Xie 	for (i = 3; i >= 0; i--) {
1015d898a00SShaohui Xie 		*pmem_buf++ = (hexval >> (i * 8)) & 0xff;
1025d898a00SShaohui Xie 		pbl_size++;
1035d898a00SShaohui Xie 	}
1045d898a00SShaohui Xie }
1055d898a00SShaohui Xie 
pbl_parser(char * name)1065d898a00SShaohui Xie static void pbl_parser(char *name)
1075d898a00SShaohui Xie {
1085d898a00SShaohui Xie 	FILE *fd = NULL;
1095d898a00SShaohui Xie 	char *line = NULL;
1105d898a00SShaohui Xie 	char *token, *saveptr1, *saveptr2;
1115d898a00SShaohui Xie 	size_t len = 0;
1125d898a00SShaohui Xie 
1135d898a00SShaohui Xie 	fname = name;
1145d898a00SShaohui Xie 	fd = fopen(name, "r");
1155d898a00SShaohui Xie 	if (fd == NULL) {
1165d898a00SShaohui Xie 		printf("Error:%s - Can't open\n", fname);
1175d898a00SShaohui Xie 		exit(EXIT_FAILURE);
1185d898a00SShaohui Xie 	}
1195d898a00SShaohui Xie 
1205d898a00SShaohui Xie 	while ((getline(&line, &len, fd)) > 0) {
1215d898a00SShaohui Xie 		lineno++;
1225d898a00SShaohui Xie 		token = strtok_r(line, "\r\n", &saveptr1);
1235d898a00SShaohui Xie 		/* drop all lines with zero tokens (= empty lines) */
1245d898a00SShaohui Xie 		if (token == NULL)
1255d898a00SShaohui Xie 			continue;
1265d898a00SShaohui Xie 		for (line = token;; line = NULL) {
1275d898a00SShaohui Xie 			token = strtok_r(line, " \t", &saveptr2);
1285d898a00SShaohui Xie 			if (token == NULL)
1295d898a00SShaohui Xie 				break;
1305d898a00SShaohui Xie 			/* Drop all text starting with '#' as comments */
1315d898a00SShaohui Xie 			if (token[0] == '#')
1325d898a00SShaohui Xie 				break;
1335d898a00SShaohui Xie 			check_get_hexval(token);
1345d898a00SShaohui Xie 		}
1355d898a00SShaohui Xie 	}
1365d898a00SShaohui Xie 	if (line)
1375d898a00SShaohui Xie 		free(line);
1385d898a00SShaohui Xie 	fclose(fd);
1395d898a00SShaohui Xie }
1405d898a00SShaohui Xie 
reverse_byte(uint32_t val)1415d898a00SShaohui Xie static uint32_t reverse_byte(uint32_t val)
1425d898a00SShaohui Xie {
1435d898a00SShaohui Xie 	uint32_t temp;
1445d898a00SShaohui Xie 	unsigned char *p1;
1455d898a00SShaohui Xie 	int j;
1465d898a00SShaohui Xie 
1475d898a00SShaohui Xie 	temp = val;
1485d898a00SShaohui Xie 	p1 = (unsigned char *)&temp;
1495d898a00SShaohui Xie 	for (j = 3; j >= 0; j--)
1505d898a00SShaohui Xie 		*p1++ = (val >> (j * 8)) & 0xff;
1515d898a00SShaohui Xie 	return temp;
1525d898a00SShaohui Xie }
1535d898a00SShaohui Xie 
1545d898a00SShaohui Xie /* write end command and crc command to memory. */
add_end_cmd(void)1555d898a00SShaohui Xie static void add_end_cmd(void)
1565d898a00SShaohui Xie {
1575d898a00SShaohui Xie 	uint32_t crc32_pbl;
1585d898a00SShaohui Xie 	int i;
1595d898a00SShaohui Xie 	unsigned char *p = (unsigned char *)&pbl_end_cmd;
1605d898a00SShaohui Xie 
1615d898a00SShaohui Xie 	if (ENDIANNESS == 'l') {
1625d898a00SShaohui Xie 		for (i = 0; i < 4; i++)
1635d898a00SShaohui Xie 			pbl_end_cmd[i] = reverse_byte(pbl_end_cmd[i]);
1645d898a00SShaohui Xie 	}
1655d898a00SShaohui Xie 
1665d898a00SShaohui Xie 	for (i = 0; i < 16; i++) {
1675d898a00SShaohui Xie 		*pmem_buf++ = *p++;
1685d898a00SShaohui Xie 		pbl_size++;
1695d898a00SShaohui Xie 	}
1705d898a00SShaohui Xie 
1715d898a00SShaohui Xie 	/* Add PBI CRC command. */
1725d898a00SShaohui Xie 	*pmem_buf++ = 0x08;
17394cb17d0SAlison Wang 	*pmem_buf++ = pbi_crc_cmd1;
17494cb17d0SAlison Wang 	*pmem_buf++ = pbi_crc_cmd2;
1755d898a00SShaohui Xie 	*pmem_buf++ = 0x40;
1765d898a00SShaohui Xie 	pbl_size += 4;
1775d898a00SShaohui Xie 
1785d898a00SShaohui Xie 	/* calculated CRC32 and write it to memory. */
1795d898a00SShaohui Xie 	crc32_pbl = pbl_crc32(0, (const char *)mem_buf, pbl_size);
1805d898a00SShaohui Xie 	*pmem_buf++ = (crc32_pbl >> 24) & 0xff;
1815d898a00SShaohui Xie 	*pmem_buf++ = (crc32_pbl >> 16) & 0xff;
1825d898a00SShaohui Xie 	*pmem_buf++ = (crc32_pbl >> 8) & 0xff;
1835d898a00SShaohui Xie 	*pmem_buf++ = (crc32_pbl) & 0xff;
1845d898a00SShaohui Xie 	pbl_size += 4;
1855d898a00SShaohui Xie }
1865d898a00SShaohui Xie 
pbl_load_uboot(int ifd,struct image_tool_params * params)187f86ed6a8SGuilherme Maciel Ferreira void pbl_load_uboot(int ifd, struct image_tool_params *params)
1885d898a00SShaohui Xie {
1895d898a00SShaohui Xie 	FILE *fp_uboot;
1905d898a00SShaohui Xie 	int size;
1915d898a00SShaohui Xie 
1925d898a00SShaohui Xie 	/* parse the rcw.cfg file. */
1935d898a00SShaohui Xie 	pbl_parser(params->imagename);
1945d898a00SShaohui Xie 
1955d898a00SShaohui Xie 	/* parse the pbi.cfg file. */
19633ed5749Syuan linyu 	if (params->imagename2[0] != '\0')
1975d898a00SShaohui Xie 		pbl_parser(params->imagename2);
1985d898a00SShaohui Xie 
19933ed5749Syuan linyu 	if (params->datafile) {
2005d898a00SShaohui Xie 		fp_uboot = fopen(params->datafile, "r");
2015d898a00SShaohui Xie 		if (fp_uboot == NULL) {
2025d898a00SShaohui Xie 			printf("Error: %s open failed\n", params->datafile);
2035d898a00SShaohui Xie 			exit(EXIT_FAILURE);
2045d898a00SShaohui Xie 		}
2055d898a00SShaohui Xie 
2065d898a00SShaohui Xie 		load_uboot(fp_uboot);
2075d898a00SShaohui Xie 		fclose(fp_uboot);
20833ed5749Syuan linyu 	}
20933ed5749Syuan linyu 	add_end_cmd();
2105d898a00SShaohui Xie 	lseek(ifd, 0, SEEK_SET);
2115d898a00SShaohui Xie 
2125d898a00SShaohui Xie 	size = pbl_size;
2135d898a00SShaohui Xie 	if (write(ifd, (const void *)&mem_buf, size) != size) {
2145d898a00SShaohui Xie 		fprintf(stderr, "Write error on %s: %s\n",
2155d898a00SShaohui Xie 			params->imagefile, strerror(errno));
2165d898a00SShaohui Xie 		exit(EXIT_FAILURE);
2175d898a00SShaohui Xie 	}
2185d898a00SShaohui Xie }
2195d898a00SShaohui Xie 
pblimage_check_image_types(uint8_t type)2205d898a00SShaohui Xie static int pblimage_check_image_types(uint8_t type)
2215d898a00SShaohui Xie {
2225d898a00SShaohui Xie 	if (type == IH_TYPE_PBLIMAGE)
2235d898a00SShaohui Xie 		return EXIT_SUCCESS;
2245d898a00SShaohui Xie 	else
2255d898a00SShaohui Xie 		return EXIT_FAILURE;
2265d898a00SShaohui Xie }
2275d898a00SShaohui Xie 
pblimage_verify_header(unsigned char * ptr,int image_size,struct image_tool_params * params)2285d898a00SShaohui Xie static int pblimage_verify_header(unsigned char *ptr, int image_size,
229f86ed6a8SGuilherme Maciel Ferreira 			struct image_tool_params *params)
2305d898a00SShaohui Xie {
2315d898a00SShaohui Xie 	struct pbl_header *pbl_hdr = (struct pbl_header *) ptr;
2325d898a00SShaohui Xie 
2335d898a00SShaohui Xie 	/* Only a few checks can be done: search for magic numbers */
2345d898a00SShaohui Xie 	if (ENDIANNESS == 'l') {
2355d898a00SShaohui Xie 		if (pbl_hdr->preamble != reverse_byte(RCW_PREAMBLE))
2365d898a00SShaohui Xie 			return -FDT_ERR_BADSTRUCTURE;
2375d898a00SShaohui Xie 
2385d898a00SShaohui Xie 		if (pbl_hdr->rcwheader != reverse_byte(RCW_HEADER))
2395d898a00SShaohui Xie 			return -FDT_ERR_BADSTRUCTURE;
2405d898a00SShaohui Xie 	} else {
2415d898a00SShaohui Xie 		if (pbl_hdr->preamble != RCW_PREAMBLE)
2425d898a00SShaohui Xie 			return -FDT_ERR_BADSTRUCTURE;
2435d898a00SShaohui Xie 
2445d898a00SShaohui Xie 		if (pbl_hdr->rcwheader != RCW_HEADER)
2455d898a00SShaohui Xie 			return -FDT_ERR_BADSTRUCTURE;
2465d898a00SShaohui Xie 	}
2475d898a00SShaohui Xie 	return 0;
2485d898a00SShaohui Xie }
2495d898a00SShaohui Xie 
pblimage_print_header(const void * ptr)2505d898a00SShaohui Xie static void pblimage_print_header(const void *ptr)
2515d898a00SShaohui Xie {
2525d898a00SShaohui Xie 	printf("Image Type:   Freescale PBL Boot Image\n");
2535d898a00SShaohui Xie }
2545d898a00SShaohui Xie 
pblimage_set_header(void * ptr,struct stat * sbuf,int ifd,struct image_tool_params * params)2555d898a00SShaohui Xie static void pblimage_set_header(void *ptr, struct stat *sbuf, int ifd,
256f86ed6a8SGuilherme Maciel Ferreira 				struct image_tool_params *params)
2575d898a00SShaohui Xie {
2585d898a00SShaohui Xie 	/*nothing need to do, pbl_load_uboot takes care of whole file. */
2595d898a00SShaohui Xie }
2605d898a00SShaohui Xie 
pblimage_check_params(struct image_tool_params * params)26194cb17d0SAlison Wang int pblimage_check_params(struct image_tool_params *params)
26294cb17d0SAlison Wang {
26394cb17d0SAlison Wang 	FILE *fp_uboot;
26494cb17d0SAlison Wang 	int fd;
26594cb17d0SAlison Wang 	struct stat st;
26694cb17d0SAlison Wang 
26794cb17d0SAlison Wang 	if (!params)
26894cb17d0SAlison Wang 		return EXIT_FAILURE;
26994cb17d0SAlison Wang 
27033ed5749Syuan linyu 	if (params->datafile) {
27194cb17d0SAlison Wang 		fp_uboot = fopen(params->datafile, "r");
27294cb17d0SAlison Wang 		if (fp_uboot == NULL) {
27394cb17d0SAlison Wang 			printf("Error: %s open failed\n", params->datafile);
27494cb17d0SAlison Wang 			exit(EXIT_FAILURE);
27594cb17d0SAlison Wang 		}
27694cb17d0SAlison Wang 		fd = fileno(fp_uboot);
27794cb17d0SAlison Wang 
27894cb17d0SAlison Wang 		if (fstat(fd, &st) == -1) {
27994cb17d0SAlison Wang 			printf("Error: Could not determine u-boot image size. %s\n",
28094cb17d0SAlison Wang 			       strerror(errno));
28194cb17d0SAlison Wang 			exit(EXIT_FAILURE);
28294cb17d0SAlison Wang 		}
28394cb17d0SAlison Wang 
28433ed5749Syuan linyu 		/* For the variable size, pad it to 64 byte boundary */
28594cb17d0SAlison Wang 		uboot_size = roundup(st.st_size, 64);
28633ed5749Syuan linyu 		fclose(fp_uboot);
28733ed5749Syuan linyu 	}
28894cb17d0SAlison Wang 
28994cb17d0SAlison Wang 	if (params->arch == IH_ARCH_ARM) {
29094cb17d0SAlison Wang 		arch_flag = IH_ARCH_ARM;
29194cb17d0SAlison Wang 		pbi_crc_cmd1 = 0x61;
29294cb17d0SAlison Wang 		pbi_crc_cmd2 = 0;
29394cb17d0SAlison Wang 		pbl_cmd_initaddr = params->addr & PBL_ADDR_24BIT_MASK;
29494cb17d0SAlison Wang 		pbl_cmd_initaddr |= PBL_ACS_CONT_CMD;
29505e35d26SYork Sun 		pbl_cmd_initaddr += uboot_size;
29694cb17d0SAlison Wang 		pbl_end_cmd[0] = 0x09610000;
29794cb17d0SAlison Wang 		pbl_end_cmd[1] = 0x00000000;
29894cb17d0SAlison Wang 		pbl_end_cmd[2] = 0x096100c0;
29994cb17d0SAlison Wang 		pbl_end_cmd[3] = 0x00000000;
30094cb17d0SAlison Wang 	} else if (params->arch == IH_ARCH_PPC) {
30194cb17d0SAlison Wang 		arch_flag = IH_ARCH_PPC;
30294cb17d0SAlison Wang 		pbi_crc_cmd1 = 0x13;
30394cb17d0SAlison Wang 		pbi_crc_cmd2 = 0x80;
30494cb17d0SAlison Wang 		pbl_cmd_initaddr = 0x82000000;
305c5938c10SZhao Qiang 		pbl_end_cmd[0] = 0x091380c0;
30694cb17d0SAlison Wang 		pbl_end_cmd[1] = 0x00000000;
30794cb17d0SAlison Wang 		pbl_end_cmd[2] = 0x091380c0;
30894cb17d0SAlison Wang 		pbl_end_cmd[3] = 0x00000000;
30994cb17d0SAlison Wang 	}
31094cb17d0SAlison Wang 
31194cb17d0SAlison Wang 	next_pbl_cmd = pbl_cmd_initaddr;
31294cb17d0SAlison Wang 	return 0;
31394cb17d0SAlison Wang };
31494cb17d0SAlison Wang 
3155d898a00SShaohui Xie /* pblimage parameters */
316a93648d1SGuilherme Maciel Ferreira U_BOOT_IMAGE_TYPE(
317a93648d1SGuilherme Maciel Ferreira 	pblimage,
318a93648d1SGuilherme Maciel Ferreira 	"Freescale PBL Boot Image support",
319a93648d1SGuilherme Maciel Ferreira 	sizeof(struct pbl_header),
320a93648d1SGuilherme Maciel Ferreira 	(void *)&pblimage_header,
321a93648d1SGuilherme Maciel Ferreira 	pblimage_check_params,
322a93648d1SGuilherme Maciel Ferreira 	pblimage_verify_header,
323a93648d1SGuilherme Maciel Ferreira 	pblimage_print_header,
324a93648d1SGuilherme Maciel Ferreira 	pblimage_set_header,
325a93648d1SGuilherme Maciel Ferreira 	NULL,
326a93648d1SGuilherme Maciel Ferreira 	pblimage_check_image_types,
327a93648d1SGuilherme Maciel Ferreira 	NULL,
328a93648d1SGuilherme Maciel Ferreira 	NULL
329a93648d1SGuilherme Maciel Ferreira );
330