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