firmware-utils: honor env SOURCE_DATE_EPOCH
[oweals/openwrt.git] / tools / firmware-utils / src / mkrtn56uimg.c
1 /*
2  *
3  *  Copyright (C) 2014 OpenWrt.org
4  *  Copyright (C) 2014 Mikko Hissa <mikko.hissa@werzek.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify it
7  *  under the terms of the GNU General Public License version 2 as published
8  *  by the Free Software Foundation.
9  *
10  */
11
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <netinet/in.h>
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <zlib.h>
22
23 #define IH_MAGIC        0x27051956
24 #define IH_NMLEN        32
25 #define IH_PRODLEN      23
26
27 #define IH_TYPE_INVALID         0
28 #define IH_TYPE_STANDALONE      1
29 #define IH_TYPE_KERNEL          2
30 #define IH_TYPE_RAMDISK         3
31 #define IH_TYPE_MULTI           4
32 #define IH_TYPE_FIRMWARE        5
33 #define IH_TYPE_SCRIPT          6
34 #define IH_TYPE_FILESYSTEM      7
35
36 /*
37  * Compression Types
38  */
39 #define IH_COMP_NONE            0
40 #define IH_COMP_GZIP            1
41 #define IH_COMP_BZIP2           2
42 #define IH_COMP_LZMA            3
43
44 typedef struct {
45         uint8_t major;
46         uint8_t minor;
47 } version_t;
48
49 typedef struct {
50         version_t       kernel;
51         version_t       fs;
52         uint8_t         productid[IH_PRODLEN];
53         uint8_t         sub_fs;
54         uint32_t        ih_ksz;
55 } asus_t;
56
57 typedef struct image_header {
58         uint32_t        ih_magic;
59         uint32_t        ih_hcrc;
60         uint32_t        ih_time;
61         uint32_t        ih_size;
62         uint32_t        ih_load;
63         uint32_t        ih_ep;
64         uint32_t        ih_dcrc;
65         uint8_t         ih_os;
66         uint8_t         ih_arch;
67         uint8_t         ih_type;
68         uint8_t         ih_comp;
69         union {
70                 uint8_t ih_name[IH_NMLEN];
71                 asus_t  asus;
72         } tail;
73 } image_header_t;
74
75 typedef struct squashfs_sb {
76         uint32_t        s_magic;
77         uint32_t        pad0[9];
78         uint64_t        bytes_used;
79 } squashfs_sb_t;
80
81 typedef enum {
82         NONE, FACTORY, SYSUPGRADE,
83 } op_mode_t;
84
85 void
86 calc_crc(image_header_t *hdr, void *data, uint32_t len)
87 {
88         /*
89          * Calculate payload checksum
90          */
91         hdr->ih_dcrc = htonl(crc32(0, (Bytef *)data, len));
92         hdr->ih_size = htonl(len);
93         /*
94          * Calculate header checksum
95          */
96         hdr->ih_hcrc = 0;
97         hdr->ih_hcrc = htonl(crc32(0, (Bytef *)hdr, sizeof(image_header_t)));
98 }
99
100
101 static void
102 usage(const char *progname, int status)
103 {
104         FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
105         int i;
106
107         fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
108         fprintf(stream, "\n"
109                         "Options:\n"
110                         "  -f <file>            generate a factory flash image <file>\n"
111                         "  -s <file>            generate a sysupgrade flash image <file>\n"
112                         "  -h                   show this screen\n");
113         exit(status);
114 }
115
116 int
117 process_image(char *progname, char *filename, op_mode_t opmode)
118 {
119         int             fd, len;
120         void            *data, *ptr;
121         char            namebuf[IH_NMLEN];
122         struct          stat sbuf;
123         uint32_t        checksum, offset_kernel, offset_sqfs, offset_end,
124                                 offset_sec_header, offset_eb, offset_image_end;
125         squashfs_sb_t *sqs;
126         image_header_t *hdr;
127
128         if ((fd = open(filename, O_RDWR, 0666)) < 0) {
129                 fprintf (stderr, "%s: Can't open %s: %s\n",
130                         progname, filename, strerror(errno));
131                 return (EXIT_FAILURE);
132         }
133
134         if (fstat(fd, &sbuf) < 0) {
135                 fprintf (stderr, "%s: Can't stat %s: %s\n",
136                         progname, filename, strerror(errno));
137                 return (EXIT_FAILURE);
138         }
139
140         if ((unsigned)sbuf.st_size < sizeof(image_header_t)) {
141                 fprintf (stderr,
142                         "%s: Bad size: \"%s\" is no valid image\n",
143                         progname, filename);
144                 return (EXIT_FAILURE);
145         }
146
147         ptr = (void *)mmap(0, sbuf.st_size,
148                                 PROT_READ | PROT_WRITE,
149                                 MAP_SHARED,
150                                 fd, 0);
151
152         if ((caddr_t)ptr == (caddr_t)-1) {
153                 fprintf (stderr, "%s: Can't read %s: %s\n",
154                         progname, filename, strerror(errno));
155                 return (EXIT_FAILURE);
156         }
157
158         hdr = ptr;
159
160         if (ntohl(hdr->ih_magic) != IH_MAGIC) {
161                 fprintf (stderr,
162                         "%s: Bad Magic Number: \"%s\" is no valid image\n",
163                         progname, filename);
164                 return (EXIT_FAILURE);
165         }
166
167         if (opmode == FACTORY) {
168                 strncpy(namebuf, hdr->tail.ih_name, IH_NMLEN);
169                 hdr->tail.asus.kernel.major = 0;
170                 hdr->tail.asus.kernel.minor = 0;
171                 hdr->tail.asus.fs.major = 0;
172                 hdr->tail.asus.fs.minor = 0;
173                 strncpy((char *)&hdr->tail.asus.productid, "RT-N56U", IH_PRODLEN);
174         }
175
176         if (hdr->tail.asus.ih_ksz == 0)
177                 hdr->tail.asus.ih_ksz = htonl(ntohl(hdr->ih_size) + sizeof(image_header_t));
178
179         offset_kernel = sizeof(image_header_t);
180         offset_sqfs = ntohl(hdr->tail.asus.ih_ksz);
181         sqs = ptr + offset_sqfs;
182         offset_sec_header = offset_sqfs + sqs->bytes_used;
183
184         /*
185          * Reserve space for the second header.
186          */
187         offset_end = offset_sec_header + sizeof(image_header_t);
188         offset_eb = ((offset_end>>16)+1)<<16;
189
190         if (opmode == FACTORY)
191                 offset_image_end = offset_eb + 4;
192         else
193                 offset_image_end = sbuf.st_size;
194         /*
195          * Move the second header at the end of the image.
196          */
197         offset_end = offset_sec_header;
198         offset_sec_header = offset_eb - sizeof(image_header_t);
199
200         /*
201          * Remove jffs2 markers between squashfs and eb boundary.
202          */
203         if (opmode == FACTORY)
204                 memset(ptr+offset_end, 0xff ,offset_eb - offset_end);
205
206         /*
207          * Grow the image if needed.
208          */
209         if (offset_image_end > sbuf.st_size) {
210                 (void) munmap((void *)ptr, sbuf.st_size);
211                 ftruncate(fd, offset_image_end);
212                 ptr = (void *)mmap(0, offset_image_end,
213                                                 PROT_READ | PROT_WRITE,
214                                                 MAP_SHARED,
215                                                 fd, 0);
216                 /*
217                  * jffs2 marker
218                  */
219                 if (opmode == FACTORY) {
220                         *(uint8_t *)(ptr+offset_image_end-4) = 0xde;
221                         *(uint8_t *)(ptr+offset_image_end-3) = 0xad;
222                         *(uint8_t *)(ptr+offset_image_end-2) = 0xc0;
223                         *(uint8_t *)(ptr+offset_image_end-1) = 0xde;
224                 }
225         }
226
227         /*
228          * Calculate checksums for the second header to be used after flashing.
229          */
230         if (opmode == FACTORY) {
231                 hdr = ptr+offset_sec_header;
232                 memcpy(hdr, ptr, sizeof(image_header_t));
233                 strncpy(hdr->tail.ih_name, namebuf, IH_NMLEN);
234                 calc_crc(hdr, ptr+offset_kernel, offset_sqfs - offset_kernel);
235                 calc_crc((image_header_t *)ptr, ptr+offset_kernel, offset_image_end - offset_kernel);
236         } else {
237                 calc_crc((image_header_t *)ptr, ptr+offset_kernel, offset_sqfs - offset_kernel);
238         }
239
240         if (sbuf.st_size > offset_image_end)
241                 (void) munmap((void *)ptr, sbuf.st_size);
242         else
243                 (void) munmap((void *)ptr, offset_image_end);
244
245         ftruncate(fd, offset_image_end);
246         (void) close (fd);
247
248         return EXIT_SUCCESS;
249 }
250
251 int
252 main(int argc, char **argv)
253 {
254         int             opt;
255         char            *filename, *progname;
256         op_mode_t       opmode = NONE;
257
258         progname = argv[0];
259
260         while ((opt = getopt(argc, argv,":s:f:h?")) != -1) {
261                 switch (opt) {
262                 case 's':
263                         opmode = SYSUPGRADE;
264                         filename = optarg;
265                         break;
266                 case 'f':
267                         opmode = FACTORY;
268                         filename = optarg;
269                         break;
270                 case 'h':
271                         opmode = NONE;
272                 default:
273                         usage(progname, EXIT_FAILURE);
274                         opmode = NONE;
275                 }
276         }
277
278         if(filename == NULL)
279                 opmode = NONE;
280
281         switch (opmode) {
282         case NONE:
283                 usage(progname, EXIT_FAILURE);
284                 break;
285         case FACTORY:
286         case SYSUPGRADE:
287                 return process_image(progname, filename, opmode);
288                 break;
289         }
290
291         return EXIT_SUCCESS;
292 }
293