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