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