Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

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