1/*
2
3	Copyright 2005, Broadcom Corporation
4	All Rights Reserved.
5
6	THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
7	KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
8	SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
9	FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
10
11*/
12/*
13
14	Modified for Tomato Firmware
15	Portions, Copyright (C) 2006-2009 Jonathan Zarate
16
17*/
18
19#include "rc.h"
20
21#include <limits.h>
22#include <sys/sysmacros.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <error.h>
28#include <sys/ioctl.h>
29#include <sys/sysinfo.h>
30#include <sys/mman.h>
31#ifdef LINUX26
32#include <linux/compiler.h>
33#include <mtd/mtd-user.h>
34#else
35#include <linux/mtd/mtd.h>
36#endif
37#include <stdint.h>
38
39#include <trxhdr.h>
40#include <bcmutils.h>
41
42#ifdef RTCONFIG_BCMARM
43#include <bcmendian.h>
44#include <bcmnvram.h>
45#include <shutils.h>
46#endif
47//	#define DEBUG_SIMULATE
48
49
50struct code_header {
51	char magic[4];
52	char res1[4];
53	char fwdate[3];
54	char fwvern[3];
55	char id[4];
56	char hw_ver;
57	char res2;
58	unsigned short flags;
59	unsigned char res3[10];
60} ;
61
62// -----------------------------------------------------------------------------
63
64static uint32 *crc_table = NULL;
65
66static void crc_done(void)
67{
68	free(crc_table);
69	crc_table = NULL;
70}
71
72// -----------------------------------------------------------------------------
73
74#ifdef RTCONFIG_BCMARM
75static int mtd_open_old(const char *mtdname, mtd_info_t *mi)
76#else
77static int mtd_open(const char *mtdname, mtd_info_t *mi)
78#endif
79{
80	char path[256];
81	int part;
82	int size;
83	int f;
84
85	if (mtd_getinfo(mtdname, &part, &size)) {
86		sprintf(path, MTD_DEV(%d), part);
87		if ((f = open(path, O_RDWR|O_SYNC)) >= 0) {
88			if ((mi) && ioctl(f, MEMGETINFO, mi) != 0) {
89				close(f);
90				return -1;
91			}
92			return f;
93		}
94	}
95	return -1;
96}
97
98static int _unlock_erase(const char *mtdname, int erase)
99{
100	int mf;
101	mtd_info_t mi;
102	erase_info_t ei;
103	int r;
104
105	if (!wait_action_idle(5)) return 0;
106	set_action(ACT_ERASE_NVRAM);
107
108	r = 0;
109#ifdef RTCONFIG_BCMARM
110	if ((mf = mtd_open_old(mtdname, &mi)) >= 0) {
111#else
112	if ((mf = mtd_open(mtdname, &mi)) >= 0) {
113#endif
114			r = 1;
115#if 1
116			ei.length = mi.erasesize;
117			for (ei.start = 0; ei.start < mi.size; ei.start += mi.erasesize) {
118				printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, (ei.start + ei.length) - 1);
119				fflush(stdout);
120
121				if (ioctl(mf, MEMUNLOCK, &ei) != 0) {
122//					perror("MEMUNLOCK");
123//					r = 0;
124//					break;
125				}
126				if (erase) {
127					if (ioctl(mf, MEMERASE, &ei) != 0) {
128						perror("MEMERASE");
129						r = 0;
130						break;
131					}
132				}
133			}
134#else
135			ei.start = 0;
136			ei.length = mi.size;
137
138			printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, ei.length - 1);
139			fflush(stdout);
140
141			if (ioctl(mf, MEMUNLOCK, &ei) != 0) {
142				perror("MEMUNLOCK");
143				r = 0;
144			}
145			else if (erase) {
146				if (ioctl(mf, MEMERASE, &ei) != 0) {
147					perror("MEMERASE");
148					r = 0;
149				}
150			}
151#endif
152
153			// checkme:
154			char buf[2];
155			read(mf, &buf, sizeof(buf));
156			close(mf);
157	}
158
159	set_action(ACT_IDLE);
160
161	if (r) printf("\"%s\" successfully %s.\n", mtdname, erase ? "erased" : "unlocked");
162        else printf("\nError %sing MTD\n", erase ? "eras" : "unlock");
163
164	sleep(1);
165	return r;
166}
167
168int mtd_unlock(const char *mtdname)
169{
170	return _unlock_erase(mtdname, 0);
171}
172
173#ifdef RTCONFIG_BCMARM
174int mtd_erase_old(const char *mtdname)
175#else
176int mtd_erase(const char *mtdname)
177#endif
178{
179	return _unlock_erase(mtdname, 1);
180}
181
182#ifdef RTCONFIG_BCMARM
183int mtd_unlock_erase_main_old(int argc, char *argv[])
184#else
185int mtd_unlock_erase_main(int argc, char *argv[])
186#endif
187{
188	char c;
189	char *dev = NULL;
190
191	while ((c = getopt(argc, argv, "d:")) != -1) {
192		switch (c) {
193		case 'd':
194			dev = optarg;
195			break;
196		}
197	}
198
199	if (!dev) {
200		usage_exit(argv[0], "-d part");
201	}
202
203	return _unlock_erase(dev, strstr(argv[0], "erase") ? 1 : 0);
204}
205
206#ifdef RTCONFIG_BCMARM
207int mtd_write_main_old(int argc, char *argv[])
208#else
209int mtd_write_main(int argc, char *argv[])
210#endif
211{
212	int mf = -1;
213	mtd_info_t mi;
214	erase_info_t ei;
215	FILE *f;
216	unsigned char *buf = NULL, *p, *bounce_buf = NULL;
217	const char *error;
218	long filelen = 0, n, wlen, unit_len;
219	struct sysinfo si;
220	uint32 ofs;
221	char c;
222	char *iname = NULL;
223	char *dev = NULL;
224	char msg_buf[2048];
225	int alloc = 0, bounce = 0, fd;
226#ifdef DEBUG_SIMULATE
227	FILE *of;
228#endif
229
230	while ((c = getopt(argc, argv, "i:d:")) != -1) {
231		switch (c) {
232		case 'i':
233			iname = optarg;
234			break;
235		case 'd':
236			dev = optarg;
237			break;
238		}
239	}
240
241	if ((iname == NULL) || (dev == NULL)) {
242		usage_exit(argv[0], "-i file -d part");
243	}
244
245	if (!wait_action_idle(10)) {
246		printf("System is busy\n");
247		return 1;
248	}
249
250	set_action(ACT_WEB_UPGRADE);
251
252	if ((f = fopen(iname, "r")) == NULL) {
253		error = "Error opening input file";
254		goto ERROR;
255	}
256
257	fd = fileno(f);
258	fseek( f, 0, SEEK_END);
259	filelen = ftell(f);
260	fseek( f, 0, SEEK_SET);
261	_dprintf("file len=0x%x\n", filelen);
262
263#ifdef RTCONFIG_BCMARM
264	if ((mf = mtd_open_old(dev, &mi)) < 0) {
265#else
266	if ((mf = mtd_open(dev, &mi)) < 0) {
267#endif
268		snprintf(msg_buf, sizeof(msg_buf), "Error opening MTD device. (errno %d (%s))", errno, strerror(errno));
269		error = msg_buf;
270		goto ERROR;
271	}
272
273	if (mi.erasesize < sizeof(struct trx_header)) {
274		error = "Error obtaining MTD information";
275		goto ERROR;
276	}
277
278	_dprintf("mtd size=%x, erasesize=%x, writesize=%x, type=%x\n", mi.size, mi.erasesize, mi.writesize, mi.type);
279
280	unit_len = ROUNDUP(filelen, mi.erasesize);
281	if (unit_len > mi.size) {
282		error = "File is too big to fit in MTD";
283		goto ERROR;
284	}
285
286	if ((buf = mmap(0, filelen, PROT_READ, MAP_SHARED, fd, 0)) == (unsigned char*)MAP_FAILED) {
287		_dprintf("mmap %x bytes fail!. errno %d (%s).\n", filelen, errno, strerror(errno));
288		alloc = 1;
289	}
290
291	sysinfo(&si);
292	if (alloc) {
293		if ((si.freeram * si.mem_unit) <= (unit_len + (4096 * 1024)))
294			unit_len = mi.erasesize;
295	}
296
297	if (mi.type == MTD_UBIVOLUME) {
298		if (!(bounce_buf = malloc(mi.writesize))) {
299			error = "Not enough memory";
300			goto ERROR;
301		}
302	}
303	_dprintf("freeram=%lx unit_len=%lx filelen=%lx mi.erasesize=%x mi.writesize=%x\n",
304		si.freeram, unit_len, filelen, mi.erasesize, mi.writesize);
305
306	if (alloc && !(buf = malloc(unit_len))) {
307		error = "Not enough memory";
308		goto ERROR;
309	}
310
311#ifdef DEBUG_SIMULATE
312	if ((of = fopen("/mnt/out.bin", "w")) == NULL) {
313		error = "Error creating test file";
314		goto ERROR;
315	}
316#endif
317
318	for (ei.start = ofs = 0, ei.length = unit_len, n = 0, error = NULL, p = buf;
319	     ofs < filelen;
320	     ofs += n, ei.start += unit_len)
321	{
322		wlen = n = MIN(unit_len, filelen - ofs);
323		if (mi.type == MTD_UBIVOLUME) {
324			if (n >= mi.writesize) {
325				n &= ~(mi.writesize - 1);
326				wlen = n;
327			} else {
328				if (!alloc)
329					memcpy(bounce_buf, p, n);
330				bounce = 1;
331				p = bounce_buf;
332				wlen = ROUNDUP(n, mi.writesize);
333			}
334		}
335
336		if (alloc && safe_fread(p, 1, n, f) != n) {
337			error = "Error reading file";
338			break;
339		}
340
341		_dprintf("ofs=%x n=%lx/%lx ei.start=%x ei.length=%x\n", ofs, n, wlen, ei.start, ei.length);
342
343#ifdef DEBUG_SIMULATE
344		if (fwrite(p, 1, wlen, of) != wlen) {
345			fclose(of);
346			error = "Error writing to test file";
347			break;
348		}
349#else
350		if (ei.start == ofs) {
351			ioctl(mf, MEMUNLOCK, &ei);
352			if (ioctl(mf, MEMERASE, &ei) != 0) {
353				snprintf(msg_buf, sizeof(msg_buf), "Error erasing MTD block. (errno %d (%s))", errno, strerror(errno));
354				error = msg_buf;
355				break;
356			}
357		}
358		if (write(mf, p, wlen) != wlen) {
359			snprintf(msg_buf, sizeof(msg_buf), "Error writing to MTD device. (errno %d (%s))", errno, strerror(errno));
360			error = msg_buf;
361			break;
362		}
363#endif
364
365		if (!(alloc || bounce))
366			p += n;
367	}
368
369#ifdef DEBUG_SIMULATE
370	fclose(of);
371#endif
372
373ERROR:
374	if (!alloc)
375		munmap((void*) buf, filelen);
376	else
377		free(buf);
378
379	if (bounce_buf)
380		free(bounce_buf);
381
382	if (mf >= 0) {
383		// dummy read to ensure chip(s) are out of lock/suspend state
384		read(mf, &n, sizeof(n));
385		close(mf);
386	}
387	if (f) fclose(f);
388
389	crc_done();
390
391	set_action(ACT_IDLE);
392
393	_dprintf("%s\n",  error ? error : "Image successfully flashed");
394	return (error ? 1 : 0);
395}
396
397#ifdef RTCONFIG_BCMARM
398
399/*
400 * Open an MTD device
401 * @param       mtd     path to or partition name of MTD device
402 * @param       flags   open() flags
403 * @return      return value of open()
404 */
405int
406mtd_open(const char *mtd, int flags)
407{
408        FILE *fp;
409        char dev[PATH_MAX];
410        int i;
411
412        if ((fp = fopen("/proc/mtd", "r"))) {
413                while (fgets(dev, sizeof(dev), fp)) {
414                        if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
415#ifdef LINUX26
416                                snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
417#else
418                                snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
419#endif
420                                fclose(fp);
421                                return open(dev, flags);
422                        }
423                }
424                fclose(fp);
425        }
426
427        return open(mtd, flags);
428}
429
430/*
431 * Erase an MTD device
432 * @param       mtd     path to or partition name of MTD device
433 * @return      0 on success and errno on failure
434 */
435int
436mtd_erase(const char *mtd)
437{
438        int mtd_fd;
439        mtd_info_t mtd_info;
440        erase_info_t erase_info;
441#ifdef RTAC87U
442	char erase_err[255] = {0};
443#endif
444
445        /* Open MTD device */
446        if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0) {
447                perror(mtd);
448                return errno;
449        }
450
451        /* Get sector size */
452        if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
453                perror(mtd);
454                close(mtd_fd);
455                return errno;
456        }
457
458        erase_info.length = mtd_info.erasesize;
459
460        for (erase_info.start = 0;
461             erase_info.start < mtd_info.size;
462             erase_info.start += mtd_info.erasesize) {
463                (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
464                if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0) {
465                        perror(mtd);
466                        close(mtd_fd);
467#ifdef RTAC87U
468						sprintf(erase_err, "logger -t ATE mtd_erase failed: [%d]", errno);
469						system(erase_err);
470#endif
471                        return errno;
472                }
473        }
474
475        close(mtd_fd);
476#ifdef RTAC87U
477	sprintf(erase_err, "logger -t ATE mtd_erase OK:[%d]", errno);
478	system(erase_err);
479#endif
480        return 0;
481}
482
483static char *
484base64enc(const char *p, char *buf, int len)
485{
486        char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
487                "0123456789+/";
488        char *s = buf;
489
490        while (*p) {
491                if (s >= buf+len-4)
492                        break;
493                *(s++) = al[(*p >> 2) & 0x3F];
494                *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
495                *s = *(s+1) = '=';
496                *(s+2) = 0;
497                if (! *(++p)) break;
498                *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
499                if (! *(++p)) break;
500                *(s++) = al[*(p++) & 0x3F];
501        }
502
503        return buf;
504}
505
506enum {
507        METHOD_GET,
508        METHOD_POST
509};
510
511static int
512wget(int method, const char *server, char *buf, size_t count, off_t offset)
513{
514        char url[PATH_MAX] = { 0 }, *s;
515        char *host = url, *path = "", auth[128] = { 0 }, line[512];
516        unsigned short port = 80;
517        int fd;
518        FILE *fp;
519        struct sockaddr_in sin;
520        int chunked = 0, len = 0;
521
522        if (server == NULL || !strcmp(server, "")) {
523                _dprintf("wget: null server input\n");
524                return (0);
525        }
526
527        strncpy(url, server, sizeof(url));
528
529        /* Parse URL */
530        if (!strncmp(url, "http://", 7)) {
531                port = 80;
532                host = url + 7;
533        }
534        if ((s = strchr(host, '/'))) {
535                *s++ = '\0';
536                path = s;
537        }
538        if ((s = strchr(host, '@'))) {
539                *s++ = '\0';
540                base64enc(host, auth, sizeof(auth));
541                host = s;
542        }
543        if ((s = strchr(host, ':'))) {
544                *s++ = '\0';
545                port = atoi(s);
546        }
547
548        /* Open socket */
549        if (!inet_aton(host, &sin.sin_addr))
550                return 0;
551        sin.sin_family = AF_INET;
552        sin.sin_port = htons(port);
553        _dprintf("Connecting to %s:%u...\n", host, port);
554        if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
555            connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 ||
556            !(fp = fdopen(fd, "r+"))) {
557                perror(host);
558                if (fd >= 0)
559                        close(fd);
560                return 0;
561        }
562        _dprintf("connected!\n");
563
564        /* Send HTTP request */
565        fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path);
566        fprintf(fp, "Host: %s\r\n", host);
567        fprintf(fp, "User-Agent: wget\r\n");
568        if (strlen(auth))
569                fprintf(fp, "Authorization: Basic %s\r\n", auth);
570        if (offset)
571                fprintf(fp, "Range: bytes=%ld-\r\n", offset);
572        if (method == METHOD_POST) {
573                fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n");
574                fprintf(fp, "Content-Length: %d\r\n\r\n", (int) strlen(buf));
575                fputs(buf, fp);
576        } else
577                fprintf(fp, "Connection: close\r\n\r\n");
578
579        /* Check HTTP response */
580        _dprintf("HTTP request sent, awaiting response...\n");
581        if (fgets(line, sizeof(line), fp)) {
582                _dprintf("%s", line);
583                for (s = line; *s && !isspace((int)*s); s++);
584                for (; isspace((int)*s); s++);
585                switch (atoi(s)) {
586                case 200: if (offset) goto done; else break;
587                case 206: if (offset) break; else goto done;
588                default: goto done;
589                }
590        }
591        /* Parse headers */
592        while (fgets(line, sizeof(line), fp)) {
593                _dprintf("%s", line);
594                for (s = line; *s == '\r'; s++);
595                if (*s == '\n')
596                        break;
597                if (!strncasecmp(s, "Content-Length:", 15)) {
598                        for (s += 15; isblank(*s); s++);
599                        chomp(s);
600                        len = atoi(s);
601                }
602                else if (!strncasecmp(s, "Transfer-Encoding:", 18)) {
603                        for (s += 18; isblank(*s); s++);
604                        chomp(s);
605                        if (!strncasecmp(s, "chunked", 7))
606                                chunked = 1;
607                }
608        }
609
610        if (chunked && fgets(line, sizeof(line), fp))
611                len = strtol(line, NULL, 16);
612
613        len = (len > count) ? count : len;
614        len = fread(buf, 1, len, fp);
615
616done:
617        /* Close socket */
618        fflush(fp);
619        fclose(fp);
620        return len;
621}
622
623int
624http_get(const char *server, char *buf, size_t count, off_t offset)
625{
626        return wget(METHOD_GET, server, buf, count, offset);
627}
628
629/*
630 * Write a file to an MTD device
631 * @param       path    file to write or a URL
632 * @param       mtd     path to or partition name of MTD device
633 * @return      0 on success and errno on failure
634 */
635int
636mtd_write(const char *path, const char *mtd)
637{
638        int mtd_fd = -1;
639        mtd_info_t mtd_info;
640        erase_info_t erase_info;
641
642        struct sysinfo info;
643        struct trx_header trx;
644        unsigned long crc;
645
646        FILE *fp;
647        char *buf = NULL;
648        long count, len, off;
649        int ret = -1;
650
651        /* Examine TRX header */
652        if ((fp = fopen(path, "r")))
653                count = safe_fread(&trx, 1, sizeof(struct trx_header), fp);
654        else
655                count = http_get(path, (char *) &trx, sizeof(struct trx_header), 0);
656        if (count < sizeof(struct trx_header)) {
657                fprintf(stderr, "%s: File is too small (%ld bytes)\n", path, count);
658                goto fail;
659        }
660
661        /* Open MTD device and get sector size */
662        if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0 ||
663            ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0 ||
664            mtd_info.erasesize < sizeof(struct trx_header)) {
665                perror(mtd);
666                goto fail;
667        }
668
669        if (trx.magic != TRX_MAGIC ||
670            trx.len > mtd_info.size ||
671            trx.len < sizeof(struct trx_header)) {
672                fprintf(stderr, "%s: Bad trx header\n", path);
673                goto fail;
674        }
675
676        /* Allocate temporary buffer */
677        /* See if we have enough memory to store the whole file */
678        sysinfo(&info);
679        if (info.freeram >= trx.len) {
680                erase_info.length = ROUNDUP(trx.len, mtd_info.erasesize);
681                if (!(buf = malloc(erase_info.length)))
682                        erase_info.length = mtd_info.erasesize;
683        }
684        /* fallback to smaller buffer */
685        else {
686                erase_info.length = mtd_info.erasesize;
687                buf = NULL;
688        }
689        if (!buf && (!(buf = malloc(erase_info.length)))) {
690                perror("malloc");
691                goto fail;
692        }
693
694        /* Calculate CRC over header */
695        crc = hndcrc32((uint8 *) &trx.flag_version,
696                       sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version),
697                       CRC32_INIT_VALUE);
698
699        if (trx.flag_version & TRX_NO_HEADER)
700                trx.len -= sizeof(struct trx_header);
701
702        /* Write file or URL to MTD device */
703        for (erase_info.start = 0; erase_info.start < trx.len; erase_info.start += count) {
704                len = MIN(erase_info.length, trx.len - erase_info.start);
705                if ((trx.flag_version & TRX_NO_HEADER) || erase_info.start)
706                        count = off = 0;
707                else {
708                        count = off = sizeof(struct trx_header);
709                        memcpy(buf, &trx, sizeof(struct trx_header));
710                }
711                if (fp)
712                        count += safe_fread(&buf[off], 1, len - off, fp);
713                else
714                        count += http_get(path, &buf[off], len - off, erase_info.start + off);
715                if (count < len) {
716                        fprintf(stderr, "%s: Truncated file (actual %ld expect %ld)\n", path,
717                                count - off, len - off);
718                        goto fail;
719                }
720                /* Update CRC */
721                crc = hndcrc32((uint8 *)&buf[off], count - off, crc);
722                /* Check CRC before writing if possible */
723                if (count == trx.len) {
724                        if (crc != trx.crc32) {
725                                fprintf(stderr, "%s: Bad CRC\n", path);
726                                goto fail;
727                        }
728                }
729                /* Do it */
730                (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
731                if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 ||
732                    write(mtd_fd, buf, count) != count) {
733                        perror(mtd);
734                        goto fail;
735                }
736        }
737
738#ifdef PLC
739  eval("gigle_util restart");
740  nvram_set("plc_pconfig_state", "2");
741  nvram_commit();
742#endif
743
744        printf("%s: CRC OK\n", mtd);
745        ret = 0;
746
747fail:
748        if (buf) {
749                /* Dummy read to ensure chip(s) are out of lock/suspend state */
750                (void) read(mtd_fd, buf, 2);
751                free(buf);
752        }
753
754        if (mtd_fd >= 0)
755                close(mtd_fd);
756        if (fp)
757                fclose(fp);
758        return ret;
759}
760
761#endif
762