/*
* Boot a Marvell SoC, with Xmodem over UART0.
- * supports Kirkwood, Dove, Armada 370, Armada XP
+ * supports Kirkwood, Dove, Armada 370, Armada XP, Armada 375, Armada 38x and
+ * Armada 39x
*
*
* References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
* Integrated Controller: Functional Specifications" December 2,
#include <errno.h>
#include <unistd.h>
#include <stdint.h>
-#include <termios.h>
#include <time.h>
#include <sys/stat.h>
+#ifdef __linux__
+#include "termios_linux.h"
+#else
+#include <termios.h>
+#endif
+
/*
* Marvell BootROM UART Sensing
*/
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 making baudrate changing function return to original exec address */
-static unsigned char kwboot_pre_baud_code[] = {
- /* exec_addr: */
- 0x00, 0x00, 0x00, 0x00, /* .word 0 */
- 0x0c, 0xe0, 0x1f, 0xe5, /* ldr lr, exec_addr */
-};
-
-/* ARM code for binary header injection to change baudrate */
+/* ARM code to change baudrate */
static unsigned char kwboot_baud_code[] = {
/* ; #define UART_BASE 0xd0012000 */
- /* ; #define THR 0x00 */
/* ; #define DLL 0x00 */
/* ; #define DLH 0x04 */
/* ; #define LCR 0x0c */
/* ; #define DLAB 0x80 */
/* ; #define LSR 0x14 */
- /* ; #define THRE 0x20 */
/* ; #define TEMT 0x40 */
/* ; #define DIV_ROUND(a, b) ((a + b/2) / b) */
/* ; */
/* ; u32 set_baudrate(u32 old_b, u32 new_b) { */
- /* ; const u8 *str = "$baudratechange"; */
- /* ; u8 c; */
- /* ; do { */
- /* ; c = *str++; */
- /* ; writel(UART_BASE + THR, c); */
- /* ; } while (c); */
/* ; while */
/* ; (!(readl(UART_BASE + LSR) & TEMT)); */
/* ; u32 lcr = readl(UART_BASE + LCR); */
/* ; writel(UART_BASE + DLL, new_dll); */
/* ; writel(UART_BASE + DLH, new_dlh); */
/* ; writel(UART_BASE + LCR, lcr & ~DLAB); */
- /* ; msleep(1); */
+ /* ; msleep(5); */
/* ; return 0; */
/* ; } */
- 0xfe, 0x5f, 0x2d, 0xe9, /* push { r1 - r12, lr } */
-
/* ; r0 = UART_BASE */
- 0x02, 0x0a, 0xa0, 0xe3, /* mov r0, #0x2000 */
- 0x01, 0x00, 0x4d, 0xe3, /* movt r0, #0xd001 */
-
- /* ; r2 = address of preamble string */
- 0xd0, 0x20, 0x8f, 0xe2, /* adr r2, preamble */
-
- /* ; Send preamble string over UART */
- /* .Lloop_preamble: */
- /* */
- /* ; Wait until Transmitter Holding is Empty */
- /* .Lloop_thre: */
- /* ; r1 = UART_BASE[LSR] & THRE */
- 0x14, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x14] */
- 0x20, 0x00, 0x11, 0xe3, /* tst r1, #0x20 */
- 0xfc, 0xff, 0xff, 0x0a, /* beq .Lloop_thre */
-
- /* ; Put character into Transmitter FIFO */
- /* ; r1 = *r2++ */
- 0x01, 0x10, 0xd2, 0xe4, /* ldrb r1, [r2], #1 */
- /* ; UART_BASE[THR] = r1 */
- 0x00, 0x10, 0x80, 0xe5, /* str r1, [r0, #0x0] */
-
- /* ; Loop until end of preamble string */
- 0x00, 0x00, 0x51, 0xe3, /* cmp r1, #0 */
- 0xf8, 0xff, 0xff, 0x1a, /* bne .Lloop_preamble */
+ 0x0d, 0x02, 0xa0, 0xe3, /* mov r0, #0xd0000000 */
+ 0x12, 0x0a, 0x80, 0xe3, /* orr r0, r0, #0x12000 */
/* ; Wait until Transmitter FIFO is Empty */
/* .Lloop_txempty: */
/* ; Read old baudrate value */
/* ; r2 = old_baudrate */
- 0x8c, 0x20, 0x9f, 0xe5, /* ldr r2, old_baudrate */
+ 0x74, 0x20, 0x9f, 0xe5, /* ldr r2, old_baudrate */
/* ; Calculate base clock */
/* ; r1 = r2 * r1 */
0x92, 0x01, 0x01, 0xe0, /* mul r1, r2, r1 */
/* ; Read new baudrate value */
- /* ; r2 = baudrate */
- 0x88, 0x20, 0x9f, 0xe5, /* ldr r2, baudrate */
+ /* ; r2 = new_baudrate */
+ 0x70, 0x20, 0x9f, 0xe5, /* ldr r2, new_baudrate */
/* ; Calculate new Divisor Latch */
/* ; r1 = DIV_ROUND(r1, r2) = */
0x80, 0x10, 0xc1, 0xe3, /* bic r1, r1, #0x80 */
0x0c, 0x10, 0x80, 0xe5, /* str r1, [r0, #0x0c] */
- /* ; Sleep 1ms ~~ 600000 cycles at 1200 MHz */
- /* ; r1 = 600000 */
- 0x9f, 0x1d, 0xa0, 0xe3, /* mov r1, #0x27c0 */
- 0x09, 0x10, 0x40, 0xe3, /* movt r1, #0x0009 */
+ /* ; Loop 0x2dc000 (2998272) cycles */
+ /* ; which is about 5ms on 1200 MHz CPU */
+ /* ; r1 = 0x2dc000 */
+ 0xb7, 0x19, 0xa0, 0xe3, /* mov r1, #0x2dc000 */
/* .Lloop_sleep: */
0x01, 0x10, 0x41, 0xe2, /* sub r1, r1, #1 */
0x00, 0x00, 0x51, 0xe3, /* cmp r1, #0 */
0xfc, 0xff, 0xff, 0x1a, /* bne .Lloop_sleep */
- /* ; Return 0 - no error */
- 0x00, 0x00, 0xa0, 0xe3, /* mov r0, #0 */
- 0xfe, 0x9f, 0xbd, 0xe8, /* pop { r1 - r12, pc } */
-
- /* ; Preamble string */
- /* preamble: */
- 0x24, 0x62, 0x61, 0x75, /* .asciz "$baudratechange" */
- 0x64, 0x72, 0x61, 0x74,
- 0x65, 0x63, 0x68, 0x61,
- 0x6e, 0x67, 0x65, 0x00,
+ /* ; Jump to the end of execution */
+ 0x01, 0x00, 0x00, 0xea, /* b end */
/* ; Placeholder for old baudrate value */
/* old_baudrate: */
/* ; Placeholder for new baudrate value */
/* new_baudrate: */
0x00, 0x00, 0x00, 0x00, /* .word 0 */
+
+ /* end: */
};
-#define KWBOOT_BAUDRATE_BIN_HEADER_SZ (sizeof(kwboot_baud_code) + \
- sizeof(struct opt_hdr_v1) + 8)
+/* ARM code from binary header executed by BootROM before changing baudrate */
+static unsigned char kwboot_baud_code_binhdr_pre[] = {
+ /* ; #define UART_BASE 0xd0012000 */
+ /* ; #define THR 0x00 */
+ /* ; #define LSR 0x14 */
+ /* ; #define THRE 0x20 */
+ /* ; */
+ /* ; void send_preamble(void) { */
+ /* ; const u8 *str = "$baudratechange"; */
+ /* ; u8 c; */
+ /* ; do { */
+ /* ; while */
+ /* ; ((readl(UART_BASE + LSR) & THRE)); */
+ /* ; c = *str++; */
+ /* ; writel(UART_BASE + THR, c); */
+ /* ; } while (c); */
+ /* ; } */
+
+ /* ; Preserve registers for BootROM */
+ 0xfe, 0x5f, 0x2d, 0xe9, /* push { r1 - r12, lr } */
+
+ /* ; r0 = UART_BASE */
+ 0x0d, 0x02, 0xa0, 0xe3, /* mov r0, #0xd0000000 */
+ 0x12, 0x0a, 0x80, 0xe3, /* orr r0, r0, #0x12000 */
+
+ /* ; r2 = address of preamble string */
+ 0x00, 0x20, 0x8f, 0xe2, /* adr r2, .Lstr_preamble */
+
+ /* ; Skip preamble data section */
+ 0x03, 0x00, 0x00, 0xea, /* b .Lloop_preamble */
+
+ /* ; Preamble string */
+ /* .Lstr_preamble: */
+ 0x24, 0x62, 0x61, 0x75, /* .asciz "$baudratechange" */
+ 0x64, 0x72, 0x61, 0x74,
+ 0x65, 0x63, 0x68, 0x61,
+ 0x6e, 0x67, 0x65, 0x00,
+
+ /* ; Send preamble string over UART */
+ /* .Lloop_preamble: */
+ /* */
+ /* ; Wait until Transmitter Holding is Empty */
+ /* .Lloop_thre: */
+ /* ; r1 = UART_BASE[LSR] & THRE */
+ 0x14, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x14] */
+ 0x20, 0x00, 0x11, 0xe3, /* tst r1, #0x20 */
+ 0xfc, 0xff, 0xff, 0x0a, /* beq .Lloop_thre */
+
+ /* ; Put character into Transmitter FIFO */
+ /* ; r1 = *r2++ */
+ 0x01, 0x10, 0xd2, 0xe4, /* ldrb r1, [r2], #1 */
+ /* ; UART_BASE[THR] = r1 */
+ 0x00, 0x10, 0x80, 0xe5, /* str r1, [r0, #0x0] */
+
+ /* ; Loop until end of preamble string */
+ 0x00, 0x00, 0x51, 0xe3, /* cmp r1, #0 */
+ 0xf8, 0xff, 0xff, 0x1a, /* bne .Lloop_preamble */
+};
+
+/* ARM code for returning from binary header back to BootROM */
+static unsigned char kwboot_baud_code_binhdr_post[] = {
+ /* ; Return 0 - no error */
+ 0x00, 0x00, 0xa0, 0xe3, /* mov r0, #0 */
+ 0xfe, 0x9f, 0xbd, 0xe8, /* pop { r1 - r12, pc } */
+};
+
+/* ARM code for jumping to the original image exec_addr */
+static unsigned char kwboot_baud_code_data_jump[] = {
+ 0x04, 0xf0, 0x1f, 0xe5, /* ldr pc, exec_addr */
+ /* ; Placeholder for exec_addr */
+ /* exec_addr: */
+ 0x00, 0x00, 0x00, 0x00, /* .word 0 */
+};
static const char kwb_baud_magic[16] = "$baudratechange";
}
static int
-kwboot_tty_send(int fd, const void *buf, size_t len)
+kwboot_tty_send(int fd, const void *buf, size_t len, int nodrain)
{
if (!buf)
return 0;
if (kwboot_write(fd, buf, len) < 0)
return -1;
+ if (nodrain)
+ return 0;
+
return tcdrain(fd);
}
static int
kwboot_tty_send_char(int fd, unsigned char c)
{
- return kwboot_tty_send(fd, &c, 1);
+ return kwboot_tty_send(fd, &c, 1, 0);
}
static speed_t
return B50;
#endif
default:
+#ifdef BOTHER
+ return BOTHER;
+#else
return B0;
+#endif
}
}
+static int
+_is_within_tolerance(int value, int reference, int tolerance)
+{
+ return 100 * value >= reference * (100 - tolerance) &&
+ 100 * value <= reference * (100 + tolerance);
+}
+
static int
kwboot_tty_change_baudrate(int fd, int baudrate)
{
return -1;
}
+#ifdef BOTHER
+ if (speed == BOTHER)
+ tio.c_ospeed = tio.c_ispeed = baudrate;
+#endif
+
rc = cfsetospeed(&tio, speed);
if (rc)
return rc;
if (rc)
return rc;
+ rc = tcgetattr(fd, &tio);
+ if (rc)
+ return rc;
+
+ if (cfgetospeed(&tio) != speed || cfgetispeed(&tio) != speed)
+ goto baud_fail;
+
+#ifdef BOTHER
+ /*
+ * Check whether set baudrate is within 3% tolerance.
+ * If BOTHER is defined, Linux always fills out c_ospeed / c_ispeed
+ * with real values.
+ */
+ if (!_is_within_tolerance(tio.c_ospeed, baudrate, 3))
+ goto baud_fail;
+
+ if (!_is_within_tolerance(tio.c_ispeed, baudrate, 3))
+ goto baud_fail;
+#endif
+
return 0;
+
+baud_fail:
+ fprintf(stderr, "Could not set baudrate to requested value\n");
+ errno = EINVAL;
+ return -1;
}
static int
kwboot_open_tty(const char *path, int baudrate)
{
- int rc, fd;
+ int rc, fd, flags;
struct termios tio;
rc = -1;
- fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
+ fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0)
goto out;
- memset(&tio, 0, sizeof(tio));
-
- tio.c_iflag = 0;
- tio.c_cflag = CREAD|CLOCAL|CS8;
+ rc = tcgetattr(fd, &tio);
+ if (rc)
+ goto out;
+ cfmakeraw(&tio);
+ tio.c_cflag |= CREAD | CLOCAL;
+ tio.c_cflag &= ~(CSTOPB | HUPCL | CRTSCTS);
tio.c_cc[VMIN] = 1;
- tio.c_cc[VTIME] = 10;
+ tio.c_cc[VTIME] = 0;
rc = tcsetattr(fd, TCSANOW, &tio);
if (rc)
goto out;
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0)
+ goto out;
+
+ rc = fcntl(fd, F_SETFL, flags & ~O_NDELAY);
+ if (rc)
+ goto out;
+
rc = kwboot_tty_change_baudrate(fd, baudrate);
if (rc)
goto out;
break;
for (count = 0; count < 128; count++) {
- rc = kwboot_tty_send(tty, msg, 8);
+ rc = kwboot_tty_send(tty, msg, 8, 0);
if (rc) {
usleep(msg_req_delay * 1000);
continue;
if (rc)
break;
- rc = kwboot_tty_send(tty, msg, 8);
+ rc = kwboot_tty_send(tty, msg, 8, 0);
if (rc) {
usleep(msg_req_delay * 1000);
continue;
}
static int
-kwboot_xm_recv_reply(int fd, char *c, int allow_non_xm, int *non_xm_print,
+kwboot_xm_recv_reply(int fd, char *c, int nak_on_non_xm,
+ int allow_non_xm, int *non_xm_print,
int baudrate, int *baud_changed)
{
int timeout = allow_non_xm ? KWBOOT_HDR_RSP_TIMEO : blk_rsp_timeo;
uint64_t recv_until = _now() + timeout;
int rc;
- if (non_xm_print)
- *non_xm_print = 0;
- if (baud_changed)
- *baud_changed = 0;
-
while (1) {
rc = kwboot_tty_recv(fd, c, 1, timeout);
if (rc) {
*non_xm_print = 1;
}
} else {
+ if (nak_on_non_xm) {
+ *c = NAK;
+ break;
+ }
timeout = recv_until - _now();
if (timeout < 0) {
errno = ETIMEDOUT;
char c;
*done_print = 0;
+ non_xm_print = 0;
+ baud_changed = 0;
- retries = 16;
+ retries = 0;
do {
- rc = kwboot_tty_send(fd, block, sizeof(*block));
+ rc = kwboot_tty_send(fd, block, sizeof(*block), 1);
if (rc)
return rc;
*done_print = 1;
}
- rc = kwboot_xm_recv_reply(fd, &c, allow_non_xm, &non_xm_print,
+ rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
+ allow_non_xm, &non_xm_print,
baudrate, &baud_changed);
if (rc)
goto can;
if (!allow_non_xm && c != ACK)
kwboot_progress(-1, '+');
- } while (c == NAK && retries-- > 0);
+ } while (c == NAK && retries++ < 16);
if (non_xm_print)
kwboot_printv("\n");
kwboot_printv("Finishing transfer\n");
- retries = 16;
+ retries = 0;
do {
rc = kwboot_tty_send_char(fd, EOT);
if (rc)
return rc;
- rc = kwboot_xm_recv_reply(fd, &c, 0, NULL, 0, NULL);
+ rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
+ 0, NULL, 0, NULL);
if (rc)
return rc;
- } while (c == NAK && retries-- > 0);
+ } while (c == NAK && retries++ < 16);
return _xm_reply_to_error(c);
}
hdrsz = kwbheader_size(img);
- kwboot_printv("Waiting 2s and flushing tty\n");
- sleep(2); /* flush isn't effective without it */
+ /*
+ * If header size is not aligned to xmodem block size (which applies
+ * for all images in kwbimage v0 format) then we have to ensure that
+ * the last xmodem block of header contains beginning of the data
+ * followed by the header. So align header size to xmodem block size.
+ */
+ hdrsz += (KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ) % KWBOOT_XM_BLKSZ;
+
+ kwboot_printv("Waiting %d ms and flushing tty\n", blk_rsp_timeo);
+ usleep(blk_rsp_timeo * 1000);
tcflush(tty, TCIOFLUSH);
pnum = 1;
if (rc)
return rc;
- img += hdrsz;
- size -= hdrsz;
-
- rc = kwboot_xmodem_one(tty, &pnum, 0, img, size, 0);
- if (rc)
- return rc;
+ /*
+ * If we have already sent image data as a part of the last
+ * xmodem header block then we have nothing more to send.
+ */
+ if (hdrsz < size) {
+ img += hdrsz;
+ size -= hdrsz;
+ rc = kwboot_xmodem_one(tty, &pnum, 0, img, size, 0);
+ if (rc)
+ return rc;
+ }
rc = kwboot_xm_finish(tty);
if (rc)
return rc;
if (baudrate) {
- char buf[sizeof(kwb_baud_magic)];
-
- /* Wait 1s for baudrate change magic */
- rc = kwboot_tty_recv(tty, buf, sizeof(buf), 1000);
- if (rc)
- return rc;
-
- if (memcmp(buf, kwb_baud_magic, sizeof(buf))) {
- errno = EPROTO;
- return -1;
- }
-
kwboot_printv("\nChanging baudrate back to 115200 Bd\n\n");
rc = kwboot_tty_change_baudrate(tty, 115200);
if (rc)
}
kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
- quit[0]|0100, quit[1]);
+ quit[0] | 0100, quit[1]);
} else
in = -1;
fd_set rfds;
int nfds = 0;
+ FD_ZERO(&rfds);
FD_SET(tty, &rfds);
nfds = nfds < tty ? tty : nfds;
return csum;
}
+static uint32_t *
+kwboot_img_csum32_ptr(void *img)
+{
+ struct main_hdr_v1 *hdr = img;
+ uint32_t datasz;
+
+ datasz = le32_to_cpu(hdr->blocksize) - sizeof(uint32_t);
+
+ return img + le32_to_cpu(hdr->srcaddr) + datasz;
+}
+
+static uint32_t
+kwboot_img_csum32(const void *img)
+{
+ const struct main_hdr_v1 *hdr = img;
+ uint32_t datasz, csum = 0;
+ const uint32_t *data;
+
+ datasz = le32_to_cpu(hdr->blocksize) - sizeof(csum);
+ if (datasz % sizeof(uint32_t))
+ return 0;
+
+ data = img + le32_to_cpu(hdr->srcaddr);
+ while (datasz > 0) {
+ csum += le32_to_cpu(*data++);
+ datasz -= 4;
+ }
+
+ return cpu_to_le32(csum);
+}
+
static int
kwboot_img_is_secure(void *img)
{
}
static void *
-kwboot_img_grow_data_left(void *img, size_t *size, size_t grow)
+kwboot_img_grow_data_right(void *img, size_t *size, size_t grow)
{
- uint32_t hdrsz, datasz, srcaddr;
struct main_hdr_v1 *hdr = img;
- uint8_t *data;
-
- srcaddr = le32_to_cpu(hdr->srcaddr);
-
- hdrsz = kwbheader_size(hdr);
- data = (uint8_t *)img + srcaddr;
- datasz = *size - srcaddr;
-
- /* only move data if there is not enough space */
- if (hdrsz + grow > srcaddr) {
- size_t need = hdrsz + grow - srcaddr;
-
- /* move data by enough bytes */
- memmove(data + need, data, datasz);
- *size += need;
- srcaddr += need;
- }
-
- srcaddr -= grow;
- hdr->srcaddr = cpu_to_le32(srcaddr);
- hdr->destaddr = cpu_to_le32(le32_to_cpu(hdr->destaddr) - grow);
+ void *result;
+
+ /*
+ * 32-bit checksum comes after end of image code, so we will be putting
+ * new code there. So we get this pointer and then increase data size
+ * (since increasing data size changes kwboot_img_csum32_ptr() return
+ * value).
+ */
+ result = kwboot_img_csum32_ptr(img);
hdr->blocksize = cpu_to_le32(le32_to_cpu(hdr->blocksize) + grow);
+ *size += grow;
- return (uint8_t *)img + srcaddr;
+ return result;
}
static void
{
uint32_t hdrsz, datasz, srcaddr;
struct main_hdr_v1 *hdr = img;
+ struct opt_hdr_v1 *ohdr;
uint8_t *data;
srcaddr = le32_to_cpu(hdr->srcaddr);
- hdrsz = kwbheader_size(img);
+ /* calculate real used space in kwbimage header */
+ if (kwbimage_version(img) == 0) {
+ hdrsz = kwbheader_size(img);
+ } else {
+ hdrsz = sizeof(*hdr);
+ for_each_opt_hdr_v1 (ohdr, hdr)
+ hdrsz += opt_hdr_v1_size(ohdr);
+ }
+
data = (uint8_t *)img + srcaddr;
datasz = *size - srcaddr;
if (kwbimage_version(img) == 1) {
hdrsz += grow;
- hdr->headersz_msb = hdrsz >> 16;
- hdr->headersz_lsb = cpu_to_le16(hdrsz & 0xffff);
+ if (hdrsz > kwbheader_size(img)) {
+ hdr->headersz_msb = hdrsz >> 16;
+ hdr->headersz_lsb = cpu_to_le16(hdrsz & 0xffff);
+ }
}
}
{
struct main_hdr_v1 *hdr = img;
struct opt_hdr_v1 *ohdr;
+ uint32_t num_args;
+ uint32_t offset;
uint32_t ohdrsz;
+ uint8_t *prev_ext;
- ohdrsz = binsz + 8 + sizeof(*ohdr);
- kwboot_img_grow_hdr(img, size, ohdrsz);
-
- if (hdr->ext & 0x1) {
+ if (hdr->ext) {
for_each_opt_hdr_v1 (ohdr, img)
if (opt_hdr_v1_next(ohdr) == NULL)
break;
- *opt_hdr_v1_ext(ohdr) |= 1;
- ohdr = opt_hdr_v1_next(ohdr);
+ prev_ext = opt_hdr_v1_ext(ohdr);
+ ohdr = _opt_hdr_v1_next(ohdr);
} else {
- hdr->ext |= 1;
ohdr = (void *)(hdr + 1);
+ prev_ext = &hdr->ext;
}
+ /*
+ * ARM executable code inside the BIN header on some mvebu platforms
+ * (e.g. A370, AXP) must always be aligned with the 128-bit boundary.
+ * This requirement can be met by inserting dummy arguments into
+ * BIN header, if needed.
+ */
+ offset = &ohdr->data[4] - (char *)img;
+ num_args = ((16 - offset % 16) % 16) / sizeof(uint32_t);
+
+ ohdrsz = sizeof(*ohdr) + 4 + 4 * num_args + binsz + 4;
+ kwboot_img_grow_hdr(hdr, size, ohdrsz);
+
+ *prev_ext = 1;
+
ohdr->headertype = OPT_HDR_V1_BINARY_TYPE;
ohdr->headersz_msb = ohdrsz >> 16;
ohdr->headersz_lsb = cpu_to_le16(ohdrsz & 0xffff);
memset(&ohdr->data[0], 0, ohdrsz - sizeof(*ohdr));
+ *(uint32_t *)&ohdr->data[0] = cpu_to_le32(num_args);
- return &ohdr->data[4];
+ return &ohdr->data[4 + 4 * num_args];
}
static void
-_copy_baudrate_change_code(struct main_hdr_v1 *hdr, void *dst, int pre,
- int old_baud, int new_baud)
+_inject_baudrate_change_code(void *img, size_t *size, int for_data,
+ int old_baud, int new_baud)
{
- size_t codesz = sizeof(kwboot_baud_code);
- uint8_t *code = dst;
-
- if (pre) {
- size_t presz = sizeof(kwboot_pre_baud_code);
+ struct main_hdr_v1 *hdr = img;
+ uint32_t orig_datasz;
+ uint32_t codesz;
+ uint8_t *code;
- /*
- * We need to prepend code that loads lr register with original
- * value of hdr->execaddr. We do this by putting the original
- * exec address before the code that loads it relatively from
- * it's beginning.
- * Afterwards we change the exec address to this code (which is
- * at offset 4, because the first 4 bytes contain the original
- * exec address).
- */
- memcpy(code, kwboot_pre_baud_code, presz);
- *(uint32_t *)code = hdr->execaddr;
+ if (for_data) {
+ orig_datasz = le32_to_cpu(hdr->blocksize) - sizeof(uint32_t);
- hdr->execaddr = cpu_to_le32(le32_to_cpu(hdr->destaddr) + 4);
+ codesz = sizeof(kwboot_baud_code) +
+ sizeof(kwboot_baud_code_data_jump);
+ code = kwboot_img_grow_data_right(img, size, codesz);
+ } else {
+ codesz = sizeof(kwboot_baud_code_binhdr_pre) +
+ sizeof(kwboot_baud_code) +
+ sizeof(kwboot_baud_code_binhdr_post);
+ code = kwboot_add_bin_ohdr_v1(img, size, codesz);
- code += presz;
+ codesz = sizeof(kwboot_baud_code_binhdr_pre);
+ memcpy(code, kwboot_baud_code_binhdr_pre, codesz);
+ code += codesz;
}
- memcpy(code, kwboot_baud_code, codesz - 8);
- *(uint32_t *)(code + codesz - 8) = cpu_to_le32(old_baud);
- *(uint32_t *)(code + codesz - 4) = cpu_to_le32(new_baud);
+ codesz = sizeof(kwboot_baud_code) - 2 * sizeof(uint32_t);
+ memcpy(code, kwboot_baud_code, codesz);
+ code += codesz;
+ *(uint32_t *)code = cpu_to_le32(old_baud);
+ code += sizeof(uint32_t);
+ *(uint32_t *)code = cpu_to_le32(new_baud);
+ code += sizeof(uint32_t);
+
+ if (for_data) {
+ codesz = sizeof(kwboot_baud_code_data_jump) - sizeof(uint32_t);
+ memcpy(code, kwboot_baud_code_data_jump, codesz);
+ code += codesz;
+ *(uint32_t *)code = hdr->execaddr;
+ code += sizeof(uint32_t);
+ hdr->execaddr = cpu_to_le32(le32_to_cpu(hdr->destaddr) + orig_datasz);
+ } else {
+ codesz = sizeof(kwboot_baud_code_binhdr_post);
+ memcpy(code, kwboot_baud_code_binhdr_post, codesz);
+ code += codesz;
+ }
}
static int
kwboot_img_patch(void *img, size_t *size, int baudrate)
{
- int rc;
struct main_hdr_v1 *hdr;
uint32_t srcaddr;
uint8_t csum;
int image_ver;
int is_secure;
- rc = -1;
hdr = img;
- if (*size < sizeof(struct main_hdr_v1)) {
- errno = EINVAL;
- goto out;
- }
+ if (*size < sizeof(struct main_hdr_v1))
+ goto err;
image_ver = kwbimage_version(img);
if (image_ver != 0 && image_ver != 1) {
fprintf(stderr, "Invalid image header version\n");
- errno = EINVAL;
- goto out;
+ goto err;
}
hdrsz = kwbheader_size(hdr);
- if (*size < hdrsz) {
- errno = EINVAL;
- goto out;
- }
+ if (*size < hdrsz)
+ goto err;
csum = kwboot_hdr_csum8(hdr) - hdr->checksum;
- if (csum != hdr->checksum) {
- errno = EINVAL;
- goto out;
- }
-
- if (image_ver == 0) {
- struct main_hdr_v0 *hdr_v0 = img;
-
- hdr_v0->nandeccmode = IBR_HDR_ECC_DISABLED;
- hdr_v0->nandpagesize = 0;
- }
+ if (csum != hdr->checksum)
+ goto err;
srcaddr = le32_to_cpu(hdr->srcaddr);
switch (hdr->blockid) {
case IBR_HDR_SATA_ID:
- if (srcaddr < 1) {
- errno = EINVAL;
- goto out;
- }
+ if (srcaddr < 1)
+ goto err;
+
hdr->srcaddr = cpu_to_le32((srcaddr - 1) * 512);
break;
}
if (hdrsz > le32_to_cpu(hdr->srcaddr) ||
- *size < le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize)) {
- errno = EINVAL;
- goto out;
- }
+ *size < le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize))
+ goto err;
+
+ if (kwboot_img_csum32(img) != *kwboot_img_csum32_ptr(img))
+ goto err;
is_secure = kwboot_img_is_secure(img);
if (is_secure) {
fprintf(stderr,
"Image has secure header with signature for non-UART booting\n");
- errno = EINVAL;
- goto out;
+ goto err;
}
kwboot_printv("Patching image boot signature to UART\n");
hdr->blockid = IBR_HDR_UART_ID;
}
- if (baudrate) {
- uint32_t codesz = sizeof(kwboot_baud_code);
- void *code;
+ if (!is_secure) {
+ if (image_ver == 1) {
+ /*
+ * Tell BootROM to send BootROM messages to UART port
+ * number 0 (used also for UART booting) with default
+ * baudrate (which should be 115200) and do not touch
+ * UART MPP configuration.
+ */
+ hdr->options &= ~0x1F;
+ hdr->options |= MAIN_HDR_V1_OPT_BAUD_DEFAULT;
+ hdr->options |= 0 << 3;
+ }
+ if (image_ver == 0)
+ ((struct main_hdr_v0 *)img)->nandeccmode = IBR_HDR_ECC_DISABLED;
+ hdr->nandpagesize = 0;
+ }
+ if (baudrate) {
if (image_ver == 0) {
fprintf(stderr,
"Cannot inject code for changing baudrate into v0 image header\n");
- errno = EINVAL;
- goto out;
+ goto err;
}
if (is_secure) {
fprintf(stderr,
"Cannot inject code for changing baudrate into image with secure header\n");
- errno = EINVAL;
- goto out;
+ goto err;
}
/*
*/
kwboot_printv("Injecting binary header code for changing baudrate to %d Bd\n",
baudrate);
-
- code = kwboot_add_bin_ohdr_v1(img, size, codesz);
- _copy_baudrate_change_code(hdr, code, 0, 115200, baudrate);
+ _inject_baudrate_change_code(img, size, 0, 115200, baudrate);
/*
* Now inject code that changes the baudrate back to 115200 Bd.
- * This code is prepended to the data part of the image, so it
- * is executed before U-Boot proper.
+ * This code is appended after the data part of the image, and
+ * execaddr is changed so that it is executed before U-Boot
+ * proper.
*/
kwboot_printv("Injecting code for changing baudrate back\n");
+ _inject_baudrate_change_code(img, size, 1, baudrate, 115200);
- codesz += sizeof(kwboot_pre_baud_code);
- code = kwboot_img_grow_data_left(img, size, codesz);
- _copy_baudrate_change_code(hdr, code, 1, baudrate, 115200);
+ /* Update the 32-bit data checksum */
+ *kwboot_img_csum32_ptr(img) = kwboot_img_csum32(img);
/* recompute header size */
hdrsz = kwbheader_size(hdr);
}
if (hdrsz % KWBOOT_XM_BLKSZ) {
- size_t offset = (KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ) %
- KWBOOT_XM_BLKSZ;
+ size_t grow = KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ;
if (is_secure) {
fprintf(stderr, "Cannot align image with secure header\n");
- errno = EINVAL;
- goto out;
+ goto err;
}
kwboot_printv("Aligning image header to Xmodem block size\n");
- kwboot_img_grow_hdr(img, size, offset);
+ kwboot_img_grow_hdr(img, size, grow);
}
hdr->checksum = kwboot_hdr_csum8(hdr) - csum;
*size = le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize);
- rc = 0;
-out:
- return rc;
+ return 0;
+err:
+ errno = EINVAL;
+ return -1;
}
static void
kwboot_usage(FILE *stream, char *progname)
{
- fprintf(stream, "kwboot version %s\n", PLAIN_VERSION);
fprintf(stream,
"Usage: %s [OPTIONS] [-b <image> | -D <image> ] [-B <baud> ] <TTY>\n",
progname);
after_img_rsv = KWBOOT_XM_BLKSZ;
baudrate = 115200;
+ printf("kwboot version %s\n", PLAIN_VERSION);
+
kwboot_verbose = isatty(STDOUT_FILENO);
do {
baudrate = 0;
else
/* ensure we have enough space for baudrate change code */
- after_img_rsv += KWBOOT_BAUDRATE_BIN_HEADER_SZ +
- sizeof(kwboot_pre_baud_code) +
- sizeof(kwboot_baud_code);
+ after_img_rsv += sizeof(struct opt_hdr_v1) + 8 + 16 +
+ sizeof(kwboot_baud_code_binhdr_pre) +
+ sizeof(kwboot_baud_code) +
+ sizeof(kwboot_baud_code_binhdr_post) +
+ KWBOOT_XM_BLKSZ +
+ sizeof(kwboot_baud_code) +
+ sizeof(kwboot_baud_code_data_jump) +
+ KWBOOT_XM_BLKSZ;
if (imgpath) {
img = kwboot_read_image(imgpath, &size, after_img_rsv);