1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2019 Broadcom
5 * (C) Copyright 2011 Free Electrons
6 * David Wagner <david.wagner@free-electrons.com>
8 * Inspired from envcrc.c:
10 * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
22 #include <sys/types.h>
27 #include <u-boot/crc.h>
30 #define CRC_SIZE sizeof(uint32_t)
31 #define UBOOT_ENV_MAGIC (0x75456e76)
47 static void usage(const char *exec_name
)
49 fprintf(stderr
, "%s [-h] <input image file> <input environment value file>\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"
53 "\tThe input file is in format:\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"
61 "If the input file is \"-\", data is read from standard input\n",
66 static struct option long_opts
[] = {
70 long int xstrtol(const char *s
)
75 tmp
= strtol(s
, NULL
, 0);
80 fprintf(stderr
, "Bad integer format: %s\n", s
);
82 fprintf(stderr
, "Error while parsing %s: %s\n", s
,
88 static int check_excluded(char *evar
, int max
)
92 while (NULL
!= exclude
[i
]) {
93 n
= strlen(exclude
[i
]);
94 if (0 == strncmp(evar
, exclude
[i
], n
< max
? n
: max
))
103 int main(int argc
, char **argv
)
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;
112 unsigned char padbyte
= 0xff;
119 int ret
= EXIT_SUCCESS
;
121 struct stat txt_file_stat
;
122 struct stat input_img_file_stat
;
123 unsigned img_filesize
= 0;
128 prg
= basename(argv
[0]);
130 /* Turn off getopt()'s internal error message */
133 /* Parse the cmdline */
134 while ((option
= getopt_long(argc
, argv
, "hV",long_opts
,NULL
)) != -1) {
140 printf("%s version %s\n", prg
, PLAIN_VERSION
);
143 fprintf(stderr
, "Missing argument for option -%c\n",
150 fprintf(stderr
, "Wrong option -%c\n", optopt
);
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
);
161 fprintf(stderr
, "Can't stat() on \"%s\": %s\n",
162 input_img_filename
, strerror(errno
));
166 img_filesize
= input_img_file_stat
.st_size
;
168 fprintf(stderr
,"finame is required\n");
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]);
180 tmpdatasize
= header
[1];
181 if (tmpdatasize
> 65536) {
182 fprintf(stderr
,"datasize > 64K not allowed\n");
185 if (dataptr
== NULL
) {
186 dataptr
= malloc(tmpdatasize
+ 4);
188 dataptr
= realloc(dataptr
,tmpdatasize
+ 4);
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);
194 if (crc
== havecrc
) {
196 offsets
[found
++] = i
;
197 datasize
= tmpdatasize
;
199 printf("CRC FAIL\n");
203 /* Check datasize and allocate the data */
205 fprintf(stderr
, "No header found\n");
210 * envptr points to the beginning of the actual environment (after the
211 * crc and possible `redundant' byte
213 envsize
= datasize
- CRC_SIZE
;
214 envptr
= dataptr
+ CRC_SIZE
;
216 /* Pad the environment with the padding byte */
217 memset(envptr
, padbyte
, envsize
);
219 /* Open the input file ... */
220 if (optind
+1 < argc
) {
221 txt_filename
= argv
[optind
+1];
222 txt_fd
= open(txt_filename
, O_RDONLY
);
224 fprintf(stderr
, "Can't open \"%s\": %s\n",
225 txt_filename
, strerror(errno
));
228 /* ... and check it */
229 ret
= fstat(txt_fd
, &txt_file_stat
);
231 fprintf(stderr
, "Can't stat() on \"%s\": %s\n",
232 txt_filename
, strerror(errno
));
236 filesize
= txt_file_stat
.st_size
;
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
,
244 fprintf(stderr
, "Falling back to read()\n");
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
,
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') {
268 } else if (filebuf
[fp
-1] == '\\') {
270 * Embedded newline in a variable.
272 * The backslash was added to the envptr; rewind
273 * and replace it with a newline
278 /* End of a variable */
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')
286 envptr
[ep
++] = filebuf
[fp
];
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
293 for( ; fp
< filesize
; fp
++ )
295 if (filebuf
[fp
] == '\n') {
296 if (fp
== 0 || filebuf
[fp
-1] == '\n') {
297 /* Ignore blank lines */
300 } else if ((fp
== 0 || filebuf
[fp
-1] == '\n') && filebuf
[fp
] == '#') {
301 while (++fp
< filesize
&& filebuf
[fp
] != '\n')
304 fprintf(stderr
, "The environment file is too large for the target environment storage\n");
309 * Make sure there is a final '\0'
310 * And do it again on the next byte to mark the end of the environment.
312 if (envptr
[ep
-1] != '\0') {
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
319 fprintf(stderr
, "The environment file is too large for the target environment storage\n");
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
);
331 memcpy(dataptr
, &targetendian_crc
, sizeof(targetendian_crc
));
332 for (i
= 0 ; i
< found
; i
++) {
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
));
342 ret
= close(input_img_fd
);