diff options
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | board/xen/xenguest_arm64/xenguest_arm64.c | 31 | ||||
-rw-r--r-- | configs/xenguest_arm64_defconfig | 2 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 7 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_xen.c | 182 | ||||
-rw-r--r-- | drivers/xen/events.c | 4 |
7 files changed, 220 insertions, 8 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bee626f..0c1b754 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1755,6 +1755,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 1d1e229..e3903bb 100644 --- a/board/xen/xenguest_arm64/xenguest_arm64.c +++ b/board/xen/xenguest_arm64/xenguest_arm64.c @@ -17,9 +17,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) @@ -56,9 +59,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) { @@ -66,7 +88,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; @@ -145,8 +167,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 f730799..45559a1 100644 --- a/configs/xenguest_arm64_defconfig +++ b/configs/xenguest_arm64_defconfig @@ -49,7 +49,7 @@ CONFIG_CMD_UMS=n 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 e146ffc..e344677 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -822,6 +822,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. + choice prompt "Console port" default 8xx_CONS_SMC1 diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index e4a92bb..25f7f8d 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 0000000..ba6504b --- /dev/null +++ b/drivers/serial/serial_xen.c @@ -0,0 +1,182 @@ +// 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 - Structure representing a Xen UART info + * @intf: Console I/O interface for Xen guest OSes + * @evtchn: Console event channel + */ +struct xen_uart_priv { + struct xencons_interface *intf; + u32 evtchn; +}; + +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 val = 0; + unsigned long gfn; + int ret; + + ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val); + if (ret < 0 || val == 0) + return ret; + + priv->evtchn = val; + console_evtchn = val; + + ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val); + if (ret < 0) + return ret; + + if (!val) + return -EINVAL; + + gfn = val; + priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT); + + 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; + + 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 */ + + 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_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) + .flags = DM_FLAG_PRE_RELOC, +#endif +}; + diff --git a/drivers/xen/events.c b/drivers/xen/events.c index b4c8481..beaccde 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -23,6 +23,8 @@ #include <xen/events.h> #include <xen/hvm.h> +extern u32 console_evtchn; + #define NR_EVS 1024 /** @@ -49,6 +51,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); |