[PATCH v3 16/29] serial: Add semihosting driver
Simon Glass
sjg at chromium.org
Mon Mar 28 08:35:11 CEST 2022
Hi Sean,
On Tue, 22 Mar 2022 at 15:00, Sean Anderson <sean.anderson at seco.com> wrote:
>
> This adds a serial driver which uses semihosting calls to read and write
> to the host's console. For convenience, if CONFIG_DM_SERIAL is enabled,
> we will instantiate a serial driver. This allows users to enable this
> driver (which has no physical device) without modifying their device
> trees or board files. We also implement a non-DM driver for SPL, or for
> much faster output in U-Boot proper.
>
> There are three ways to print to the console:
>
> Method Baud
> ================== =====
> smh_putc in a loop 170
> smh_puts 1600
> smh_write with :tt 20000
> ================== =====
>
> These speeds were measured using a 175 character message with a J-Link
> adapter. For reference, U-Boot typically prints around 2700 characters
> during boot on this board. There are two major factors affecting the
> speed of these functions. First, each breakpoint incurs a delay. Second,
> each debugger memory transaction incurs a delay. smh_putc has a
> breakpoint and memory transaction for every character. smh_puts has one
> breakpoint, but still has to use a transaction for every character. This
> is because we don't know the length up front, so OpenOCD has to check if
> each character is nul. smh_write has only one breakpoint and one memory
> transfer.
>
> DM serial drivers can only implement a putc interface, so we are stuck
> with the slowest API. Non-DM drivers can implement puts, which is vastly
> more efficient. When the driver starts up, we try to open :tt. Since
> this is an extension, this may fail. If it does, we fall back to
> smh_puts. We don't check :semihosting-features, since there are
> nonconforming implementations (OpenOCD) which don't implement it (but
> *do* implement :tt).
>
> Some semihosting implementations (QEMU) don't handle READC properly. To
> work around this, we try to use open/read (much like for stdin) if
> possible.
>
> There is no non-blocking I/O available, so we don't implement pending.
> This will cause __serial_tstc to always return true. If
> CONFIG_SERIAL_RX_BUFFER is enabled, _serial_tstc will try and read
> characters forever. To avoid this, we depend on this config being
> disabled.
>
> Signed-off-by: Sean Anderson <sean.anderson at seco.com>
> ---
>
> (no changes since v2)
>
> Changes in v2:
> - Fix baud numbers being off by 10
> - Fix typos in commit message
> - Rename non-DM driver struct to match format of other drivers
>
> drivers/serial/Kconfig | 22 +++++
> drivers/serial/Makefile | 1 +
> drivers/serial/serial.c | 2 +
> drivers/serial/serial_semihosting.c | 147 ++++++++++++++++++++++++++++
> include/serial.h | 1 +
> 5 files changed, 173 insertions(+)
> create mode 100644 drivers/serial/serial_semihosting.c
>
> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index 345d1881f5..cc20759505 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -399,6 +399,15 @@ config DEBUG_UART_SANDBOX
> start up driver model. The driver will be available until the real
> driver model serial is running.
>
> +config DEBUG_UART_SEMIHOSTING
> + bool "semihosting"
> + depends on SEMIHOSTING_SERIAL
> + help
> + Select this to enable the debug UART using the semihosting driver.
> + This provides basic serial output from the console without needing to
> + start up driver model. The driver will be available until the real
> + driver model serial is running.
> +
> config DEBUG_UART_SIFIVE
> bool "SiFive UART"
> depends on SIFIVE_SERIAL
> @@ -778,6 +787,19 @@ config SCIF_CONSOLE
> on systems with RCar or SH SoCs, say Y to this option. If unsure,
> say N.
>
> +config SEMIHOSTING_SERIAL
> + bool "Semihosting UART support"
> + depends on SEMIHOSTING && !SERIAL_RX_BUFFER
> + help
> + Select this to enable a serial UART using semihosting. Special halt
> + instructions will be issued which an external debugger (such as a
> + JTAG emulator) may interpret. The debugger will display U-Boot's
> + console output on the host system.
> +
> + Enable this option only if you are using a debugger which supports
> + semihosting. If you are not using a debugger, this driver will halt
> + the boot.
> +
> config UNIPHIER_SERIAL
> bool "Support for UniPhier on-chip UART"
> depends on ARCH_UNIPHIER
> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
> index 52e70aa191..b68b5e7b2b 100644
> --- a/drivers/serial/Makefile
> +++ b/drivers/serial/Makefile
> @@ -52,6 +52,7 @@ endif
> obj-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o
> obj-$(CONFIG_SANDBOX_SERIAL) += sandbox.o
> obj-$(CONFIG_SCIF_CONSOLE) += serial_sh.o
> +obj-$(CONFIG_SEMIHOSTING_SERIAL) += serial_semihosting.o
> obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o
> obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o
> obj-$(CONFIG_FSL_LINFLEXUART) += serial_linflexuart.o
> diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
> index ebbd21916d..6cdbb89841 100644
> --- a/drivers/serial/serial.c
> +++ b/drivers/serial/serial.c
> @@ -126,6 +126,7 @@ serial_initfunc(mxc_serial_initialize);
> serial_initfunc(ns16550_serial_initialize);
> serial_initfunc(pl01x_serial_initialize);
> serial_initfunc(pxa_serial_initialize);
> +serial_initfunc(smh_serial_initialize);
> serial_initfunc(sh_serial_initialize);
> serial_initfunc(mtk_serial_initialize);
>
> @@ -180,6 +181,7 @@ int serial_initialize(void)
> ns16550_serial_initialize();
> pl01x_serial_initialize();
> pxa_serial_initialize();
> + smh_serial_initialize();
> sh_serial_initialize();
> mtk_serial_initialize();
>
> diff --git a/drivers/serial/serial_semihosting.c b/drivers/serial/serial_semihosting.c
> new file mode 100644
> index 0000000000..7c7c5d9455
> --- /dev/null
> +++ b/drivers/serial/serial_semihosting.c
> @@ -0,0 +1,147 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson at seco.com>
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <serial.h>
> +#include <semihosting.h>
> +
> +/**
> + * struct smh_serial_priv - Semihosting serial private data
> + * @infd: stdin file descriptor (or error)
> + */
> +struct smh_serial_priv {
> + int infd;
> + int outfd;
> +};
> +
> +#if CONFIG_IS_ENABLED(DM_SERIAL)
> +static int smh_serial_getc(struct udevice *dev)
> +{
> + char ch = 0;
> + struct smh_serial_priv *priv = dev_get_priv(dev);
> +
> + if (priv->infd < 0)
> + return smh_getc();
> +
> + smh_read(priv->infd, &ch, sizeof(ch));
> + return ch;
> +}
> +
> +static int smh_serial_putc(struct udevice *dev, const char ch)
> +{
> + smh_putc(ch);
> + return 0;
> +}
> +
> +static const struct dm_serial_ops smh_serial_ops = {
> + .putc = smh_serial_putc,
> + .getc = smh_serial_getc,
> +};
> +
> +static int smh_serial_probe(struct udevice *dev)
> +{
> + struct smh_serial_priv *priv = dev_get_priv(dev);
> +
> + priv->infd = smh_open(":tt", MODE_READ);
> + return 0;
> +}
> +
> +U_BOOT_DRIVER(smh_serial) = {
> + .name = "serial_semihosting",
> + .id = UCLASS_SERIAL,
> + .probe = smh_serial_probe,
> + .priv_auto = sizeof(struct smh_serial_priv),
> + .ops = &smh_serial_ops,
> + .flags = DM_FLAG_PRE_RELOC,
> +};
> +
> +U_BOOT_DRVINFO(smh_serial) = {
> + .name = "serial_semihosting",
> +};
> +#else /* DM_SERIAL */
> +static int infd = -ENODEV;
> +static int outfd = -ENODEV;
> +
> +static int smh_serial_start(void)
> +{
> + infd = smh_open(":tt", MODE_READ);
> + outfd = smh_open(":tt", MODE_WRITE);
> + return 0;
> +}
> +
> +static int smh_serial_stop(void)
> +{
> + if (outfd >= 0)
> + smh_close(outfd);
> + return 0;
> +}
> +
> +static void smh_serial_setbrg(void)
> +{
> +}
> +
> +static int smh_serial_getc(void)
> +{
> + char ch = 0;
> +
> + if (infd < 0)
> + return smh_getc();
> +
> + smh_read(infd, &ch, sizeof(ch));
> + return ch;
> +}
> +
> +static int smh_serial_tstc(void)
> +{
> + return 1;
> +}
> +
> +static void smh_serial_puts(const char *s)
> +{
> + ulong unused;
> +
> + if (outfd < 0)
> + smh_puts(s);
> + else
> + smh_write(outfd, s, strlen(s), &unused);
> +}
> +
> +struct serial_device serial_smh_device = {
> + .name = "serial_smh",
> + .start = smh_serial_start,
> + .stop = smh_serial_stop,
> + .setbrg = smh_serial_setbrg,
> + .getc = smh_serial_getc,
> + .tstc = smh_serial_tstc,
> + .putc = smh_putc,
> + .puts = smh_serial_puts,
> +};
> +
> +void smh_serial_initialize(void)
> +{
> + serial_register(&serial_smh_device);
> +}
> +
> +__weak struct serial_device *default_serial_console(void)
> +{
> + return &serial_smh_device;
> +}
> +#endif
> +
> +#ifdef CONFIG_DEBUG_UART_SEMIHOSTING
> +#include <debug_uart.h>
> +
> +static inline void _debug_uart_init(void)
> +{
> +}
> +
> +static inline void _debug_uart_putc(int c)
> +{
> + smh_putc(c);
> +}
> +
> +DEBUG_UART_FUNCS
> +#endif
> diff --git a/include/serial.h b/include/serial.h
> index 19a8c0c67d..2681d26c82 100644
> --- a/include/serial.h
> +++ b/include/serial.h
> @@ -23,6 +23,7 @@ struct serial_device {
> void default_serial_puts(const char *s);
>
> extern struct serial_device serial_smc_device;
> +extern struct serial_device serial_smh_device;
> extern struct serial_device serial_scc_device;
> extern struct serial_device *default_serial_console(void);
>
> --
> 2.25.1
>
More information about the U-Boot
mailing list