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