root/tools/firmware/ihex2fw.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nybble
  2. hex
  3. usage
  4. main
  5. process_ihex
  6. file_record
  7. ihex_binrec_size
  8. output_records

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Parser/loader for IHEX formatted data.
   4  *
   5  * Copyright © 2008 David Woodhouse <dwmw2@infradead.org>
   6  * Copyright © 2005 Jan Harkes <jaharkes@cs.cmu.edu>
   7  */
   8 
   9 #include <stdint.h>
  10 #include <arpa/inet.h>
  11 #include <stdio.h>
  12 #include <errno.h>
  13 #include <sys/types.h>
  14 #include <sys/stat.h>
  15 #include <sys/mman.h>
  16 #include <fcntl.h>
  17 #include <string.h>
  18 #include <unistd.h>
  19 #include <stdlib.h>
  20 #define _GNU_SOURCE
  21 #include <getopt.h>
  22 
  23 
  24 #define __ALIGN_KERNEL_MASK(x, mask)    (((x) + (mask)) & ~(mask))
  25 #define __ALIGN_KERNEL(x, a)            __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
  26 #define ALIGN(x, a)                     __ALIGN_KERNEL((x), (a))
  27 
  28 struct ihex_binrec {
  29         struct ihex_binrec *next; /* not part of the real data structure */
  30         uint32_t addr;
  31         uint16_t len;
  32         uint8_t data[];
  33 };
  34 
  35 /**
  36  * nybble/hex are little helpers to parse hexadecimal numbers to a byte value
  37  **/
  38 static uint8_t nybble(const uint8_t n)
  39 {
  40         if      (n >= '0' && n <= '9') return n - '0';
  41         else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
  42         else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
  43         return 0;
  44 }
  45 
  46 static uint8_t hex(const uint8_t *data, uint8_t *crc)
  47 {
  48         uint8_t val = (nybble(data[0]) << 4) | nybble(data[1]);
  49         *crc += val;
  50         return val;
  51 }
  52 
  53 static int process_ihex(uint8_t *data, ssize_t size);
  54 static void file_record(struct ihex_binrec *record);
  55 static int output_records(int outfd);
  56 
  57 static int sort_records = 0;
  58 static int wide_records = 0;
  59 static int include_jump = 0;
  60 
  61 static int usage(void)
  62 {
  63         fprintf(stderr, "ihex2fw: Convert ihex files into binary "
  64                 "representation for use by Linux kernel\n");
  65         fprintf(stderr, "usage: ihex2fw [<options>] <src.HEX> <dst.fw>\n");
  66         fprintf(stderr, "       -w: wide records (16-bit length)\n");
  67         fprintf(stderr, "       -s: sort records by address\n");
  68         fprintf(stderr, "       -j: include records for CS:IP/EIP address\n");
  69         return 1;
  70 }
  71 
  72 int main(int argc, char **argv)
  73 {
  74         int infd, outfd;
  75         struct stat st;
  76         uint8_t *data;
  77         int opt;
  78 
  79         while ((opt = getopt(argc, argv, "wsj")) != -1) {
  80                 switch (opt) {
  81                 case 'w':
  82                         wide_records = 1;
  83                         break;
  84                 case 's':
  85                         sort_records = 1;
  86                         break;
  87                 case 'j':
  88                         include_jump = 1;
  89                         break;
  90                 default:
  91                         return usage();
  92                 }
  93         }
  94 
  95         if (optind + 2 != argc)
  96                 return usage();
  97 
  98         if (!strcmp(argv[optind], "-"))
  99                 infd = 0;
 100         else
 101                 infd = open(argv[optind], O_RDONLY);
 102         if (infd == -1) {
 103                 fprintf(stderr, "Failed to open source file: %s",
 104                         strerror(errno));
 105                 return usage();
 106         }
 107         if (fstat(infd, &st)) {
 108                 perror("stat");
 109                 return 1;
 110         }
 111         data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, infd, 0);
 112         if (data == MAP_FAILED) {
 113                 perror("mmap");
 114                 return 1;
 115         }
 116 
 117         if (!strcmp(argv[optind+1], "-"))
 118                 outfd = 1;
 119         else
 120                 outfd = open(argv[optind+1], O_TRUNC|O_CREAT|O_WRONLY, 0644);
 121         if (outfd == -1) {
 122                 fprintf(stderr, "Failed to open destination file: %s",
 123                         strerror(errno));
 124                 return usage();
 125         }
 126         if (process_ihex(data, st.st_size))
 127                 return 1;
 128 
 129         return output_records(outfd);
 130 }
 131 
 132 static int process_ihex(uint8_t *data, ssize_t size)
 133 {
 134         struct ihex_binrec *record;
 135         size_t record_size;
 136         uint32_t offset = 0;
 137         uint32_t data32;
 138         uint8_t type, crc = 0, crcbyte = 0;
 139         int i, j;
 140         int line = 1;
 141         int len;
 142 
 143         i = 0;
 144 next_record:
 145         /* search for the start of record character */
 146         while (i < size) {
 147                 if (data[i] == '\n') line++;
 148                 if (data[i++] == ':') break;
 149         }
 150 
 151         /* Minimum record length would be about 10 characters */
 152         if (i + 10 > size) {
 153                 fprintf(stderr, "Can't find valid record at line %d\n", line);
 154                 return -EINVAL;
 155         }
 156 
 157         len = hex(data + i, &crc); i += 2;
 158         if (wide_records) {
 159                 len <<= 8;
 160                 len += hex(data + i, &crc); i += 2;
 161         }
 162         record_size = ALIGN(sizeof(*record) + len, 4);
 163         record = malloc(record_size);
 164         if (!record) {
 165                 fprintf(stderr, "out of memory for records\n");
 166                 return -ENOMEM;
 167         }
 168         memset(record, 0, record_size);
 169         record->len = len;
 170 
 171         /* now check if we have enough data to read everything */
 172         if (i + 8 + (record->len * 2) > size) {
 173                 fprintf(stderr, "Not enough data to read complete record at line %d\n",
 174                         line);
 175                 return -EINVAL;
 176         }
 177 
 178         record->addr  = hex(data + i, &crc) << 8; i += 2;
 179         record->addr |= hex(data + i, &crc); i += 2;
 180         type = hex(data + i, &crc); i += 2;
 181 
 182         for (j = 0; j < record->len; j++, i += 2)
 183                 record->data[j] = hex(data + i, &crc);
 184 
 185         /* check CRC */
 186         crcbyte = hex(data + i, &crc); i += 2;
 187         if (crc != 0) {
 188                 fprintf(stderr, "CRC failure at line %d: got 0x%X, expected 0x%X\n",
 189                         line, crcbyte, (unsigned char)(crcbyte-crc));
 190                 return -EINVAL;
 191         }
 192 
 193         /* Done reading the record */
 194         switch (type) {
 195         case 0:
 196                 /* old style EOF record? */
 197                 if (!record->len)
 198                         break;
 199 
 200                 record->addr += offset;
 201                 file_record(record);
 202                 goto next_record;
 203 
 204         case 1: /* End-Of-File Record */
 205                 if (record->addr || record->len) {
 206                         fprintf(stderr, "Bad EOF record (type 01) format at line %d",
 207                                 line);
 208                         return -EINVAL;
 209                 }
 210                 break;
 211 
 212         case 2: /* Extended Segment Address Record (HEX86) */
 213         case 4: /* Extended Linear Address Record (HEX386) */
 214                 if (record->addr || record->len != 2) {
 215                         fprintf(stderr, "Bad HEX86/HEX386 record (type %02X) at line %d\n",
 216                                 type, line);
 217                         return -EINVAL;
 218                 }
 219 
 220                 /* We shouldn't really be using the offset for HEX86 because
 221                  * the wraparound case is specified quite differently. */
 222                 offset = record->data[0] << 8 | record->data[1];
 223                 offset <<= (type == 2 ? 4 : 16);
 224                 goto next_record;
 225 
 226         case 3: /* Start Segment Address Record */
 227         case 5: /* Start Linear Address Record */
 228                 if (record->addr || record->len != 4) {
 229                         fprintf(stderr, "Bad Start Address record (type %02X) at line %d\n",
 230                                 type, line);
 231                         return -EINVAL;
 232                 }
 233 
 234                 memcpy(&data32, &record->data[0], sizeof(data32));
 235                 data32 = htonl(data32);
 236                 memcpy(&record->data[0], &data32, sizeof(data32));
 237 
 238                 /* These records contain the CS/IP or EIP where execution
 239                  * starts. If requested output this as a record. */
 240                 if (include_jump)
 241                         file_record(record);
 242                 goto next_record;
 243 
 244         default:
 245                 fprintf(stderr, "Unknown record (type %02X)\n", type);
 246                 return -EINVAL;
 247         }
 248 
 249         return 0;
 250 }
 251 
 252 static struct ihex_binrec *records;
 253 
 254 static void file_record(struct ihex_binrec *record)
 255 {
 256         struct ihex_binrec **p = &records;
 257 
 258         while ((*p) && (!sort_records || (*p)->addr < record->addr))
 259                 p = &((*p)->next);
 260 
 261         record->next = *p;
 262         *p = record;
 263 }
 264 
 265 static uint16_t ihex_binrec_size(struct ihex_binrec *p)
 266 {
 267         return p->len + sizeof(p->addr) + sizeof(p->len);
 268 }
 269 
 270 static int output_records(int outfd)
 271 {
 272         unsigned char zeroes[6] = {0, 0, 0, 0, 0, 0};
 273         struct ihex_binrec *p = records;
 274 
 275         while (p) {
 276                 uint16_t writelen = ALIGN(ihex_binrec_size(p), 4);
 277 
 278                 p->addr = htonl(p->addr);
 279                 p->len = htons(p->len);
 280                 if (write(outfd, &p->addr, writelen) != writelen)
 281                         return 1;
 282                 p = p->next;
 283         }
 284         /* EOF record is zero length, since we don't bother to represent
 285            the type field in the binary version */
 286         if (write(outfd, zeroes, 6) != 6)
 287                 return 1;
 288         return 0;
 289 }

/* [<][>][^][v][top][bottom][index][help] */