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