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