18da13dd047783e85475f96c833d7acb515e2aa6
[openwrt/openwrt.git] / tools / firmware-utils / src / mktplinkfw-lib.c
1 /*
2 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org>
3 *
4 * This tool was based on:
5 * TP-Link WR941 V2 firmware checksum fixing tool.
6 * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 *
12 */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <string.h>
18 #include <unistd.h> /* for unlink() */
19 #include <libgen.h>
20 #include <getopt.h> /* for getopt() */
21 #include <stdarg.h>
22 #include <stdbool.h>
23 #include <endian.h>
24 #include <errno.h>
25 #include <sys/stat.h>
26
27 #include <arpa/inet.h>
28 #include <netinet/in.h>
29
30 #include "mktplinkfw-lib.h"
31 #include "md5.h"
32
33 extern char *ofname;
34 extern char *progname;
35 extern uint32_t kernel_len;
36 extern struct file_info kernel_info;
37 extern struct file_info rootfs_info;
38 extern struct flash_layout *layout;
39 extern uint32_t rootfs_ofs;
40 extern uint32_t rootfs_align;
41 extern int combined;
42 extern int strip_padding;
43 extern int add_jffs2_eof;
44
45 static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
46
47 void fill_header(char *buf, int len);
48
49 struct flash_layout *find_layout(struct flash_layout *layouts, const char *id)
50 {
51 struct flash_layout *ret;
52 struct flash_layout *l;
53
54 ret = NULL;
55 for (l = layouts; l->id != NULL; l++){
56 if (strcasecmp(id, l->id) == 0) {
57 ret = l;
58 break;
59 }
60 };
61
62 return ret;
63 }
64
65 void get_md5(const char *data, int size, uint8_t *md5)
66 {
67 MD5_CTX ctx;
68
69 MD5_Init(&ctx);
70 MD5_Update(&ctx, data, size);
71 MD5_Final(md5, &ctx);
72 }
73
74 int get_file_stat(struct file_info *fdata)
75 {
76 struct stat st;
77 int res;
78
79 if (fdata->file_name == NULL)
80 return 0;
81
82 res = stat(fdata->file_name, &st);
83 if (res){
84 ERRS("stat failed on %s", fdata->file_name);
85 return res;
86 }
87
88 fdata->file_size = st.st_size;
89 return 0;
90 }
91
92 int read_to_buf(const struct file_info *fdata, char *buf)
93 {
94 FILE *f;
95 int ret = EXIT_FAILURE;
96
97 f = fopen(fdata->file_name, "r");
98 if (f == NULL) {
99 ERRS("could not open \"%s\" for reading", fdata->file_name);
100 goto out;
101 }
102
103 errno = 0;
104 fread(buf, fdata->file_size, 1, f);
105 if (errno != 0) {
106 ERRS("unable to read from file \"%s\"", fdata->file_name);
107 goto out_close;
108 }
109
110 ret = EXIT_SUCCESS;
111
112 out_close:
113 fclose(f);
114 out:
115 return ret;
116 }
117
118 static int pad_jffs2(char *buf, int currlen, int maxlen)
119 {
120 int len;
121 uint32_t pad_mask;
122
123 len = currlen;
124 pad_mask = (64 * 1024);
125 while ((len < maxlen) && (pad_mask != 0)) {
126 uint32_t mask;
127 int i;
128
129 for (i = 10; i < 32; i++) {
130 mask = 1 << i;
131 if (pad_mask & mask)
132 break;
133 }
134
135 len = ALIGN(len, mask);
136
137 for (i = 10; i < 32; i++) {
138 mask = 1 << i;
139 if ((len & (mask - 1)) == 0)
140 pad_mask &= ~mask;
141 }
142
143 for (i = 0; i < sizeof(jffs2_eof_mark); i++)
144 buf[len + i] = jffs2_eof_mark[i];
145
146 len += sizeof(jffs2_eof_mark);
147 }
148
149 return len;
150 }
151
152 int write_fw(const char *ofname, const char *data, int len)
153 {
154 FILE *f;
155 int ret = EXIT_FAILURE;
156
157 f = fopen(ofname, "w");
158 if (f == NULL) {
159 ERRS("could not open \"%s\" for writing", ofname);
160 goto out;
161 }
162
163 errno = 0;
164 fwrite(data, len, 1, f);
165 if (errno) {
166 ERRS("unable to write output file");
167 goto out_flush;
168 }
169
170 DBG("firmware file \"%s\" completed", ofname);
171
172 ret = EXIT_SUCCESS;
173
174 out_flush:
175 fflush(f);
176 fclose(f);
177 if (ret != EXIT_SUCCESS) {
178 unlink(ofname);
179 }
180 out:
181 return ret;
182 }
183
184 /* Helper functions to inspect_fw() representing different output formats */
185 inline void inspect_fw_pstr(const char *label, const char *str)
186 {
187 printf("%-23s: %s\n", label, str);
188 }
189
190 inline void inspect_fw_phex(const char *label, uint32_t val)
191 {
192 printf("%-23s: 0x%08x\n", label, val);
193 }
194
195 inline void inspect_fw_phexdec(const char *label, uint32_t val)
196 {
197 printf("%-23s: 0x%08x / %8u bytes\n", label, val, val);
198 }
199
200 inline void inspect_fw_pmd5sum(const char *label, const uint8_t *val, const char *text)
201 {
202 int i;
203
204 printf("%-23s:", label);
205 for (i=0; i<MD5SUM_LEN; i++)
206 printf(" %02x", val[i]);
207 printf(" %s\n", text);
208 }
209
210 // header_size = sizeof(struct fw_header)
211 int build_fw(size_t header_size)
212 {
213 int buflen;
214 char *buf;
215 char *p;
216 int ret = EXIT_FAILURE;
217 int writelen = 0;
218
219 writelen = header_size + kernel_len;
220
221 if (combined)
222 buflen = writelen;
223 else
224 buflen = layout->fw_max_len;
225
226 buf = malloc(buflen);
227 if (!buf) {
228 ERR("no memory for buffer\n");
229 goto out;
230 }
231
232 memset(buf, 0xff, buflen);
233 p = buf + header_size;
234 ret = read_to_buf(&kernel_info, p);
235 if (ret)
236 goto out_free_buf;
237
238 if (!combined) {
239 if (rootfs_align)
240 p = buf + writelen;
241 else
242 p = buf + rootfs_ofs;
243
244 ret = read_to_buf(&rootfs_info, p);
245 if (ret)
246 goto out_free_buf;
247
248 if (rootfs_align)
249 writelen += rootfs_info.file_size;
250 else
251 writelen = rootfs_ofs + rootfs_info.file_size;
252
253 if (add_jffs2_eof)
254 writelen = pad_jffs2(buf, writelen, layout->fw_max_len);
255 }
256
257 if (!strip_padding)
258 writelen = buflen;
259
260 fill_header(buf, writelen);
261 ret = write_fw(ofname, buf, writelen);
262 if (ret)
263 goto out_free_buf;
264
265 ret = EXIT_SUCCESS;
266
267 out_free_buf:
268 free(buf);
269 out:
270 return ret;
271 }