1 /* 2 * Boot a Marvell SoC, with Xmodem over UART0. 3 * supports Kirkwood, Dove, Armada 370, Armada XP 4 * 5 * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com> 6 * 7 * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281 8 * Integrated Controller: Functional Specifications" December 2, 9 * 2008. Chapter 24.2 "BootROM Firmware". 10 */ 11 12 #include "kwbimage.h" 13 #include "mkimage.h" 14 15 #include <stdlib.h> 16 #include <stdio.h> 17 #include <string.h> 18 #include <stdarg.h> 19 #include <image.h> 20 #include <libgen.h> 21 #include <fcntl.h> 22 #include <errno.h> 23 #include <unistd.h> 24 #include <stdint.h> 25 #include <termios.h> 26 #include <sys/mman.h> 27 #include <sys/stat.h> 28 29 #ifdef __GNUC__ 30 #define PACKED __attribute((packed)) 31 #else 32 #define PACKED 33 #endif 34 35 /* 36 * Marvell BootROM UART Sensing 37 */ 38 39 static unsigned char kwboot_msg_boot[] = { 40 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 41 }; 42 43 static unsigned char kwboot_msg_debug[] = { 44 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 45 }; 46 47 /* Defines known to work on Kirkwood */ 48 #define KWBOOT_MSG_REQ_DELAY 10 /* ms */ 49 #define KWBOOT_MSG_RSP_TIMEO 50 /* ms */ 50 51 /* Defines known to work on Armada XP */ 52 #define KWBOOT_MSG_REQ_DELAY_AXP 1000 /* ms */ 53 #define KWBOOT_MSG_RSP_TIMEO_AXP 1000 /* ms */ 54 55 /* 56 * Xmodem Transfers 57 */ 58 59 #define SOH 1 /* sender start of block header */ 60 #define EOT 4 /* sender end of block transfer */ 61 #define ACK 6 /* target block ack */ 62 #define NAK 21 /* target block negative ack */ 63 #define CAN 24 /* target/sender transfer cancellation */ 64 65 struct kwboot_block { 66 uint8_t soh; 67 uint8_t pnum; 68 uint8_t _pnum; 69 uint8_t data[128]; 70 uint8_t csum; 71 } PACKED; 72 73 #define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */ 74 75 static int kwboot_verbose; 76 77 static int msg_req_delay = KWBOOT_MSG_REQ_DELAY; 78 static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO; 79 static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO; 80 81 static void 82 kwboot_printv(const char *fmt, ...) 83 { 84 va_list ap; 85 86 if (kwboot_verbose) { 87 va_start(ap, fmt); 88 vprintf(fmt, ap); 89 va_end(ap); 90 fflush(stdout); 91 } 92 } 93 94 static void 95 __spinner(void) 96 { 97 const char seq[] = { '-', '\\', '|', '/' }; 98 const int div = 8; 99 static int state, bs; 100 101 if (state % div == 0) { 102 fputc(bs, stdout); 103 fputc(seq[state / div % sizeof(seq)], stdout); 104 fflush(stdout); 105 } 106 107 bs = '\b'; 108 state++; 109 } 110 111 static void 112 kwboot_spinner(void) 113 { 114 if (kwboot_verbose) 115 __spinner(); 116 } 117 118 static void 119 __progress(int pct, char c) 120 { 121 const int width = 70; 122 static const char *nl = ""; 123 static int pos; 124 125 if (pos % width == 0) 126 printf("%s%3d %% [", nl, pct); 127 128 fputc(c, stdout); 129 130 nl = "]\n"; 131 pos++; 132 133 if (pct == 100) { 134 while (pos++ < width) 135 fputc(' ', stdout); 136 fputs(nl, stdout); 137 } 138 139 fflush(stdout); 140 141 } 142 143 static void 144 kwboot_progress(int _pct, char c) 145 { 146 static int pct; 147 148 if (_pct != -1) 149 pct = _pct; 150 151 if (kwboot_verbose) 152 __progress(pct, c); 153 } 154 155 static int 156 kwboot_tty_recv(int fd, void *buf, size_t len, int timeo) 157 { 158 int rc, nfds; 159 fd_set rfds; 160 struct timeval tv; 161 ssize_t n; 162 163 rc = -1; 164 165 FD_ZERO(&rfds); 166 FD_SET(fd, &rfds); 167 168 tv.tv_sec = 0; 169 tv.tv_usec = timeo * 1000; 170 if (tv.tv_usec > 1000000) { 171 tv.tv_sec += tv.tv_usec / 1000000; 172 tv.tv_usec %= 1000000; 173 } 174 175 do { 176 nfds = select(fd + 1, &rfds, NULL, NULL, &tv); 177 if (nfds < 0) 178 goto out; 179 if (!nfds) { 180 errno = ETIMEDOUT; 181 goto out; 182 } 183 184 n = read(fd, buf, len); 185 if (n <= 0) 186 goto out; 187 188 buf = (char *)buf + n; 189 len -= n; 190 } while (len > 0); 191 192 rc = 0; 193 out: 194 return rc; 195 } 196 197 static int 198 kwboot_tty_send(int fd, const void *buf, size_t len) 199 { 200 int rc; 201 ssize_t n; 202 203 if (!buf) 204 return 0; 205 206 rc = -1; 207 208 do { 209 n = write(fd, buf, len); 210 if (n < 0) 211 goto out; 212 213 buf = (char *)buf + n; 214 len -= n; 215 } while (len > 0); 216 217 rc = tcdrain(fd); 218 out: 219 return rc; 220 } 221 222 static int 223 kwboot_tty_send_char(int fd, unsigned char c) 224 { 225 return kwboot_tty_send(fd, &c, 1); 226 } 227 228 static speed_t 229 kwboot_tty_speed(int baudrate) 230 { 231 switch (baudrate) { 232 case 115200: 233 return B115200; 234 case 57600: 235 return B57600; 236 case 38400: 237 return B38400; 238 case 19200: 239 return B19200; 240 case 9600: 241 return B9600; 242 } 243 244 return -1; 245 } 246 247 static int 248 kwboot_open_tty(const char *path, speed_t speed) 249 { 250 int rc, fd; 251 struct termios tio; 252 253 rc = -1; 254 255 fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY); 256 if (fd < 0) 257 goto out; 258 259 memset(&tio, 0, sizeof(tio)); 260 261 tio.c_iflag = 0; 262 tio.c_cflag = CREAD|CLOCAL|CS8; 263 264 tio.c_cc[VMIN] = 1; 265 tio.c_cc[VTIME] = 10; 266 267 cfsetospeed(&tio, speed); 268 cfsetispeed(&tio, speed); 269 270 rc = tcsetattr(fd, TCSANOW, &tio); 271 if (rc) 272 goto out; 273 274 rc = fd; 275 out: 276 if (rc < 0) { 277 if (fd >= 0) 278 close(fd); 279 } 280 281 return rc; 282 } 283 284 static int 285 kwboot_bootmsg(int tty, void *msg) 286 { 287 int rc; 288 char c; 289 int count; 290 291 if (msg == NULL) 292 kwboot_printv("Please reboot the target into UART boot mode..."); 293 else 294 kwboot_printv("Sending boot message. Please reboot the target..."); 295 296 do { 297 rc = tcflush(tty, TCIOFLUSH); 298 if (rc) 299 break; 300 301 for (count = 0; count < 128; count++) { 302 rc = kwboot_tty_send(tty, msg, 8); 303 if (rc) { 304 usleep(msg_req_delay * 1000); 305 continue; 306 } 307 } 308 309 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); 310 311 kwboot_spinner(); 312 313 } while (rc || c != NAK); 314 315 kwboot_printv("\n"); 316 317 return rc; 318 } 319 320 static int 321 kwboot_debugmsg(int tty, void *msg) 322 { 323 int rc; 324 325 kwboot_printv("Sending debug message. Please reboot the target..."); 326 327 do { 328 char buf[16]; 329 330 rc = tcflush(tty, TCIOFLUSH); 331 if (rc) 332 break; 333 334 rc = kwboot_tty_send(tty, msg, 8); 335 if (rc) { 336 usleep(msg_req_delay * 1000); 337 continue; 338 } 339 340 rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); 341 342 kwboot_spinner(); 343 344 } while (rc); 345 346 kwboot_printv("\n"); 347 348 return rc; 349 } 350 351 static int 352 kwboot_xm_makeblock(struct kwboot_block *block, const void *data, 353 size_t size, int pnum) 354 { 355 const size_t blksz = sizeof(block->data); 356 size_t n; 357 int i; 358 359 block->soh = SOH; 360 block->pnum = pnum; 361 block->_pnum = ~block->pnum; 362 363 n = size < blksz ? size : blksz; 364 memcpy(&block->data[0], data, n); 365 memset(&block->data[n], 0, blksz - n); 366 367 block->csum = 0; 368 for (i = 0; i < n; i++) 369 block->csum += block->data[i]; 370 371 return n; 372 } 373 374 static int 375 kwboot_xm_sendblock(int fd, struct kwboot_block *block) 376 { 377 int rc, retries; 378 char c; 379 380 retries = 16; 381 do { 382 rc = kwboot_tty_send(fd, block, sizeof(*block)); 383 if (rc) 384 break; 385 386 do { 387 rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo); 388 if (rc) 389 break; 390 391 if (c != ACK && c != NAK && c != CAN) 392 printf("%c", c); 393 394 } while (c != ACK && c != NAK && c != CAN); 395 396 if (c != ACK) 397 kwboot_progress(-1, '+'); 398 399 } while (c == NAK && retries-- > 0); 400 401 rc = -1; 402 403 switch (c) { 404 case ACK: 405 rc = 0; 406 break; 407 case NAK: 408 errno = EBADMSG; 409 break; 410 case CAN: 411 errno = ECANCELED; 412 break; 413 default: 414 errno = EPROTO; 415 break; 416 } 417 418 return rc; 419 } 420 421 static int 422 kwboot_xmodem(int tty, const void *_data, size_t size) 423 { 424 const uint8_t *data = _data; 425 int rc, pnum, N, err; 426 427 pnum = 1; 428 N = 0; 429 430 kwboot_printv("Sending boot image...\n"); 431 432 sleep(2); /* flush isn't effective without it */ 433 tcflush(tty, TCIOFLUSH); 434 435 do { 436 struct kwboot_block block; 437 int n; 438 439 n = kwboot_xm_makeblock(&block, 440 data + N, size - N, 441 pnum++); 442 if (n < 0) 443 goto can; 444 445 if (!n) 446 break; 447 448 rc = kwboot_xm_sendblock(tty, &block); 449 if (rc) 450 goto out; 451 452 N += n; 453 kwboot_progress(N * 100 / size, '.'); 454 } while (1); 455 456 rc = kwboot_tty_send_char(tty, EOT); 457 458 out: 459 return rc; 460 461 can: 462 err = errno; 463 kwboot_tty_send_char(tty, CAN); 464 errno = err; 465 goto out; 466 } 467 468 static int 469 kwboot_term_pipe(int in, int out, char *quit, int *s) 470 { 471 ssize_t nin, nout; 472 char _buf[128], *buf = _buf; 473 474 nin = read(in, buf, sizeof(buf)); 475 if (nin <= 0) 476 return -1; 477 478 if (quit) { 479 int i; 480 481 for (i = 0; i < nin; i++) { 482 if (*buf == quit[*s]) { 483 (*s)++; 484 if (!quit[*s]) 485 return 0; 486 buf++; 487 nin--; 488 } else 489 while (*s > 0) { 490 nout = write(out, quit, *s); 491 if (nout <= 0) 492 return -1; 493 (*s) -= nout; 494 } 495 } 496 } 497 498 while (nin > 0) { 499 nout = write(out, buf, nin); 500 if (nout <= 0) 501 return -1; 502 nin -= nout; 503 } 504 505 return 0; 506 } 507 508 static int 509 kwboot_terminal(int tty) 510 { 511 int rc, in, s; 512 char *quit = "\34c"; 513 struct termios otio, tio; 514 515 rc = -1; 516 517 in = STDIN_FILENO; 518 if (isatty(in)) { 519 rc = tcgetattr(in, &otio); 520 if (!rc) { 521 tio = otio; 522 cfmakeraw(&tio); 523 rc = tcsetattr(in, TCSANOW, &tio); 524 } 525 if (rc) { 526 perror("tcsetattr"); 527 goto out; 528 } 529 530 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", 531 quit[0]|0100, quit[1]); 532 } else 533 in = -1; 534 535 rc = 0; 536 s = 0; 537 538 do { 539 fd_set rfds; 540 int nfds = 0; 541 542 FD_SET(tty, &rfds); 543 nfds = nfds < tty ? tty : nfds; 544 545 if (in >= 0) { 546 FD_SET(in, &rfds); 547 nfds = nfds < in ? in : nfds; 548 } 549 550 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL); 551 if (nfds < 0) 552 break; 553 554 if (FD_ISSET(tty, &rfds)) { 555 rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); 556 if (rc) 557 break; 558 } 559 560 if (FD_ISSET(in, &rfds)) { 561 rc = kwboot_term_pipe(in, tty, quit, &s); 562 if (rc) 563 break; 564 } 565 } while (quit[s] != 0); 566 567 tcsetattr(in, TCSANOW, &otio); 568 out: 569 return rc; 570 } 571 572 static void * 573 kwboot_mmap_image(const char *path, size_t *size, int prot) 574 { 575 int rc, fd, flags; 576 struct stat st; 577 void *img; 578 579 rc = -1; 580 img = NULL; 581 582 fd = open(path, O_RDONLY); 583 if (fd < 0) 584 goto out; 585 586 rc = fstat(fd, &st); 587 if (rc) 588 goto out; 589 590 flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED; 591 592 img = mmap(NULL, st.st_size, prot, flags, fd, 0); 593 if (img == MAP_FAILED) { 594 img = NULL; 595 goto out; 596 } 597 598 rc = 0; 599 *size = st.st_size; 600 out: 601 if (rc && img) { 602 munmap(img, st.st_size); 603 img = NULL; 604 } 605 if (fd >= 0) 606 close(fd); 607 608 return img; 609 } 610 611 static uint8_t 612 kwboot_img_csum8(void *_data, size_t size) 613 { 614 uint8_t *data = _data, csum; 615 616 for (csum = 0; size-- > 0; data++) 617 csum += *data; 618 619 return csum; 620 } 621 622 static int 623 kwboot_img_patch_hdr(void *img, size_t size) 624 { 625 int rc; 626 struct main_hdr_v1 *hdr; 627 uint8_t csum; 628 size_t hdrsz = sizeof(*hdr); 629 int image_ver; 630 631 rc = -1; 632 hdr = img; 633 634 if (size < hdrsz) { 635 errno = EINVAL; 636 goto out; 637 } 638 639 image_ver = image_version(img); 640 if (image_ver < 0) { 641 fprintf(stderr, "Invalid image header version\n"); 642 errno = EINVAL; 643 goto out; 644 } 645 646 if (image_ver == 0) 647 hdrsz = sizeof(*hdr); 648 else 649 hdrsz = KWBHEADER_V1_SIZE(hdr); 650 651 csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum; 652 if (csum != hdr->checksum) { 653 errno = EINVAL; 654 goto out; 655 } 656 657 if (hdr->blockid == IBR_HDR_UART_ID) { 658 rc = 0; 659 goto out; 660 } 661 662 hdr->blockid = IBR_HDR_UART_ID; 663 664 if (image_ver == 0) { 665 struct main_hdr_v0 *hdr_v0 = img; 666 667 hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED; 668 hdr_v0->nandpagesize = 0; 669 670 hdr_v0->srcaddr = hdr_v0->ext 671 ? sizeof(struct kwb_header) 672 : sizeof(*hdr_v0); 673 } 674 675 hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum; 676 677 rc = 0; 678 out: 679 return rc; 680 } 681 682 static void 683 kwboot_usage(FILE *stream, char *progname) 684 { 685 fprintf(stream, 686 "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n", 687 progname); 688 fprintf(stream, "\n"); 689 fprintf(stream, 690 " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n"); 691 fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n"); 692 fprintf(stream, 693 " -D <image>: boot <image> without preamble (Dove)\n"); 694 fprintf(stream, " -d: enter debug mode\n"); 695 fprintf(stream, " -a: use timings for Armada XP\n"); 696 fprintf(stream, " -q <req-delay>: use specific request-delay\n"); 697 fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n"); 698 fprintf(stream, 699 " -o <block-timeo>: use specific xmodem block timeout\n"); 700 fprintf(stream, "\n"); 701 fprintf(stream, " -t: mini terminal\n"); 702 fprintf(stream, "\n"); 703 fprintf(stream, " -B <baud>: set baud rate\n"); 704 fprintf(stream, "\n"); 705 } 706 707 int 708 main(int argc, char **argv) 709 { 710 const char *ttypath, *imgpath; 711 int rv, rc, tty, term, prot, patch; 712 void *bootmsg; 713 void *debugmsg; 714 void *img; 715 size_t size; 716 speed_t speed; 717 718 rv = 1; 719 tty = -1; 720 bootmsg = NULL; 721 debugmsg = NULL; 722 imgpath = NULL; 723 img = NULL; 724 term = 0; 725 patch = 0; 726 size = 0; 727 speed = B115200; 728 729 kwboot_verbose = isatty(STDOUT_FILENO); 730 731 do { 732 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:"); 733 if (c < 0) 734 break; 735 736 switch (c) { 737 case 'b': 738 bootmsg = kwboot_msg_boot; 739 imgpath = optarg; 740 break; 741 742 case 'D': 743 bootmsg = NULL; 744 imgpath = optarg; 745 break; 746 747 case 'd': 748 debugmsg = kwboot_msg_debug; 749 break; 750 751 case 'p': 752 patch = 1; 753 break; 754 755 case 't': 756 term = 1; 757 break; 758 759 case 'a': 760 msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; 761 msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; 762 break; 763 764 case 'q': 765 msg_req_delay = atoi(optarg); 766 break; 767 768 case 's': 769 msg_rsp_timeo = atoi(optarg); 770 break; 771 772 case 'o': 773 blk_rsp_timeo = atoi(optarg); 774 break; 775 776 case 'B': 777 speed = kwboot_tty_speed(atoi(optarg)); 778 if (speed == -1) 779 goto usage; 780 break; 781 782 case 'h': 783 rv = 0; 784 default: 785 goto usage; 786 } 787 } while (1); 788 789 if (!bootmsg && !term && !debugmsg) 790 goto usage; 791 792 if (patch && !imgpath) 793 goto usage; 794 795 if (argc - optind < 1) 796 goto usage; 797 798 ttypath = argv[optind++]; 799 800 tty = kwboot_open_tty(ttypath, speed); 801 if (tty < 0) { 802 perror(ttypath); 803 goto out; 804 } 805 806 if (imgpath) { 807 prot = PROT_READ | (patch ? PROT_WRITE : 0); 808 809 img = kwboot_mmap_image(imgpath, &size, prot); 810 if (!img) { 811 perror(imgpath); 812 goto out; 813 } 814 } 815 816 if (patch) { 817 rc = kwboot_img_patch_hdr(img, size); 818 if (rc) { 819 fprintf(stderr, "%s: Invalid image.\n", imgpath); 820 goto out; 821 } 822 } 823 824 if (debugmsg) { 825 rc = kwboot_debugmsg(tty, debugmsg); 826 if (rc) { 827 perror("debugmsg"); 828 goto out; 829 } 830 } else if (bootmsg) { 831 rc = kwboot_bootmsg(tty, bootmsg); 832 if (rc) { 833 perror("bootmsg"); 834 goto out; 835 } 836 } 837 838 if (img) { 839 rc = kwboot_xmodem(tty, img, size); 840 if (rc) { 841 perror("xmodem"); 842 goto out; 843 } 844 } 845 846 if (term) { 847 rc = kwboot_terminal(tty); 848 if (rc && !(errno == EINTR)) { 849 perror("terminal"); 850 goto out; 851 } 852 } 853 854 rv = 0; 855 out: 856 if (tty >= 0) 857 close(tty); 858 859 if (img) 860 munmap(img, size); 861 862 return rv; 863 864 usage: 865 kwboot_usage(rv ? stderr : stdout, basename(argv[0])); 866 goto out; 867 } 868