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; 7984899e2dSStefan Roese 80d131ad68SLuka Perkov static void 81d131ad68SLuka Perkov kwboot_printv(const char *fmt, ...) 82d131ad68SLuka Perkov { 83d131ad68SLuka Perkov va_list ap; 84d131ad68SLuka Perkov 85d131ad68SLuka Perkov if (kwboot_verbose) { 86d131ad68SLuka Perkov va_start(ap, fmt); 87d131ad68SLuka Perkov vprintf(fmt, ap); 88d131ad68SLuka Perkov va_end(ap); 89d131ad68SLuka Perkov fflush(stdout); 90d131ad68SLuka Perkov } 91d131ad68SLuka Perkov } 92d131ad68SLuka Perkov 93d131ad68SLuka Perkov static void 94d131ad68SLuka Perkov __spinner(void) 95d131ad68SLuka Perkov { 96d131ad68SLuka Perkov const char seq[] = { '-', '\\', '|', '/' }; 97d131ad68SLuka Perkov const int div = 8; 98d131ad68SLuka Perkov static int state, bs; 99d131ad68SLuka Perkov 100d131ad68SLuka Perkov if (state % div == 0) { 101d131ad68SLuka Perkov fputc(bs, stdout); 102d131ad68SLuka Perkov fputc(seq[state / div % sizeof(seq)], stdout); 103d131ad68SLuka Perkov fflush(stdout); 104d131ad68SLuka Perkov } 105d131ad68SLuka Perkov 106d131ad68SLuka Perkov bs = '\b'; 107d131ad68SLuka Perkov state++; 108d131ad68SLuka Perkov } 109d131ad68SLuka Perkov 110d131ad68SLuka Perkov static void 111d131ad68SLuka Perkov kwboot_spinner(void) 112d131ad68SLuka Perkov { 113d131ad68SLuka Perkov if (kwboot_verbose) 114d131ad68SLuka Perkov __spinner(); 115d131ad68SLuka Perkov } 116d131ad68SLuka Perkov 117d131ad68SLuka Perkov static void 118d131ad68SLuka Perkov __progress(int pct, char c) 119d131ad68SLuka Perkov { 120d131ad68SLuka Perkov const int width = 70; 121d131ad68SLuka Perkov static const char *nl = ""; 122d131ad68SLuka Perkov static int pos; 123d131ad68SLuka Perkov 124d131ad68SLuka Perkov if (pos % width == 0) 125d131ad68SLuka Perkov printf("%s%3d %% [", nl, pct); 126d131ad68SLuka Perkov 127d131ad68SLuka Perkov fputc(c, stdout); 128d131ad68SLuka Perkov 129d131ad68SLuka Perkov nl = "]\n"; 130d131ad68SLuka Perkov pos++; 131d131ad68SLuka Perkov 132d131ad68SLuka Perkov if (pct == 100) { 133d131ad68SLuka Perkov while (pos++ < width) 134d131ad68SLuka Perkov fputc(' ', stdout); 135d131ad68SLuka Perkov fputs(nl, stdout); 136d131ad68SLuka Perkov } 137d131ad68SLuka Perkov 138d131ad68SLuka Perkov fflush(stdout); 139d131ad68SLuka Perkov 140d131ad68SLuka Perkov } 141d131ad68SLuka Perkov 142d131ad68SLuka Perkov static void 143d131ad68SLuka Perkov kwboot_progress(int _pct, char c) 144d131ad68SLuka Perkov { 145d131ad68SLuka Perkov static int pct; 146d131ad68SLuka Perkov 147d131ad68SLuka Perkov if (_pct != -1) 148d131ad68SLuka Perkov pct = _pct; 149d131ad68SLuka Perkov 150d131ad68SLuka Perkov if (kwboot_verbose) 151d131ad68SLuka Perkov __progress(pct, c); 152d131ad68SLuka Perkov } 153d131ad68SLuka Perkov 154d131ad68SLuka Perkov static int 155d131ad68SLuka Perkov kwboot_tty_recv(int fd, void *buf, size_t len, int timeo) 156d131ad68SLuka Perkov { 157d131ad68SLuka Perkov int rc, nfds; 158d131ad68SLuka Perkov fd_set rfds; 159d131ad68SLuka Perkov struct timeval tv; 160d131ad68SLuka Perkov ssize_t n; 161d131ad68SLuka Perkov 162d131ad68SLuka Perkov rc = -1; 163d131ad68SLuka Perkov 164d131ad68SLuka Perkov FD_ZERO(&rfds); 165d131ad68SLuka Perkov FD_SET(fd, &rfds); 166d131ad68SLuka Perkov 167d131ad68SLuka Perkov tv.tv_sec = 0; 168d131ad68SLuka Perkov tv.tv_usec = timeo * 1000; 169d131ad68SLuka Perkov if (tv.tv_usec > 1000000) { 170d131ad68SLuka Perkov tv.tv_sec += tv.tv_usec / 1000000; 171d131ad68SLuka Perkov tv.tv_usec %= 1000000; 172d131ad68SLuka Perkov } 173d131ad68SLuka Perkov 174d131ad68SLuka Perkov do { 175d131ad68SLuka Perkov nfds = select(fd + 1, &rfds, NULL, NULL, &tv); 176d131ad68SLuka Perkov if (nfds < 0) 177d131ad68SLuka Perkov goto out; 178d131ad68SLuka Perkov if (!nfds) { 179d131ad68SLuka Perkov errno = ETIMEDOUT; 180d131ad68SLuka Perkov goto out; 181d131ad68SLuka Perkov } 182d131ad68SLuka Perkov 183d131ad68SLuka Perkov n = read(fd, buf, len); 184d131ad68SLuka Perkov if (n < 0) 185d131ad68SLuka Perkov goto out; 186d131ad68SLuka Perkov 187d131ad68SLuka Perkov buf = (char *)buf + n; 188d131ad68SLuka Perkov len -= n; 189d131ad68SLuka Perkov } while (len > 0); 190d131ad68SLuka Perkov 191d131ad68SLuka Perkov rc = 0; 192d131ad68SLuka Perkov out: 193d131ad68SLuka Perkov return rc; 194d131ad68SLuka Perkov } 195d131ad68SLuka Perkov 196d131ad68SLuka Perkov static int 197d131ad68SLuka Perkov kwboot_tty_send(int fd, const void *buf, size_t len) 198d131ad68SLuka Perkov { 199d131ad68SLuka Perkov int rc; 200d131ad68SLuka Perkov ssize_t n; 201d131ad68SLuka Perkov 20284899e2dSStefan Roese if (!buf) 20384899e2dSStefan Roese return 0; 20484899e2dSStefan Roese 205d131ad68SLuka Perkov rc = -1; 206d131ad68SLuka Perkov 207d131ad68SLuka Perkov do { 208d131ad68SLuka Perkov n = write(fd, buf, len); 209d131ad68SLuka Perkov if (n < 0) 210d131ad68SLuka Perkov goto out; 211d131ad68SLuka Perkov 212d131ad68SLuka Perkov buf = (char *)buf + n; 213d131ad68SLuka Perkov len -= n; 214d131ad68SLuka Perkov } while (len > 0); 215d131ad68SLuka Perkov 216d131ad68SLuka Perkov rc = tcdrain(fd); 217d131ad68SLuka Perkov out: 218d131ad68SLuka Perkov return rc; 219d131ad68SLuka Perkov } 220d131ad68SLuka Perkov 221d131ad68SLuka Perkov static int 222d131ad68SLuka Perkov kwboot_tty_send_char(int fd, unsigned char c) 223d131ad68SLuka Perkov { 224d131ad68SLuka Perkov return kwboot_tty_send(fd, &c, 1); 225d131ad68SLuka Perkov } 226d131ad68SLuka Perkov 227d131ad68SLuka Perkov static speed_t 228d131ad68SLuka Perkov kwboot_tty_speed(int baudrate) 229d131ad68SLuka Perkov { 230d131ad68SLuka Perkov switch (baudrate) { 231d131ad68SLuka Perkov case 115200: 232d131ad68SLuka Perkov return B115200; 233d131ad68SLuka Perkov case 57600: 234d131ad68SLuka Perkov return B57600; 235d131ad68SLuka Perkov case 38400: 236d131ad68SLuka Perkov return B38400; 237d131ad68SLuka Perkov case 19200: 238d131ad68SLuka Perkov return B19200; 239d131ad68SLuka Perkov case 9600: 240d131ad68SLuka Perkov return B9600; 241d131ad68SLuka Perkov } 242d131ad68SLuka Perkov 243d131ad68SLuka Perkov return -1; 244d131ad68SLuka Perkov } 245d131ad68SLuka Perkov 246d131ad68SLuka Perkov static int 247d131ad68SLuka Perkov kwboot_open_tty(const char *path, speed_t speed) 248d131ad68SLuka Perkov { 249d131ad68SLuka Perkov int rc, fd; 250d131ad68SLuka Perkov struct termios tio; 251d131ad68SLuka Perkov 252d131ad68SLuka Perkov rc = -1; 253d131ad68SLuka Perkov 254d131ad68SLuka Perkov fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY); 255d131ad68SLuka Perkov if (fd < 0) 256d131ad68SLuka Perkov goto out; 257d131ad68SLuka Perkov 258d131ad68SLuka Perkov memset(&tio, 0, sizeof(tio)); 259d131ad68SLuka Perkov 260d131ad68SLuka Perkov tio.c_iflag = 0; 261d131ad68SLuka Perkov tio.c_cflag = CREAD|CLOCAL|CS8; 262d131ad68SLuka Perkov 263d131ad68SLuka Perkov tio.c_cc[VMIN] = 1; 264d131ad68SLuka Perkov tio.c_cc[VTIME] = 10; 265d131ad68SLuka Perkov 266d131ad68SLuka Perkov cfsetospeed(&tio, speed); 267d131ad68SLuka Perkov cfsetispeed(&tio, speed); 268d131ad68SLuka Perkov 269d131ad68SLuka Perkov rc = tcsetattr(fd, TCSANOW, &tio); 270d131ad68SLuka Perkov if (rc) 271d131ad68SLuka Perkov goto out; 272d131ad68SLuka Perkov 273d131ad68SLuka Perkov rc = fd; 274d131ad68SLuka Perkov out: 275d131ad68SLuka Perkov if (rc < 0) { 276d131ad68SLuka Perkov if (fd >= 0) 277d131ad68SLuka Perkov close(fd); 278d131ad68SLuka Perkov } 279d131ad68SLuka Perkov 280d131ad68SLuka Perkov return rc; 281d131ad68SLuka Perkov } 282d131ad68SLuka Perkov 283d131ad68SLuka Perkov static int 284d131ad68SLuka Perkov kwboot_bootmsg(int tty, void *msg) 285d131ad68SLuka Perkov { 286d131ad68SLuka Perkov int rc; 287d131ad68SLuka Perkov char c; 288d131ad68SLuka Perkov 28984899e2dSStefan Roese if (msg == NULL) 29084899e2dSStefan Roese kwboot_printv("Please reboot the target into UART boot mode..."); 29184899e2dSStefan Roese else 292d131ad68SLuka Perkov kwboot_printv("Sending boot message. Please reboot the target..."); 293d131ad68SLuka Perkov 294d131ad68SLuka Perkov do { 295d131ad68SLuka Perkov rc = tcflush(tty, TCIOFLUSH); 296d131ad68SLuka Perkov if (rc) 297d131ad68SLuka Perkov break; 298d131ad68SLuka Perkov 299d131ad68SLuka Perkov rc = kwboot_tty_send(tty, msg, 8); 300d131ad68SLuka Perkov if (rc) { 30184899e2dSStefan Roese usleep(msg_req_delay * 1000); 302d131ad68SLuka Perkov continue; 303d131ad68SLuka Perkov } 304d131ad68SLuka Perkov 30584899e2dSStefan Roese rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); 306d131ad68SLuka Perkov 307d131ad68SLuka Perkov kwboot_spinner(); 308d131ad68SLuka Perkov 309d131ad68SLuka Perkov } while (rc || c != NAK); 310d131ad68SLuka Perkov 311d131ad68SLuka Perkov kwboot_printv("\n"); 312d131ad68SLuka Perkov 313d131ad68SLuka Perkov return rc; 314d131ad68SLuka Perkov } 315d131ad68SLuka Perkov 316d131ad68SLuka Perkov static int 31784899e2dSStefan Roese kwboot_debugmsg(int tty, void *msg) 31884899e2dSStefan Roese { 31984899e2dSStefan Roese int rc; 32084899e2dSStefan Roese 32184899e2dSStefan Roese kwboot_printv("Sending debug message. Please reboot the target..."); 32284899e2dSStefan Roese 32384899e2dSStefan Roese do { 32484899e2dSStefan Roese char buf[16]; 32584899e2dSStefan Roese 32684899e2dSStefan Roese rc = tcflush(tty, TCIOFLUSH); 32784899e2dSStefan Roese if (rc) 32884899e2dSStefan Roese break; 32984899e2dSStefan Roese 33084899e2dSStefan Roese rc = kwboot_tty_send(tty, msg, 8); 33184899e2dSStefan Roese if (rc) { 33284899e2dSStefan Roese usleep(msg_req_delay * 1000); 33384899e2dSStefan Roese continue; 33484899e2dSStefan Roese } 33584899e2dSStefan Roese 33684899e2dSStefan Roese rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); 33784899e2dSStefan Roese 33884899e2dSStefan Roese kwboot_spinner(); 33984899e2dSStefan Roese 34084899e2dSStefan Roese } while (rc); 34184899e2dSStefan Roese 34284899e2dSStefan Roese kwboot_printv("\n"); 34384899e2dSStefan Roese 34484899e2dSStefan Roese return rc; 34584899e2dSStefan Roese } 34684899e2dSStefan Roese 34784899e2dSStefan Roese static int 348d131ad68SLuka Perkov kwboot_xm_makeblock(struct kwboot_block *block, const void *data, 349d131ad68SLuka Perkov size_t size, int pnum) 350d131ad68SLuka Perkov { 351d131ad68SLuka Perkov const size_t blksz = sizeof(block->data); 352d131ad68SLuka Perkov size_t n; 353d131ad68SLuka Perkov int i; 354d131ad68SLuka Perkov 35584899e2dSStefan Roese block->soh = SOH; 356d131ad68SLuka Perkov block->pnum = pnum; 357d131ad68SLuka Perkov block->_pnum = ~block->pnum; 358d131ad68SLuka Perkov 359d131ad68SLuka Perkov n = size < blksz ? size : blksz; 360d131ad68SLuka Perkov memcpy(&block->data[0], data, n); 361d131ad68SLuka Perkov memset(&block->data[n], 0, blksz - n); 362d131ad68SLuka Perkov 363d131ad68SLuka Perkov block->csum = 0; 364d131ad68SLuka Perkov for (i = 0; i < n; i++) 365d131ad68SLuka Perkov block->csum += block->data[i]; 366d131ad68SLuka Perkov 367d131ad68SLuka Perkov return n; 368d131ad68SLuka Perkov } 369d131ad68SLuka Perkov 370d131ad68SLuka Perkov static int 371d131ad68SLuka Perkov kwboot_xm_sendblock(int fd, struct kwboot_block *block) 372d131ad68SLuka Perkov { 373d131ad68SLuka Perkov int rc, retries; 374d131ad68SLuka Perkov char c; 375d131ad68SLuka Perkov 376d131ad68SLuka Perkov retries = 16; 377d131ad68SLuka Perkov do { 378d131ad68SLuka Perkov rc = kwboot_tty_send(fd, block, sizeof(*block)); 379d131ad68SLuka Perkov if (rc) 380d131ad68SLuka Perkov break; 381d131ad68SLuka Perkov 38284899e2dSStefan Roese do { 383d131ad68SLuka Perkov rc = kwboot_tty_recv(fd, &c, 1, KWBOOT_BLK_RSP_TIMEO); 384d131ad68SLuka Perkov if (rc) 385d131ad68SLuka Perkov break; 386d131ad68SLuka Perkov 38784899e2dSStefan Roese if (c != ACK && c != NAK && c != CAN) 38884899e2dSStefan Roese printf("%c", c); 38984899e2dSStefan Roese 39084899e2dSStefan Roese } while (c != ACK && c != NAK && c != CAN); 39184899e2dSStefan Roese 392d131ad68SLuka Perkov if (c != ACK) 393d131ad68SLuka Perkov kwboot_progress(-1, '+'); 394d131ad68SLuka Perkov 395d131ad68SLuka Perkov } while (c == NAK && retries-- > 0); 396d131ad68SLuka Perkov 397d131ad68SLuka Perkov rc = -1; 398d131ad68SLuka Perkov 399d131ad68SLuka Perkov switch (c) { 400d131ad68SLuka Perkov case ACK: 401d131ad68SLuka Perkov rc = 0; 402d131ad68SLuka Perkov break; 403d131ad68SLuka Perkov case NAK: 404d131ad68SLuka Perkov errno = EBADMSG; 405d131ad68SLuka Perkov break; 406d131ad68SLuka Perkov case CAN: 407d131ad68SLuka Perkov errno = ECANCELED; 408d131ad68SLuka Perkov break; 409d131ad68SLuka Perkov default: 410d131ad68SLuka Perkov errno = EPROTO; 411d131ad68SLuka Perkov break; 412d131ad68SLuka Perkov } 413d131ad68SLuka Perkov 414d131ad68SLuka Perkov return rc; 415d131ad68SLuka Perkov } 416d131ad68SLuka Perkov 417d131ad68SLuka Perkov static int 418d131ad68SLuka Perkov kwboot_xmodem(int tty, const void *_data, size_t size) 419d131ad68SLuka Perkov { 420d131ad68SLuka Perkov const uint8_t *data = _data; 421d131ad68SLuka Perkov int rc, pnum, N, err; 422d131ad68SLuka Perkov 423d131ad68SLuka Perkov pnum = 1; 424d131ad68SLuka Perkov N = 0; 425d131ad68SLuka Perkov 426d131ad68SLuka Perkov kwboot_printv("Sending boot image...\n"); 427d131ad68SLuka Perkov 428d131ad68SLuka Perkov do { 429d131ad68SLuka Perkov struct kwboot_block block; 430d131ad68SLuka Perkov int n; 431d131ad68SLuka Perkov 432d131ad68SLuka Perkov n = kwboot_xm_makeblock(&block, 433d131ad68SLuka Perkov data + N, size - N, 434d131ad68SLuka Perkov pnum++); 435d131ad68SLuka Perkov if (n < 0) 436d131ad68SLuka Perkov goto can; 437d131ad68SLuka Perkov 438d131ad68SLuka Perkov if (!n) 439d131ad68SLuka Perkov break; 440d131ad68SLuka Perkov 441d131ad68SLuka Perkov rc = kwboot_xm_sendblock(tty, &block); 442d131ad68SLuka Perkov if (rc) 443d131ad68SLuka Perkov goto out; 444d131ad68SLuka Perkov 445d131ad68SLuka Perkov N += n; 446d131ad68SLuka Perkov kwboot_progress(N * 100 / size, '.'); 447d131ad68SLuka Perkov } while (1); 448d131ad68SLuka Perkov 449d131ad68SLuka Perkov rc = kwboot_tty_send_char(tty, EOT); 450d131ad68SLuka Perkov 451d131ad68SLuka Perkov out: 452d131ad68SLuka Perkov return rc; 453d131ad68SLuka Perkov 454d131ad68SLuka Perkov can: 455d131ad68SLuka Perkov err = errno; 456d131ad68SLuka Perkov kwboot_tty_send_char(tty, CAN); 457d131ad68SLuka Perkov errno = err; 458d131ad68SLuka Perkov goto out; 459d131ad68SLuka Perkov } 460d131ad68SLuka Perkov 461d131ad68SLuka Perkov static int 462d131ad68SLuka Perkov kwboot_term_pipe(int in, int out, char *quit, int *s) 463d131ad68SLuka Perkov { 464d131ad68SLuka Perkov ssize_t nin, nout; 465d131ad68SLuka Perkov char _buf[128], *buf = _buf; 466d131ad68SLuka Perkov 467d131ad68SLuka Perkov nin = read(in, buf, sizeof(buf)); 468d131ad68SLuka Perkov if (nin < 0) 469d131ad68SLuka Perkov return -1; 470d131ad68SLuka Perkov 471d131ad68SLuka Perkov if (quit) { 472d131ad68SLuka Perkov int i; 473d131ad68SLuka Perkov 474d131ad68SLuka Perkov for (i = 0; i < nin; i++) { 475d131ad68SLuka Perkov if (*buf == quit[*s]) { 476d131ad68SLuka Perkov (*s)++; 477d131ad68SLuka Perkov if (!quit[*s]) 478d131ad68SLuka Perkov return 0; 479d131ad68SLuka Perkov buf++; 480d131ad68SLuka Perkov nin--; 481d131ad68SLuka Perkov } else 482d131ad68SLuka Perkov while (*s > 0) { 483d131ad68SLuka Perkov nout = write(out, quit, *s); 484d131ad68SLuka Perkov if (nout <= 0) 485d131ad68SLuka Perkov return -1; 486d131ad68SLuka Perkov (*s) -= nout; 487d131ad68SLuka Perkov } 488d131ad68SLuka Perkov } 489d131ad68SLuka Perkov } 490d131ad68SLuka Perkov 491d131ad68SLuka Perkov while (nin > 0) { 492d131ad68SLuka Perkov nout = write(out, buf, nin); 493d131ad68SLuka Perkov if (nout <= 0) 494d131ad68SLuka Perkov return -1; 495d131ad68SLuka Perkov nin -= nout; 496d131ad68SLuka Perkov } 497d131ad68SLuka Perkov 498d131ad68SLuka Perkov return 0; 499d131ad68SLuka Perkov } 500d131ad68SLuka Perkov 501d131ad68SLuka Perkov static int 502d131ad68SLuka Perkov kwboot_terminal(int tty) 503d131ad68SLuka Perkov { 504d131ad68SLuka Perkov int rc, in, s; 505d131ad68SLuka Perkov char *quit = "\34c"; 506d131ad68SLuka Perkov struct termios otio, tio; 507d131ad68SLuka Perkov 508d131ad68SLuka Perkov rc = -1; 509d131ad68SLuka Perkov 510d131ad68SLuka Perkov in = STDIN_FILENO; 511d131ad68SLuka Perkov if (isatty(in)) { 512d131ad68SLuka Perkov rc = tcgetattr(in, &otio); 513d131ad68SLuka Perkov if (!rc) { 514d131ad68SLuka Perkov tio = otio; 515d131ad68SLuka Perkov cfmakeraw(&tio); 516d131ad68SLuka Perkov rc = tcsetattr(in, TCSANOW, &tio); 517d131ad68SLuka Perkov } 518d131ad68SLuka Perkov if (rc) { 519d131ad68SLuka Perkov perror("tcsetattr"); 520d131ad68SLuka Perkov goto out; 521d131ad68SLuka Perkov } 522d131ad68SLuka Perkov 523d131ad68SLuka Perkov kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", 524d131ad68SLuka Perkov quit[0]|0100, quit[1]); 525d131ad68SLuka Perkov } else 526d131ad68SLuka Perkov in = -1; 527d131ad68SLuka Perkov 528d131ad68SLuka Perkov rc = 0; 529d131ad68SLuka Perkov s = 0; 530d131ad68SLuka Perkov 531d131ad68SLuka Perkov do { 532d131ad68SLuka Perkov fd_set rfds; 533d131ad68SLuka Perkov int nfds = 0; 534d131ad68SLuka Perkov 535d131ad68SLuka Perkov FD_SET(tty, &rfds); 536d131ad68SLuka Perkov nfds = nfds < tty ? tty : nfds; 537d131ad68SLuka Perkov 538d131ad68SLuka Perkov if (in >= 0) { 539d131ad68SLuka Perkov FD_SET(in, &rfds); 540d131ad68SLuka Perkov nfds = nfds < in ? in : nfds; 541d131ad68SLuka Perkov } 542d131ad68SLuka Perkov 543d131ad68SLuka Perkov nfds = select(nfds + 1, &rfds, NULL, NULL, NULL); 544d131ad68SLuka Perkov if (nfds < 0) 545d131ad68SLuka Perkov break; 546d131ad68SLuka Perkov 547d131ad68SLuka Perkov if (FD_ISSET(tty, &rfds)) { 548d131ad68SLuka Perkov rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); 549d131ad68SLuka Perkov if (rc) 550d131ad68SLuka Perkov break; 551d131ad68SLuka Perkov } 552d131ad68SLuka Perkov 553d131ad68SLuka Perkov if (FD_ISSET(in, &rfds)) { 554d131ad68SLuka Perkov rc = kwboot_term_pipe(in, tty, quit, &s); 555d131ad68SLuka Perkov if (rc) 556d131ad68SLuka Perkov break; 557d131ad68SLuka Perkov } 558d131ad68SLuka Perkov } while (quit[s] != 0); 559d131ad68SLuka Perkov 560d131ad68SLuka Perkov tcsetattr(in, TCSANOW, &otio); 561d131ad68SLuka Perkov out: 562d131ad68SLuka Perkov return rc; 563d131ad68SLuka Perkov } 564d131ad68SLuka Perkov 565d131ad68SLuka Perkov static void * 566d131ad68SLuka Perkov kwboot_mmap_image(const char *path, size_t *size, int prot) 567d131ad68SLuka Perkov { 568d131ad68SLuka Perkov int rc, fd, flags; 569d131ad68SLuka Perkov struct stat st; 570d131ad68SLuka Perkov void *img; 571d131ad68SLuka Perkov 572d131ad68SLuka Perkov rc = -1; 573d131ad68SLuka Perkov img = NULL; 574d131ad68SLuka Perkov 575d131ad68SLuka Perkov fd = open(path, O_RDONLY); 576d131ad68SLuka Perkov if (fd < 0) 577d131ad68SLuka Perkov goto out; 578d131ad68SLuka Perkov 579d131ad68SLuka Perkov rc = fstat(fd, &st); 580d131ad68SLuka Perkov if (rc) 581d131ad68SLuka Perkov goto out; 582d131ad68SLuka Perkov 583d131ad68SLuka Perkov flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED; 584d131ad68SLuka Perkov 585d131ad68SLuka Perkov img = mmap(NULL, st.st_size, prot, flags, fd, 0); 586d131ad68SLuka Perkov if (img == MAP_FAILED) { 587d131ad68SLuka Perkov img = NULL; 588d131ad68SLuka Perkov goto out; 589d131ad68SLuka Perkov } 590d131ad68SLuka Perkov 591d131ad68SLuka Perkov rc = 0; 592d131ad68SLuka Perkov *size = st.st_size; 593d131ad68SLuka Perkov out: 594d131ad68SLuka Perkov if (rc && img) { 595d131ad68SLuka Perkov munmap(img, st.st_size); 596d131ad68SLuka Perkov img = NULL; 597d131ad68SLuka Perkov } 598d131ad68SLuka Perkov if (fd >= 0) 599d131ad68SLuka Perkov close(fd); 600d131ad68SLuka Perkov 601d131ad68SLuka Perkov return img; 602d131ad68SLuka Perkov } 603d131ad68SLuka Perkov 604d131ad68SLuka Perkov static uint8_t 605d131ad68SLuka Perkov kwboot_img_csum8(void *_data, size_t size) 606d131ad68SLuka Perkov { 607d131ad68SLuka Perkov uint8_t *data = _data, csum; 608d131ad68SLuka Perkov 609d131ad68SLuka Perkov for (csum = 0; size-- > 0; data++) 610d131ad68SLuka Perkov csum += *data; 611d131ad68SLuka Perkov 612d131ad68SLuka Perkov return csum; 613d131ad68SLuka Perkov } 614d131ad68SLuka Perkov 615d131ad68SLuka Perkov static int 616d131ad68SLuka Perkov kwboot_img_patch_hdr(void *img, size_t size) 617d131ad68SLuka Perkov { 618d131ad68SLuka Perkov int rc; 619e29f1db3SStefan Roese struct main_hdr_v1 *hdr; 620d131ad68SLuka Perkov uint8_t csum; 621e29f1db3SStefan Roese size_t hdrsz = sizeof(*hdr); 622e29f1db3SStefan Roese int image_ver; 623d131ad68SLuka Perkov 624d131ad68SLuka Perkov rc = -1; 625d131ad68SLuka Perkov hdr = img; 626d131ad68SLuka Perkov 627d131ad68SLuka Perkov if (size < hdrsz) { 628d131ad68SLuka Perkov errno = EINVAL; 629d131ad68SLuka Perkov goto out; 630d131ad68SLuka Perkov } 631d131ad68SLuka Perkov 632e29f1db3SStefan Roese image_ver = image_version(img); 633e29f1db3SStefan Roese if (image_ver < 0) { 634e29f1db3SStefan Roese fprintf(stderr, "Invalid image header version\n"); 635e29f1db3SStefan Roese errno = EINVAL; 636e29f1db3SStefan Roese goto out; 637e29f1db3SStefan Roese } 638e29f1db3SStefan Roese 639e29f1db3SStefan Roese if (image_ver == 0) 640e29f1db3SStefan Roese hdrsz = sizeof(*hdr); 641e29f1db3SStefan Roese else 642e29f1db3SStefan Roese hdrsz = KWBHEADER_V1_SIZE(hdr); 643e29f1db3SStefan Roese 644e29f1db3SStefan Roese csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checksum; 645e29f1db3SStefan Roese if (csum != hdr->checksum) { 646d131ad68SLuka Perkov errno = EINVAL; 647d131ad68SLuka Perkov goto out; 648d131ad68SLuka Perkov } 649d131ad68SLuka Perkov 650d131ad68SLuka Perkov if (hdr->blockid == IBR_HDR_UART_ID) { 651d131ad68SLuka Perkov rc = 0; 652d131ad68SLuka Perkov goto out; 653d131ad68SLuka Perkov } 654d131ad68SLuka Perkov 655d131ad68SLuka Perkov hdr->blockid = IBR_HDR_UART_ID; 656d131ad68SLuka Perkov 657f4db6c97SStefan Roese /* 658f4db6c97SStefan Roese * Subtract mkimage header size from destination address 659f4db6c97SStefan Roese * as this header is not expected by the Marvell BootROM. 660f4db6c97SStefan Roese * This way, the execution address is identical to the 661f4db6c97SStefan Roese * one the image is compiled for (TEXT_BASE). 662f4db6c97SStefan Roese */ 663f4db6c97SStefan Roese hdr->destaddr = hdr->destaddr - sizeof(struct image_header); 664f4db6c97SStefan Roese 665e29f1db3SStefan Roese if (image_ver == 0) { 666e29f1db3SStefan Roese struct main_hdr_v0 *hdr_v0 = img; 667d131ad68SLuka Perkov 668e29f1db3SStefan Roese hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED; 669e29f1db3SStefan Roese hdr_v0->nandpagesize = 0; 670e29f1db3SStefan Roese 671e29f1db3SStefan Roese hdr_v0->srcaddr = hdr_v0->ext 672d131ad68SLuka Perkov ? sizeof(struct kwb_header) 673e29f1db3SStefan Roese : sizeof(*hdr_v0); 674e29f1db3SStefan Roese } 675d131ad68SLuka Perkov 676e29f1db3SStefan Roese hdr->checksum = kwboot_img_csum8(hdr, hdrsz) - csum; 677d131ad68SLuka Perkov 678d131ad68SLuka Perkov rc = 0; 679d131ad68SLuka Perkov out: 680d131ad68SLuka Perkov return rc; 681d131ad68SLuka Perkov } 682d131ad68SLuka Perkov 683d131ad68SLuka Perkov static void 684d131ad68SLuka Perkov kwboot_usage(FILE *stream, char *progname) 685d131ad68SLuka Perkov { 686d131ad68SLuka Perkov fprintf(stream, 687*8669dacfSKevin Smith "Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n", 68884899e2dSStefan Roese progname); 689d131ad68SLuka Perkov fprintf(stream, "\n"); 69084899e2dSStefan Roese fprintf(stream, 69184899e2dSStefan Roese " -b <image>: boot <image> with preamble (Kirkwood, Armada 370/XP)\n"); 692d131ad68SLuka Perkov fprintf(stream, " -p: patch <image> to type 0x69 (uart boot)\n"); 69384899e2dSStefan Roese fprintf(stream, 69484899e2dSStefan Roese " -D <image>: boot <image> without preamble (Dove)\n"); 69584899e2dSStefan Roese fprintf(stream, " -d: enter debug mode\n"); 69684899e2dSStefan Roese fprintf(stream, " -a: use timings for Armada XP\n"); 6971c0df9efSStefan Roese fprintf(stream, " -q <req-delay>: use specific request-delay\n"); 6981c0df9efSStefan Roese fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n"); 699d131ad68SLuka Perkov fprintf(stream, "\n"); 700d131ad68SLuka Perkov fprintf(stream, " -t: mini terminal\n"); 701d131ad68SLuka Perkov fprintf(stream, "\n"); 702d131ad68SLuka Perkov fprintf(stream, " -B <baud>: set baud rate\n"); 703d131ad68SLuka Perkov fprintf(stream, "\n"); 704d131ad68SLuka Perkov } 705d131ad68SLuka Perkov 706d131ad68SLuka Perkov int 707d131ad68SLuka Perkov main(int argc, char **argv) 708d131ad68SLuka Perkov { 709d131ad68SLuka Perkov const char *ttypath, *imgpath; 710d131ad68SLuka Perkov int rv, rc, tty, term, prot, patch; 711d131ad68SLuka Perkov void *bootmsg; 71284899e2dSStefan Roese void *debugmsg; 713d131ad68SLuka Perkov void *img; 714d131ad68SLuka Perkov size_t size; 715d131ad68SLuka Perkov speed_t speed; 716d131ad68SLuka Perkov 717d131ad68SLuka Perkov rv = 1; 718d131ad68SLuka Perkov tty = -1; 719d131ad68SLuka Perkov bootmsg = NULL; 72084899e2dSStefan Roese debugmsg = NULL; 721d131ad68SLuka Perkov imgpath = NULL; 722d131ad68SLuka Perkov img = NULL; 723d131ad68SLuka Perkov term = 0; 724d131ad68SLuka Perkov patch = 0; 725d131ad68SLuka Perkov size = 0; 726d131ad68SLuka Perkov speed = B115200; 727d131ad68SLuka Perkov 728d131ad68SLuka Perkov kwboot_verbose = isatty(STDOUT_FILENO); 729d131ad68SLuka Perkov 730d131ad68SLuka Perkov do { 7311c0df9efSStefan Roese int c = getopt(argc, argv, "hb:ptaB:dD:q:s:"); 732d131ad68SLuka Perkov if (c < 0) 733d131ad68SLuka Perkov break; 734d131ad68SLuka Perkov 735d131ad68SLuka Perkov switch (c) { 736d131ad68SLuka Perkov case 'b': 737d131ad68SLuka Perkov bootmsg = kwboot_msg_boot; 738d131ad68SLuka Perkov imgpath = optarg; 739d131ad68SLuka Perkov break; 740d131ad68SLuka Perkov 74184899e2dSStefan Roese case 'D': 74284899e2dSStefan Roese bootmsg = NULL; 74384899e2dSStefan Roese imgpath = optarg; 74484899e2dSStefan Roese break; 74584899e2dSStefan Roese 74684899e2dSStefan Roese case 'd': 74784899e2dSStefan Roese debugmsg = kwboot_msg_debug; 74884899e2dSStefan Roese break; 74984899e2dSStefan Roese 750d131ad68SLuka Perkov case 'p': 751d131ad68SLuka Perkov patch = 1; 752d131ad68SLuka Perkov break; 753d131ad68SLuka Perkov 754d131ad68SLuka Perkov case 't': 755d131ad68SLuka Perkov term = 1; 756d131ad68SLuka Perkov break; 757d131ad68SLuka Perkov 75884899e2dSStefan Roese case 'a': 75984899e2dSStefan Roese msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; 76084899e2dSStefan Roese msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; 76184899e2dSStefan Roese break; 76284899e2dSStefan Roese 7631c0df9efSStefan Roese case 'q': 7641c0df9efSStefan Roese msg_req_delay = atoi(optarg); 7651c0df9efSStefan Roese break; 7661c0df9efSStefan Roese 7671c0df9efSStefan Roese case 's': 7681c0df9efSStefan Roese msg_rsp_timeo = atoi(optarg); 7691c0df9efSStefan Roese break; 7701c0df9efSStefan Roese 771d131ad68SLuka Perkov case 'B': 772d131ad68SLuka Perkov speed = kwboot_tty_speed(atoi(optarg)); 773d131ad68SLuka Perkov if (speed == -1) 774d131ad68SLuka Perkov goto usage; 775d131ad68SLuka Perkov break; 776d131ad68SLuka Perkov 777d131ad68SLuka Perkov case 'h': 778d131ad68SLuka Perkov rv = 0; 779d131ad68SLuka Perkov default: 780d131ad68SLuka Perkov goto usage; 781d131ad68SLuka Perkov } 782d131ad68SLuka Perkov } while (1); 783d131ad68SLuka Perkov 78484899e2dSStefan Roese if (!bootmsg && !term && !debugmsg) 785d131ad68SLuka Perkov goto usage; 786d131ad68SLuka Perkov 787d131ad68SLuka Perkov if (patch && !imgpath) 788d131ad68SLuka Perkov goto usage; 789d131ad68SLuka Perkov 790d131ad68SLuka Perkov if (argc - optind < 1) 791d131ad68SLuka Perkov goto usage; 792d131ad68SLuka Perkov 793d131ad68SLuka Perkov ttypath = argv[optind++]; 794d131ad68SLuka Perkov 795d131ad68SLuka Perkov tty = kwboot_open_tty(ttypath, speed); 796d131ad68SLuka Perkov if (tty < 0) { 797d131ad68SLuka Perkov perror(ttypath); 798d131ad68SLuka Perkov goto out; 799d131ad68SLuka Perkov } 800d131ad68SLuka Perkov 801d131ad68SLuka Perkov if (imgpath) { 802d131ad68SLuka Perkov prot = PROT_READ | (patch ? PROT_WRITE : 0); 803d131ad68SLuka Perkov 804d131ad68SLuka Perkov img = kwboot_mmap_image(imgpath, &size, prot); 805d131ad68SLuka Perkov if (!img) { 806d131ad68SLuka Perkov perror(imgpath); 807d131ad68SLuka Perkov goto out; 808d131ad68SLuka Perkov } 809d131ad68SLuka Perkov } 810d131ad68SLuka Perkov 811d131ad68SLuka Perkov if (patch) { 812d131ad68SLuka Perkov rc = kwboot_img_patch_hdr(img, size); 813d131ad68SLuka Perkov if (rc) { 814d131ad68SLuka Perkov fprintf(stderr, "%s: Invalid image.\n", imgpath); 815d131ad68SLuka Perkov goto out; 816d131ad68SLuka Perkov } 817d131ad68SLuka Perkov } 818d131ad68SLuka Perkov 81984899e2dSStefan Roese if (debugmsg) { 82084899e2dSStefan Roese rc = kwboot_debugmsg(tty, debugmsg); 82184899e2dSStefan Roese if (rc) { 82284899e2dSStefan Roese perror("debugmsg"); 82384899e2dSStefan Roese goto out; 82484899e2dSStefan Roese } 82584899e2dSStefan Roese } else { 826d131ad68SLuka Perkov rc = kwboot_bootmsg(tty, bootmsg); 827d131ad68SLuka Perkov if (rc) { 828d131ad68SLuka Perkov perror("bootmsg"); 829d131ad68SLuka Perkov goto out; 830d131ad68SLuka Perkov } 831d131ad68SLuka Perkov } 832d131ad68SLuka Perkov 833d131ad68SLuka Perkov if (img) { 834d131ad68SLuka Perkov rc = kwboot_xmodem(tty, img, size); 835d131ad68SLuka Perkov if (rc) { 836d131ad68SLuka Perkov perror("xmodem"); 837d131ad68SLuka Perkov goto out; 838d131ad68SLuka Perkov } 839d131ad68SLuka Perkov } 840d131ad68SLuka Perkov 841d131ad68SLuka Perkov if (term) { 842d131ad68SLuka Perkov rc = kwboot_terminal(tty); 843d131ad68SLuka Perkov if (rc && !(errno == EINTR)) { 844d131ad68SLuka Perkov perror("terminal"); 845d131ad68SLuka Perkov goto out; 846d131ad68SLuka Perkov } 847d131ad68SLuka Perkov } 848d131ad68SLuka Perkov 849d131ad68SLuka Perkov rv = 0; 850d131ad68SLuka Perkov out: 851d131ad68SLuka Perkov if (tty >= 0) 852d131ad68SLuka Perkov close(tty); 853d131ad68SLuka Perkov 854d131ad68SLuka Perkov if (img) 855d131ad68SLuka Perkov munmap(img, size); 856d131ad68SLuka Perkov 857d131ad68SLuka Perkov return rv; 858d131ad68SLuka Perkov 859d131ad68SLuka Perkov usage: 860d131ad68SLuka Perkov kwboot_usage(rv ? stderr : stdout, basename(argv[0])); 861d131ad68SLuka Perkov goto out; 862d131ad68SLuka Perkov } 863