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; 58static int include_jump = 0; 59 60static 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 71int 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 default: 90 return usage(); 91 } 92 } 93 94 if (optind + 2 != argc) 95 return usage(); 96 97 if (!strcmp(argv[optind], "-")) 98 infd = 0; 99 else 100 infd = open(argv[optind], O_RDONLY); 101 if (infd == -1) { 102 fprintf(stderr, "Failed to open source file: %s", 103 strerror(errno)); 104 return usage(); 105 } 106 if (fstat(infd, &st)) { 107 perror("stat"); 108 return 1; 109 } 110 data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, infd, 0); 111 if (data == MAP_FAILED) { 112 perror("mmap"); 113 return 1; 114 } 115 116 if (!strcmp(argv[optind+1], "-")) 117 outfd = 1; 118 else 119 outfd = open(argv[optind+1], O_TRUNC|O_CREAT|O_WRONLY, 0644); 120 if (outfd == -1) { 121 fprintf(stderr, "Failed to open destination file: %s", 122 strerror(errno)); 123 return usage(); 124 } 125 if (process_ihex(data, st.st_size)) 126 return 1; 127 128 return output_records(outfd); 129} 130 131static int process_ihex(uint8_t *data, ssize_t size) 132{ 133 struct ihex_binrec *record; 134 uint32_t offset = 0; 135 uint32_t data32; 136 uint8_t type, crc = 0, crcbyte = 0; 137 int i, j; 138 int line = 1; 139 int len; 140 141 i = 0; 142next_record: 143 /* search for the start of record character */ 144 while (i < size) { 145 if (data[i] == '\n') line++; 146 if (data[i++] == ':') break; 147 } 148 149 /* Minimum record length would be about 10 characters */ 150 if (i + 10 > size) { 151 fprintf(stderr, "Can't find valid record at line %d\n", line); 152 return -EINVAL; 153 } 154 155 len = hex(data + i, &crc); i += 2; 156 if (wide_records) { 157 len <<= 8; 158 len += hex(data + i, &crc); i += 2; 159 } 160 record = malloc((sizeof (*record) + len + 3) & ~3); 161 if (!record) { 162 fprintf(stderr, "out of memory for records\n"); 163 return -ENOMEM; 164 } 165 memset(record, 0, (sizeof(*record) + len + 3) & ~3); 166 record->len = len; 167 168 /* now check if we have enough data to read everything */ 169 if (i + 8 + (record->len * 2) > size) { 170 fprintf(stderr, "Not enough data to read complete record at line %d\n", 171 line); 172 return -EINVAL; 173 } 174 175 record->addr = hex(data + i, &crc) << 8; i += 2; 176 record->addr |= hex(data + i, &crc); i += 2; 177 type = hex(data + i, &crc); i += 2; 178 179 for (j = 0; j < record->len; j++, i += 2) 180 record->data[j] = hex(data + i, &crc); 181 182 /* check CRC */ 183 crcbyte = hex(data + i, &crc); i += 2; 184 if (crc != 0) { 185 fprintf(stderr, "CRC failure at line %d: got 0x%X, expected 0x%X\n", 186 line, crcbyte, (unsigned char)(crcbyte-crc)); 187 return -EINVAL; 188 } 189 190 /* Done reading the record */ 191 switch (type) { 192 case 0: 193 /* old style EOF record? */ 194 if (!record->len) 195 break; 196 197 record->addr += offset; 198 file_record(record); 199 goto next_record; 200 201 case 1: /* End-Of-File Record */ 202 if (record->addr || record->len) { 203 fprintf(stderr, "Bad EOF record (type 01) format at line %d", 204 line); 205 return -EINVAL; 206 } 207 break; 208 209 case 2: /* Extended Segment Address Record (HEX86) */ 210 case 4: /* Extended Linear Address Record (HEX386) */ 211 if (record->addr || record->len != 2) { 212 fprintf(stderr, "Bad HEX86/HEX386 record (type %02X) at line %d\n", 213 type, line); 214 return -EINVAL; 215 } 216 217 /* We shouldn't really be using the offset for HEX86 because 218 * the wraparound case is specified quite differently. */ 219 offset = record->data[0] << 8 | record->data[1]; 220 offset <<= (type == 2 ? 4 : 16); 221 goto next_record; 222 223 case 3: /* Start Segment Address Record */ 224 case 5: /* Start Linear Address Record */ 225 if (record->addr || record->len != 4) { 226 fprintf(stderr, "Bad Start Address record (type %02X) at line %d\n", 227 type, line); 228 return -EINVAL; 229 } 230 231 memcpy(&data32, &record->data[0], sizeof(data32)); 232 data32 = htonl(data32); 233 memcpy(&record->data[0], &data32, sizeof(data32)); 234 235 /* These records contain the CS/IP or EIP where execution 236 * starts. If requested output this as a record. */ 237 if (include_jump) 238 file_record(record); 239 goto next_record; 240 241 default: 242 fprintf(stderr, "Unknown record (type %02X)\n", type); 243 return -EINVAL; 244 } 245 246 return 0; 247} 248 249static struct ihex_binrec *records; 250 251static void file_record(struct ihex_binrec *record) 252{ 253 struct ihex_binrec **p = &records; 254 255 while ((*p) && (!sort_records || (*p)->addr < record->addr)) 256 p = &((*p)->next); 257 258 record->next = *p; 259 *p = record; 260} 261 262static int output_records(int outfd) 263{ 264 unsigned char zeroes[6] = {0, 0, 0, 0, 0, 0}; 265 struct ihex_binrec *p = records; 266 267 while (p) { 268 uint16_t writelen = (p->len + 9) & ~3; 269 270 p->addr = htonl(p->addr); 271 p->len = htons(p->len); 272 if (write(outfd, &p->addr, writelen) != writelen) 273 return 1; 274 p = p->next; 275 } 276 /* EOF record is zero length, since we don't bother to represent 277 the type field in the binary version */ 278 if (write(outfd, zeroes, 6) != 6) 279 return 1; 280 return 0; 281} 282