[PATCH 07/17] serial: serial_xen: Add Xen PV serial driver
Simon Glass
sjg at chromium.org
Fri Jul 3 05:50:27 CEST 2020
Hi Anastasiia,
On Wed, 1 Jul 2020 at 10:30, Anastasiia Lukianenko <vicooodin at gmail.com> wrote:
>
> From: Peng Fan <peng.fan at nxp.com>
>
> Add support for Xen para-virtualized serial driver. This
> driver fully supports serial console for the virtual machine.
>
> Please note that as the driver is initialized late, so no banner
> nor memory size is visible.
>
> Signed-off-by: Peng Fan <peng.fan at nxp.com>
> Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko at epam.com>
> Signed-off-by: Anastasiia Lukianenko <anastasiia_lukianenko at epam.com>
> ---
> arch/arm/Kconfig | 1 +
> board/xen/xenguest_arm64/xenguest_arm64.c | 31 +++-
> configs/xenguest_arm64_defconfig | 4 +-
> drivers/serial/Kconfig | 7 +
> drivers/serial/Makefile | 1 +
> drivers/serial/serial_xen.c | 175 ++++++++++++++++++++++
> drivers/xen/events.c | 4 +
> 7 files changed, 214 insertions(+), 9 deletions(-)
> create mode 100644 drivers/serial/serial_xen.c
Reviewed-by: Simon Glass <sjg at chromium.org>
nits below
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index c469863967..d4de1139aa 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1723,6 +1723,7 @@ config TARGET_XENGUEST_ARM64
> select XEN
> select OF_CONTROL
> select LINUX_KERNEL_IMAGE_HEADER
> + select XEN_SERIAL
> endchoice
>
> config ARCH_SUPPORT_TFABOOT
> diff --git a/board/xen/xenguest_arm64/xenguest_arm64.c b/board/xen/xenguest_arm64/xenguest_arm64.c
> index 9e099f388f..fd10a002e9 100644
> --- a/board/xen/xenguest_arm64/xenguest_arm64.c
> +++ b/board/xen/xenguest_arm64/xenguest_arm64.c
> @@ -18,9 +18,12 @@
> #include <asm/armv8/mmu.h>
> #include <asm/xen.h>
> #include <asm/xen/hypercall.h>
> +#include <asm/xen/system.h>
>
> #include <linux/compiler.h>
>
> +#include <xen/hvm.h>
> +
> DECLARE_GLOBAL_DATA_PTR;
>
> int board_init(void)
> @@ -57,9 +60,28 @@ static int get_next_memory_node(const void *blob, int mem)
>
> static int setup_mem_map(void)
> {
> - int i, ret, mem, reg = 0;
> + int i = 0, ret, mem, reg = 0;
> struct fdt_resource res;
> const void *blob = gd->fdt_blob;
> + u64 gfn;
> +
> + /*
> + * Add "magic" region which is used by Xen to provide some essentials
> + * for the guest: we need console.
> + */
> + ret = hvm_get_parameter_maintain_dcache(HVM_PARAM_CONSOLE_PFN, &gfn);
> + if (ret < 0) {
> + printf("%s: Can't get HVM_PARAM_CONSOLE_PFN, ret %d\n",
> + __func__, ret);
> + return -EINVAL;
> + }
> +
> + xen_mem_map[i].virt = PFN_PHYS(gfn);
> + xen_mem_map[i].phys = PFN_PHYS(gfn);
> + xen_mem_map[i].size = PAGE_SIZE;
> + xen_mem_map[i].attrs = (PTE_BLOCK_MEMTYPE(MT_NORMAL) |
> + PTE_BLOCK_INNER_SHARE);
> + i++;
>
> mem = get_next_memory_node(blob, -1);
> if (mem < 0) {
> @@ -67,7 +89,7 @@ static int setup_mem_map(void)
> return -EINVAL;
> }
>
> - for (i = 0; i < MAX_MEM_MAP_REGIONS; i++) {
> + for (; i < MAX_MEM_MAP_REGIONS; i++) {
> ret = fdt_get_resource(blob, mem, "reg", reg++, &res);
> if (ret == -FDT_ERR_NOTFOUND) {
> reg = 0;
> @@ -146,8 +168,3 @@ int print_cpuinfo(void)
> return 0;
> }
>
> -__weak struct serial_device *default_serial_console(void)
> -{
> - return NULL;
> -}
> -
> diff --git a/configs/xenguest_arm64_defconfig b/configs/xenguest_arm64_defconfig
> index 2a8caf8647..45559a161b 100644
> --- a/configs/xenguest_arm64_defconfig
> +++ b/configs/xenguest_arm64_defconfig
> @@ -47,9 +47,9 @@ CONFIG_CMD_UMS=n
> #CONFIG_EFI_PARTITION=y
> # CONFIG_EFI_LOADER is not set
>
> -# CONFIG_DM is not set
> +CONFIG_DM=y
> # CONFIG_MMC is not set
> -# CONFIG_DM_SERIAL is not set
> +CONFIG_DM_SERIAL=y
> # CONFIG_REQUIRE_SERIAL_CONSOLE is not set
>
> CONFIG_OF_BOARD=y
> diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
> index 17d0e73623..33c989a66d 100644
> --- a/drivers/serial/Kconfig
> +++ b/drivers/serial/Kconfig
> @@ -821,6 +821,13 @@ config MPC8XX_CONS
> depends on MPC8xx
> default y
>
> +config XEN_SERIAL
> + bool "XEN serial support"
> + depends on XEN
> + help
> + If built without DM support, then requires Xen
> + to be built with CONFIG_VERBOSE_DEBUG.
Yes but what does it do? Also should probably not support non-DM at this point.
> +
> choice
> prompt "Console port"
> default 8xx_CONS_SMC1
> diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
> index e4a92bbbb7..25f7f8d342 100644
> --- a/drivers/serial/Makefile
> +++ b/drivers/serial/Makefile
> @@ -70,6 +70,7 @@ obj-$(CONFIG_OWL_SERIAL) += serial_owl.o
> obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o
> obj-$(CONFIG_MTK_SERIAL) += serial_mtk.o
> obj-$(CONFIG_SIFIVE_SERIAL) += serial_sifive.o
> +obj-$(CONFIG_XEN_SERIAL) += serial_xen.o
>
> ifndef CONFIG_SPL_BUILD
> obj-$(CONFIG_USB_TTY) += usbtty.o
> diff --git a/drivers/serial/serial_xen.c b/drivers/serial/serial_xen.c
> new file mode 100644
> index 0000000000..dcd4b2df79
> --- /dev/null
> +++ b/drivers/serial/serial_xen.c
> @@ -0,0 +1,175 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0+
> + *
> + * (C) 2018 NXP
> + * (C) 2020 EPAM Systems Inc.
> + */
> +#include <common.h>
> +#include <cpu_func.h>
> +#include <dm.h>
> +#include <serial.h>
> +#include <watchdog.h>
> +
> +#include <linux/bug.h>
> +
> +#include <xen/hvm.h>
> +#include <xen/events.h>
> +
> +#include <xen/interface/sched.h>
> +#include <xen/interface/hvm/hvm_op.h>
> +#include <xen/interface/hvm/params.h>
> +#include <xen/interface/io/console.h>
> +#include <xen/interface/io/ring.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +u32 console_evtchn;
> +
> +struct xen_uart_priv {
> + struct xencons_interface *intf;
> + u32 evtchn;
> + int vtermno;
> + struct hvc_struct *hvc;
comment for struct
> +};
> +
> +int xen_serial_setbrg(struct udevice *dev, int baudrate)
> +{
> + return 0;
> +}
> +
> +static int xen_serial_probe(struct udevice *dev)
> +{
> + struct xen_uart_priv *priv = dev_get_priv(dev);
> + u64 v = 0;
> + unsigned long gfn;
> + int r;
> +
> + r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
Can you use ret and val instead of single-char var names? It is OK for
loops, but not here.
> + if (r < 0 || v == 0)
> + return r;
> +
> + priv->evtchn = v;
> + console_evtchn = v;
> +
> + r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);
> + if (r < 0 || v == 0)
> + return -ENODEV;
return r if non-zero
return -EINVAL perhaps or -ENXIO if !v
-ENODEV means there is no device and is reserved for driver model.
Clearly in this case there is a device.
> +
> + gfn = v;
> +
> + priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
> + if (!priv->intf)
Don't you already check for !v above?
> + return -EINVAL;
Blank line
> + return 0;
> +}
> +
> +static int xen_serial_pending(struct udevice *dev, bool input)
> +{
> + struct xen_uart_priv *priv = dev_get_priv(dev);
> + struct xencons_interface *intf = priv->intf;
> +
> + if (!input || intf->in_cons == intf->in_prod)
> + return 0;
blank line before final return. Please fix globally
> + return 1;
> +}
> +
> +static int xen_serial_getc(struct udevice *dev)
> +{
> + struct xen_uart_priv *priv = dev_get_priv(dev);
> + struct xencons_interface *intf = priv->intf;
> + XENCONS_RING_IDX cons;
> + char c;
> +
> + while (intf->in_cons == intf->in_prod) {
> + mb(); /* wait */
> + }
Drop {}. Has this been through patman?
> +
> + cons = intf->in_cons;
> + mb(); /* get pointers before reading ring */
> +
> + c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
> +
> + mb(); /* read ring before consuming */
> + intf->in_cons = cons;
> +
> + notify_remote_via_evtchn(priv->evtchn);
> + return c;
> +}
> +
> +static int __write_console(struct udevice *dev, const char *data, int len)
> +{
> + struct xen_uart_priv *priv = dev_get_priv(dev);
> + struct xencons_interface *intf = priv->intf;
> + XENCONS_RING_IDX cons, prod;
> + int sent = 0;
> +
> + cons = intf->out_cons;
> + prod = intf->out_prod;
> + mb(); /* Update pointer */
> +
> + WARN_ON((prod - cons) > sizeof(intf->out));
> +
> + while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
> + intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
> +
> + mb(); /* Update data before pointer */
> + intf->out_prod = prod;
> +
> + if (sent)
> + notify_remote_via_evtchn(priv->evtchn);
> + return sent;
> +}
> +
> +static int write_console(struct udevice *dev, const char *data, int len)
> +{
> + /*
> + * Make sure the whole buffer is emitted, polling if
> + * necessary. We don't ever want to rely on the hvc daemon
> + * because the most interesting console output is when the
> + * kernel is crippled.
> + */
> + while (len) {
> + int sent = __write_console(dev, data, len);
> +
> + data += sent;
> + len -= sent;
> +
> + if (unlikely(len))
> + HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
> + }
> + return 0;
> +}
> +
> +static int xen_serial_putc(struct udevice *dev, const char ch)
> +{
> + write_console(dev, &ch, 1);
> + return 0;
> +}
> +
> +static const struct dm_serial_ops xen_serial_ops = {
> + .putc = xen_serial_putc,
> + .getc = xen_serial_getc,
> + .pending = xen_serial_pending,
> +};
> +
> +#if CONFIG_IS_ENABLED(OF_CONTROL)
> +static const struct udevice_id xen_serial_ids[] = {
> + { .compatible = "xen,xen" },
> + { }
> +};
> +#endif
> +
> +U_BOOT_DRIVER(serial_xen) = {
> + .name = "serial_xen",
> + .id = UCLASS_SERIAL,
> +#if CONFIG_IS_ENABLED(OF_CONTROL)
of_patch_ptr() - but I think you can drop this
> + .of_match = xen_serial_ids,
> +#endif
> + .priv_auto_alloc_size = sizeof(struct xen_uart_priv),
> + .probe = xen_serial_probe,
> + .ops = &xen_serial_ops,
> +#if !CONFIG_IS_ENABLED(OF_CONTROL)
and this?
> + .flags = DM_FLAG_PRE_RELOC,
> +#endif
> +};
> +
> diff --git a/drivers/xen/events.c b/drivers/xen/events.c
> index eddc6b6e29..a1b36a2196 100644
> --- a/drivers/xen/events.c
> +++ b/drivers/xen/events.c
> @@ -25,6 +25,8 @@
> #include <xen/events.h>
> #include <xen/hvm.h>
>
> +extern u32 console_evtchn;
> +
> #define NR_EVS 1024
>
> /* this represents a event handler. Chaining or sharing is not allowed */
> @@ -47,6 +49,8 @@ void unbind_all_ports(void)
> struct vcpu_info *vcpu_info = &s->vcpu_info[cpu];
>
> for (i = 0; i < NR_EVS; i++) {
> + if (i == console_evtchn)
> + continue;
> if (test_and_clear_bit(i, bound_ports)) {
> printf("port %d still bound!\n", i);
> unbind_evtchn(i);
> --
> 2.17.1
>
Regards,
Simon
More information about the U-Boot
mailing list