/firmware/ihex2fw.c
C | 280 lines | 216 code | 39 blank | 25 comment | 55 complexity | 4b1ef3e4ed575246ec40bbb6214c587b MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
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 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 130static 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; 141next_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 248static struct ihex_binrec *records; 249 250static 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 261static 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}