@@ -175,6 +175,7 @@ config NEW_VGIC
config SBSA_VUART_CONSOLE
bool "Emulated SBSA UART console support"
default y
+ select HAS_VUART
help
Allows a guest to use SBSA Generic UART as a console. The
SBSA Generic UART implements a subset of ARM PL011 UART.
@@ -785,7 +785,7 @@ static int __init construct_domU(struct domain *d,
*/
if ( kinfo.vpl011 )
{
- rc = domain_vpl011_init(d, NULL);
+ rc = virtdev_uart_init(d, NULL);
if ( rc < 0 )
return rc;
}
@@ -891,7 +891,7 @@ void __init create_domUs(void)
* d->arch.vpl011.irq. So the logic to find the vIRQ has to
* be hardcoded.
* The logic here shall be consistent with the one in
- * domain_vpl011_init().
+ * vpl011_init().
*/
if ( flags & CDF_directmap )
{
@@ -1067,7 +1067,7 @@ int domain_relinquish_resources(struct domain *d)
* Release the resources allocated for vpl011 which were
* allocated via a DOMCTL call XEN_DOMCTL_vuart_op.
*/
- domain_vpl011_deinit(d);
+ virtdev_uart_exit(d);
#ifdef CONFIG_IOREQ_SERVER
ioreq_server_destroy_all(d);
@@ -14,6 +14,7 @@
#include <xen/mm.h>
#include <xen/sched.h>
#include <xen/types.h>
+#include <xen/virtdev-uart.h>
#include <xsm/xsm.h>
#include <public/domctl.h>
@@ -30,10 +31,7 @@ static int handle_vuart_init(struct domain *d,
struct xen_domctl_vuart_op *vuart_op)
{
int rc;
- struct vpl011_init_info info;
-
- info.console_domid = vuart_op->console_domid;
- info.gfn = _gfn(vuart_op->gfn);
+ struct virtdev_uart_params info;
if ( d->creation_finished )
return -EPERM;
@@ -41,8 +39,11 @@ static int handle_vuart_init(struct domain *d,
if ( vuart_op->type != XEN_DOMCTL_VUART_TYPE_VPL011 )
return -EOPNOTSUPP;
- rc = domain_vpl011_init(d, &info);
+ info.console_domid = vuart_op->console_domid;
+ info.gfn = _gfn(vuart_op->gfn);
+ info.evtchn = (evtchn_port_t)-1;
+ rc = virtdev_uart_init(d, &info);
if ( !rc )
vuart_op->evtchn = info.evtchn;
@@ -23,6 +23,7 @@
#include <public/io/ring.h>
#include <public/io/console.h>
#include <xen/mm.h>
+#include <xen/virtdev-uart.h>
/* helper macros */
#define VPL011_LOCK(d,flags) spin_lock_irqsave(&(d)->arch.vpl011.lock, flags)
@@ -59,26 +60,6 @@ struct vpl011 {
evtchn_port_t evtchn;
};
-struct vpl011_init_info {
- domid_t console_domid;
- gfn_t gfn;
- evtchn_port_t evtchn;
-};
-
-#ifdef CONFIG_SBSA_VUART_CONSOLE
-int domain_vpl011_init(struct domain *d,
- struct vpl011_init_info *info);
-void domain_vpl011_deinit(struct domain *d);
-int vpl011_rx_char_xen(struct domain *d, char c);
-#else
-static inline int domain_vpl011_init(struct domain *d,
- struct vpl011_init_info *info)
-{
- return -ENOSYS;
-}
-
-static inline void domain_vpl011_deinit(struct domain *d) { }
-#endif
#endif /* _VPL011_H_ */
/*
@@ -19,6 +19,7 @@
#include <xen/sched.h>
#include <xen/console.h>
#include <xen/serial.h>
+#include <xen/virtdev-uart.h>
#include <public/domctl.h>
#include <public/io/console.h>
#include <asm/pl011-uart.h>
@@ -26,6 +27,8 @@
#include <asm/vpl011.h>
#include <asm/vreg.h>
+static void cf_check vpl011_exit(struct domain *d);
+
/*
* Since pl011 registers are 32-bit registers, all registers
* are handled similarly allowing 8-bit, 16-bit and 32-bit
@@ -566,9 +569,9 @@ static void vpl011_data_avail(struct domain *d,
}
/*
- * vpl011_rx_char_xen adds a char to a domain's vpl011 receive buffer.
+ * vpl011_putchar adds a char to a domain's vpl011 receive buffer.
*/
-int vpl011_rx_char_xen(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;
@@ -637,7 +640,8 @@ static void vpl011_notification(struct vcpu *v, unsigned int port)
VPL011_UNLOCK(d, flags);
}
-int domain_vpl011_init(struct domain *d, struct vpl011_init_info *info)
+static int cf_check vpl011_init(struct domain *d,
+ struct virtdev_uart_params *params)
{
int rc;
struct vpl011 *vpl011 = &d->arch.vpl011;
@@ -689,27 +693,28 @@ int domain_vpl011_init(struct domain *d, struct vpl011_init_info *info)
}
/*
- * info is NULL when the backend is in Xen.
- * info is != NULL when the backend is in a domain.
+ * params is NULL when the backend is in Xen.
+ * params is != NULL when the backend is in a domain.
*/
- if ( info != NULL )
+ if ( params )
{
vpl011->backend_in_domain = true;
/* Map the guest PFN to Xen address space. */
rc = prepare_ring_for_helper(d,
- gfn_x(info->gfn),
+ gfn_x(params->gfn),
&vpl011->backend.dom.ring_page,
&vpl011->backend.dom.ring_buf);
if ( rc < 0 )
goto out;
- rc = alloc_unbound_xen_event_channel(d, 0, info->console_domid,
+ rc = alloc_unbound_xen_event_channel(d, 0, params->console_domid,
vpl011_notification);
if ( rc < 0 )
goto out1;
- vpl011->evtchn = info->evtchn = rc;
+ params->evtchn = rc;
+ vpl011->evtchn = rc;
}
else
{
@@ -740,13 +745,13 @@ int domain_vpl011_init(struct domain *d, struct vpl011_init_info *info)
return 0;
out1:
- domain_vpl011_deinit(d);
+ vpl011_exit(d);
out:
return rc;
}
-void domain_vpl011_deinit(struct domain *d)
+static void cf_check vpl011_exit(struct domain *d)
{
struct vpl011 *vpl011 = &d->arch.vpl011;
@@ -783,6 +788,12 @@ void domain_vpl011_deinit(struct domain *d)
XFREE(vpl011->backend.xen);
}
+static void cf_check vpl011_dump(struct domain *d)
+{
+}
+
+VIRTDEV_UART_REGISTER(vpl011);
+
/*
* Local variables:
* mode: C
@@ -17,6 +17,9 @@
* /!\ This device is not intended to be enumerable or exposed to the OS
* (e.g. via Device Tree).
*
+ * Not hooked into virtdev-uart framework because this emulator is limited
+ * to hardware domains only and cannot own physical console input.
+ *
* Julien Grall <julien.grall@linaro.org>
* Ian Campbell <ian.campbell@citrix.com>
* Copyright (c) 2012 Citrix Systems.
@@ -69,6 +69,7 @@ SECTIONS
__proc_info_end = .;
VPCI_ARRAY
+ VIRTDEV_UART_SECTION
} :text
#if defined(BUILD_ID)
@@ -56,6 +56,7 @@ SECTIONS
*(.data.rel.ro.*)
VPCI_ARRAY
+ VIRTDEV_UART_SECTION
. = ALIGN(POINTER_ALIGN);
} :text
@@ -51,6 +51,7 @@ SECTIONS
*(.data.rel.ro.*)
VPCI_ARRAY
+ VIRTDEV_UART_SECTION
. = ALIGN(POINTER_ALIGN);
} :text
@@ -30,6 +30,7 @@
#include <xen/vpci.h>
#include <xen/nospec.h>
#include <xen/vm_event.h>
+#include <xen/virtdev-uart.h>
#include <asm/shadow.h>
#include <asm/hap.h>
#include <asm/current.h>
@@ -146,6 +146,7 @@ SECTIONS
__note_gnu_build_id_end = .;
#endif
VPCI_ARRAY
+ VIRTDEV_UART_SECTION
} PHDR(text)
#if defined(CONFIG_PVH_GUEST) && !defined(EFI)
@@ -22,6 +22,7 @@
#include <xen/mm.h>
#include <xen/watchdog.h>
#include <xen/init.h>
+#include <xen/virtdev-uart.h>
#include <asm/div64.h>
static unsigned char keypress_key;
@@ -350,6 +351,8 @@ static void cf_check dump_domains(unsigned char key)
v->periodic_period / 1000000);
}
}
+
+ virtdev_uart_dump(d);
}
for_each_domain ( d )
@@ -19,4 +19,9 @@ config HAS_VPCI_GUEST_SUPPORT
bool
select HAS_VPCI
+config HAS_VUART
+ bool "UART emulation framework"
+ help
+ This selects UART emulation framework.
+
endmenu
@@ -5,3 +5,4 @@ obj-$(CONFIG_HAS_VPCI) += vpci/
obj-$(CONFIG_HAS_PASSTHROUGH) += passthrough/
obj-$(CONFIG_ACPI) += acpi/
obj-$(CONFIG_VIDEO) += video/
+obj-$(CONFIG_HAS_VUART) += virtdev-uart.o
@@ -34,13 +34,11 @@
#include <asm/setup.h>
#include <xen/sections.h>
#include <xen/consoled.h>
+#include <xen/virtdev-uart.h>
#ifdef CONFIG_X86
#include <asm/guest.h>
#endif
-#ifdef CONFIG_SBSA_VUART_CONSOLE
-#include <asm/vpl011.h>
-#endif
/* console: comma-separated list of console outputs. */
static char __initdata opt_console[30] = OPT_CONSOLE_STR;
@@ -545,6 +543,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;
@@ -555,11 +554,9 @@ static void __serial_rx(char c)
*/
send_global_virq(VIRQ_CONSOLE);
}
-#ifdef CONFIG_SBSA_VUART_CONSOLE
- else
+ else if ( domain_has_vuart(d) )
/* Deliver input to the emulated UART. */
- rc = vpl011_rx_char_xen(d, c);
-#endif
+ rc = virtdev_uart_putchar(d, c);
if ( consoled_is_enabled() )
/* Deliver input to the PV shim console. */
new file mode 100644
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/errno.h>
+#include <xen/event.h>
+#include <xen/virtdev-uart.h>
+#include <public/virtdev.h>
+
+extern const struct virtdev_uart *__start_virtdev_uart;
+
+int virtdev_uart_init(struct domain *d, struct virtdev_uart_params *params)
+{
+ int rc;
+
+ ASSERT(__start_virtdev_uart);
+
+ rc = __start_virtdev_uart->init(d, params);
+ if ( rc )
+ return rc;
+
+#if !defined(__i386__) && !defined(__x86_64__)
+ d->arch.emulation_flags |= VIRTDEV_UART;
+#endif
+
+ return 0;
+}
+
+void virtdev_uart_exit(struct domain *d)
+{
+ ASSERT(__start_virtdev_uart);
+
+ __start_virtdev_uart->exit(d);
+
+#if !defined(__i386__) && !defined(__x86_64__)
+ d->arch.emulation_flags &= ~VIRTDEV_UART;
+#endif
+}
+
+int virtdev_uart_putchar(struct domain *d, char c)
+{
+ ASSERT(__start_virtdev_uart);
+ ASSERT(d->arch.emulation_flags & VIRTDEV_UART);
+
+ return __start_virtdev_uart->putchar(d, c);
+}
+
+void virtdev_uart_dump(struct domain *d)
+{
+ ASSERT(__start_virtdev_uart);
+
+ __start_virtdev_uart->dump(d);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
@@ -17,6 +17,7 @@ enum {
VIRTDEV_PIT = 1U << 8,
VIRTDEV_PIRQ = 1U << 9,
VIRTDEV_PCI = 1U << 10,
+ VIRTDEV_UART = 1U << 11,
};
#if defined(__i386__) || defined(__x86_64__)
@@ -54,6 +54,9 @@ void arch_get_domain_info(const struct domain *d,
#define is_domain_direct_mapped(d) ((d)->cdf & CDF_directmap)
#define is_domain_using_staticmem(d) ((d)->cdf & CDF_staticmem)
+#define domain_has_vuart(d) \
+ ( IS_ENABLED(CONFIG_HAS_VUART) && \
+ (d)->arch.emulation_flags & VIRTDEV_UART )
/*
* Arch-specifics.
new file mode 100644
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef XEN__VIRTDEV_UART_H
+#define XEN__VIRTDEV_UART_H
+
+#include <public/xen.h>
+#include <public/event_channel.h>
+#include <xen/types.h>
+
+struct virtdev_uart_params {
+ domid_t console_domid;
+ gfn_t gfn;
+ evtchn_port_t evtchn;
+};
+
+struct virtdev_uart {
+ int (*putchar)(struct domain *d, char c);
+ int (*init)(struct domain *d, struct virtdev_uart_params *params);
+ void (*exit)(struct domain *d);
+ void (*dump)(struct domain *d);
+};
+
+#define VIRTDEV_UART_REGISTER(x) \
+ static const struct virtdev_uart *x##_entry \
+ __used_section(".data.virtdev.uart") = \
+ &(const struct virtdev_uart){ \
+ .init = x ## _init, \
+ .exit = x ## _exit, \
+ .dump = x ## _dump, \
+ .putchar = x ## _putchar, \
+ }
+
+#ifdef CONFIG_HAS_VUART
+
+int virtdev_uart_putchar(struct domain *d, char c);
+int virtdev_uart_init(struct domain *d, struct virtdev_uart_params *params);
+void virtdev_uart_exit(struct domain *d);
+void virtdev_uart_dump(struct domain *d);
+
+#else
+
+static inline int virtdev_uart_putchar(struct domain *d, char c)
+{
+ ASSERT_UNREACHABLE();
+ return -ENODEV;
+}
+
+static inline int virtdev_uart_init(struct domain *d,
+ struct virtdev_uart_params *params)
+{
+ return 0;
+}
+
+static inline void virtdev_uart_exit(struct domain *d)
+{
+}
+
+static inline void virtdev_uart_dump(struct domain *d)
+{
+}
+
+#endif /* CONFIG_HAS_VUART */
+
+#endif /* XEN__VIRTDEV_UART_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
@@ -193,4 +193,14 @@
#define VPCI_ARRAY
#endif
+#ifdef CONFIG_HAS_VUART
+#define VIRTDEV_UART_SECTION \
+ . = ALIGN(POINTER_ALIGN); \
+ __start_virtdev_uart = .; \
+ *(.data.virtdev.uart) \
+ __end_virtdev_uart = .;
+#else
+#define VIRTDEV_UART_SECTION
+#endif
+
#endif /* __XEN_LDS_H__ */