1d131ad68SLuka Perkov /* 284899e2dSStefan Roese * Boot a Marvell SoC, with Xmodem over UART0. 384899e2dSStefan 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 12f4db6c97SStefan Roese #include "kwbimage.h" 13f4db6c97SStefan Roese #include "mkimage.h" 14f4db6c97SStefan Roese 15d131ad68SLuka Perkov #include <stdlib.h> 16d131ad68SLuka Perkov #include <stdio.h> 17d131ad68SLuka Perkov #include <string.h> 18d131ad68SLuka Perkov #include <stdarg.h> 19f4db6c97SStefan Roese #include <image.h> 20d131ad68SLuka Perkov #include <libgen.h> 21d131ad68SLuka Perkov #include <fcntl.h> 22d131ad68SLuka Perkov #include <errno.h> 23d131ad68SLuka Perkov #include <unistd.h> 24d131ad68SLuka Perkov #include <stdint.h> 25d131ad68SLuka Perkov #include <termios.h> 26d131ad68SLuka Perkov #include <sys/mman.h> 27d131ad68SLuka Perkov #include <sys/stat.h> 28d131ad68SLuka Perkov 29d131ad68SLuka Perkov #ifdef __GNUC__ 30d131ad68SLuka Perkov #define PACKED __attribute((packed)) 31d131ad68SLuka Perkov #else 32d131ad68SLuka Perkov #define PACKED 33d131ad68SLuka Perkov #endif 34d131ad68SLuka Perkov 35d131ad68SLuka Perkov /* 36d131ad68SLuka Perkov * Marvell BootROM UART Sensing 37d131ad68SLuka Perkov */ 38d131ad68SLuka Perkov 39d131ad68SLuka Perkov static unsigned char kwboot_msg_boot[] = { 40d131ad68SLuka Perkov 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 41d131ad68SLuka Perkov }; 42d131ad68SLuka Perkov 4384899e2dSStefan Roese static unsigned char kwboot_msg_debug[] = { 4484899e2dSStefan Roese 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 4584899e2dSStefan Roese }; 4684899e2dSStefan Roese 4784899e2dSStefan Roese /* Defines known to work on Kirkwood */ 48d131ad68SLuka Perkov #define KWBOOT_MSG_REQ_DELAY 10 /* ms */ 49d131ad68SLuka Perkov #define KWBOOT_MSG_RSP_TIMEO 50 /* ms */ 50d131ad68SLuka Perkov 5184899e2dSStefan Roese /* Defines known to work on Armada XP */ 5284899e2dSStefan Roese #define KWBOOT_MSG_REQ_DELAY_AXP 1000 /* ms */ 5384899e2dSStefan Roese #define KWBOOT_MSG_RSP_TIMEO_AXP 1000 /* ms */ 5484899e2dSStefan Roese 55d131ad68SLuka Perkov /* 56d131ad68SLuka Perkov * Xmodem Transfers 57d131ad68SLuka Perkov */ 58d131ad68SLuka Perkov 59d131ad68SLuka Perkov #define SOH 1 /* sender start of block header */ 60d131ad68SLuka Perkov #define EOT 4 /* sender end of block transfer */ 61d131ad68SLuka Perkov #define ACK 6 /* target block ack */ 62d131ad68SLuka Perkov #define NAK 21 /* target block negative ack */ 63d131ad68SLuka Perkov #define CAN 24 /* target/sender transfer cancellation */ 64d131ad68SLuka Perkov 65d131ad68SLuka Perkov struct kwboot_block { 66d131ad68SLuka Perkov uint8_t soh; 67d131ad68SLuka Perkov uint8_t pnum; 68d131ad68SLuka Perkov uint8_t _pnum; 69d131ad68SLuka Perkov uint8_t data[128]; 70d131ad68SLuka Perkov uint8_t csum; 71d131ad68SLuka Perkov } PACKED; 72d131ad68SLuka Perkov 73d131ad68SLuka Perkov #define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */ 74d131ad68SLuka Perkov 75d131ad68SLuka Perkov static int kwboot_verbose; 76d131ad68SLuka Perkov 7784899e2dSStefan Roese static int msg_req_delay = KWBOOT_MSG_REQ_DELAY; 7884899e2dSStefan Roese static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO; 797497a6a1SKevin Smith static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO; 8084899e2dSStefan Roese 81d131ad68SLuka Perkov static void 82d131ad68SLuka Perkov kwboot_printv(const char *fmt, ...) 83d131ad68SLuka Perkov { 84d131ad68SLuka Perkov va_list ap; 85d131ad68SLuka Perkov 86d131ad68SLuka Perkov if (kwboot_verbose) { 87d131ad68SLuka Perkov va_start(ap, fmt); 88d131ad68SLuka Perkov vprintf(fmt, ap); 89d131ad68SLuka Perkov va_end(ap); 90d131ad68SLuka Perkov fflush(stdout); 91d131ad68SLuka Perkov } 92d131ad68SLuka Perkov } 93d131ad68SLuka Perkov 94d131ad68SLuka Perkov static void 95d131ad68SLuka Perkov __spinner(void) 96d131ad68SLuka Perkov { 97d131ad68SLuka Perkov const char seq[] = { '-', '\\', '|', '/' }; 98d131ad68SLuka Perkov const int div = 8; 99d131ad68SLuka Perkov static int state, bs; 100d131ad68SLuka Perkov 101d131ad68SLuka Perkov if (state % div == 0) { 102d131ad68SLuka Perkov fputc(bs, stdout); 103d131ad68SLuka Perkov fputc(seq[state / div % sizeof(seq)], stdout); 104d131ad68SLuka Perkov fflush(stdout); 105d131ad68SLuka Perkov } 106d131ad68SLuka Perkov 107d131ad68SLuka Perkov bs = '\b'; 108d131ad68SLuka Perkov state++; 109d131ad68SLuka Perkov } 110d131ad68SLuka Perkov 111d131ad68SLuka Perkov static void 112d131ad68SLuka Perkov kwboot_spinner(void) 113d131ad68SLuka Perkov { 114d131ad68SLuka Perkov if (kwboot_verbose) 115d131ad68SLuka Perkov __spinner(); 116d131ad68SLuka Perkov } 117d131ad68SLuka Perkov 118d131ad68SLuka Perkov static void 119d131ad68SLuka Perkov __progress(int pct, char c) 120d131ad68SLuka Perkov { 121d131ad68SLuka Perkov const int width = 70; 122d131ad68SLuka Perkov static const char *nl = ""; 123d131ad68SLuka Perkov static int pos; 124d131ad68SLuka Perkov 125d131ad68SLuka Perkov if (pos % width == 0) 126d131ad68SLuka Perkov printf("%s%3d %% [", nl, pct); 127d131ad68SLuka Perkov 128d131ad68SLuka Perkov fputc(c, stdout); 129d131ad68SLuka Perkov 130d131ad68SLuka Perkov nl = "]\n"; 131d131ad68SLuka Perkov pos++; 132d131ad68SLuka Perkov 133d131ad68SLuka Perkov if (pct == 100) { 134d131ad68SLuka Perkov while (pos++ < width) 135d131ad68SLuka Perkov fputc(' ', stdout); 136d131ad68SLuka Perkov fputs(nl, stdout); 137d131ad68SLuka Perkov } 138d131ad68SLuka Perkov 139d131ad68SLuka Perkov fflush(stdout); 140d131ad68SLuka Perkov 141d131ad68SLuka Perkov } 142d131ad68SLuka Perkov 143d131ad68SLuka Perkov static void 144d131ad68SLuka Perkov kwboot_progress(int _pct, char c) 145d131ad68SLuka Perkov { 146d131ad68SLuka Perkov static int pct; 147d131ad68SLuka Perkov 148d131ad68SLuka Perkov if (_pct != -1) 149d131ad68SLuka Perkov pct = _pct; 150d131ad68SLuka Perkov 151d131ad68SLuka Perkov if (kwboot_verbose) 152d131ad68SLuka Perkov __progress(pct, c); 153d131ad68SLuka Perkov } 154d131ad68SLuka Perkov 155d131ad68SLuka Perkov static int 156d131ad68SLuka Perkov kwboot_tty_recv(int fd, void *buf, size_t len, int timeo) 157d131ad68SLuka Perkov { 158d131ad68SLuka Perkov int rc, nfds; 159d131ad68SLuka Perkov fd_set rfds; 160d131ad68SLuka Perkov struct timeval tv; 161d131ad68SLuka Perkov ssize_t n; 162d131ad68SLuka Perkov 163d131ad68SLuka Perkov rc = -1; 164d131ad68SLuka Perkov 165d131ad68SLuka Perkov FD_ZERO(&rfds); 166d131ad68SLuka Perkov FD_SET(fd, &rfds); 167d131ad68SLuka Perkov 168d131ad68SLuka Perkov tv.tv_sec = 0; 169d131ad68SLuka Perkov tv.tv_usec = timeo * 1000; 170d131ad68SLuka Perkov if (tv.tv_usec > 1000000) { 171d131ad68SLuka Perkov tv.tv_sec += tv.tv_usec / 1000000; 172d131ad68SLuka Perkov tv.tv_usec %= 1000000; 173d131ad68SLuka Perkov } 174d131ad68SLuka Perkov 175d131ad68SLuka Perkov do { 176d131ad68SLuka Perkov nfds = select(fd + 1, &rfds, NULL, NULL, &tv); 177d131ad68SLuka Perkov if (nfds < 0) 178d131ad68SLuka Perkov goto out; 179d131ad68SLuka Perkov if (!nfds) { 180d131ad68SLuka Perkov errno = ETIMEDOUT; 181d131ad68SLuka Perkov goto out; 182d131ad68SLuka Perkov } 183d131ad68SLuka Perkov 184d131ad68SLuka Perkov n = read(fd, buf, len); 1854469bd7bSWilly Tarreau if (n <= 0) 186d131ad68SLuka Perkov goto out; 187d131ad68SLuka Perkov 188d131ad68SLuka Perkov buf = (char *)buf + n; 189d131ad68SLuka Perkov len -= n; 190d131ad68SLuka Perkov } while (len > 0); 191d131ad68SLuka Perkov 192d131ad68SLuka Perkov rc = 0; 193d131ad68SLuka Perkov out: 194d131ad68SLuka Perkov return rc; 195d131ad68SLuka Perkov } 196d131ad68SLuka Perkov 197d131ad68SLuka Perkov static int 198d131ad68SLuka Perkov kwboot_tty_send(int fd, const void *buf, size_t len) 199d131ad68SLuka Perkov { 200d131ad68SLuka Perkov int rc; 201d131ad68SLuka Perkov ssize_t n; 202d131ad68SLuka Perkov 20384899e2dSStefan Roese if (!buf) 20484899e2dSStefan Roese return 0; 20584899e2dSStefan Roese 206d131ad68SLuka Perkov rc = -1; 207d131ad68SLuka Perkov 208d131ad68SLuka Perkov do { 209d131ad68SLuka Perkov n = write(fd, buf, len); 210d131ad68SLuka Perkov if (n < 0) 211d131ad68SLuka Perkov goto out; 212d131ad68SLuka Perkov 213d131ad68SLuka Perkov buf = (char *)buf + n; 214d131ad68SLuka Perkov len -= n; 215d131ad68SLuka Perkov } while (len > 0); 216d131ad68SLuka Perkov 217d131ad68SLuka Perkov rc = tcdrain(fd); 218d131ad68SLuka Perkov out: 219d131ad68SLuka Perkov return rc; 220d131ad68SLuka Perkov } 221d131ad68SLuka Perkov 222d131ad68SLuka Perkov static int 223d131ad68SLuka Perkov kwboot_tty_send_char(int fd, unsigned char c) 224d131ad68SLuka Perkov { 225d131ad68SLuka Perkov return kwboot_tty_send(fd, &c, 1); 226d131ad68SLuka Perkov } 227d131ad68SLuka Perkov 228d131ad68SLuka Perkov static speed_t 229d131ad68SLuka Perkov kwboot_tty_speed(int baudrate) 230d131ad68SLuka Perkov { 231d131ad68SLuka Perkov switch (baudrate) { 232d131ad68SLuka Perkov case 115200: 233d131ad68SLuka Perkov return B115200; 234d131ad68SLuka Perkov case 57600: 235d131ad68SLuka Perkov return B57600; 236d131ad68SLuka Perkov case 38400: 237d131ad68SLuka Perkov return B38400; 238d131ad68SLuka Perkov case 19200: 239d131ad68SLuka Perkov return B19200; 240d131ad68SLuka Perkov case 9600: 241d131ad68SLuka Perkov return B9600; 242d131ad68SLuka Perkov } 243d131ad68SLuka Perkov 244d131ad68SLuka Perkov return -1; 245d131ad68SLuka Perkov } 246d131ad68SLuka Perkov 247d131ad68SLuka Perkov static int 248d131ad68SLuka Perkov kwboot_open_tty(const char *path, speed_t speed) 249d131ad68SLuka Perkov { 250d131ad68SLuka Perkov int rc, fd; 251d131ad68SLuka Perkov struct termios tio; 252d131ad68SLuka Perkov 253d131ad68SLuka Perkov rc = -1; 254d131ad68SLuka Perkov 255d131ad68SLuka Perkov fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY); 256d131ad68SLuka Perkov if (fd < 0) 257d131ad68SLuka Perkov goto out; 258d131ad68SLuka Perkov 259d131ad68SLuka Perkov memset(&tio, 0, sizeof(tio)); 260d131ad68SLuka Perkov 261d131ad68SLuka Perkov tio.c_iflag = 0; 262d131ad68SLuka Perkov tio.c_cflag = CREAD|CLOCAL|CS8; 263d131ad68SLuka Perkov 264d131ad68SLuka Perkov tio.c_cc[VMIN] = 1; 265d131ad68SLuka Perkov tio.c_cc[VTIME] = 10; 266d131ad68SLuka Perkov 267d131ad68SLuka Perkov cfsetospeed(&tio, speed); 268d131ad68SLuka Perkov cfsetispeed(&tio, speed); 269d131ad68SLuka Perkov 270d131ad68SLuka Perkov rc = tcsetattr(fd, TCSANOW, &tio); 271d131ad68SLuka Perkov if (rc) 272d131ad68SLuka Perkov goto out; 273d131ad68SLuka Perkov 274d131ad68SLuka Perkov rc = fd; 275d131ad68SLuka Perkov out: 276d131ad68SLuka Perkov if (rc < 0) { 277d131ad68SLuka Perkov if (fd >= 0) 278d131ad68SLuka Perkov close(fd); 279d131ad68SLuka Perkov } 280d131ad68SLuka Perkov 281d131ad68SLuka Perkov return rc; 282d131ad68SLuka Perkov } 283d131ad68SLuka Perkov 284d131ad68SLuka Perkov static int 285d131ad68SLuka Perkov kwboot_bootmsg(int tty, void *msg) 286d131ad68SLuka Perkov { 287d131ad68SLuka Perkov int rc; 288d131ad68SLuka Perkov char c; 289*9ca6fae9SJon Nettleton int count; 290d131ad68SLuka Perkov 29184899e2dSStefan Roese if (msg == NULL) 29284899e2dSStefan Roese kwboot_printv("Please reboot the target into UART boot mode..."); 29384899e2dSStefan Roese else 294d131ad68SLuka Perkov kwboot_printv("Sending boot message. Please reboot the target..."); 295d131ad68SLuka Perkov 296d131ad68SLuka Perkov do { 297d131ad68SLuka Perkov rc = tcflush(tty, TCIOFLUSH); 298d131ad68SLuka Perkov if (rc) 299d131ad68SLuka Perkov break; 300d131ad68SLuka Perkov 301*9ca6fae9SJon Nettleton for (count = 0; count < 128; count++) { 302d131ad68SLuka Perkov rc = kwboot_tty_send(tty, msg, 8); 303d131ad68SLuka Perkov if (rc) { 30484899e2dSStefan Roese usleep(msg_req_delay * 1000); 305d131ad68SLuka Perkov continue; 306d131ad68SLuka Perkov } 307*9ca6fae9SJon Nettleton } 308d131ad68SLuka Perkov 30984899e2dSStefan Roese rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); 310d131ad68SLuka Perkov 311d131ad68SLuka Perkov kwboot_spinner(); 312d131ad68SLuka Perkov 313d131ad68SLuka Perkov } while (rc || c != NAK); 314d131ad68SLuka Perkov 315d131ad68SLuka Perkov kwboot_printv("\n"); 316d131ad68SLuka Perkov 317d131ad68SLuka Perkov return rc; 318d131ad68SLuka Perkov } 319d131ad68SLuka Perkov 320d131ad68SLuka Perkov static int 32184899e2dSStefan Roese kwboot_debugmsg(int tty, void *msg) 32284899e2dSStefan Roese { 32384899e2dSStefan Roese int rc; 32484899e2dSStefan Roese 32584899e2dSStefan Roese kwboot_printv("Sending debug message. Please reboot the target..."); 32684899e2dSStefan Roese 32784899e2dSStefan Roese do { 32884899e2dSStefan Roese char buf[16]; 32984899e2dSStefan Roese 33084899e2dSStefan Roese rc = tcflush(tty, TCIOFLUSH); 33184899e2dSStefan Roese if (rc) 33284899e2dSStefan Roese break; 33384899e2dSStefan Roese 33484899e2dSStefan Roese rc = kwboot_tty_send(tty, msg, 8); 33584899e2dSStefan Roese if (rc) { 33684899e2dSStefan Roese usleep(msg_req_delay * 1000); 33784899e2dSStefan Roese continue; 33884899e2dSStefan Roese } 33984899e2dSStefan Roese 34084899e2dSStefan Roese rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); 34184899e2dSStefan Roese 34284899e2dSStefan Roese kwboot_spinner(); 34384899e2dSStefan Roese 34484899e2dSStefan Roese } while (rc); 34584899e2dSStefan Roese 34684899e2dSStefan Roese kwboot_printv("\n"); 34784899e2dSStefan Roese 34884899e2dSStefan Roese return rc; 34984899e2dSStefan Roese } 35084899e2dSStefan Roese 35184899e2dSStefan Roese static int 352d131ad68SLuka Perkov kwboot_xm_makeblock(struct kwboot_block *block, const void *data, 353d131ad68SLuka Perkov size_t size, int pnum) 354d131ad68SLuka Perkov { 355d131ad68SLuka Perkov const size_t blksz = sizeof(block->data); 356d131ad68SLuka Perkov size_t n; 357d131ad68SLuka Perkov int i; 358d131ad68SLuka Perkov 35984899e2dSStefan Roese block->soh = SOH; 360d131ad68SLuka Perkov block->pnum = pnum; 361d131ad68SLuka Perkov block->_pnum = ~block->pnum; 362d131ad68SLuka Perkov 363d131ad68SLuka Perkov n = size < blksz ? size : blksz; 364d131ad68SLuka Perkov memcpy(&block->data[0], data, n); 365d131ad68SLuka Perkov memset(&block->data[n], 0, blksz - n); 366d131ad68SLuka Perkov 367d131ad68SLuka Perkov block->csum = 0; 368d131ad68SLuka Perkov for (i = 0; i < n; i++) 369d131ad68SLuka Perkov block->csum += block->data[i]; 370d131ad68SLuka Perkov 371d131ad68SLuka Perkov return n; 372d131ad68SLuka Perkov } 373d131ad68SLuka Perkov 374d131ad68SLuka Perkov static int 375d131ad68SLuka Perkov kwboot_xm_sendblock(int fd, struct kwboot_block *block) 376d131ad68SLuka Perkov { 377d131ad68SLuka Perkov int rc, retries; 378d131ad68SLuka Perkov char c; 379d131ad68SLuka Perkov 380d131ad68SLuka Perkov retries = 16; 381d131ad68SLuka Perkov do { 382d131ad68SLuka Perkov rc = kwboot_tty_send(fd, block, sizeof(*block)); 383d131ad68SLuka Perkov if (rc) 384d131ad68SLuka Perkov break; 385d131ad68SLuka Perkov 38684899e2dSStefan Roese do { 3877497a6a1SKevin Smith rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo); 388d131ad68SLuka Perkov if (rc) 389d131ad68SLuka Perkov break; 390d131ad68SLuka Perkov 39184899e2dSStefan Roese if (c != ACK && c != NAK && c != CAN) 39284899e2dSStefan Roese printf("%c", c); 39384899e2dSStefan Roese 39484899e2dSStefan Roese } while (c != ACK && c != NAK && c != CAN); 39584899e2dSStefan Roese 396d131ad68SLuka Perkov if (c != ACK) 397d131ad68SLuka Perkov kwboot_progress(-1, '+'); 398d131ad68SLuka Perkov 399d131ad68SLuka Perkov } while (c == NAK && retries-- > 0); 400d131ad68SLuka Perkov 401d131ad68SLuka Perkov rc = -1; 402d131ad68SLuka Perkov 403d131ad68SLuka Perkov switch (c) { 404d131ad68SLuka Perkov case ACK: 405d131ad68SLuka Perkov rc = 0; 406d131ad68SLuka Perkov break; 407d131ad68SLuka Perkov case NAK: 408d131ad68SLuka Perkov errno = EBADMSG; 409d131ad68SLuka Perkov break; 410d131ad68SLuka Perkov case CAN: 411d131ad68SLuka Perkov errno = ECANCELED; 412d131ad68SLuka Perkov break; 413d131ad68SLuka Perkov default: 414d131ad68SLuka Perkov errno = EPROTO; 415d131ad68SLuka Perkov break; 416d131ad68SLuka Perkov } 417d131ad68SLuka Perkov 418d131ad68SLuka Perkov return rc; 419d131ad68SLuka Perkov } 420d131ad68SLuka Perkov 421d131ad68SLuka Perkov static int 422d131ad68SLuka Perkov kwboot_xmodem(int tty, const void *_data, size_t size) 423d131ad68SLuka Perkov { 424d131ad68SLuka Perkov const uint8_t *data = _data; 425d131ad68SLuka Perkov int rc, pnum, N, err; 426d131ad68SLuka Perkov 427d131ad68SLuka Perkov pnum = 1; 428d131ad68SLuka Perkov N = 0; 429d131ad68SLuka Perkov 430d131ad68SLuka Perkov kwboot_printv("Sending boot image...\n"); 431d131ad68SLuka Perkov 432*9ca6fae9SJon Nettleton sleep(2); /* flush isn't effective without it */ 433*9ca6fae9SJon Nettleton tcflush(tty, TCIOFLUSH); 434*9ca6fae9SJon Nettleton 435d131ad68SLuka Perkov do { 436d131ad68SLuka Perkov struct kwboot_block block; 437d131ad68SLuka Perkov int n; 438d131ad68SLuka Perkov 439d131ad68SLuka Perkov n = kwboot_xm_makeblock(&block, 440d131ad68SLuka Perkov data + N, size - N, 441d131ad68SLuka Perkov pnum++); 442d131ad68SLuka Perkov if (n < 0) 443d131ad68SLuka Perkov goto can; 444d131ad68SLuka Perkov 445d131ad68SLuka Perkov if (!n) 446d131ad68SLuka Perkov break; 447d131ad68SLuka Perkov 448d131ad68SLuka Perkov rc = kwboot_xm_sendblock(tty, &block); 449d131ad68SLuka Perkov if (rc) 450d131ad68SLuka Perkov goto out; 451d131ad68SLuka Perkov 452d131ad68SLuka Perkov N += n; 453d131ad68SLuka Perkov kwboot_progress(N * 100 / size, '.'); 454d131ad68SLuka Perkov } while (1); 455d131ad68SLuka Perkov 456d131ad68SLuka Perkov rc = kwboot_tty_send_char(tty, EOT); 457d131ad68SLuka Perkov 458d131ad68SLuka Perkov out: 459d131ad68SLuka Perkov return rc; 460d131ad68SLuka Perkov 461d131ad68SLuka Perkov can: 462d131ad68SLuka Perkov err = errno; 463d131ad68SLuka Perkov kwboot_tty_send_char(tty, CAN); 464d131ad68SLuka Perkov errno = err; 465d131ad68SLuka Perkov goto out; 466d131ad68SLuka Perkov } 467d131ad68SLuka Perkov 468d131ad68SLuka Perkov static int 469d131ad68SLuka Perkov kwboot_term_pipe(int in, int out, char *quit, int *s) 470d131ad68SLuka Perkov { 471d131ad68SLuka Perkov ssize_t nin, nout; 472d131ad68SLuka Perkov char _buf[128], *buf = _buf; 473d131ad68SLuka Perkov 474d131ad68SLuka Perkov nin = read(in, buf, sizeof(buf)); 4754469bd7bSWilly Tarreau if (nin <= 0) 476d131ad68SLuka Perkov return -1; 477d131ad68SLuka Perkov 478d131ad68SLuka Perkov if (quit) { 479d131ad68SLuka Perkov int i; 480d131ad68SLuka Perkov 481d131ad68SLuka Perkov for (i = 0; i < nin; i++) { 482d131ad68SLuka Perkov if (*buf == quit[*s]) { 483d131ad68SLuka Perkov (*s)++; 484d131ad68SLuka Perkov if (!quit[*s]) 485d131ad68SLuka Perkov return 0; 486d131ad68SLuka Perkov buf++; 487d131ad68SLuka Perkov nin--; 488d131ad68SLuka Perkov } else 489d131ad68SLuka Perkov while (*s > 0) { 490d131ad68SLuka Perkov nout = write(out, quit, *s); 491d131ad68SLuka Perkov if (nout <= 0) 492d131ad68SLuka Perkov return -1; 493d131ad68SLuka Perkov (*s) -= nout; 494d131ad68SLuka Perkov } 495d131ad68SLuka Perkov } 496d131ad68SLuka Perkov } 497d131ad68SLuka Perkov 498d131ad68SLuka Perkov while (nin > 0) { 499d131ad68SLuka Perkov nout = write(out, buf, nin); 500d131ad68SLuka Perkov if (nout <= 0) 501d131ad68SLuka Perkov return -1; 502d131ad68SLuka Perkov nin -= nout; 503d131ad68SLuka Perkov } 504d131ad68SLuka Perkov 505d131ad68SLuka Perkov return 0; 506d131ad68SLuka Perkov } 507d131ad68SLuka Perkov 508d131ad68SLuka Perkov static int 509d131ad68SLuka Perkov kwboot_terminal(int tty) 510d131ad68SLuka Perkov { 511d131ad68SLuka Perkov int rc, in, s; 512d131ad68SLuka Perkov char *quit = "\34c"; 513d131ad68SLuka Perkov struct termios otio, tio; 514d131ad68SLuka Perkov 515d131ad68SLuka Perkov rc = -1; 516d131ad68SLuka Perkov 517d131ad68SLuka Perkov in = STDIN_FILENO; 518d131ad68SLuka Perkov if (isatty(in)) { 519d131ad68SLuka Perkov rc = tcgetattr(in, &otio); 520d131ad68SLuka Perkov if (!rc) { 521d131ad68SLuka Perkov tio = otio; 522d131ad68SLuka Perkov cfmakeraw(&tio); 523d131ad68SLuka Perkov rc = tcsetattr(in, TCSANOW, &tio); 524d131ad68SLuka Perkov } 525d131ad68SLuka Perkov if (rc) { 526d131ad68SLuka Perkov perror("tcsetattr"); 527d131ad68SLuka Perkov goto out; 528d131ad68SLuka Perkov } 529d131ad68SLuka Perkov 530d131ad68SLuka Perkov kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", 531d131ad68SLuka Perkov quit[0]|0100, quit[1]); 532d131ad68SLuka Perkov } else 533d131ad68SLuka Perkov in = -1; 534d131ad68SLuka Perkov 535d131ad68SLuka Perkov rc = 0; 536d131ad68SLuka Perkov s = 0; 537d131ad68SLuka Perkov 538d131ad68SLuka Perkov do { 539d131ad68SLuka Perkov fd_set rfds; 540d131ad68SLuka Perkov int nfds = 0; 541d131ad68SLuka Perkov 542d131ad68SLuka Perkov FD_SET(tty, &rfds); 543d131ad68SLuka Perkov nfds = nfds < tty ? tty : nfds; 544d131ad68SLuka Perkov 545d131ad68SLuka Perkov if (in >= 0) { 546d131ad68SLuka Perkov FD_SET(in, &rfds); 547d131ad68SLuka Perkov nfds = nfds < in ? in : nfds; 548d131ad68SLuka Perkov } 549d131ad68SLuka Perkov 550d131ad68SLuka Perkov nfds = select(nfds + 1, &rfds, NULL, NULL, NULL); 551d131ad68SLuka Perkov if (nfds < 0) 552d131ad68SLuka Perkov break; 553d131ad68SLuka Perkov 554d131ad68SLuka Perkov if (FD_ISSET(tty, &rfds)) { 555d131ad68SLuka Perkov rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); 556d131ad68SLuka Perkov if (rc) 557d131ad68SLuka Perkov break; 558d131ad68SLuka Perkov } 559d131ad68SLuka Perkov 560d131ad68SLuka Perkov if (FD_ISSET(in, &rfds)) { 561d131ad68SLuka Perkov rc = kwboot_term_pipe(in, tty, quit, &s); 562d131ad68SLuka Perkov if (rc) 563d131ad68SLuka Perkov break; 564d131ad68SLuka Perkov } 565d131ad68SLuka Perkov } while (quit[s] != 0); 566d131ad68SLuka Perkov 567d131ad68SLuka Perkov tcsetattr(in, TCSANOW, &otio); 568d131ad68SLuka Perkov out: 569d131ad68SLuka Perkov return rc; 570d131ad68SLuka Perkov } 571d131ad68SLuka Perkov 572d131ad68SLuka Perkov static void * 573d131ad68SLuka Perkov kwboot_mmap_image(const char *path, size_t *size, int prot) 574d131ad68SLuka Perkov { 575d131ad68SLuka Perkov int rc, fd, flags; 576d131ad68SLuka Perkov struct stat st; 577d131ad68SLuka Perkov void *img; 578d131ad68SLuka Perkov 579d131ad68SLuka Perkov rc = -1; 580d131ad68SLuka Perkov img = NULL; 581d131ad68SLuka Perkov 582d131ad68SLuka Perkov fd = open(path, O_RDONLY); 583d131ad68SLuka Perkov if (fd < 0) 584d131ad68SLuka Perkov goto out; 585d131ad68SLuka Perkov 586d131ad68SLuka Perkov rc = fstat(fd, &st); 587d131ad68SLuka Perkov if (rc) 588d131ad68SLuka Perkov goto out; 589d131ad68SLuka Perkov 590d131ad68SLuka Perkov flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED; 591d131ad68SLuka Perkov 592d131ad68SLuka Perkov img = mmap(NULL, st.st_size, prot, flags, fd, 0); 593d131ad68SLuka Perkov if (img == MAP_FAILED) { 594d131ad68SLuka Perkov img = NULL; 595d131ad68SLuka Perkov goto out; 596d131ad68SLuka Perkov } 597d131ad68SLuka Perkov 598d131ad68SLuka Perkov rc = 0; 599d131ad68SLuka Perkov *size = st.st_size; 600d131ad68SLuka Perkov out: 601d131ad68SLuka Perkov if (rc && img) { 602d131ad68SLuka Perkov munmap(img, st.st_size); 603d131ad68SLuka Perkov img = NULL; 604d131ad68SLuka Perkov } 605d131ad68SLuka Perkov if (fd >= 0) 606d131ad68SLuka Perkov close(fd); 607d131ad68SLuka Perkov 608d131ad68SLuka Perkov return img; 609d131ad68SLuka Perkov } 610d131ad68SLuka Perkov 611d131ad68SLuka Perkov static uint8_t 612d131ad68SLuka Perkov kwboot_img_csum8(void *_data, size_t size) 613d131ad68SLuka Perkov { 614d131ad68SLuka Perkov uint8_t *data = _data, csum; 615d131ad68SLuka Perkov 616d131ad68SLuka Perkov for (csum = 0; size-- > 0; data++) 617d131ad68SLuka Perkov csum += *data; 618d131ad68SLuka Perkov 619d131ad68SLuka Perkov return csum; 620d131ad68SLuka Perkov } 621d131ad68SLuka Perkov 622d131ad68SLuka Perkov static int 623d131ad68SLuka Perkov kwboot_img_patch_hdr(void *img, size_t size) 624d131ad68SLuka Perkov { 625d131ad68SLuka Perkov int rc; 626e29f1db3SStefan Roese struct main_hdr_v1 *hdr; 627d131ad68SLuka Perkov uint8_t csum; 628e29f1db3SStefan Roese size_t hdrsz = sizeof(*hdr); 629e29f1db3SStefan Roese int image_ver; 630d131ad68SLuka Perkov 631d131ad68SLuka Perkov rc = -1; 632d131ad68SLuka Perkov hdr = img; 633d131ad68SLuka Perkov 634d131ad68SLuka Perkov if (size < hdrsz) { 635d131ad68SLuka Perkov errno = EINVAL; 636d131ad68SLuka Perkov goto out; 637d131ad68SLuka Perkov } 638d131ad68SLuka Perkov 639e29f1db3SStefan Roese image_ver = image_version(img); 640e29f1db3SStefan Roese if (image_ver < 0) { 641e29f1db3SStefan Roese fprintf(stderr, "Invalid image header version\n"); 642e29f1db3SStefan Roese errno = EINVAL; 643e29f1db3SStefan Roese goto out; 644e29f1db3SStefan Roese } 645e29f1db3SStefan Roese 646e29f1db3SStefan Roese if (image_ver == 0) 647e29f1db3SStefan Roese hdrsz = sizeof(*hdr); 648e29f1db3SStefan Roese else 649e29f1db3SStefan Roese hdrsz = KWBHEADER_V1_SIZE(hdr); 650e29f1db3SStefan Roese 651e29f1db3SStefan Roese csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum; 652e29f1db3SStefan Roese if (csum != hdr->checksum) { 653d131ad68SLuka Perkov errno = EINVAL; 654d131ad68SLuka Perkov goto out; 655d131ad68SLuka Perkov } 656d131ad68SLuka Perkov 657d131ad68SLuka Perkov if (hdr->blockid == IBR_HDR_UART_ID) { 658d131ad68SLuka Perkov rc = 0; 659d131ad68SLuka Perkov goto out; 660d131ad68SLuka Perkov } 661d131ad68SLuka Perkov 662d131ad68SLuka Perkov hdr->blockid = IBR_HDR_UART_ID; 663d131ad68SLuka Perkov 664e29f1db3SStefan Roese if (image_ver == 0) { 665e29f1db3SStefan Roese struct main_hdr_v0 *hdr_v0 = img; 666d131ad68SLuka Perkov 667e29f1db3SStefan Roese hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED; 668e29f1db3SStefan Roese hdr_v0->nandpagesize = 0; 669e29f1db3SStefan Roese 670e29f1db3SStefan Roese hdr_v0->srcaddr = hdr_v0->ext 671d131ad68SLuka Perkov ? sizeof(struct kwb_header) 672e29f1db3SStefan Roese : sizeof(*hdr_v0); 673e29f1db3SStefan Roese } 674d131ad68SLuka Perkov 675e29f1db3SStefan Roese hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum; 676d131ad68SLuka Perkov 677d131ad68SLuka Perkov rc = 0; 678d131ad68SLuka Perkov out: 679d131ad68SLuka Perkov return rc; 680d131ad68SLuka Perkov } 681d131ad68SLuka Perkov 682d131ad68SLuka Perkov static void 683d131ad68SLuka Perkov kwboot_usage(FILE *stream, char *progname) 684d131ad68SLuka Perkov { 685d131ad68SLuka Perkov fprintf(stream, 6868669dacfSKevin Smith "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n", 68784899e2dSStefan Roese progname); 688d131ad68SLuka Perkov fprintf(stream, "\n"); 68984899e2dSStefan Roese fprintf(stream, 69084899e2dSStefan Roese " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n"); 691d131ad68SLuka Perkov fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n"); 69284899e2dSStefan Roese fprintf(stream, 69384899e2dSStefan Roese " -D <image>: boot <image> without preamble (Dove)\n"); 69484899e2dSStefan Roese fprintf(stream, " -d: enter debug mode\n"); 69584899e2dSStefan Roese fprintf(stream, " -a: use timings for Armada XP\n"); 6961c0df9efSStefan Roese fprintf(stream, " -q <req-delay>: use specific request-delay\n"); 6971c0df9efSStefan Roese fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n"); 6987497a6a1SKevin Smith fprintf(stream, 6997497a6a1SKevin Smith " -o <block-timeo>: use specific xmodem block timeout\n"); 700d131ad68SLuka Perkov fprintf(stream, "\n"); 701d131ad68SLuka Perkov fprintf(stream, " -t: mini terminal\n"); 702d131ad68SLuka Perkov fprintf(stream, "\n"); 703d131ad68SLuka Perkov fprintf(stream, " -B <baud>: set baud rate\n"); 704d131ad68SLuka Perkov fprintf(stream, "\n"); 705d131ad68SLuka Perkov } 706d131ad68SLuka Perkov 707d131ad68SLuka Perkov int 708d131ad68SLuka Perkov main(int argc, char **argv) 709d131ad68SLuka Perkov { 710d131ad68SLuka Perkov const char *ttypath, *imgpath; 711d131ad68SLuka Perkov int rv, rc, tty, term, prot, patch; 712d131ad68SLuka Perkov void *bootmsg; 71384899e2dSStefan Roese void *debugmsg; 714d131ad68SLuka Perkov void *img; 715d131ad68SLuka Perkov size_t size; 716d131ad68SLuka Perkov speed_t speed; 717d131ad68SLuka Perkov 718d131ad68SLuka Perkov rv = 1; 719d131ad68SLuka Perkov tty = -1; 720d131ad68SLuka Perkov bootmsg = NULL; 72184899e2dSStefan Roese debugmsg = NULL; 722d131ad68SLuka Perkov imgpath = NULL; 723d131ad68SLuka Perkov img = NULL; 724d131ad68SLuka Perkov term = 0; 725d131ad68SLuka Perkov patch = 0; 726d131ad68SLuka Perkov size = 0; 727d131ad68SLuka Perkov speed = B115200; 728d131ad68SLuka Perkov 729d131ad68SLuka Perkov kwboot_verbose = isatty(STDOUT_FILENO); 730d131ad68SLuka Perkov 731d131ad68SLuka Perkov do { 7327497a6a1SKevin Smith int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:"); 733d131ad68SLuka Perkov if (c < 0) 734d131ad68SLuka Perkov break; 735d131ad68SLuka Perkov 736d131ad68SLuka Perkov switch (c) { 737d131ad68SLuka Perkov case 'b': 738d131ad68SLuka Perkov bootmsg = kwboot_msg_boot; 739d131ad68SLuka Perkov imgpath = optarg; 740d131ad68SLuka Perkov break; 741d131ad68SLuka Perkov 74284899e2dSStefan Roese case 'D': 74384899e2dSStefan Roese bootmsg = NULL; 74484899e2dSStefan Roese imgpath = optarg; 74584899e2dSStefan Roese break; 74684899e2dSStefan Roese 74784899e2dSStefan Roese case 'd': 74884899e2dSStefan Roese debugmsg = kwboot_msg_debug; 74984899e2dSStefan Roese break; 75084899e2dSStefan Roese 751d131ad68SLuka Perkov case 'p': 752d131ad68SLuka Perkov patch = 1; 753d131ad68SLuka Perkov break; 754d131ad68SLuka Perkov 755d131ad68SLuka Perkov case 't': 756d131ad68SLuka Perkov term = 1; 757d131ad68SLuka Perkov break; 758d131ad68SLuka Perkov 75984899e2dSStefan Roese case 'a': 76084899e2dSStefan Roese msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; 76184899e2dSStefan Roese msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; 76284899e2dSStefan Roese break; 76384899e2dSStefan Roese 7641c0df9efSStefan Roese case 'q': 7651c0df9efSStefan Roese msg_req_delay = atoi(optarg); 7661c0df9efSStefan Roese break; 7671c0df9efSStefan Roese 7681c0df9efSStefan Roese case 's': 7691c0df9efSStefan Roese msg_rsp_timeo = atoi(optarg); 7701c0df9efSStefan Roese break; 7711c0df9efSStefan Roese 7727497a6a1SKevin Smith case 'o': 7737497a6a1SKevin Smith blk_rsp_timeo = atoi(optarg); 7747497a6a1SKevin Smith break; 7757497a6a1SKevin Smith 776d131ad68SLuka Perkov case 'B': 777d131ad68SLuka Perkov speed = kwboot_tty_speed(atoi(optarg)); 778d131ad68SLuka Perkov if (speed == -1) 779d131ad68SLuka Perkov goto usage; 780d131ad68SLuka Perkov break; 781d131ad68SLuka Perkov 782d131ad68SLuka Perkov case 'h': 783d131ad68SLuka Perkov rv = 0; 784d131ad68SLuka Perkov default: 785d131ad68SLuka Perkov goto usage; 786d131ad68SLuka Perkov } 787d131ad68SLuka Perkov } while (1); 788d131ad68SLuka Perkov 78984899e2dSStefan Roese if (!bootmsg && !term && !debugmsg) 790d131ad68SLuka Perkov goto usage; 791d131ad68SLuka Perkov 792d131ad68SLuka Perkov if (patch && !imgpath) 793d131ad68SLuka Perkov goto usage; 794d131ad68SLuka Perkov 795d131ad68SLuka Perkov if (argc - optind < 1) 796d131ad68SLuka Perkov goto usage; 797d131ad68SLuka Perkov 798d131ad68SLuka Perkov ttypath = argv[optind++]; 799d131ad68SLuka Perkov 800d131ad68SLuka Perkov tty = kwboot_open_tty(ttypath, speed); 801d131ad68SLuka Perkov if (tty < 0) { 802d131ad68SLuka Perkov perror(ttypath); 803d131ad68SLuka Perkov goto out; 804d131ad68SLuka Perkov } 805d131ad68SLuka Perkov 806d131ad68SLuka Perkov if (imgpath) { 807d131ad68SLuka Perkov prot = PROT_READ | (patch ? PROT_WRITE : 0); 808d131ad68SLuka Perkov 809d131ad68SLuka Perkov img = kwboot_mmap_image(imgpath, &size, prot); 810d131ad68SLuka Perkov if (!img) { 811d131ad68SLuka Perkov perror(imgpath); 812d131ad68SLuka Perkov goto out; 813d131ad68SLuka Perkov } 814d131ad68SLuka Perkov } 815d131ad68SLuka Perkov 816d131ad68SLuka Perkov if (patch) { 817d131ad68SLuka Perkov rc = kwboot_img_patch_hdr(img, size); 818d131ad68SLuka Perkov if (rc) { 819d131ad68SLuka Perkov fprintf(stderr, "%s: Invalid image.\n", imgpath); 820d131ad68SLuka Perkov goto out; 821d131ad68SLuka Perkov } 822d131ad68SLuka Perkov } 823d131ad68SLuka Perkov 82484899e2dSStefan Roese if (debugmsg) { 82584899e2dSStefan Roese rc = kwboot_debugmsg(tty, debugmsg); 82684899e2dSStefan Roese if (rc) { 82784899e2dSStefan Roese perror("debugmsg"); 82884899e2dSStefan Roese goto out; 82984899e2dSStefan Roese } 8303475a71dSWilly Tarreau } else if (bootmsg) { 831d131ad68SLuka Perkov rc = kwboot_bootmsg(tty, bootmsg); 832d131ad68SLuka Perkov if (rc) { 833d131ad68SLuka Perkov perror("bootmsg"); 834d131ad68SLuka Perkov goto out; 835d131ad68SLuka Perkov } 836d131ad68SLuka Perkov } 837d131ad68SLuka Perkov 838d131ad68SLuka Perkov if (img) { 839d131ad68SLuka Perkov rc = kwboot_xmodem(tty, img, size); 840d131ad68SLuka Perkov if (rc) { 841d131ad68SLuka Perkov perror("xmodem"); 842d131ad68SLuka Perkov goto out; 843d131ad68SLuka Perkov } 844d131ad68SLuka Perkov } 845d131ad68SLuka Perkov 846d131ad68SLuka Perkov if (term) { 847d131ad68SLuka Perkov rc = kwboot_terminal(tty); 848d131ad68SLuka Perkov if (rc && !(errno == EINTR)) { 849d131ad68SLuka Perkov perror("terminal"); 850d131ad68SLuka Perkov goto out; 851d131ad68SLuka Perkov } 852d131ad68SLuka Perkov } 853d131ad68SLuka Perkov 854d131ad68SLuka Perkov rv = 0; 855d131ad68SLuka Perkov out: 856d131ad68SLuka Perkov if (tty >= 0) 857d131ad68SLuka Perkov close(tty); 858d131ad68SLuka Perkov 859d131ad68SLuka Perkov if (img) 860d131ad68SLuka Perkov munmap(img, size); 861d131ad68SLuka Perkov 862d131ad68SLuka Perkov return rv; 863d131ad68SLuka Perkov 864d131ad68SLuka Perkov usage: 865d131ad68SLuka Perkov kwboot_usage(rv ? stderr : stdout, basename(argv[0])); 866d131ad68SLuka Perkov goto out; 867d131ad68SLuka Perkov } 868