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