firmware-utils: replace GPL 2.0+ boilerplate/reference with SPDX
[openwrt/staging/mkresin.git] / tools / firmware-utils / src / mkchkimg.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Make CHK Image
4 *
5 * This utility creates Netgear .chk files.
6 *
7 * Copyright (C) 2008 Dave C. Reeve <Dave.Reeve@dreeve.org>
8 */
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdarg.h>
13 #include <errno.h>
14 #include <arpa/inet.h>
15 #include <unistd.h>
16
17 #define BUF_LEN (2048)
18
19 #define MAX_BOARD_ID_LEN (64)
20
21 /*
22 * Note on the reserved field of the chk_header:
23 * OFW naming scheme is typically: DEVICENAME-VA.B.C.D_E.F.G.chk, with A-G
24 * between 0 and 255. For instance: EX3700_EX3800-V1.0.0.58_1.0.38.chk
25 * The reserved field works like this:
26 * reserved[0]: region code. 1 for WW (WorldWide) and 2 for NA (North America)
27 * reserved[1]: A
28 * reserved[2]: B
29 * reserved[3]: C
30 * reserved[4]: D
31 * reserved[5]: E
32 * reserved[6]: F
33 * reserved[7]: G
34 */
35 struct chk_header {
36 uint32_t magic;
37 uint32_t header_len;
38 uint8_t reserved[8];
39 uint32_t kernel_chksum;
40 uint32_t rootfs_chksum;
41 uint32_t kernel_len;
42 uint32_t rootfs_len;
43 uint32_t image_chksum;
44 uint32_t header_chksum;
45 /* char board_id[] - upto MAX_BOARD_ID_LEN */
46 };
47
48 static void __attribute__ ((format (printf, 2, 3)))
49 fatal_error (int maybe_errno, const char * format, ...)
50 {
51 va_list ap;
52
53 fprintf (stderr, "mkchkimg: ");
54 va_start (ap, format);
55 vfprintf (stderr, format, ap);
56 va_end (ap);
57
58 if (maybe_errno) {
59 fprintf (stderr, ": %s\n", strerror (maybe_errno));
60 } else {
61 fprintf (stderr, "\n");
62 }
63
64 exit (EXIT_FAILURE);
65 }
66
67 static void __attribute__ ((format (printf, 1, 2)))
68 message (const char * format, ...)
69 {
70 va_list ap;
71
72 fprintf (stderr, "mkchkimg: ");
73 va_start (ap, format);
74 vfprintf (stderr, format, ap);
75 va_end (ap);
76 fprintf (stderr, "\n");
77 }
78
79 struct ngr_checksum {
80 uint32_t c0;
81 uint32_t c1;
82 };
83
84 static inline void
85 netgear_checksum_init (struct ngr_checksum * c)
86 {
87 c->c0 = c->c1 = 0;
88 }
89
90 static inline void
91 netgear_checksum_add (struct ngr_checksum * c, unsigned char * buf, size_t len)
92 {
93 size_t i;
94
95 for (i=0; i<len; i++) {
96 c->c0 += buf[i] & 0xff;
97 c->c1 += c->c0;
98 }
99 }
100
101 static inline unsigned long
102 netgear_checksum_fini (struct ngr_checksum * c)
103 {
104 uint32_t b, checksum;
105
106 b = (c->c0 & 65535) + ((c->c0 >> 16) & 65535);
107 c->c0 = ((b >> 16) + b) & 65535;
108 b = (c->c1 & 65535) + ((c->c1 >> 16) & 65535);
109 c->c1 = ((b >> 16) + b) & 65535;
110 checksum = ((c->c1 << 16) | c->c0);
111 return checksum;
112 }
113
114 static void
115 print_help (void)
116 {
117 fprintf (stderr, "Usage: mkchkimg -o output -k kernel [-f filesys] [-b board_id] [-r region]\n");
118 }
119
120 int
121 main (int argc, char * argv[])
122 {
123 int opt;
124 char * ptr;
125 size_t len;
126 size_t header_len;
127 struct chk_header * hdr;
128 struct ngr_checksum chk_part, chk_whole;
129 char buf[BUF_LEN];
130 char * output_file, * kern_file, * fs_file;
131 FILE * out_fp, * kern_fp, * fs_fp;
132 char * board_id;
133 unsigned long region;
134
135 /* Default values */
136 board_id = "U12H072T00_NETGEAR";
137 region = 1; /* 1=WW, 2=NA */
138 output_file = NULL;
139 kern_file = NULL;
140 fs_file = NULL;
141 fs_fp = NULL;
142
143 while ((opt = getopt (argc, argv, ":b:r:k:f:o:h")) != -1) {
144 switch (opt) {
145 case 'b':
146 /* Board Identity */
147 if (strlen (optarg) > MAX_BOARD_ID_LEN) {
148 fatal_error (0, "Board lenght exceeds %d",
149 MAX_BOARD_ID_LEN);
150 }
151 board_id = optarg;
152 break;
153
154 case 'r':
155 /* Region */
156 errno = 0;
157 region = strtoul (optarg, &ptr, 0);
158 if (errno || ptr==optarg || *ptr!='\0') {
159 fatal_error (0, "Cannot parse region %s", optarg);
160 }
161 if (region > 0xff) {
162 fatal_error (0, "Region cannot exceed 0xff");
163 }
164 break;
165
166 case 'k':
167 /* Kernel */
168 kern_file = optarg;
169 break;
170
171 case 'f':
172 /* Filing System */
173 fs_file = optarg;
174 break;
175
176 case 'o':
177 /* Output file */
178 output_file = optarg;
179 break;
180
181 case 'h':
182 print_help ();
183 return EXIT_SUCCESS;
184
185 case ':':
186 print_help ();
187 fatal_error (0, "Option -%c missing argument", optopt);
188 break;
189
190 case '?':
191 print_help ();
192 fatal_error (0, "Unknown argument -%c", optopt);
193 break;
194
195 default:
196 break;
197 }
198 }
199
200 /* Check we have all the options expected */
201 if (!kern_file) {
202 print_help ();
203 fatal_error (0, "Kernel file expected");
204 }
205 if (!output_file) {
206 print_help ();
207 fatal_error (0, "Output file required");
208 }
209 message ("Netgear CHK writer - v0.1");
210
211 /* Open the input file */
212 kern_fp = fopen (kern_file, "r");
213 if (!kern_fp) {
214 fatal_error (errno, "Cannot open %s", kern_file);
215 }
216
217 /* Open the fs file, if specified */
218 if (fs_file) {
219 fs_fp = fopen (fs_file, "r");
220 if (!fs_fp) {
221 fclose(kern_fp);
222 fatal_error (errno, "Cannot open %s", fs_file);
223 }
224 }
225
226 /* Open the output file */
227 out_fp = fopen (output_file, "w+");
228 if (!out_fp) {
229 fclose(kern_fp);
230 if (fs_fp) {
231 fclose(fs_fp);
232 }
233 fatal_error (errno, "Cannot open %s", output_file);
234 }
235
236 /* Write zeros when the chk header will be */
237 buf[0] = '\0';
238 header_len = sizeof (struct chk_header) + strlen (board_id);
239 if (fwrite (buf, 1, header_len, out_fp) != header_len) {
240 fatal_error (errno, "Cannot write header");
241 }
242
243 /* Allocate storage for header, we fill in as we go */
244 hdr = malloc (sizeof (struct chk_header));
245 if (!hdr) {
246 fatal_error (0, "malloc failed");
247 }
248 bzero (hdr, sizeof (struct chk_header));
249
250 /* Fill in known values */
251 hdr->magic = htonl (0x2a23245e);
252 hdr->header_len = htonl(header_len);
253 hdr->reserved[0] = (unsigned char)(region & 0xff);
254 memset(&hdr->reserved[1], 99, sizeof(hdr->reserved) - 1);
255
256 message (" Board Id: %s", board_id);
257 message (" Region: %s", region == 1 ? "World Wide (WW)"
258 : (region == 2 ? "North America (NA)" : "Unknown"));
259
260 /* Copy the trx file, calculating the checksum as we go */
261 netgear_checksum_init (&chk_part);
262 netgear_checksum_init (&chk_whole);
263 while (!feof (kern_fp)) {
264 len = fread (buf, 1, BUF_LEN, kern_fp);
265 if (len < 1) {
266 break;
267 }
268 if (fwrite (buf, len, 1, out_fp) != 1) {
269 fatal_error (errno, "Write error");
270 }
271 hdr->kernel_len += len;
272 netgear_checksum_add (&chk_part, (unsigned char *)buf, len);
273 netgear_checksum_add (&chk_whole, (unsigned char *)buf, len);
274 }
275 fclose(kern_fp);
276 hdr->kernel_chksum = netgear_checksum_fini (&chk_part);
277 message (" Kernel Len: %u", hdr->kernel_len);
278 message ("Kernel Checksum: 0x%08x", hdr->kernel_chksum);
279 hdr->kernel_len = htonl (hdr->kernel_len);
280 hdr->kernel_chksum = htonl (hdr->kernel_chksum);
281
282 /* Now copy the root fs, calculating the checksum as we go */
283 if (fs_fp) {
284 netgear_checksum_init (&chk_part);
285 while (!feof (fs_fp)) {
286 len = fread (buf, 1, BUF_LEN, fs_fp);
287 if (len < 1) {
288 break;
289 }
290 if (fwrite (buf, len, 1, out_fp) != 1) {
291 fatal_error (errno, "Write error");
292 }
293 hdr->rootfs_len += len;
294 netgear_checksum_add (&chk_part, (unsigned char *)buf, len);
295 netgear_checksum_add (&chk_whole, (unsigned char *)buf, len);
296 }
297 fclose(fs_fp);
298 hdr->rootfs_chksum = (netgear_checksum_fini (&chk_part));
299 message (" Rootfs Len: %u", hdr->rootfs_len);
300 message ("Rootfs Checksum: 0x%08x", hdr->rootfs_chksum);
301 hdr->rootfs_len = htonl (hdr->rootfs_len);
302 hdr->rootfs_chksum = htonl (hdr->rootfs_chksum);
303 }
304
305 /* Calcautate the image checksum */
306 hdr->image_chksum = netgear_checksum_fini (&chk_whole);
307 message (" Image Checksum: 0x%08x", hdr->image_chksum);
308 hdr->image_chksum = htonl (hdr->image_chksum);
309
310 /* Calculate the header checksum */
311 netgear_checksum_init (&chk_part);
312 netgear_checksum_add (&chk_part, (unsigned char *)hdr,
313 sizeof (struct chk_header));
314 netgear_checksum_add (&chk_part, (unsigned char *)board_id,
315 strlen (board_id));
316 hdr->header_chksum = htonl (netgear_checksum_fini (&chk_part));
317
318 /* Finally rewind the output and write headers */
319 rewind (out_fp);
320 if (fwrite (hdr, sizeof (struct chk_header), 1, out_fp) != 1) {
321 fatal_error (errno, "Cannot write header");
322 }
323 if (fwrite (board_id, strlen (board_id), 1, out_fp) != 1) {
324 fatal_error (errno, "Cannot write board id");
325 }
326
327 /* Success */
328 fclose(out_fp);
329 return EXIT_SUCCESS;
330 }
331