blob: f685ff2e30c72617e699f428cfd806a8c9226d98 [file] [log] [blame]
David Wagnera6337e62011-09-26 03:26:34 +00001/*
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 *
Wolfgang Denk1a459662013-07-08 09:37:19 +02009 * SPDX-License-Identifier: GPL-2.0+
David Wagnera6337e62011-09-26 03:26:34 +000010 */
11
Horst Kronstorferaf44f4b2011-12-21 10:39:39 +000012/* We want the GNU version of basename() */
13#define _GNU_SOURCE
14
David Wagnera6337e62011-09-26 03:26:34 +000015#include <errno.h>
16#include <fcntl.h>
17#include <stdio.h>
David Wagnerd1acdae2012-01-13 13:27:35 +000018#include <stdlib.h>
David Wagnera6337e62011-09-26 03:26:34 +000019#include <stdint.h>
20#include <string.h>
21#include <unistd.h>
Andreas Bießmann558cd992012-06-28 08:01:58 +020022#include <libgen.h>
David Wagnera6337e62011-09-26 03:26:34 +000023#include <sys/types.h>
24#include <sys/stat.h>
David Wagner6ee39f82012-01-13 13:27:38 +000025#include <sys/mman.h>
David Wagnera6337e62011-09-26 03:26:34 +000026
David Wagnerd1acdae2012-01-13 13:27:35 +000027#include "compiler.h"
David Wagnera6337e62011-09-26 03:26:34 +000028#include <u-boot/crc.h>
Horst Kronstorfer18954202011-12-05 00:55:26 +000029#include <version.h>
David Wagnera6337e62011-09-26 03:26:34 +000030
31#define CRC_SIZE sizeof(uint32_t)
32
33static void usage(const char *exec_name)
34{
David Wagner8a1b8fc2012-01-13 13:27:34 +000035 fprintf(stderr, "%s [-h] [-r] [-b] [-p <byte>] -s <environment partition size> -o <output> <input file>\n"
David Wagnera6337e62011-09-26 03:26:34 +000036 "\n"
David Wagner8a1b8fc2012-01-13 13:27:34 +000037 "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"
David Wagnera6337e62011-09-26 03:26:34 +000038 "\n"
39 "\tThe input file is in format:\n"
40 "\t\tkey1=value1\n"
41 "\t\tkey2=value2\n"
42 "\t\t...\n"
43 "\t-r : the environment has multiple copies in flash\n"
44 "\t-b : the target is big endian (default is little endian)\n"
David Wagner8a1b8fc2012-01-13 13:27:34 +000045 "\t-p <byte> : fill the image with <byte> bytes instead of 0xff bytes\n"
Horst Kronstorfer18954202011-12-05 00:55:26 +000046 "\t-V : print version information and exit\n"
David Wagnera6337e62011-09-26 03:26:34 +000047 "\n"
48 "If the input file is \"-\", data is read from standard input\n",
49 exec_name);
50}
51
David Wagner3d0f9bd2012-01-13 13:27:36 +000052long int xstrtol(const char *s)
53{
54 long int tmp;
55
56 errno = 0;
57 tmp = strtol(s, NULL, 0);
58 if (!errno)
59 return tmp;
60
61 if (errno == ERANGE)
62 fprintf(stderr, "Bad integer format: %s\n", s);
63 else
64 fprintf(stderr, "Error while parsing %s: %s\n", s,
65 strerror(errno));
66
67 exit(EXIT_FAILURE);
68}
69
David Wagnera6337e62011-09-26 03:26:34 +000070int main(int argc, char **argv)
71{
72 uint32_t crc, targetendian_crc;
73 const char *txt_filename = NULL, *bin_filename = NULL;
74 int txt_fd, bin_fd;
75 unsigned char *dataptr, *envptr;
76 unsigned char *filebuf = NULL;
77 unsigned int filesize = 0, envsize = 0, datasize = 0;
78 int bigendian = 0;
79 int redundant = 0;
80 unsigned char padbyte = 0xff;
81
82 int option;
83 int ret = EXIT_SUCCESS;
84
85 struct stat txt_file_stat;
86
87 int fp, ep;
Horst Kronstorferaf44f4b2011-12-21 10:39:39 +000088 const char *prg;
89
90 prg = basename(argv[0]);
David Wagnera6337e62011-09-26 03:26:34 +000091
Horst Kronstorfer5e0c63e2011-12-05 00:55:24 +000092 /* Turn off getopt()'s internal error message */
93 opterr = 0;
94
David Wagnera6337e62011-09-26 03:26:34 +000095 /* Parse the cmdline */
Horst Kronstorfer18954202011-12-05 00:55:26 +000096 while ((option = getopt(argc, argv, ":s:o:rbp:hV")) != -1) {
David Wagnera6337e62011-09-26 03:26:34 +000097 switch (option) {
98 case 's':
David Wagner3d0f9bd2012-01-13 13:27:36 +000099 datasize = xstrtol(optarg);
David Wagnera6337e62011-09-26 03:26:34 +0000100 break;
101 case 'o':
102 bin_filename = strdup(optarg);
103 if (!bin_filename) {
David Wagner8a1b8fc2012-01-13 13:27:34 +0000104 fprintf(stderr, "Can't strdup() the output filename\n");
David Wagnera6337e62011-09-26 03:26:34 +0000105 return EXIT_FAILURE;
106 }
107 break;
108 case 'r':
109 redundant = 1;
110 break;
111 case 'b':
112 bigendian = 1;
113 break;
114 case 'p':
David Wagner3d0f9bd2012-01-13 13:27:36 +0000115 padbyte = xstrtol(optarg);
David Wagnera6337e62011-09-26 03:26:34 +0000116 break;
117 case 'h':
Horst Kronstorferaf44f4b2011-12-21 10:39:39 +0000118 usage(prg);
David Wagnera6337e62011-09-26 03:26:34 +0000119 return EXIT_SUCCESS;
Horst Kronstorfer18954202011-12-05 00:55:26 +0000120 case 'V':
121 printf("%s version %s\n", prg, PLAIN_VERSION);
122 return EXIT_SUCCESS;
Horst Kronstorfer5e0c63e2011-12-05 00:55:24 +0000123 case ':':
124 fprintf(stderr, "Missing argument for option -%c\n",
Horst Kronstorfer72ebafb2011-12-24 00:41:58 +0000125 optopt);
Wolfgang Denke758a5c2012-03-01 21:19:40 +0000126 usage(prg);
Horst Kronstorfer5e0c63e2011-12-05 00:55:24 +0000127 return EXIT_FAILURE;
David Wagnera6337e62011-09-26 03:26:34 +0000128 default:
Horst Kronstorfer72ebafb2011-12-24 00:41:58 +0000129 fprintf(stderr, "Wrong option -%c\n", optopt);
Horst Kronstorferaf44f4b2011-12-21 10:39:39 +0000130 usage(prg);
David Wagnera6337e62011-09-26 03:26:34 +0000131 return EXIT_FAILURE;
132 }
133 }
134
135 /* Check datasize and allocate the data */
136 if (datasize == 0) {
David Wagner8a1b8fc2012-01-13 13:27:34 +0000137 fprintf(stderr, "Please specify the size of the environment partition.\n");
Horst Kronstorferaf44f4b2011-12-21 10:39:39 +0000138 usage(prg);
David Wagnera6337e62011-09-26 03:26:34 +0000139 return EXIT_FAILURE;
140 }
141
142 dataptr = malloc(datasize * sizeof(*dataptr));
143 if (!dataptr) {
David Wagner8a1b8fc2012-01-13 13:27:34 +0000144 fprintf(stderr, "Can't alloc %d bytes for dataptr.\n",
145 datasize);
David Wagnera6337e62011-09-26 03:26:34 +0000146 return EXIT_FAILURE;
147 }
148
149 /*
150 * envptr points to the beginning of the actual environment (after the
David Wagner8a1b8fc2012-01-13 13:27:34 +0000151 * crc and possible `redundant' byte
David Wagnera6337e62011-09-26 03:26:34 +0000152 */
153 envsize = datasize - (CRC_SIZE + redundant);
154 envptr = dataptr + CRC_SIZE + redundant;
155
156 /* Pad the environment with the padding byte */
157 memset(envptr, padbyte, envsize);
158
159 /* Open the input file ... */
David Wagner48995b52012-01-13 13:27:37 +0000160 if (optind >= argc || strcmp(argv[optind], "-") == 0) {
David Wagnera6337e62011-09-26 03:26:34 +0000161 int readbytes = 0;
David Wagner48995b52012-01-13 13:27:37 +0000162 int readlen = sizeof(*envptr) * 4096;
David Wagnera6337e62011-09-26 03:26:34 +0000163 txt_fd = STDIN_FILENO;
164
165 do {
166 filebuf = realloc(filebuf, readlen);
David Wagner3d0f9bd2012-01-13 13:27:36 +0000167 if (!filebuf) {
168 fprintf(stderr, "Can't realloc memory for the input file buffer\n");
169 return EXIT_FAILURE;
170 }
David Wagnera6337e62011-09-26 03:26:34 +0000171 readbytes = read(txt_fd, filebuf + filesize, readlen);
David Wagner3d0f9bd2012-01-13 13:27:36 +0000172 if (errno) {
173 fprintf(stderr, "Error while reading stdin: %s\n",
174 strerror(errno));
175 return EXIT_FAILURE;
176 }
David Wagnera6337e62011-09-26 03:26:34 +0000177 filesize += readbytes;
178 } while (readbytes == readlen);
179
180 } else {
David Wagner48995b52012-01-13 13:27:37 +0000181 txt_filename = argv[optind];
David Wagnera6337e62011-09-26 03:26:34 +0000182 txt_fd = open(txt_filename, O_RDONLY);
183 if (txt_fd == -1) {
184 fprintf(stderr, "Can't open \"%s\": %s\n",
185 txt_filename, strerror(errno));
186 return EXIT_FAILURE;
187 }
188 /* ... and check it */
189 ret = fstat(txt_fd, &txt_file_stat);
190 if (ret == -1) {
David Wagner8a1b8fc2012-01-13 13:27:34 +0000191 fprintf(stderr, "Can't stat() on \"%s\": %s\n",
192 txt_filename, strerror(errno));
David Wagnera6337e62011-09-26 03:26:34 +0000193 return EXIT_FAILURE;
194 }
195
196 filesize = txt_file_stat.st_size;
David Wagner6ee39f82012-01-13 13:27:38 +0000197
198 filebuf = mmap(NULL, sizeof(*envptr) * filesize, PROT_READ,
199 MAP_PRIVATE, txt_fd, 0);
200 if (filebuf == MAP_FAILED) {
Dirk Behme1ebff632012-04-05 23:15:07 +0000201 fprintf(stderr, "mmap (%zu bytes) failed: %s\n",
David Wagner6ee39f82012-01-13 13:27:38 +0000202 sizeof(*envptr) * filesize,
203 strerror(errno));
204 fprintf(stderr, "Falling back to read()\n");
205
206 filebuf = malloc(sizeof(*envptr) * filesize);
207 ret = read(txt_fd, filebuf, sizeof(*envptr) * filesize);
208 if (ret != sizeof(*envptr) * filesize) {
Dirk Behme1ebff632012-04-05 23:15:07 +0000209 fprintf(stderr, "Can't read the whole input file (%zu bytes): %s\n",
David Wagner6ee39f82012-01-13 13:27:38 +0000210 sizeof(*envptr) * filesize,
211 strerror(errno));
212
213 return EXIT_FAILURE;
214 }
David Wagnera6337e62011-09-26 03:26:34 +0000215 }
216 ret = close(txt_fd);
217 }
David Wagner8a1b8fc2012-01-13 13:27:34 +0000218 /* The +1 is for the additionnal ending \0. See below. */
219 if (filesize + 1 > envsize) {
220 fprintf(stderr, "The input file is larger than the environment partition size\n");
David Wagnera6337e62011-09-26 03:26:34 +0000221 return EXIT_FAILURE;
222 }
223
224 /* Replace newlines separating variables with \0 */
225 for (fp = 0, ep = 0 ; fp < filesize ; fp++) {
226 if (filebuf[fp] == '\n') {
David Wagnerdbee61d2011-12-20 14:59:29 +0000227 if (ep == 0) {
David Wagnera6337e62011-09-26 03:26:34 +0000228 /*
David Wagnerdbee61d2011-12-20 14:59:29 +0000229 * Newlines at the beginning of the file ?
230 * Ignore them.
David Wagnera6337e62011-09-26 03:26:34 +0000231 */
232 continue;
233 } else if (filebuf[fp-1] == '\\') {
234 /*
235 * Embedded newline in a variable.
236 *
David Wagnerdbee61d2011-12-20 14:59:29 +0000237 * The backslash was added to the envptr; rewind
238 * and replace it with a newline
David Wagnera6337e62011-09-26 03:26:34 +0000239 */
240 ep--;
241 envptr[ep++] = '\n';
242 } else {
243 /* End of a variable */
244 envptr[ep++] = '\0';
245 }
David Wagnera6337e62011-09-26 03:26:34 +0000246 } else {
247 envptr[ep++] = filebuf[fp];
248 }
249 }
250 /*
251 * Make sure there is a final '\0'
252 * And do it again on the next byte to mark the end of the environment.
253 */
254 if (envptr[ep-1] != '\0') {
255 envptr[ep++] = '\0';
256 /*
257 * The text file doesn't have an ending newline. We need to
258 * check the env size again to make sure we have room for two \0
259 */
260 if (ep >= envsize) {
David Wagner8a1b8fc2012-01-13 13:27:34 +0000261 fprintf(stderr, "The environment file is too large for the target environment storage\n");
David Wagnera6337e62011-09-26 03:26:34 +0000262 return EXIT_FAILURE;
263 }
264 envptr[ep] = '\0';
265 } else {
266 envptr[ep] = '\0';
267 }
268
269 /* Computes the CRC and put it at the beginning of the data */
270 crc = crc32(0, envptr, envsize);
271 targetendian_crc = bigendian ? cpu_to_be32(crc) : cpu_to_le32(crc);
272
David Wagnerd8d26592012-01-13 13:27:40 +0000273 memcpy(dataptr, &targetendian_crc, sizeof(targetendian_crc));
274 if (redundant)
275 dataptr[sizeof(targetendian_crc)] = 1;
David Wagnera6337e62011-09-26 03:26:34 +0000276
David Wagner48995b52012-01-13 13:27:37 +0000277 if (!bin_filename || strcmp(bin_filename, "-") == 0) {
278 bin_fd = STDOUT_FILENO;
279 } else {
Mike Frysinger4f7136e2012-07-18 16:59:45 +0000280 bin_fd = creat(bin_filename, S_IRUSR | S_IWUSR | S_IRGRP |
281 S_IWGRP);
David Wagner48995b52012-01-13 13:27:37 +0000282 if (bin_fd == -1) {
283 fprintf(stderr, "Can't open output file \"%s\": %s\n",
284 bin_filename, strerror(errno));
285 return EXIT_FAILURE;
286 }
David Wagnera6337e62011-09-26 03:26:34 +0000287 }
288
289 if (write(bin_fd, dataptr, sizeof(*dataptr) * datasize) !=
290 sizeof(*dataptr) * datasize) {
291 fprintf(stderr, "write() failed: %s\n", strerror(errno));
292 return EXIT_FAILURE;
293 }
294
295 ret = close(bin_fd);
296
297 return ret;
298}