PageRenderTime 21ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/kwboot.c

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