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