xref: /OK3568_Linux_fs/u-boot/tools/mkenvimage.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * (C) Copyright 2011 Free Electrons
3*4882a593Smuzhiyun  * David Wagner <david.wagner@free-electrons.com>
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Inspired from envcrc.c:
6*4882a593Smuzhiyun  * (C) Copyright 2001
7*4882a593Smuzhiyun  * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <errno.h>
13*4882a593Smuzhiyun #include <fcntl.h>
14*4882a593Smuzhiyun #include <stdio.h>
15*4882a593Smuzhiyun #include <stdlib.h>
16*4882a593Smuzhiyun #include <stdint.h>
17*4882a593Smuzhiyun #include <string.h>
18*4882a593Smuzhiyun #include <unistd.h>
19*4882a593Smuzhiyun #include <libgen.h>
20*4882a593Smuzhiyun #include <sys/types.h>
21*4882a593Smuzhiyun #include <sys/stat.h>
22*4882a593Smuzhiyun #include <sys/mman.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include "compiler.h"
25*4882a593Smuzhiyun #include <u-boot/crc.h>
26*4882a593Smuzhiyun #include <version.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #define CRC_SIZE sizeof(uint32_t)
29*4882a593Smuzhiyun 
usage(const char * exec_name)30*4882a593Smuzhiyun static void usage(const char *exec_name)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	fprintf(stderr, "%s [-h] [-r] [-b] [-p <byte>] -s <environment partition size> -o <output> <input file>\n"
33*4882a593Smuzhiyun 	       "\n"
34*4882a593Smuzhiyun 	       "This tool takes a key=value input file (same as would a `printenv' show) and generates the corresponding environment image, ready to be flashed.\n"
35*4882a593Smuzhiyun 	       "\n"
36*4882a593Smuzhiyun 	       "\tThe input file is in format:\n"
37*4882a593Smuzhiyun 	       "\t\tkey1=value1\n"
38*4882a593Smuzhiyun 	       "\t\tkey2=value2\n"
39*4882a593Smuzhiyun 	       "\t\t...\n"
40*4882a593Smuzhiyun 	       "\tEmpty lines are skipped, and lines with a # in the first\n"
41*4882a593Smuzhiyun 	       "\tcolumn are treated as comments (also skipped).\n"
42*4882a593Smuzhiyun 	       "\t-r : the environment has multiple copies in flash\n"
43*4882a593Smuzhiyun 	       "\t-b : the target is big endian (default is little endian)\n"
44*4882a593Smuzhiyun 	       "\t-p <byte> : fill the image with <byte> bytes instead of 0xff bytes\n"
45*4882a593Smuzhiyun 	       "\t-V : print version information and exit\n"
46*4882a593Smuzhiyun 	       "\n"
47*4882a593Smuzhiyun 	       "If the input file is \"-\", data is read from standard input\n",
48*4882a593Smuzhiyun 	       exec_name);
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun 
xstrtol(const char * s)51*4882a593Smuzhiyun long int xstrtol(const char *s)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun 	long int tmp;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	errno = 0;
56*4882a593Smuzhiyun 	tmp = strtol(s, NULL, 0);
57*4882a593Smuzhiyun 	if (!errno)
58*4882a593Smuzhiyun 		return tmp;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	if (errno == ERANGE)
61*4882a593Smuzhiyun 		fprintf(stderr, "Bad integer format: %s\n",  s);
62*4882a593Smuzhiyun 	else
63*4882a593Smuzhiyun 		fprintf(stderr, "Error while parsing %s: %s\n", s,
64*4882a593Smuzhiyun 				strerror(errno));
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	exit(EXIT_FAILURE);
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun 
main(int argc,char ** argv)69*4882a593Smuzhiyun int main(int argc, char **argv)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun 	uint32_t crc, targetendian_crc;
72*4882a593Smuzhiyun 	const char *txt_filename = NULL, *bin_filename = NULL;
73*4882a593Smuzhiyun 	int txt_fd, bin_fd;
74*4882a593Smuzhiyun 	unsigned char *dataptr, *envptr;
75*4882a593Smuzhiyun 	unsigned char *filebuf = NULL;
76*4882a593Smuzhiyun 	unsigned int filesize = 0, envsize = 0, datasize = 0;
77*4882a593Smuzhiyun 	int bigendian = 0;
78*4882a593Smuzhiyun 	int redundant = 0;
79*4882a593Smuzhiyun 	unsigned char padbyte = 0xff;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	int option;
82*4882a593Smuzhiyun 	int ret = EXIT_SUCCESS;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	struct stat txt_file_stat;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	int fp, ep;
87*4882a593Smuzhiyun 	const char *prg;
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	prg = basename(argv[0]);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	/* Turn off getopt()'s internal error message */
92*4882a593Smuzhiyun 	opterr = 0;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	/* Parse the cmdline */
95*4882a593Smuzhiyun 	while ((option = getopt(argc, argv, ":s:o:rbp:hV")) != -1) {
96*4882a593Smuzhiyun 		switch (option) {
97*4882a593Smuzhiyun 		case 's':
98*4882a593Smuzhiyun 			datasize = xstrtol(optarg);
99*4882a593Smuzhiyun 			break;
100*4882a593Smuzhiyun 		case 'o':
101*4882a593Smuzhiyun 			bin_filename = strdup(optarg);
102*4882a593Smuzhiyun 			if (!bin_filename) {
103*4882a593Smuzhiyun 				fprintf(stderr, "Can't strdup() the output filename\n");
104*4882a593Smuzhiyun 				return EXIT_FAILURE;
105*4882a593Smuzhiyun 			}
106*4882a593Smuzhiyun 			break;
107*4882a593Smuzhiyun 		case 'r':
108*4882a593Smuzhiyun 			redundant = 1;
109*4882a593Smuzhiyun 			break;
110*4882a593Smuzhiyun 		case 'b':
111*4882a593Smuzhiyun 			bigendian = 1;
112*4882a593Smuzhiyun 			break;
113*4882a593Smuzhiyun 		case 'p':
114*4882a593Smuzhiyun 			padbyte = xstrtol(optarg);
115*4882a593Smuzhiyun 			break;
116*4882a593Smuzhiyun 		case 'h':
117*4882a593Smuzhiyun 			usage(prg);
118*4882a593Smuzhiyun 			return EXIT_SUCCESS;
119*4882a593Smuzhiyun 		case 'V':
120*4882a593Smuzhiyun 			printf("%s version %s\n", prg, PLAIN_VERSION);
121*4882a593Smuzhiyun 			return EXIT_SUCCESS;
122*4882a593Smuzhiyun 		case ':':
123*4882a593Smuzhiyun 			fprintf(stderr, "Missing argument for option -%c\n",
124*4882a593Smuzhiyun 				optopt);
125*4882a593Smuzhiyun 			usage(prg);
126*4882a593Smuzhiyun 			return EXIT_FAILURE;
127*4882a593Smuzhiyun 		default:
128*4882a593Smuzhiyun 			fprintf(stderr, "Wrong option -%c\n", optopt);
129*4882a593Smuzhiyun 			usage(prg);
130*4882a593Smuzhiyun 			return EXIT_FAILURE;
131*4882a593Smuzhiyun 		}
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	/* Check datasize and allocate the data */
135*4882a593Smuzhiyun 	if (datasize == 0) {
136*4882a593Smuzhiyun 		fprintf(stderr, "Please specify the size of the environment partition.\n");
137*4882a593Smuzhiyun 		usage(prg);
138*4882a593Smuzhiyun 		return EXIT_FAILURE;
139*4882a593Smuzhiyun 	}
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	dataptr = malloc(datasize * sizeof(*dataptr));
142*4882a593Smuzhiyun 	if (!dataptr) {
143*4882a593Smuzhiyun 		fprintf(stderr, "Can't alloc %d bytes for dataptr.\n",
144*4882a593Smuzhiyun 				datasize);
145*4882a593Smuzhiyun 		return EXIT_FAILURE;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	/*
149*4882a593Smuzhiyun 	 * envptr points to the beginning of the actual environment (after the
150*4882a593Smuzhiyun 	 * crc and possible `redundant' byte
151*4882a593Smuzhiyun 	 */
152*4882a593Smuzhiyun 	envsize = datasize - (CRC_SIZE + redundant);
153*4882a593Smuzhiyun 	envptr = dataptr + CRC_SIZE + redundant;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	/* Pad the environment with the padding byte */
156*4882a593Smuzhiyun 	memset(envptr, padbyte, envsize);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	/* Open the input file ... */
159*4882a593Smuzhiyun 	if (optind >= argc || strcmp(argv[optind], "-") == 0) {
160*4882a593Smuzhiyun 		int readbytes = 0;
161*4882a593Smuzhiyun 		int readlen = sizeof(*envptr) * 4096;
162*4882a593Smuzhiyun 		txt_fd = STDIN_FILENO;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 		do {
165*4882a593Smuzhiyun 			filebuf = realloc(filebuf, readlen);
166*4882a593Smuzhiyun 			if (!filebuf) {
167*4882a593Smuzhiyun 				fprintf(stderr, "Can't realloc memory for the input file buffer\n");
168*4882a593Smuzhiyun 				return EXIT_FAILURE;
169*4882a593Smuzhiyun 			}
170*4882a593Smuzhiyun 			readbytes = read(txt_fd, filebuf + filesize, readlen);
171*4882a593Smuzhiyun 			if (errno) {
172*4882a593Smuzhiyun 				fprintf(stderr, "Error while reading stdin: %s\n",
173*4882a593Smuzhiyun 						strerror(errno));
174*4882a593Smuzhiyun 				return EXIT_FAILURE;
175*4882a593Smuzhiyun 			}
176*4882a593Smuzhiyun 			filesize += readbytes;
177*4882a593Smuzhiyun 		} while (readbytes == readlen);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	} else {
180*4882a593Smuzhiyun 		txt_filename = argv[optind];
181*4882a593Smuzhiyun 		txt_fd = open(txt_filename, O_RDONLY);
182*4882a593Smuzhiyun 		if (txt_fd == -1) {
183*4882a593Smuzhiyun 			fprintf(stderr, "Can't open \"%s\": %s\n",
184*4882a593Smuzhiyun 					txt_filename, strerror(errno));
185*4882a593Smuzhiyun 			return EXIT_FAILURE;
186*4882a593Smuzhiyun 		}
187*4882a593Smuzhiyun 		/* ... and check it */
188*4882a593Smuzhiyun 		ret = fstat(txt_fd, &txt_file_stat);
189*4882a593Smuzhiyun 		if (ret == -1) {
190*4882a593Smuzhiyun 			fprintf(stderr, "Can't stat() on \"%s\": %s\n",
191*4882a593Smuzhiyun 					txt_filename, strerror(errno));
192*4882a593Smuzhiyun 			return EXIT_FAILURE;
193*4882a593Smuzhiyun 		}
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 		filesize = txt_file_stat.st_size;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 		filebuf = mmap(NULL, sizeof(*envptr) * filesize, PROT_READ,
198*4882a593Smuzhiyun 			       MAP_PRIVATE, txt_fd, 0);
199*4882a593Smuzhiyun 		if (filebuf == MAP_FAILED) {
200*4882a593Smuzhiyun 			fprintf(stderr, "mmap (%zu bytes) failed: %s\n",
201*4882a593Smuzhiyun 					sizeof(*envptr) * filesize,
202*4882a593Smuzhiyun 					strerror(errno));
203*4882a593Smuzhiyun 			fprintf(stderr, "Falling back to read()\n");
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 			filebuf = malloc(sizeof(*envptr) * filesize);
206*4882a593Smuzhiyun 			ret = read(txt_fd, filebuf, sizeof(*envptr) * filesize);
207*4882a593Smuzhiyun 			if (ret != sizeof(*envptr) * filesize) {
208*4882a593Smuzhiyun 				fprintf(stderr, "Can't read the whole input file (%zu bytes): %s\n",
209*4882a593Smuzhiyun 					sizeof(*envptr) * filesize,
210*4882a593Smuzhiyun 					strerror(errno));
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 				return EXIT_FAILURE;
213*4882a593Smuzhiyun 			}
214*4882a593Smuzhiyun 		}
215*4882a593Smuzhiyun 		ret = close(txt_fd);
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	/* Parse a byte at time until reaching the file OR until the environment fills
219*4882a593Smuzhiyun 	 * up. Check ep against envsize - 1 to allow for extra trailing '\0'. */
220*4882a593Smuzhiyun 	for (fp = 0, ep = 0 ; fp < filesize && ep < envsize - 1; fp++) {
221*4882a593Smuzhiyun 		if (filebuf[fp] == '\n') {
222*4882a593Smuzhiyun 			if (fp == 0 || filebuf[fp-1] == '\n') {
223*4882a593Smuzhiyun 				/*
224*4882a593Smuzhiyun 				 * Skip empty lines.
225*4882a593Smuzhiyun 				 */
226*4882a593Smuzhiyun 				continue;
227*4882a593Smuzhiyun 			} else if (filebuf[fp-1] == '\\') {
228*4882a593Smuzhiyun 				/*
229*4882a593Smuzhiyun 				 * Embedded newline in a variable.
230*4882a593Smuzhiyun 				 *
231*4882a593Smuzhiyun 				 * The backslash was added to the envptr; rewind
232*4882a593Smuzhiyun 				 * and replace it with a newline
233*4882a593Smuzhiyun 				 */
234*4882a593Smuzhiyun 				ep--;
235*4882a593Smuzhiyun 				envptr[ep++] = '\n';
236*4882a593Smuzhiyun 			} else {
237*4882a593Smuzhiyun 				/* End of a variable */
238*4882a593Smuzhiyun 				envptr[ep++] = '\0';
239*4882a593Smuzhiyun 			}
240*4882a593Smuzhiyun 		} else if ((fp == 0 || filebuf[fp-1] == '\n') && filebuf[fp] == '#') {
241*4882a593Smuzhiyun 			/* Comment, skip the line. */
242*4882a593Smuzhiyun 			while (++fp < filesize && filebuf[fp] != '\n')
243*4882a593Smuzhiyun 			continue;
244*4882a593Smuzhiyun 		} else {
245*4882a593Smuzhiyun 			envptr[ep++] = filebuf[fp];
246*4882a593Smuzhiyun 		}
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun 	/* If there are more bytes in the file still, it means the env filled up
249*4882a593Smuzhiyun 	 * before parsing the whole file.  Eat comments & whitespace here to see if
250*4882a593Smuzhiyun 	 * there was anything meaning full left in the file, and if so, throw a error
251*4882a593Smuzhiyun 	 * and exit. */
252*4882a593Smuzhiyun 	for( ; fp < filesize; fp++ )
253*4882a593Smuzhiyun 	{
254*4882a593Smuzhiyun 		if (filebuf[fp] == '\n') {
255*4882a593Smuzhiyun 			if (fp == 0 || filebuf[fp-1] == '\n') {
256*4882a593Smuzhiyun 				/* Ignore blank lines */
257*4882a593Smuzhiyun 				continue;
258*4882a593Smuzhiyun 			}
259*4882a593Smuzhiyun 		} else if ((fp == 0 || filebuf[fp-1] == '\n') && filebuf[fp] == '#') {
260*4882a593Smuzhiyun 			while (++fp < filesize && filebuf[fp] != '\n')
261*4882a593Smuzhiyun 			continue;
262*4882a593Smuzhiyun 		} else {
263*4882a593Smuzhiyun 			fprintf(stderr, "The environment file is too large for the target environment storage\n");
264*4882a593Smuzhiyun 			return EXIT_FAILURE;
265*4882a593Smuzhiyun 		}
266*4882a593Smuzhiyun 	}
267*4882a593Smuzhiyun 	/*
268*4882a593Smuzhiyun 	 * Make sure there is a final '\0'
269*4882a593Smuzhiyun 	 * And do it again on the next byte to mark the end of the environment.
270*4882a593Smuzhiyun 	 */
271*4882a593Smuzhiyun 	if (envptr[ep-1] != '\0') {
272*4882a593Smuzhiyun 		envptr[ep++] = '\0';
273*4882a593Smuzhiyun 		/*
274*4882a593Smuzhiyun 		 * The text file doesn't have an ending newline.  We need to
275*4882a593Smuzhiyun 		 * check the env size again to make sure we have room for two \0
276*4882a593Smuzhiyun 		 */
277*4882a593Smuzhiyun 		if (ep >= envsize) {
278*4882a593Smuzhiyun 			fprintf(stderr, "The environment file is too large for the target environment storage\n");
279*4882a593Smuzhiyun 			return EXIT_FAILURE;
280*4882a593Smuzhiyun 		}
281*4882a593Smuzhiyun 		envptr[ep] = '\0';
282*4882a593Smuzhiyun 	} else {
283*4882a593Smuzhiyun 		envptr[ep] = '\0';
284*4882a593Smuzhiyun 	}
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	/* Computes the CRC and put it at the beginning of the data */
287*4882a593Smuzhiyun 	crc = crc32(0, envptr, envsize);
288*4882a593Smuzhiyun 	targetendian_crc = bigendian ? cpu_to_be32(crc) : cpu_to_le32(crc);
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	memcpy(dataptr, &targetendian_crc, sizeof(targetendian_crc));
291*4882a593Smuzhiyun 	if (redundant)
292*4882a593Smuzhiyun 		dataptr[sizeof(targetendian_crc)] = 1;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	if (!bin_filename || strcmp(bin_filename, "-") == 0) {
295*4882a593Smuzhiyun 		bin_fd = STDOUT_FILENO;
296*4882a593Smuzhiyun 	} else {
297*4882a593Smuzhiyun 		bin_fd = creat(bin_filename, S_IRUSR | S_IWUSR | S_IRGRP |
298*4882a593Smuzhiyun 					     S_IWGRP);
299*4882a593Smuzhiyun 		if (bin_fd == -1) {
300*4882a593Smuzhiyun 			fprintf(stderr, "Can't open output file \"%s\": %s\n",
301*4882a593Smuzhiyun 					bin_filename, strerror(errno));
302*4882a593Smuzhiyun 			return EXIT_FAILURE;
303*4882a593Smuzhiyun 		}
304*4882a593Smuzhiyun 	}
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	if (write(bin_fd, dataptr, sizeof(*dataptr) * datasize) !=
307*4882a593Smuzhiyun 			sizeof(*dataptr) * datasize) {
308*4882a593Smuzhiyun 		fprintf(stderr, "write() failed: %s\n", strerror(errno));
309*4882a593Smuzhiyun 		return EXIT_FAILURE;
310*4882a593Smuzhiyun 	}
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	ret = close(bin_fd);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	return ret;
315*4882a593Smuzhiyun }
316