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