[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v1 15/16] drivers/vuart: introduce framework for UART emulators
From: Denis Mukhin <dmukhin@xxxxxxxx> Introduce a driver framework to abstract UART emulators in the hypervisor. That allows for architecture-independent handling of virtual UARTs in the console driver and simplifies enabling new UART emulators. The framework is built under CONFIG_HAS_VUART, which is automatically enabled once the user enables any UART emulator. Current implementation supports maximum of one vUART of each kind per domain. All domains with enabled virtual PL011 will have DOMAIN_EMU_UART_PL011 bit set in d->arch.emulation_flags. Introduce domain_has_vuart() in arch code to check whether domain has virtual UART. Use domain_has_vuart() in the console driver code to check whether to forward console input to the domain using vUART. Hook existing PL011 UART emulator to the new driver framework. Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx> --- Original code: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-7-c5d36b31d66c@xxxxxxxx/ --- xen/arch/arm/dom0less-build.c | 22 +++---- xen/arch/arm/domctl.c | 3 +- xen/arch/arm/include/asm/domain.h | 1 + xen/arch/arm/include/asm/kernel.h | 3 - xen/arch/arm/xen.lds.S | 1 + xen/arch/ppc/include/asm/domain.h | 1 + xen/arch/ppc/xen.lds.S | 1 + xen/arch/riscv/include/asm/domain.h | 1 + xen/arch/riscv/xen.lds.S | 1 + xen/arch/x86/xen.lds.S | 1 + xen/common/domain.c | 10 +++ xen/common/keyhandler.c | 3 + xen/drivers/char/console.c | 5 +- xen/drivers/vuart/Makefile | 1 + xen/drivers/vuart/vuart-pl011.c | 71 +++++++++++++++++++-- xen/drivers/vuart/vuart.c | 95 +++++++++++++++++++++++++++++ xen/include/xen/domain-emu.h | 2 + xen/include/xen/domain.h | 2 + xen/include/xen/vuart.h | 25 ++++++-- xen/include/xen/xen.lds.h | 10 +++ 20 files changed, 227 insertions(+), 32 deletions(-) create mode 100644 xen/drivers/vuart/vuart.c diff --git a/xen/arch/arm/dom0less-build.c b/xen/arch/arm/dom0less-build.c index 11b8498d3b22..c04441ef4657 100644 --- a/xen/arch/arm/dom0less-build.c +++ b/xen/arch/arm/dom0less-build.c @@ -176,14 +176,9 @@ int __init make_arch_nodes(struct kernel_info *kinfo) if ( ret ) return -EINVAL; - if ( kinfo->arch.vpl011 ) - { -#ifdef CONFIG_HAS_VUART_PL011 - ret = vuart_add_fwnode(kinfo->d, kinfo); -#endif - if ( ret ) - return -EINVAL; - } + ret = vuart_add_fwnode(kinfo->d, kinfo); + if ( ret ) + return ret; return 0; } @@ -207,19 +202,16 @@ int __init init_vuart(struct domain *d, struct kernel_info *kinfo, { int rc = 0; - kinfo->arch.vpl011 = dt_property_read_bool(node, "vpl011"); - /* * Base address and irq number are needed when creating vpl011 device * tree node in prepare_dtb_domU, so initialization on related variables * shall be done first. */ - if ( kinfo->arch.vpl011 ) - { + if ( dt_property_read_bool(node, "vpl011") ) + d->emulation_flags |= DOMAIN_EMU_UART_PL011; + + if ( domain_has_vuart(d) ) rc = vuart_init(d, NULL); - if ( rc < 0 ) - return rc; - } return rc; } diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c index dde25ceff6d0..45144eca1ae2 100644 --- a/xen/arch/arm/domctl.c +++ b/xen/arch/arm/domctl.c @@ -43,8 +43,9 @@ static int handle_vuart_init(struct domain *d, if ( vuart_op->type != XEN_DOMCTL_VUART_TYPE_VPL011 ) return -EOPNOTSUPP; - rc = vuart_init(d, ¶ms); + d->emulation_flags |= DOMAIN_EMU_UART_PL011; + rc = vuart_init(d, ¶ms); if ( !rc ) vuart_op->evtchn = params.evtchn; diff --git a/xen/arch/arm/include/asm/domain.h b/xen/arch/arm/include/asm/domain.h index 38873c66f1f8..32ef281151d8 100644 --- a/xen/arch/arm/include/asm/domain.h +++ b/xen/arch/arm/include/asm/domain.h @@ -2,6 +2,7 @@ #define __ASM_DOMAIN_H__ #include <xen/cache.h> +#include <xen/domain-emu.h> #include <xen/timer.h> #include <asm/page.h> #include <asm/p2m.h> diff --git a/xen/arch/arm/include/asm/kernel.h b/xen/arch/arm/include/asm/kernel.h index 7c3b7fde5b64..cfeab792c76e 100644 --- a/xen/arch/arm/include/asm/kernel.h +++ b/xen/arch/arm/include/asm/kernel.h @@ -13,9 +13,6 @@ struct arch_kernel_info #ifdef CONFIG_ARM_64 enum domain_type type; #endif - - /* Enable pl011 emulation */ - bool vpl011; }; #endif /* #ifdef __ARCH_ARM_KERNEL_H__ */ diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S index 5bfbe1e92c1e..e876e3efbe4c 100644 --- a/xen/arch/arm/xen.lds.S +++ b/xen/arch/arm/xen.lds.S @@ -66,6 +66,7 @@ SECTIONS __proc_info_end = .; VPCI_ARRAY + VUART_ARRAY } :text #if defined(BUILD_ID) diff --git a/xen/arch/ppc/include/asm/domain.h b/xen/arch/ppc/include/asm/domain.h index 3a447272c6f2..426e6297c935 100644 --- a/xen/arch/ppc/include/asm/domain.h +++ b/xen/arch/ppc/include/asm/domain.h @@ -2,6 +2,7 @@ #ifndef __ASM_PPC_DOMAIN_H__ #define __ASM_PPC_DOMAIN_H__ +#include <xen/domain-emu.h> #include <xen/xmalloc.h> #include <public/hvm/params.h> diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S index 1366e2819eed..56bf4a0638d5 100644 --- a/xen/arch/ppc/xen.lds.S +++ b/xen/arch/ppc/xen.lds.S @@ -55,6 +55,7 @@ SECTIONS *(.data.rel.ro.*) VPCI_ARRAY + VUART_ARRAY . = ALIGN(POINTER_ALIGN); } :text diff --git a/xen/arch/riscv/include/asm/domain.h b/xen/arch/riscv/include/asm/domain.h index c3d965a559b6..fcde3bc78dc6 100644 --- a/xen/arch/riscv/include/asm/domain.h +++ b/xen/arch/riscv/include/asm/domain.h @@ -2,6 +2,7 @@ #ifndef ASM__RISCV__DOMAIN_H #define ASM__RISCV__DOMAIN_H +#include <xen/domain-emu.h> #include <xen/xmalloc.h> #include <public/hvm/params.h> diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S index 8c3c06de01f6..45592fca128f 100644 --- a/xen/arch/riscv/xen.lds.S +++ b/xen/arch/riscv/xen.lds.S @@ -50,6 +50,7 @@ SECTIONS *(.data.rel.ro.*) VPCI_ARRAY + VUART_ARRAY . = ALIGN(POINTER_ALIGN); } :text diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index bf956b6c5fc0..b04ed1e6138c 100644 --- a/xen/arch/x86/xen.lds.S +++ b/xen/arch/x86/xen.lds.S @@ -149,6 +149,7 @@ SECTIONS __note_gnu_build_id_end = .; #endif VPCI_ARRAY + VUART_ARRAY } PHDR(text) #if defined(CONFIG_PVH_GUEST) && !defined(EFI) diff --git a/xen/common/domain.c b/xen/common/domain.c index 8c8f70347a91..071fee81fe2c 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -2436,6 +2436,16 @@ void thaw_domains(void) rcu_read_unlock(&domlist_read_lock); } +bool domain_has_vuart(const struct domain *d) +{ + uint32_t mask = 0; + + if ( IS_ENABLED(CONFIG_HAS_VUART_PL011) ) + mask |= DOMAIN_EMU_UART_PL011; + + return !!(d->emulation_flags & mask); +} + /* * Local variables: * mode: C diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c index eccd97c565c6..21e970aded9a 100644 --- a/xen/common/keyhandler.c +++ b/xen/common/keyhandler.c @@ -22,6 +22,7 @@ #include <xen/mm.h> #include <xen/watchdog.h> #include <xen/init.h> +#include <xen/vuart.h> #include <asm/div64.h> static unsigned char keypress_key; @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key) v->periodic_period / 1000000); } } + + vuart_dump(d); } for_each_domain ( d ) diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index f322d59515ab..5ddc05cc44da 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -594,6 +594,7 @@ static void __serial_rx(char c) /* * Deliver input to the hardware domain buffer, unless it is * already full. + * NB: must be the first check: hardware domain may have emulated UART. */ if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE ) serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c; @@ -604,11 +605,9 @@ static void __serial_rx(char c) */ send_global_virq(VIRQ_CONSOLE); } -#ifdef CONFIG_HAS_VUART_PL011 - else + else if ( domain_has_vuart(d) ) /* Deliver input to the emulated UART. */ rc = vuart_putchar(d, c); -#endif if ( consoled_is_enabled() ) /* Deliver input to the PV shim console. */ diff --git a/xen/drivers/vuart/Makefile b/xen/drivers/vuart/Makefile index 1c775ffb7f1d..fa9aab666c2a 100644 --- a/xen/drivers/vuart/Makefile +++ b/xen/drivers/vuart/Makefile @@ -1,2 +1,3 @@ +obj-$(CONFIG_HAS_VUART) += vuart.o obj-$(CONFIG_HAS_VUART_MMIO) += vuart-mmio.o obj-$(CONFIG_HAS_VUART_PL011) += vuart-pl011.o diff --git a/xen/drivers/vuart/vuart-pl011.c b/xen/drivers/vuart/vuart-pl011.c index bebfb5e0365c..312618ad1b6f 100644 --- a/xen/drivers/vuart/vuart-pl011.c +++ b/xen/drivers/vuart/vuart-pl011.c @@ -638,9 +638,9 @@ static void vpl011_data_avail(struct domain *d, } /* - * vuart_putchar adds a char to a domain's vpl011 receive buffer. + * vpl011_putchar adds a char to a domain's vpl011 receive buffer. */ -int vuart_putchar(struct domain *d, char c) +static int cf_check vpl011_putchar(struct domain *d, char c) { unsigned long flags; struct vpl011 *vpl011 = d->arch.vpl011; @@ -709,7 +709,8 @@ static void vpl011_notification(struct vcpu *v, unsigned int port) spin_unlock_irqrestore(&vpl011->lock, flags); } -int vuart_init(struct domain *d, struct vuart_params *params) +static int cf_check vpl011_init(struct domain *d, + struct vuart_params *params) { struct vpl011 *vpl011; int rc; @@ -824,7 +825,7 @@ err_out: return rc; } -void vuart_exit(struct domain *d) +static void cf_check vpl011_exit(struct domain *d) { if ( d->arch.vpl011 ) { @@ -833,9 +834,57 @@ void vuart_exit(struct domain *d) } } -int __init vuart_add_fwnode(struct domain *d, void *node) +static void cf_check vpl011_dump(const struct domain *d) { - struct kernel_info *kinfo = node; + struct vpl011 *vdev = d->arch.vpl011; + + if ( !vdev ) + return; + + /* Allow printing state in case of a deadlock. */ + if ( !spin_trylock(&vdev->lock) ) + return; + + printk("Virtual PL011@%"PRIpaddr" IRQ#%d owner %pd\n", + vdev->base_addr, vdev->virq, d); + + if ( vdev->backend_in_domain ) + { + printk(" Event channel %"PRIu32"\n", vdev->evtchn); + } + else + { + const struct vpl011_xen_backend *cons = vdev->backend.xen; + + printk(" RX FIFO size %u in_prod %u in_cons %u used %u\n", + SBSA_UART_FIFO_SIZE, cons->in_prod, cons->in_cons, + cons->in_prod - cons->in_cons); + + printk(" TX FIFO size %u out_prod %u used %u\n", + SBSA_UART_OUT_BUF_SIZE, cons->out_prod, cons->out_prod); + } + + printk(" %02"PRIx8" DR %08"PRIx8"\n", DR , 0); + printk(" %02"PRIx8" RSR %08"PRIx8"\n", RSR , 0); + printk(" %02"PRIx8" FR %08"PRIx8"\n", FR , vdev->uartfr); + printk(" %02"PRIx8" ILPR %08"PRIx8"\n", ILPR , 0); + printk(" %02"PRIx8" IBRD %08"PRIx8"\n", IBRD , 0); + printk(" %02"PRIx8" FBRD %08"PRIx8"\n", FBRD , 0); + printk(" %02"PRIx8" LCR_H %08"PRIx8"\n", LCR_H, 0); + printk(" %02"PRIx8" CR %08"PRIx8"\n", CR , vdev->uartcr); + printk(" %02"PRIx8" IFLS %08"PRIx8"\n", IFLS , 0); + printk(" %02"PRIx8" IMSC %08"PRIx8"\n", IMSC , vdev->uartimsc); + printk(" %02"PRIx8" RIS %08"PRIx8"\n", RIS , vdev->uartris); + printk(" %02"PRIX8" MIS %08"PRIX8"\n", MIS , vdev->shadow_uartmis); + printk(" %02"PRIx8" ICR %08"PRIx8"\n", ICR , vdev->uarticr); + printk(" %02"PRIx8" DMACR %08"PRIx8"\n", DMACR, 0); + + spin_unlock(&vdev->lock); +} + +static int cf_check vpl011_add_fwnode(struct domain *d, void *info) +{ + struct kernel_info *kinfo = info; void *fdt = kinfo->fdt; int res; gic_interrupt_t intr; @@ -884,6 +933,16 @@ int __init vuart_add_fwnode(struct domain *d, void *node) return 0; } +static const struct vuart_ops vpl011_ops = { + .add_fwnode = vpl011_add_fwnode, + .init = vpl011_init, + .exit = vpl011_exit, + .dump = vpl011_dump, + .putchar = vpl011_putchar, +}; + +VUART_REGISTER(vpl011, &vpl011_ops); + /* * Local variables: * mode: C diff --git a/xen/drivers/vuart/vuart.c b/xen/drivers/vuart/vuart.c new file mode 100644 index 000000000000..ab3c2504bff5 --- /dev/null +++ b/xen/drivers/vuart/vuart.c @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <xen/errno.h> +#include <xen/sched.h> +#include <xen/vuart.h> + +extern const struct vuart_ops *const __vuart_array_start[]; +extern const struct vuart_ops *const __vuart_array_end[]; + +#define VUART_ARRAY_SIZE (__vuart_array_end - __vuart_array_start) + +#define for_each_vuart(vdev) \ + for (unsigned __i = 0; \ + __i < VUART_ARRAY_SIZE && (vdev = __vuart_array_start[__i], 1); \ + __i++) + +int vuart_add_fwnode(struct domain *d, void *node) +{ + const struct vuart_ops *vdev; + int rc; + + for_each_vuart(vdev) + { + if ( !vdev->add_fwnode ) + continue; + + rc = vdev->add_fwnode(d, node); + if ( rc ) + return rc; + } + + return 0; +} + +int vuart_init(struct domain *d, struct vuart_params *params) +{ + const struct vuart_ops *vdev; + int rc; + + for_each_vuart(vdev) + { + rc = vdev->init(d, params); + if ( rc ) + return rc; + } + + d->console.input_allowed = true; + + return 0; +} + +/* + * Release any resources taken by UART emulators. + * + * NB: no flags are cleared, since currently exit() is called only during + * domain destroy. + */ +void vuart_exit(struct domain *d) +{ + const struct vuart_ops *vdev; + + for_each_vuart(vdev) + vdev->exit(d); +} + +void vuart_dump(const struct domain *d) +{ + const struct vuart_ops *vdev; + + for_each_vuart(vdev) + vdev->dump(d); +} + +/* + * Put character to the first suitable emulated UART's FIFO. + */ +int vuart_putchar(struct domain *d, char c) +{ + const struct vuart_ops *vdev = NULL; + + for_each_vuart(vdev) + if ( vdev->putchar ) + break; + + return vdev ? vdev->putchar(d, c) : -ENODEV; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/domain-emu.h b/xen/include/xen/domain-emu.h index 410963ff480c..1d3a6c80fadd 100644 --- a/xen/include/xen/domain-emu.h +++ b/xen/include/xen/domain-emu.h @@ -17,6 +17,8 @@ #define DOMAIN_EMU_PIRQ (1U << 9) #define DOMAIN_EMU_PCI (1U << 10) +#define DOMAIN_EMU_UART_PL011 (1U << 15) + #endif /* XEN_DOMAIN_EMU_H */ /* diff --git a/xen/include/xen/domain.h b/xen/include/xen/domain.h index e10baf2615fd..e40906380760 100644 --- a/xen/include/xen/domain.h +++ b/xen/include/xen/domain.h @@ -59,6 +59,8 @@ domid_t get_initial_domain_id(void); #define is_domain_direct_mapped(d) ((d)->cdf & CDF_directmap) #define is_domain_using_staticmem(d) ((d)->cdf & CDF_staticmem) +bool domain_has_vuart(const struct domain *d); + /* * Arch-specifics. */ diff --git a/xen/include/xen/vuart.h b/xen/include/xen/vuart.h index 928b60bbb4e2..1f4b47575359 100644 --- a/xen/include/xen/vuart.h +++ b/xen/include/xen/vuart.h @@ -12,16 +12,29 @@ struct vuart_params { evtchn_port_t evtchn; }; -#ifdef CONFIG_HAS_VUART_PL011 +struct vuart_ops { + int (*add_fwnode)(struct domain *d, void *node); + int (*init)(struct domain *d, struct vuart_params *params); + void (*exit)(struct domain *d); + void (*dump)(const struct domain *d); + int (*putchar)(struct domain *d, char c); +}; -int __init vuart_add_fwnode(struct domain *d, void *node); +#define VUART_REGISTER(name, x) \ + static const struct vuart_ops *const __name##_entry \ + __used_section(".data.vuart." #name) = (x); + +#ifdef CONFIG_HAS_VUART + +int vuart_add_fwnode(struct domain *d, void *node); int vuart_init(struct domain *d, struct vuart_params *params); void vuart_exit(struct domain *d); +void vuart_dump(const struct domain *d); int vuart_putchar(struct domain *d, char c); #else -static inline int __init vuart_add_fwnode(struct domain *d, void *node) +static inline int vuart_add_fwnode(struct domain *d, void *node) { return 0; } @@ -35,13 +48,17 @@ static inline void vuart_exit(struct domain *d) { } +static inline void vuart_dump(const struct domain *d) +{ +} + static inline int vuart_putchar(struct domain *d, char c) { ASSERT_UNREACHABLE(); return -ENODEV; } -#endif /* CONFIG_HAS_VUART_PL011 */ +#endif /* CONFIG_HAS_VUART */ #ifdef CONFIG_HAS_VUART_MMIO diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h index 793d0e11450c..1671257e19ee 100644 --- a/xen/include/xen/xen.lds.h +++ b/xen/include/xen/xen.lds.h @@ -194,4 +194,14 @@ #define VPCI_ARRAY #endif +#ifdef CONFIG_HAS_VUART +#define VUART_ARRAY \ + . = ALIGN(POINTER_ALIGN); \ + __vuart_array_start = .; \ + *(SORT(.data.vuart.*)) \ + __vuart_array_end = .; +#else +#define VUART_ARRAY +#endif + #endif /* __XEN_LDS_H__ */ -- 2.34.1
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |