1 // SPDX-License-Identifier: GPL-2.0-only
14 #if !defined(__BYTE_ORDER)
15 #error "Unknown byte order"
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)
25 #error "Unsupported endianness"
29 #define IMAGE_VERSION 1
30 #define FILE_VERSION 1
31 #define FILE_DESCRIPTION "OpenWrt"
32 #define FILE_TYPE_MASK 0x1
35 #define PACKAGE_FLAG 2
37 #define FILE_TYPE_APPLICATION 0x04000000
39 #define VERSION_OFFSET_INVALID 0xffffffff
41 #define COMPRESSION_TYPE_NONE 0xffffffff
42 #define COMPRESSION_TYPE_7Z 0x00000002
55 uint32_t length_unpadded
;
57 uint32_t version_offset
;
69 char description
[224];
75 uint32_t compression_type
;
76 } __attribute__ ((packed
));
85 } __attribute__ ((packed
));
103 uint16_t package_crc
;
104 uint16_t package_flag
;
108 struct file_desc files
[128];
110 /* RSA signature, not required */
114 } __attribute__ ((packed
));
118 static size_t buflen
;
120 static size_t length_unpadded
;
121 static size_t length
;
124 static uint32_t crc16_xmodem(char *buf
, size_t len
) {
125 uint32_t poly
= 0x1021;
130 for (i
= 0; i
< len
; i
++) {
132 crc
= crc
^ (b
<< 8);
134 for (j
= 0; j
< 8; j
++) {
137 crc
= (crc
^ poly
) & 0xffff;
145 static int create_buffer_and_read_file(char *filename
) {
148 f
= fopen(filename
, "r");
150 fprintf(stderr
, "failed to open input file\n");
154 fseek(f
, 0L, SEEK_END
);
155 length_unpadded
= ftell(f
);
158 length
= length_unpadded
;
159 if (length_unpadded
% 8 != 0) {
160 length
+= 8 - length_unpadded
% 8;
163 buflen
= sizeof(struct file_header
) + sizeof(struct image_header
) + length
;
164 buf
= malloc(buflen
);
166 fprintf(stderr
, "failed to allocate buffer\n");
170 memset(buf
, 0, buflen
);
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");
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
);
192 header
->file_type
= cpu_to_be32(FILE_TYPE_APPLICATION
);
194 header
->version
= cpu_to_be32(FILE_VERSION
);
196 header
->product_id
= cpu_to_be32(product_id
);
197 header
->device_id
= cpu_to_be32(device_id
);
199 header
->length_unpadded
= cpu_to_be32(length_unpadded
);
201 header
->version_offset
= cpu_to_be32(VERSION_OFFSET_INVALID
);
203 header
->year
= cpu_to_be16(1970);
210 snprintf(header
->description
, sizeof(header
->description
), "%s", FILE_DESCRIPTION
);
212 header
->length
= cpu_to_be32(length
);
214 crc
= crc16_xmodem(buf
+ sizeof(struct image_header
) + sizeof(struct file_header
), length
);
215 header
->file_crc
= cpu_to_be32(crc
);
217 header
->compression_type
= cpu_to_be32(compression_type
);
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
);
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
);
229 header
->version
= cpu_to_be32(IMAGE_VERSION
);
231 header
->file_count
= cpu_to_be32(1);
233 header
->product_id
= cpu_to_be32(product_id
);
234 header
->device_id
= cpu_to_be32(device_id
);
236 header
->year
= cpu_to_be16(1970);
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
);
247 header
->length
= cpu_to_be32(buflen
- sizeof(struct image_header
));
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
);
256 crc
= crc16_xmodem((char *)header
, sizeof(struct image_header
) - sizeof(header
->header_crc
));
257 header
->header_crc
= cpu_to_be32(crc
);
260 static int write_output_file(char *filename
) {
264 f
= fopen(filename
, "w");
266 fprintf(stderr
, "failed to open output file\n");
271 if (fwrite(buf
, buflen
, 1, f
) != 1) {
272 fprintf(stderr
, "failed to write output file\n");
282 static void usage(char* argv
[]) {
283 printf("Usage: %s [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"
295 int main(int argc
, char* argv
[]) {
296 int ret
= EXIT_FAILURE
;
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
;
307 c
= getopt(argc
, argv
, "p:d:c:i:o:");
313 product_id
= strtoul(optarg
, NULL
, 0);
316 device_id
= strtoul(optarg
, NULL
, 0);
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
;
329 input_filename
= optarg
;
332 output_filename
= optarg
;
340 if (!product_id
|| !device_id
||
341 !input_filename
|| strlen(input_filename
) == 0 ||
342 !output_filename
|| strlen(output_filename
) == 0) {
348 if (create_buffer_and_read_file(input_filename
)) {
352 build_file_header(product_id
, device_id
, compression_type
);
354 build_image_header(product_id
, device_id
);
356 if (write_output_file(output_filename
)) {