Commit 2473238eac95ba6dd2c4ba19cc36aaf01465076b
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 |
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698
-
mentioned in commit d43698