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