2 * --- ZyXEL header format ---
3 * Original Version by Benjamin Berg <benjamin@sipsolutions.net>
4 * C implementation based on generation-script by Christian Lamparter <chunkeey@gmail.com>
6 * The firmware image prefixed with a header (which is written into the MTD device).
7 * The header is one erase block (~64KiB) in size, but the checksum only convers the
8 * first 2KiB. Padding is 0xff. All integers are in big-endian.
10 * The checksum is always a 16-Bit System V checksum (sum -s) stored in a 32-Bit integer.
12 * 4 bytes: checksum of the rootfs image
13 * 4 bytes: length of the contained rootfs image file (big endian)
14 * 32 bytes: Firmware Version string (NUL terminated, 0xff padded)
15 * 4 bytes: checksum over the header partition (big endian - see below)
16 * 64 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded)
17 * 4 bytes: checksum of the kernel partition
18 * 4 bytes: length of the contained kernel image file (big endian)
19 * rest: 0xff padding (To erase block size)
21 * The kernel partition checksum and length is not used for every device.
22 * If it's notused, pad those 8 bytes with 0xFF.
24 * The checksums are calculated by adding up all bytes and if a 16bit
25 * overflow occurs, one is added and the sum is masked to 16 bit:
26 * csum = csum + databyte; if (csum > 0xffff) { csum += 1; csum &= 0xffff };
27 * Should the file have an odd number of bytes then the byte len-0x800 is
30 * The checksum for the header is calculated over the first 2048 bytes with
31 * the rootfs image checksum as the placeholder during calculation.
33 * This program is free software; you can redistribute it and/or modify it
34 * under the terms of the GNU General Public License version 2 as published
35 * by the Free Software Foundation.
49 #include <arpa/inet.h>
51 #define VERSION_STRING_LEN 31
52 #define ROOTFS_HEADER_LEN 40
54 #define KERNEL_HEADER_LEN 8
56 #define BOARD_NAME_LEN 64
57 #define BOARD_HEADER_LEN 68
59 #define HEADER_PARTITION_CALC_LENGTH 2048
60 #define HEADER_PARTITION_LENGTH 0x10000
63 char *name
; /* name of the file */
64 char *data
; /* file content */
65 size_t size
; /* length of the file */
68 static char *progname
;
70 static char *board_name
= 0;
71 static char *version_name
= 0;
72 static unsigned int rootfs_size
= 0;
73 static unsigned int header_length
= HEADER_PARTITION_LENGTH
;
75 static struct file_info kernel
= { NULL
, NULL
, 0 };
76 static struct file_info rootfs
= { NULL
, NULL
, 0 };
77 static struct file_info rootfs_out
= { NULL
, NULL
, 0 };
78 static struct file_info out
= { NULL
, NULL
, 0 };
80 #define ERR(fmt, ...) do { \
81 fprintf(stderr, "[%s] *** error: " fmt "\n", \
82 progname, ## __VA_ARGS__ ); \
85 void map_file(struct file_info
*finfo
)
87 struct stat file_stat
= {0};
90 fd
= open(finfo
->name
, O_RDONLY
, (mode_t
)0600);
92 ERR("Error while opening file %s.", finfo
->name
);
96 if (fstat(fd
, &file_stat
) == -1) {
97 ERR("Error getting file size for %s.", finfo
->name
);
101 finfo
->size
= file_stat
.st_size
;
102 finfo
->data
= mmap(0, finfo
->size
, PROT_READ
, MAP_SHARED
, fd
, 0);
104 if (finfo
->data
== MAP_FAILED
) {
105 ERR("Error mapping file %s.", finfo
->name
);
112 void unmap_file(struct file_info
*finfo
)
114 if(munmap(finfo
->data
, finfo
->size
) == -1) {
115 ERR("Error unmapping file %s.", finfo
->name
);
120 void write_file(struct file_info
*finfo
)
122 FILE *fout
= fopen(finfo
->name
, "w");
124 fwrite(finfo
->data
, finfo
->size
, 1, fout
);
127 ERR("Wanted to write, but something went wrong.");
134 void usage(int status
)
136 FILE *stream
= (status
!= EXIT_SUCCESS
) ? stderr
: stdout
;
138 fprintf(stream
, "Usage: %s [OPTIONS...]\n", progname
);
142 " -k <kernel> path for kernel image\n"
143 " -r <rootfs> path for rootfs image\n"
144 " -s <rfssize> size of output rootfs\n"
145 " -v <version> version string\n"
146 " -b <boardname> name of board to generate image for\n"
147 " -o <out_name> name of output image\n"
148 " -l <hdr_length> length of header, default 65536\n"
149 " -h show this screen\n"
155 static int sysv_chksm(const unsigned char *data
, int size
)
159 unsigned int s
= 0; /* The sum of all the input bytes, modulo (UINT_MAX + 1). */
162 for (int i
= 0; i
< size
; i
++) {
166 r
= (s
& 0xffff) + ((s
& 0xffffffff) >> 16);
167 checksum
= (r
& 0xffff) + (r
>> 16);
172 static int zyxel_chksm(const unsigned char *data
, int size
)
174 return htonl(sysv_chksm(data
, size
));
177 char *generate_rootfs_header(struct file_info filesystem
, char *version
)
179 size_t version_string_length
;
180 unsigned int chksm
, size
;
184 rootfs_header
= malloc(ROOTFS_HEADER_LEN
);
185 if (!rootfs_header
) {
186 ERR("Couldn't allocate memory for rootfs header!");
190 /* Prepare padding for firmware-version string here */
191 memset(rootfs_header
, 0xff, ROOTFS_HEADER_LEN
);
193 chksm
= zyxel_chksm((const unsigned char *)filesystem
.data
, filesystem
.size
);
194 size
= htonl(filesystem
.size
);
196 /* 4 bytes: checksum of the rootfs image */
197 memcpy(rootfs_header
+ ptr
, &chksm
, 4);
200 /* 4 bytes: length of the contained rootfs image file (big endian) */
201 memcpy(rootfs_header
+ ptr
, &size
, 4);
204 /* 32 bytes: Firmware Version string (NUL terminated, 0xff padded) */
205 version_string_length
= strlen(version
) <= VERSION_STRING_LEN
? strlen(version
) : VERSION_STRING_LEN
;
206 memcpy(rootfs_header
+ ptr
, version
, version_string_length
);
207 ptr
+= version_string_length
;
208 /* Add null-terminator */
209 rootfs_header
[ptr
] = 0x0;
211 return rootfs_header
;
214 char *generate_kernel_header(struct file_info kernel
)
216 unsigned int chksm
, size
;
220 kernel_header
= malloc(KERNEL_HEADER_LEN
);
221 if (!kernel_header
) {
222 ERR("Couldn't allocate memory for kernel header!");
226 chksm
= zyxel_chksm((const unsigned char *)kernel
.data
, kernel
.size
);
227 size
= htonl(kernel
.size
);
229 /* 4 bytes: checksum of the kernel image */
230 memcpy(kernel_header
+ ptr
, &chksm
, 4);
233 /* 4 bytes: length of the contained kernel image file (big endian) */
234 memcpy(kernel_header
+ ptr
, &size
, 4);
236 return kernel_header
;
239 unsigned int generate_board_header_checksum(char *kernel_hdr
, char *rootfs_hdr
, char *boardname
)
246 * The checksum of the board header is calculated over the first 2048 bytes of
247 * the header partition with the rootfs checksum used as a placeholder for then
248 * board checksum we calculate in this step. The checksum gained from this step
249 * is then used for the final board header partition.
252 board_hdr_tmp
= malloc(HEADER_PARTITION_CALC_LENGTH
);
253 if (!board_hdr_tmp
) {
254 ERR("Couldn't allocate memory for temporary board header!");
257 memset(board_hdr_tmp
, 0xff, HEADER_PARTITION_CALC_LENGTH
);
259 /* 40 bytes: RootFS header */
260 memcpy(board_hdr_tmp
, rootfs_hdr
, ROOTFS_HEADER_LEN
);
261 ptr
+= ROOTFS_HEADER_LEN
;
263 /* 4 bytes: RootFS checksum (BE) as placeholder for board-header checksum */
264 memcpy(board_hdr_tmp
+ ptr
, rootfs_hdr
, 4);
267 /* 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded) */
268 memcpy(board_hdr_tmp
+ ptr
, boardname
, strlen(boardname
));
269 ptr
+= strlen(boardname
);
270 /* Add null-terminator */
271 board_hdr_tmp
[ptr
] = 0x0;
272 ptr
= ROOTFS_HEADER_LEN
+ 4 + BOARD_NAME_LEN
;
274 /* 8 bytes: Kernel header */
276 memcpy(board_hdr_tmp
+ ptr
, kernel_hdr
, 8);
278 /* Calculate the checksum over the first 2048 bytes */
279 sum
= zyxel_chksm((const unsigned char *)board_hdr_tmp
, HEADER_PARTITION_CALC_LENGTH
);
284 char *generate_board_header(char *kernel_hdr
, char *rootfs_hdr
, char *boardname
)
286 unsigned int board_checksum
;
289 board_hdr
= malloc(BOARD_HEADER_LEN
);
291 ERR("Couldn't allocate memory for board header!");
294 memset(board_hdr
, 0xff, BOARD_HEADER_LEN
);
296 /* 4 bytes: checksum over the header partition (big endian) */
297 board_checksum
= generate_board_header_checksum(kernel_hdr
, rootfs_hdr
, boardname
);
298 memcpy(board_hdr
, &board_checksum
, 4);
300 /* 32 bytes: Model (e.g. "NBG6617", NUL termiated, 0xff padded) */
301 memcpy(board_hdr
+ 4, boardname
, strlen(boardname
));
302 board_hdr
[4 + strlen(boardname
)] = 0x0;
309 char *rootfs_header
= NULL
;
310 char *kernel_header
= NULL
;
311 char *board_header
= NULL
;
320 /* As ZyXEL Web-GUI only accept images with a rootfs equal or larger than the first firmware shipped
321 * for the device, we need to pad rootfs partition to this size. To perform further calculations, we
322 * decide the size of this part here. In case the rootfs we want to integrate in our image is larger,
323 * take it's size, otherwise the supplied size.
325 * Be careful! We rely on assertion of correct size to be performed beforehand. It is unknown if images
326 * with a to large rootfs are accepted or not.
328 rootfs_out
.size
= rootfs_size
< rootfs
.size
? rootfs
.size
: rootfs_size
;
331 * Allocate memory and copy input rootfs for temporary output rootfs.
332 * This is important as we have to generate the rootfs checksum over the
333 * entire rootfs partition. As we might have to pad the partition to allow
334 * for flashing via ZyXEL's Web-GUI, we prepare the rootfs partition for the
335 * output image here (and also use it for calculating the rootfs checksum).
337 * The roofs padding has to be done with 0x00.
339 rootfs_out
.data
= calloc(rootfs_out
.size
, sizeof(char));
340 memcpy(rootfs_out
.data
, rootfs
.data
, rootfs
.size
);
342 /* Prepare headers */
343 rootfs_header
= generate_rootfs_header(rootfs_out
, version_name
);
345 kernel_header
= generate_kernel_header(kernel
);
346 board_header
= generate_board_header(kernel_header
, rootfs_header
, board_name
);
348 /* Prepare output file */
349 out
.size
= header_length
+ rootfs_out
.size
;
351 out
.size
+= kernel
.size
;
352 out
.data
= malloc(out
.size
);
353 memset(out
.data
, 0xFF, out
.size
);
355 /* Build output image */
356 memcpy(out
.data
, rootfs_header
, ROOTFS_HEADER_LEN
);
357 memcpy(out
.data
+ ROOTFS_HEADER_LEN
, board_header
, BOARD_HEADER_LEN
);
359 memcpy(out
.data
+ ROOTFS_HEADER_LEN
+ BOARD_HEADER_LEN
, kernel_header
, KERNEL_HEADER_LEN
);
361 memcpy(out
.data
+ ptr
, rootfs_out
.data
, rootfs_out
.size
);
362 ptr
+= rootfs_out
.size
;
364 memcpy(out
.data
+ ptr
, kernel
.data
, kernel
.size
);
366 /* Write back output image */
369 /* Free allocated memory */
374 free(rootfs_out
.data
);
387 ERR("No rootfs filename supplied");
392 ERR("No output filename supplied");
397 ERR("No board-name supplied");
402 ERR("No version supplied");
406 if (rootfs_size
<= 0) {
407 ERR("Invalid rootfs size supplied");
411 if (strlen(board_name
) > 31) {
412 ERR("Board name is to long");
418 int main(int argc
, char *argv
[])
421 progname
= basename(argv
[0]);
425 c
= getopt(argc
, argv
, "b:k:o:r:s:v:l:h");
437 kernel
.name
= optarg
;
443 rootfs
.name
= optarg
;
446 sscanf(optarg
, "%u", &rootfs_size
);
449 version_name
= optarg
;
452 sscanf(optarg
, "%u", &header_length
);
460 ret
= check_options();
464 return build_image();