Commit 2473238eac95ba6dd2c4ba19cc36aaf01465076b

Authored by Mark Brown
Committed by Linus Torvalds
1 parent b6777c40c7

ihex: add support for CS:IP/EIP records

ihex firmwares can include a jump address for starting execution.  Add a
-j option which will cause this to be written into the generated file as a
record with address zero and data consisting of the address to jump to,
allowing drivers to make use of this information.

This format is chosen because it most closely follows the original ihex
format, though it may make more sense to write a record with length zero
and the address stored as the address.  The records are not omitted by
default since our ihex format does not include record type information and
so including additional records may lead to confusion.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 14 additions and 3 deletions Inline Diff

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