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