diff options
Diffstat (limited to 'tools/mkenvimage.c')
-rw-r--r-- | tools/mkenvimage.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/tools/mkenvimage.c b/tools/mkenvimage.c new file mode 100644 index 0000000..9c32f4a --- /dev/null +++ b/tools/mkenvimage.c @@ -0,0 +1,270 @@ +/* + * (C) Copyright 2011 Free Electrons + * David Wagner <david.wagner@free-electrons.com> + * + * Inspired from envcrc.c: + * (C) Copyright 2001 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <compiler.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <u-boot/crc.h> + +#define CRC_SIZE sizeof(uint32_t) + +static void usage(const char *exec_name) +{ + fprintf(stderr, "%s [-h] [-r] [-b] [-p <byte>] " + "-s <environment partition size> -o <output> <input file>\n" + "\n" + "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" + "\n" + "\tThe input file is in format:\n" + "\t\tkey1=value1\n" + "\t\tkey2=value2\n" + "\t\t...\n" + "\t-r : the environment has multiple copies in flash\n" + "\t-b : the target is big endian (default is little endian)\n" + "\t-p <byte> : fill the image with <byte> bytes instead of " + "0xff bytes\n" + "\n" + "If the input file is \"-\", data is read from standard input\n", + exec_name); +} + +int main(int argc, char **argv) +{ + uint32_t crc, targetendian_crc; + const char *txt_filename = NULL, *bin_filename = NULL; + int txt_fd, bin_fd; + unsigned char *dataptr, *envptr; + unsigned char *filebuf = NULL; + unsigned int filesize = 0, envsize = 0, datasize = 0; + int bigendian = 0; + int redundant = 0; + unsigned char padbyte = 0xff; + + int option; + int ret = EXIT_SUCCESS; + + struct stat txt_file_stat; + + int fp, ep; + + /* Parse the cmdline */ + while ((option = getopt(argc, argv, "s:o:rbp:h")) != -1) { + switch (option) { + case 's': + datasize = strtol(optarg, NULL, 0); + break; + case 'o': + bin_filename = strdup(optarg); + if (!bin_filename) { + fprintf(stderr, "Can't strdup() the output " + "filename\n"); + return EXIT_FAILURE; + } + break; + case 'r': + redundant = 1; + break; + case 'b': + bigendian = 1; + break; + case 'p': + padbyte = strtol(optarg, NULL, 0); + break; + case 'h': + usage(argv[0]); + return EXIT_SUCCESS; + default: + fprintf(stderr, "Wrong option -%c\n", option); + usage(argv[0]); + return EXIT_FAILURE; + } + } + + /* Check datasize and allocate the data */ + if (datasize == 0) { + fprintf(stderr, + "Please specify the size of the envrionnment " + "partition.\n"); + usage(argv[0]); + return EXIT_FAILURE; + } + + dataptr = malloc(datasize * sizeof(*dataptr)); + if (!dataptr) { + fprintf(stderr, "Can't alloc dataptr.\n"); + return EXIT_FAILURE; + } + + /* + * envptr points to the beginning of the actual environment (after the + * crc and possible `redundant' bit + */ + envsize = datasize - (CRC_SIZE + redundant); + envptr = dataptr + CRC_SIZE + redundant; + + /* Pad the environment with the padding byte */ + memset(envptr, padbyte, envsize); + + /* Open the input file ... */ + if (optind >= argc) { + fprintf(stderr, "Please specify an input filename\n"); + return EXIT_FAILURE; + } + + txt_filename = argv[optind]; + if (strcmp(txt_filename, "-") == 0) { + int readbytes = 0; + int readlen = sizeof(*envptr) * 2048; + txt_fd = STDIN_FILENO; + + do { + filebuf = realloc(filebuf, readlen); + readbytes = read(txt_fd, filebuf + filesize, readlen); + filesize += readbytes; + } while (readbytes == readlen); + + } else { + txt_fd = open(txt_filename, O_RDONLY); + if (txt_fd == -1) { + fprintf(stderr, "Can't open \"%s\": %s\n", + txt_filename, strerror(errno)); + return EXIT_FAILURE; + } + /* ... and check it */ + ret = fstat(txt_fd, &txt_file_stat); + if (ret == -1) { + fprintf(stderr, "Can't stat() on \"%s\": " + "%s\n", txt_filename, strerror(errno)); + return EXIT_FAILURE; + } + + filesize = txt_file_stat.st_size; + /* Read the raw input file and transform it */ + filebuf = malloc(sizeof(*envptr) * filesize); + ret = read(txt_fd, filebuf, sizeof(*envptr) * filesize); + if (ret != sizeof(*envptr) * filesize) { + fprintf(stderr, "Can't read the whole input file\n"); + return EXIT_FAILURE; + } + ret = close(txt_fd); + } + /* + * The right test to do is "=>" (not ">") because of the additionnal + * ending \0. See below. + */ + if (filesize >= envsize) { + fprintf(stderr, "The input file is larger than the " + "envrionnment partition size\n"); + return EXIT_FAILURE; + } + + /* Replace newlines separating variables with \0 */ + for (fp = 0, ep = 0 ; fp < filesize ; fp++) { + if (filebuf[fp] == '\n') { + if (fp == 0) { + /* + * Newline at the beggining of the file ? + * Ignore it. + */ + continue; + } else if (filebuf[fp-1] == '\\') { + /* + * Embedded newline in a variable. + * + * The backslash was added to the envptr ; + * rewind and replace it with a newline + */ + ep--; + envptr[ep++] = '\n'; + } else { + /* End of a variable */ + envptr[ep++] = '\0'; + } + } else if (filebuf[fp] == '#') { + if (fp != 0 && filebuf[fp-1] == '\n') { + /* This line is a comment, let's skip it */ + while (fp < txt_file_stat.st_size && fp++ && + filebuf[fp] != '\n'); + } else { + envptr[ep++] = filebuf[fp]; + } + } else { + envptr[ep++] = filebuf[fp]; + } + } + /* + * Make sure there is a final '\0' + * And do it again on the next byte to mark the end of the environment. + */ + if (envptr[ep-1] != '\0') { + envptr[ep++] = '\0'; + /* + * The text file doesn't have an ending newline. We need to + * check the env size again to make sure we have room for two \0 + */ + if (ep >= envsize) { + fprintf(stderr, "The environment file is too large for " + "the target environment storage\n"); + return EXIT_FAILURE; + } + envptr[ep] = '\0'; + } else { + envptr[ep] = '\0'; + } + + /* Computes the CRC and put it at the beginning of the data */ + crc = crc32(0, envptr, envsize); + targetendian_crc = bigendian ? cpu_to_be32(crc) : cpu_to_le32(crc); + + memcpy(dataptr, &targetendian_crc, sizeof(uint32_t)); + + bin_fd = creat(bin_filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (bin_fd == -1) { + fprintf(stderr, "Can't open output file \"%s\": %s\n", + bin_filename, strerror(errno)); + return EXIT_FAILURE; + } + + if (write(bin_fd, dataptr, sizeof(*dataptr) * datasize) != + sizeof(*dataptr) * datasize) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + ret = close(bin_fd); + + return ret; +} |