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