aboutsummaryrefslogtreecommitdiff
path: root/tools/kwboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/kwboot.c')
-rw-r--r--tools/kwboot.c154
1 files changed, 115 insertions, 39 deletions
diff --git a/tools/kwboot.c b/tools/kwboot.c
index c3d8ab6..2684f0e 100644
--- a/tools/kwboot.c
+++ b/tools/kwboot.c
@@ -63,7 +63,6 @@ static unsigned char kwboot_msg_debug[] = {
#define EOT 4 /* sender end of block transfer */
#define ACK 6 /* target block ack */
#define NAK 21 /* target block negative ack */
-#define CAN 24 /* target/sender transfer cancellation */
#define KWBOOT_XM_BLKSZ 128 /* xmodem block size */
@@ -75,7 +74,7 @@ struct kwboot_block {
uint8_t csum;
} __packed;
-#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
+#define KWBOOT_BLK_RSP_TIMEO 2000 /* ms */
#define KWBOOT_HDR_RSP_TIMEO 10000 /* ms */
/* ARM code to change baudrate */
@@ -293,13 +292,15 @@ static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
static ssize_t
kwboot_write(int fd, const char *buf, size_t len)
{
- size_t tot = 0;
+ ssize_t tot = 0;
while (tot < len) {
ssize_t wr = write(fd, buf + tot, len - tot);
- if (wr < 0)
- return -1;
+ if (wr < 0 && errno == EINTR)
+ continue;
+ else if (wr < 0)
+ return wr;
tot += wr;
}
@@ -408,15 +409,19 @@ kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
do {
nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
- if (nfds < 0)
+ if (nfds < 0 && errno == EINTR)
+ continue;
+ else if (nfds < 0)
goto out;
- if (!nfds) {
+ else if (!nfds) {
errno = ETIMEDOUT;
goto out;
}
n = read(fd, buf, len);
- if (n <= 0)
+ if (n < 0 && errno == EINTR)
+ continue;
+ else if (n <= 0)
goto out;
buf = (char *)buf + n;
@@ -718,6 +723,7 @@ out:
static int
kwboot_bootmsg(int tty, void *msg)
{
+ struct kwboot_block block;
int rc;
char c;
int count;
@@ -748,7 +754,40 @@ kwboot_bootmsg(int tty, void *msg)
kwboot_printv("\n");
- return rc;
+ if (rc)
+ return rc;
+
+ /*
+ * At this stage we have sent more boot message patterns and BootROM
+ * (at least on Armada XP and 385) started interpreting sent bytes as
+ * part of xmodem packets. If BootROM is expecting SOH byte as start of
+ * a xmodem packet and it receives byte 0xff, then it throws it away and
+ * sends a NAK reply to host. If BootROM does not receive any byte for
+ * 2s when expecting some continuation of the xmodem packet, it throws
+ * away the partially received xmodem data and sends NAK reply to host.
+ *
+ * Therefore for starting xmodem transfer we have two options: Either
+ * wait 2s or send 132 0xff bytes (which is the size of xmodem packet)
+ * to ensure that BootROM throws away any partially received data.
+ */
+
+ /* flush output queue with remaining boot message patterns */
+ tcflush(tty, TCOFLUSH);
+
+ /* send one xmodem packet with 0xff bytes to force BootROM to re-sync */
+ memset(&block, 0xff, sizeof(block));
+ kwboot_tty_send(tty, &block, sizeof(block), 0);
+
+ /*
+ * Sending 132 bytes via 115200B/8-N-1 takes 11.45 ms, reading 132 bytes
+ * takes 11.45 ms, so waiting for 30 ms should be enough.
+ */
+ usleep(30 * 1000);
+
+ /* flush remaining NAK replies from input queue */
+ tcflush(tty, TCIFLUSH);
+
+ return 0;
}
static int
@@ -826,7 +865,7 @@ _now(void)
static int
_is_xm_reply(char c)
{
- return c == ACK || c == NAK || c == CAN;
+ return c == ACK || c == NAK;
}
static int
@@ -841,9 +880,6 @@ _xm_reply_to_error(int c)
case NAK:
errno = EBADMSG;
break;
- case CAN:
- errno = ECANCELED;
- break;
default:
errno = EPROTO;
break;
@@ -879,7 +915,8 @@ kwboot_baud_magic_handle(int fd, char c, int baudrate)
}
static int
-kwboot_xm_recv_reply(int fd, char *c, int nak_on_non_xm,
+kwboot_xm_recv_reply(int fd, char *c, int stop_on_non_xm,
+ int ignore_nak_reply,
int allow_non_xm, int *non_xm_print,
int baudrate, int *baud_changed)
{
@@ -899,8 +936,14 @@ kwboot_xm_recv_reply(int fd, char *c, int nak_on_non_xm,
}
/* If received xmodem reply, end. */
- if (_is_xm_reply(*c))
+ if (_is_xm_reply(*c)) {
+ if (*c == NAK && ignore_nak_reply) {
+ timeout = recv_until - _now();
+ if (timeout >= 0)
+ continue;
+ }
break;
+ }
/*
* If receiving/printing non-xmodem text output is allowed and
@@ -928,10 +971,8 @@ kwboot_xm_recv_reply(int fd, char *c, int nak_on_non_xm,
*non_xm_print = 1;
}
} else {
- if (nak_on_non_xm) {
- *c = NAK;
+ if (stop_on_non_xm)
break;
- }
timeout = recv_until - _now();
if (timeout < 0) {
errno = ETIMEDOUT;
@@ -945,7 +986,7 @@ kwboot_xm_recv_reply(int fd, char *c, int nak_on_non_xm,
static int
kwboot_xm_sendblock(int fd, struct kwboot_block *block, int allow_non_xm,
- int *done_print, int baudrate)
+ int *done_print, int baudrate, int allow_retries)
{
int non_xm_print, baud_changed;
int rc, err, retries;
@@ -959,7 +1000,7 @@ kwboot_xm_sendblock(int fd, struct kwboot_block *block, int allow_non_xm,
do {
rc = kwboot_tty_send(fd, block, sizeof(*block), 1);
if (rc)
- return rc;
+ goto err;
if (allow_non_xm && !*done_print) {
kwboot_progress(100, '.');
@@ -968,29 +1009,32 @@ kwboot_xm_sendblock(int fd, struct kwboot_block *block, int allow_non_xm,
}
rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
+ retries > 8,
allow_non_xm, &non_xm_print,
baudrate, &baud_changed);
if (rc)
- goto can;
+ goto err;
- if (!allow_non_xm && c != ACK)
- kwboot_progress(-1, '+');
- } while (c == NAK && retries++ < 16);
+ if (!allow_non_xm && c != ACK) {
+ if (c == NAK && allow_retries && retries + 1 < 16)
+ kwboot_progress(-1, '+');
+ else
+ kwboot_progress(-1, 'E');
+ }
+ } while (c == NAK && allow_retries && retries++ < 16);
if (non_xm_print)
kwboot_printv("\n");
if (allow_non_xm && baudrate && !baud_changed) {
fprintf(stderr, "Baudrate was not changed\n");
- rc = -1;
errno = EPROTO;
- goto can;
+ return -1;
}
return _xm_reply_to_error(c);
-can:
+err:
err = errno;
- kwboot_tty_send_char(fd, CAN);
kwboot_printv("\n");
errno = err;
return rc;
@@ -1011,6 +1055,7 @@ kwboot_xm_finish(int fd)
return rc;
rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
+ retries > 8,
0, NULL, 0, NULL);
if (rc)
return rc;
@@ -1043,8 +1088,30 @@ kwboot_xmodem_one(int tty, int *pnum, int header, const uint8_t *data,
last_block = (left <= blksz);
+ /*
+ * Handling of repeated xmodem packets is completely broken in
+ * Armada 385 BootROM - it completely ignores xmodem packet
+ * numbers, they are only used for checksum verification.
+ * BootROM can handle a retry of the xmodem packet only during
+ * the transmission of kwbimage header and only if BootROM
+ * itself sent NAK response to previous attempt (it does it on
+ * checksum failure). During the transmission of kwbimage data
+ * part, BootROM always expects next xmodem packet, even if it
+ * sent NAK to previous attempt - there is absolutely no way to
+ * repair incorrectly transmitted xmodem packet during kwbimage
+ * data part upload. Also, if kwboot receives non-ACK/NAK
+ * response (meaning that original BootROM response was damaged
+ * on UART) there is no way to detect if BootROM accepted xmodem
+ * packet or not and no way to check if kwboot could repeat the
+ * packet or not.
+ *
+ * Stop transfer and return failure if kwboot receives unknown
+ * reply if non-xmodem reply is not allowed (for all xmodem
+ * packets except the last header packet) or when non-ACK reply
+ * is received during data part transfer.
+ */
rc = kwboot_xm_sendblock(tty, &block, header && last_block,
- &done_print, baudrate);
+ &done_print, baudrate, header);
if (rc)
goto out;
@@ -1081,10 +1148,6 @@ kwboot_xmodem(int tty, const void *_img, size_t size, int baudrate)
*/
hdrsz += (KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ) % KWBOOT_XM_BLKSZ;
- kwboot_printv("Waiting 2s and flushing tty\n");
- sleep(2); /* flush isn't effective without it */
- tcflush(tty, TCIOFLUSH);
-
pnum = 1;
rc = kwboot_xmodem_one(tty, &pnum, 1, img, hdrsz, baudrate);
@@ -1568,6 +1631,7 @@ kwboot_img_patch(void *img, size_t *size, int baudrate)
* baudrate (which should be 115200) and do not touch
* UART MPP configuration.
*/
+ hdr->flags |= 0x1;
hdr->options &= ~0x1F;
hdr->options |= MAIN_HDR_V1_OPT_BAUD_DEFAULT;
hdr->options |= 0 << 3;
@@ -1672,6 +1736,8 @@ main(int argc, char **argv)
size_t size;
size_t after_img_rsv;
int baudrate;
+ int prev_optind;
+ int c;
rv = 1;
tty = -1;
@@ -1689,22 +1755,32 @@ main(int argc, char **argv)
kwboot_verbose = isatty(STDOUT_FILENO);
do {
- int c = getopt(argc, argv, "hb:ptaB:dD:q:s:o:");
+ prev_optind = optind;
+ c = getopt(argc, argv, "hbptaB:dD:q:s:o:");
if (c < 0)
break;
switch (c) {
case 'b':
+ if (imgpath || bootmsg || debugmsg)
+ goto usage;
bootmsg = kwboot_msg_boot;
- imgpath = optarg;
+ if (prev_optind == optind)
+ goto usage;
+ if (argv[optind] && argv[optind][0] != '-')
+ imgpath = argv[optind++];
break;
case 'D':
+ if (imgpath || bootmsg || debugmsg)
+ goto usage;
bootmsg = NULL;
imgpath = optarg;
break;
case 'd':
+ if (imgpath || bootmsg || debugmsg)
+ goto usage;
debugmsg = kwboot_msg_debug;
break;
@@ -1744,14 +1820,14 @@ main(int argc, char **argv)
}
} while (1);
- if (!bootmsg && !term && !debugmsg)
- goto usage;
-
- if (argc - optind < 1)
+ if (!bootmsg && !term && !debugmsg && !imgpath)
goto usage;
ttypath = argv[optind++];
+ if (optind != argc)
+ goto usage;
+
tty = kwboot_open_tty(ttypath, imgpath ? 115200 : baudrate);
if (tty < 0) {
perror(ttypath);