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; 79*7497a6a1SKevin 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); 185d131ad68SLuka Perkov 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; 289d131ad68SLuka Perkov 29084899e2dSStefan Roese if (msg == NULL) 29184899e2dSStefan Roese kwboot_printv("Please reboot the target into UART boot mode..."); 29284899e2dSStefan Roese else 293d131ad68SLuka Perkov kwboot_printv("Sending boot message. Please reboot the target..."); 294d131ad68SLuka Perkov 295d131ad68SLuka Perkov do { 296d131ad68SLuka Perkov rc = tcflush(tty, TCIOFLUSH); 297d131ad68SLuka Perkov if (rc) 298d131ad68SLuka Perkov break; 299d131ad68SLuka Perkov 300d131ad68SLuka Perkov rc = kwboot_tty_send(tty, msg, 8); 301d131ad68SLuka Perkov if (rc) { 30284899e2dSStefan Roese usleep(msg_req_delay * 1000); 303d131ad68SLuka Perkov continue; 304d131ad68SLuka Perkov } 305d131ad68SLuka Perkov 30684899e2dSStefan Roese rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); 307d131ad68SLuka Perkov 308d131ad68SLuka Perkov kwboot_spinner(); 309d131ad68SLuka Perkov 310d131ad68SLuka Perkov } while (rc || c != NAK); 311d131ad68SLuka Perkov 312d131ad68SLuka Perkov kwboot_printv("\n"); 313d131ad68SLuka Perkov 314d131ad68SLuka Perkov return rc; 315d131ad68SLuka Perkov } 316d131ad68SLuka Perkov 317d131ad68SLuka Perkov static int 31884899e2dSStefan Roese kwboot_debugmsg(int tty, void *msg) 31984899e2dSStefan Roese { 32084899e2dSStefan Roese int rc; 32184899e2dSStefan Roese 32284899e2dSStefan Roese kwboot_printv("Sending debug message. Please reboot the target..."); 32384899e2dSStefan Roese 32484899e2dSStefan Roese do { 32584899e2dSStefan Roese char buf[16]; 32684899e2dSStefan Roese 32784899e2dSStefan Roese rc = tcflush(tty, TCIOFLUSH); 32884899e2dSStefan Roese if (rc) 32984899e2dSStefan Roese break; 33084899e2dSStefan Roese 33184899e2dSStefan Roese rc = kwboot_tty_send(tty, msg, 8); 33284899e2dSStefan Roese if (rc) { 33384899e2dSStefan Roese usleep(msg_req_delay * 1000); 33484899e2dSStefan Roese continue; 33584899e2dSStefan Roese } 33684899e2dSStefan Roese 33784899e2dSStefan Roese rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); 33884899e2dSStefan Roese 33984899e2dSStefan Roese kwboot_spinner(); 34084899e2dSStefan Roese 34184899e2dSStefan Roese } while (rc); 34284899e2dSStefan Roese 34384899e2dSStefan Roese kwboot_printv("\n"); 34484899e2dSStefan Roese 34584899e2dSStefan Roese return rc; 34684899e2dSStefan Roese } 34784899e2dSStefan Roese 34884899e2dSStefan Roese static int 349d131ad68SLuka Perkov kwboot_xm_makeblock(struct kwboot_block *block, const void *data, 350d131ad68SLuka Perkov size_t size, int pnum) 351d131ad68SLuka Perkov { 352d131ad68SLuka Perkov const size_t blksz = sizeof(block->data); 353d131ad68SLuka Perkov size_t n; 354d131ad68SLuka Perkov int i; 355d131ad68SLuka Perkov 35684899e2dSStefan Roese block->soh = SOH; 357d131ad68SLuka Perkov block->pnum = pnum; 358d131ad68SLuka Perkov block->_pnum = ~block->pnum; 359d131ad68SLuka Perkov 360d131ad68SLuka Perkov n = size < blksz ? size : blksz; 361d131ad68SLuka Perkov memcpy(&block->data[0], data, n); 362d131ad68SLuka Perkov memset(&block->data[n], 0, blksz - n); 363d131ad68SLuka Perkov 364d131ad68SLuka Perkov block->csum = 0; 365d131ad68SLuka Perkov for (i = 0; i < n; i++) 366d131ad68SLuka Perkov block->csum += block->data[i]; 367d131ad68SLuka Perkov 368d131ad68SLuka Perkov return n; 369d131ad68SLuka Perkov } 370d131ad68SLuka Perkov 371d131ad68SLuka Perkov static int 372d131ad68SLuka Perkov kwboot_xm_sendblock(int fd, struct kwboot_block *block) 373d131ad68SLuka Perkov { 374d131ad68SLuka Perkov int rc, retries; 375d131ad68SLuka Perkov char c; 376d131ad68SLuka Perkov 377d131ad68SLuka Perkov retries = 16; 378d131ad68SLuka Perkov do { 379d131ad68SLuka Perkov rc = kwboot_tty_send(fd, block, sizeof(*block)); 380d131ad68SLuka Perkov if (rc) 381d131ad68SLuka Perkov break; 382d131ad68SLuka Perkov 38384899e2dSStefan Roese do { 384*7497a6a1SKevin Smith rc = kwboot_tty_recv(fd, &c, 1, blk_rsp_timeo); 385d131ad68SLuka Perkov if (rc) 386d131ad68SLuka Perkov break; 387d131ad68SLuka Perkov 38884899e2dSStefan Roese if (c != ACK && c != NAK && c != CAN) 38984899e2dSStefan Roese printf("%c", c); 39084899e2dSStefan Roese 39184899e2dSStefan Roese } while (c != ACK && c != NAK && c != CAN); 39284899e2dSStefan Roese 393d131ad68SLuka Perkov if (c != ACK) 394d131ad68SLuka Perkov kwboot_progress(-1, '+'); 395d131ad68SLuka Perkov 396d131ad68SLuka Perkov } while (c == NAK && retries-- > 0); 397d131ad68SLuka Perkov 398d131ad68SLuka Perkov rc = -1; 399d131ad68SLuka Perkov 400d131ad68SLuka Perkov switch (c) { 401d131ad68SLuka Perkov case ACK: 402d131ad68SLuka Perkov rc = 0; 403d131ad68SLuka Perkov break; 404d131ad68SLuka Perkov case NAK: 405d131ad68SLuka Perkov errno = EBADMSG; 406d131ad68SLuka Perkov break; 407d131ad68SLuka Perkov case CAN: 408d131ad68SLuka Perkov errno = ECANCELED; 409d131ad68SLuka Perkov break; 410d131ad68SLuka Perkov default: 411d131ad68SLuka Perkov errno = EPROTO; 412d131ad68SLuka Perkov break; 413d131ad68SLuka Perkov } 414d131ad68SLuka Perkov 415d131ad68SLuka Perkov return rc; 416d131ad68SLuka Perkov } 417d131ad68SLuka Perkov 418d131ad68SLuka Perkov static int 419d131ad68SLuka Perkov kwboot_xmodem(int tty, const void *_data, size_t size) 420d131ad68SLuka Perkov { 421d131ad68SLuka Perkov const uint8_t *data = _data; 422d131ad68SLuka Perkov int rc, pnum, N, err; 423d131ad68SLuka Perkov 424d131ad68SLuka Perkov pnum = 1; 425d131ad68SLuka Perkov N = 0; 426d131ad68SLuka Perkov 427d131ad68SLuka Perkov kwboot_printv("Sending boot image...\n"); 428d131ad68SLuka Perkov 429d131ad68SLuka Perkov do { 430d131ad68SLuka Perkov struct kwboot_block block; 431d131ad68SLuka Perkov int n; 432d131ad68SLuka Perkov 433d131ad68SLuka Perkov n = kwboot_xm_makeblock(&block, 434d131ad68SLuka Perkov data + N, size - N, 435d131ad68SLuka Perkov pnum++); 436d131ad68SLuka Perkov if (n < 0) 437d131ad68SLuka Perkov goto can; 438d131ad68SLuka Perkov 439d131ad68SLuka Perkov if (!n) 440d131ad68SLuka Perkov break; 441d131ad68SLuka Perkov 442d131ad68SLuka Perkov rc = kwboot_xm_sendblock(tty, &block); 443d131ad68SLuka Perkov if (rc) 444d131ad68SLuka Perkov goto out; 445d131ad68SLuka Perkov 446d131ad68SLuka Perkov N += n; 447d131ad68SLuka Perkov kwboot_progress(N * 100 / size, '.'); 448d131ad68SLuka Perkov } while (1); 449d131ad68SLuka Perkov 450d131ad68SLuka Perkov rc = kwboot_tty_send_char(tty, EOT); 451d131ad68SLuka Perkov 452d131ad68SLuka Perkov out: 453d131ad68SLuka Perkov return rc; 454d131ad68SLuka Perkov 455d131ad68SLuka Perkov can: 456d131ad68SLuka Perkov err = errno; 457d131ad68SLuka Perkov kwboot_tty_send_char(tty, CAN); 458d131ad68SLuka Perkov errno = err; 459d131ad68SLuka Perkov goto out; 460d131ad68SLuka Perkov } 461d131ad68SLuka Perkov 462d131ad68SLuka Perkov static int 463d131ad68SLuka Perkov kwboot_term_pipe(int in, int out, char *quit, int *s) 464d131ad68SLuka Perkov { 465d131ad68SLuka Perkov ssize_t nin, nout; 466d131ad68SLuka Perkov char _buf[128], *buf = _buf; 467d131ad68SLuka Perkov 468d131ad68SLuka Perkov nin = read(in, buf, sizeof(buf)); 469d131ad68SLuka Perkov if (nin < 0) 470d131ad68SLuka Perkov return -1; 471d131ad68SLuka Perkov 472d131ad68SLuka Perkov if (quit) { 473d131ad68SLuka Perkov int i; 474d131ad68SLuka Perkov 475d131ad68SLuka Perkov for (i = 0; i < nin; i++) { 476d131ad68SLuka Perkov if (*buf == quit[*s]) { 477d131ad68SLuka Perkov (*s)++; 478d131ad68SLuka Perkov if (!quit[*s]) 479d131ad68SLuka Perkov return 0; 480d131ad68SLuka Perkov buf++; 481d131ad68SLuka Perkov nin--; 482d131ad68SLuka Perkov } else 483d131ad68SLuka Perkov while (*s > 0) { 484d131ad68SLuka Perkov nout = write(out, quit, *s); 485d131ad68SLuka Perkov if (nout <= 0) 486d131ad68SLuka Perkov return -1; 487d131ad68SLuka Perkov (*s) -= nout; 488d131ad68SLuka Perkov } 489d131ad68SLuka Perkov } 490d131ad68SLuka Perkov } 491d131ad68SLuka Perkov 492d131ad68SLuka Perkov while (nin > 0) { 493d131ad68SLuka Perkov nout = write(out, buf, nin); 494d131ad68SLuka Perkov if (nout <= 0) 495d131ad68SLuka Perkov return -1; 496d131ad68SLuka Perkov nin -= nout; 497d131ad68SLuka Perkov } 498d131ad68SLuka Perkov 499d131ad68SLuka Perkov return 0; 500d131ad68SLuka Perkov } 501d131ad68SLuka Perkov 502d131ad68SLuka Perkov static int 503d131ad68SLuka Perkov kwboot_terminal(int tty) 504d131ad68SLuka Perkov { 505d131ad68SLuka Perkov int rc, in, s; 506d131ad68SLuka Perkov char *quit = "\34c"; 507d131ad68SLuka Perkov struct termios otio, tio; 508d131ad68SLuka Perkov 509d131ad68SLuka Perkov rc = -1; 510d131ad68SLuka Perkov 511d131ad68SLuka Perkov in = STDIN_FILENO; 512d131ad68SLuka Perkov if (isatty(in)) { 513d131ad68SLuka Perkov rc = tcgetattr(in, &otio); 514d131ad68SLuka Perkov if (!rc) { 515d131ad68SLuka Perkov tio = otio; 516d131ad68SLuka Perkov cfmakeraw(&tio); 517d131ad68SLuka Perkov rc = tcsetattr(in, TCSANOW, &tio); 518d131ad68SLuka Perkov } 519d131ad68SLuka Perkov if (rc) { 520d131ad68SLuka Perkov perror("tcsetattr"); 521d131ad68SLuka Perkov goto out; 522d131ad68SLuka Perkov } 523d131ad68SLuka Perkov 524d131ad68SLuka Perkov kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", 525d131ad68SLuka Perkov quit[0]|0100, quit[1]); 526d131ad68SLuka Perkov } else 527d131ad68SLuka Perkov in = -1; 528d131ad68SLuka Perkov 529d131ad68SLuka Perkov rc = 0; 530d131ad68SLuka Perkov s = 0; 531d131ad68SLuka Perkov 532d131ad68SLuka Perkov do { 533d131ad68SLuka Perkov fd_set rfds; 534d131ad68SLuka Perkov int nfds = 0; 535d131ad68SLuka Perkov 536d131ad68SLuka Perkov FD_SET(tty, &rfds); 537d131ad68SLuka Perkov nfds = nfds < tty ? tty : nfds; 538d131ad68SLuka Perkov 539d131ad68SLuka Perkov if (in >= 0) { 540d131ad68SLuka Perkov FD_SET(in, &rfds); 541d131ad68SLuka Perkov nfds = nfds < in ? in : nfds; 542d131ad68SLuka Perkov } 543d131ad68SLuka Perkov 544d131ad68SLuka Perkov nfds = select(nfds + 1, &rfds, NULL, NULL, NULL); 545d131ad68SLuka Perkov if (nfds < 0) 546d131ad68SLuka Perkov break; 547d131ad68SLuka Perkov 548d131ad68SLuka Perkov if (FD_ISSET(tty, &rfds)) { 549d131ad68SLuka Perkov rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); 550d131ad68SLuka Perkov if (rc) 551d131ad68SLuka Perkov break; 552d131ad68SLuka Perkov } 553d131ad68SLuka Perkov 554d131ad68SLuka Perkov if (FD_ISSET(in, &rfds)) { 555d131ad68SLuka Perkov rc = kwboot_term_pipe(in, tty, quit, &s); 556d131ad68SLuka Perkov if (rc) 557d131ad68SLuka Perkov break; 558d131ad68SLuka Perkov } 559d131ad68SLuka Perkov } while (quit[s] != 0); 560d131ad68SLuka Perkov 561d131ad68SLuka Perkov tcsetattr(in, TCSANOW, &otio); 562d131ad68SLuka Perkov out: 563d131ad68SLuka Perkov return rc; 564d131ad68SLuka Perkov } 565d131ad68SLuka Perkov 566d131ad68SLuka Perkov static void * 567d131ad68SLuka Perkov kwboot_mmap_image(const char *path, size_t *size, int prot) 568d131ad68SLuka Perkov { 569d131ad68SLuka Perkov int rc, fd, flags; 570d131ad68SLuka Perkov struct stat st; 571d131ad68SLuka Perkov void *img; 572d131ad68SLuka Perkov 573d131ad68SLuka Perkov rc = -1; 574d131ad68SLuka Perkov img = NULL; 575d131ad68SLuka Perkov 576d131ad68SLuka Perkov fd = open(path, O_RDONLY); 577d131ad68SLuka Perkov if (fd < 0) 578d131ad68SLuka Perkov goto out; 579d131ad68SLuka Perkov 580d131ad68SLuka Perkov rc = fstat(fd, &st); 581d131ad68SLuka Perkov if (rc) 582d131ad68SLuka Perkov goto out; 583d131ad68SLuka Perkov 584d131ad68SLuka Perkov flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED; 585d131ad68SLuka Perkov 586d131ad68SLuka Perkov img = mmap(NULL, st.st_size, prot, flags, fd, 0); 587d131ad68SLuka Perkov if (img == MAP_FAILED) { 588d131ad68SLuka Perkov img = NULL; 589d131ad68SLuka Perkov goto out; 590d131ad68SLuka Perkov } 591d131ad68SLuka Perkov 592d131ad68SLuka Perkov rc = 0; 593d131ad68SLuka Perkov *size = st.st_size; 594d131ad68SLuka Perkov out: 595d131ad68SLuka Perkov if (rc && img) { 596d131ad68SLuka Perkov munmap(img, st.st_size); 597d131ad68SLuka Perkov img = NULL; 598d131ad68SLuka Perkov } 599d131ad68SLuka Perkov if (fd >= 0) 600d131ad68SLuka Perkov close(fd); 601d131ad68SLuka Perkov 602d131ad68SLuka Perkov return img; 603d131ad68SLuka Perkov } 604d131ad68SLuka Perkov 605d131ad68SLuka Perkov static uint8_t 606d131ad68SLuka Perkov kwboot_img_csum8(void *_data, size_t size) 607d131ad68SLuka Perkov { 608d131ad68SLuka Perkov uint8_t *data = _data, csum; 609d131ad68SLuka Perkov 610d131ad68SLuka Perkov for (csum = 0; size-- > 0; data++) 611d131ad68SLuka Perkov csum += *data; 612d131ad68SLuka Perkov 613d131ad68SLuka Perkov return csum; 614d131ad68SLuka Perkov } 615d131ad68SLuka Perkov 616d131ad68SLuka Perkov static int 617d131ad68SLuka Perkov kwboot_img_patch_hdr(void *img, size_t size) 618d131ad68SLuka Perkov { 619d131ad68SLuka Perkov int rc; 620e29f1db3SStefan Roese struct main_hdr_v1 *hdr; 621d131ad68SLuka Perkov uint8_t csum; 622e29f1db3SStefan Roese size_t hdrsz = sizeof(*hdr); 623e29f1db3SStefan Roese int image_ver; 624d131ad68SLuka Perkov 625d131ad68SLuka Perkov rc = -1; 626d131ad68SLuka Perkov hdr = img; 627d131ad68SLuka Perkov 628d131ad68SLuka Perkov if (size < hdrsz) { 629d131ad68SLuka Perkov errno = EINVAL; 630d131ad68SLuka Perkov goto out; 631d131ad68SLuka Perkov } 632d131ad68SLuka Perkov 633e29f1db3SStefan Roese image_ver = image_version(img); 634e29f1db3SStefan Roese if (image_ver < 0) { 635e29f1db3SStefan Roese fprintf(stderr, "Invalid image header version\n"); 636e29f1db3SStefan Roese errno = EINVAL; 637e29f1db3SStefan Roese goto out; 638e29f1db3SStefan Roese } 639e29f1db3SStefan Roese 640e29f1db3SStefan Roese if (image_ver == 0) 641e29f1db3SStefan Roese hdrsz = sizeof(*hdr); 642e29f1db3SStefan Roese else 643e29f1db3SStefan Roese hdrsz = KWBHEADER_V1_SIZE(hdr); 644e29f1db3SStefan Roese 645e29f1db3SStefan Roese csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum; 646e29f1db3SStefan Roese if (csum != hdr->checksum) { 647d131ad68SLuka Perkov errno = EINVAL; 648d131ad68SLuka Perkov goto out; 649d131ad68SLuka Perkov } 650d131ad68SLuka Perkov 651d131ad68SLuka Perkov if (hdr->blockid == IBR_HDR_UART_ID) { 652d131ad68SLuka Perkov rc = 0; 653d131ad68SLuka Perkov goto out; 654d131ad68SLuka Perkov } 655d131ad68SLuka Perkov 656d131ad68SLuka Perkov hdr->blockid = IBR_HDR_UART_ID; 657d131ad68SLuka Perkov 658f4db6c97SStefan Roese /* 659f4db6c97SStefan Roese * Subtract mkimage header size from destination address 660f4db6c97SStefan Roese * as this header is not expected by the Marvell BootROM. 661f4db6c97SStefan Roese * This way, the execution address is identical to the 662f4db6c97SStefan Roese * one the image is compiled for (TEXT_BASE). 663f4db6c97SStefan Roese */ 664f4db6c97SStefan Roese hdr->destaddr = hdr->destaddr - sizeof(struct image_header); 665f4db6c97SStefan Roese 666e29f1db3SStefan Roese if (image_ver == 0) { 667e29f1db3SStefan Roese struct main_hdr_v0 *hdr_v0 = img; 668d131ad68SLuka Perkov 669e29f1db3SStefan Roese hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED; 670e29f1db3SStefan Roese hdr_v0->nandpagesize = 0; 671e29f1db3SStefan Roese 672e29f1db3SStefan Roese hdr_v0->srcaddr = hdr_v0->ext 673d131ad68SLuka Perkov ? sizeof(struct kwb_header) 674e29f1db3SStefan Roese : sizeof(*hdr_v0); 675e29f1db3SStefan Roese } 676d131ad68SLuka Perkov 677e29f1db3SStefan Roese hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum; 678d131ad68SLuka Perkov 679d131ad68SLuka Perkov rc = 0; 680d131ad68SLuka Perkov out: 681d131ad68SLuka Perkov return rc; 682d131ad68SLuka Perkov } 683d131ad68SLuka Perkov 684d131ad68SLuka Perkov static void 685d131ad68SLuka Perkov kwboot_usage(FILE *stream, char *progname) 686d131ad68SLuka Perkov { 687d131ad68SLuka Perkov fprintf(stream, 6888669dacfSKevin Smith "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n", 68984899e2dSStefan Roese progname); 690d131ad68SLuka Perkov fprintf(stream, "\n"); 69184899e2dSStefan Roese fprintf(stream, 69284899e2dSStefan Roese " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n"); 693d131ad68SLuka Perkov fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n"); 69484899e2dSStefan Roese fprintf(stream, 69584899e2dSStefan Roese " -D <image>: boot <image> without preamble (Dove)\n"); 69684899e2dSStefan Roese fprintf(stream, " -d: enter debug mode\n"); 69784899e2dSStefan Roese fprintf(stream, " -a: use timings for Armada XP\n"); 6981c0df9efSStefan Roese fprintf(stream, " -q <req-delay>: use specific request-delay\n"); 6991c0df9efSStefan Roese fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n"); 700*7497a6a1SKevin Smith fprintf(stream, 701*7497a6a1SKevin Smith " -o <block-timeo>: use specific xmodem block timeout\n"); 702d131ad68SLuka Perkov fprintf(stream, "\n"); 703d131ad68SLuka Perkov fprintf(stream, " -t: mini terminal\n"); 704d131ad68SLuka Perkov fprintf(stream, "\n"); 705d131ad68SLuka Perkov fprintf(stream, " -B <baud>: set baud rate\n"); 706d131ad68SLuka Perkov fprintf(stream, "\n"); 707d131ad68SLuka Perkov } 708d131ad68SLuka Perkov 709d131ad68SLuka Perkov int 710d131ad68SLuka Perkov main(int argc, char **argv) 711d131ad68SLuka Perkov { 712d131ad68SLuka Perkov const char *ttypath, *imgpath; 713d131ad68SLuka Perkov int rv, rc, tty, term, prot, patch; 714d131ad68SLuka Perkov void *bootmsg; 71584899e2dSStefan Roese void *debugmsg; 716d131ad68SLuka Perkov void *img; 717d131ad68SLuka Perkov size_t size; 718d131ad68SLuka Perkov speed_t speed; 719d131ad68SLuka Perkov 720d131ad68SLuka Perkov rv = 1; 721d131ad68SLuka Perkov tty = -1; 722d131ad68SLuka Perkov bootmsg = NULL; 72384899e2dSStefan Roese debugmsg = NULL; 724d131ad68SLuka Perkov imgpath = NULL; 725d131ad68SLuka Perkov img = NULL; 726d131ad68SLuka Perkov term = 0; 727d131ad68SLuka Perkov patch = 0; 728d131ad68SLuka Perkov size = 0; 729d131ad68SLuka Perkov speed = B115200; 730d131ad68SLuka Perkov 731d131ad68SLuka Perkov kwboot_verbose = isatty(STDOUT_FILENO); 732d131ad68SLuka Perkov 733d131ad68SLuka Perkov do { 734*7497a6a1SKevin Smith int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:"); 735d131ad68SLuka Perkov if (c < 0) 736d131ad68SLuka Perkov break; 737d131ad68SLuka Perkov 738d131ad68SLuka Perkov switch (c) { 739d131ad68SLuka Perkov case 'b': 740d131ad68SLuka Perkov bootmsg = kwboot_msg_boot; 741d131ad68SLuka Perkov imgpath = optarg; 742d131ad68SLuka Perkov break; 743d131ad68SLuka Perkov 74484899e2dSStefan Roese case 'D': 74584899e2dSStefan Roese bootmsg = NULL; 74684899e2dSStefan Roese imgpath = optarg; 74784899e2dSStefan Roese break; 74884899e2dSStefan Roese 74984899e2dSStefan Roese case 'd': 75084899e2dSStefan Roese debugmsg = kwboot_msg_debug; 75184899e2dSStefan Roese break; 75284899e2dSStefan Roese 753d131ad68SLuka Perkov case 'p': 754d131ad68SLuka Perkov patch = 1; 755d131ad68SLuka Perkov break; 756d131ad68SLuka Perkov 757d131ad68SLuka Perkov case 't': 758d131ad68SLuka Perkov term = 1; 759d131ad68SLuka Perkov break; 760d131ad68SLuka Perkov 76184899e2dSStefan Roese case 'a': 76284899e2dSStefan Roese msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; 76384899e2dSStefan Roese msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; 76484899e2dSStefan Roese break; 76584899e2dSStefan Roese 7661c0df9efSStefan Roese case 'q': 7671c0df9efSStefan Roese msg_req_delay = atoi(optarg); 7681c0df9efSStefan Roese break; 7691c0df9efSStefan Roese 7701c0df9efSStefan Roese case 's': 7711c0df9efSStefan Roese msg_rsp_timeo = atoi(optarg); 7721c0df9efSStefan Roese break; 7731c0df9efSStefan Roese 774*7497a6a1SKevin Smith case 'o': 775*7497a6a1SKevin Smith blk_rsp_timeo = atoi(optarg); 776*7497a6a1SKevin Smith break; 777*7497a6a1SKevin Smith 778d131ad68SLuka Perkov case 'B': 779d131ad68SLuka Perkov speed = kwboot_tty_speed(atoi(optarg)); 780d131ad68SLuka Perkov if (speed == -1) 781d131ad68SLuka Perkov goto usage; 782d131ad68SLuka Perkov break; 783d131ad68SLuka Perkov 784d131ad68SLuka Perkov case 'h': 785d131ad68SLuka Perkov rv = 0; 786d131ad68SLuka Perkov default: 787d131ad68SLuka Perkov goto usage; 788d131ad68SLuka Perkov } 789d131ad68SLuka Perkov } while (1); 790d131ad68SLuka Perkov 79184899e2dSStefan Roese if (!bootmsg && !term && !debugmsg) 792d131ad68SLuka Perkov goto usage; 793d131ad68SLuka Perkov 794d131ad68SLuka Perkov if (patch && !imgpath) 795d131ad68SLuka Perkov goto usage; 796d131ad68SLuka Perkov 797d131ad68SLuka Perkov if (argc - optind < 1) 798d131ad68SLuka Perkov goto usage; 799d131ad68SLuka Perkov 800d131ad68SLuka Perkov ttypath = argv[optind++]; 801d131ad68SLuka Perkov 802d131ad68SLuka Perkov tty = kwboot_open_tty(ttypath, speed); 803d131ad68SLuka Perkov if (tty < 0) { 804d131ad68SLuka Perkov perror(ttypath); 805d131ad68SLuka Perkov goto out; 806d131ad68SLuka Perkov } 807d131ad68SLuka Perkov 808d131ad68SLuka Perkov if (imgpath) { 809d131ad68SLuka Perkov prot = PROT_READ | (patch ? PROT_WRITE : 0); 810d131ad68SLuka Perkov 811d131ad68SLuka Perkov img = kwboot_mmap_image(imgpath, &size, prot); 812d131ad68SLuka Perkov if (!img) { 813d131ad68SLuka Perkov perror(imgpath); 814d131ad68SLuka Perkov goto out; 815d131ad68SLuka Perkov } 816d131ad68SLuka Perkov } 817d131ad68SLuka Perkov 818d131ad68SLuka Perkov if (patch) { 819d131ad68SLuka Perkov rc = kwboot_img_patch_hdr(img, size); 820d131ad68SLuka Perkov if (rc) { 821d131ad68SLuka Perkov fprintf(stderr, "%s: Invalid image.\n", imgpath); 822d131ad68SLuka Perkov goto out; 823d131ad68SLuka Perkov } 824d131ad68SLuka Perkov } 825d131ad68SLuka Perkov 82684899e2dSStefan Roese if (debugmsg) { 82784899e2dSStefan Roese rc = kwboot_debugmsg(tty, debugmsg); 82884899e2dSStefan Roese if (rc) { 82984899e2dSStefan Roese perror("debugmsg"); 83084899e2dSStefan Roese goto out; 83184899e2dSStefan Roese } 83284899e2dSStefan Roese } else { 833d131ad68SLuka Perkov rc = kwboot_bootmsg(tty, bootmsg); 834d131ad68SLuka Perkov if (rc) { 835d131ad68SLuka Perkov perror("bootmsg"); 836d131ad68SLuka Perkov goto out; 837d131ad68SLuka Perkov } 838d131ad68SLuka Perkov } 839d131ad68SLuka Perkov 840d131ad68SLuka Perkov if (img) { 841d131ad68SLuka Perkov rc = kwboot_xmodem(tty, img, size); 842d131ad68SLuka Perkov if (rc) { 843d131ad68SLuka Perkov perror("xmodem"); 844d131ad68SLuka Perkov goto out; 845d131ad68SLuka Perkov } 846d131ad68SLuka Perkov } 847d131ad68SLuka Perkov 848d131ad68SLuka Perkov if (term) { 849d131ad68SLuka Perkov rc = kwboot_terminal(tty); 850d131ad68SLuka Perkov if (rc && !(errno == EINTR)) { 851d131ad68SLuka Perkov perror("terminal"); 852d131ad68SLuka Perkov goto out; 853d131ad68SLuka Perkov } 854d131ad68SLuka Perkov } 855d131ad68SLuka Perkov 856d131ad68SLuka Perkov rv = 0; 857d131ad68SLuka Perkov out: 858d131ad68SLuka Perkov if (tty >= 0) 859d131ad68SLuka Perkov close(tty); 860d131ad68SLuka Perkov 861d131ad68SLuka Perkov if (img) 862d131ad68SLuka Perkov munmap(img, size); 863d131ad68SLuka Perkov 864d131ad68SLuka Perkov return rv; 865d131ad68SLuka Perkov 866d131ad68SLuka Perkov usage: 867d131ad68SLuka Perkov kwboot_usage(rv ? stderr : stdout, basename(argv[0])); 868d131ad68SLuka Perkov goto out; 869d131ad68SLuka Perkov } 870