xref: /rk3399_rockchip-uboot/tools/mkenvimage.c (revision 49fbf4371e0e218cfc5448d6d189baafb7cfbd2c)
1 /*
2  * (C) Copyright 2011 Free Electrons
3  * David Wagner <david.wagner@free-electrons.com>
4  *
5  * Inspired from envcrc.c:
6  * (C) Copyright 2001
7  * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
8  *
9  * See file CREDITS for list of people who contributed to this
10  * project.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of
15  * the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25  * MA 02111-1307 USA
26  */
27 
28 /* We want the GNU version of basename() */
29 #define _GNU_SOURCE
30 
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <compiler.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 #include <u-boot/crc.h>
42 
43 #define CRC_SIZE sizeof(uint32_t)
44 
45 static void usage(const char *exec_name)
46 {
47 	fprintf(stderr, "%s [-h] [-r] [-b] [-p <byte>] "
48 	       "-s <environment partition size> -o <output> <input file>\n"
49 	       "\n"
50 	       "This tool takes a key=value input file (same as would a "
51 	       "`printenv' show) and generates the corresponding environment "
52 	       "image, ready to be flashed.\n"
53 	       "\n"
54 	       "\tThe input file is in format:\n"
55 	       "\t\tkey1=value1\n"
56 	       "\t\tkey2=value2\n"
57 	       "\t\t...\n"
58 	       "\t-r : the environment has multiple copies in flash\n"
59 	       "\t-b : the target is big endian (default is little endian)\n"
60 	       "\t-p <byte> : fill the image with <byte> bytes instead of "
61 	       "0xff bytes\n"
62 	       "\n"
63 	       "If the input file is \"-\", data is read from standard input\n",
64 	       exec_name);
65 }
66 
67 int main(int argc, char **argv)
68 {
69 	uint32_t crc, targetendian_crc;
70 	const char *txt_filename = NULL, *bin_filename = NULL;
71 	int txt_fd, bin_fd;
72 	unsigned char *dataptr, *envptr;
73 	unsigned char *filebuf = NULL;
74 	unsigned int filesize = 0, envsize = 0, datasize = 0;
75 	int bigendian = 0;
76 	int redundant = 0;
77 	unsigned char padbyte = 0xff;
78 
79 	int option;
80 	int ret = EXIT_SUCCESS;
81 
82 	struct stat txt_file_stat;
83 
84 	int fp, ep;
85 	const char *prg;
86 
87 	prg = basename(argv[0]);
88 
89 	/* Parse the cmdline */
90 	while ((option = getopt(argc, argv, "s:o:rbp:h")) != -1) {
91 		switch (option) {
92 		case 's':
93 			datasize = strtol(optarg, NULL, 0);
94 			break;
95 		case 'o':
96 			bin_filename = strdup(optarg);
97 			if (!bin_filename) {
98 				fprintf(stderr, "Can't strdup() the output "
99 						"filename\n");
100 				return EXIT_FAILURE;
101 			}
102 			break;
103 		case 'r':
104 			redundant = 1;
105 			break;
106 		case 'b':
107 			bigendian = 1;
108 			break;
109 		case 'p':
110 			padbyte = strtol(optarg, NULL, 0);
111 			break;
112 		case 'h':
113 			usage(prg);
114 			return EXIT_SUCCESS;
115 		default:
116 			fprintf(stderr, "Wrong option -%c\n", option);
117 			usage(prg);
118 			return EXIT_FAILURE;
119 		}
120 	}
121 
122 	/* Check datasize and allocate the data */
123 	if (datasize == 0) {
124 		fprintf(stderr,
125 			"Please specify the size of the envrionnment "
126 			"partition.\n");
127 		usage(prg);
128 		return EXIT_FAILURE;
129 	}
130 
131 	dataptr = malloc(datasize * sizeof(*dataptr));
132 	if (!dataptr) {
133 		fprintf(stderr, "Can't alloc dataptr.\n");
134 		return EXIT_FAILURE;
135 	}
136 
137 	/*
138 	 * envptr points to the beginning of the actual environment (after the
139 	 * crc and possible `redundant' bit
140 	 */
141 	envsize = datasize - (CRC_SIZE + redundant);
142 	envptr = dataptr + CRC_SIZE + redundant;
143 
144 	/* Pad the environment with the padding byte */
145 	memset(envptr, padbyte, envsize);
146 
147 	/* Open the input file ... */
148 	if (optind >= argc) {
149 		fprintf(stderr, "Please specify an input filename\n");
150 		return EXIT_FAILURE;
151 	}
152 
153 	txt_filename = argv[optind];
154 	if (strcmp(txt_filename, "-") == 0) {
155 		int readbytes = 0;
156 		int readlen = sizeof(*envptr) * 2048;
157 		txt_fd = STDIN_FILENO;
158 
159 		do {
160 			filebuf = realloc(filebuf, readlen);
161 			readbytes = read(txt_fd, filebuf + filesize, readlen);
162 			filesize += readbytes;
163 		} while (readbytes == readlen);
164 
165 	} else {
166 		txt_fd = open(txt_filename, O_RDONLY);
167 		if (txt_fd == -1) {
168 			fprintf(stderr, "Can't open \"%s\": %s\n",
169 					txt_filename, strerror(errno));
170 			return EXIT_FAILURE;
171 		}
172 		/* ... and check it */
173 		ret = fstat(txt_fd, &txt_file_stat);
174 		if (ret == -1) {
175 			fprintf(stderr, "Can't stat() on \"%s\": "
176 					"%s\n", txt_filename, strerror(errno));
177 			return EXIT_FAILURE;
178 		}
179 
180 		filesize = txt_file_stat.st_size;
181 		/* Read the raw input file and transform it */
182 		filebuf = malloc(sizeof(*envptr) * filesize);
183 		ret = read(txt_fd, filebuf, sizeof(*envptr) * filesize);
184 		if (ret != sizeof(*envptr) * filesize) {
185 			fprintf(stderr, "Can't read the whole input file\n");
186 			return EXIT_FAILURE;
187 		}
188 		ret = close(txt_fd);
189 	}
190 	/*
191 	 * The right test to do is "=>" (not ">") because of the additionnal
192 	 * ending \0. See below.
193 	 */
194 	if (filesize >= envsize) {
195 		fprintf(stderr, "The input file is larger than the "
196 				"envrionnment partition size\n");
197 		return EXIT_FAILURE;
198 	}
199 
200 	/* Replace newlines separating variables with \0 */
201 	for (fp = 0, ep = 0 ; fp < filesize ; fp++) {
202 		if (filebuf[fp] == '\n') {
203 			if (fp == 0) {
204 				/*
205 				 * Newline at the beggining of the file ?
206 				 * Ignore it.
207 				 */
208 				continue;
209 			} else if (filebuf[fp-1] == '\\') {
210 				/*
211 				 * Embedded newline in a variable.
212 				 *
213 				 * The backslash was added to the envptr ;
214 				 * rewind and replace it with a newline
215 				 */
216 				ep--;
217 				envptr[ep++] = '\n';
218 			} else {
219 				/* End of a variable */
220 				envptr[ep++] = '\0';
221 			}
222 		} else if (filebuf[fp] == '#') {
223 			if (fp != 0 && filebuf[fp-1] == '\n') {
224 				/* This line is a comment, let's skip it */
225 				while (fp < txt_file_stat.st_size && fp++ &&
226 				       filebuf[fp] != '\n');
227 			} else {
228 				envptr[ep++] = filebuf[fp];
229 			}
230 		} else {
231 			envptr[ep++] = filebuf[fp];
232 		}
233 	}
234 	/*
235 	 * Make sure there is a final '\0'
236 	 * And do it again on the next byte to mark the end of the environment.
237 	 */
238 	if (envptr[ep-1] != '\0') {
239 		envptr[ep++] = '\0';
240 		/*
241 		 * The text file doesn't have an ending newline.  We need to
242 		 * check the env size again to make sure we have room for two \0
243 		 */
244 		if (ep >= envsize) {
245 			fprintf(stderr, "The environment file is too large for "
246 					"the target environment storage\n");
247 			return EXIT_FAILURE;
248 		}
249 		envptr[ep] = '\0';
250 	} else {
251 		envptr[ep] = '\0';
252 	}
253 
254 	/* Computes the CRC and put it at the beginning of the data */
255 	crc = crc32(0, envptr, envsize);
256 	targetendian_crc = bigendian ? cpu_to_be32(crc) : cpu_to_le32(crc);
257 
258 	memcpy(dataptr, &targetendian_crc, sizeof(uint32_t));
259 
260 	bin_fd = creat(bin_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
261 	if (bin_fd == -1) {
262 		fprintf(stderr, "Can't open output file \"%s\": %s\n",
263 				bin_filename, strerror(errno));
264 		return EXIT_FAILURE;
265 	}
266 
267 	if (write(bin_fd, dataptr, sizeof(*dataptr) * datasize) !=
268 			sizeof(*dataptr) * datasize) {
269 		fprintf(stderr, "write() failed: %s\n", strerror(errno));
270 		return EXIT_FAILURE;
271 	}
272 
273 	ret = close(bin_fd);
274 
275 	return ret;
276 }
277