Linux Audio

Check our new training course

Loading...
  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}