From d9a3efb834b07a0083b1e434b19271dc9c25dadc Mon Sep 17 00:00:00 2001 From: Dmytro Firsov Date: Fri, 2 Jul 2021 11:31:56 +0300 Subject: [PATCH] xenvm: drivers: serial: Implement serial interface to Xen PV console This commit adds minimal support of Xen hypervisor console via UART-like driver. Implementation allows to use poll_in/poll_out char interface for uart_console.c driver directly to HV console instead of using Xen virtual PL011 UART. Future implementation will support interrupt driven interface on Xen event channels, currently it is under development. Also this commit introduces early console_io Xen interface, which allows to receive printk/stdout messages quickly after start, but requires Xen, built with CONFIG_DEBUG option. Signed-off-by: Dmytro Firsov --- CODEOWNERS | 8 +- drivers/CMakeLists.txt | 1 + drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.xen | 30 ++++++ drivers/serial/uart_hvc_xen.c | 172 ++++++++++++++++++++++++++++++++++ drivers/xen/CMakeLists.txt | 5 + drivers/xen/events.c | 25 +++++ drivers/xen/hvm.c | 39 ++++++++ include/xen/console.h | 23 +++++ include/xen/events.h | 13 +++ include/xen/generic.h | 14 +++ include/xen/hvm.h | 17 ++++ 13 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 drivers/serial/Kconfig.xen create mode 100644 drivers/serial/uart_hvc_xen.c create mode 100644 drivers/xen/CMakeLists.txt create mode 100644 drivers/xen/events.c create mode 100644 drivers/xen/hvm.c create mode 100644 include/xen/console.h create mode 100644 include/xen/events.h create mode 100644 include/xen/generic.h create mode 100644 include/xen/hvm.h diff --git a/CODEOWNERS b/CODEOWNERS index d8b28facda9..ca2a6d1c31a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -24,6 +24,7 @@ /arch/arm/core/aarch32/cortex_a_r/ @MaureenHelm @galak @ioannisg @bbolen @stephanosio /arch/arm64/ @carlocaione /arch/arm64/core/cortex_r/ @povergoing +/arch/arm64/core/xen/ @lorc @firscity /arch/common/ @ioannisg @andyross /soc/arc/snps_*/ @abrodkin @ruuddw @evgeniy-paltsev /soc/nios2/ @nashif @@ -60,7 +61,7 @@ /soc/arm64/qemu_cortex_a53/ @carlocaione /soc/arm64/bcm_vk/ @abhishek-brcm /soc/arm64/nxp_layerscape/ @JiafeiPan -/soc/arm64/xenvm/ @lorc +/soc/arm64/xenvm/ @lorc @firscity /soc/arm64/arm/ @povergoing /soc/arm64/arm/fvp_aemv8a/ @carlocaione /submanifests/* @mbolivar-nordic @@ -166,7 +167,7 @@ /boards/arm64/qemu_cortex_a53/ @carlocaione /boards/arm64/bcm958402m2_a72/ @abhishek-brcm /boards/arm64/nxp_ls1046ardb/ @JiafeiPan -/boards/arm64/xenvm/ @lorc +/boards/arm64/xenvm/ @lorc @firscity /boards/arm64/fvp_baser_aemv8r/ @povergoing /boards/arm64/fvp_base_revc_2xaemv8a/ @carlocaione /boards/arm64/intel_socfpga_agilex_socdk/ @siclim @ngboonkhai @@ -343,6 +344,8 @@ /drivers/serial/serial_test.c @str4t0m /drivers/serial/*esp32c3* @uLipe /drivers/serial/*esp32s2* @glaubermaroto +/drivers/serial/Kconfig.xen @lorc @firscity +/drivers/serial/uart_hvc_xen.c @lorc @firscity /drivers/disk/ @jfischer-no /drivers/disk/sdmmc_sdhc.h @JunYangNXP /drivers/disk/sdmmc_spi.c @JunYangNXP @@ -389,6 +392,7 @@ /drivers/wifi/eswifi/ @loicpoulain @nandojve /drivers/wifi/winc1500/ @kludentwo /drivers/virtualization/ @tbursztyka +/drivers/xen/ @lorc @firscity /dts/arc/ @abrodkin @ruuddw @iriszzw @evgeniy-paltsev /dts/arm/acsip/ @NorthernDean /dts/arm/atmel/sam4e* @nandojve diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 9837eb9f81d..528d45b70c9 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -63,3 +63,4 @@ add_subdirectory_ifdef(CONFIG_BBRAM bbram) add_subdirectory_ifdef(CONFIG_FPGA fpga) add_subdirectory_ifdef(CONFIG_PINCTRL pinctrl) add_subdirectory_ifdef(CONFIG_MBOX mbox) +add_subdirectory_ifdef(CONFIG_BOARD_XENVM xen) diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index 5bfc1bf927a..576492a1e04 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -45,6 +45,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_RCAR uart_rcar.c) zephyr_library_sources_ifdef(CONFIG_UART_XEC uart_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_UART_NEORV32 uart_neorv32.c) zephyr_library_sources_ifdef(CONFIG_USART_GD32 usart_gd32.c) +zephyr_library_sources_ifdef(CONFIG_UART_XEN_HVC uart_hvc_xen.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 475c17f2e88..569b7ed2f25 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -171,4 +171,6 @@ source "drivers/serial/Kconfig.test" source "drivers/serial/Kconfig.neorv32" +source "drivers/serial/Kconfig.xen" + endif # SERIAL diff --git a/drivers/serial/Kconfig.xen b/drivers/serial/Kconfig.xen new file mode 100644 index 00000000000..e95ddb7c458 --- /dev/null +++ b/drivers/serial/Kconfig.xen @@ -0,0 +1,30 @@ +# Xen hypervisor console via UART setup +# +# Copyright (c) 2021 EPAM Systems +# SPDX-License-Identifier: Apache-2.0 +# + +config UART_XEN_HVC + bool "Xen hypervisor console UART Driver" + select SERIAL_HAS_DRIVER + depends on BOARD_XENVM + default y + help + Enable Xen hypervisor console driver. + +config XEN_HVC_INIT_PRIORITY + int "Xen hypervisor console init priority" + depends on UART_XEN_HVC + default 55 + help + Set init priority for Xen HVC, should be inited before UART + console driver (HVC gets inited on PRE_KERNEL_1 stage). + +config XEN_EARLY_CONSOLEIO + bool "Early printk/stdout through console_io Xen interface" + depends on BOARD_XENVM + default n + help + Enable setting of console_io symbol hook for stdout and printk. + Log output will become available on PRE_KERNEL_1 stage. Requires + Xen, compiled with CONFIG_DEBUG flag. diff --git a/drivers/serial/uart_hvc_xen.c b/drivers/serial/uart_hvc_xen.c new file mode 100644 index 00000000000..9b0004bc792 --- /dev/null +++ b/drivers/serial/uart_hvc_xen.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(uart_hvc_xen); + +static struct hvc_xen_data hvc_data = {0}; + +static int read_from_ring(const struct device *dev, char *str, int len) +{ + int recv = 0; + struct hvc_xen_data *hvc_data = dev->data; + XENCONS_RING_IDX cons = hvc_data->intf->in_cons; + XENCONS_RING_IDX prod = hvc_data->intf->in_prod; + XENCONS_RING_IDX in_idx = 0; + + compiler_barrier(); + __ASSERT((prod - cons) <= sizeof(hvc_data->intf->in), + "Invalid input ring buffer"); + + while (cons != prod && recv < len) { + in_idx = MASK_XENCONS_IDX(cons, hvc_data->intf->in); + str[recv] = hvc_data->intf->in[in_idx]; + recv++; + cons++; + } + + compiler_barrier(); + hvc_data->intf->in_cons = cons; + + notify_evtchn(hvc_data->evtchn); + return recv; +} + +static int write_to_ring(const struct device *dev, const char *str, int len) +{ + int sent = 0; + struct hvc_xen_data *hvc_data = dev->data; + XENCONS_RING_IDX cons = hvc_data->intf->out_cons; + XENCONS_RING_IDX prod = hvc_data->intf->out_prod; + XENCONS_RING_IDX out_idx = 0; + + compiler_barrier(); + __ASSERT((prod - cons) <= sizeof(hvc_data->intf->out), + "Invalid output ring buffer"); + + while ((sent < len) && ((prod - cons) < sizeof(hvc_data->intf->out))) { + out_idx = MASK_XENCONS_IDX(prod, hvc_data->intf->out); + hvc_data->intf->out[out_idx] = str[sent]; + prod++; + sent++; + } + + compiler_barrier(); + hvc_data->intf->out_prod = prod; + + if (sent) { + notify_evtchn(hvc_data->evtchn); + } + + return sent; +} + +static int xen_hvc_poll_in(const struct device *dev, + unsigned char *c) +{ + int ret = 0; + char temp; + + ret = read_from_ring(dev, &temp, sizeof(temp)); + if (!ret) { + /* Char was not received */ + return -1; + } + + *c = temp; + return 0; +} + +static void xen_hvc_poll_out(const struct device *dev, + unsigned char c) +{ + /* Not a good solution (notifying HV every time), but needed for poll_out */ + (void) write_to_ring(dev, &c, sizeof(c)); +} + +static const struct uart_driver_api xen_hvc_api = { + .poll_in = xen_hvc_poll_in, + .poll_out = xen_hvc_poll_out, +}; + +int xen_console_init(const struct device *dev) +{ + int ret = 0; + uint64_t console_pfn = 0; + uintptr_t console_addr = 0; + struct hvc_xen_data *data = dev->data; + + data->dev = dev; + + ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &data->evtchn); + if (ret) { + LOG_ERR("%s: failed to get Xen console evtchn, ret = %d\n", + __func__, ret); + return ret; + } + + ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &console_pfn); + if (ret) { + LOG_ERR("%s: failed to get Xen console PFN, ret = %d\n", + __func__, ret); + return ret; + } + + console_addr = (uintptr_t) (console_pfn << XEN_PAGE_SHIFT); + device_map(DEVICE_MMIO_RAM_PTR(dev), console_addr, XEN_PAGE_SIZE, + K_MEM_CACHE_WB); + + data->intf = (struct xencons_interface *) DEVICE_MMIO_GET(dev); + + LOG_INF("Xen HVC inited successfully\n"); + + return 0; +} + +DEVICE_DT_DEFINE(DT_NODELABEL(xen_hvc), xen_console_init, NULL, &hvc_data, + NULL, PRE_KERNEL_1, CONFIG_XEN_HVC_INIT_PRIORITY, + &xen_hvc_api); + +#ifdef CONFIG_XEN_EARLY_CONSOLEIO +extern void __printk_hook_install(int (*fn)(int)); +extern void __stdout_hook_install(int (*fn)(int)); + +int xen_consoleio_putc(int c) +{ + char symbol = (char) c; + + HYPERVISOR_console_io(CONSOLEIO_write, sizeof(symbol), &symbol); + return c; +} + + + +int consoleio_hooks_set(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Will be replaced with poll_in/poll_out by uart_console.c later on boot */ + __stdout_hook_install(xen_consoleio_putc); + __printk_hook_install(xen_consoleio_putc); + + return 0; +} + +SYS_INIT(consoleio_hooks_set, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#endif /* CONFIG_XEN_EARLY_CONSOLEIO */ diff --git a/drivers/xen/CMakeLists.txt b/drivers/xen/CMakeLists.txt new file mode 100644 index 00000000000..2060ee25e15 --- /dev/null +++ b/drivers/xen/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2021 EPAM Systems + +zephyr_sources(hvm.c) +zephyr_sources(events.c) diff --git a/drivers/xen/events.c b/drivers/xen/events.c new file mode 100644 index 00000000000..897a2b89628 --- /dev/null +++ b/drivers/xen/events.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +void notify_evtchn(evtchn_port_t port) +{ + struct evtchn_send send; + + if (port >= EVTCHN_2L_NR_CHANNELS) { + printk("%s: trying to send notify for invalid evtchn #%u\n", + __func__, port); + return; + } + + send.port = port; + + HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); +} diff --git a/drivers/xen/hvm.c b/drivers/xen/hvm.c new file mode 100644 index 00000000000..80895a84ef9 --- /dev/null +++ b/drivers/xen/hvm.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include + +int hvm_set_parameter(int idx, uint64_t value) +{ + struct xen_hvm_param xhv; + + xhv.domid = DOMID_SELF; + xhv.index = idx; + xhv.value = value; + + return HYPERVISOR_hvm_op(HVMOP_set_param, &xhv); +} + +int hvm_get_parameter(int idx, uint64_t *value) +{ + int ret = 0; + struct xen_hvm_param xhv; + + xhv.domid = DOMID_SELF; + xhv.index = idx; + + ret = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv); + if (ret < 0) + return ret; + + *value = xhv.value; + return ret; +} diff --git a/include/xen/console.h b/include/xen/console.h new file mode 100644 index 00000000000..2e617b04d1d --- /dev/null +++ b/include/xen/console.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __XEN_CONSOLE_H__ +#define __XEN_CONSOLE_H__ + +#include +#include +#include +#include + +struct hvc_xen_data { + DEVICE_MMIO_RAM; /* should be first */ + const struct device *dev; + struct xencons_interface *intf; + uint64_t evtchn; +}; + +int xen_console_init(const struct device *dev); + +#endif /* __XEN_CONSOLE_H__ */ diff --git a/include/xen/events.h b/include/xen/events.h new file mode 100644 index 00000000000..eee0ff0e8fb --- /dev/null +++ b/include/xen/events.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __XEN_EVENTS_H__ +#define __XEN_EVENTS_H__ + +#include + +void notify_evtchn(evtchn_port_t port); + +#endif /* __XEN_EVENTS_H__ */ diff --git a/include/xen/generic.h b/include/xen/generic.h new file mode 100644 index 00000000000..c1123a73278 --- /dev/null +++ b/include/xen/generic.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __XEN_GENERIC_H__ +#define __XEN_GENERIC_H__ + +#include + +#define XEN_PAGE_SIZE 4096 +#define XEN_PAGE_SHIFT 12 + +#endif /* __XEN_GENERIC_H__ */ diff --git a/include/xen/hvm.h b/include/xen/hvm.h new file mode 100644 index 00000000000..1bd770ba1df --- /dev/null +++ b/include/xen/hvm.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __XEN_HVM_H__ +#define __XEN_HVM_H__ + +#include +#include + +#include + +int hvm_set_parameter(int idx, uint64_t value); +int hvm_get_parameter(int idx, uint64_t *value); + +#endif /* __XEN_HVM_H__ */