|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v8 04/26] emul/ns16x50: implement emulator stub
From: Denis Mukhin <dmukhin@xxxxxxxx> Define the placeholder for NS16550 UART emulator. Implement alloc/free vUART hooks, add stub for I/O port handler, initialize emulator state and define debug logging facilities. Signed-off-by: Denis Mukhin <dmukhin@xxxxxxxx> --- xen/common/emul/vuart/Makefile | 1 + xen/common/emul/vuart/ns16x50.c | 417 ++++++++++++++++++++++++++++++++ 2 files changed, 418 insertions(+) create mode 100644 xen/common/emul/vuart/ns16x50.c diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile index 97f792dc6641..fe904f6cb65d 100644 --- a/xen/common/emul/vuart/Makefile +++ b/xen/common/emul/vuart/Makefile @@ -1 +1,2 @@ obj-y += vuart.o +obj-$(CONFIG_VUART_NS16X50) += ns16x50.o diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c new file mode 100644 index 000000000000..6e1cfecbaa55 --- /dev/null +++ b/xen/common/emul/vuart/ns16x50.c @@ -0,0 +1,417 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * NS16550-compatible UART Emulator. + * + * See: + * - Serial and UART Tutorial: + * https://download.freebsd.org/doc/en/articles/serial-uart/serial-uart_en.pdf + * - UART w/ 16 byte FIFO: + * https://www.ti.com/lit/ds/symlink/tl16c550c.pdf + * - UART w/ 64 byte FIFO: + * https://www.ti.com/lit/ds/symlink/tl16c750.pdf + * - DesignWare DW_apb_uart Databook, v4.02a: + * https://iccircle.com/static/upload/img20240313113905.pdf + * + * Limitations: + * - Only x86; + * - Only Xen console as a backend, no inter-domain communication (similar to + * vpl011 on Arm); + * - Only 8n1 emulation (8-bit data, no parity, 1 stop bit); + * - No baud rate emulation (reports 115200 baud to the guest OS); + * - No FIFO-less mode emulation; + * - No RX FIFO interrupt moderation (FCR) emulation; + * - No integration w/ VM snapshotting (HVM_REGISTER_SAVE_RESTORE() and + * friends); + * - No ISA IRQ sharing allowed; + * - No MMIO-based UART emulation. + */ + +#define pr_prefix "ns16x50" +#define pr_fmt(fmt) pr_prefix ": " fmt + +#ifdef CONFIG_VUART_NS16X50_DEBUG +#define guest_prefix "FROM GUEST " +#define ns16x50_log_level 2 +#else +#define guest_prefix "" +#define ns16x50_log_level 0 +#endif + +#include <asm/x86_emulate.h> + +#include <xen/8250-uart.h> +#include <xen/console.h> +#include <xen/err.h> +#include <xen/iocap.h> +#include <xen/resource.h> +#include <xen/vuart.h> +#include <xen/xvmalloc.h> + +#include <public/io/console.h> + +#define ns16x50_log(n, lvl, vdev, fmt, args...) \ +do { \ + if ( ns16x50_log_level >= n ) \ + gprintk(lvl, pr_fmt("%s: " fmt), (vdev)->name, ## args); \ +} while (0) + +#define ns16x50_err(vdev, fmt, args...) \ + ns16x50_log(0, XENLOG_ERR, vdev, fmt, ## args) +#define ns16x50_warn(vdev, fmt, args...) \ + ns16x50_log(1, XENLOG_WARNING, vdev, fmt, ## args) +#define ns16x50_info(vdev, fmt, args...) \ + ns16x50_log(2, XENLOG_INFO, vdev, fmt, ## args) +#define ns16x50_debug(vdev, fmt, args...) \ + ns16x50_log(3, XENLOG_DEBUG, vdev, fmt, ## args) + +/* + * Number of supported registers in the UART. + */ +#define NS16X50_REGS_NUM (UART_SCR + 1) + +/* + * Number of emulated registers. + * + * - Emulated registers [0..NS16X50_REGS_NUM] are R/W registers for DLAB=0. + * - DLAB=1, R/W, DLL = (NS16X50_REGS_NUM + 0) + * - DLAB=1, R/W, DLM = (NS16X50_REGS_NUM + 1) + */ +#define NS16X50_EMU_REGS_NUM (NS16X50_REGS_NUM + 2) + +/* + * Virtual ns16x50 device state. + */ +struct vuart_ns16x50 { + uint8_t regs[NS16X50_EMU_REGS_NUM]; /* Emulated registers */ + const struct vuart_info *info; /* UART description */ + struct domain *owner; /* Owner domain */ + const char *name; /* Device name */ + spinlock_t lock; /* Protection */ + struct xencons_interface cons; /* Emulated RX/TX FIFOs */ +}; + +static inline void ns16x50_lock_init(struct vuart_ns16x50 *vdev) +{ + spin_lock_init(&vdev->lock); +} + +static inline void ns16x50_lock(struct vuart_ns16x50 *vdev, + unsigned long *flags) +{ + spin_lock(&vdev->lock); +} + +static inline bool ns16x50_trylock(struct vuart_ns16x50 *vdev, + unsigned long *flags) +{ + return spin_trylock(&vdev->lock); +} + +static inline void ns16x50_unlock(struct vuart_ns16x50 *vdev, + unsigned long *flags) +{ + spin_unlock(&vdev->lock); +} + +/* + * Emulate 8-bit write access to ns16x50 register. + */ +static int ns16x50_io_write8( + struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data) +{ + int rc = 0; + + return rc; +} + +/* + * Emulate 16-bit write access to ns16x50 register. + * NB: some guest OSes use outw() to access UART_DLL. + */ +static int ns16x50_io_write16( + struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data) +{ + int rc = -EINVAL; + + return rc; +} + +/* + * Emulate write access to ns16x50 register. + */ +static int ns16x50_io_write( + struct vuart_ns16x50 *vdev, uint8_t reg, uint32_t size, uint32_t *data) +{ + int rc; + + switch ( size ) + { + case 1: + rc = ns16x50_io_write8(vdev, reg, (uint8_t *)data); + break; + + case 2: + rc = ns16x50_io_write16(vdev, reg, (uint16_t *)data); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +/* + * Emulate 8-bit read access to ns16x50 register. + */ +static int ns16x50_io_read8( + struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data) +{ + uint8_t val = UINT8_MAX; + int rc = 0; + + *data = val; + + return rc; +} + +/* + * Emulate 16-bit read access to ns16x50 register. + */ +static int ns16x50_io_read16( + struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data) +{ + uint16_t val = UINT16_MAX; + int rc = -EINVAL; + + *data = val; + + return rc; +} + +/* + * Emulate read access to ns16x50 register. + */ +static int ns16x50_io_read( + struct vuart_ns16x50 *vdev, uint8_t reg, uint32_t size, uint32_t *data) +{ + int rc; + + switch ( size ) + { + case 1: + rc = ns16x50_io_read8(vdev, reg, (uint8_t *)data); + break; + + case 2: + rc = ns16x50_io_read16(vdev, reg, (uint16_t *)data); + break; + + default: + *data = UINT32_MAX; + rc = -EINVAL; + break; + } + + return rc; +} + +static void ns16x50_access(int dir, unsigned int type, + unsigned long addr, unsigned int size, + uint32_t *data) +{ + const char op = (dir == IOREQ_WRITE) ? 'W' : 'R'; + struct domain *d = rcu_lock_current_domain(); + struct vuart *vuart = vuart_find_by_resource(d, type, addr, size); + struct vuart_ns16x50 *vdev = NULL; + const struct domain *owner; + const struct vuart_info *info; + const char *as; + uint32_t reg; + unsigned long flags; + int rc; + + if ( !vuart ) + { + printk(XENLOG_ERR "%c @ %#"PRIx64" %d: not initialized\n", + op, addr, size); + + ASSERT_UNREACHABLE(); + goto out; + } + + vdev = vuart->vdev; + ASSERT(vdev); + + ns16x50_lock(vdev, &flags); + + owner = vuart->owner; + ASSERT(owner); + + if ( d != owner ) + { + ns16x50_err(vdev, "%c @ %#"PRIx64" %d: does not match current domain %pd\n", + op, addr, size, d); + + ASSERT_UNREACHABLE(); + goto out_unlock; + } + + info = vuart->info; + ASSERT(info); + as = (info->base_type & IORESOURCE_IO) ? "ioport": "iomem"; + + reg = addr - info->base_addr; + if ( !IS_ALIGNED(reg, size) ) + { + ns16x50_err(vdev, "%c %s %#"PRIx64" %d: unaligned access\n", + op, as, addr, size); + goto out_unlock; + } + + if ( reg >= NS16X50_REGS_NUM ) + { + ns16x50_err(vdev, "%c %s %#"PRIx64" %d: LCR=%#02x %#02x %#x: not implemented\n", + op, as, addr, size, vdev->regs[UART_LCR], reg, *data); + goto out_unlock; + } + + if ( dir == IOREQ_WRITE ) + rc = ns16x50_io_write(vdev, reg, size, data); + else + rc = ns16x50_io_read(vdev, reg, size, data); + + + if ( rc == 0 ) + ns16x50_debug(vdev, "%c %s %#"PRIx64" %d: LCR=%#02x %#02x %#08x\n", + op, as, addr, size, vdev->regs[UART_LCR], reg, *data); + else + ns16x50_err(vdev, "%c %s %#"PRIx64" %d: LCR=%#02x %#02x %#08x: unsupported access\n", + op, as, addr, size, vdev->regs[UART_LCR], reg, *data); + +out_unlock: + ns16x50_unlock(vdev, &flags); +out: + rcu_unlock_domain(d); +} + +/* + * Emulate I/O access to ns16x50 register. + * Note, emulation always returns X86EMUL_OKAY, once I/O port trap is enabled. + */ +static int cf_check ns16x50_io_handle( + int dir, unsigned int addr, unsigned int size, uint32_t *data) +{ + ns16x50_access(dir, IORESOURCE_IO, addr, size, data); + + return X86EMUL_OKAY; +} + +static void ns16x50_reset(struct vuart_ns16x50 *vdev) +{ + memset(vdev->regs, 0, sizeof(vdev->regs)); +} + +static int ns16x50_init(void *arg) +{ + struct vuart_ns16x50 *vdev = arg; + const struct vuart_info *info; + struct domain *d; + + ASSERT(vdev); + + d = vdev->owner; + info = vdev->info; + + if ( info->base_type & IORESOURCE_IO ) + { + register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle); + } + else if ( info->base_type & IORESOURCE_MEM ) + { + BUG_ON("unimplemented"); + } + else + ASSERT_UNREACHABLE(); + + ns16x50_reset(vdev); + + return 0; +} + +static void cf_check ns16x50_deinit(void *arg) +{ + struct vuart_ns16x50 *vdev = arg; + + ASSERT(vdev); +} + +static void * cf_check ns16x50_alloc(struct domain *d, const struct vuart_info *info) +{ + struct vuart_ns16x50 *vdev; + int rc; + + if ( !is_hvm_domain(d) ) + { + ns16x50_err(info, "not an HVM domain\n"); + return ERR_PTR(-ENOSYS); + } + + if ( vuart_find_by_resource(d, info->base_type, info->base_addr, + info->size) ) + { + ns16x50_err(info, "already registered\n"); + return ERR_PTR(-EBUSY); + } + + vdev = xvzalloc(typeof(*vdev)); + if ( !vdev ) + { + ns16x50_err(info, "failed to allocate memory\n"); + return ERR_PTR(-ENOMEM); + } + + ns16x50_lock_init(vdev); + + vdev->name = info->name; + vdev->owner = d; + vdev->info = info; + + rc = ns16x50_init(vdev); + if ( rc ) + { + xvfree(vdev); + return ERR_PTR(rc); + } + + return vdev; +} + +static void cf_check ns16x50_free(void *arg) +{ + if ( arg ) + ns16x50_deinit(arg); + + xvfree(arg); +} + +#define ns16x50_emulator \ +{ \ + .compatible = "ns16550", \ + .alloc = ns16x50_alloc, \ + .free = ns16x50_free, \ + .dump_state = NULL, \ + .put_rx = NULL, \ +} + +VUART_REGISTER(ns16x50, ns16x50_emulator); + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.56.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |