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