tools: wrt400n: fix making factory images with kernel bigger than 1MB
[openwrt/openwrt.git] / tools / firmware-utils / src / wrt400n.c
1 /*
2 * WRT400n - Firmware Generation Creator
3 *
4 * Creates a firmware image for the Linksys WRT400n router,
5 * that can be uploaded via the firmware upload page,
6 * from a kernel image file and root fs file
7 *
8 * Author: Sandeep Mistry
9 * Author: Yousong Zhou
10 * - remove size limit on kernel and rootfs part
11 */
12 #include <arpa/inet.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdint.h>
16 #include <stdbool.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include "cyg_crc.h"
24
25 // Prelimiary test shows that the OEM upgrade program only checks checksum of
26 // the whole firmware image, ignoring specific checksums of kernel and rootfs
27 // part. But we are computing crc32 sums of fixed length of data here in lieu
28 // of read_fw utility found in OEM SDK
29 #define CRC_SZ_KERNEL 0x100000
30 #define CRC_SZ_ROOTFS 0x2fffc4
31 // OEM firmware has size limit on "linux" and "rootfs" partition. That's also
32 // the limit for factory firmware images as fwupgrade utility in the OEM system
33 // needs to write kernel and rootfs to those two partitions (/dev/mtd2 and
34 // /dev/mtd3 to be exact)
35 #define SZ_MAX_KERNEL (0x180000 - 0x40000)
36 #define SZ_MAX_ROOTFS (0x7b0000 - 0x18000)
37
38
39 // Header format:
40 //
41 // GPL Tarball: http://downloads.linksysbycisco.com/downloads/WRT400N_1.0.01.19_US.tar,0.gz
42 // File: WRT400N_1.0.01.19_US/FW_WRT400N_1.0.01.19_US_20081229/GTK/user/include/fw_upgrade.h
43 //
44 // Description
45 // - checksum: CRC32 of kernel and root fs, back to back
46 // - magic: GMTKRT400N
47 // - kernel_length: kernel length in bytes
48 // - kernel_upgrade_flag: should we upgrade the kernel - set to 1
49 // - rootfs_length: root fs length in byte
50 // - rootfs_upgrade_flag: should we upgrade the root fs - set to 1
51 // - kernel_checksum: Gary S. Brown's 32 bit CRC algorithm for kernel, with remaining bits
52 // set to 0xFF upto 0x100000 bytes (total length)
53 // - rootfs_checksum: Gary S. Brown's 32 bit CRC algorithm for root fs, with remaining bits
54 // set to 0xFF upto 0x2FFFC4 bytes (total length)
55 // - fw_totalsize: total firmware image file length (header length + kernel length + root fs length)
56 // - reserved[4]: reserved ??? - set to all 0xFF
57 struct imghdr_t
58 {
59 uint32_t checksum; /* CRC32 */
60 uint8_t magic[12]; /* The value of GTIMG_MAGIC */
61 uint32_t kernel_length; /* The length of the kernel image */
62 //uint32_t kernel_entry_point; /* Kernel's entry point for RedBoot's information */
63 uint32_t kernel_upgrade_flag; /* Set to 1 if we need to upgrade the kernel parition of the Flash */
64 uint32_t rootfs_length; /* The length of the rootfs image */
65 //uint32_t rootfs_entry_point; /* Not in use */
66 uint32_t rootfs_upgrade_flag; /* Set to 1 if we need to upgrade the rootfs parition of the Flash */
67
68 // Add 3 items by Vic Yu, 2006-05/10
69 uint32_t kernel_checksum;
70 uint32_t rootfs_checksum;
71 uint32_t fw_totalsize;
72 uint32_t reserved[4];
73 };
74
75 struct file_info {
76 char *filename;
77 int filesize;
78 int fd;
79 uint32_t crc32sum;
80 int crc_sz;
81 };
82
83 int get_file_info(struct file_info *fi, uint32_t *crc32_acc)
84 {
85 int fd;
86 int filesize = 0;
87 uint32_t crc32sum = 0;
88 uint32_t _crc32_acc;
89 int crc_rem = fi->crc_sz;
90 int sz;
91 char buf[1024];
92
93 if (crc32_acc)
94 _crc32_acc = *crc32_acc;
95 else
96 _crc32_acc = 0;
97
98 fd = open(fi->filename, O_RDONLY);
99 if(fd < 0) {
100 fprintf(stderr, "error: opening '%s'\n", fi->filename);
101 return -1;
102 }
103 while (true) {
104 sz = read(fd, buf, sizeof(buf));
105 if (sz > 0) {
106 if (crc32_acc)
107 _crc32_acc = cyg_crc32_accumulate(_crc32_acc, buf, sz);
108 if (crc_rem > 0) {
109 if (crc_rem < sz) {
110 crc32sum = cyg_crc32_accumulate(crc32sum, buf, crc_rem);
111 crc_rem = 0;
112 } else {
113 crc32sum = cyg_crc32_accumulate(crc32sum, buf, sz);
114 crc_rem -= sz;
115 }
116 }
117 filesize += sz;
118 } else if (sz == 0) {
119 break;
120 } else {
121 fprintf(stderr, "error read '%s'", strerror(errno));
122 return -1;
123 }
124 }
125
126 if (crc_rem) {
127 memset(buf, 0xff, sizeof(buf));
128 while (crc_rem > 0) {
129 if (crc_rem > sizeof(buf)) {
130 crc32sum = cyg_crc32_accumulate(crc32sum, buf, sizeof(buf));
131 crc_rem -= sizeof(buf);
132 } else {
133 crc32sum = cyg_crc32_accumulate(crc32sum, buf, crc_rem);
134 crc_rem = 0;
135 break;
136 }
137 }
138 }
139
140 fi->fd = fd;
141 fi->filesize = filesize;
142 fi->crc32sum = crc32sum;
143 if (crc32_acc)
144 *crc32_acc = _crc32_acc;
145 return 0;
146 }
147
148 int copy_file(int fromfd, int tofd)
149 {
150 int szr, szw;
151 int buf[4096];
152
153 if (lseek(fromfd, SEEK_SET, 0) < 0) {
154 fprintf(stderr, "lseek: %s\n", strerror(errno));
155 return -1;
156 }
157 while (true) {
158 szr = read(fromfd, buf, sizeof(buf));
159 if (szr > 0) {
160 szw = write(tofd, buf, szr);
161 if (szw != szr) {
162 fprintf(stderr, "copy_file: error writing %d bytes: %s\n", szr, strerror(errno));
163 return -1;
164 }
165 } else if (szr == 0) {
166 break;
167 } else {
168 fprintf(stderr, "copy_file: error reading: %s\n", strerror(errno));
169 return -1;
170 }
171 }
172 return 0;
173 }
174
175 int main(int argc, char *argv[])
176 {
177 struct file_info kernel_fi;
178 struct file_info rootfs_fi;
179 struct file_info output_fi;
180 struct imghdr_t ih;
181 int fd, sz;
182 int ret = -1;
183
184 if(argc != 4) {
185 printf("Usage:\n\t%s <kernel file> <rootfs file> <output file>\n", argv[0]);
186 return 1;
187 }
188
189 kernel_fi.crc_sz = CRC_SZ_KERNEL;
190 rootfs_fi.crc_sz = CRC_SZ_ROOTFS;
191
192 kernel_fi.fd = -1;
193 rootfs_fi.fd = -1;
194 output_fi.fd = -1;
195
196 kernel_fi.filename = argv[1];
197 rootfs_fi.filename = argv[2];
198 output_fi.filename = argv[3];
199
200 kernel_fi.crc32sum = 0;
201 rootfs_fi.crc32sum = 0;
202 output_fi.crc32sum = 0xffffffff;
203
204 if (get_file_info(&kernel_fi, &output_fi.crc32sum) < 0)
205 goto done;
206 if (get_file_info(&rootfs_fi, &output_fi.crc32sum) < 0)
207 goto done;
208 output_fi.crc32sum = ~output_fi.crc32sum;
209
210 // print out stats
211 fprintf(stderr, "%s: size %d (0x%x), crc32 = 0x%x\n",
212 kernel_fi.filename, kernel_fi.filesize, kernel_fi.filesize, kernel_fi.crc32sum);
213 fprintf(stderr, "%s: size %d (0x%x), crc32 = 0x%x\n",
214 rootfs_fi.filename, rootfs_fi.filesize, rootfs_fi.filesize, rootfs_fi.crc32sum);
215 if (kernel_fi.filesize > SZ_MAX_KERNEL) {
216 fprintf(stderr, "%s: filesize exceeds 0x%x limit\n", SZ_MAX_KERNEL);
217 goto done;
218 }
219 if (rootfs_fi.filesize > SZ_MAX_ROOTFS) {
220 fprintf(stderr, "%s: filesize exceeds 0x%x limit\n", SZ_MAX_ROOTFS);
221 goto done;
222 }
223
224 // now for the header ...
225 memset(&ih, 0xff, sizeof(ih));
226 strcpy(ih.magic, "GMTKRT400N");
227 ih.checksum = htonl(output_fi.crc32sum);
228 ih.kernel_length = htonl(kernel_fi.filesize);
229 ih.rootfs_length = htonl(rootfs_fi.filesize);
230 ih.kernel_upgrade_flag = htonl(0x1);
231 ih.rootfs_upgrade_flag = htonl(0x1);
232 ih.kernel_checksum = htonl(kernel_fi.crc32sum);
233 ih.rootfs_checksum = htonl(rootfs_fi.crc32sum);
234 ih.fw_totalsize = htonl(kernel_fi.filesize + rootfs_fi.filesize + sizeof(ih));
235
236 output_fi.fd = open(output_fi.filename, O_WRONLY | O_CREAT | O_TRUNC,
237 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
238 sz = write(output_fi.fd, &ih, sizeof(ih));
239 if (sz != sizeof(ih)) {
240 fprintf(stderr, "error writing imghdr to output: %s\n", strerror(errno));
241 goto done;
242 }
243 if (copy_file(kernel_fi.fd, output_fi.fd)) {
244 fprintf(stderr, "error copying %s to %s\n", kernel_fi.filename, output_fi.filename);
245 goto done;
246 }
247 if (copy_file(rootfs_fi.fd, output_fi.fd)) {
248 fprintf(stderr, "error copying %s to %s\n", rootfs_fi.filename, output_fi.filename);
249 goto done;
250 }
251 // print some stats out
252 fprintf(stderr, "crc = 0x%x, total size = %d (0x%x)\n",
253 output_fi.crc32sum, ntohl(ih.fw_totalsize), ntohl(ih.fw_totalsize));
254 ret = 0;
255
256 done:
257 #define CLOSE_FI_FD(fi) do { \
258 if ((fi).fd >= 0) { \
259 close((fi).fd); \
260 } \
261 } while (0)
262
263 CLOSE_FI_FD(kernel_fi);
264 CLOSE_FI_FD(rootfs_fi);
265 CLOSE_FI_FD(output_fi);
266 return ret;
267 }