- kwboot higher baudrate (Pali & Marek)
This commit is contained in:
Tom Rini 2021-10-01 09:15:48 -04:00
commit 570320da3b
6 changed files with 1404 additions and 319 deletions

View file

@ -775,6 +775,16 @@ S: Maintained
T: git https://source.denx.de/u-boot/custodians/u-boot-i2c.git
F: drivers/i2c/
KWBIMAGE / KWBOOT TOOLS
M: Pali Rohár <pali@kernel.org>
M: Marek Behún <marek.behun@nic.cz>
M: Stefan Roese <sr@denx.de>
S: Maintained
T: git https://source.denx.de/u-boot/custodians/u-boot-marvell.git
F: doc/README.kwbimage
F: doc/kwboot.1
F: tools/kwb*
LOGGING
M: Simon Glass <sjg@chromium.org>
S: Maintained

View file

@ -1,21 +1,22 @@
.TH KWBOOT 1 "2012-05-19"
.TH KWBOOT 1 "2021-08-25"
.SH NAME
kwboot \- Boot Marvell Kirkwood SoCs over a serial link.
kwboot \- Boot Marvell Kirkwood (and others 32-bit) 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
The \fBkwboot\fP program boots boards based on Marvell's 32-bit
platforms including Kirkwood, Dove, A370, AXP, A375, A38x
and A39x 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.
(\fIkwbimage v0\fP or \fIv1\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
@ -36,25 +37,23 @@ by the second-stage loader.
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.
must be of type "UART boot" (0x69). The \fBkwboot\fP program changes
this type automatically, unless the \fIimage\fP is signed, in which
case it cannot be changed.
This mode writes handshake status and upload progress indication to
stdout.
stdout. It is possible that \fIimage\fP contains an optional binary
code in it's header which may also print some output via UART (for
example U-Boot SPL does this). In such a case, this output is also
written to stdout after the header is sent.
.TP
.BI "\-p"
In combination with \fB-b\fP, patches the header in \fIimage\fP prior
to upload, to "UART boot" type.
Obsolete. Does nothing.
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.
In the past, when this option was used, the program patched the header
in the image prior upload, to "UART boot" type. This is now done by
default.
.TP
.BI "\-t"
@ -65,11 +64,26 @@ 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.
after receiving \fBctrl-\e\fP followed by \fBc\fP from console input.
.TP
.BI "\-B \fIbaudrate\fP"
Adjust the baud rate on \fITTY\fP. Default rate is 115200.
If used in combination with \fB-b\fP, inject into the image header
code that changes baud rate to \fIbaudrate\fP after uploading image
header, and code that changes the baud rate back to the default
(115200 Bd) before executing payload, and also adjust the baud rate
on \fITTY\fP correspondingly. This can make the upload significantly
faster.
If used in combination with \fB-t\fP, adjust the baud rate to
\fIbaudrate\fP on \fITTY\fP before starting terminal.
If both \fB-b\fP and \fB-t\fP are used, the baud rate is changed
back to 115200 after the upload.
Tested values for \fIbaudrate\fP for Armada 38x include: 115200,
230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000,
2000000, 2500000, 3125000, 4000000 and 5200000.
.SH "SEE ALSO"
.PP
@ -82,3 +96,7 @@ Daniel Stodden <daniel.stodden@gmail.com>
Luka Perkov <luka@openwrt.org>
.br
David Purdy <david.c.purdy@gmail.com>
.br
Pali Rohár <pali@kernel.org>
.br
Marek Behún <marek.behun@nic.cz>

View file

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Image manipulator for Marvell SoCs
* supports Kirkwood, Dove, Armada 370, Armada XP, and Armada 38x
* supports Kirkwood, Dove, Armada 370, Armada XP, Armada 375, Armada 38x and
* Armada 39x
*
* (C) Copyright 2013 Thomas Petazzoni
* <thomas.petazzoni@free-electrons.com>
@ -280,14 +281,6 @@ static uint8_t image_checksum8(void *start, uint32_t len)
return csum;
}
size_t kwbimage_header_size(unsigned char *ptr)
{
if (image_version((void *)ptr) == 0)
return sizeof(struct main_hdr_v0);
else
return KWBHEADER_V1_SIZE((struct main_hdr_v1 *)ptr);
}
/*
* Verify checksum over a complete header that includes the checksum field.
* Return 1 when OK, otherwise 0.
@ -298,7 +291,7 @@ static int main_hdr_checksum_ok(void *hdr)
struct main_hdr_v0 *main_hdr = (struct main_hdr_v0 *)hdr;
uint8_t checksum;
checksum = image_checksum8(hdr, kwbimage_header_size(hdr));
checksum = image_checksum8(hdr, kwbheader_size_for_csum(hdr));
/* Calculated checksum includes the header checksum field. Compensate
* for that.
*/
@ -542,7 +535,7 @@ static int kwb_export_pubkey(RSA *key, struct pubkey_der_v1 *dst, FILE *hashf,
}
if (4 + size_seq > sizeof(dst->key)) {
fprintf(stderr, "export pk failed: seq too large (%d, %lu)\n",
fprintf(stderr, "export pk failed: seq too large (%d, %zu)\n",
4 + size_seq, sizeof(dst->key));
fprintf(stderr, errmsg, keyname);
return -ENOBUFS;
@ -1618,34 +1611,20 @@ static void kwbimage_set_header(void *ptr, struct stat *sbuf, int ifd,
static void kwbimage_print_header(const void *ptr)
{
struct main_hdr_v0 *mhdr = (struct main_hdr_v0 *)ptr;
struct opt_hdr_v1 *ohdr;
printf("Image Type: MVEBU Boot from %s Image\n",
image_boot_mode_name(mhdr->blockid));
printf("Image version:%d\n", image_version((void *)ptr));
if (image_version((void *)ptr) == 1) {
struct main_hdr_v1 *mhdr = (struct main_hdr_v1 *)ptr;
printf("Image version:%d\n", kwbimage_version(ptr));
if (mhdr->ext & 0x1) {
struct opt_hdr_v1 *ohdr = (struct opt_hdr_v1 *)
((uint8_t *)ptr +
sizeof(*mhdr));
while (1) {
uint32_t ohdr_size;
ohdr_size = (ohdr->headersz_msb << 16) |
le16_to_cpu(ohdr->headersz_lsb);
if (ohdr->headertype == OPT_HDR_V1_BINARY_TYPE) {
printf("BIN Hdr Size: ");
genimg_print_size(ohdr_size - 12 - 4 * ohdr->data[0]);
}
if (!(*((uint8_t *)ohdr + ohdr_size - 4) & 0x1))
break;
ohdr = (struct opt_hdr_v1 *)((uint8_t *)ohdr +
ohdr_size);
}
for_each_opt_hdr_v1 (ohdr, mhdr) {
if (ohdr->headertype == OPT_HDR_V1_BINARY_TYPE) {
printf("BIN Hdr Size: ");
genimg_print_size(opt_hdr_v1_size(ohdr) - 12 -
4 * ohdr->data[0]);
}
}
printf("Data Size: ");
genimg_print_size(mhdr->blocksize - sizeof(uint32_t));
printf("Load Address: %08x\n", mhdr->destaddr);
@ -1663,8 +1642,8 @@ static int kwbimage_check_image_types(uint8_t type)
static int kwbimage_verify_header(unsigned char *ptr, int image_size,
struct image_tool_params *params)
{
uint8_t checksum;
size_t header_size = kwbimage_header_size(ptr);
size_t header_size = kwbheader_size(ptr);
uint8_t csum;
if (header_size > image_size)
return -FDT_ERR_BADSTRUCTURE;
@ -1673,52 +1652,27 @@ static int kwbimage_verify_header(unsigned char *ptr, int image_size,
return -FDT_ERR_BADSTRUCTURE;
/* Only version 0 extended header has checksum */
if (image_version((void *)ptr) == 0) {
if (kwbimage_version(ptr) == 0) {
struct main_hdr_v0 *mhdr = (struct main_hdr_v0 *)ptr;
if (mhdr->ext & 0x1) {
struct ext_hdr_v0 *ext_hdr;
struct ext_hdr_v0 *ext_hdr = (void *)(mhdr + 1);
if (header_size + sizeof(*ext_hdr) > image_size)
return -FDT_ERR_BADSTRUCTURE;
ext_hdr = (struct ext_hdr_v0 *)
(ptr + sizeof(struct main_hdr_v0));
checksum = image_checksum8(ext_hdr,
sizeof(struct ext_hdr_v0)
- sizeof(uint8_t));
if (checksum != ext_hdr->checksum)
csum = image_checksum8(ext_hdr, sizeof(*ext_hdr) - 1);
if (csum != ext_hdr->checksum)
return -FDT_ERR_BADSTRUCTURE;
}
} else if (image_version((void *)ptr) == 1) {
} else if (kwbimage_version(ptr) == 1) {
struct main_hdr_v1 *mhdr = (struct main_hdr_v1 *)ptr;
const uint8_t *mhdr_end;
struct opt_hdr_v1 *ohdr;
uint32_t offset;
uint32_t size;
if (mhdr->ext & 0x1) {
uint32_t ohdr_size;
struct opt_hdr_v1 *ohdr = (struct opt_hdr_v1 *)
(ptr + sizeof(*mhdr));
while (1) {
if ((uint8_t *)ohdr + sizeof(*ohdr) >
(uint8_t *)mhdr + header_size)
return -FDT_ERR_BADSTRUCTURE;
ohdr_size = (ohdr->headersz_msb << 16) |
le16_to_cpu(ohdr->headersz_lsb);
if (ohdr_size < 8 ||
(uint8_t *)ohdr + ohdr_size >
(uint8_t *)mhdr + header_size)
return -FDT_ERR_BADSTRUCTURE;
if (!(*((uint8_t *)ohdr + ohdr_size - 4) & 0x1))
break;
ohdr = (struct opt_hdr_v1 *)((uint8_t *)ohdr +
ohdr_size);
}
}
mhdr_end = (uint8_t *)mhdr + header_size;
for_each_opt_hdr_v1 (ohdr, ptr)
if (!opt_hdr_v1_valid_size(ohdr, mhdr_end))
return -FDT_ERR_BADSTRUCTURE;
offset = le32_to_cpu(mhdr->srcaddr);
@ -1864,37 +1818,25 @@ static int kwbimage_generate(struct image_tool_params *params,
static int kwbimage_extract_subimage(void *ptr, struct image_tool_params *params)
{
struct main_hdr_v1 *mhdr = (struct main_hdr_v1 *)ptr;
size_t header_size = kwbimage_header_size(ptr);
size_t header_size = kwbheader_size(ptr);
struct opt_hdr_v1 *ohdr;
int idx = params->pflag;
int cur_idx = 0;
uint32_t offset;
ulong image;
ulong size;
if (image_version((void *)ptr) == 1 && (mhdr->ext & 0x1)) {
struct opt_hdr_v1 *ohdr = (struct opt_hdr_v1 *)
((uint8_t *)ptr +
sizeof(*mhdr));
for_each_opt_hdr_v1 (ohdr, ptr) {
if (ohdr->headertype != OPT_HDR_V1_BINARY_TYPE)
continue;
while (1) {
uint32_t ohdr_size = (ohdr->headersz_msb << 16) |
le16_to_cpu(ohdr->headersz_lsb);
if (ohdr->headertype == OPT_HDR_V1_BINARY_TYPE) {
if (idx == cur_idx) {
image = (ulong)&ohdr->data[4 +
4 * ohdr->data[0]];
size = ohdr_size - 12 -
4 * ohdr->data[0];
goto extract;
}
++cur_idx;
}
if (!(*((uint8_t *)ohdr + ohdr_size - 4) & 0x1))
break;
ohdr = (struct opt_hdr_v1 *)((uint8_t *)ohdr +
ohdr_size);
if (idx == cur_idx) {
image = (ulong)&ohdr->data[4 + 4 * ohdr->data[0]];
size = opt_hdr_v1_size(ohdr) - 12 - 4 * ohdr->data[0];
goto extract;
}
++cur_idx;
}
if (idx != cur_idx) {

View file

@ -69,12 +69,7 @@ struct ext_hdr_v0 {
uint8_t checksum;
} __packed;
struct kwb_header {
struct main_hdr_v0 kwb_hdr;
struct ext_hdr_v0 kwb_exthdr;
} __packed;
/* Structure of the main header, version 1 (Armada 370/38x/XP) */
/* Structure of the main header, version 1 (Armada 370/XP/375/38x/39x) */
struct main_hdr_v1 {
uint8_t blockid; /* 0x0 */
uint8_t flags; /* 0x1 */
@ -108,7 +103,7 @@ struct main_hdr_v1 {
#define MAIN_HDR_V1_OPT_BAUD_115200 0x7
/*
* Header for the optional headers, version 1 (Armada 370, Armada XP)
* Header for the optional headers, version 1 (Armada 370/XP/375/38x/39x)
*/
struct opt_hdr_v1 {
uint8_t headertype;
@ -132,7 +127,7 @@ struct sig_v1 {
} __packed;
/*
* Structure of secure header (Armada 38x)
* Structure of secure header (Armada XP/375/38x/39x)
*/
struct secure_hdr_v1 {
uint8_t headertype; /* 0x0 */
@ -195,9 +190,6 @@ struct register_set_hdr_v1 {
#define OPT_HDR_V1_BINARY_TYPE 0x2
#define OPT_HDR_V1_REGISTER_TYPE 0x3
#define KWBHEADER_V1_SIZE(hdr) \
(((hdr)->headersz_msb << 16) | le16_to_cpu((hdr)->headersz_lsb))
enum kwbimage_cmd {
CMD_INVALID,
CMD_BOOT_FROM,
@ -225,10 +217,91 @@ void init_kwb_image_type (void);
* header, byte 8 was reserved, and always set to 0. In the v1 header,
* byte 8 has been changed to a proper field, set to 1.
*/
static inline unsigned int image_version(void *header)
static inline unsigned int kwbimage_version(const void *header)
{
unsigned char *ptr = header;
const unsigned char *ptr = header;
return ptr[8];
}
static inline size_t kwbheader_size(const void *header)
{
if (kwbimage_version(header) == 0) {
const struct main_hdr_v0 *hdr = header;
return sizeof(*hdr) +
(hdr->ext & 0x1) ? sizeof(struct ext_hdr_v0) : 0;
} else {
const struct main_hdr_v1 *hdr = header;
return (hdr->headersz_msb << 16) |
le16_to_cpu(hdr->headersz_lsb);
}
}
static inline size_t kwbheader_size_for_csum(const void *header)
{
if (kwbimage_version(header) == 0)
return sizeof(struct main_hdr_v0);
else
return kwbheader_size(header);
}
static inline uint32_t opt_hdr_v1_size(const struct opt_hdr_v1 *ohdr)
{
return (ohdr->headersz_msb << 16) | le16_to_cpu(ohdr->headersz_lsb);
}
static inline int opt_hdr_v1_valid_size(const struct opt_hdr_v1 *ohdr,
const void *mhdr_end)
{
uint32_t ohdr_size;
if ((void *)(ohdr + 1) > mhdr_end)
return 0;
ohdr_size = opt_hdr_v1_size(ohdr);
if (ohdr_size < 8 || (void *)((uint8_t *)ohdr + ohdr_size) > mhdr_end)
return 0;
return 1;
}
static inline struct opt_hdr_v1 *opt_hdr_v1_first(void *img) {
struct main_hdr_v1 *mhdr;
if (kwbimage_version(img) != 1)
return NULL;
mhdr = img;
if (mhdr->ext & 0x1)
return (struct opt_hdr_v1 *)(mhdr + 1);
else
return NULL;
}
static inline uint8_t *opt_hdr_v1_ext(struct opt_hdr_v1 *cur)
{
uint32_t size = opt_hdr_v1_size(cur);
return (uint8_t *)cur + size - 4;
}
static inline struct opt_hdr_v1 *_opt_hdr_v1_next(struct opt_hdr_v1 *cur)
{
return (struct opt_hdr_v1 *)((uint8_t *)cur + opt_hdr_v1_size(cur));
}
static inline struct opt_hdr_v1 *opt_hdr_v1_next(struct opt_hdr_v1 *cur)
{
if (*opt_hdr_v1_ext(cur) & 0x1)
return _opt_hdr_v1_next(cur);
else
return NULL;
}
#define for_each_opt_hdr_v1(ohdr, img) \
for ((ohdr) = opt_hdr_v1_first((img)); \
(ohdr) != NULL; \
(ohdr) = opt_hdr_v1_next((ohdr)))
#endif /* _KWBIMAGE_H_ */

File diff suppressed because it is too large Load diff

189
tools/termios_linux.h Normal file
View file

@ -0,0 +1,189 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* termios fuctions to support arbitrary baudrates (on Linux)
*
* Copyright (c) 2021 Pali Rohár <pali@kernel.org>
* Copyright (c) 2021 Marek Behún <marek.behun@nic.cz>
*/
#ifndef _TERMIOS_LINUX_H_
#define _TERMIOS_LINUX_H_
/*
* We need to use raw TCGETS2/TCSETS2 or TCGETS/TCSETS ioctls with the BOTHER
* flag in struct termios2/termios, defined in Linux headers <asm/ioctls.h>
* (included by <sys/ioctl.h>) and <asm/termbits.h>. Since these headers
* conflict with glibc's header file <termios.h>, it is not possible to use
* libc's termios functions and we need to reimplement them via ioctl() calls.
*
* An arbitrary baudrate is supported when the macro BOTHER is defined. The
* baudrate value itself is then stored into the c_ospeed and c_ispeed members.
* If ioctls TCGETS2/TCSETS2 are defined and supported then these fields are
* present in struct termios2, otherwise these fields are present in struct
* termios.
*
* Note that the Bnnn constants from <termios.h> need not be compatible with Bnnn
* constants from <asm/termbits.h>.
*/
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <asm/termbits.h>
#if defined(BOTHER) && defined(TCGETS2)
#define termios termios2
#endif
static inline int tcgetattr(int fd, struct termios *t)
{
#if defined(BOTHER) && defined(TCGETS2)
return ioctl(fd, TCGETS2, t);
#else
return ioctl(fd, TCGETS, t);
#endif
}
static inline int tcsetattr(int fd, int a, const struct termios *t)
{
int cmd;
switch (a) {
#if defined(BOTHER) && defined(TCGETS2)
case TCSANOW:
cmd = TCSETS2;
break;
case TCSADRAIN:
cmd = TCSETSW2;
break;
case TCSAFLUSH:
cmd = TCSETSF2;
break;
#else
case TCSANOW:
cmd = TCSETS;
break;
case TCSADRAIN:
cmd = TCSETSW;
break;
case TCSAFLUSH:
cmd = TCSETSF;
break;
#endif
default:
errno = EINVAL;
return -1;
}
return ioctl(fd, cmd, t);
}
static inline int tcdrain(int fd)
{
return ioctl(fd, TCSBRK, 1);
}
static inline int tcflush(int fd, int q)
{
return ioctl(fd, TCFLSH, q);
}
static inline int tcsendbreak(int fd, int d)
{
return ioctl(fd, TCSBRK, d);
}
static inline int tcflow(int fd, int a)
{
return ioctl(fd, TCXONC, a);
}
static inline pid_t tcgetsid(int fd)
{
pid_t sid;
if (ioctl(fd, TIOCGSID, &sid) < 0)
return (pid_t)-1;
return sid;
}
static inline speed_t cfgetospeed(const struct termios *t)
{
return t->c_cflag & CBAUD;
}
static inline int cfsetospeed(struct termios *t, speed_t s)
{
if (s & ~CBAUD) {
errno = EINVAL;
return -1;
}
t->c_cflag &= ~CBAUD;
t->c_cflag |= s;
return 0;
}
#ifdef IBSHIFT
static inline speed_t cfgetispeed(const struct termios *t)
{
speed_t s = (t->c_cflag >> IBSHIFT) & CBAUD;
if (s == B0)
return cfgetospeed(t);
else
return s;
}
static inline int cfsetispeed(struct termios *t, speed_t s)
{
if (s == 0)
s = B0;
if (s & ~CBAUD) {
errno = EINVAL;
return -1;
}
t->c_cflag &= ~(CBAUD << IBSHIFT);
t->c_cflag |= s << IBSHIFT;
return 0;
}
#else /* !IBSHIFT */
static inline speed_t cfgetispeed(const struct termios *t)
{
return cfgetospeed(t);
}
static inline int cfsetispeed(struct termios *t, speed_t s)
{
return cfsetospeed(t, s);
}
#endif /* !IBSHIFT */
static inline int cfsetspeed(struct termios *t, speed_t s)
{
if (cfsetospeed(t, s))
return -1;
#ifdef IBSHIFT
if (cfsetispeed(t, s))
return -1;
#endif
return 0;
}
static void cfmakeraw(struct termios *t)
{
t->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
ICRNL | IXON);
t->c_oflag &= ~OPOST;
t->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
t->c_cflag &= ~(CSIZE | PARENB);
t->c_cflag |= CS8;
}
#endif /* _TERMIOS_LINUX_H_ */