mkh3cimg: add image tool for H3C devices
[project/firmware-utils.git] / src / mkh3cimg.c
1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 #include <byteswap.h>
10 #include <endian.h>
11 #include <getopt.h>
12
13
14 #if !defined(__BYTE_ORDER)
15 #error "Unknown byte order"
16 #endif
17
18 #if __BYTE_ORDER == __BIG_ENDIAN
19 #define cpu_to_be16(x) (x)
20 #define cpu_to_be32(x) (x)
21 #elif __BYTE_ORDER == __LITTLE_ENDIAN
22 #define cpu_to_be16(x) bswap_16(x)
23 #define cpu_to_be32(x) bswap_32(x)
24 #else
25 #error "Unsupported endianness"
26 #endif
27
28
29 #define IMAGE_VERSION 1
30 #define FILE_VERSION 1
31 #define FILE_DESCRIPTION "OpenWrt"
32 #define FILE_TYPE_MASK 0x1
33
34
35 #define PACKAGE_FLAG 2
36
37 #define FILE_TYPE_APPLICATION 0x04000000
38
39 #define VERSION_OFFSET_INVALID 0xffffffff
40
41 #define COMPRESSION_TYPE_NONE 0xffffffff
42 #define COMPRESSION_TYPE_7Z 0x00000002
43
44
45 struct file_header {
46 uint8_t res1[4];
47
48 uint32_t header_crc;
49
50 uint32_t file_type;
51 uint32_t version;
52 uint32_t product_id;
53 uint32_t device_id;
54
55 uint32_t length_unpadded;
56
57 uint32_t version_offset;
58
59 uint16_t year;
60 uint8_t month;
61 uint8_t day;
62 uint8_t res2[1];
63 uint8_t hour;
64 uint8_t minute;
65 uint8_t second;
66
67 uint8_t res3[64];
68
69 char description[224];
70
71 uint32_t length;
72
73 uint32_t file_crc;
74
75 uint32_t compression_type;
76 } __attribute__ ((packed));
77
78 struct file_desc {
79 uint32_t file_type;
80 uint32_t offset;
81 uint32_t length;
82 uint32_t file_crc;
83 uint32_t version;
84 uint32_t type_mask;
85 } __attribute__ ((packed));
86
87 struct image_header {
88 uint32_t version;
89
90 uint32_t file_count;
91
92 uint32_t product_id;
93 uint32_t device_id;
94
95 uint16_t year;
96 uint8_t month;
97 uint8_t day;
98 uint8_t res1[1];
99 uint8_t hour;
100 uint8_t minute;
101 uint8_t second;
102
103 uint16_t package_crc;
104 uint16_t package_flag;
105
106 uint32_t length;
107
108 struct file_desc files[128];
109
110 /* RSA signature, not required */
111 uint8_t res2[3072];
112
113 uint32_t header_crc;
114 } __attribute__ ((packed));
115
116
117 static void *buf;
118 static size_t buflen;
119
120 static size_t length_unpadded;
121 static size_t length;
122
123
124 static uint32_t crc16_xmodem(char *buf, size_t len) {
125 uint32_t poly = 0x1021;
126 uint32_t crc = 0;
127 char b;
128 int i, j;
129
130 for (i = 0; i < len; i++) {
131 b = buf[i];
132 crc = crc ^ (b << 8);
133
134 for (j = 0; j < 8; j++) {
135 crc = crc << 1;
136 if (crc & 0x10000) {
137 crc = (crc ^ poly) & 0xffff;
138 }
139 }
140 }
141
142 return crc;
143 }
144
145 static int create_buffer_and_read_file(char *filename) {
146 FILE *f;
147
148 f = fopen(filename, "r");
149 if (f == NULL) {
150 fprintf(stderr, "failed to open input file\n");
151 goto err;
152 }
153
154 fseek(f, 0L, SEEK_END);
155 length_unpadded = ftell(f);
156 rewind(f);
157
158 length = length_unpadded;
159 if (length_unpadded % 8 != 0) {
160 length += 8 - length_unpadded % 8;
161 }
162
163 buflen = sizeof(struct file_header) + sizeof(struct image_header) + length;
164 buf = malloc(buflen);
165 if (!buf) {
166 fprintf(stderr, "failed to allocate buffer\n");
167 goto err_close;
168 }
169
170 memset(buf, 0, buflen);
171
172 if (fread(buf + sizeof(struct file_header) + sizeof(struct image_header), length_unpadded, 1, f) != 1) {
173 fprintf(stderr, "failed to read input file\n");
174 goto err_free;
175 }
176
177 fclose(f);
178 return 0;
179
180 err_free:
181 free(buf);
182 err_close:
183 fclose(f);
184 err:
185 return -1;
186 }
187
188 static void build_file_header(uint32_t product_id, uint32_t device_id, uint32_t compression_type) {
189 struct file_header *header = buf + sizeof(struct image_header);
190 uint32_t crc;
191
192 header->file_type = cpu_to_be32(FILE_TYPE_APPLICATION);
193
194 header->version = cpu_to_be32(FILE_VERSION);
195
196 header->product_id = cpu_to_be32(product_id);
197 header->device_id = cpu_to_be32(device_id);
198
199 header->length_unpadded = cpu_to_be32(length_unpadded);
200
201 header->version_offset = cpu_to_be32(VERSION_OFFSET_INVALID);
202
203 header->year = cpu_to_be16(1970);
204 header->month = 1;
205 header->day = 1;
206 header->hour = 0;
207 header->minute = 0;
208 header->second = 0;
209
210 snprintf(header->description, sizeof(header->description), "%s", FILE_DESCRIPTION);
211
212 header->length = cpu_to_be32(length);
213
214 crc = crc16_xmodem(buf + sizeof(struct image_header) + sizeof(struct file_header), length);
215 header->file_crc = cpu_to_be32(crc);
216
217 header->compression_type = cpu_to_be32(compression_type);
218
219 crc = crc16_xmodem((char *)header + sizeof(header->res1) + sizeof(header->header_crc),
220 sizeof(struct file_header) - sizeof(header->res1) - sizeof(header->header_crc));
221 header->header_crc = cpu_to_be32(crc);
222 }
223
224 static void build_image_header(uint32_t product_id, uint32_t device_id) {
225 struct image_header *header = buf;
226 struct file_header *file_header = buf + sizeof(struct image_header);
227 uint32_t crc;
228
229 header->version = cpu_to_be32(IMAGE_VERSION);
230
231 header->file_count = cpu_to_be32(1);
232
233 header->product_id = cpu_to_be32(product_id);
234 header->device_id = cpu_to_be32(device_id);
235
236 header->year = cpu_to_be16(1970);
237 header->month = 1;
238 header->day = 1;
239 header->hour = 0;
240 header->minute = 0;
241 header->second = 0;
242
243 crc = crc16_xmodem(buf + sizeof(struct file_header), buflen - sizeof(struct file_header));
244 header->package_crc = cpu_to_be16(crc);
245 header->package_flag = cpu_to_be16(PACKAGE_FLAG);
246
247 header->length = cpu_to_be32(buflen - sizeof(struct image_header));
248
249 header->files[0].file_type = file_header->file_type;
250 header->files[0].offset = cpu_to_be32(sizeof(struct image_header));
251 header->files[0].length = cpu_to_be32(sizeof(struct file_header) + length);
252 header->files[0].file_crc = file_header->file_crc;
253 header->files[0].version = file_header->version;
254 header->files[0].type_mask = cpu_to_be32(FILE_TYPE_MASK);
255
256 crc = crc16_xmodem((char *)header, sizeof(struct image_header) - sizeof(header->header_crc));
257 header->header_crc = cpu_to_be32(crc);
258 }
259
260 static int write_output_file(char *filename) {
261 int ret = 0;
262 FILE *f;
263
264 f = fopen(filename, "w");
265 if (f == NULL) {
266 fprintf(stderr, "failed to open output file\n");
267 ret = -1;
268 goto err;
269 }
270
271 if (fwrite(buf, buflen, 1, f) != 1) {
272 fprintf(stderr, "failed to write output file\n");
273 ret = -1;
274 }
275
276 fclose(f);
277
278 err:
279 return ret;
280 }
281
282 static void usage(char* argv[]) {
283 printf("Usage: %s [OPTIONS...]\n"
284 "\n"
285 "Options:\n"
286 " -p <product id> product id (32-bit unsigned integer)\n"
287 " -d <device id> device id (32-bit unsigned integer)\n"
288 " -c <compression> compression type of the input file (7z or none)\n"
289 " (in case of 7z only LZMA compression is allowed)\n"
290 " -i <file> input filename\n"
291 " -o <file> output filename\n"
292 , argv[0]);
293 }
294
295 int main(int argc, char* argv[]) {
296 int ret = EXIT_FAILURE;
297
298 static uint32_t product_id = 0;
299 static uint32_t device_id = 0;
300 static uint32_t compression_type = COMPRESSION_TYPE_NONE;
301 static char *input_filename = NULL;
302 static char *output_filename = NULL;
303
304 while ( 1 ) {
305 int c;
306
307 c = getopt(argc, argv, "p:d:c:i:o:");
308 if (c == -1)
309 break;
310
311 switch (c) {
312 case 'p':
313 product_id = strtoul(optarg, NULL, 0);
314 break;
315 case 'd':
316 device_id = strtoul(optarg, NULL, 0);
317 break;
318 case 'c':
319 if (strcmp(optarg, "none") == 0) {
320 compression_type = COMPRESSION_TYPE_NONE;
321 } else if (strcmp(optarg, "7z") == 0) {
322 compression_type = COMPRESSION_TYPE_7Z;
323 } else {
324 usage(argv);
325 return EXIT_FAILURE;
326 }
327 break;
328 case 'i':
329 input_filename = optarg;
330 break;
331 case 'o':
332 output_filename = optarg;
333 break;
334 default:
335 usage(argv);
336 goto err;
337 }
338 }
339
340 if (!product_id || !device_id ||
341 !input_filename || strlen(input_filename) == 0 ||
342 !output_filename || strlen(output_filename) == 0) {
343
344 usage(argv);
345 goto err;
346 }
347
348 if (create_buffer_and_read_file(input_filename)) {
349 goto err;
350 }
351
352 build_file_header(product_id, device_id, compression_type);
353
354 build_image_header(product_id, device_id);
355
356 if (write_output_file(output_filename)) {
357 goto err_free;
358 }
359
360 ret = EXIT_SUCCESS;
361
362 err_free:
363 free(buf);
364 err:
365 return ret;
366 }