firmware-utils: bcm4908kernel: tool adding BCM4908 kernel header
[openwrt/openwrt.git] / tools / firmware-utils / src / bcm4908kernel.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 <unistd.h>
14
15 #if !defined(__BYTE_ORDER)
16 #error "Unknown byte order"
17 #endif
18
19 #if __BYTE_ORDER == __BIG_ENDIAN
20 #define cpu_to_le32(x) bswap_32(x)
21 #define le32_to_cpu(x) bswap_32(x)
22 #elif __BYTE_ORDER == __LITTLE_ENDIAN
23 #define cpu_to_le32(x) (x)
24 #define le32_to_cpu(x) (x)
25 #else
26 #error "Unsupported endianness"
27 #endif
28
29 struct bcm4908kernel_header {
30 uint32_t unk1;
31 uint32_t unk2;
32 uint32_t length;
33 uint8_t magic[4];
34 uint32_t unused;
35 };
36
37 static void usage() {
38 printf("Usage:\n");
39 printf("\n");
40 printf("\t-i pathname\t\t\tinput kernel filepath\n");
41 printf("\t-o pathname\t\t\toutput kernel filepath\n");
42 }
43
44 int main(int argc, char **argv) {
45 struct bcm4908kernel_header header;
46 uint8_t buf[1024];
47 FILE *out = NULL;
48 FILE *in = NULL;
49 size_t length;
50 size_t bytes;
51 int err = 0;
52 char c;
53
54 if (argc >= 2 && !strcmp(argv[1], "-h")) {
55 usage();
56 return 0;
57 }
58
59 while ((c = getopt(argc, argv, "i:o:")) != -1) {
60 switch (c) {
61 case 'i':
62 in = fopen(optarg, "r");
63 break;
64 case 'o':
65 out = fopen(optarg, "w+");
66 break;
67 }
68 }
69
70 if (!in || !out) {
71 fprintf(stderr, "Failed to open input and/or output file\n");
72 usage();
73 return -EINVAL;
74 }
75
76 if (fread(&header, 1, sizeof(header), in) != sizeof(header)) {
77 fprintf(stderr, "Failed to read %zu bytes from input file\n", sizeof(header));
78 err = -EIO;
79 goto err_close;
80 }
81
82 if (!memcmp(header.magic, "BRCM", 4)) {
83 fprintf(stderr, "Input file already contains BCM4908 kernel header\n");
84 err = -EIO;
85 goto err_close;
86 }
87
88 err = fseek(out, sizeof(header), SEEK_SET);
89 if (err) {
90 err = -errno;
91 fprintf(stderr, "Failed to fseek(): %d\n", err);
92 goto err_close;
93 }
94
95 length = 0;
96 rewind(in);
97 while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) {
98 if (fwrite(buf, 1, bytes, out) != bytes) {
99 fprintf(stderr, "Failed to write %zu B to the output file\n", bytes);
100 err = -EIO;
101 goto err_close;
102 }
103 length += bytes;
104 }
105
106 header.unk1 = cpu_to_le32(0x00080000);
107 header.unk2 = cpu_to_le32(0x00080000);
108 header.length = cpu_to_le32(length);
109 header.magic[0] = 'B';
110 header.magic[1] = 'R';
111 header.magic[2] = 'C';
112 header.magic[3] = 'M';
113 header.unused = 0;
114
115 fseek(out, 0, SEEK_SET);
116
117 if (fwrite(&header, 1, sizeof(header), out) != sizeof(header)) {
118 fprintf(stderr, "Failed to write header to the output file\n");
119 err = -EIO;
120 goto err_close;
121 }
122
123 err_close:
124 fclose(out);
125 fclose(in);
126 return err;
127 }