uboot/tools/mkenvimage.c
<<
>>
Prefs
   1/*
   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 *
   9 * See file CREDITS for list of people who contributed to this
  10 * project.
  11 *
  12 * This program is free software; you can redistribute it and/or
  13 * modify it under the terms of the GNU General Public License as
  14 * published by the Free Software Foundation; either version 2 of
  15 * the License, or (at your option) any later version.
  16 *
  17 * This program is distributed in the hope that it will be useful,
  18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20 * GNU General Public License for more details.
  21 *
  22 * You should have received a copy of the GNU General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  25 * MA 02111-1307 USA
  26 */
  27
  28/* We want the GNU version of basename() */
  29#define _GNU_SOURCE
  30
  31#include <errno.h>
  32#include <fcntl.h>
  33#include <stdio.h>
  34#include <stdlib.h>
  35#include <stdint.h>
  36#include <string.h>
  37#include <unistd.h>
  38#include <sys/types.h>
  39#include <sys/stat.h>
  40#include <sys/mman.h>
  41
  42#include "compiler.h"
  43#include <u-boot/crc.h>
  44#include <version.h>
  45
  46#define CRC_SIZE sizeof(uint32_t)
  47
  48static void usage(const char *exec_name)
  49{
  50        fprintf(stderr, "%s [-h] [-r] [-b] [-p <byte>] -s <environment partition size> -o <output> <input file>\n"
  51               "\n"
  52               "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"
  53               "\n"
  54               "\tThe input file is in format:\n"
  55               "\t\tkey1=value1\n"
  56               "\t\tkey2=value2\n"
  57               "\t\t...\n"
  58               "\t-r : the environment has multiple copies in flash\n"
  59               "\t-b : the target is big endian (default is little endian)\n"
  60               "\t-p <byte> : fill the image with <byte> bytes instead of 0xff bytes\n"
  61               "\t-V : print version information and exit\n"
  62               "\n"
  63               "If the input file is \"-\", data is read from standard input\n",
  64               exec_name);
  65}
  66
  67long int xstrtol(const char *s)
  68{
  69        long int tmp;
  70
  71        errno = 0;
  72        tmp = strtol(s, NULL, 0);
  73        if (!errno)
  74                return tmp;
  75
  76        if (errno == ERANGE)
  77                fprintf(stderr, "Bad integer format: %s\n",  s);
  78        else
  79                fprintf(stderr, "Error while parsing %s: %s\n", s,
  80                                strerror(errno));
  81
  82        exit(EXIT_FAILURE);
  83}
  84
  85int main(int argc, char **argv)
  86{
  87        uint32_t crc, targetendian_crc;
  88        const char *txt_filename = NULL, *bin_filename = NULL;
  89        int txt_fd, bin_fd;
  90        unsigned char *dataptr, *envptr;
  91        unsigned char *filebuf = NULL;
  92        unsigned int filesize = 0, envsize = 0, datasize = 0;
  93        int bigendian = 0;
  94        int redundant = 0;
  95        unsigned char padbyte = 0xff;
  96
  97        int option;
  98        int ret = EXIT_SUCCESS;
  99
 100        struct stat txt_file_stat;
 101
 102        int fp, ep;
 103        const char *prg;
 104
 105        prg = basename(argv[0]);
 106
 107        /* Turn off getopt()'s internal error message */
 108        opterr = 0;
 109
 110        /* Parse the cmdline */
 111        while ((option = getopt(argc, argv, ":s:o:rbp:hV")) != -1) {
 112                switch (option) {
 113                case 's':
 114                        datasize = xstrtol(optarg);
 115                        break;
 116                case 'o':
 117                        bin_filename = strdup(optarg);
 118                        if (!bin_filename) {
 119                                fprintf(stderr, "Can't strdup() the output filename\n");
 120                                return EXIT_FAILURE;
 121                        }
 122                        break;
 123                case 'r':
 124                        redundant = 1;
 125                        break;
 126                case 'b':
 127                        bigendian = 1;
 128                        break;
 129                case 'p':
 130                        padbyte = xstrtol(optarg);
 131                        break;
 132                case 'h':
 133                        usage(prg);
 134                        return EXIT_SUCCESS;
 135                case 'V':
 136                        printf("%s version %s\n", prg, PLAIN_VERSION);
 137                        return EXIT_SUCCESS;
 138                case ':':
 139                        fprintf(stderr, "Missing argument for option -%c\n",
 140                                optopt);
 141                        usage(prg);
 142                        return EXIT_FAILURE;
 143                default:
 144                        fprintf(stderr, "Wrong option -%c\n", optopt);
 145                        usage(prg);
 146                        return EXIT_FAILURE;
 147                }
 148        }
 149
 150        /* Check datasize and allocate the data */
 151        if (datasize == 0) {
 152                fprintf(stderr, "Please specify the size of the environment partition.\n");
 153                usage(prg);
 154                return EXIT_FAILURE;
 155        }
 156
 157        dataptr = malloc(datasize * sizeof(*dataptr));
 158        if (!dataptr) {
 159                fprintf(stderr, "Can't alloc %d bytes for dataptr.\n",
 160                                datasize);
 161                return EXIT_FAILURE;
 162        }
 163
 164        /*
 165         * envptr points to the beginning of the actual environment (after the
 166         * crc and possible `redundant' byte
 167         */
 168        envsize = datasize - (CRC_SIZE + redundant);
 169        envptr = dataptr + CRC_SIZE + redundant;
 170
 171        /* Pad the environment with the padding byte */
 172        memset(envptr, padbyte, envsize);
 173
 174        /* Open the input file ... */
 175        if (optind >= argc || strcmp(argv[optind], "-") == 0) {
 176                int readbytes = 0;
 177                int readlen = sizeof(*envptr) * 4096;
 178                txt_fd = STDIN_FILENO;
 179
 180                do {
 181                        filebuf = realloc(filebuf, readlen);
 182                        if (!filebuf) {
 183                                fprintf(stderr, "Can't realloc memory for the input file buffer\n");
 184                                return EXIT_FAILURE;
 185                        }
 186                        readbytes = read(txt_fd, filebuf + filesize, readlen);
 187                        if (errno) {
 188                                fprintf(stderr, "Error while reading stdin: %s\n",
 189                                                strerror(errno));
 190                                return EXIT_FAILURE;
 191                        }
 192                        filesize += readbytes;
 193                } while (readbytes == readlen);
 194
 195        } else {
 196                txt_filename = argv[optind];
 197                txt_fd = open(txt_filename, O_RDONLY);
 198                if (txt_fd == -1) {
 199                        fprintf(stderr, "Can't open \"%s\": %s\n",
 200                                        txt_filename, strerror(errno));
 201                        return EXIT_FAILURE;
 202                }
 203                /* ... and check it */
 204                ret = fstat(txt_fd, &txt_file_stat);
 205                if (ret == -1) {
 206                        fprintf(stderr, "Can't stat() on \"%s\": %s\n",
 207                                        txt_filename, strerror(errno));
 208                        return EXIT_FAILURE;
 209                }
 210
 211                filesize = txt_file_stat.st_size;
 212
 213                filebuf = mmap(NULL, sizeof(*envptr) * filesize, PROT_READ,
 214                               MAP_PRIVATE, txt_fd, 0);
 215                if (filebuf == MAP_FAILED) {
 216                        fprintf(stderr, "mmap (%zu bytes) failed: %s\n",
 217                                        sizeof(*envptr) * filesize,
 218                                        strerror(errno));
 219                        fprintf(stderr, "Falling back to read()\n");
 220
 221                        filebuf = malloc(sizeof(*envptr) * filesize);
 222                        ret = read(txt_fd, filebuf, sizeof(*envptr) * filesize);
 223                        if (ret != sizeof(*envptr) * filesize) {
 224                                fprintf(stderr, "Can't read the whole input file (%zu bytes): %s\n",
 225                                        sizeof(*envptr) * filesize,
 226                                        strerror(errno));
 227
 228                                return EXIT_FAILURE;
 229                        }
 230                }
 231                ret = close(txt_fd);
 232        }
 233        /* The +1 is for the additionnal ending \0. See below. */
 234        if (filesize + 1 > envsize) {
 235                fprintf(stderr, "The input file is larger than the environment partition size\n");
 236                return EXIT_FAILURE;
 237        }
 238
 239        /* Replace newlines separating variables with \0 */
 240        for (fp = 0, ep = 0 ; fp < filesize ; fp++) {
 241                if (filebuf[fp] == '\n') {
 242                        if (ep == 0) {
 243                                /*
 244                                 * Newlines at the beginning of the file ?
 245                                 * Ignore them.
 246                                 */
 247                                continue;
 248                        } else if (filebuf[fp-1] == '\\') {
 249                                /*
 250                                 * Embedded newline in a variable.
 251                                 *
 252                                 * The backslash was added to the envptr; rewind
 253                                 * and replace it with a newline
 254                                 */
 255                                ep--;
 256                                envptr[ep++] = '\n';
 257                        } else {
 258                                /* End of a variable */
 259                                envptr[ep++] = '\0';
 260                        }
 261                } else {
 262                        envptr[ep++] = filebuf[fp];
 263                }
 264        }
 265        /*
 266         * Make sure there is a final '\0'
 267         * And do it again on the next byte to mark the end of the environment.
 268         */
 269        if (envptr[ep-1] != '\0') {
 270                envptr[ep++] = '\0';
 271                /*
 272                 * The text file doesn't have an ending newline.  We need to
 273                 * check the env size again to make sure we have room for two \0
 274                 */
 275                if (ep >= envsize) {
 276                        fprintf(stderr, "The environment file is too large for the target environment storage\n");
 277                        return EXIT_FAILURE;
 278                }
 279                envptr[ep] = '\0';
 280        } else {
 281                envptr[ep] = '\0';
 282        }
 283
 284        /* Computes the CRC and put it at the beginning of the data */
 285        crc = crc32(0, envptr, envsize);
 286        targetendian_crc = bigendian ? cpu_to_be32(crc) : cpu_to_le32(crc);
 287
 288        memcpy(dataptr, &targetendian_crc, sizeof(targetendian_crc));
 289        if (redundant)
 290                dataptr[sizeof(targetendian_crc)] = 1;
 291
 292        if (!bin_filename || strcmp(bin_filename, "-") == 0) {
 293                bin_fd = STDOUT_FILENO;
 294        } else {
 295                bin_fd = creat(bin_filename, S_IRUSR | S_IWUSR | S_IRGRP |
 296                                             S_IWGRP);
 297                if (bin_fd == -1) {
 298                        fprintf(stderr, "Can't open output file \"%s\": %s\n",
 299                                        bin_filename, strerror(errno));
 300                        return EXIT_FAILURE;
 301                }
 302        }
 303
 304        if (write(bin_fd, dataptr, sizeof(*dataptr) * datasize) !=
 305                        sizeof(*dataptr) * datasize) {
 306                fprintf(stderr, "write() failed: %s\n", strerror(errno));
 307                return EXIT_FAILURE;
 308        }
 309
 310        ret = close(bin_fd);
 311
 312        return ret;
 313}
 314