689dc8b37553841664b38d179674c636dd13613d
[openwrt/staging/ldir.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 char 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
106 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
107 fprintf(stream, "\n"
108 "Options:\n"
109 " -f <file> generate a factory flash image <file>\n"
110 " -s <file> generate a sysupgrade flash image <file>\n"
111 " -h show this screen\n");
112 exit(status);
113 }
114
115 int
116 process_image(char *progname, char *filename, op_mode_t opmode)
117 {
118 int fd;
119 void *ptr;
120 char namebuf[IH_NMLEN];
121 struct stat sbuf;
122 uint32_t offset_kernel, offset_sqfs, offset_end,
123 offset_sec_header, offset_eb, offset_image_end;
124 squashfs_sb_t *sqs;
125 image_header_t *hdr;
126
127 if ((fd = open(filename, O_RDWR, 0666)) < 0) {
128 fprintf (stderr, "%s: Can't open %s: %s\n",
129 progname, filename, strerror(errno));
130 return (EXIT_FAILURE);
131 }
132
133 if (fstat(fd, &sbuf) < 0) {
134 fprintf (stderr, "%s: Can't stat %s: %s\n",
135 progname, filename, strerror(errno));
136 return (EXIT_FAILURE);
137 }
138
139 if ((unsigned)sbuf.st_size < sizeof(image_header_t)) {
140 fprintf (stderr,
141 "%s: Bad size: \"%s\" is no valid image\n",
142 progname, filename);
143 return (EXIT_FAILURE);
144 }
145
146 ptr = (void *)mmap(0, sbuf.st_size,
147 PROT_READ | PROT_WRITE,
148 MAP_SHARED,
149 fd, 0);
150
151 if ((caddr_t)ptr == (caddr_t)-1) {
152 fprintf (stderr, "%s: Can't read %s: %s\n",
153 progname, filename, strerror(errno));
154 return (EXIT_FAILURE);
155 }
156
157 hdr = ptr;
158
159 if (ntohl(hdr->ih_magic) != IH_MAGIC) {
160 fprintf (stderr,
161 "%s: Bad Magic Number: \"%s\" is no valid image\n",
162 progname, filename);
163 return (EXIT_FAILURE);
164 }
165
166 if (opmode == FACTORY) {
167 strncpy(namebuf, hdr->tail.ih_name, IH_NMLEN);
168 hdr->tail.asus.kernel.major = 0;
169 hdr->tail.asus.kernel.minor = 0;
170 hdr->tail.asus.fs.major = 0;
171 hdr->tail.asus.fs.minor = 0;
172 strncpy((char *)&hdr->tail.asus.productid, "RT-N56U", IH_PRODLEN);
173 }
174
175 if (hdr->tail.asus.ih_ksz == 0)
176 hdr->tail.asus.ih_ksz = htonl(ntohl(hdr->ih_size) + sizeof(image_header_t));
177
178 offset_kernel = sizeof(image_header_t);
179 offset_sqfs = ntohl(hdr->tail.asus.ih_ksz);
180 sqs = ptr + offset_sqfs;
181 offset_sec_header = offset_sqfs + sqs->bytes_used;
182
183 /*
184 * Reserve space for the second header.
185 */
186 offset_end = offset_sec_header + sizeof(image_header_t);
187 offset_eb = ((offset_end>>16)+1)<<16;
188
189 if (opmode == FACTORY)
190 offset_image_end = offset_eb + 4;
191 else
192 offset_image_end = sbuf.st_size;
193 /*
194 * Move the second header at the end of the image.
195 */
196 offset_end = offset_sec_header;
197 offset_sec_header = offset_eb - sizeof(image_header_t);
198
199 /*
200 * Remove jffs2 markers between squashfs and eb boundary.
201 */
202 if (opmode == FACTORY)
203 memset(ptr+offset_end, 0xff ,offset_eb - offset_end);
204
205 /*
206 * Grow the image if needed.
207 */
208 if (offset_image_end > sbuf.st_size) {
209 (void) munmap((void *)ptr, sbuf.st_size);
210 ftruncate(fd, offset_image_end);
211 ptr = (void *)mmap(0, offset_image_end,
212 PROT_READ | PROT_WRITE,
213 MAP_SHARED,
214 fd, 0);
215 /*
216 * jffs2 marker
217 */
218 if (opmode == FACTORY) {
219 *(uint8_t *)(ptr+offset_image_end-4) = 0xde;
220 *(uint8_t *)(ptr+offset_image_end-3) = 0xad;
221 *(uint8_t *)(ptr+offset_image_end-2) = 0xc0;
222 *(uint8_t *)(ptr+offset_image_end-1) = 0xde;
223 }
224 }
225
226 /*
227 * Calculate checksums for the second header to be used after flashing.
228 */
229 if (opmode == FACTORY) {
230 hdr = ptr+offset_sec_header;
231 memcpy(hdr, ptr, sizeof(image_header_t));
232 strncpy(hdr->tail.ih_name, namebuf, IH_NMLEN);
233 calc_crc(hdr, ptr+offset_kernel, offset_sqfs - offset_kernel);
234 calc_crc((image_header_t *)ptr, ptr+offset_kernel, offset_image_end - offset_kernel);
235 } else {
236 calc_crc((image_header_t *)ptr, ptr+offset_kernel, offset_sqfs - offset_kernel);
237 }
238
239 if (sbuf.st_size > offset_image_end)
240 (void) munmap((void *)ptr, sbuf.st_size);
241 else
242 (void) munmap((void *)ptr, offset_image_end);
243
244 ftruncate(fd, offset_image_end);
245 (void) close (fd);
246
247 return EXIT_SUCCESS;
248 }
249
250 int
251 main(int argc, char **argv)
252 {
253 int opt;
254 char *filename = NULL;
255 char *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