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