Patchwork [U-Boot,1/2] tools: rename kwboot to mvboot

login
register
mail settings
Submitter Luka Perkov
Date Feb. 12, 2013, 8:52 a.m.
Message ID <20130212085227-8049@mutt-kz>
Download mbox | patch
Permalink /patch/219741/
State New
Delegated to: Prafulla Wadaskar
Headers show

Comments

Luka Perkov - Feb. 12, 2013, 8:52 a.m.
kwboot tool is used to boot Marvell Kirkwood SoCs over the serial link. Before
adding support for other Marvell SoCs, such as Dove or Armada, this patch
renames the tool so it's name makes more sense in the future.

Signed-off-by: Luka Perkov <luka@openwrt.org>
CC: Prafulla Wadaskar <prafulla@marvell.com>
CC: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
CC: Daniel Stodden <daniel.stodden@gmail.com>
---
 doc/kwboot.1     |  84 -------
 doc/mvboot.1     |  84 +++++++
 tools/.gitignore |   2 +-
 tools/Makefile   |   6 +-
 tools/kwboot.c   | 742 -------------------------------------------------------
 tools/mvboot.c   | 742 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 830 insertions(+), 830 deletions(-)
 delete mode 100644 doc/kwboot.1
 create mode 100644 doc/mvboot.1
 delete mode 100644 tools/kwboot.c
 create mode 100644 tools/mvboot.c

Patch

diff --git a/doc/kwboot.1 b/doc/kwboot.1
deleted file mode 100644
index 25fe69a..0000000
--- a/doc/kwboot.1
+++ /dev/null
@@ -1,84 +0,0 @@ 
-.TH KWBOOT 1 "2012-05-19"
-
-.SH NAME
-kwboot \- Boot Marvell Kirkwood SoCs over a serial link.
-.SH SYNOPSIS
-.B kwboot
-.RB [ "-b \fIimage\fP" ]
-.RB [ "-p" ]
-.RB [ "-t" ]
-.RB [ "-B \fIbaudrate\fP" ]
-.RB \fITTY\fP
-.SH "DESCRIPTION"
-
-The \fBmkimage\fP program boots boards based on Marvell's Kirkwood
-platform over their integrated UART. Boot image files will typically
-contain a second stage boot loader, such as U-Boot. The image file
-must conform to Marvell's BootROM firmware image format
-(\fIkwbimage\fP), created using a tool such as \fBmkimage\fP.
-
-Following power-up or a system reset, system BootROM code polls the
-UART for a brief period of time, sensing a handshake message which
-initiates an image upload. This program sends this boot message until
-it receives a positive acknowledgement. The image is transfered using
-Xmodem.
-
-Additionally, this program implements a minimal terminal mode, which
-can be used either standalone, or entered immediately following boot
-image transfer completion. This is often useful to catch early boot
-messages, or to manually interrupt a default boot procedure performed
-by the second-stage loader.
-
-.SH "OPTIONS"
-
-.TP
-.BI "\-b \fIimage\fP"
-Handshake; then upload file \fIimage\fP over \fITTY\fP.
-
-Note that for the encapsulated boot code to be executed, \fIimage\fP
-must be of type "UART boot" (0x69). Boot images of different types,
-such as backup images of vendor firmware downloaded from flash memory
-(type 0x8B), will not work (or not as expected). See \fB-p\fP for a
-workaround.
-
-This mode writes handshake status and upload progress indication to
-stdout.
-
-.TP
-.BI "\-p"
-In combination with \fB-b\fP, patches the header in \fIimage\fP prior
-to upload, to "UART boot" type.
-
-This option attempts on-the-fly conversion of some none-UART image
-types, such as images which were originally formatted to be stored in
-flash memory.
-
-Conversion is performed in memory. The contents of \fIimage\fP will
-not be altered.
-
-.TP
-.BI "\-t"
-Run a terminal program, connecting standard input and output to
-.RB \fITTY\fP.
-
-If used in combination with \fB-b\fP, terminal mode is entered
-immediately following a successful image upload.
-
-If standard I/O streams connect to a console, this mode will terminate
-after receiving 'ctrl-\\' followed by 'c' from console input.
-
-.TP
-.BI "\-B \fIbaudrate\fP"
-Adjust the baud rate on \fITTY\fP. Default rate is 115200.
-
-.SH "SEE ALSO"
-.PP
-\fBmkimage\fP(1)
-
-.SH "AUTHORS"
-
-Daniel Stodden <daniel.stodden@gmail.com>
-.br
-Luka Perkov <luka@openwrt.org>
-.br
-David Purdy <david.c.purdy@gmail.com>
diff --git a/doc/mvboot.1 b/doc/mvboot.1
new file mode 100644
index 0000000..563d0e5
--- /dev/null
+++ b/doc/mvboot.1
@@ -0,0 +1,84 @@ 
+.TH MVBOOT 1 "2012-05-19"
+
+.SH NAME
+mvboot \- Boot Marvell Kirkwood SoCs over a serial link.
+.SH SYNOPSIS
+.B mvboot
+.RB [ "-b \fIimage\fP" ]
+.RB [ "-p" ]
+.RB [ "-t" ]
+.RB [ "-B \fIbaudrate\fP" ]
+.RB \fITTY\fP
+.SH "DESCRIPTION"
+
+The \fBmkimage\fP program boots boards based on Marvell's Kirkwood
+platform over their integrated UART. Boot image files will typically
+contain a second stage boot loader, such as U-Boot. The image file
+must conform to Marvell's BootROM firmware image format
+(\fIkwbimage\fP), created using a tool such as \fBmkimage\fP.
+
+Following power-up or a system reset, system BootROM code polls the
+UART for a brief period of time, sensing a handshake message which
+initiates an image upload. This program sends this boot message until
+it receives a positive acknowledgement. The image is transfered using
+Xmodem.
+
+Additionally, this program implements a minimal terminal mode, which
+can be used either standalone, or entered immediately following boot
+image transfer completion. This is often useful to catch early boot
+messages, or to manually interrupt a default boot procedure performed
+by the second-stage loader.
+
+.SH "OPTIONS"
+
+.TP
+.BI "\-b \fIimage\fP"
+Handshake; then upload file \fIimage\fP over \fITTY\fP.
+
+Note that for the encapsulated boot code to be executed, \fIimage\fP
+must be of type "UART boot" (0x69). Boot images of different types,
+such as backup images of vendor firmware downloaded from flash memory
+(type 0x8B), will not work (or not as expected). See \fB-p\fP for a
+workaround.
+
+This mode writes handshake status and upload progress indication to
+stdout.
+
+.TP
+.BI "\-p"
+In combination with \fB-b\fP, patches the header in \fIimage\fP prior
+to upload, to "UART boot" type.
+
+This option attempts on-the-fly conversion of some none-UART image
+types, such as images which were originally formatted to be stored in
+flash memory.
+
+Conversion is performed in memory. The contents of \fIimage\fP will
+not be altered.
+
+.TP
+.BI "\-t"
+Run a terminal program, connecting standard input and output to
+.RB \fITTY\fP.
+
+If used in combination with \fB-b\fP, terminal mode is entered
+immediately following a successful image upload.
+
+If standard I/O streams connect to a console, this mode will terminate
+after receiving 'ctrl-\\' followed by 'c' from console input.
+
+.TP
+.BI "\-B \fIbaudrate\fP"
+Adjust the baud rate on \fITTY\fP. Default rate is 115200.
+
+.SH "SEE ALSO"
+.PP
+\fBmkimage\fP(1)
+
+.SH "AUTHORS"
+
+Daniel Stodden <daniel.stodden@gmail.com>
+.br
+Luka Perkov <luka@openwrt.org>
+.br
+David Purdy <david.c.purdy@gmail.com>
diff --git a/tools/.gitignore b/tools/.gitignore
index 9bce719..89debe3 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -2,10 +2,10 @@ 
 /envcrc
 /gen_eth_addr
 /img2srec
-/kwboot
 /mkenvimage
 /mkimage
 /mpc86x_clk
+/mvboot
 /mxsboot
 /ncb
 /ncp
diff --git a/tools/Makefile b/tools/Makefile
index 686840a..adfb643 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -72,7 +72,7 @@  BIN_FILES-$(CONFIG_SMDK5250) += mksmdk5250spl$(SFX)
 BIN_FILES-$(CONFIG_MX28) += mxsboot$(SFX)
 BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX)
 BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX)
-BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX)
+BIN_FILES-$(CONFIG_KIRKWOOD) += mvboot$(SFX)
 
 # Source files which exist outside the tools directory
 EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o
@@ -103,7 +103,7 @@  OBJ_FILES-$(CONFIG_NETCONSOLE) += ncb.o
 NOPED_OBJ_FILES-y += os_support.o
 OBJ_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1.o
 NOPED_OBJ_FILES-y += ublimage.o
-OBJ_FILES-$(CONFIG_KIRKWOOD) += kwboot.o
+OBJ_FILES-$(CONFIG_KIRKWOOD) += mvboot.o
 
 # Don't build by default
 #ifeq ($(ARCH),ppc)
@@ -239,7 +239,7 @@  $(obj)ncb$(SFX):	$(obj)ncb.o
 $(obj)ubsha1$(SFX):	$(obj)os_support.o $(obj)sha1.o $(obj)ubsha1.o
 	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
 
-$(obj)kwboot$(SFX): $(obj)kwboot.o
+$(obj)mvboot$(SFX): $(obj)mvboot.o
 	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
 	$(HOSTSTRIP) $@
 
diff --git a/tools/kwboot.c b/tools/kwboot.c
deleted file mode 100644
index e773f01..0000000
--- a/tools/kwboot.c
+++ /dev/null
@@ -1,742 +0,0 @@ 
-/*
- * Boot a Marvell Kirkwood SoC, with Xmodem over UART0.
- *
- * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
- *
- * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
- *   Integrated Controller: Functional Specifications" December 2,
- *   2008. Chapter 24.2 "BootROM Firmware".
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <libgen.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <termios.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#include "kwbimage.h"
-
-#ifdef __GNUC__
-#define PACKED __attribute((packed))
-#else
-#define PACKED
-#endif
-
-/*
- * Marvell BootROM UART Sensing
- */
-
-static unsigned char kwboot_msg_boot[] = {
-	0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
-};
-
-#define KWBOOT_MSG_REQ_DELAY	10 /* ms */
-#define KWBOOT_MSG_RSP_TIMEO	50 /* ms */
-
-/*
- * Xmodem Transfers
- */
-
-#define SOH	1	/* sender start of block header */
-#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 */
-
-struct kwboot_block {
-	uint8_t soh;
-	uint8_t pnum;
-	uint8_t _pnum;
-	uint8_t data[128];
-	uint8_t csum;
-} PACKED;
-
-#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
-
-static int kwboot_verbose;
-
-static void
-kwboot_printv(const char *fmt, ...)
-{
-	va_list ap;
-
-	if (kwboot_verbose) {
-		va_start(ap, fmt);
-		vprintf(fmt, ap);
-		va_end(ap);
-		fflush(stdout);
-	}
-}
-
-static void
-__spinner(void)
-{
-	const char seq[] = { '-', '\\', '|', '/' };
-	const int div = 8;
-	static int state, bs;
-
-	if (state % div == 0) {
-		fputc(bs, stdout);
-		fputc(seq[state / div % sizeof(seq)], stdout);
-		fflush(stdout);
-	}
-
-	bs = '\b';
-	state++;
-}
-
-static void
-kwboot_spinner(void)
-{
-	if (kwboot_verbose)
-		__spinner();
-}
-
-static void
-__progress(int pct, char c)
-{
-	const int width = 70;
-	static const char *nl = "";
-	static int pos;
-
-	if (pos % width == 0)
-		printf("%s%3d %% [", nl, pct);
-
-	fputc(c, stdout);
-
-	nl = "]\n";
-	pos++;
-
-	if (pct == 100) {
-		while (pos++ < width)
-			fputc(' ', stdout);
-		fputs(nl, stdout);
-	}
-
-	fflush(stdout);
-
-}
-
-static void
-kwboot_progress(int _pct, char c)
-{
-	static int pct;
-
-	if (_pct != -1)
-		pct = _pct;
-
-	if (kwboot_verbose)
-		__progress(pct, c);
-}
-
-static int
-kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
-{
-	int rc, nfds;
-	fd_set rfds;
-	struct timeval tv;
-	ssize_t n;
-
-	rc = -1;
-
-	FD_ZERO(&rfds);
-	FD_SET(fd, &rfds);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = timeo * 1000;
-	if (tv.tv_usec > 1000000) {
-		tv.tv_sec += tv.tv_usec / 1000000;
-		tv.tv_usec %= 1000000;
-	}
-
-	do {
-		nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
-		if (nfds < 0)
-			goto out;
-		if (!nfds) {
-			errno = ETIMEDOUT;
-			goto out;
-		}
-
-		n = read(fd, buf, len);
-		if (n < 0)
-			goto out;
-
-		buf = (char *)buf + n;
-		len -= n;
-	} while (len > 0);
-
-	rc = 0;
-out:
-	return rc;
-}
-
-static int
-kwboot_tty_send(int fd, const void *buf, size_t len)
-{
-	int rc;
-	ssize_t n;
-
-	rc = -1;
-
-	do {
-		n = write(fd, buf, len);
-		if (n < 0)
-			goto out;
-
-		buf = (char *)buf + n;
-		len -= n;
-	} while (len > 0);
-
-	rc = tcdrain(fd);
-out:
-	return rc;
-}
-
-static int
-kwboot_tty_send_char(int fd, unsigned char c)
-{
-	return kwboot_tty_send(fd, &c, 1);
-}
-
-static speed_t
-kwboot_tty_speed(int baudrate)
-{
-	switch (baudrate) {
-	case 115200:
-		return B115200;
-	case 57600:
-		return B57600;
-	case 38400:
-		return B38400;
-	case 19200:
-		return B19200;
-	case 9600:
-		return B9600;
-	}
-
-	return -1;
-}
-
-static int
-kwboot_open_tty(const char *path, speed_t speed)
-{
-	int rc, fd;
-	struct termios tio;
-
-	rc = -1;
-
-	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;
-
-	tio.c_cc[VMIN] = 1;
-	tio.c_cc[VTIME] = 10;
-
-	cfsetospeed(&tio, speed);
-	cfsetispeed(&tio, speed);
-
-	rc = tcsetattr(fd, TCSANOW, &tio);
-	if (rc)
-		goto out;
-
-	rc = fd;
-out:
-	if (rc < 0) {
-		if (fd >= 0)
-			close(fd);
-	}
-
-	return rc;
-}
-
-static int
-kwboot_bootmsg(int tty, void *msg)
-{
-	int rc;
-	char c;
-
-	kwboot_printv("Sending boot message. Please reboot the target...");
-
-	do {
-		rc = tcflush(tty, TCIOFLUSH);
-		if (rc)
-			break;
-
-		rc = kwboot_tty_send(tty, msg, 8);
-		if (rc) {
-			usleep(KWBOOT_MSG_REQ_DELAY * 1000);
-			continue;
-		}
-
-		rc = kwboot_tty_recv(tty, &c, 1, KWBOOT_MSG_RSP_TIMEO);
-
-		kwboot_spinner();
-
-	} while (rc || c != NAK);
-
-	kwboot_printv("\n");
-
-	return rc;
-}
-
-static int
-kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
-		    size_t size, int pnum)
-{
-	const size_t blksz = sizeof(block->data);
-	size_t n;
-	int i;
-
-	block->pnum = pnum;
-	block->_pnum = ~block->pnum;
-
-	n = size < blksz ? size : blksz;
-	memcpy(&block->data[0], data, n);
-	memset(&block->data[n], 0, blksz - n);
-
-	block->csum = 0;
-	for (i = 0; i < n; i++)
-		block->csum += block->data[i];
-
-	return n;
-}
-
-static int
-kwboot_xm_sendblock(int fd, struct kwboot_block *block)
-{
-	int rc, retries;
-	char c;
-
-	retries = 16;
-	do {
-		rc = kwboot_tty_send(fd, block, sizeof(*block));
-		if (rc)
-			break;
-
-		rc = kwboot_tty_recv(fd, &c, 1, KWBOOT_BLK_RSP_TIMEO);
-		if (rc)
-			break;
-
-		if (c != ACK)
-			kwboot_progress(-1, '+');
-
-	} while (c == NAK && retries-- > 0);
-
-	rc = -1;
-
-	switch (c) {
-	case ACK:
-		rc = 0;
-		break;
-	case NAK:
-		errno = EBADMSG;
-		break;
-	case CAN:
-		errno = ECANCELED;
-		break;
-	default:
-		errno = EPROTO;
-		break;
-	}
-
-	return rc;
-}
-
-static int
-kwboot_xmodem(int tty, const void *_data, size_t size)
-{
-	const uint8_t *data = _data;
-	int rc, pnum, N, err;
-
-	pnum = 1;
-	N = 0;
-
-	kwboot_printv("Sending boot image...\n");
-
-	do {
-		struct kwboot_block block;
-		int n;
-
-		n = kwboot_xm_makeblock(&block,
-					data + N, size - N,
-					pnum++);
-		if (n < 0)
-			goto can;
-
-		if (!n)
-			break;
-
-		rc = kwboot_xm_sendblock(tty, &block);
-		if (rc)
-			goto out;
-
-		N += n;
-		kwboot_progress(N * 100 / size, '.');
-	} while (1);
-
-	rc = kwboot_tty_send_char(tty, EOT);
-
-out:
-	return rc;
-
-can:
-	err = errno;
-	kwboot_tty_send_char(tty, CAN);
-	errno = err;
-	goto out;
-}
-
-static int
-kwboot_term_pipe(int in, int out, char *quit, int *s)
-{
-	ssize_t nin, nout;
-	char _buf[128], *buf = _buf;
-
-	nin = read(in, buf, sizeof(buf));
-	if (nin < 0)
-		return -1;
-
-	if (quit) {
-		int i;
-
-		for (i = 0; i < nin; i++) {
-			if (*buf == quit[*s]) {
-				(*s)++;
-				if (!quit[*s])
-					return 0;
-				buf++;
-				nin--;
-			} else
-				while (*s > 0) {
-					nout = write(out, quit, *s);
-					if (nout <= 0)
-						return -1;
-					(*s) -= nout;
-				}
-		}
-	}
-
-	while (nin > 0) {
-		nout = write(out, buf, nin);
-		if (nout <= 0)
-			return -1;
-		nin -= nout;
-	}
-
-	return 0;
-}
-
-static int
-kwboot_terminal(int tty)
-{
-	int rc, in, s;
-	char *quit = "\34c";
-	struct termios otio, tio;
-
-	rc = -1;
-
-	in = STDIN_FILENO;
-	if (isatty(in)) {
-		rc = tcgetattr(in, &otio);
-		if (!rc) {
-			tio = otio;
-			cfmakeraw(&tio);
-			rc = tcsetattr(in, TCSANOW, &tio);
-		}
-		if (rc) {
-			perror("tcsetattr");
-			goto out;
-		}
-
-		kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
-			      quit[0]|0100, quit[1]);
-	} else
-		in = -1;
-
-	rc = 0;
-	s = 0;
-
-	do {
-		fd_set rfds;
-		int nfds = 0;
-
-		FD_SET(tty, &rfds);
-		nfds = nfds < tty ? tty : nfds;
-
-		if (in >= 0) {
-			FD_SET(in, &rfds);
-			nfds = nfds < in ? in : nfds;
-		}
-
-		nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
-		if (nfds < 0)
-			break;
-
-		if (FD_ISSET(tty, &rfds)) {
-			rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
-			if (rc)
-				break;
-		}
-
-		if (FD_ISSET(in, &rfds)) {
-			rc = kwboot_term_pipe(in, tty, quit, &s);
-			if (rc)
-				break;
-		}
-	} while (quit[s] != 0);
-
-	tcsetattr(in, TCSANOW, &otio);
-out:
-	return rc;
-}
-
-static void *
-kwboot_mmap_image(const char *path, size_t *size, int prot)
-{
-	int rc, fd, flags;
-	struct stat st;
-	void *img;
-
-	rc = -1;
-	fd = -1;
-	img = NULL;
-
-	fd = open(path, O_RDONLY);
-	if (fd < 0)
-		goto out;
-
-	rc = fstat(fd, &st);
-	if (rc)
-		goto out;
-
-	flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
-
-	img = mmap(NULL, st.st_size, prot, flags, fd, 0);
-	if (img == MAP_FAILED) {
-		img = NULL;
-		goto out;
-	}
-
-	rc = 0;
-	*size = st.st_size;
-out:
-	if (rc && img) {
-		munmap(img, st.st_size);
-		img = NULL;
-	}
-	if (fd >= 0)
-		close(fd);
-
-	return img;
-}
-
-static uint8_t
-kwboot_img_csum8(void *_data, size_t size)
-{
-	uint8_t *data = _data, csum;
-
-	for (csum = 0; size-- > 0; data++)
-		csum += *data;
-
-	return csum;
-}
-
-static int
-kwboot_img_patch_hdr(void *img, size_t size)
-{
-	int rc;
-	bhr_t *hdr;
-	uint8_t csum;
-	const size_t hdrsz = sizeof(*hdr);
-
-	rc = -1;
-	hdr = img;
-
-	if (size < hdrsz) {
-		errno = EINVAL;
-		goto out;
-	}
-
-	csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checkSum;
-	if (csum != hdr->checkSum) {
-		errno = EINVAL;
-		goto out;
-	}
-
-	if (hdr->blockid == IBR_HDR_UART_ID) {
-		rc = 0;
-		goto out;
-	}
-
-	hdr->blockid = IBR_HDR_UART_ID;
-
-	hdr->nandeccmode = IBR_HDR_ECC_DISABLED;
-	hdr->nandpagesize = 0;
-
-	hdr->srcaddr = hdr->ext
-		? sizeof(struct kwb_header)
-		: sizeof(*hdr);
-
-	hdr->checkSum = kwboot_img_csum8(hdr, hdrsz) - csum;
-
-	rc = 0;
-out:
-	return rc;
-}
-
-static void
-kwboot_usage(FILE *stream, char *progname)
-{
-	fprintf(stream,
-		"Usage: %s -b <image> [ -p ] [ -t ] "
-		"[-B <baud> ] <TTY>\n", progname);
-	fprintf(stream, "\n");
-	fprintf(stream, "  -b <image>: boot <image>\n");
-	fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
-	fprintf(stream, "\n");
-	fprintf(stream, "  -t: mini terminal\n");
-	fprintf(stream, "\n");
-	fprintf(stream, "  -B <baud>: set baud rate\n");
-	fprintf(stream, "\n");
-}
-
-int
-main(int argc, char **argv)
-{
-	const char *ttypath, *imgpath;
-	int rv, rc, tty, term, prot, patch;
-	void *bootmsg;
-	void *img;
-	size_t size;
-	speed_t speed;
-
-	rv = 1;
-	tty = -1;
-	bootmsg = NULL;
-	imgpath = NULL;
-	img = NULL;
-	term = 0;
-	patch = 0;
-	size = 0;
-	speed = B115200;
-
-	kwboot_verbose = isatty(STDOUT_FILENO);
-
-	do {
-		int c = getopt(argc, argv, "hb:ptB:");
-		if (c < 0)
-			break;
-
-		switch (c) {
-		case 'b':
-			bootmsg = kwboot_msg_boot;
-			imgpath = optarg;
-			break;
-
-		case 'p':
-			patch = 1;
-			break;
-
-		case 't':
-			term = 1;
-			break;
-
-		case 'B':
-			speed = kwboot_tty_speed(atoi(optarg));
-			if (speed == -1)
-				goto usage;
-			break;
-
-		case 'h':
-			rv = 0;
-		default:
-			goto usage;
-		}
-	} while (1);
-
-	if (!bootmsg && !term)
-		goto usage;
-
-	if (patch && !imgpath)
-		goto usage;
-
-	if (argc - optind < 1)
-		goto usage;
-
-	ttypath = argv[optind++];
-
-	tty = kwboot_open_tty(ttypath, speed);
-	if (tty < 0) {
-		perror(ttypath);
-		goto out;
-	}
-
-	if (imgpath) {
-		prot = PROT_READ | (patch ? PROT_WRITE : 0);
-
-		img = kwboot_mmap_image(imgpath, &size, prot);
-		if (!img) {
-			perror(imgpath);
-			goto out;
-		}
-	}
-
-	if (patch) {
-		rc = kwboot_img_patch_hdr(img, size);
-		if (rc) {
-			fprintf(stderr, "%s: Invalid image.\n", imgpath);
-			goto out;
-		}
-	}
-
-	if (bootmsg) {
-		rc = kwboot_bootmsg(tty, bootmsg);
-		if (rc) {
-			perror("bootmsg");
-			goto out;
-		}
-	}
-
-	if (img) {
-		rc = kwboot_xmodem(tty, img, size);
-		if (rc) {
-			perror("xmodem");
-			goto out;
-		}
-	}
-
-	if (term) {
-		rc = kwboot_terminal(tty);
-		if (rc && !(errno == EINTR)) {
-			perror("terminal");
-			goto out;
-		}
-	}
-
-	rv = 0;
-out:
-	if (tty >= 0)
-		close(tty);
-
-	if (img)
-		munmap(img, size);
-
-	return rv;
-
-usage:
-	kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
-	goto out;
-}
diff --git a/tools/mvboot.c b/tools/mvboot.c
new file mode 100644
index 0000000..94fb0dd
--- /dev/null
+++ b/tools/mvboot.c
@@ -0,0 +1,742 @@ 
+/*
+ * Boot a Marvell Kirkwood SoC, with Xmodem over UART0.
+ *
+ * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
+ *
+ * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
+ *   Integrated Controller: Functional Specifications" December 2,
+ *   2008. Chapter 24.2 "BootROM Firmware".
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "kwbimage.h"
+
+#ifdef __GNUC__
+#define PACKED __attribute((packed))
+#else
+#define PACKED
+#endif
+
+/*
+ * Marvell BootROM UART Sensing
+ */
+
+static unsigned char mvboot_msg_boot[] = {
+	0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
+};
+
+#define MVBOOT_MSG_REQ_DELAY	10 /* ms */
+#define MVBOOT_MSG_RSP_TIMEO	50 /* ms */
+
+/*
+ * Xmodem Transfers
+ */
+
+#define SOH	1	/* sender start of block header */
+#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 */
+
+struct mvboot_block {
+	uint8_t soh;
+	uint8_t pnum;
+	uint8_t _pnum;
+	uint8_t data[128];
+	uint8_t csum;
+} PACKED;
+
+#define MVBOOT_BLK_RSP_TIMEO 1000 /* ms */
+
+static int mvboot_verbose;
+
+static void
+mvboot_printv(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (mvboot_verbose) {
+		va_start(ap, fmt);
+		vprintf(fmt, ap);
+		va_end(ap);
+		fflush(stdout);
+	}
+}
+
+static void
+__spinner(void)
+{
+	const char seq[] = { '-', '\\', '|', '/' };
+	const int div = 8;
+	static int state, bs;
+
+	if (state % div == 0) {
+		fputc(bs, stdout);
+		fputc(seq[state / div % sizeof(seq)], stdout);
+		fflush(stdout);
+	}
+
+	bs = '\b';
+	state++;
+}
+
+static void
+mvboot_spinner(void)
+{
+	if (mvboot_verbose)
+		__spinner();
+}
+
+static void
+__progress(int pct, char c)
+{
+	const int width = 70;
+	static const char *nl = "";
+	static int pos;
+
+	if (pos % width == 0)
+		printf("%s%3d %% [", nl, pct);
+
+	fputc(c, stdout);
+
+	nl = "]\n";
+	pos++;
+
+	if (pct == 100) {
+		while (pos++ < width)
+			fputc(' ', stdout);
+		fputs(nl, stdout);
+	}
+
+	fflush(stdout);
+
+}
+
+static void
+mvboot_progress(int _pct, char c)
+{
+	static int pct;
+
+	if (_pct != -1)
+		pct = _pct;
+
+	if (mvboot_verbose)
+		__progress(pct, c);
+}
+
+static int
+mvboot_tty_recv(int fd, void *buf, size_t len, int timeo)
+{
+	int rc, nfds;
+	fd_set rfds;
+	struct timeval tv;
+	ssize_t n;
+
+	rc = -1;
+
+	FD_ZERO(&rfds);
+	FD_SET(fd, &rfds);
+
+	tv.tv_sec = 0;
+	tv.tv_usec = timeo * 1000;
+	if (tv.tv_usec > 1000000) {
+		tv.tv_sec += tv.tv_usec / 1000000;
+		tv.tv_usec %= 1000000;
+	}
+
+	do {
+		nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
+		if (nfds < 0)
+			goto out;
+		if (!nfds) {
+			errno = ETIMEDOUT;
+			goto out;
+		}
+
+		n = read(fd, buf, len);
+		if (n < 0)
+			goto out;
+
+		buf = (char *)buf + n;
+		len -= n;
+	} while (len > 0);
+
+	rc = 0;
+out:
+	return rc;
+}
+
+static int
+mvboot_tty_send(int fd, const void *buf, size_t len)
+{
+	int rc;
+	ssize_t n;
+
+	rc = -1;
+
+	do {
+		n = write(fd, buf, len);
+		if (n < 0)
+			goto out;
+
+		buf = (char *)buf + n;
+		len -= n;
+	} while (len > 0);
+
+	rc = tcdrain(fd);
+out:
+	return rc;
+}
+
+static int
+mvboot_tty_send_char(int fd, unsigned char c)
+{
+	return mvboot_tty_send(fd, &c, 1);
+}
+
+static speed_t
+mvboot_tty_speed(int baudrate)
+{
+	switch (baudrate) {
+	case 115200:
+		return B115200;
+	case 57600:
+		return B57600;
+	case 38400:
+		return B38400;
+	case 19200:
+		return B19200;
+	case 9600:
+		return B9600;
+	}
+
+	return -1;
+}
+
+static int
+mvboot_open_tty(const char *path, speed_t speed)
+{
+	int rc, fd;
+	struct termios tio;
+
+	rc = -1;
+
+	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;
+
+	tio.c_cc[VMIN] = 1;
+	tio.c_cc[VTIME] = 10;
+
+	cfsetospeed(&tio, speed);
+	cfsetispeed(&tio, speed);
+
+	rc = tcsetattr(fd, TCSANOW, &tio);
+	if (rc)
+		goto out;
+
+	rc = fd;
+out:
+	if (rc < 0) {
+		if (fd >= 0)
+			close(fd);
+	}
+
+	return rc;
+}
+
+static int
+mvboot_bootmsg(int tty, void *msg)
+{
+	int rc;
+	char c;
+
+	mvboot_printv("Sending boot message. Please reboot the target...");
+
+	do {
+		rc = tcflush(tty, TCIOFLUSH);
+		if (rc)
+			break;
+
+		rc = mvboot_tty_send(tty, msg, 8);
+		if (rc) {
+			usleep(MVBOOT_MSG_REQ_DELAY * 1000);
+			continue;
+		}
+
+		rc = mvboot_tty_recv(tty, &c, 1, MVBOOT_MSG_RSP_TIMEO);
+
+		mvboot_spinner();
+
+	} while (rc || c != NAK);
+
+	mvboot_printv("\n");
+
+	return rc;
+}
+
+static int
+mvboot_xm_makeblock(struct mvboot_block *block, const void *data,
+		    size_t size, int pnum)
+{
+	const size_t blksz = sizeof(block->data);
+	size_t n;
+	int i;
+
+	block->pnum = pnum;
+	block->_pnum = ~block->pnum;
+
+	n = size < blksz ? size : blksz;
+	memcpy(&block->data[0], data, n);
+	memset(&block->data[n], 0, blksz - n);
+
+	block->csum = 0;
+	for (i = 0; i < n; i++)
+		block->csum += block->data[i];
+
+	return n;
+}
+
+static int
+mvboot_xm_sendblock(int fd, struct mvboot_block *block)
+{
+	int rc, retries;
+	char c;
+
+	retries = 16;
+	do {
+		rc = mvboot_tty_send(fd, block, sizeof(*block));
+		if (rc)
+			break;
+
+		rc = mvboot_tty_recv(fd, &c, 1, MVBOOT_BLK_RSP_TIMEO);
+		if (rc)
+			break;
+
+		if (c != ACK)
+			mvboot_progress(-1, '+');
+
+	} while (c == NAK && retries-- > 0);
+
+	rc = -1;
+
+	switch (c) {
+	case ACK:
+		rc = 0;
+		break;
+	case NAK:
+		errno = EBADMSG;
+		break;
+	case CAN:
+		errno = ECANCELED;
+		break;
+	default:
+		errno = EPROTO;
+		break;
+	}
+
+	return rc;
+}
+
+static int
+mvboot_xmodem(int tty, const void *_data, size_t size)
+{
+	const uint8_t *data = _data;
+	int rc, pnum, N, err;
+
+	pnum = 1;
+	N = 0;
+
+	mvboot_printv("Sending boot image...\n");
+
+	do {
+		struct mvboot_block block;
+		int n;
+
+		n = mvboot_xm_makeblock(&block,
+					data + N, size - N,
+					pnum++);
+		if (n < 0)
+			goto can;
+
+		if (!n)
+			break;
+
+		rc = mvboot_xm_sendblock(tty, &block);
+		if (rc)
+			goto out;
+
+		N += n;
+		mvboot_progress(N * 100 / size, '.');
+	} while (1);
+
+	rc = mvboot_tty_send_char(tty, EOT);
+
+out:
+	return rc;
+
+can:
+	err = errno;
+	mvboot_tty_send_char(tty, CAN);
+	errno = err;
+	goto out;
+}
+
+static int
+mvboot_term_pipe(int in, int out, char *quit, int *s)
+{
+	ssize_t nin, nout;
+	char _buf[128], *buf = _buf;
+
+	nin = read(in, buf, sizeof(buf));
+	if (nin < 0)
+		return -1;
+
+	if (quit) {
+		int i;
+
+		for (i = 0; i < nin; i++) {
+			if (*buf == quit[*s]) {
+				(*s)++;
+				if (!quit[*s])
+					return 0;
+				buf++;
+				nin--;
+			} else
+				while (*s > 0) {
+					nout = write(out, quit, *s);
+					if (nout <= 0)
+						return -1;
+					(*s) -= nout;
+				}
+		}
+	}
+
+	while (nin > 0) {
+		nout = write(out, buf, nin);
+		if (nout <= 0)
+			return -1;
+		nin -= nout;
+	}
+
+	return 0;
+}
+
+static int
+mvboot_terminal(int tty)
+{
+	int rc, in, s;
+	char *quit = "\34c";
+	struct termios otio, tio;
+
+	rc = -1;
+
+	in = STDIN_FILENO;
+	if (isatty(in)) {
+		rc = tcgetattr(in, &otio);
+		if (!rc) {
+			tio = otio;
+			cfmakeraw(&tio);
+			rc = tcsetattr(in, TCSANOW, &tio);
+		}
+		if (rc) {
+			perror("tcsetattr");
+			goto out;
+		}
+
+		mvboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
+			      quit[0]|0100, quit[1]);
+	} else
+		in = -1;
+
+	rc = 0;
+	s = 0;
+
+	do {
+		fd_set rfds;
+		int nfds = 0;
+
+		FD_SET(tty, &rfds);
+		nfds = nfds < tty ? tty : nfds;
+
+		if (in >= 0) {
+			FD_SET(in, &rfds);
+			nfds = nfds < in ? in : nfds;
+		}
+
+		nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
+		if (nfds < 0)
+			break;
+
+		if (FD_ISSET(tty, &rfds)) {
+			rc = mvboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
+			if (rc)
+				break;
+		}
+
+		if (FD_ISSET(in, &rfds)) {
+			rc = mvboot_term_pipe(in, tty, quit, &s);
+			if (rc)
+				break;
+		}
+	} while (quit[s] != 0);
+
+	tcsetattr(in, TCSANOW, &otio);
+out:
+	return rc;
+}
+
+static void *
+mvboot_mmap_image(const char *path, size_t *size, int prot)
+{
+	int rc, fd, flags;
+	struct stat st;
+	void *img;
+
+	rc = -1;
+	fd = -1;
+	img = NULL;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		goto out;
+
+	rc = fstat(fd, &st);
+	if (rc)
+		goto out;
+
+	flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
+
+	img = mmap(NULL, st.st_size, prot, flags, fd, 0);
+	if (img == MAP_FAILED) {
+		img = NULL;
+		goto out;
+	}
+
+	rc = 0;
+	*size = st.st_size;
+out:
+	if (rc && img) {
+		munmap(img, st.st_size);
+		img = NULL;
+	}
+	if (fd >= 0)
+		close(fd);
+
+	return img;
+}
+
+static uint8_t
+mvboot_img_csum8(void *_data, size_t size)
+{
+	uint8_t *data = _data, csum;
+
+	for (csum = 0; size-- > 0; data++)
+		csum += *data;
+
+	return csum;
+}
+
+static int
+mvboot_img_patch_hdr(void *img, size_t size)
+{
+	int rc;
+	bhr_t *hdr;
+	uint8_t csum;
+	const size_t hdrsz = sizeof(*hdr);
+
+	rc = -1;
+	hdr = img;
+
+	if (size < hdrsz) {
+		errno = EINVAL;
+		goto out;
+	}
+
+	csum = mvboot_img_csum8(hdr, hdrsz) - hdr->checkSum;
+	if (csum != hdr->checkSum) {
+		errno = EINVAL;
+		goto out;
+	}
+
+	if (hdr->blockid == IBR_HDR_UART_ID) {
+		rc = 0;
+		goto out;
+	}
+
+	hdr->blockid = IBR_HDR_UART_ID;
+
+	hdr->nandeccmode = IBR_HDR_ECC_DISABLED;
+	hdr->nandpagesize = 0;
+
+	hdr->srcaddr = hdr->ext
+		? sizeof(struct kwb_header)
+		: sizeof(*hdr);
+
+	hdr->checkSum = mvboot_img_csum8(hdr, hdrsz) - csum;
+
+	rc = 0;
+out:
+	return rc;
+}
+
+static void
+mvboot_usage(FILE *stream, char *progname)
+{
+	fprintf(stream,
+		"Usage: %s -b <image> [ -p ] [ -t ] "
+		"[-B <baud> ] <TTY>\n", progname);
+	fprintf(stream, "\n");
+	fprintf(stream, "  -b <image>: boot <image>\n");
+	fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
+	fprintf(stream, "\n");
+	fprintf(stream, "  -t: mini terminal\n");
+	fprintf(stream, "\n");
+	fprintf(stream, "  -B <baud>: set baud rate\n");
+	fprintf(stream, "\n");
+}
+
+int
+main(int argc, char **argv)
+{
+	const char *ttypath, *imgpath;
+	int rv, rc, tty, term, prot, patch;
+	void *bootmsg;
+	void *img;
+	size_t size;
+	speed_t speed;
+
+	rv = 1;
+	tty = -1;
+	bootmsg = NULL;
+	imgpath = NULL;
+	img = NULL;
+	term = 0;
+	patch = 0;
+	size = 0;
+	speed = B115200;
+
+	mvboot_verbose = isatty(STDOUT_FILENO);
+
+	do {
+		int c = getopt(argc, argv, "hb:ptB:");
+		if (c < 0)
+			break;
+
+		switch (c) {
+		case 'b':
+			bootmsg = mvboot_msg_boot;
+			imgpath = optarg;
+			break;
+
+		case 'p':
+			patch = 1;
+			break;
+
+		case 't':
+			term = 1;
+			break;
+
+		case 'B':
+			speed = mvboot_tty_speed(atoi(optarg));
+			if (speed == -1)
+				goto usage;
+			break;
+
+		case 'h':
+			rv = 0;
+		default:
+			goto usage;
+		}
+	} while (1);
+
+	if (!bootmsg && !term)
+		goto usage;
+
+	if (patch && !imgpath)
+		goto usage;
+
+	if (argc - optind < 1)
+		goto usage;
+
+	ttypath = argv[optind++];
+
+	tty = mvboot_open_tty(ttypath, speed);
+	if (tty < 0) {
+		perror(ttypath);
+		goto out;
+	}
+
+	if (imgpath) {
+		prot = PROT_READ | (patch ? PROT_WRITE : 0);
+
+		img = mvboot_mmap_image(imgpath, &size, prot);
+		if (!img) {
+			perror(imgpath);
+			goto out;
+		}
+	}
+
+	if (patch) {
+		rc = mvboot_img_patch_hdr(img, size);
+		if (rc) {
+			fprintf(stderr, "%s: Invalid image.\n", imgpath);
+			goto out;
+		}
+	}
+
+	if (bootmsg) {
+		rc = mvboot_bootmsg(tty, bootmsg);
+		if (rc) {
+			perror("bootmsg");
+			goto out;
+		}
+	}
+
+	if (img) {
+		rc = mvboot_xmodem(tty, img, size);
+		if (rc) {
+			perror("xmodem");
+			goto out;
+		}
+	}
+
+	if (term) {
+		rc = mvboot_terminal(tty);
+		if (rc && !(errno == EINTR)) {
+			perror("terminal");
+			goto out;
+		}
+	}
+
+	rv = 0;
+out:
+	if (tty >= 0)
+		close(tty);
+
+	if (img)
+		munmap(img, size);
+
+	return rv;
+
+usage:
+	mvboot_usage(rv ? stderr : stdout, basename(argv[0]));
+	goto out;
+}