1/*
2 * MTD utility functions
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 *
19 * <<Broadcom-WL-IPTag/Open:>>
20 *
21 * $Id: mtd.c 520342 2014-12-11 05:39:44Z $
22 */
23#include "rc.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <limits.h>
30#include <sys/sysmacros.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <error.h>
36#include <sys/ioctl.h>
37#include <sys/sysinfo.h>
38
39#ifdef LINUX26
40#include <mtd/mtd-user.h>
41#else /* LINUX26 */
42#include <linux/mtd/mtd.h>
43#endif /* LINUX26 */
44
45#include <trxhdr.h>
46#include <bcmutils.h>
47#include <bcmendian.h>
48#include <bcmnvram.h>
49#include <shutils.h>
50
51/*
52 * Open an MTD device
53 * @param	mtd	path to or partition name of MTD device
54 * @param	flags	open() flags
55 * @return	return value of open()
56 */
57int
58mtd_open(const char *mtd, int flags)
59{
60	FILE *fp;
61	char dev[PATH_MAX];
62	int i;
63
64	if ((fp = fopen("/proc/mtd", "r"))) {
65		while (fgets(dev, sizeof(dev), fp)) {
66			if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
67#ifdef LINUX26
68				snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
69#else
70				snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
71#endif
72				fclose(fp);
73				return open(dev, flags);
74			}
75		}
76		fclose(fp);
77	}
78
79	return open(mtd, flags);
80}
81
82/*
83 * Erase an MTD device
84 * @param	mtd	path to or partition name of MTD device
85 * @return	0 on success and errno on failure
86 */
87int
88mtd_erase(const char *mtd)
89{
90	int mtd_fd;
91	mtd_info_t mtd_info;
92	erase_info_t erase_info;
93
94	/* Open MTD device */
95	if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0) {
96		perror(mtd);
97		return errno;
98	}
99
100	/* Get sector size */
101	if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
102		perror(mtd);
103		close(mtd_fd);
104		return errno;
105	}
106
107	erase_info.length = mtd_info.erasesize;
108
109	for (erase_info.start = 0;
110	     erase_info.start < mtd_info.size;
111	     erase_info.start += mtd_info.erasesize) {
112		(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
113		if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0) {
114			perror(mtd);
115			close(mtd_fd);
116			_dprintf("\nError erasing MTD\n");
117			return errno;
118		}
119	}
120
121	close(mtd_fd);
122
123	return 0;
124}
125
126int
127mtd_unlock(const char *mtdname)
128{
129	int mtd_fd;
130	mtd_info_t mtd_info;
131	erase_info_t erase_info;
132	int ret;
133
134	if(!wait_action_idle(5)) return 0;
135	set_action(ACT_ERASE_NVRAM);
136
137	ret = 0;
138	/* Open MTD device */
139	if ((mtd_fd = mtd_open(mtdname, O_RDWR)) < 0) {
140		perror(mtdname);
141		return errno;
142	}
143
144	/* Get sector size */
145	if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
146		perror(mtdname);
147		close(mtd_fd);
148		return errno;
149	}
150
151	ret = 1;
152	erase_info.length = mtd_info.erasesize;
153
154	for (erase_info.start = 0;
155	     erase_info.start < mtd_info.size;
156	     erase_info.start += mtd_info.erasesize) {
157		printf("Unlocking 0x%x - 0x%x\n", erase_info.start, (erase_info.start + erase_info.length) - 1);
158		fflush(stdout);
159
160		(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
161	}
162
163	char buf[2];
164	read(mtd_fd, &buf, sizeof(buf));
165	close(mtd_fd);
166
167	set_action(ACT_IDLE);
168
169	if(ret) printf("\"%s\" successfully unlocked.\n", mtdname);
170	else printf("Error unlocking MTD \"%s\".\n", mtdname);
171	sleep(1);
172
173	return ret;
174}
175
176static char *
177base64enc(const char *p, char *buf, int len)
178{
179        char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
180                "0123456789+/";
181        char *s = buf;
182
183        while (*p) {
184                if (s >= buf+len-4)
185                        break;
186                *(s++) = al[(*p >> 2) & 0x3F];
187                *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
188                *s = *(s+1) = '=';
189                *(s+2) = 0;
190                if (! *(++p)) break;
191                *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
192                if (! *(++p)) break;
193                *(s++) = al[*(p++) & 0x3F];
194        }
195
196        return buf;
197}
198
199enum {
200        METHOD_GET,
201        METHOD_POST
202};
203
204static int
205wget(int method, const char *server, char *buf, size_t count, off_t offset)
206{
207        char url[PATH_MAX] = { 0 }, *s;
208        char *host = url, *path = "", auth[128] = { 0 }, line[512];
209        unsigned short port = 80;
210        int fd;
211        FILE *fp;
212        struct sockaddr_in sin;
213        int chunked = 0, len = 0;
214
215        if (server == NULL || !strcmp(server, "")) {
216                _dprintf("wget: null server input\n");
217                return (0);
218        }
219
220        strncpy(url, server, sizeof(url));
221
222        /* Parse URL */
223        if (!strncmp(url, "http://", 7)) {
224                port = 80;
225                host = url + 7;
226        }
227        if ((s = strchr(host, '/'))) {
228                *s++ = '\0';
229                path = s;
230        }
231        if ((s = strchr(host, '@'))) {
232                *s++ = '\0';
233                base64enc(host, auth, sizeof(auth));
234                host = s;
235        }
236        if ((s = strchr(host, ':'))) {
237                *s++ = '\0';
238                port = atoi(s);
239        }
240
241        /* Open socket */
242        if (!inet_aton(host, &sin.sin_addr))
243                return 0;
244        sin.sin_family = AF_INET;
245        sin.sin_port = htons(port);
246        _dprintf("Connecting to %s:%u...\n", host, port);
247        if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
248            connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 ||
249            !(fp = fdopen(fd, "r+"))) {
250                perror(host);
251                if (fd >= 0)
252                        close(fd);
253                return 0;
254        }
255        _dprintf("connected!\n");
256
257        /* Send HTTP request */
258        fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path);
259        fprintf(fp, "Host: %s\r\n", host);
260        fprintf(fp, "User-Agent: wget\r\n");
261        if (strlen(auth))
262                fprintf(fp, "Authorization: Basic %s\r\n", auth);
263        if (offset)
264                fprintf(fp, "Range: bytes=%ld-\r\n", offset);
265        if (method == METHOD_POST) {
266                fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n");
267                fprintf(fp, "Content-Length: %d\r\n\r\n", (int) strlen(buf));
268                fputs(buf, fp);
269        } else
270                fprintf(fp, "Connection: close\r\n\r\n");
271
272        /* Check HTTP response */
273        _dprintf("HTTP request sent, awaiting response...\n");
274        if (fgets(line, sizeof(line), fp)) {
275                _dprintf("%s", line);
276                for (s = line; *s && !isspace((int)*s); s++);
277                for (; isspace((int)*s); s++);
278                switch (atoi(s)) {
279                case 200: if (offset) goto done; else break;
280                case 206: if (offset) break; else goto done;
281                default: goto done;
282                }
283        }
284        /* Parse headers */
285        while (fgets(line, sizeof(line), fp)) {
286                _dprintf("%s", line);
287                for (s = line; *s == '\r'; s++);
288                if (*s == '\n')
289                        break;
290                if (!strncasecmp(s, "Content-Length:", 15)) {
291                        for (s += 15; isblank(*s); s++);
292                        chomp(s);
293                        len = atoi(s);
294                }
295                else if (!strncasecmp(s, "Transfer-Encoding:", 18)) {
296                        for (s += 18; isblank(*s); s++);
297                        chomp(s);
298                        if (!strncasecmp(s, "chunked", 7))
299                                chunked = 1;
300                }
301        }
302
303        if (chunked && fgets(line, sizeof(line), fp))
304                len = strtol(line, NULL, 16);
305
306        len = (len > count) ? count : len;
307        len = fread(buf, 1, len, fp);
308
309done:
310        /* Close socket */
311        fflush(fp);
312        fclose(fp);
313        return len;
314}
315
316int http_get(const char *server, char *buf, size_t count, off_t offset)
317{
318        return wget(METHOD_GET, server, buf, count, offset);
319}
320
321/*
322 * Write a file to an MTD device
323 * @param	path	file to write or a URL
324 * @param	mtd	path to or partition name of MTD device
325 * @return	0 on success and errno on failure
326 */
327int
328mtd_write(const char *path, const char *mtd)
329{
330	int mtd_fd = -1;
331	mtd_info_t mtd_info;
332	erase_info_t erase_info;
333
334	struct sysinfo info;
335	struct trx_header trx;
336	unsigned long crc;
337
338	FILE *fp;
339	char *buf = NULL;
340	long count, len, off;
341	int ret = -1;
342
343	/* Examine TRX header */
344	if ((fp = fopen(path, "r")))
345		count = safe_fread(&trx, 1, sizeof(struct trx_header), fp);
346	else
347		count = http_get(path, (char *) &trx, sizeof(struct trx_header), 0);
348	if (count < sizeof(struct trx_header)) {
349		_dprintf("%s: File is too small (%ld bytes)\n", path, count);
350		goto fail;
351	}
352
353	_dprintf("File size: %s (%ld bytes)\n", path, count);
354
355	/* Open MTD device and get sector size */
356	if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0 ||
357	    ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0 ||
358	    mtd_info.erasesize < sizeof(struct trx_header)) {
359		perror(mtd);
360		goto fail;
361	}
362
363	_dprintf("mtd size=%x, erasesize=%x, writesize=%x, type=%x\n", mtd_info.size, mtd_info.erasesize, mtd_info.writesize, mtd_info.type);
364
365	if (trx.magic != TRX_MAGIC ||
366	    trx.len > mtd_info.size ||
367	    trx.len < sizeof(struct trx_header)) {
368		_dprintf("%s: Bad trx header\n", path);
369		goto fail;
370	}
371
372
373	/* Allocate temporary buffer */
374	/* See if we have enough memory to store the whole file */
375	sysinfo(&info);
376	if (info.freeram >= trx.len) {
377		erase_info.length = ROUNDUP(trx.len, mtd_info.erasesize);
378		if (!(buf = malloc(erase_info.length)))
379			erase_info.length = mtd_info.erasesize;
380	}
381	/* fallback to smaller buffer */
382	else {
383		erase_info.length = mtd_info.erasesize;
384		buf = NULL;
385	}
386	if (!buf && (!(buf = malloc(erase_info.length)))) {
387		perror("malloc");
388		goto fail;
389	}
390
391	/* Calculate CRC over header */
392	crc = hndcrc32((uint8 *) &trx.flag_version,
393	               sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version),
394	               CRC32_INIT_VALUE);
395
396	if (trx.flag_version & TRX_NO_HEADER)
397		trx.len -= sizeof(struct trx_header);
398
399	/* Write file or URL to MTD device */
400	for (erase_info.start = 0; erase_info.start < trx.len; erase_info.start += count) {
401		len = MIN(erase_info.length, trx.len - erase_info.start);
402		if ((trx.flag_version & TRX_NO_HEADER) || erase_info.start)
403			count = off = 0;
404		else {
405			count = off = sizeof(struct trx_header);
406			memcpy(buf, &trx, sizeof(struct trx_header));
407		}
408		if (fp)
409			count += safe_fread(&buf[off], 1, len - off, fp);
410		else
411			count += http_get(path, &buf[off], len - off, erase_info.start + off);
412		if (count < len) {
413			_dprintf("%s: Truncated file (actual %ld expect %ld)\n", path,
414				count - off, len - off);
415			goto fail;
416		}
417		/* Update CRC */
418		crc = hndcrc32((uint8 *)&buf[off], count - off, crc);
419		/* Check CRC before writing if possible */
420		if (count == trx.len) {
421			if (crc != trx.crc32) {
422				_dprintf("%s: Bad CRC\n", path);
423				goto fail;
424			}
425		}
426		/* Do it */
427		(void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
428		if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 ||
429		    write(mtd_fd, buf, count) != count) {
430			perror(mtd);
431			goto fail;
432		}
433	}
434
435	_dprintf("%s: CRC OK\n", mtd);
436	ret = 0;
437
438fail:
439	if (buf) {
440		/* Dummy read to ensure chip(s) are out of lock/suspend state */
441		(void) read(mtd_fd, buf, 2);
442		free(buf);
443	}
444
445	if (mtd_fd >= 0)
446		close(mtd_fd);
447	if (fp)
448		fclose(fp);
449	return ret;
450}
451
452int mtd_unlock_erase_main_old(int argc, char *argv[])
453{
454	char c;
455	char *dev = NULL;
456
457	while ((c = getopt(argc, argv, "d:")) != -1) {
458		switch (c) {
459		case 'd':
460			dev = optarg;
461			break;
462		}
463	}
464
465	if (!dev) {
466		usage_exit(argv[0], "-d part");
467	}
468
469	return mtd_erase(dev);
470}
471
472int mtd_write_main_old(int argc, char *argv[])
473{
474	char c;
475	char *iname = NULL;
476	char *dev = NULL;
477
478	while ((c = getopt(argc, argv, "i:d:")) != -1) {
479		switch (c) {
480		case 'i':
481			iname = optarg;
482			break;
483		case 'd':
484			dev = optarg;
485			break;
486		}
487	}
488
489	if ((iname == NULL) || (dev == NULL)) {
490		usage_exit(argv[0], "-i file -d part");
491	}
492
493	if (!wait_action_idle(10)) {
494		printf("System is busy\n");
495		return 1;
496	}
497
498	set_action(ACT_WEB_UPGRADE);
499
500	return mtd_write(iname, dev);
501
502}
503
504