firmware-utils: bcm4908img: tool adding BCM4908 image tail
[openwrt/openwrt.git] / tools / firmware-utils / src / bcm4908img.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
4 */
5
6 #include <byteswap.h>
7 #include <endian.h>
8 #include <errno.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15
16 #if !defined(__BYTE_ORDER)
17 #error "Unknown byte order"
18 #endif
19
20 #if __BYTE_ORDER == __BIG_ENDIAN
21 #define cpu_to_le32(x) bswap_32(x)
22 #define le32_to_cpu(x) bswap_32(x)
23 #elif __BYTE_ORDER == __LITTLE_ENDIAN
24 #define cpu_to_le32(x) (x)
25 #define le32_to_cpu(x) (x)
26 #else
27 #error "Unsupported endianness"
28 #endif
29
30 struct bcm4908img_tail {
31 uint32_t crc32;
32 uint32_t unk1;
33 uint32_t family;
34 uint32_t unk2;
35 uint32_t unk3;
36 };
37
38 char *pathname;
39 static size_t prefix_len;
40 static size_t suffix_len;
41
42 static inline size_t bcm4908img_min(size_t x, size_t y) {
43 return x < y ? x : y;
44 }
45
46 /**************************************************
47 * CRC32
48 **************************************************/
49
50 static const uint32_t crc32_tbl[] = {
51 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
52 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
53 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
54 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
55 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
56 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
57 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
58 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
59 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
60 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
61 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
62 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
63 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
64 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
65 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
66 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
67 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
68 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
69 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
70 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
71 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
72 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
73 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
74 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
75 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
76 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
77 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
78 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
79 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
80 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
81 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
82 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
83 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
84 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
85 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
86 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
87 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
88 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
89 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
90 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
91 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
92 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
93 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
94 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
95 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
96 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
97 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
98 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
99 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
100 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
101 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
102 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
103 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
104 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
105 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
106 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
107 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
108 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
109 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
110 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
111 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
112 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
113 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
114 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
115 };
116
117 uint32_t bcm4908img_crc32(uint32_t crc, uint8_t *buf, size_t len) {
118 while (len) {
119 crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8);
120 buf++;
121 len--;
122 }
123
124 return crc;
125 }
126
127 /**************************************************
128 * Check
129 **************************************************/
130
131 static void bcm4908img_check_parse_options(int argc, char **argv) {
132 int c;
133
134 while ((c = getopt(argc, argv, "p:s:")) != -1) {
135 switch (c) {
136 case 'p':
137 prefix_len = atoi(optarg);
138 break;
139 case 's':
140 suffix_len = atoi(optarg);
141 break;
142 }
143 }
144 }
145
146 static int bcm4908img_check(int argc, char **argv) {
147 struct bcm4908img_tail tail;
148 struct stat st;
149 uint8_t buf[1024];
150 uint32_t crc32;
151 size_t length;
152 size_t bytes;
153 FILE *fp;
154 int err = 0;
155
156 if (argc < 3) {
157 fprintf(stderr, "No BCM4908 image pathname passed\n");
158 err = -EINVAL;
159 goto out;
160 }
161 pathname = argv[2];
162
163 optind = 3;
164 bcm4908img_check_parse_options(argc, argv);
165
166 if (stat(pathname, &st)) {
167 fprintf(stderr, "Failed to stat %s\n", pathname);
168 err = -EIO;
169 goto out;
170 }
171
172 fp = fopen(pathname, "r");
173 if (!fp) {
174 fprintf(stderr, "Failed to open %s\n", pathname);
175 err = -EACCES;
176 goto out;
177 }
178
179 fseek(fp, prefix_len, SEEK_SET);
180
181 crc32 = 0xffffffff;
182 length = st.st_size - prefix_len - sizeof(tail) - suffix_len;
183 while (length && (bytes = fread(buf, 1, bcm4908img_min(sizeof(buf), length), fp)) > 0) {
184 crc32 = bcm4908img_crc32(crc32, buf, bytes);
185 length -= bytes;
186 }
187
188 if (length) {
189 fprintf(stderr, "Failed to read last %zd B of data from %s\n", length, pathname);
190 err = -EIO;
191 goto err_close;
192 }
193
194 if (fread(&tail, 1, sizeof(tail), fp) != sizeof(tail)) {
195 fprintf(stderr, "Failed to read BCM4908 image tail\n");
196 err = -EIO;
197 goto err_close;
198 }
199
200 if (crc32 != le32_to_cpu(tail.crc32)) {
201 fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(tail.crc32));
202 err = -EPROTO;
203 goto err_close;
204 }
205
206 printf("Found a valid BCM4908 image (crc: 0x%08x)\n", crc32);
207
208 err_close:
209 fclose(fp);
210 out:
211 return err;
212 }
213
214 /**************************************************
215 * Create
216 **************************************************/
217
218 static ssize_t bcm4908img_create_append_file(FILE *trx, const char *in_path, uint32_t *crc32) {
219 FILE *in;
220 size_t bytes;
221 ssize_t length = 0;
222 uint8_t buf[1024];
223
224 in = fopen(in_path, "r");
225 if (!in) {
226 fprintf(stderr, "Failed to open %s\n", in_path);
227 return -EACCES;
228 }
229
230 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
231 if (fwrite(buf, 1, bytes, trx) != bytes) {
232 fprintf(stderr, "Failed to write %zu B to %s\n", bytes, pathname);
233 length = -EIO;
234 break;
235 }
236 *crc32 = bcm4908img_crc32(*crc32, buf, bytes);
237 length += bytes;
238 }
239
240 fclose(in);
241
242 return length;
243 }
244
245 static ssize_t bcm4908img_create_append_zeros(FILE *trx, size_t length) {
246 uint8_t *buf;
247
248 buf = malloc(length);
249 if (!buf)
250 return -ENOMEM;
251 memset(buf, 0, length);
252
253 if (fwrite(buf, 1, length, trx) != length) {
254 fprintf(stderr, "Failed to write %zu B to %s\n", length, pathname);
255 free(buf);
256 return -EIO;
257 }
258
259 free(buf);
260
261 return length;
262 }
263
264 static ssize_t bcm4908img_create_align(FILE *trx, size_t cur_offset, size_t alignment) {
265 if (cur_offset & (alignment - 1)) {
266 size_t length = alignment - (cur_offset % alignment);
267 return bcm4908img_create_append_zeros(trx, length);
268 }
269
270 return 0;
271 }
272
273 static int bcm4908img_create(int argc, char **argv) {
274 struct bcm4908img_tail tail = {
275 .unk1 = cpu_to_le32(0x5732),
276 .family = cpu_to_le32(0x4908),
277 .unk2 = cpu_to_le32(0x03),
278 .unk3 = cpu_to_le32(0x02),
279 };
280 uint32_t crc32 = 0xffffffff;
281 size_t cur_offset = 0;
282 ssize_t bytes;
283 FILE *fp;
284 int c;
285 int err = 0;
286
287 if (argc < 3) {
288 fprintf(stderr, "No BCM4908 image pathname passed\n");
289 err = -EINVAL;
290 goto out;
291 }
292 pathname = argv[2];
293
294 fp = fopen(pathname, "w+");
295 if (!fp) {
296 fprintf(stderr, "Failed to open %s\n", pathname);
297 err = -EACCES;
298 goto out;
299 }
300
301 optind = 3;
302 while ((c = getopt(argc, argv, "f:a:A:")) != -1) {
303 switch (c) {
304 case 'f':
305 bytes = bcm4908img_create_append_file(fp, optarg, &crc32);
306 if (bytes < 0) {
307 fprintf(stderr, "Failed to append file %s\n", optarg);
308 } else {
309 cur_offset += bytes;
310 }
311 break;
312 case 'a':
313 bytes = bcm4908img_create_align(fp, cur_offset, strtol(optarg, NULL, 0));
314 if (bytes < 0)
315 fprintf(stderr, "Failed to append zeros\n");
316 else
317 cur_offset += bytes;
318 break;
319 case 'A':
320 bytes = strtol(optarg, NULL, 0) - cur_offset;
321 if (bytes < 0) {
322 fprintf(stderr, "Current BCM4908 image length is 0x%zx, can't pad it with zeros to 0x%lx\n", cur_offset, strtol(optarg, NULL, 0));
323 } else {
324 bytes = bcm4908img_create_append_zeros(fp, bytes);
325 if (bytes < 0)
326 fprintf(stderr, "Failed to append zeros\n");
327 else
328 cur_offset += bytes;
329 }
330 break;
331 }
332 if (err)
333 goto err_close;
334 }
335
336 tail.crc32 = cpu_to_le32(crc32);
337
338 bytes = fwrite(&tail, 1, sizeof(tail), fp);
339 if (bytes != sizeof(tail)) {
340 fprintf(stderr, "Failed to write BCM4908 image tail to %s\n", pathname);
341 return -EIO;
342 }
343
344 err_close:
345 fclose(fp);
346 out:
347 return err;
348 }
349
350 /**************************************************
351 * Start
352 **************************************************/
353
354 static void usage() {
355 printf("Usage:\n");
356 printf("\n");
357 printf("Checking a BCM4908 image:\n");
358 printf("\tbcm4908img check <file> [options]\tcheck if images is valid\n");
359 printf("\t-p prefix\t\t\tlength of custom header to skip (default: 0)\n");
360 printf("\t-s suffix\t\t\tlength of custom tail to skip (default: 0)\n");
361 printf("\n");
362 printf("Creating a new BCM4908 image:\n");
363 printf("\tbcm4908img create <file> [options]\n");
364 printf("\t-f file\t\t\t\tadd data from specified file\n");
365 printf("\t-a alignment\t\t\tpad image with zeros to specified alignment\n");
366 printf("\t-A offset\t\t\t\tappend zeros until reaching specified offset\n");
367 }
368
369 int main(int argc, char **argv) {
370 if (argc > 1) {
371 if (!strcmp(argv[1], "check"))
372 return bcm4908img_check(argc, argv);
373 else if (!strcmp(argv[1], "create"))
374 return bcm4908img_create(argc, argv);
375 }
376
377 usage();
378 return 0;
379 }