uboot/tools/kwboot.c
<<
>>
Prefs
   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/*
  30 * Marvell BootROM UART Sensing
  31 */
  32
  33static unsigned char kwboot_msg_boot[] = {
  34        0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
  35};
  36
  37static unsigned char kwboot_msg_debug[] = {
  38        0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
  39};
  40
  41/* Defines known to work on Kirkwood */
  42#define KWBOOT_MSG_REQ_DELAY    10 /* ms */
  43#define KWBOOT_MSG_RSP_TIMEO    50 /* ms */
  44
  45/* Defines known to work on Armada XP */
  46#define KWBOOT_MSG_REQ_DELAY_AXP        1000 /* ms */
  47#define KWBOOT_MSG_RSP_TIMEO_AXP        1000 /* ms */
  48
  49/*
  50 * Xmodem Transfers
  51 */
  52
  53#define SOH     1       /* sender start of block header */
  54#define EOT     4       /* sender end of block transfer */
  55#define ACK     6       /* target block ack */
  56#define NAK     21      /* target block negative ack */
  57#define CAN     24      /* target/sender transfer cancellation */
  58
  59struct kwboot_block {
  60        uint8_t soh;
  61        uint8_t pnum;
  62        uint8_t _pnum;
  63        uint8_t data[128];
  64        uint8_t csum;
  65} __packed;
  66
  67#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
  68
  69static int kwboot_verbose;
  70
  71static int msg_req_delay = KWBOOT_MSG_REQ_DELAY;
  72static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
  73static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
  74
  75static void
  76kwboot_printv(const char *fmt, ...)
  77{
  78        va_list ap;
  79
  80        if (kwboot_verbose) {
  81                va_start(ap, fmt);
  82                vprintf(fmt, ap);
  83                va_end(ap);
  84                fflush(stdout);
  85        }
  86}
  87
  88static void
  89__spinner(void)
  90{
  91        const char seq[] = { '-', '\\', '|', '/' };
  92        const int div = 8;
  93        static int state, bs;
  94
  95        if (state % div == 0) {
  96                fputc(bs, stdout);
  97                fputc(seq[state / div % sizeof(seq)], stdout);
  98                fflush(stdout);
  99        }
 100
 101        bs = '\b';
 102        state++;
 103}
 104
 105static void
 106kwboot_spinner(void)
 107{
 108        if (kwboot_verbose)
 109                __spinner();
 110}
 111
 112static void
 113__progress(int pct, char c)
 114{
 115        const int width = 70;
 116        static const char *nl = "";
 117        static int pos;
 118
 119        if (pos % width == 0)
 120                printf("%s%3d %% [", nl, pct);
 121
 122        fputc(c, stdout);
 123
 124        nl = "]\n";
 125        pos++;
 126
 127        if (pct == 100) {
 128                while (pos++ < width)
 129                        fputc(' ', stdout);
 130                fputs(nl, stdout);
 131        }
 132
 133        fflush(stdout);
 134
 135}
 136
 137static void
 138kwboot_progress(int _pct, char c)
 139{
 140        static int pct;
 141
 142        if (_pct != -1)
 143                pct = _pct;
 144
 145        if (kwboot_verbose)
 146                __progress(pct, c);
 147}
 148
 149static int
 150kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
 151{
 152        int rc, nfds;
 153        fd_set rfds;
 154        struct timeval tv;
 155        ssize_t n;
 156
 157        rc = -1;
 158
 159        FD_ZERO(&rfds);
 160        FD_SET(fd, &rfds);
 161
 162        tv.tv_sec = 0;
 163        tv.tv_usec = timeo * 1000;
 164        if (tv.tv_usec > 1000000) {
 165                tv.tv_sec += tv.tv_usec / 1000000;
 166                tv.tv_usec %= 1000000;
 167        }
 168
 169        do {
 170                nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
 171                if (nfds < 0)
 172                        goto out;
 173                if (!nfds) {
 174                        errno = ETIMEDOUT;
 175                        goto out;
 176                }
 177
 178                n = read(fd, buf, len);
 179                if (n <= 0)
 180                        goto out;
 181
 182                buf = (char *)buf + n;
 183                len -= n;
 184        } while (len > 0);
 185
 186        rc = 0;
 187out:
 188        return rc;
 189}
 190
 191static int
 192kwboot_tty_send(int fd, const void *buf, size_t len)
 193{
 194        int rc;
 195        ssize_t n;
 196
 197        if (!buf)
 198                return 0;
 199
 200        rc = -1;
 201
 202        do {
 203                n = write(fd, buf, len);
 204                if (n < 0)
 205                        goto out;
 206
 207                buf = (char *)buf + n;
 208                len -= n;
 209        } while (len > 0);
 210
 211        rc = tcdrain(fd);
 212out:
 213        return rc;
 214}
 215
 216static int
 217kwboot_tty_send_char(int fd, unsigned char c)
 218{
 219        return kwboot_tty_send(fd, &c, 1);
 220}
 221
 222static speed_t
 223kwboot_tty_speed(int baudrate)
 224{
 225        switch (baudrate) {
 226        case 115200:
 227                return B115200;
 228        case 57600:
 229                return B57600;
 230        case 38400:
 231                return B38400;
 232        case 19200:
 233                return B19200;
 234        case 9600:
 235                return B9600;
 236        }
 237
 238        return -1;
 239}
 240
 241static int
 242kwboot_open_tty(const char *path, speed_t speed)
 243{
 244        int rc, fd;
 245        struct termios tio;
 246
 247        rc = -1;
 248
 249        fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
 250        if (fd < 0)
 251                goto out;
 252
 253        memset(&tio, 0, sizeof(tio));
 254
 255        tio.c_iflag = 0;
 256        tio.c_cflag = CREAD|CLOCAL|CS8;
 257
 258        tio.c_cc[VMIN] = 1;
 259        tio.c_cc[VTIME] = 10;
 260
 261        cfsetospeed(&tio, speed);
 262        cfsetispeed(&tio, speed);
 263
 264        rc = tcsetattr(fd, TCSANOW, &tio);
 265        if (rc)
 266                goto out;
 267
 268        rc = fd;
 269out:
 270        if (rc < 0) {
 271                if (fd >= 0)
 272                        close(fd);
 273        }
 274
 275        return rc;
 276}
 277
 278static int
 279kwboot_bootmsg(int tty, void *msg)
 280{
 281        int rc;
 282        char c;
 283        int count;
 284
 285        if (msg == NULL)
 286                kwboot_printv("Please reboot the target into UART boot mode...");
 287        else
 288                kwboot_printv("Sending boot message. Please reboot the target...");
 289
 290        do {
 291                rc = tcflush(tty, TCIOFLUSH);
 292                if (rc)
 293                        break;
 294
 295                for (count = 0; count < 128; count++) {
 296                        rc = kwboot_tty_send(tty, msg, 8);
 297                        if (rc) {
 298                                usleep(msg_req_delay * 1000);
 299                                continue;
 300                        }
 301                }
 302
 303                rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
 304
 305                kwboot_spinner();
 306
 307        } while (rc || c != NAK);
 308
 309        kwboot_printv("\n");
 310
 311        return rc;
 312}
 313
 314static int
 315kwboot_debugmsg(int tty, void *msg)
 316{
 317        int rc;
 318
 319        kwboot_printv("Sending debug message. Please reboot the target...");
 320
 321        do {
 322                char buf[16];
 323
 324                rc = tcflush(tty, TCIOFLUSH);
 325                if (rc)
 326                        break;
 327
 328                rc = kwboot_tty_send(tty, msg, 8);
 329                if (rc) {
 330                        usleep(msg_req_delay * 1000);
 331                        continue;
 332                }
 333
 334                rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo);
 335
 336                kwboot_spinner();
 337
 338        } while (rc);
 339
 340        kwboot_printv("\n");
 341
 342        return rc;
 343}
 344
 345static int
 346kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
 347                    size_t size, int pnum)
 348{
 349        const size_t blksz = sizeof(block->data);
 350        size_t n;
 351        int i;
 352
 353        block->soh = SOH;
 354        block->pnum = pnum;
 355        block->_pnum = ~block->pnum;
 356
 357        n = size < blksz ? size : blksz;
 358        memcpy(&block->data[0], data, n);
 359        memset(&block->data[n], 0, blksz - n);
 360
 361        block->csum = 0;
 362        for (i = 0; i < n; i++)
 363                block->csum += block->data[i];
 364
 365        return n;
 366}
 367
 368static int
 369kwboot_xm_sendblock(int fd, struct kwboot_block *block)
 370{
 371        int rc, retries;
 372        char c;
 373
 374        retries = 16;
 375        do {
 376                rc = kwboot_tty_send(fd, block, sizeof(*block));
 377                if (rc)
 378                        break;
 379
 380                do {
 381                        rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo);
 382                        if (rc)
 383                                break;
 384
 385                        if (c != ACK && c != NAK && c != CAN)
 386                                printf("%c", c);
 387
 388                } while (c != ACK && c != NAK && c != CAN);
 389
 390                if (c != ACK)
 391                        kwboot_progress(-1, '+');
 392
 393        } while (c == NAK && retries-- > 0);
 394
 395        rc = -1;
 396
 397        switch (c) {
 398        case ACK:
 399                rc = 0;
 400                break;
 401        case NAK:
 402                errno = EBADMSG;
 403                break;
 404        case CAN:
 405                errno = ECANCELED;
 406                break;
 407        default:
 408                errno = EPROTO;
 409                break;
 410        }
 411
 412        return rc;
 413}
 414
 415static int
 416kwboot_xmodem(int tty, const void *_data, size_t size)
 417{
 418        const uint8_t *data = _data;
 419        int rc, pnum, N, err;
 420
 421        pnum = 1;
 422        N = 0;
 423
 424        kwboot_printv("Sending boot image...\n");
 425
 426        sleep(2); /* flush isn't effective without it */
 427        tcflush(tty, TCIOFLUSH);
 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
 452out:
 453        return rc;
 454
 455can:
 456        err = errno;
 457        kwboot_tty_send_char(tty, CAN);
 458        errno = err;
 459        goto out;
 460}
 461
 462static int
 463kwboot_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
 493        while (nin > 0) {
 494                nout = write(out, buf, nin);
 495                if (nout <= 0)
 496                        return -1;
 497                nin -= nout;
 498        }
 499
 500        return 0;
 501}
 502
 503static int
 504kwboot_terminal(int tty)
 505{
 506        int rc, in, s;
 507        char *quit = "\34c";
 508        struct termios otio, tio;
 509
 510        rc = -1;
 511
 512        in = STDIN_FILENO;
 513        if (isatty(in)) {
 514                rc = tcgetattr(in, &otio);
 515                if (!rc) {
 516                        tio = otio;
 517                        cfmakeraw(&tio);
 518                        rc = tcsetattr(in, TCSANOW, &tio);
 519                }
 520                if (rc) {
 521                        perror("tcsetattr");
 522                        goto out;
 523                }
 524
 525                kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
 526                              quit[0]|0100, quit[1]);
 527        } else
 528                in = -1;
 529
 530        rc = 0;
 531        s = 0;
 532
 533        do {
 534                fd_set rfds;
 535                int nfds = 0;
 536
 537                FD_SET(tty, &rfds);
 538                nfds = nfds < tty ? tty : nfds;
 539
 540                if (in >= 0) {
 541                        FD_SET(in, &rfds);
 542                        nfds = nfds < in ? in : nfds;
 543                }
 544
 545                nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
 546                if (nfds < 0)
 547                        break;
 548
 549                if (FD_ISSET(tty, &rfds)) {
 550                        rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
 551                        if (rc)
 552                                break;
 553                }
 554
 555                if (FD_ISSET(in, &rfds)) {
 556                        rc = kwboot_term_pipe(in, tty, quit, &s);
 557                        if (rc)
 558                                break;
 559                }
 560        } while (quit[s] != 0);
 561
 562        if (in >= 0)
 563                tcsetattr(in, TCSANOW, &otio);
 564        printf("\n");
 565out:
 566        return rc;
 567}
 568
 569static void *
 570kwboot_mmap_image(const char *path, size_t *size, int prot)
 571{
 572        int rc, fd, flags;
 573        struct stat st;
 574        void *img;
 575
 576        rc = -1;
 577        img = NULL;
 578
 579        fd = open(path, O_RDONLY);
 580        if (fd < 0)
 581                goto out;
 582
 583        rc = fstat(fd, &st);
 584        if (rc)
 585                goto out;
 586
 587        flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
 588
 589        img = mmap(NULL, st.st_size, prot, flags, fd, 0);
 590        if (img == MAP_FAILED) {
 591                img = NULL;
 592                goto out;
 593        }
 594
 595        rc = 0;
 596        *size = st.st_size;
 597out:
 598        if (rc && img) {
 599                munmap(img, st.st_size);
 600                img = NULL;
 601        }
 602        if (fd >= 0)
 603                close(fd);
 604
 605        return img;
 606}
 607
 608static uint8_t
 609kwboot_img_csum8(void *_data, size_t size)
 610{
 611        uint8_t *data = _data, csum;
 612
 613        for (csum = 0; size-- > 0; data++)
 614                csum += *data;
 615
 616        return csum;
 617}
 618
 619static int
 620kwboot_img_patch_hdr(void *img, size_t size)
 621{
 622        int rc;
 623        struct main_hdr_v1 *hdr;
 624        uint8_t csum;
 625        size_t hdrsz = sizeof(*hdr);
 626        int image_ver;
 627
 628        rc = -1;
 629        hdr = img;
 630
 631        if (size < hdrsz) {
 632                errno = EINVAL;
 633                goto out;
 634        }
 635
 636        image_ver = image_version(img);
 637        if (image_ver != 0 && image_ver != 1) {
 638                fprintf(stderr, "Invalid image header version\n");
 639                errno = EINVAL;
 640                goto out;
 641        }
 642
 643        if (image_ver == 0)
 644                hdrsz = sizeof(*hdr);
 645        else
 646                hdrsz = KWBHEADER_V1_SIZE(hdr);
 647
 648        if (size < hdrsz) {
 649                errno = EINVAL;
 650                goto out;
 651        }
 652
 653        csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum;
 654        if (csum != hdr->checksum) {
 655                errno = EINVAL;
 656                goto out;
 657        }
 658
 659        if (hdr->blockid == IBR_HDR_UART_ID) {
 660                rc = 0;
 661                goto out;
 662        }
 663
 664        hdr->blockid = IBR_HDR_UART_ID;
 665
 666        if (image_ver == 0) {
 667                struct main_hdr_v0 *hdr_v0 = img;
 668
 669                hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED;
 670                hdr_v0->nandpagesize = 0;
 671
 672                hdr_v0->srcaddr = hdr_v0->ext
 673                        ? sizeof(struct kwb_header)
 674                        : sizeof(*hdr_v0);
 675        }
 676
 677        hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum;
 678
 679        rc = 0;
 680out:
 681        return rc;
 682}
 683
 684static void
 685kwboot_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
 709int
 710main(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 if (bootmsg) {
 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;
 857out:
 858        if (tty >= 0)
 859                close(tty);
 860
 861        if (img)
 862                munmap(img, size);
 863
 864        return rv;
 865
 866usage:
 867        kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
 868        goto out;
 869}
 870