• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/mkrasimage.c

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * --- ZyXEL header format ---
  4  * Original Version by Benjamin Berg <benjamin@sipsolutions.net>
  5  * C implementation based on generation-script by Christian Lamparter <chunkeey@gmail.com>
  6  *
  7  * The firmware image prefixed with a header (which is written into the MTD device).
  8  * The header is one erase block (~64KiB) in size, but the checksum only convers the
  9  * first 2KiB. Padding is 0xff. All integers are in big-endian.
 10  *
 11  * The checksum is always a 16-Bit System V checksum (sum -s) stored in a 32-Bit integer.
 12  *
 13  *   4 bytes:  checksum of the rootfs image
 14  *   4 bytes:  length of the contained rootfs image file (big endian)
 15  *  32 bytes:  Firmware Version string (NUL terminated, 0xff padded)
 16  *   4 bytes:  checksum over the header partition (big endian - see below)
 17  *  64 bytes:  Model (e.g. "NBG6617", NUL termiated, 0xff padded)
 18  *   4 bytes:  checksum of the kernel partition
 19  *   4 bytes:  length of the contained kernel image file (big endian)
 20  *      rest:  0xff padding (To erase block size)
 21  *
 22  * The kernel partition checksum and length is not used for every device.
 23  * If it's notused, pad those 8 bytes with 0xFF.
 24  *
 25  * The checksums are calculated by adding up all bytes and if a 16bit
 26  * overflow occurs, one is added and the sum is masked to 16 bit:
 27  *   csum = csum + databyte; if (csum > 0xffff) { csum += 1; csum &= 0xffff };
 28  * Should the file have an odd number of bytes then the byte len-0x800 is
 29  * used additionally.
 30  *
 31  * The checksum for the header is calculated over the first 2048 bytes with
 32  * the rootfs image checksum as the placeholder during calculation.
 33  */
 34 #include <fcntl.h>
 35 #include <getopt.h>
 36 #include <libgen.h>
 37 #include <stdio.h>
 38 #include <string.h>
 39 #include <stdlib.h>
 40 #include <unistd.h>
 41 
 42 #include <sys/mman.h>
 43 #include <sys/stat.h>
 44 
 45 #include <arpa/inet.h>
 46 
 47 #define VERSION_STRING_LEN 31
 48 #define ROOTFS_HEADER_LEN 40
 49 
 50 #define KERNEL_HEADER_LEN 8
 51 
 52 #define BOARD_NAME_LEN 64
 53 #define BOARD_HEADER_LEN 68
 54 
 55 #define HEADER_PARTITION_CALC_LENGTH 2048
 56 #define HEADER_PARTITION_LENGTH 0x10000
 57 
 58 struct file_info {
 59     char *name;    /* name of the file */
 60     char *data;    /* file content */
 61     size_t size;   /* length of the file */
 62 };
 63 
 64 static char *progname;
 65 
 66 static char *board_name = 0;
 67 static char *version_name = 0;
 68 static unsigned int rootfs_size = 0;
 69 static unsigned int header_length = HEADER_PARTITION_LENGTH;
 70 
 71 static struct file_info kernel = { NULL, NULL, 0 };
 72 static struct file_info rootfs = { NULL, NULL, 0 };
 73 static struct file_info rootfs_out = { NULL, NULL, 0 };
 74 static struct file_info out = { NULL, NULL, 0 };
 75 
 76 #define ERR(fmt, ...) do { \
 77     fprintf(stderr, "[%s] *** error: " fmt "\n", \
 78             progname, ## __VA_ARGS__ ); \
 79 } while (0)
 80 
 81 void map_file(struct file_info *finfo)
 82 {
 83     struct stat file_stat = {0};
 84     int fd;
 85 
 86     fd = open(finfo->name, O_RDONLY, (mode_t)0600);
 87     if (fd == -1) {
 88         ERR("Error while opening file %s.", finfo->name);
 89         exit(EXIT_FAILURE);
 90     }
 91 
 92     if (fstat(fd, &file_stat) == -1) {
 93         ERR("Error getting file size for %s.", finfo->name);
 94         exit(EXIT_FAILURE);
 95     }
 96 
 97     finfo->size = file_stat.st_size;
 98     finfo->data = mmap(0, finfo->size, PROT_READ, MAP_SHARED, fd, 0);
 99 
100     if (finfo->data == MAP_FAILED) {
101         ERR("Error mapping file %s.", finfo->name);
102         exit(EXIT_FAILURE);
103     }
104 
105     close(fd);
106 }
107 
108 void unmap_file(struct file_info *finfo)
109 {
110     if(munmap(finfo->data, finfo->size) == -1) {
111         ERR("Error unmapping file %s.", finfo->name);
112         exit(EXIT_FAILURE);
113     }
114 }
115 
116 void write_file(struct file_info *finfo)
117 {
118     FILE *fout = fopen(finfo->name, "w");
119 
120     fwrite(finfo->data, finfo->size, 1, fout);
121 
122     if (ferror(fout)) {
123         ERR("Wanted to write, but something went wrong.");
124         exit(EXIT_FAILURE);
125     }
126 
127     fclose(fout);
128 }
129 
130 void usage(int status)
131 {
132     FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
133 
134     fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
135     fprintf(stream,
136             "\n"
137             "Options:\n"
138             "  -k <kernel>     path for kernel image\n"
139             "  -r <rootfs>     path for rootfs image\n"
140             "  -s <rfssize>    size of output rootfs\n"
141             "  -v <version>    version string\n"
142             "  -b <boardname>  name of board to generate image for\n"
143             "  -o <out_name>   name of output image\n"
144             "  -l <hdr_length> length of header, default 65536\n"
145             "  -h              show this screen\n"
146     );
147 
148     exit(status);
149 }
150 
151 static int sysv_chksm(const unsigned char *data, int size)
152 {
153     int r;
154     int checksum;
155     unsigned int s = 0; /* The sum of all the input bytes, modulo (UINT_MAX + 1).  */
156 
157 
158     for (int i = 0; i < size; i++) {
159         s += data[i];
160     }
161 
162     r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
163     checksum = (r & 0xffff) + (r >> 16);
164 
165     return checksum;
166 }
167 
168 static int zyxel_chksm(const unsigned char *data, int size)
169 {
170      return htonl(sysv_chksm(data, size));
171 }
172 
173 char *generate_rootfs_header(struct file_info filesystem, char *version)
174 {
175     size_t version_string_length;
176     unsigned int chksm, size;
177     char *rootfs_header;
178     size_t ptr = 0;
179 
180     rootfs_header = malloc(ROOTFS_HEADER_LEN);
181     if (!rootfs_header) {
182         ERR("Couldn't allocate memory for rootfs header!");
183         exit(EXIT_FAILURE);
184     }
185 
186     /* Prepare padding for firmware-version string here */
187     memset(rootfs_header, 0xff, ROOTFS_HEADER_LEN);
188 
189     chksm = zyxel_chksm((const unsigned char *)filesystem.data, filesystem.size);
190     size = htonl(filesystem.size);
191 
192     /* 4 bytes:  checksum of the rootfs image */
193     memcpy(rootfs_header + ptr, &chksm, 4);
194     ptr += 4;
195 
196     /* 4 bytes:  length of the contained rootfs image file (big endian) */
197     memcpy(rootfs_header + ptr, &size, 4);
198     ptr += 4;
199 
200     /* 32 bytes:  Firmware Version string (NUL terminated, 0xff padded) */
201     version_string_length = strlen(version) <= VERSION_STRING_LEN ? strlen(version) : VERSION_STRING_LEN;
202     memcpy(rootfs_header + ptr, version, version_string_length);
203     ptr += version_string_length;
204     /* Add null-terminator */
205     rootfs_header[ptr] = 0x0;
206 
207     return rootfs_header;
208 }
209 
210 char *generate_kernel_header(struct file_info kernel)
211 {
212     unsigned int chksm, size;
213     char *kernel_header;
214     size_t ptr = 0;
215 
216     kernel_header = malloc(KERNEL_HEADER_LEN);
217     if (!kernel_header) {
218         ERR("Couldn't allocate memory for kernel header!");
219         exit(EXIT_FAILURE);
220     }
221 
222     chksm = zyxel_chksm((const unsigned char *)kernel.data, kernel.size);
223     size = htonl(kernel.size);
224 
225     /* 4 bytes:  checksum of the kernel image */
226     memcpy(kernel_header + ptr, &chksm, 4);
227     ptr += 4;
228 
229     /* 4 bytes:  length of the contained kernel image file (big endian) */
230     memcpy(kernel_header + ptr, &size, 4);
231 
232     return kernel_header;
233 }
234 
235 unsigned int generate_board_header_checksum(char *kernel_hdr, char *rootfs_hdr, char *boardname)
236 {
237     char *board_hdr_tmp;
238     unsigned int sum;
239     size_t ptr = 0;
240 
241     /*
242      * The checksum of the board header is calculated over the first 2048 bytes of
243      * the header partition with the rootfs checksum used as a placeholder for then
244      * board checksum we calculate in this step. The checksum gained from this step
245      * is then used for the final board header partition.
246      */
247 
248     board_hdr_tmp = malloc(HEADER_PARTITION_CALC_LENGTH);
249     if (!board_hdr_tmp) {
250         ERR("Couldn't allocate memory for temporary board header!");
251         exit(EXIT_FAILURE);
252     }
253     memset(board_hdr_tmp, 0xff, HEADER_PARTITION_CALC_LENGTH);
254 
255     /* 40 bytes:  RootFS header */
256     memcpy(board_hdr_tmp, rootfs_hdr, ROOTFS_HEADER_LEN);
257     ptr += ROOTFS_HEADER_LEN;
258 
259     /* 4 bytes:  RootFS checksum (BE) as placeholder for board-header checksum */
260     memcpy(board_hdr_tmp + ptr, rootfs_hdr, 4);
261     ptr += 4;
262 
263     /* 32 bytes:  Model (e.g. "NBG6617", NUL termiated, 0xff padded) */
264     memcpy(board_hdr_tmp + ptr, boardname, strlen(boardname));
265     ptr += strlen(boardname);
266     /* Add null-terminator */
267     board_hdr_tmp[ptr] = 0x0;
268     ptr = ROOTFS_HEADER_LEN + 4 + BOARD_NAME_LEN;
269 
270     /* 8 bytes:  Kernel header */
271     if (kernel_hdr)
272         memcpy(board_hdr_tmp + ptr, kernel_hdr, 8);
273 
274     /* Calculate the checksum over the first 2048 bytes */
275     sum = zyxel_chksm((const unsigned char *)board_hdr_tmp, HEADER_PARTITION_CALC_LENGTH);
276     free(board_hdr_tmp);
277     return sum;
278 }
279 
280 char *generate_board_header(char *kernel_hdr, char *rootfs_hdr, char *boardname)
281 {
282     unsigned int board_checksum;
283     char *board_hdr;
284 
285     board_hdr = malloc(BOARD_HEADER_LEN);
286     if (!board_hdr) {
287         ERR("Couldn't allocate memory for board header!");
288         exit(EXIT_FAILURE);
289     }
290     memset(board_hdr, 0xff, BOARD_HEADER_LEN);
291 
292     /* 4 bytes:  checksum over the header partition (big endian) */
293     board_checksum = generate_board_header_checksum(kernel_hdr, rootfs_hdr, boardname);
294     memcpy(board_hdr, &board_checksum, 4);
295 
296     /* 32 bytes:  Model (e.g. "NBG6617", NUL termiated, 0xff padded) */
297     memcpy(board_hdr + 4, boardname, strlen(boardname));
298     board_hdr[4 + strlen(boardname)] = 0x0;
299 
300     return board_hdr;
301 }
302 
303 int build_image()
304 {
305     char *rootfs_header = NULL;
306     char *kernel_header = NULL;
307     char *board_header = NULL;
308 
309     size_t ptr;
310 
311     /* Load files */
312     if (kernel.name)
313         map_file(&kernel);
314     map_file(&rootfs);
315 
316     /* As ZyXEL Web-GUI only accept images with a rootfs equal or larger than the first firmware shipped
317      * for the device, we need to pad rootfs partition to this size. To perform further calculations, we
318      * decide the size of this part here. In case the rootfs we want to integrate in our image is larger,
319      * take it's size, otherwise the supplied size.
320      *
321      * Be careful! We rely on assertion of correct size to be performed beforehand. It is unknown if images
322      * with a to large rootfs are accepted or not.
323      */
324     rootfs_out.size = rootfs_size < rootfs.size ? rootfs.size : rootfs_size;
325 
326     /*
327      * Allocate memory and copy input rootfs for temporary output rootfs.
328      * This is important as we have to generate the rootfs checksum over the
329      * entire rootfs partition. As we might have to pad the partition to allow
330      * for flashing via ZyXEL's Web-GUI, we prepare the rootfs partition for the
331      * output image here (and also use it for calculating the rootfs checksum).
332      *
333      * The roofs padding has to be done with 0x00.
334      */
335     rootfs_out.data = calloc(rootfs_out.size, sizeof(char));
336     memcpy(rootfs_out.data, rootfs.data, rootfs.size);
337 
338     /* Prepare headers */
339     rootfs_header = generate_rootfs_header(rootfs_out, version_name);
340     if (kernel.name)
341         kernel_header = generate_kernel_header(kernel);
342     board_header = generate_board_header(kernel_header, rootfs_header, board_name);
343 
344     /* Prepare output file */
345     out.size = header_length + rootfs_out.size;
346     if (kernel.name)
347         out.size += kernel.size;
348     out.data = malloc(out.size);
349     memset(out.data, 0xFF, out.size);
350 
351     /* Build output image */
352     memcpy(out.data, rootfs_header, ROOTFS_HEADER_LEN);
353     memcpy(out.data + ROOTFS_HEADER_LEN, board_header, BOARD_HEADER_LEN);
354     if (kernel.name)
355         memcpy(out.data + ROOTFS_HEADER_LEN + BOARD_HEADER_LEN, kernel_header, KERNEL_HEADER_LEN);
356     ptr = header_length;
357     memcpy(out.data + ptr, rootfs_out.data, rootfs_out.size);
358     ptr += rootfs_out.size;
359     if (kernel.name)
360         memcpy(out.data + ptr, kernel.data, kernel.size);
361 
362     /* Write back output image */
363     write_file(&out);
364 
365     /* Free allocated memory */
366     if (kernel.name)
367         unmap_file(&kernel);
368     unmap_file(&rootfs);
369     free(out.data);
370     free(rootfs_out.data);
371 
372     free(rootfs_header);
373     if (kernel.name)
374         free(kernel_header);
375     free(board_header);
376 
377     return 0;
378 }
379 
380 int check_options()
381 {
382     if (!rootfs.name) {
383         ERR("No rootfs filename supplied");
384         return -2;
385     }
386 
387     if (!out.name) {
388         ERR("No output filename supplied");
389         return -3;
390     }
391 
392     if (!board_name) {
393         ERR("No board-name supplied");
394         return -4;
395     }
396 
397     if (!version_name) {
398         ERR("No version supplied");
399         return -5;
400     }
401 
402     if (rootfs_size <= 0) {
403         ERR("Invalid rootfs size supplied");
404         return -6;
405     }
406 
407     if (strlen(board_name) > 31) {
408         ERR("Board name is to long");
409         return -7;
410     }
411     return 0;
412 }
413 
414 int main(int argc, char *argv[])
415 {
416     int ret;
417     progname = basename(argv[0]);
418     while (1) {
419         int c;
420 
421         c = getopt(argc, argv, "b:k:o:r:s:v:l:h");
422         if (c == -1)
423             break;
424 
425         switch (c) {
426             case 'b':
427                 board_name = optarg;
428                 break;
429             case 'h':
430                 usage(EXIT_SUCCESS);
431                 break;
432             case 'k':
433                 kernel.name = optarg;
434                 break;
435             case 'o':
436                 out.name = optarg;
437                 break;
438             case 'r':
439                 rootfs.name = optarg;
440                 break;
441             case 's':
442                 sscanf(optarg, "%u", &rootfs_size);
443                 break;
444             case 'v':
445                 version_name = optarg;
446                 break;
447             case 'l':
448                 sscanf(optarg, "%u", &header_length);
449                 break;
450             default:
451                 usage(EXIT_FAILURE);
452                 break;
453         }
454     }
455 
456     ret = check_options();
457     if (ret)
458         usage(EXIT_FAILURE);
459 
460     return build_image();
461 }
462 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt