Back to home page

LXR

 
 

    


0001 /*
0002  * Parser/loader for IHEX formatted data.
0003  *
0004  * Copyright © 2008 David Woodhouse <dwmw2@infradead.org>
0005  * Copyright © 2005 Jan Harkes <jaharkes@cs.cmu.edu>
0006  *
0007  * This program is free software; you can redistribute it and/or modify
0008  * it under the terms of the GNU General Public License version 2 as
0009  * published by the Free Software Foundation.
0010  */
0011 
0012 #include <stdint.h>
0013 #include <arpa/inet.h>
0014 #include <stdio.h>
0015 #include <errno.h>
0016 #include <sys/types.h>
0017 #include <sys/stat.h>
0018 #include <sys/mman.h>
0019 #include <fcntl.h>
0020 #include <string.h>
0021 #include <unistd.h>
0022 #include <stdlib.h>
0023 #define _GNU_SOURCE
0024 #include <getopt.h>
0025 
0026 
0027 struct ihex_binrec {
0028     struct ihex_binrec *next; /* not part of the real data structure */
0029         uint32_t addr;
0030         uint16_t len;
0031         uint8_t data[];
0032 };
0033 
0034 /**
0035  * nybble/hex are little helpers to parse hexadecimal numbers to a byte value
0036  **/
0037 static uint8_t nybble(const uint8_t n)
0038 {
0039        if      (n >= '0' && n <= '9') return n - '0';
0040        else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
0041        else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
0042        return 0;
0043 }
0044 
0045 static uint8_t hex(const uint8_t *data, uint8_t *crc)
0046 {
0047        uint8_t val = (nybble(data[0]) << 4) | nybble(data[1]);
0048        *crc += val;
0049        return val;
0050 }
0051 
0052 static int process_ihex(uint8_t *data, ssize_t size);
0053 static void file_record(struct ihex_binrec *record);
0054 static int output_records(int outfd);
0055 
0056 static int sort_records = 0;
0057 static int wide_records = 0;
0058 static int include_jump = 0;
0059 
0060 static int usage(void)
0061 {
0062     fprintf(stderr, "ihex2fw: Convert ihex files into binary "
0063         "representation for use by Linux kernel\n");
0064     fprintf(stderr, "usage: ihex2fw [<options>] <src.HEX> <dst.fw>\n");
0065     fprintf(stderr, "       -w: wide records (16-bit length)\n");
0066     fprintf(stderr, "       -s: sort records by address\n");
0067     fprintf(stderr, "       -j: include records for CS:IP/EIP address\n");
0068     return 1;
0069 }
0070 
0071 int main(int argc, char **argv)
0072 {
0073     int infd, outfd;
0074     struct stat st;
0075     uint8_t *data;
0076     int opt;
0077 
0078     while ((opt = getopt(argc, argv, "wsj")) != -1) {
0079         switch (opt) {
0080         case 'w':
0081             wide_records = 1;
0082             break;
0083         case 's':
0084             sort_records = 1;
0085             break;
0086         case 'j':
0087             include_jump = 1;
0088             break;
0089         default:
0090             return usage();
0091         }
0092     }
0093 
0094     if (optind + 2 != argc)
0095         return usage();
0096 
0097     if (!strcmp(argv[optind], "-"))
0098         infd = 0;
0099     else
0100         infd = open(argv[optind], O_RDONLY);
0101     if (infd == -1) {
0102         fprintf(stderr, "Failed to open source file: %s",
0103             strerror(errno));
0104         return usage();
0105     }
0106     if (fstat(infd, &st)) {
0107         perror("stat");
0108         return 1;
0109     }
0110     data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, infd, 0);
0111     if (data == MAP_FAILED) {
0112         perror("mmap");
0113         return 1;
0114     }
0115 
0116     if (!strcmp(argv[optind+1], "-"))
0117         outfd = 1;
0118     else
0119         outfd = open(argv[optind+1], O_TRUNC|O_CREAT|O_WRONLY, 0644);
0120     if (outfd == -1) {
0121         fprintf(stderr, "Failed to open destination file: %s",
0122             strerror(errno));
0123         return usage();
0124     }
0125     if (process_ihex(data, st.st_size))
0126         return 1;
0127 
0128     return output_records(outfd);
0129 }
0130 
0131 static int process_ihex(uint8_t *data, ssize_t size)
0132 {
0133     struct ihex_binrec *record;
0134     uint32_t offset = 0;
0135     uint32_t data32;
0136     uint8_t type, crc = 0, crcbyte = 0;
0137     int i, j;
0138     int line = 1;
0139     int len;
0140 
0141     i = 0;
0142 next_record:
0143     /* search for the start of record character */
0144     while (i < size) {
0145         if (data[i] == '\n') line++;
0146         if (data[i++] == ':') break;
0147     }
0148 
0149     /* Minimum record length would be about 10 characters */
0150     if (i + 10 > size) {
0151         fprintf(stderr, "Can't find valid record at line %d\n", line);
0152         return -EINVAL;
0153     }
0154 
0155     len = hex(data + i, &crc); i += 2;
0156     if (wide_records) {
0157         len <<= 8;
0158         len += hex(data + i, &crc); i += 2;
0159     }
0160     record = malloc((sizeof (*record) + len + 3) & ~3);
0161     if (!record) {
0162         fprintf(stderr, "out of memory for records\n");
0163         return -ENOMEM;
0164     }
0165     memset(record, 0, (sizeof(*record) + len + 3) & ~3);
0166     record->len = len;
0167 
0168     /* now check if we have enough data to read everything */
0169     if (i + 8 + (record->len * 2) > size) {
0170         fprintf(stderr, "Not enough data to read complete record at line %d\n",
0171             line);
0172         return -EINVAL;
0173     }
0174 
0175     record->addr  = hex(data + i, &crc) << 8; i += 2;
0176     record->addr |= hex(data + i, &crc); i += 2;
0177     type = hex(data + i, &crc); i += 2;
0178 
0179     for (j = 0; j < record->len; j++, i += 2)
0180         record->data[j] = hex(data + i, &crc);
0181 
0182     /* check CRC */
0183     crcbyte = hex(data + i, &crc); i += 2;
0184     if (crc != 0) {
0185         fprintf(stderr, "CRC failure at line %d: got 0x%X, expected 0x%X\n",
0186             line, crcbyte, (unsigned char)(crcbyte-crc));
0187         return -EINVAL;
0188     }
0189 
0190     /* Done reading the record */
0191     switch (type) {
0192     case 0:
0193         /* old style EOF record? */
0194         if (!record->len)
0195             break;
0196 
0197         record->addr += offset;
0198         file_record(record);
0199         goto next_record;
0200 
0201     case 1: /* End-Of-File Record */
0202         if (record->addr || record->len) {
0203             fprintf(stderr, "Bad EOF record (type 01) format at line %d",
0204                 line);
0205             return -EINVAL;
0206         }
0207         break;
0208 
0209     case 2: /* Extended Segment Address Record (HEX86) */
0210     case 4: /* Extended Linear Address Record (HEX386) */
0211         if (record->addr || record->len != 2) {
0212             fprintf(stderr, "Bad HEX86/HEX386 record (type %02X) at line %d\n",
0213                 type, line);
0214             return -EINVAL;
0215         }
0216 
0217         /* We shouldn't really be using the offset for HEX86 because
0218          * the wraparound case is specified quite differently. */
0219         offset = record->data[0] << 8 | record->data[1];
0220         offset <<= (type == 2 ? 4 : 16);
0221         goto next_record;
0222 
0223     case 3: /* Start Segment Address Record */
0224     case 5: /* Start Linear Address Record */
0225         if (record->addr || record->len != 4) {
0226             fprintf(stderr, "Bad Start Address record (type %02X) at line %d\n",
0227                 type, line);
0228             return -EINVAL;
0229         }
0230 
0231         memcpy(&data32, &record->data[0], sizeof(data32));
0232         data32 = htonl(data32);
0233         memcpy(&record->data[0], &data32, sizeof(data32));
0234 
0235         /* These records contain the CS/IP or EIP where execution
0236          * starts. If requested output this as a record. */
0237         if (include_jump)
0238             file_record(record);
0239         goto next_record;
0240 
0241     default:
0242         fprintf(stderr, "Unknown record (type %02X)\n", type);
0243         return -EINVAL;
0244     }
0245 
0246     return 0;
0247 }
0248 
0249 static struct ihex_binrec *records;
0250 
0251 static void file_record(struct ihex_binrec *record)
0252 {
0253     struct ihex_binrec **p = &records;
0254 
0255     while ((*p) && (!sort_records || (*p)->addr < record->addr))
0256         p = &((*p)->next);
0257 
0258     record->next = *p;
0259     *p = record;
0260 }
0261 
0262 static int output_records(int outfd)
0263 {
0264     unsigned char zeroes[6] = {0, 0, 0, 0, 0, 0};
0265     struct ihex_binrec *p = records;
0266 
0267     while (p) {
0268         uint16_t writelen = (p->len + 9) & ~3;
0269 
0270         p->addr = htonl(p->addr);
0271         p->len = htons(p->len);
0272         if (write(outfd, &p->addr, writelen) != writelen)
0273             return 1;
0274         p = p->next;
0275     }
0276     /* EOF record is zero length, since we don't bother to represent
0277        the type field in the binary version */
0278     if (write(outfd, zeroes, 6) != 6)
0279         return 1;
0280     return 0;
0281 }