ihex2fw.c 5.8 KB

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