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