ramips: fix netgear r6120 factory image generation
[oweals/openwrt.git] / tools / firmware-utils / src / mksercommfw.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <asm/byteorder.h>
7
8 /* #define DEBUG 1 */
9
10 #ifdef DEBUG
11 #define DBG(...) {printf(__VA_ARGS__); }
12 #else
13 #define DBG(...) {}
14 #endif
15
16 #define ERR(...) {printf(__VA_ARGS__); }
17 #define ALIGN(a,b) ((a) + ((b) - ((a) % (b))))
18 #define ROOTFS_ALIGN 128
19 #define HEADER_SIZE 71
20
21 /*
22  * Fw Header Layout for Netgear / Sercomm devices (bytes)
23  *
24  * Size : 512 bytes + zipped image size
25  *
26  * Locations:
27  * magic  : 0-6    ASCII
28  * version: 7-11   fixed
29  * hwID   : 11-44  ASCII
30  * hwVer  : 45-54  ASCII
31  * swVer  : 55-62  uint32_t in BE
32  * magic  : 63-69  ASCII
33  * ChkSum : 511    Inverse value of the full image checksum while this location is 0x00
34  */
35
36 static const char* magic = "sErCoMm"; /* 7 */
37
38 /* 7-11: version control/download control ? */
39 static const unsigned char version[4] = { 0x00, 0x01, 0x00, 0x00 };
40
41
42 /* 512 onwards -> ZIP containing rootfs with the same Header */
43
44 struct file_info {
45         char* file_name; /* name of the file */
46         char* file_data; /* data of the file in memory */
47         u_int32_t file_size; /* length of the file */
48 };
49
50 static u_int8_t getCheckSum(char* data, int len) {
51         u_int8_t new = 0;
52
53         if (!data) {
54                 ERR("Invalid pointer provided!\n");
55                 return 0;
56         }
57
58         for (int i = 0; i < len; i++) {
59                 new += data[i];
60         }
61
62         return new;
63 }
64
65 static int bufferFile(struct file_info* finfo) {
66         int fs = 0;
67         FILE* fp = NULL;
68
69         if (!finfo || !finfo->file_name) {
70                 ERR("Invalid pointer provided!\n");
71                 return -1;
72         }
73
74         DBG("Opening file: %s\n", finfo->file_name);
75
76         if (!(fp = fopen(finfo->file_name, "rb"))) {
77                 ERR("Error opening file: %s\n", finfo->file_name);
78                 return -1;
79         }
80
81         /* Get filesize */
82         rewind(fp);
83         fseek(fp, 0L, SEEK_END);
84         fs = ftell(fp);
85         rewind(fp);
86
87         if (fs < 0) {
88                 ERR("Error getting filesize: %s\n", finfo->file_name);
89                 fclose(fp);
90                 return -1;
91         }
92
93         DBG("Filesize: %i\n", fs);
94         finfo->file_size = fs;
95
96         if (!(finfo->file_data = malloc(fs))) {
97                 ERR("Out of memory!\n");
98                 fclose(fp);
99                 return -1;
100         }
101
102         if (fread(finfo->file_data, 1, fs, fp) != fs) {
103                 ERR("Error reading file %s\n", finfo->file_name);
104                 fclose(fp);
105                 return -1;
106         }
107
108         DBG("File: read successful\n");
109         fclose(fp);
110
111         return 0;
112 }
113
114 static int writeFile(struct file_info* finfo) {
115         FILE* fp;
116
117         if (!finfo || !finfo->file_name) {
118                 ERR("Invalid pointer provided!\n");
119                 return -1;
120         }
121
122         DBG("Opening file: %s\n", finfo->file_name);
123
124         if (!(fp = fopen(finfo->file_name, "w"))) {
125                 ERR("Error opening file: %s\n", finfo->file_name);
126                 return -1;
127         }
128
129         DBG("Writing file: %s\n", finfo->file_name);
130
131         if (fwrite(finfo->file_data, 1, finfo->file_size, fp) != finfo->file_size) {
132                 ERR("Wanted to write, but something went wrong!\n");
133                 fclose(fp);
134                 return -1;
135         }
136
137         fclose(fp);
138         return 0;
139 }
140
141 static void fi_clean(struct file_info* finfo) {
142         if (!finfo)
143                 return;
144
145         if (finfo->file_name) {
146                 finfo->file_name = NULL;
147         }
148
149         if (finfo->file_data) {
150                 free(finfo->file_data);
151                 finfo->file_data = NULL;
152         }
153
154         finfo->file_size = 0;
155 }
156
157 static void usage(char* argv[]) {
158     printf("Usage: %s <sysupgradefile> <kernel_offset> <HWID> <HWVER> <SWID>\n"
159            "All are positional arguments ...    \n"
160            "    sysupgradefile:     File with the kernel uimage at 0\n"
161            "    kernel_offset:      Offset where the kernel is located (decimal, hex or octal notation)\n"
162            "    HWID:               Hardware ID, ASCII\n"
163            "    HWVER:              Hardware Version, ASCII\n"
164            "    SWID:               Software Version (decimal, hex or octal notation)\n"
165            "    \n"
166            , argv[0]);
167 }
168
169 int main(int argc, char* argv[]) {
170         int ret = 1;
171         int rootfsname_sz;
172         int zipfsname_sz;
173         int zipcmd_sz;
174         u_int32_t kernel_offset = 0x90000; /* offset for the kernel inside the rootfs, default val */
175         u_int32_t swVer = 0;
176         struct file_info sysupgrade = { 0 };
177         struct file_info header = { 0 };
178         struct file_info rootfs = { 0 };
179         struct file_info zippedfs = { 0 };
180         struct file_info image = { 0 };
181         char* hwID = NULL;
182         char* hwVer = NULL;
183         char* rootfsname = NULL;
184         char* zipfsname = NULL;
185         char* zipcmd = NULL;
186         u_int8_t chkSum;
187
188         if (argc == 2) {
189                 struct file_info myfile = { argv[1], 0, 0 };
190
191                 if (bufferFile(&myfile))
192                         return 1;
193
194                 chkSum = getCheckSum(myfile.file_data, myfile.file_size);
195                 printf("Checksum for File: 0x%hhX\n", chkSum);
196
197                 return 0;
198         }
199
200         if (argc != 6) {
201                 usage(argv);
202                 return 1;
203         }
204
205         printf("Building fw image for sercomm devices ..\n");
206
207         /* process args */
208         hwID = argv[3];
209         hwVer = argv[4];
210
211         sysupgrade.file_name = argv[1];
212         image.file_name = argv[1];
213         kernel_offset = (u_int32_t) strtol(argv[2], NULL, 0);
214         swVer = (u_int32_t) strtol(argv[5], NULL, 0);
215         swVer = __cpu_to_be32(swVer);
216
217         /* Check if files actually exist */
218         if (access(sysupgrade.file_name, (F_OK | R_OK))) {
219                 /* Error */
220                 ERR("File not found: %s\n", sysupgrade.file_name);
221                 goto cleanup;
222         }
223
224         /* Calculate amount of required memory (incl. 0-term) */
225         rootfsname_sz = strlen(sysupgrade.file_name) + 7 + 1;
226         zipfsname_sz = strlen(sysupgrade.file_name) + 7 + 4 + 1;
227
228         /* Allocate required memory */
229         if (!(rootfsname = (char*) malloc(rootfsname_sz)) || !(zipfsname =
230                         (char*) malloc(zipfsname_sz))) {
231                 /* Error */
232                 ERR("Out of memory!\n");
233                 goto cleanup;
234         }
235
236         /* Create filenames */
237         if (snprintf(rootfsname, rootfsname_sz, "%s.rootfs", sysupgrade.file_name)
238                         >= rootfsname_sz
239                         || snprintf(zipfsname, zipfsname_sz, "%s.rootfs.zip",
240                                         sysupgrade.file_name) >= zipfsname_sz) {
241                 /* Error */
242                 ERR("Buffer too small!\n");
243                 goto cleanup;
244         }
245
246         /* Buffer all files */
247         if (bufferFile(&sysupgrade)) {
248                 /* Error */
249                 goto cleanup;
250         }
251
252         DBG("Building header: %s %s %2X %s\n", hwID, hwVer, swVer, magic);
253
254         /* Construct the firmware header/magic */
255         header.file_name = NULL;
256         header.file_size = HEADER_SIZE;
257
258         if (!(header.file_data = (char*) calloc(1, HEADER_SIZE))) {
259                 /* Error */
260                 ERR("Out of memory!\n");
261                 goto cleanup;
262         }
263
264         strncpy(header.file_data + 0, magic, 7);
265         memcpy(header.file_data + 7, version, sizeof(version));
266         strncpy(header.file_data + 11, hwID, 34);
267         strncpy(header.file_data + 45, hwVer, 10);
268         memcpy(header.file_data + 55, &swVer, sizeof(swVer));
269         strncpy(header.file_data + 63, magic, 7);
270
271         DBG("Creating rootfs ..\n");
272
273         /* Construct a rootfs */
274         rootfs.file_name = rootfsname;
275         rootfs.file_size = ALIGN(
276                         sysupgrade.file_size + kernel_offset + header.file_size,
277                         ROOTFS_ALIGN);
278
279         if (!(rootfs.file_data = calloc(1, rootfs.file_size))) {
280                 /* Error */
281                 ERR("Out of memory!\n");
282                 goto cleanup;
283         }
284
285         /* copy Owrt image to kernel location */
286         memcpy(rootfs.file_data + kernel_offset, sysupgrade.file_data,
287                         sysupgrade.file_size);
288
289         /* Append header after the owrt image.  The updater searches for it */
290         memcpy(rootfs.file_data + kernel_offset + sysupgrade.file_size,
291                         header.file_data, header.file_size);
292
293         /* Write to file */
294         if (writeFile(&rootfs)) {
295                 /* Error */
296                 goto cleanup;
297         }
298
299         /* Construct a zip */
300         DBG("Preparing to zip ..\n");
301
302         /* now that we got the rootfs, repeat the whole thing again(sorta):
303          * 1. zip the rootfs */
304         zipcmd_sz = 3 + 1 + strlen(zipfsname) + 1 + strlen(rootfs.file_name) + 1;
305
306         if (!(zipcmd = malloc(zipcmd_sz))) {
307                 /* Error */
308                 ERR("Out of memory!\n");
309                 goto cleanup;
310         }
311
312         if (snprintf(zipcmd, zipcmd_sz, "%s %s %s", "zip", zipfsname,
313                         rootfs.file_name) >= zipcmd_sz) {
314                 /* Error */
315                 ERR("Buffer too small!\n");
316                 goto cleanup;
317         }
318
319         if (system(zipcmd)) {
320                 /* Error */
321                 ERR("Error creating a zip file!\n");
322                 goto cleanup;
323         }
324
325         /* and load zipped fs */
326         zippedfs.file_name = zipfsname;
327
328         if (bufferFile(&zippedfs)) {
329                 /* Error */
330                 goto cleanup;
331         }
332
333         DBG("Creating Image.\n");
334
335         /* 2. create new file 512 + rootfs size */
336         image.file_size = zippedfs.file_size + 512;
337         if (!(image.file_data = malloc(zippedfs.file_size + 512))) {
338                 /* Error */
339                 ERR("Out of memory!\n");
340                 goto cleanup;
341         }
342
343         /* 3. add header to file */
344         memcpy(image.file_data, header.file_data, header.file_size);
345
346         /* 4. clear remaining space */
347         if (header.file_size < 512)
348                 memset(image.file_data + header.file_size, 0, 512 - header.file_size);
349
350         /* 5. copy zipfile at loc 512 */
351         memcpy(image.file_data + 512, zippedfs.file_data, zippedfs.file_size);
352
353         /* 6. do a checksum run, and compute checksum */
354         chkSum = getCheckSum(image.file_data, image.file_size);
355
356         DBG("Checksum for Image: %hhX\n", chkSum);
357
358         /* 7. write the checksum inverted into byte 511 to bring it to 0 on verification */
359         chkSum = (chkSum ^ 0xFF) + 1;
360         image.file_data[511] = (char) chkSum;
361
362         chkSum = getCheckSum(image.file_data, image.file_size);
363         DBG("Checksum for after fix: %hhX\n", chkSum);
364
365         if (chkSum != 0) {
366                 ERR("Invalid checksum!\n")
367                 goto cleanup;
368         }
369
370         /* 8. pray that the updater will accept the file */
371         if (writeFile(&image)) {
372                 /* Error */
373                 goto cleanup;
374         }
375
376         /* All seems OK */
377         ret = 0;
378
379         cleanup:
380
381         if (rootfs.file_name && !access(rootfs.file_name, F_OK | W_OK))
382                 remove(rootfs.file_name);
383
384         if (zippedfs.file_name && !access(zippedfs.file_name, F_OK | W_OK))
385                 remove(zippedfs.file_name);
386
387         fi_clean(&sysupgrade);
388         fi_clean(&header);
389         fi_clean(&rootfs);
390         fi_clean(&zippedfs);
391         fi_clean(&image);
392
393         if (rootfsname)
394                 free(rootfsname);
395
396         if (zipfsname)
397                 free(zipfsname);
398
399         if (zipcmd)
400                 free(zipcmd);
401
402         return ret;
403 }