[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