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 290 if (msg == NULL) 291 kwboot_printv("Please reboot the target into UART boot mode..."); 292 else 293 kwboot_printv("Sending boot message. Please reboot the target..."); 294 295 do { 296 rc = tcflush(tty, TCIOFLUSH); 297 if (rc) 298 break; 299 300 rc = kwboot_tty_send(tty, msg, 8); 301 if (rc) { 302 usleep(msg_req_delay * 1000); 303 continue; 304 } 305 306 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); 307 308 kwboot_spinner(); 309 310 } while (rc || c != NAK); 311 312 kwboot_printv("\n"); 313 314 return rc; 315 } 316 317 static int 318 kwboot_debugmsg(int tty, void *msg) 319 { 320 int rc; 321 322 kwboot_printv("Sending debug message. Please reboot the target..."); 323 324 do { 325 char buf[16]; 326 327 rc = tcflush(tty, TCIOFLUSH); 328 if (rc) 329 break; 330 331 rc = kwboot_tty_send(tty, msg, 8); 332 if (rc) { 333 usleep(msg_req_delay * 1000); 334 continue; 335 } 336 337 rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); 338 339 kwboot_spinner(); 340 341 } while (rc); 342 343 kwboot_printv("\n"); 344 345 return rc; 346 } 347 348 static int 349 kwboot_xm_makeblock(struct kwboot_block *block, const void *data, 350 size_t size, int pnum) 351 { 352 const size_t blksz = sizeof(block->data); 353 size_t n; 354 int i; 355 356 block->soh = SOH; 357 block->pnum = pnum; 358 block->_pnum = ~block->pnum; 359 360 n = size < blksz ? size : blksz; 361 memcpy(&block->data[0], data, n); 362 memset(&block->data[n], 0, blksz - n); 363 364 block->csum = 0; 365 for (i = 0; i < n; i++) 366 block->csum += block->data[i]; 367 368 return n; 369 } 370 371 static int 372 kwboot_xm_sendblock(int fd, struct kwboot_block *block) 373 { 374 int rc, retries; 375 char c; 376 377 retries = 16; 378 do { 379 rc = kwboot_tty_send(fd, block, sizeof(*block)); 380 if (rc) 381 break; 382 383 do { 384 rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo); 385 if (rc) 386 break; 387 388 if (c != ACK && c != NAK && c != CAN) 389 printf("%c", c); 390 391 } while (c != ACK && c != NAK && c != CAN); 392 393 if (c != ACK) 394 kwboot_progress(-1, '+'); 395 396 } while (c == NAK && retries-- > 0); 397 398 rc = -1; 399 400 switch (c) { 401 case ACK: 402 rc = 0; 403 break; 404 case NAK: 405 errno = EBADMSG; 406 break; 407 case CAN: 408 errno = ECANCELED; 409 break; 410 default: 411 errno = EPROTO; 412 break; 413 } 414 415 return rc; 416 } 417 418 static int 419 kwboot_xmodem(int tty, const void *_data, size_t size) 420 { 421 const uint8_t *data = _data; 422 int rc, pnum, N, err; 423 424 pnum = 1; 425 N = 0; 426 427 kwboot_printv("Sending boot image...\n"); 428 429 do { 430 struct kwboot_block block; 431 int n; 432 433 n = kwboot_xm_makeblock(&block, 434 data + N, size - N, 435 pnum++); 436 if (n < 0) 437 goto can; 438 439 if (!n) 440 break; 441 442 rc = kwboot_xm_sendblock(tty, &block); 443 if (rc) 444 goto out; 445 446 N += n; 447 kwboot_progress(N * 100 / size, '.'); 448 } while (1); 449 450 rc = kwboot_tty_send_char(tty, EOT); 451 452 out: 453 return rc; 454 455 can: 456 err = errno; 457 kwboot_tty_send_char(tty, CAN); 458 errno = err; 459 goto out; 460 } 461 462 static int 463 kwboot_term_pipe(int in, int out, char *quit, int *s) 464 { 465 ssize_t nin, nout; 466 char _buf[128], *buf = _buf; 467 468 nin = read(in, buf, sizeof(buf)); 469 if (nin < 0) 470 return -1; 471 472 if (quit) { 473 int i; 474 475 for (i = 0; i < nin; i++) { 476 if (*buf == quit[*s]) { 477 (*s)++; 478 if (!quit[*s]) 479 return 0; 480 buf++; 481 nin--; 482 } else 483 while (*s > 0) { 484 nout = write(out, quit, *s); 485 if (nout <= 0) 486 return -1; 487 (*s) -= nout; 488 } 489 } 490 } 491 492 while (nin > 0) { 493 nout = write(out, buf, nin); 494 if (nout <= 0) 495 return -1; 496 nin -= nout; 497 } 498 499 return 0; 500 } 501 502 static int 503 kwboot_terminal(int tty) 504 { 505 int rc, in, s; 506 char *quit = "\34c"; 507 struct termios otio, tio; 508 509 rc = -1; 510 511 in = STDIN_FILENO; 512 if (isatty(in)) { 513 rc = tcgetattr(in, &otio); 514 if (!rc) { 515 tio = otio; 516 cfmakeraw(&tio); 517 rc = tcsetattr(in, TCSANOW, &tio); 518 } 519 if (rc) { 520 perror("tcsetattr"); 521 goto out; 522 } 523 524 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", 525 quit[0]|0100, quit[1]); 526 } else 527 in = -1; 528 529 rc = 0; 530 s = 0; 531 532 do { 533 fd_set rfds; 534 int nfds = 0; 535 536 FD_SET(tty, &rfds); 537 nfds = nfds < tty ? tty : nfds; 538 539 if (in >= 0) { 540 FD_SET(in, &rfds); 541 nfds = nfds < in ? in : nfds; 542 } 543 544 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL); 545 if (nfds < 0) 546 break; 547 548 if (FD_ISSET(tty, &rfds)) { 549 rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); 550 if (rc) 551 break; 552 } 553 554 if (FD_ISSET(in, &rfds)) { 555 rc = kwboot_term_pipe(in, tty, quit, &s); 556 if (rc) 557 break; 558 } 559 } while (quit[s] != 0); 560 561 tcsetattr(in, TCSANOW, &otio); 562 out: 563 return rc; 564 } 565 566 static void * 567 kwboot_mmap_image(const char *path, size_t *size, int prot) 568 { 569 int rc, fd, flags; 570 struct stat st; 571 void *img; 572 573 rc = -1; 574 img = NULL; 575 576 fd = open(path, O_RDONLY); 577 if (fd < 0) 578 goto out; 579 580 rc = fstat(fd, &st); 581 if (rc) 582 goto out; 583 584 flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED; 585 586 img = mmap(NULL, st.st_size, prot, flags, fd, 0); 587 if (img == MAP_FAILED) { 588 img = NULL; 589 goto out; 590 } 591 592 rc = 0; 593 *size = st.st_size; 594 out: 595 if (rc && img) { 596 munmap(img, st.st_size); 597 img = NULL; 598 } 599 if (fd >= 0) 600 close(fd); 601 602 return img; 603 } 604 605 static uint8_t 606 kwboot_img_csum8(void *_data, size_t size) 607 { 608 uint8_t *data = _data, csum; 609 610 for (csum = 0; size-- > 0; data++) 611 csum += *data; 612 613 return csum; 614 } 615 616 static int 617 kwboot_img_patch_hdr(void *img, size_t size) 618 { 619 int rc; 620 struct main_hdr_v1 *hdr; 621 uint8_t csum; 622 size_t hdrsz = sizeof(*hdr); 623 int image_ver; 624 625 rc = -1; 626 hdr = img; 627 628 if (size < hdrsz) { 629 errno = EINVAL; 630 goto out; 631 } 632 633 image_ver = image_version(img); 634 if (image_ver < 0) { 635 fprintf(stderr, "Invalid image header version\n"); 636 errno = EINVAL; 637 goto out; 638 } 639 640 if (image_ver == 0) 641 hdrsz = sizeof(*hdr); 642 else 643 hdrsz = KWBHEADER_V1_SIZE(hdr); 644 645 csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum; 646 if (csum != hdr->checksum) { 647 errno = EINVAL; 648 goto out; 649 } 650 651 if (hdr->blockid == IBR_HDR_UART_ID) { 652 rc = 0; 653 goto out; 654 } 655 656 hdr->blockid = IBR_HDR_UART_ID; 657 658 if (image_ver == 0) { 659 struct main_hdr_v0 *hdr_v0 = img; 660 661 hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED; 662 hdr_v0->nandpagesize = 0; 663 664 hdr_v0->srcaddr = hdr_v0->ext 665 ? sizeof(struct kwb_header) 666 : sizeof(*hdr_v0); 667 } else { 668 /* 669 * Subtract mkimage header size from destination address 670 * as this header is not expected by the Marvell BootROM. 671 * This way, the execution address is identical to the 672 * one the image is compiled for (TEXT_BASE). 673 */ 674 hdr->destaddr = hdr->destaddr - sizeof(struct image_header); 675 } 676 677 hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum; 678 679 rc = 0; 680 out: 681 return rc; 682 } 683 684 static void 685 kwboot_usage(FILE *stream, char *progname) 686 { 687 fprintf(stream, 688 "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n", 689 progname); 690 fprintf(stream, "\n"); 691 fprintf(stream, 692 " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n"); 693 fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n"); 694 fprintf(stream, 695 " -D <image>: boot <image> without preamble (Dove)\n"); 696 fprintf(stream, " -d: enter debug mode\n"); 697 fprintf(stream, " -a: use timings for Armada XP\n"); 698 fprintf(stream, " -q <req-delay>: use specific request-delay\n"); 699 fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n"); 700 fprintf(stream, 701 " -o <block-timeo>: use specific xmodem block timeout\n"); 702 fprintf(stream, "\n"); 703 fprintf(stream, " -t: mini terminal\n"); 704 fprintf(stream, "\n"); 705 fprintf(stream, " -B <baud>: set baud rate\n"); 706 fprintf(stream, "\n"); 707 } 708 709 int 710 main(int argc, char **argv) 711 { 712 const char *ttypath, *imgpath; 713 int rv, rc, tty, term, prot, patch; 714 void *bootmsg; 715 void *debugmsg; 716 void *img; 717 size_t size; 718 speed_t speed; 719 720 rv = 1; 721 tty = -1; 722 bootmsg = NULL; 723 debugmsg = NULL; 724 imgpath = NULL; 725 img = NULL; 726 term = 0; 727 patch = 0; 728 size = 0; 729 speed = B115200; 730 731 kwboot_verbose = isatty(STDOUT_FILENO); 732 733 do { 734 int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:"); 735 if (c < 0) 736 break; 737 738 switch (c) { 739 case 'b': 740 bootmsg = kwboot_msg_boot; 741 imgpath = optarg; 742 break; 743 744 case 'D': 745 bootmsg = NULL; 746 imgpath = optarg; 747 break; 748 749 case 'd': 750 debugmsg = kwboot_msg_debug; 751 break; 752 753 case 'p': 754 patch = 1; 755 break; 756 757 case 't': 758 term = 1; 759 break; 760 761 case 'a': 762 msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; 763 msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; 764 break; 765 766 case 'q': 767 msg_req_delay = atoi(optarg); 768 break; 769 770 case 's': 771 msg_rsp_timeo = atoi(optarg); 772 break; 773 774 case 'o': 775 blk_rsp_timeo = atoi(optarg); 776 break; 777 778 case 'B': 779 speed = kwboot_tty_speed(atoi(optarg)); 780 if (speed == -1) 781 goto usage; 782 break; 783 784 case 'h': 785 rv = 0; 786 default: 787 goto usage; 788 } 789 } while (1); 790 791 if (!bootmsg && !term && !debugmsg) 792 goto usage; 793 794 if (patch && !imgpath) 795 goto usage; 796 797 if (argc - optind < 1) 798 goto usage; 799 800 ttypath = argv[optind++]; 801 802 tty = kwboot_open_tty(ttypath, speed); 803 if (tty < 0) { 804 perror(ttypath); 805 goto out; 806 } 807 808 if (imgpath) { 809 prot = PROT_READ | (patch ? PROT_WRITE : 0); 810 811 img = kwboot_mmap_image(imgpath, &size, prot); 812 if (!img) { 813 perror(imgpath); 814 goto out; 815 } 816 } 817 818 if (patch) { 819 rc = kwboot_img_patch_hdr(img, size); 820 if (rc) { 821 fprintf(stderr, "%s: Invalid image.\n", imgpath); 822 goto out; 823 } 824 } 825 826 if (debugmsg) { 827 rc = kwboot_debugmsg(tty, debugmsg); 828 if (rc) { 829 perror("debugmsg"); 830 goto out; 831 } 832 } else { 833 rc = kwboot_bootmsg(tty, bootmsg); 834 if (rc) { 835 perror("bootmsg"); 836 goto out; 837 } 838 } 839 840 if (img) { 841 rc = kwboot_xmodem(tty, img, size); 842 if (rc) { 843 perror("xmodem"); 844 goto out; 845 } 846 } 847 848 if (term) { 849 rc = kwboot_terminal(tty); 850 if (rc && !(errno == EINTR)) { 851 perror("terminal"); 852 goto out; 853 } 854 } 855 856 rv = 0; 857 out: 858 if (tty >= 0) 859 close(tty); 860 861 if (img) 862 munmap(img, size); 863 864 return rv; 865 866 usage: 867 kwboot_usage(rv ? stderr : stdout, basename(argv[0])); 868 goto out; 869 } 870