* [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
@ 2025-09-05 23:26 dmukhin
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
` (16 more replies)
0 siblings, 17 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:26 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
x86 port of Xen lacks vUART facility similar to Arm's vpl011 to support x86
guest OS bring up in the embedded setups.
This patch series introduces initial in-hypervisor emulator for
NS8250/NS16x50-compatible UARTs under CONFIG_VUART_NS16X50.
In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
early guest firmware and OS bringup debugging, because it eliminates
dependency on the external emulator (qemu) being operational by the time
domains are created.
The emulator also allows to forward the physical console input to the x86
domain which is useful when a system has only one physical UART for early
debugging and this UART is owned by Xen.
By default, CONFIG_VUART_NS16X50 enables emulation of NS16550 at I/O port
0x2f8, IRQ#3 in guest OS (legacy COM2). Legacy COM resources cannot be
selected at built-time or via per-domain xl configuration in this initial
submission.
CONFIG_VUART_NS16X50_DEBUG enables some extra debugging facilities useful
for NS16550 emulator development/debugging (disabled by default).
The NS16550 emulator is disabled in default x86 configuration and goes under
CONFIG_EXPERT in Kconfig.
Limitations
===========
- Only x86;
- Only legacy COM2 resources, custom I/O ports/IRQs are not supported;
- 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 toolstack integration;
- 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 MMIO-based UART emulation.
Series
======
Patch 1 introduces the new vUART framework, that is the code originally
posted here:
https://lore.kernel.org/xen-devel/20250624035443.344099-16-dmukhin@ford.com/
Required for emulator.
Patch 2 adds missing NS16550 definitions, required for emulator.
Patch 3 introduces the basic emulator skeleton - state machine
initialization stubs, I/O port handler stub, logging, etc.
Patches 4-11 incrementally populate the minimal NS16550 register emulation.
Patch 12 hooks vUART state debugging (disabled by default).
Pathes 13-15 introduce necessary changes to enable NS16550 on dom0 (and PVH).
Link to CI: https://gitlab.com/xen-project/people/dmukhin/xen/-/pipelines/2024756493
Link to branch: https://gitlab.com/xen-project/people/dmukhin/xen/-/tree/vuart-ns8250-v6?ref_type=heads
Testing
=======
```shell
echo CONFIG_EXPERT=y >> .config
echo CONFIG_VUART_NS16X50=y >> .config
make olddefconfig
```
COM2 (0x2f8) resources are used by default.
To test w/ virtual COM2, the guest kernel parameters should contain
something like the following:
earlycon=uart,io,0x2f8,115200n8 console=uart,io,0x2f8,115200n8
HVM
---
Tested only boot of HVM linux guest with OVMF as the virtual firmware.
SeaBIOS as a virtual firmware is not tested.
PVH (dom0)
----------
Xen is able to forward physical console input to the domain with virtual
NS16550. To switch the console focus press Ctrl+aaa.
Console switch is limited on x86 to dom0 and Xen (fixes pending).
Changes since v5:
- Split THR/RBR into two separate patches.
- Addressed feedback from v5.
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-1-dmukhin@ford.com/
Changes since v4:
- Split the series to make it simpler to review.
- Addressed feedback from v4.
- Dropped xl changes, which I will submit separately.
- Link to v4: https://lore.kernel.org/xen-devel/20250731192130.3948419-1-dmukhin@ford.com/
Changes since v3:
- Reduced the blast radius of the series, thanks to reviews, individual
aspects (like console focus) touched in v3 moved to separate threads.
- Kept the UART emulator framework since I need to redo some of emulator code
and there's more-or-less agreement on it (where to place, naming, scope).
- Applied the feedback from
https://lore.kernel.org/xen-devel/20250624035443.344099-1-dmukhin@ford.com/
- Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-0-c5d36b31d66c@ford.com/
Changes since v2:
- renamed emulator s/NS8250/NS16550/g
- reduced the patch series after addressing v2 feedback
- introduced driver framework for UART emulators
- unified guest OS printouts across all available UART emulators
- Link to v2: https://lore.kernel.org/xen-devel/20241205-vuart-ns8250-v1-0-e9aa923127eb@ford.com/
Changes since v1:
- dropped kmalloc/kfree aliases
- fixed ECLAIR jobs (thanks Andrew Cooper)
- addressed console forwarding on arm32 and arm64 (thanks to Luca Fancellu)
- moved NS8250 debugging stubs into its own patch
- added fix for https://gitlab.com/xen-project/xen/-/issues/184
- Link to v1: https://lore.kernel.org/r/20241126-vuart-ns8250-v1-v1-0-87b9a8375b7a@ford.com
Denis Mukhin (15):
emul/vuart: introduce framework for UART emulators
xen/8250-uart: update definitions
emul/ns16x50: implement emulator stub
emul/ns16x50: implement DLL/DLM registers
emul/ns16x50: implement SCR register
emul/ns16x50: implement IER/IIR registers
emul/ns16x50: implement LCR/LSR registers
emul/ns16x50: implement MCR/MSR registers
emul/ns16x50: implement RBR register
emul/ns16x50: implement THR register
emul/ns16x50: implement FCR register (write-only)
emul/ns16550: implement dump_state() hook
x86/domain: enable per-domain I/O port bitmaps
xen/domain: allocate d->irq_caps before arch-specific initialization
emul/ns16x50: implement IRQ emulation via vIOAPIC
xen/arch/arm/xen.lds.S | 1 +
xen/arch/ppc/xen.lds.S | 1 +
xen/arch/riscv/xen.lds.S | 1 +
xen/arch/x86/Makefile | 1 +
xen/arch/x86/dom0_build.c | 112 +--
xen/arch/x86/hvm/dom0_build.c | 7 +
xen/arch/x86/hvm/hvm.c | 56 +-
xen/arch/x86/hvm/nestedhvm.c | 8 +-
xen/arch/x86/hvm/quirks.c | 3 -
xen/arch/x86/hvm/svm/nestedsvm.c | 2 +-
xen/arch/x86/hvm/vioapic.c | 10 +
xen/arch/x86/hvm/vmx/vvmx.c | 4 +-
xen/arch/x86/include/asm/hvm/nestedhvm.h | 3 +-
xen/arch/x86/include/asm/hvm/support.h | 2 -
xen/arch/x86/include/asm/iocap.h | 2 +
xen/arch/x86/include/asm/irq.h | 8 +
xen/arch/x86/ioport.c | 163 ++++
xen/arch/x86/irq.c | 8 +
xen/arch/x86/pv/dom0_build.c | 7 +
xen/arch/x86/xen.lds.S | 1 +
xen/common/Kconfig | 2 +
xen/common/Makefile | 1 +
xen/common/domain.c | 8 +-
xen/common/emul/Kconfig | 6 +
xen/common/emul/Makefile | 1 +
xen/common/emul/vuart/Kconfig | 25 +
xen/common/emul/vuart/Makefile | 2 +
xen/common/emul/vuart/ns16x50.c | 984 +++++++++++++++++++++++
xen/common/emul/vuart/vuart.c | 157 ++++
xen/common/keyhandler.c | 3 +
xen/drivers/char/console.c | 6 +-
xen/drivers/char/ns16550.c | 16 +-
xen/drivers/passthrough/x86/hvm.c | 11 +-
xen/include/xen/8250-uart.h | 50 +-
xen/include/xen/sched.h | 4 +
xen/include/xen/serial.h | 3 +
xen/include/xen/vuart.h | 116 +++
xen/include/xen/xen.lds.h | 10 +
38 files changed, 1634 insertions(+), 171 deletions(-)
create mode 100644 xen/arch/x86/ioport.c
create mode 100644 xen/common/emul/Kconfig
create mode 100644 xen/common/emul/Makefile
create mode 100644 xen/common/emul/vuart/Kconfig
create mode 100644 xen/common/emul/vuart/Makefile
create mode 100644 xen/common/emul/vuart/ns16x50.c
create mode 100644 xen/common/emul/vuart/vuart.c
create mode 100644 xen/include/xen/vuart.h
--
2.51.0
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:02 ` Stefano Stabellini
` (2 more replies)
2025-09-05 23:27 ` [PATCH v6 02/15] xen/8250-uart: update definitions dmukhin
` (15 subsequent siblings)
16 siblings, 3 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
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_VUART_FRAMEWORK, which will be
automatically enabled once the user enables any UART emulator.
Current implementation supports maximum of one vUART of each kind per domain.
Use new domain_has_vuart() in the console driver code to check whether to
forward console input to the domain using vUART.
Enable console forwarding over vUART for hardware domains with a vUART. That
enables console forwarding to dom0 on x86, since console can be forwarded only
to Xen, dom0 and pvshim on x86 as of now.
Note: existing vUARTs are deliberately *not* hooked to the new framework to
minimize the scope of the patch: vpl011 (i.e. SBSA) emulator and "vuart" (i.e.
minimalistic MMIO-mapped dtuart for hwdoms on Arm) are kept unmodified.
No functional changes for non-x86 architectures.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- addressed v5 feedback
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-2-dmukhin@ford.com/
---
xen/arch/arm/xen.lds.S | 1 +
xen/arch/ppc/xen.lds.S | 1 +
xen/arch/riscv/xen.lds.S | 1 +
xen/arch/x86/xen.lds.S | 1 +
xen/common/Kconfig | 2 +
xen/common/Makefile | 1 +
xen/common/emul/Kconfig | 6 ++
xen/common/emul/Makefile | 1 +
xen/common/emul/vuart/Kconfig | 6 ++
xen/common/emul/vuart/Makefile | 1 +
xen/common/emul/vuart/vuart.c | 157 +++++++++++++++++++++++++++++++++
xen/common/keyhandler.c | 3 +
xen/drivers/char/console.c | 6 +-
xen/include/xen/sched.h | 4 +
xen/include/xen/serial.h | 3 +
xen/include/xen/vuart.h | 116 ++++++++++++++++++++++++
xen/include/xen/xen.lds.h | 10 +++
17 files changed, 319 insertions(+), 1 deletion(-)
create mode 100644 xen/common/emul/Kconfig
create mode 100644 xen/common/emul/Makefile
create mode 100644 xen/common/emul/vuart/Kconfig
create mode 100644 xen/common/emul/vuart/Makefile
create mode 100644 xen/common/emul/vuart/vuart.c
create mode 100644 xen/include/xen/vuart.h
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index db17ff1efa98..cd05b18770f4 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -58,6 +58,7 @@ SECTIONS
*(.rodata)
*(.rodata.*)
VPCI_ARRAY
+ VUART_ARRAY
*(.data.rel.ro)
*(.data.rel.ro.*)
diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S
index 1de0b77fc6b9..f9d4e5b0dcd8 100644
--- a/xen/arch/ppc/xen.lds.S
+++ b/xen/arch/ppc/xen.lds.S
@@ -52,6 +52,7 @@ SECTIONS
*(.rodata)
*(.rodata.*)
VPCI_ARRAY
+ VUART_ARRAY
*(.data.rel.ro)
*(.data.rel.ro.*)
diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S
index edcadff90bfe..59dcaa5fef9a 100644
--- a/xen/arch/riscv/xen.lds.S
+++ b/xen/arch/riscv/xen.lds.S
@@ -47,6 +47,7 @@ SECTIONS
*(.rodata)
*(.rodata.*)
VPCI_ARRAY
+ VUART_ARRAY
*(.data.rel.ro)
*(.data.rel.ro.*)
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 966e514f2034..d877b93a6964 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -132,6 +132,7 @@ SECTIONS
*(.rodata)
*(.rodata.*)
VPCI_ARRAY
+ VUART_ARRAY
*(.data.rel.ro)
*(.data.rel.ro.*)
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 76f9ce705f7a..78a32b69e2b2 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -676,4 +676,6 @@ config PM_STATS
Enable collection of performance management statistics to aid in
analyzing and tuning power/performance characteristics of the system
+source "common/emul/Kconfig"
+
endmenu
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 0c7d0f5d46e1..8c8462565050 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
obj-$(CONFIG_IOREQ_SERVER) += dm.o
obj-y += domain.o
obj-y += domid.o
+obj-y += emul/
obj-y += event_2l.o
obj-y += event_channel.o
obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o
diff --git a/xen/common/emul/Kconfig b/xen/common/emul/Kconfig
new file mode 100644
index 000000000000..7c6764d1756b
--- /dev/null
+++ b/xen/common/emul/Kconfig
@@ -0,0 +1,6 @@
+menu "Domain Emulation Features"
+ visible if EXPERT
+
+source "common/emul/vuart/Kconfig"
+
+endmenu
diff --git a/xen/common/emul/Makefile b/xen/common/emul/Makefile
new file mode 100644
index 000000000000..ae0b575c3901
--- /dev/null
+++ b/xen/common/emul/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VUART_FRAMEWORK) += vuart/
diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
new file mode 100644
index 000000000000..ce1b976b7da7
--- /dev/null
+++ b/xen/common/emul/vuart/Kconfig
@@ -0,0 +1,6 @@
+config VUART_FRAMEWORK
+ bool
+
+menu "UART Emulation"
+
+endmenu
diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
new file mode 100644
index 000000000000..97f792dc6641
--- /dev/null
+++ b/xen/common/emul/vuart/Makefile
@@ -0,0 +1 @@
+obj-y += vuart.o
diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
new file mode 100644
index 000000000000..3dfcba217248
--- /dev/null
+++ b/xen/common/emul/vuart/vuart.c
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UART emulator framework.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+#include <xen/err.h>
+#include <xen/sched.h>
+#include <xen/vuart.h>
+#include <xen/xvmalloc.h>
+
+#define for_each_emulator(e) \
+ for ( e = vuart_array_start; e < vuart_array_end; e++ )
+
+extern const struct vuart_emulator vuart_array_start[];
+extern const struct vuart_emulator vuart_array_end[];
+
+static const struct vuart_emulator *
+vuart_match_by_compatible(const struct domain *d, const char *compat)
+{
+ const struct vuart_emulator *emulator;
+
+ for_each_emulator(emulator)
+ if ( emulator->compatible &&
+ !strncmp(compat, emulator->compatible,
+ strlen(emulator->compatible)) )
+ return emulator;
+
+ return NULL;
+}
+
+const static struct vuart *
+vuart_find_by_console_permission(const struct domain *d)
+{
+ const struct vuart *vuart = d->console.vuart;
+
+ if ( !vuart || !vuart->emulator || !vuart->emulator->put_rx ||
+ !(vuart->flags & VUART_CONSOLE_INPUT))
+ return NULL;
+
+ return vuart;
+}
+
+struct vuart *vuart_find_by_io_range(struct domain *d, unsigned long addr,
+ unsigned long size)
+{
+ struct vuart *vuart = d->console.vuart;
+
+ if ( !vuart || !vuart->info )
+ return NULL;
+
+ if ( addr >= vuart->info->base_addr &&
+ addr + size - 1 <= vuart->info->base_addr + vuart->info->size - 1 )
+ return vuart;
+
+ return NULL;
+}
+
+int vuart_init(struct domain *d, struct vuart_info *info)
+{
+ const struct vuart_emulator *emulator;
+ struct vuart *vuart;
+ int rc;
+
+ if ( d->console.vuart )
+ return -EBUSY;
+
+ emulator = vuart_match_by_compatible(d, info->compatible);
+ if ( !emulator )
+ return -ENODEV;
+
+ vuart = xzalloc(typeof(*vuart));
+ if ( !vuart )
+ return -ENOMEM;
+
+ vuart->info = xvzalloc(typeof(*info));
+ if ( !vuart->info )
+ {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ memcpy(vuart->info, info, sizeof(*info));
+
+ vuart->vdev = emulator->alloc(d, vuart->info);
+ if ( IS_ERR(vuart->vdev) )
+ {
+ rc = PTR_ERR(vuart->vdev);
+ goto err_out;
+ }
+
+ vuart->emulator = emulator;
+ vuart->owner = d;
+ vuart->flags |= VUART_CONSOLE_INPUT;
+
+ d->console.input_allowed = true;
+ d->console.vuart = vuart;
+
+ return 0;
+
+ err_out:
+ if ( vuart )
+ xvfree(vuart->info);
+ xvfree(vuart);
+
+ return rc;
+}
+
+/*
+ * Release any resources taken by UART emulators.
+ *
+ * NB: no flags are cleared, since currently exit() is called only during
+ * domain destroy.
+ */
+void vuart_deinit(struct domain *d)
+{
+ struct vuart *vuart = d->console.vuart;
+
+ if ( vuart )
+ {
+ vuart->emulator->free(vuart->vdev);
+ xvfree(vuart->info);
+ }
+ XVFREE(d->console.vuart);
+}
+
+void vuart_dump_state(const struct domain *d)
+{
+ struct vuart *vuart = d->console.vuart;
+
+ if ( vuart )
+ vuart->emulator->dump_state(vuart->vdev);
+}
+
+/*
+ * Put character to the *first* suitable emulated UART's FIFO.
+ */
+int vuart_put_rx(struct domain *d, char c)
+{
+ const struct vuart *vuart = vuart_find_by_console_permission(d);
+
+ return vuart ? vuart->emulator->put_rx(vuart->vdev, c) : -ENODEV;
+}
+
+bool domain_has_vuart(const struct domain *d)
+{
+ return vuart_find_by_console_permission(d);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
index cb6df2823b00..156e64d9eb58 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;
@@ -352,6 +353,8 @@ static void cf_check dump_domains(unsigned char key)
v->periodic_period / 1000000);
}
}
+
+ vuart_dump_state(d);
}
for_each_domain ( d )
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 9bd5b4825da6..d5164897a776 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -33,6 +33,7 @@
#include <asm/setup.h>
#include <xen/sections.h>
#include <xen/consoled.h>
+#include <xen/vuart.h>
#ifdef CONFIG_X86
#include <asm/guest.h>
@@ -596,11 +597,12 @@ static void __serial_rx(char c)
if ( !d )
return;
- if ( is_hardware_domain(d) )
+ if ( is_hardware_domain(d) && !domain_has_vuart(d) )
{
/*
* 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;
@@ -611,6 +613,8 @@ static void __serial_rx(char c)
*/
send_global_virq(VIRQ_CONSOLE);
}
+ else if ( domain_has_vuart(d) )
+ rc = vuart_put_rx(d, c);
#ifdef CONFIG_SBSA_VUART_CONSOLE
else
/* Deliver input to the emulated UART. */
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 02bdc256ce37..613f4596e33d 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -23,6 +23,7 @@
#include <asm/atomic.h>
#include <asm/current.h>
#include <xen/vpci.h>
+#include <xen/vuart.h>
#include <xen/wait.h>
#include <public/xen.h>
#include <public/domctl.h>
@@ -660,6 +661,9 @@ struct domain
struct {
/* Permission to take ownership of the physical console input. */
bool input_allowed;
+#ifdef CONFIG_VUART_FRAMEWORK
+ struct vuart *vuart;
+#endif
} console;
} __aligned(PAGE_SIZE);
diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
index 8e1844555208..123eee67df35 100644
--- a/xen/include/xen/serial.h
+++ b/xen/include/xen/serial.h
@@ -36,6 +36,9 @@ struct vuart_info {
unsigned long data_off; /* Data register offset */
unsigned long status_off; /* Status register offset */
unsigned long status; /* Ready status value */
+ unsigned int irq; /* Interrupt */
+ char compatible[16]; /* Compatible string */
+ char name[16]; /* User-friendly name */
};
struct serial_port {
diff --git a/xen/include/xen/vuart.h b/xen/include/xen/vuart.h
new file mode 100644
index 000000000000..54f2f29f3f4a
--- /dev/null
+++ b/xen/include/xen/vuart.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UART emulator framework.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+#ifndef XEN_VUART_H
+#define XEN_VUART_H
+
+#include <xen/serial.h>
+#include <public/xen.h>
+
+struct vuart_emulator;
+
+enum {
+ VUART_CONSOLE_INPUT = BIT(0, U), /* Physical console input forwarding. */
+};
+
+
+/*
+ * FIXME: #ifdef is temporary to avoid clash with
+ * arch/arm/include/asm/domain.h
+ */
+#ifdef CONFIG_VUART_FRAMEWORK
+struct vuart {
+ const struct vuart_emulator *emulator;
+ struct vuart_info *info;
+ struct domain *owner;
+ uint32_t flags;
+ void *vdev;
+};
+#endif
+
+struct vuart_emulator {
+ /* UART compatible string. Cannot be NULL or empty. */
+ const char *compatible;
+
+ /*
+ * Allocate emulated UART state (RX/TX FIFOs, locks, initialize registers,
+ * hook I/O handlers, etc.)
+ * Cannot be NULL.
+ */
+ void *(*alloc)(struct domain *d, const struct vuart_info *info);
+
+ /*
+ * Release resources used to emulate UART state (flush RX/TX FIFOs, unhook
+ * I/O handlers, etc.).
+ * Cannot be NULL.
+ */
+ void (*free)(void *arg);
+
+ /*
+ * Print emulated UART state, including registers, on the console.
+ * Can be NULL.
+ */
+ void (*dump_state)(void *arg);
+
+ /*
+ * Place character to the emulated RX FIFO.
+ * Used to forward physical console input to the guest OS.
+ * Can be NULL.
+ */
+ int (*put_rx)(void *arg, char c);
+};
+
+#define VUART_REGISTER(name, x) \
+ static const struct vuart_emulator name##_entry \
+ __used_section(".data.rel.ro.vuart") = x
+
+struct vuart *vuart_find_by_io_range(struct domain *d,
+ unsigned long base_addr,
+ unsigned long size);
+
+int vuart_put_rx(struct domain *d, char c);
+
+#ifdef CONFIG_VUART_FRAMEWORK
+
+int vuart_init(struct domain *d, struct vuart_info *info);
+void vuart_deinit(struct domain *d);
+void vuart_dump_state(const struct domain *d);
+bool domain_has_vuart(const struct domain *d);
+
+#else
+
+static inline int vuart_init(struct domain *d, struct vuart_info *info)
+{
+ return 0;
+}
+
+static inline void vuart_deinit(struct domain *d)
+{
+}
+
+static inline void vuart_dump_state(const struct domain *d)
+{
+}
+
+static inline bool domain_has_vuart(const struct domain *d)
+{
+ return false;
+}
+
+#endif /* CONFIG_VUART_FRAMEWORK */
+
+#endif /* XEN_VUART_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
+
diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
index b126dfe88792..2d65f32ddad3 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_VUART_FRAMEWORK
+#define VUART_ARRAY \
+ . = ALIGN(POINTER_ALIGN); \
+ vuart_array_start = .; \
+ *(.data.rel.ro.vuart) \
+ vuart_array_end = .;
+#else
+#define VUART_ARRAY
+#endif
+
#endif /* __XEN_LDS_H__ */
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 02/15] xen/8250-uart: update definitions
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:02 ` Stefano Stabellini
2025-09-06 14:57 ` Mykola Kvach
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
` (14 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Added missing definitions needed for NS16550 UART emulator.
Newly introduced MSR definitions re-used in the existing ns16550 driver.
Also, corrected FCR DMA definition bit#3 (0x08) as per:
https://www.ti.com/lit/ds/symlink/tl16c550c.pdf
See "7.7.2 FIFO Control Register (FCR)".
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- fixed commentaries
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-3-dmukhin@ford.com/
---
xen/drivers/char/ns16550.c | 16 ++++++------
xen/include/xen/8250-uart.h | 50 ++++++++++++++++++++++++++++++-------
2 files changed, 49 insertions(+), 17 deletions(-)
diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index df7fff7f81df..0e80fadbb894 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -388,7 +388,7 @@ static void __init cf_check ns16550_init_preirq(struct serial_port *port)
/* Check this really is a 16550+. Otherwise we have no FIFOs. */
if ( uart->fifo_size <= 1 &&
- ((ns_read_reg(uart, UART_IIR) & 0xc0) == 0xc0) &&
+ ((ns_read_reg(uart, UART_IIR) & UART_IIR_FE) == UART_IIR_FE) &&
((ns_read_reg(uart, UART_FCR) & UART_FCR_TRG14) == UART_FCR_TRG14) )
uart->fifo_size = 16;
}
@@ -728,20 +728,20 @@ static int __init check_existence(struct ns16550 *uart)
* Mask out IER[7:4] bits for test as some UARTs (e.g. TL
* 16C754B) allow only to modify them if an EFR bit is set.
*/
- scratch2 = ns_read_reg(uart, UART_IER) & 0x0f;
- ns_write_reg(uart,UART_IER, 0x0F);
- scratch3 = ns_read_reg(uart, UART_IER) & 0x0f;
+ scratch2 = ns_read_reg(uart, UART_IER) & UART_IER_MASK;
+ ns_write_reg(uart, UART_IER, UART_IER_MASK);
+ scratch3 = ns_read_reg(uart, UART_IER) & UART_IER_MASK;
ns_write_reg(uart, UART_IER, scratch);
- if ( (scratch2 != 0) || (scratch3 != 0x0F) )
+ if ( (scratch2 != 0) || (scratch3 != UART_IER_MASK) )
return 0;
/*
* Check to see if a UART is really there.
* Use loopback test mode.
*/
- ns_write_reg(uart, UART_MCR, UART_MCR_LOOP | 0x0A);
- status = ns_read_reg(uart, UART_MSR) & 0xF0;
- return (status == 0x90);
+ ns_write_reg(uart, UART_MCR, UART_MCR_LOOP | UART_MCR_RTS | UART_MCR_OUT2);
+ status = ns_read_reg(uart, UART_MSR) & UART_MSR_STATUS;
+ return (status == (UART_MSR_CTS | UART_MSR_DCD));
}
#ifdef CONFIG_HAS_PCI
diff --git a/xen/include/xen/8250-uart.h b/xen/include/xen/8250-uart.h
index d13352940c13..bbbffb14d320 100644
--- a/xen/include/xen/8250-uart.h
+++ b/xen/include/xen/8250-uart.h
@@ -32,6 +32,7 @@
#define UART_MCR 0x04 /* Modem control */
#define UART_LSR 0x05 /* line status */
#define UART_MSR 0x06 /* Modem status */
+#define UART_SCR 0x07 /* Scratch pad */
#define UART_USR 0x1f /* Status register (DW) */
#define UART_DLL 0x00 /* divisor latch (ls) (DLAB=1) */
#define UART_DLM 0x01 /* divisor latch (ms) (DLAB=1) */
@@ -42,6 +43,8 @@
#define UART_IER_ETHREI 0x02 /* tx reg. empty */
#define UART_IER_ELSI 0x04 /* rx line status */
#define UART_IER_EMSI 0x08 /* MODEM status */
+#define UART_IER_MASK \
+ (UART_IER_ERDAI | UART_IER_ETHREI | UART_IER_ELSI | UART_IER_EMSI)
/* Interrupt Identification Register */
#define UART_IIR_NOINT 0x01 /* no interrupt pending */
@@ -51,12 +54,19 @@
#define UART_IIR_THR 0x02 /* - tx reg. empty */
#define UART_IIR_MSI 0x00 /* - MODEM status */
#define UART_IIR_BSY 0x07 /* - busy detect (DW) */
+#define UART_IIR_FE 0xc0 /* FIFO enabled (2 bits) */
/* FIFO Control Register */
-#define UART_FCR_ENABLE 0x01 /* enable FIFO */
-#define UART_FCR_CLRX 0x02 /* clear Rx FIFO */
-#define UART_FCR_CLTX 0x04 /* clear Tx FIFO */
-#define UART_FCR_DMA 0x10 /* enter DMA mode */
+#define UART_FCR_ENABLE BIT(0, U) /* enable FIFO */
+#define UART_FCR_CLRX BIT(1, U) /* clear Rx FIFO */
+#define UART_FCR_CLTX BIT(2, U) /* clear Tx FIFO */
+#define UART_FCR_DMA BIT(3, U) /* enter DMA mode */
+#define UART_FCR_RESERVED0 BIT(4, U) /* reserved; always 0 */
+#define UART_FCR_RESERVED1 BIT(5, U) /* reserved; always 0 */
+#define UART_FCR_RTB0 BIT(6, U) /* receiver trigger bit #0 */
+#define UART_FCR_RTB1 BIT(7, U) /* receiver trigger bit #1 */
+#define UART_FCR_TRG_MASK (UART_FCR_RTB0 | UART_FCR_RTB1)
+
#define UART_FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */
#define UART_FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */
#define UART_FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */
@@ -96,11 +106,32 @@
#define UART_LCR_CONF_MODE_B 0xBF /* Configuration mode B */
/* Modem Control Register */
-#define UART_MCR_DTR 0x01 /* Data Terminal Ready */
-#define UART_MCR_RTS 0x02 /* Request to Send */
-#define UART_MCR_OUT2 0x08 /* OUT2: interrupt mask */
-#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
-#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */
+#define UART_MCR_DTR BIT(0, U) /* Data Terminal Ready */
+#define UART_MCR_RTS BIT(1, U) /* Request to Send */
+#define UART_MCR_OUT1 BIT(2, U) /* Output #1 */
+#define UART_MCR_OUT2 BIT(3, U) /* Output #2 */
+#define UART_MCR_LOOP BIT(4, U) /* Enable loopback test mode */
+#define UART_MCR_RESERVED0 BIT(5, U) /* Reserved #0 */
+#define UART_MCR_TCRTLR BIT(6, U) /* Access TCR/TLR (TI16C752, EFR[4]=1) */
+#define UART_MCR_RESERVED1 BIT(7, U) /* Reserved #1 */
+#define UART_MCR_MASK \
+ (UART_MCR_DTR | UART_MCR_RTS | \
+ UART_MCR_OUT1 | UART_MCR_OUT2 | \
+ UART_MCR_LOOP | UART_MCR_TCRTLR)
+
+/* Modem Status Register */
+#define UART_MSR_DCTS BIT(0, U) /* Change in CTS */
+#define UART_MSR_DDSR BIT(1, U) /* Change in DSR */
+#define UART_MSR_TERI BIT(2, U) /* Change in RI */
+#define UART_MSR_DDCD BIT(3, U) /* Change in DCD */
+#define UART_MSR_CTS BIT(4, U)
+#define UART_MSR_DSR BIT(5, U)
+#define UART_MSR_RI BIT(6, U)
+#define UART_MSR_DCD BIT(7, U)
+#define UART_MSR_CHANGE \
+ (UART_MSR_DCTS | UART_MSR_DDSR | UART_MSR_TERI | UART_MSR_DDCD)
+#define UART_MSR_STATUS \
+ (UART_MSR_CTS | UART_MSR_DSR | UART_MSR_RI | UART_MSR_DCD)
/* Line Status Register */
#define UART_LSR_DR 0x01 /* Data ready */
@@ -111,6 +142,7 @@
#define UART_LSR_THRE 0x20 /* Xmit hold reg empty */
#define UART_LSR_TEMT 0x40 /* Xmitter empty */
#define UART_LSR_ERR 0x80 /* Error */
+#define UART_LSR_MASK (UART_LSR_OE | UART_LSR_BI)
/* These parity settings can be ORed directly into the LCR. */
#define UART_PARITY_NONE (0<<3)
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
2025-09-05 23:27 ` [PATCH v6 02/15] xen/8250-uart: update definitions dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 0:34 ` dmukhin
` (3 more replies)
2025-09-05 23:27 ` [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers dmukhin
` (13 subsequent siblings)
16 siblings, 4 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
The change is the first on the way on introducing minimally functional
NS16550-compatible UART emulator.
Define UART state and a set of emulated registers.
Implement alloc/free vUART hooks.
Stub out I/O port handler.
Add initialization of the NS16x50-compatible UART emulator state machine.
Plumb debug logging.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- v5 feedback
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-4-dmukhin@ford.com/
---
xen/arch/x86/hvm/hvm.c | 21 ++
xen/common/emul/vuart/Kconfig | 19 ++
xen/common/emul/vuart/Makefile | 1 +
xen/common/emul/vuart/ns16x50.c | 366 ++++++++++++++++++++++++++++++++
4 files changed, 407 insertions(+)
create mode 100644 xen/common/emul/vuart/ns16x50.c
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 23bd7f078a1d..91c971f11e14 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -28,6 +28,7 @@
#include <xen/softirq.h>
#include <xen/trace.h>
#include <xen/vm_event.h>
+#include <xen/vuart.h>
#include <xen/vpci.h>
#include <xen/wait.h>
#include <xen/warning.h>
@@ -689,6 +690,22 @@ int hvm_domain_initialise(struct domain *d,
if ( rc != 0 )
goto fail1;
+ /* Limit NS16550 emulator for dom0 only for now. */
+ if ( IS_ENABLED(CONFIG_VUART_NS16X50) && is_hardware_domain(d) )
+ {
+ struct vuart_info info = {
+ .name = "COM2",
+ .compatible = "ns16550",
+ .base_addr = 0x2f8,
+ .size = 8,
+ .irq = 3,
+ };
+
+ rc = vuart_init(d, &info);
+ if ( rc )
+ goto out_vioapic_deinit;
+ }
+
stdvga_init(d);
rtc_init(d);
@@ -712,6 +729,8 @@ int hvm_domain_initialise(struct domain *d,
return 0;
fail2:
+ vuart_deinit(d);
+ out_vioapic_deinit:
vioapic_deinit(d);
fail1:
if ( is_hardware_domain(d) )
@@ -774,6 +793,8 @@ void hvm_domain_destroy(struct domain *d)
if ( hvm_funcs.domain_destroy )
alternative_vcall(hvm_funcs.domain_destroy, d);
+ vuart_deinit(d);
+
vioapic_deinit(d);
XFREE(d->arch.hvm.pl_time);
diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
index ce1b976b7da7..a27d7ca135af 100644
--- a/xen/common/emul/vuart/Kconfig
+++ b/xen/common/emul/vuart/Kconfig
@@ -3,4 +3,23 @@ config VUART_FRAMEWORK
menu "UART Emulation"
+config VUART_NS16X50
+ bool "NS16550-compatible UART Emulator" if EXPERT
+ depends on X86 && HVM
+ select VUART_FRAMEWORK
+ default n
+ help
+ In-hypervisor NS16x50 UART emulation.
+
+ Only legacy PC COM2 port is emulated.
+
+ This is strictly for testing purposes (such as early HVM guest console),
+ and not appropriate for use in production.
+
+config VUART_NS16X50_DEBUG
+ bool "NS16550-compatible UART Emulator Debugging"
+ depends on VUART_NS16X50 && DEBUG
+ help
+ Enable development debugging.
+
endmenu
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..0462a961e785
--- /dev/null
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -0,0 +1,366 @@
+/* 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
+ *
+ * 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 <xen/8250-uart.h>
+#include <xen/console.h>
+#include <xen/err.h>
+#include <xen/iocap.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
+ * - R/O, IIR (IIR_THR) = NS16X50_REGS_NUM + 2
+ */
+#define NS16X50_EMU_REGS_NUM (NS16X50_REGS_NUM + 3)
+
+/*
+ * 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 uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
+{
+ return 0;
+}
+
+/*
+ * 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 = 0xff;
+ 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 = 0xffff;
+ 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 = 0xffffffff;
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * 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)
+{
+#define op(dir) (((dir) == IOREQ_WRITE) ? 'W' : 'R')
+ struct domain *d = rcu_lock_current_domain();
+ struct vuart *vuart = vuart_find_by_io_range(d, addr, size);
+ struct vuart_ns16x50 *vdev;
+ const struct domain *owner;
+ const struct vuart_info *info;
+ uint32_t reg;
+ unsigned dlab;
+ int rc;
+
+ if ( !vuart || !vuart->vdev )
+ {
+ printk(XENLOG_ERR "%c io 0x%04x %d: not initialized\n",
+ op(dir), addr, size);
+
+ ASSERT_UNREACHABLE();
+ goto out;
+ }
+ vdev = vuart->vdev;
+
+ owner = vuart->owner;
+ ASSERT(owner);
+ if ( d != owner )
+ {
+ ns16x50_err(vdev, "%c io 0x%04x %d: does not match current domain %pv\n",
+ op(dir), addr, size, d);
+
+ ASSERT_UNREACHABLE();
+ goto out;
+ }
+
+ info = vuart->info;
+ ASSERT(info);
+ reg = addr - info->base_addr;
+ if ( !IS_ALIGNED(reg, size) )
+ {
+ ns16x50_err(vdev, "%c 0x%04x %d: unaligned access\n",
+ op(dir), addr, size);
+ goto out;
+ }
+
+ dlab = ns16x50_dlab_get(vdev);
+ if ( reg >= NS16X50_REGS_NUM )
+ {
+ ns16x50_err(vdev, "%c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
+ op(dir), addr, size, dlab, reg, *data);
+ goto out;
+ }
+
+ spin_lock(&vdev->lock);
+
+ if ( dir == IOREQ_WRITE )
+ {
+ ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
+ op(dir), addr, size, dlab, reg, *data);
+ rc = ns16x50_io_write(vdev, reg, size, data);
+ }
+ else
+ {
+ rc = ns16x50_io_read(vdev, reg, size, data);
+ ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
+ op(dir), addr, size, dlab, reg, *data);
+ }
+ if ( rc < 0 )
+ ns16x50_err(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
+ op(dir), addr, size, dlab, reg, *data);
+
+ spin_unlock(&vdev->lock);
+
+out:
+ rcu_unlock_domain(d);
+
+ return X86EMUL_OKAY;
+#undef op
+}
+
+static int ns16x50_init(void *arg)
+{
+ struct vuart_ns16x50 *vdev = arg;
+ const struct vuart_info *info = vdev->info;
+ struct domain *d = vdev->owner;
+
+ ASSERT(vdev);
+
+ register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
+
+ 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 ( !info )
+ return ERR_PTR(-EINVAL);
+
+ if ( vuart_find_by_io_range(d, info->base_addr, info->size) )
+ {
+ ns16x50_err(info, "already registered\n");
+ return ERR_PTR(-EBUSY);
+ }
+
+ if ( !is_hvm_domain(d) )
+ {
+ ns16x50_err(info, "not an HVM domain\n");
+ return ERR_PTR(-ENOSYS);
+ }
+
+ vdev = xvzalloc(typeof(*vdev));
+ if ( !vdev )
+ {
+ ns16x50_err(info, "failed to allocate memory\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ spin_lock_init(&vdev->lock);
+ vdev->name = info->name;
+ vdev->owner = d;
+ vdev->info = info;
+
+ rc = ns16x50_init(vdev);
+ if ( rc )
+ 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.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (2 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 21:12 ` Mykola Kvach
2025-09-05 23:27 ` [PATCH v6 05/15] emul/ns16x50: implement SCR register dmukhin
` (12 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add DLL/DLM registers emulation.
DLL/DLM registers report hardcoded 115200 baud rate to the guest OS.
Add stub for ns16x50_dlab_get() helper.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- dropped ns16x50_dlab_get() hunk (moved to emulator stub)
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-5-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 0462a961e785..7f479a5be4a2 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -97,8 +97,13 @@ static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
static int ns16x50_io_write8(
struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
{
+ uint8_t *regs = vdev->regs;
+ uint8_t val = *data;
int rc = 0;
+ if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
+ regs[NS16X50_REGS_NUM + reg] = val;
+
return rc;
}
@@ -109,8 +114,16 @@ static int ns16x50_io_write8(
static int ns16x50_io_write16(
struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data)
{
+ uint16_t val = *data;
int rc = -EINVAL;
+ if ( ns16x50_dlab_get(vdev) && reg == UART_DLL )
+ {
+ vdev->regs[NS16X50_REGS_NUM + UART_DLL] = val & 0xff;
+ vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
+ rc = 0;
+ }
+
return rc;
}
@@ -146,9 +159,13 @@ static int ns16x50_io_write(
static int ns16x50_io_read8(
struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
{
+ uint8_t *regs = vdev->regs;
uint8_t val = 0xff;
int rc = 0;
+ if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
+ val = regs[NS16X50_REGS_NUM + reg];
+
*data = val;
return rc;
@@ -163,6 +180,13 @@ static int ns16x50_io_read16(
uint16_t val = 0xffff;
int rc = -EINVAL;
+ if ( ns16x50_dlab_get(vdev) && reg == UART_DLL )
+ {
+ val = vdev->regs[NS16X50_REGS_NUM + UART_DLM] << 8 |
+ vdev->regs[NS16X50_REGS_NUM + UART_DLL];
+ rc = 0;
+ }
+
*data = val;
return rc;
@@ -280,12 +304,17 @@ out:
static int ns16x50_init(void *arg)
{
+ const uint16_t divisor = (UART_CLOCK_HZ / 115200) >> 4;
struct vuart_ns16x50 *vdev = arg;
const struct vuart_info *info = vdev->info;
struct domain *d = vdev->owner;
ASSERT(vdev);
+ /* NB: report 115200 baud rate. */
+ vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
+ vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
+
register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
return 0;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 05/15] emul/ns16x50: implement SCR register
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (3 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 21:24 ` Mykola Kvach
2025-09-05 23:27 ` [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers dmukhin
` (11 subsequent siblings)
16 siblings, 2 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add SCR register emulation to the I/O port handler.
Firmware (e.g. OVMF) may use SCR during the guest OS boot.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- moved earlier in the series to simplify I/O handler population in
the follow on patches
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-11-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 7f479a5be4a2..51ec85e57627 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -103,6 +103,20 @@ static int ns16x50_io_write8(
if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
regs[NS16X50_REGS_NUM + reg] = val;
+ else
+ {
+ switch ( reg )
+ {
+ /* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
+ case UART_SCR:
+ regs[UART_SCR] = val;
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ }
return rc;
}
@@ -165,6 +179,19 @@ static int ns16x50_io_read8(
if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
val = regs[NS16X50_REGS_NUM + reg];
+ else
+ {
+ switch ( reg )
+ {
+ case UART_SCR:
+ val = regs[UART_SCR];
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ }
*data = val;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (4 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 05/15] emul/ns16x50: implement SCR register dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 1:42 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 07/15] emul/ns16x50: implement LCR/LSR registers dmukhin
` (10 subsequent siblings)
16 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add interrupt enable register emulation (IER) and interrupt identity reason
(IIR) register emulation to the I/O port handler.
Also add routines for asserting/deasserting the virtual ns16x50 interrupt
line as a dependent on IIR code.
Poke ns16x50_irq_check() on every I/O register access because the emulator
does not have clock emulation anyway (e.g. for baud rate emulation).
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- stubbed out ns16x50_iir_check_xxx()
- dropped UART_IIR_THR handling (moved to THR register patch)
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-6-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 135 ++++++++++++++++++++++++++++++++
1 file changed, 135 insertions(+)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 51ec85e57627..a7429c84c36e 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -91,6 +91,121 @@ static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
return 0;
}
+static bool cf_check ns16x50_iir_check_lsi(const struct vuart_ns16x50 *vdev)
+{
+ return false;
+}
+
+static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev)
+{
+ return false;
+}
+
+static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
+{
+ return false;
+}
+
+static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
+{
+ return false;
+}
+
+/*
+ * Get the interrupt identity reason.
+ *
+ * IIR is re-calculated once called, because ns16x50 always reports high
+ * priority events first.
+ * regs[NS16X50_REGS_NUM + UART_IIR] is used to store THR reason only.
+ */
+static uint8_t ns16x50_iir_get(const struct vuart_ns16x50 *vdev)
+{
+ /*
+ * Interrupt identity reasons by priority.
+ * NB: high priority are at lower indexes below.
+ */
+ static const struct {
+ bool (*check)(const struct vuart_ns16x50 *vdev);
+ uint8_t ier;
+ uint8_t iir;
+ } iir_by_prio[] = {
+ [0] = { ns16x50_iir_check_lsi, UART_IER_ELSI, UART_IIR_LSI },
+ [1] = { ns16x50_iir_check_rda, UART_IER_ERDAI, UART_IIR_RDA },
+ [2] = { ns16x50_iir_check_thr, UART_IER_ETHREI, UART_IIR_THR },
+ [3] = { ns16x50_iir_check_msi, UART_IER_EMSI, UART_IIR_MSI },
+ };
+ const uint8_t *regs = vdev->regs;
+ uint8_t iir = 0;
+ unsigned int i;
+
+ /*
+ * NB: every interaction w/ ns16x50 registers (except DLAB=1) goes
+ * through that call.
+ */
+ ASSERT(spin_is_locked(&vdev->lock));
+
+ for ( i = 0; i < ARRAY_SIZE(iir_by_prio); i++ )
+ {
+ if ( (regs[UART_IER] & iir_by_prio[i].ier) &&
+ iir_by_prio[i].check(vdev) )
+ break;
+
+ }
+ if ( i == ARRAY_SIZE(iir_by_prio) )
+ iir |= UART_IIR_NOINT;
+ else
+ iir |= iir_by_prio[i].iir;
+
+ if ( regs[UART_FCR] & UART_FCR_ENABLE )
+ iir |= UART_IIR_FE;
+
+ return iir;
+}
+
+static void ns16x50_irq_assert(const struct vuart_ns16x50 *vdev)
+{
+ struct domain *d = vdev->owner;
+ const struct vuart_info *info = vdev->info;
+ int vector;
+
+ if ( has_vpic(d) )
+ vector = hvm_isa_irq_assert(d, info->irq, vioapic_get_vector);
+ else
+ ASSERT_UNREACHABLE();
+
+ ns16x50_debug(vdev, "IRQ#%d vector %d assert\n", info->irq, vector);
+}
+
+static void ns16x50_irq_deassert(const struct vuart_ns16x50 *vdev)
+{
+ struct domain *d = vdev->owner;
+ const struct vuart_info *info = vdev->info;
+
+ if ( has_vpic(d) )
+ hvm_isa_irq_deassert(d, info->irq);
+ else
+ ASSERT_UNREACHABLE();
+
+ ns16x50_debug(vdev, "IRQ#%d deassert\n", info->irq);
+}
+
+/*
+ * Assert/deassert virtual ns16x50 interrupt line.
+ */
+static void ns16x50_irq_check(const struct vuart_ns16x50 *vdev)
+{
+ uint8_t iir = ns16x50_iir_get(vdev);
+ const struct vuart_info *info = vdev->info;
+
+ if ( iir & UART_IIR_NOINT )
+ ns16x50_irq_deassert(vdev);
+ else
+ ns16x50_irq_assert(vdev);
+
+ ns16x50_debug(vdev, "IRQ#%d IIR 0x%02x %s\n", info->irq, iir,
+ (iir & UART_IIR_NOINT) ? "deassert" : "assert");
+}
+
/*
* Emulate 8-bit write access to ns16x50 register.
*/
@@ -107,6 +222,10 @@ static int ns16x50_io_write8(
{
switch ( reg )
{
+ case UART_IER:
+ regs[UART_IER] = val & UART_IER_MASK;
+ break;
+
/* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
case UART_SCR:
regs[UART_SCR] = val;
@@ -116,6 +235,8 @@ static int ns16x50_io_write8(
rc = -EINVAL;
break;
}
+
+ ns16x50_irq_check(vdev);
}
return rc;
@@ -183,6 +304,14 @@ static int ns16x50_io_read8(
{
switch ( reg )
{
+ case UART_IER:
+ val = regs[UART_IER];
+ break;
+
+ case UART_IIR: /* RO */
+ val = ns16x50_iir_get(vdev);
+ break;
+
case UART_SCR:
val = regs[UART_SCR];
break;
@@ -191,6 +320,8 @@ static int ns16x50_io_read8(
rc = -EINVAL;
break;
}
+
+ ns16x50_irq_check(vdev);
}
*data = val;
@@ -344,6 +475,10 @@ static int ns16x50_init(void *arg)
register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
+ spin_lock(&vdev->lock);
+ ns16x50_irq_check(vdev);
+ spin_unlock(&vdev->lock);
+
return 0;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 07/15] emul/ns16x50: implement LCR/LSR registers
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (5 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:04 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers dmukhin
` (9 subsequent siblings)
16 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add LCR/LSR registers implementation to the I/O port handler.
Add implementation of ns16x50_dlab_get() and ns16x50_iir_check_lsi().
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- Moved earlier in the series so follow on patches (THR/RBR) could make use of
LSR bits more naturally
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-9-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index a7429c84c36e..9d1fe0284362 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -88,12 +88,12 @@ struct vuart_ns16x50 {
static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
{
- return 0;
+ return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
}
static bool cf_check ns16x50_iir_check_lsi(const struct vuart_ns16x50 *vdev)
{
- return false;
+ return vdev->regs[UART_LSR] & UART_LSR_MASK;
}
static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev)
@@ -226,11 +226,16 @@ static int ns16x50_io_write8(
regs[UART_IER] = val & UART_IER_MASK;
break;
+ case UART_LCR:
+ regs[UART_LCR] = val;
+ break;
+
/* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
case UART_SCR:
regs[UART_SCR] = val;
break;
+ case UART_LSR: /* RO */
default:
rc = -EINVAL;
break;
@@ -312,6 +317,15 @@ static int ns16x50_io_read8(
val = ns16x50_iir_get(vdev);
break;
+ case UART_LCR:
+ val = regs[UART_LCR];
+ break;
+
+ case UART_LSR:
+ val = regs[UART_LSR] | UART_LSR_THRE | UART_LSR_TEMT;
+ regs[UART_LSR] = val & ~UART_LSR_MASK;
+ break;
+
case UART_SCR:
val = regs[UART_SCR];
break;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (6 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 07/15] emul/ns16x50: implement LCR/LSR registers dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 1:42 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 09/15] emul/ns16x50: implement RBR register dmukhin
` (8 subsequent siblings)
16 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add MCR/MSR registers emulation to the I/O port handler.
Add implementation of ns16x50_iir_check_msi().
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes in v5:
- Moved earlier in the series
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-10-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 62 ++++++++++++++++++++++++++++++++-
1 file changed, 61 insertions(+), 1 deletion(-)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 9d1fe0284362..a8ec9f6c3a04 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -108,7 +108,7 @@ static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
{
- return false;
+ return vdev->regs[UART_MSR] & UART_MSR_CHANGE;
}
/*
@@ -230,12 +230,63 @@ static int ns16x50_io_write8(
regs[UART_LCR] = val;
break;
+ case UART_MCR: {
+ uint8_t msr_curr, msr_next, msr_delta;
+
+ msr_curr = regs[UART_MSR];
+ msr_next = 0;
+ msr_delta = 0;
+
+ if ( val & UART_MCR_RESERVED0 )
+ ns16x50_warn(vdev, "MCR: attempt to set reserved bit: %x\n",
+ UART_MCR_RESERVED0);
+
+ if ( val & UART_MCR_TCRTLR )
+ ns16x50_warn(vdev, "MCR: not supported: %x\n",
+ UART_MCR_TCRTLR);
+
+ if ( val & UART_MCR_RESERVED1 )
+ ns16x50_warn(vdev, "MCR: attempt to set reserved bit: %x\n",
+ UART_MCR_RESERVED1);
+
+ /* Set modem status */
+ if ( val & UART_MCR_LOOP )
+ {
+ if ( val & UART_MCR_DTR )
+ msr_next |= UART_MSR_DSR;
+ if ( val & UART_MCR_RTS )
+ msr_next |= UART_MSR_CTS;
+ if ( val & UART_MCR_OUT1 )
+ msr_next |= UART_MSR_RI;
+ if ( val & UART_MCR_OUT2 )
+ msr_next |= UART_MSR_DCD;
+ }
+ else
+ msr_next |= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+
+ /* Calculate changes in modem status */
+ if ( (msr_curr & UART_MSR_CTS) ^ (msr_next & UART_MSR_CTS) )
+ msr_delta |= UART_MSR_DCTS;
+ if ( (msr_curr & UART_MSR_DSR) ^ (msr_next & UART_MSR_DSR) )
+ msr_delta |= UART_MSR_DDSR;
+ if ( (msr_curr & UART_MSR_RI) & (msr_next & UART_MSR_RI) )
+ msr_delta |= UART_MSR_TERI;
+ if ( (msr_curr & UART_MSR_DCD) ^ (msr_next & UART_MSR_DCD) )
+ msr_delta |= UART_MSR_DDCD;
+
+ regs[UART_MCR] = val & UART_MCR_MASK;
+ regs[UART_MSR] = msr_next | msr_delta;
+
+ break;
+ }
+
/* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
case UART_SCR:
regs[UART_SCR] = val;
break;
case UART_LSR: /* RO */
+ case UART_MSR: /* RO */
default:
rc = -EINVAL;
break;
@@ -321,11 +372,20 @@ static int ns16x50_io_read8(
val = regs[UART_LCR];
break;
+ case UART_MCR:
+ val = regs[UART_MCR];
+ break;
+
case UART_LSR:
val = regs[UART_LSR] | UART_LSR_THRE | UART_LSR_TEMT;
regs[UART_LSR] = val & ~UART_LSR_MASK;
break;
+ case UART_MSR:
+ val = regs[UART_MSR];
+ regs[UART_MSR] &= ~UART_MSR_CHANGE;
+ break;
+
case UART_SCR:
val = regs[UART_SCR];
break;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 09/15] emul/ns16x50: implement RBR register
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (7 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:04 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 10/15] emul/ns16x50: implement THR register dmukhin
` (7 subsequent siblings)
16 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add RBR register emulation to the I/O port handlder.
Add RX FIFO management code since RBR depends on RX FIFO.
RX FIFO is not emulated as per UART specs for simplicity (not need to emulate
baud rate). Emulator does not emulate NS8250 (no FIFO), NS16550a (16 bytes) or
NS16750 (64 bytes).
RX FIFO is emulated by means of using xencons_interface which conveniently
provides primitives for buffer management and later can be used for
inter-domain communication similarly to vpl011.
Add UART_LSR_DR handling since it depends on RBR register access.
Finally, implement put_rx() vUART hook for placing a character into the
emulated RX FIFO from console driver. That implements physical console
forwarding to the guest OS over emulated NS16550.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- new patch
- Link to v5 (both THR/RBR): https://lore.kernel.org/xen-devel/20250828235409.2835815-7-dmukhin@ford.com/
- Link to v5 (rx_put): https://lore.kernel.org/xen-devel/20250828235409.2835815-12-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 121 +++++++++++++++++++++++++++++++-
1 file changed, 119 insertions(+), 2 deletions(-)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index a8ec9f6c3a04..cac5128f0573 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -86,6 +86,68 @@ struct vuart_ns16x50 {
struct xencons_interface cons; /* Emulated RX/TX FIFOs */
};
+static bool ns16x50_fifo_rx_empty(const struct vuart_ns16x50 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->in_prod == cons->in_cons;
+}
+
+static bool ns16x50_fifo_rx_full(const struct vuart_ns16x50 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->in_prod - cons->in_cons == ARRAY_SIZE(cons->in);
+}
+
+static void ns16x50_fifo_rx_reset(struct vuart_ns16x50 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ cons->in_cons = cons->in_prod;
+}
+
+/*
+ * Transfer character from RX FIFO and return the RX FIFO status after the
+ * transfer.
+ */
+static int ns16x50_fifo_rx_getchar(struct vuart_ns16x50 *vdev, uint8_t *ptr)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ if ( ns16x50_fifo_rx_empty(vdev) )
+ return -ENODATA;
+
+ *ptr = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)];
+ cons->in_cons++;
+
+ return ns16x50_fifo_rx_empty(vdev) ? -ENODATA : 0;
+}
+
+static int ns16x50_fifo_rx_putchar(struct vuart_ns16x50 *vdev, char c)
+{
+ struct xencons_interface *cons = &vdev->cons;
+ int rc;
+
+ /*
+ * FIFO-less 8250/16450 UARTs: newly arrived word overwrites the contents
+ * of the THR.
+ */
+ if ( ns16x50_fifo_rx_full(vdev) )
+ {
+ ns16x50_debug(vdev, "RX FIFO full; resetting\n");
+ ns16x50_fifo_rx_reset(vdev);
+ rc = -ENOSPC;
+ }
+ else
+ rc = 0;
+
+ cons->in[MASK_XENCONS_IDX(cons->in_prod, cons->in)] = c;
+ cons->in_prod++;
+
+ return rc;
+}
+
static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
{
return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
@@ -98,7 +160,7 @@ static bool cf_check ns16x50_iir_check_lsi(const struct vuart_ns16x50 *vdev)
static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev)
{
- return false;
+ return !ns16x50_fifo_rx_empty(vdev);
}
static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
@@ -360,6 +422,17 @@ static int ns16x50_io_read8(
{
switch ( reg )
{
+ case UART_RBR:
+ /* NB: do not forget to clear overrun condition */
+ regs[UART_LSR] &= ~UART_LSR_OE;
+
+ if ( ns16x50_fifo_rx_getchar(vdev, &val) )
+ regs[UART_LSR] &= ~UART_LSR_DR;
+ else
+ regs[UART_LSR] |= UART_LSR_DR;
+
+ break;
+
case UART_IER:
val = regs[UART_IER];
break;
@@ -610,13 +683,57 @@ static void cf_check ns16x50_free(void *arg)
xvfree(arg);
}
+static int cf_check ns16x50_put_rx(void *arg, char ch)
+{
+ struct vuart_ns16x50 *vdev = arg;
+ uint8_t *regs;
+ uint8_t dlab;
+ int rc = -EBUSY;
+
+ spin_lock(&vdev->lock);
+
+ dlab = ns16x50_dlab_get(vdev);
+ regs = vdev->regs;
+
+ if ( dlab )
+ ns16x50_debug(vdev, "THR/RBR access disabled: DLAB=1\n");
+ else if ( regs[UART_MCR] & UART_MCR_LOOP )
+ ns16x50_debug(vdev, "THR/RBR access disabled: loopback mode\n");
+ else
+ {
+ const struct domain *d = vdev->owner;
+
+ /*
+ * Echo the user input on Xen console iff Xen console input is owned
+ * by ns16x50 domain.
+ * NB: use 'console_timestamps=none' to disable Xen timestamps.
+ */
+ if ( is_console_printable(ch) )
+ guest_printk(d, "%c", ch);
+
+ if ( ns16x50_fifo_rx_putchar(vdev, ch) )
+ regs[UART_LSR] |= UART_LSR_OE;
+
+ regs[UART_LSR] |= UART_LSR_DR;
+
+ /* TODO: check FCR when to fire an interrupt */
+ ns16x50_irq_check(vdev);
+
+ rc = 0;
+ }
+
+ spin_unlock(&vdev->lock);
+
+ return rc;
+}
+
#define ns16x50_emulator \
{ \
.compatible = "ns16550", \
.alloc = ns16x50_alloc, \
.free = ns16x50_free, \
.dump_state = NULL, \
- .put_rx = NULL, \
+ .put_rx = ns16x50_put_rx, \
}
VUART_REGISTER(ns16x50, ns16x50_emulator);
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 10/15] emul/ns16x50: implement THR register
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (8 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 09/15] emul/ns16x50: implement RBR register dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 1:59 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 11/15] emul/ns16x50: implement FCR register (write-only) dmukhin
` (6 subsequent siblings)
16 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add THR register emulation to the I/O port handlder.
Add TX FIFO management code since THR depends on TX FIFO.
TX FIFOs is not emulated as per UART specs for simplicity (not need to emulate
baud rate). Emulator does not emulate NS8250 (no FIFO), NS16550a (16 bytes) or
NS16750 (64 bytes).
TX FIFOs is emulated by using xencons_interface which conveniently provides
primitives for buffer management and later can be used for inter-domain
communication similarly to vpl011.
Add UART_IIR_THR interrupt reason handling since it depends on THR register
access.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- new patch
- Link to v5 (both THR/RBR): https://lore.kernel.org/xen-devel/20250828235409.2835815-7-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 103 +++++++++++++++++++++++++++++++-
1 file changed, 102 insertions(+), 1 deletion(-)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index cac5128f0573..987d4c06e23b 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -148,6 +148,66 @@ static int ns16x50_fifo_rx_putchar(struct vuart_ns16x50 *vdev, char c)
return rc;
}
+static bool ns16x50_fifo_tx_full(const struct vuart_ns16x50 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->out_prod - cons->out_cons == ARRAY_SIZE(cons->out);
+}
+
+static void ns16x50_fifo_tx_reset(struct vuart_ns16x50 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ cons->out_cons = cons->out_prod;
+}
+
+/*
+ * Flush cached output to Xen console.
+ */
+static void ns16x50_fifo_tx_flush(struct vuart_ns16x50 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+ struct domain *d = vdev->owner;
+ XENCONS_RING_IDX i, n, len = cons->out_prod - cons->out_cons;
+
+ ASSERT(len <= ARRAY_SIZE(cons->out));
+ if ( !len )
+ return;
+
+ i = MASK_XENCONS_IDX(cons->out_cons, cons->out);
+ n = min_t(XENCONS_RING_IDX, len, ARRAY_SIZE(cons->out) - i);
+ if ( n )
+ guest_printk(d, guest_prefix "%.*s", n, &cons->out[i]);
+
+ i = 0;
+ n = len - n;
+ if ( n )
+ guest_printk(d, guest_prefix "%.*s", n, &cons->out[i]);
+
+ cons->out_cons += len;
+}
+
+/*
+ * Accumulate guest OS output before sending to Xen console.
+ */
+static void ns16x50_fifo_tx_putchar(struct vuart_ns16x50 *vdev, char ch)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ if ( !is_console_printable(ch) )
+ return;
+
+ if ( !ns16x50_fifo_tx_full(vdev) )
+ {
+ cons->out[MASK_XENCONS_IDX(cons->out_prod, cons->out)] = ch;
+ cons->out_prod++;
+ }
+
+ if ( ch == '\n' || ch == '\0' || ns16x50_fifo_tx_full(vdev) )
+ ns16x50_fifo_tx_flush(vdev);
+}
+
static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
{
return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
@@ -165,7 +225,7 @@ static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev)
static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
{
- return false;
+ return vdev->regs[NS16X50_REGS_NUM + UART_IIR] & UART_IIR_THR;
}
static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
@@ -284,7 +344,31 @@ static int ns16x50_io_write8(
{
switch ( reg )
{
+ case UART_THR:
+ if ( regs[UART_MCR] & UART_MCR_LOOP )
+ {
+ if ( ns16x50_fifo_rx_putchar(vdev, val) )
+ regs[UART_LSR] |= UART_LSR_OE;
+
+ regs[UART_LSR] |= UART_LSR_DR;
+ }
+ else
+ {
+ ns16x50_fifo_tx_putchar(vdev, val);
+ regs[NS16X50_REGS_NUM + UART_IIR] |= UART_IIR_THR;
+ }
+
+ break;
+
case UART_IER:
+ /*
+ * NB: Make sure THR interrupt is re-triggered once guest OS
+ * re-enables ETHREI in IER since all THR writes are immediate,
+ * there's no baud rate emulation.
+ */
+ if ( val & regs[UART_IER] & UART_IER_ETHREI )
+ regs[NS16X50_REGS_NUM + UART_IIR] |= UART_IIR_THR;
+
regs[UART_IER] = val & UART_IER_MASK;
break;
@@ -439,6 +523,16 @@ static int ns16x50_io_read8(
case UART_IIR: /* RO */
val = ns16x50_iir_get(vdev);
+
+ /*
+ * Since there's no baud rate emulation, transmits are immediate
+ * to the guest. Clear IIR scratch location to make sure there
+ * will be interrupt generated once guest re-enabled ETHREI in
+ * IER.
+ */
+ if ( val & UART_IIR_THR )
+ regs[NS16X50_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
+
break;
case UART_LCR:
@@ -620,6 +714,9 @@ static int ns16x50_init(void *arg)
vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
+ /* Report UART is ready to transmit. */
+ vdev->regs[NS16X50_REGS_NUM + UART_IIR] = UART_IIR_THR;
+
register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
spin_lock(&vdev->lock);
@@ -634,6 +731,10 @@ static void cf_check ns16x50_deinit(void *arg)
struct vuart_ns16x50 *vdev = arg;
ASSERT(vdev);
+
+ spin_lock(&vdev->lock);
+ ns16x50_fifo_tx_flush(vdev);
+ spin_unlock(&vdev->lock);
}
static void * cf_check ns16x50_alloc(struct domain *d, const struct vuart_info *info)
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 11/15] emul/ns16x50: implement FCR register (write-only)
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (9 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 10/15] emul/ns16x50: implement THR register dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 2:04 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 12/15] emul/ns16550: implement dump_state() hook dmukhin
` (5 subsequent siblings)
16 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Add emulation logic for FCR register.
Note, that does not hook FIFO interrupt moderation to the FIFO management
code for simplicity.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- fixed UART_FCR_CLRX and UART_FCR_CLTX handling
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-8-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 987d4c06e23b..3fc1112709df 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -372,6 +372,36 @@ static int ns16x50_io_write8(
regs[UART_IER] = val & UART_IER_MASK;
break;
+ case UART_FCR: /* WO */
+ if ( val & UART_FCR_RESERVED0 )
+ ns16x50_warn(vdev, "FCR: attempt to set reserved bit: %x\n",
+ UART_FCR_RESERVED0);
+
+ if ( val & UART_FCR_RESERVED1 )
+ ns16x50_warn(vdev, "FCR: attempt to set reserved bit: %x\n",
+ UART_FCR_RESERVED1);
+
+ if ( val & UART_FCR_CLRX )
+ {
+ ns16x50_fifo_rx_reset(vdev);
+ regs[UART_LSR] &= ~UART_LSR_DR;
+ }
+
+ if ( val & UART_FCR_CLTX )
+ {
+ ns16x50_fifo_tx_reset(vdev);
+ regs[NS16X50_REGS_NUM + UART_IIR] |= UART_IIR_THR;
+ }
+
+ if ( val & UART_FCR_ENABLE )
+ val &= UART_FCR_ENABLE | UART_FCR_DMA | UART_FCR_TRG_MASK;
+ else
+ val = 0;
+
+ regs[UART_FCR] = val;
+
+ break;
+
case UART_LCR:
regs[UART_LCR] = val;
break;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 12/15] emul/ns16550: implement dump_state() hook
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (10 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 11/15] emul/ns16x50: implement FCR register (write-only) dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 13/15] x86/domain: enable per-domain I/O port bitmaps dmukhin
` (4 subsequent siblings)
16 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Implement dump_state() vUART hook for debugging UART state machine over Xen
console. dump_state() prints state of all emulated registers (including
state-less IIR) and state of RX/TX FIFOs.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- Fixed printouts formatters
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-13-dmukhin@ford.com/
---
xen/common/emul/vuart/ns16x50.c | 59 ++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 3fc1112709df..a4fa3a9be713 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -648,6 +648,58 @@ static int ns16x50_io_read(
return rc;
}
+static void cf_check ns16x50_dump_state(void *arg)
+{
+#ifdef CONFIG_VUART_NS16X50_DEBUG
+ struct vuart_ns16x50 *vdev = arg;
+ const struct domain *d = vdev->owner;
+ const struct vuart_info *info = vdev->info;
+ const struct xencons_interface *cons;
+ const uint8_t *regs;
+
+ if ( !vdev )
+ return;
+
+ /* Allow printing state in case of a deadlock. */
+ if ( !spin_trylock(&vdev->lock) )
+ return;
+
+ cons = &vdev->cons;
+ regs = &vdev->regs[0];
+
+ printk("Virtual " pr_prefix " (%s) I/O port 0x%04x IRQ#%d owner %pd\n",
+ vdev->name, info->base_addr, info->irq, d);
+
+ printk(" RX FIFO size %ld in_prod %d in_cons %d used %d\n",
+ ARRAY_SIZE(cons->in), cons->in_prod, cons->in_cons,
+ cons->in_prod - cons->in_cons);
+
+ printk(" TX FIFO size %ld out_prod %d out_cons %d used %d\n",
+ ARRAY_SIZE(cons->out), cons->out_prod, cons->out_cons,
+ cons->out_prod - cons->out_cons);
+
+ printk(" %02"PRIx8" RBR %02"PRIx8" THR %02"PRIx8" DLL %02"PRIx8" DLM %02"PRIx8"\n",
+ UART_RBR,
+ cons->in[MASK_XENCONS_IDX(cons->in_prod, cons)],
+ cons->out[MASK_XENCONS_IDX(cons->out_prod, cons)],
+ regs[NS16X50_REGS_NUM + UART_DLL],
+ regs[NS16X50_REGS_NUM + UART_DLM]);
+
+ printk(" %02"PRIx8" IER %02"PRIx8"\n", UART_IER, regs[UART_IER]);
+
+ printk(" %02"PRIx8" FCR %02"PRIx8" IIR %02"PRIx8"\n",
+ UART_FCR, regs[UART_FCR], ns16x50_iir_get(vdev));
+
+ printk(" %02"PRIx8" LCR %02"PRIx8"\n", UART_LCR, regs[UART_LCR]);
+ printk(" %02"PRIx8" MCR %02"PRIx8"\n", UART_MCR, regs[UART_MCR]);
+ printk(" %02"PRIx8" LSR %02"PRIx8"\n", UART_LSR, regs[UART_LSR]);
+ printk(" %02"PRIx8" MSR %02"PRIx8"\n", UART_MSR, regs[UART_MSR]);
+ printk(" %02"PRIx8" SCR %02"PRIx8"\n", UART_SCR, regs[UART_SCR]);
+
+ spin_unlock(&vdev->lock);
+#endif /* CONFIG_VUART_NS16X50_DEBUG */
+}
+
/*
* Emulate I/O access to ns16x50 register.
* Note, emulation always returns X86EMUL_OKAY, once I/O port trap is enabled.
@@ -724,6 +776,9 @@ static int cf_check ns16x50_io_handle(
spin_unlock(&vdev->lock);
+ if ( ns16x50_log_level >= 3 )
+ ns16x50_dump_state(vdev);
+
out:
rcu_unlock_domain(d);
@@ -854,6 +909,8 @@ static int cf_check ns16x50_put_rx(void *arg, char ch)
}
spin_unlock(&vdev->lock);
+ if ( ns16x50_log_level >= 3 )
+ ns16x50_dump_state(vdev);
return rc;
}
@@ -863,7 +920,7 @@ static int cf_check ns16x50_put_rx(void *arg, char ch)
.compatible = "ns16550", \
.alloc = ns16x50_alloc, \
.free = ns16x50_free, \
- .dump_state = NULL, \
+ .dump_state = ns16x50_dump_state, \
.put_rx = ns16x50_put_rx, \
}
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 13/15] x86/domain: enable per-domain I/O port bitmaps
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (11 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 12/15] emul/ns16550: implement dump_state() hook dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 14/15] xen/domain: allocate d->irq_caps before arch-specific initialization dmukhin
` (3 subsequent siblings)
16 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Current design enables all HVM domains share the same I/O port bitmap.
It is necessary for domains crafting its own I/O port address space depending
on the user configuration.
Ensure NS16550 emulator does not share I/O ports with the physical I/O ports,
which is essential for emulation in PVH hwdom case (dom0).
Not a functional change.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- fixed printout formatters in ns16x50_init()
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-14-dmukhin@ford.com/
---
xen/arch/x86/Makefile | 1 +
xen/arch/x86/dom0_build.c | 111 +--------------
xen/arch/x86/hvm/hvm.c | 35 +----
xen/arch/x86/hvm/nestedhvm.c | 8 +-
xen/arch/x86/hvm/quirks.c | 3 -
xen/arch/x86/hvm/svm/nestedsvm.c | 2 +-
xen/arch/x86/hvm/vmx/vvmx.c | 4 +-
xen/arch/x86/include/asm/hvm/nestedhvm.h | 3 +-
xen/arch/x86/include/asm/hvm/support.h | 2 -
xen/arch/x86/include/asm/iocap.h | 2 +
xen/arch/x86/ioport.c | 163 +++++++++++++++++++++++
xen/arch/x86/pv/dom0_build.c | 4 +
xen/common/emul/vuart/ns16x50.c | 11 ++
13 files changed, 200 insertions(+), 149 deletions(-)
create mode 100644 xen/arch/x86/ioport.c
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index d7aed7d92c15..85a8475e126c 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -44,6 +44,7 @@ obj-y += msi.o
obj-y += msr.o
obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o
obj-$(CONFIG_RETURN_THUNK) += indirect-thunk.o
+obj-y += ioport.o
obj-$(CONFIG_PV) += ioport_emulate.o
obj-y += irq.o
obj-$(CONFIG_KEXEC) += machine_kexec.o
diff --git a/xen/arch/x86/dom0_build.c b/xen/arch/x86/dom0_build.c
index 0b467fd4a4fc..26202b33345c 100644
--- a/xen/arch/x86/dom0_build.c
+++ b/xen/arch/x86/dom0_build.c
@@ -298,9 +298,6 @@ int __init parse_arch_dom0_param(const char *s, const char *e)
return 0;
}
-static char __initdata opt_dom0_ioports_disable[200] = "";
-string_param("dom0_ioports_disable", opt_dom0_ioports_disable);
-
static bool __initdata ro_hpet = true;
boolean_param("ro-hpet", ro_hpet);
@@ -433,122 +430,20 @@ unsigned long __init dom0_compute_nr_pages(
return nr_pages;
}
-static void __init process_dom0_ioports_disable(struct domain *dom0)
-{
- unsigned long io_from, io_to;
- char *t, *s = opt_dom0_ioports_disable;
- const char *u;
-
- if ( *s == '\0' )
- return;
-
- while ( (t = strsep(&s, ",")) != NULL )
- {
- io_from = simple_strtoul(t, &u, 16);
- if ( u == t )
- {
- parse_error:
- printk("Invalid ioport range <%s> "
- "in dom0_ioports_disable, skipping\n", t);
- continue;
- }
-
- if ( *u == '\0' )
- io_to = io_from;
- else if ( *u == '-' )
- io_to = simple_strtoul(u + 1, &u, 16);
- else
- goto parse_error;
-
- if ( (*u != '\0') || (io_to < io_from) || (io_to >= 65536) )
- goto parse_error;
-
- printk("Disabling dom0 access to ioport range %04lx-%04lx\n",
- io_from, io_to);
-
- if ( ioports_deny_access(dom0, io_from, io_to) != 0 )
- BUG();
- }
-}
-
+/* Modify I/O memory access permissions. */
int __init dom0_setup_permissions(struct domain *d)
{
unsigned long mfn;
- unsigned int i, offs;
- int rc;
+ unsigned int i;
+ int rc = 0;
if ( pv_shim )
return 0;
- /* The hardware domain is initially permitted full I/O capabilities. */
- rc = ioports_permit_access(d, 0, 0xFFFF);
rc |= iomem_permit_access(d, 0UL,
PFN_DOWN(1UL << domain_max_paddr_bits(d)) - 1);
rc |= irqs_permit_access(d, 1, nr_irqs_gsi - 1);
- /* Modify I/O port access permissions. */
-
- for ( offs = 0, i = ISOLATE_LSB(i8259A_alias_mask) ?: 2;
- offs <= i8259A_alias_mask; offs += i )
- {
- if ( offs & ~i8259A_alias_mask )
- continue;
- /* Master Interrupt Controller (PIC). */
- rc |= ioports_deny_access(d, 0x20 + offs, 0x21 + offs);
- /* Slave Interrupt Controller (PIC). */
- rc |= ioports_deny_access(d, 0xA0 + offs, 0xA1 + offs);
- }
-
- /* ELCR of both PICs. */
- rc |= ioports_deny_access(d, 0x4D0, 0x4D1);
-
- /* Interval Timer (PIT). */
- for ( offs = 0, i = ISOLATE_LSB(pit_alias_mask) ?: 4;
- offs <= pit_alias_mask; offs += i )
- if ( !(offs & ~pit_alias_mask) )
- rc |= ioports_deny_access(d, PIT_CH0 + offs, PIT_MODE + offs);
-
- /* PIT Channel 2 / PC Speaker Control. */
- rc |= ioports_deny_access(d, 0x61, 0x61);
-
- /* INIT# and alternative A20M# control. */
- rc |= ioports_deny_access(d, 0x92, 0x92);
-
- /* IGNNE# control. */
- rc |= ioports_deny_access(d, 0xF0, 0xF0);
-
- /* ACPI PM Timer. */
- if ( pmtmr_ioport )
- rc |= ioports_deny_access(d, pmtmr_ioport, pmtmr_ioport + 3);
-
- /* Reset control. */
- rc |= ioports_deny_access(d, 0xCF9, 0xCF9);
-
- /* PCI configuration space (NB. 0xCF8 has special treatment). */
- rc |= ioports_deny_access(d, 0xCFC, 0xCFF);
-
-#ifdef CONFIG_HVM
- if ( is_hvm_domain(d) )
- {
- /* ISA DMA controller, channels 0-3 (incl possible aliases). */
- rc |= ioports_deny_access(d, 0x00, 0x1F);
- /* ISA DMA controller, page registers (incl various reserved ones). */
- rc |= ioports_deny_access(d, 0x80 + !!hvm_port80_allowed, 0x8F);
- /* ISA DMA controller, channels 4-7 (incl usual aliases). */
- rc |= ioports_deny_access(d, 0xC0, 0xDF);
-
- /* HVM debug console IO port. */
- rc |= ioports_deny_access(d, XEN_HVM_DEBUGCONS_IOPORT,
- XEN_HVM_DEBUGCONS_IOPORT);
- if ( amd_acpi_c1e_quirk )
- rc |= ioports_deny_access(d, acpi_smi_cmd, acpi_smi_cmd);
- }
-#endif
- /* Command-line I/O ranges. */
- process_dom0_ioports_disable(d);
-
- /* Modify I/O memory access permissions. */
-
/* Local APIC. */
if ( mp_lapic_addr != 0 )
{
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 91c971f11e14..93109db77283 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -51,6 +51,7 @@
#include <asm/hvm/vm_event.h>
#include <asm/hvm/vpt.h>
#include <asm/i387.h>
+#include <asm/iocap.h>
#include <asm/mc146818rtc.h>
#include <asm/mce.h>
#include <asm/monitor.h>
@@ -81,14 +82,6 @@ integer_param("hvm_debug", opt_hvm_debug_level);
struct hvm_function_table __ro_after_init hvm_funcs;
-/*
- * The I/O permission bitmap is globally shared by all HVM guests except
- * the hardware domain which needs a more permissive one.
- */
-#define HVM_IOBITMAP_SIZE (3 * PAGE_SIZE)
-unsigned long __section(".bss.page_aligned") __aligned(PAGE_SIZE)
- hvm_io_bitmap[HVM_IOBITMAP_SIZE / BYTES_PER_LONG];
-
/* Xen command-line option to enable HAP */
static bool __initdata opt_hap_enabled = true;
boolean_param("hap", opt_hap_enabled);
@@ -205,15 +198,6 @@ static int __init cf_check hvm_enable(void)
if ( opt_hvm_fep )
warning_add(warning_hvm_fep);
- /*
- * Allow direct access to the PC debug ports 0x80 and 0xed (they are
- * often used for I/O delays, but the vmexits simply slow things down).
- */
- memset(hvm_io_bitmap, ~0, sizeof(hvm_io_bitmap));
- if ( hvm_port80_allowed )
- __clear_bit(0x80, hvm_io_bitmap);
- __clear_bit(0xed, hvm_io_bitmap);
-
register_cpu_notifier(&cpu_nfb);
return 0;
@@ -645,19 +629,12 @@ int hvm_domain_initialise(struct domain *d,
rwlock_init(&d->arch.hvm.pl_time->pt_migrate);
- /* Set the default IO Bitmap. */
- if ( is_hardware_domain(d) )
+ rc = ioports_setup_access(d);
+ if ( rc )
{
- d->arch.hvm.io_bitmap = _xmalloc(HVM_IOBITMAP_SIZE, PAGE_SIZE);
- if ( d->arch.hvm.io_bitmap == NULL )
- {
- rc = -ENOMEM;
- goto fail1;
- }
- memset(d->arch.hvm.io_bitmap, ~0, HVM_IOBITMAP_SIZE);
+ printk("%pd failed to setup I/O bitmap: %d\n", d, rc);
+ goto fail1;
}
- else
- d->arch.hvm.io_bitmap = hvm_io_bitmap;
register_g2m_portio_handler(d);
register_vpci_portio_handler(d);
@@ -684,6 +661,8 @@ int hvm_domain_initialise(struct domain *d,
break;
}
+ BUG_ON(!d->arch.ioport_caps);
+
vpic_init(d);
rc = vioapic_init(d);
diff --git a/xen/arch/x86/hvm/nestedhvm.c b/xen/arch/x86/hvm/nestedhvm.c
index bddd77d8109b..d4e03123d910 100644
--- a/xen/arch/x86/hvm/nestedhvm.c
+++ b/xen/arch/x86/hvm/nestedhvm.c
@@ -107,7 +107,7 @@ nestedhvm_vmcx_flushtlb(struct p2m_domain *p2m)
* The users of the bitmap patterns are in SVM/VMX specific code.
*
* bitmap port 0x80 port 0xed
- * hvm_io_bitmap cleared cleared
+ * hvm.io_bitmap cleared cleared
* iomap[0] cleared set
* iomap[1] set cleared
* iomap[2] set set
@@ -115,7 +115,7 @@ nestedhvm_vmcx_flushtlb(struct p2m_domain *p2m)
static int __init cf_check nestedhvm_setup(void)
{
- /* Same format and size as hvm_io_bitmap (Intel needs only 2 pages). */
+ /* Same format and size as hvm.io_bitmap (Intel needs only 2 pages). */
unsigned nr = cpu_has_vmx ? 2 : 3;
unsigned int i, order = get_order_from_pages(nr);
@@ -165,7 +165,7 @@ static int __init cf_check nestedhvm_setup(void)
__initcall(nestedhvm_setup);
unsigned long *
-nestedhvm_vcpu_iomap_get(bool ioport_80, bool ioport_ed)
+nestedhvm_vcpu_iomap_get(struct vcpu *v, bool ioport_80, bool ioport_ed)
{
int i;
@@ -174,7 +174,7 @@ nestedhvm_vcpu_iomap_get(bool ioport_80, bool ioport_ed)
if (ioport_80 == 0) {
if (ioport_ed == 0)
- return hvm_io_bitmap;
+ return v->domain->arch.hvm.io_bitmap;
i = 0;
} else {
if (ioport_ed == 0)
diff --git a/xen/arch/x86/hvm/quirks.c b/xen/arch/x86/hvm/quirks.c
index 9202f5a47fe9..f4d95441fcff 100644
--- a/xen/arch/x86/hvm/quirks.c
+++ b/xen/arch/x86/hvm/quirks.c
@@ -73,9 +73,6 @@ static int __init cf_check check_port80(void)
dmi_check_system(hvm_no_port80_dmi_table);
- if ( !hvm_port80_allowed )
- __set_bit(0x80, hvm_io_bitmap);
-
return 0;
}
__initcall(check_port80);
diff --git a/xen/arch/x86/hvm/svm/nestedsvm.c b/xen/arch/x86/hvm/svm/nestedsvm.c
index dc2b6a42534a..cc8500b61665 100644
--- a/xen/arch/x86/hvm/svm/nestedsvm.c
+++ b/xen/arch/x86/hvm/svm/nestedsvm.c
@@ -381,7 +381,7 @@ static int nsvm_vmrun_permissionmap(struct vcpu *v, bool viopm)
hvm_unmap_guest_frame(ns_viomap, 0);
}
- svm->ns_iomap = nestedhvm_vcpu_iomap_get(ioport_80, ioport_ed);
+ svm->ns_iomap = nestedhvm_vcpu_iomap_get(v, ioport_80, ioport_ed);
nv->nv_ioport80 = ioport_80;
nv->nv_ioportED = ioport_ed;
diff --git a/xen/arch/x86/hvm/vmx/vvmx.c b/xen/arch/x86/hvm/vmx/vvmx.c
index e4f3a5fe4c71..4da3e6e90e6c 100644
--- a/xen/arch/x86/hvm/vmx/vvmx.c
+++ b/xen/arch/x86/hvm/vmx/vvmx.c
@@ -554,7 +554,7 @@ unsigned long *_shadow_io_bitmap(struct vcpu *v)
port80 = bitmap[0x80 >> 3] & (1 << (0x80 & 0x7)) ? 1 : 0;
portED = bitmap[0xed >> 3] & (1 << (0xed & 0x7)) ? 1 : 0;
- return nestedhvm_vcpu_iomap_get(port80, portED);
+ return nestedhvm_vcpu_iomap_get(v, port80, portED);
}
static void update_msrbitmap(struct vcpu *v, uint32_t shadow_ctrl)
@@ -622,7 +622,7 @@ void nvmx_update_exec_control(struct vcpu *v, u32 host_cntrl)
* L1 VMM doesn't intercept IO instruction.
* Use host configuration and reset IO_BITMAP
*/
- bitmap = hvm_io_bitmap;
+ bitmap = v->domain->arch.hvm.io_bitmap;
}
else {
/* use IO bitmap */
diff --git a/xen/arch/x86/include/asm/hvm/nestedhvm.h b/xen/arch/x86/include/asm/hvm/nestedhvm.h
index ea2c1bc328c7..d691ccb07dd6 100644
--- a/xen/arch/x86/include/asm/hvm/nestedhvm.h
+++ b/xen/arch/x86/include/asm/hvm/nestedhvm.h
@@ -50,7 +50,8 @@ int nestedhvm_hap_nested_page_fault(struct vcpu *v, paddr_t *L2_gpa,
struct npfec npfec);
/* IO permission map */
-unsigned long *nestedhvm_vcpu_iomap_get(bool ioport_80, bool ioport_ed);
+unsigned long *nestedhvm_vcpu_iomap_get(struct vcpu *v,
+ bool ioport_80, bool ioport_ed);
/* Misc */
#define nestedhvm_paging_mode_hap(v) (!!nhvm_vmcx_hap_enabled(v))
diff --git a/xen/arch/x86/include/asm/hvm/support.h b/xen/arch/x86/include/asm/hvm/support.h
index 2a7ba36af06f..7e36d00cc188 100644
--- a/xen/arch/x86/include/asm/hvm/support.h
+++ b/xen/arch/x86/include/asm/hvm/support.h
@@ -41,8 +41,6 @@ extern unsigned int opt_hvm_debug_level;
#define HVM_DBG_LOG(level, _f, _a...) do {} while (0)
#endif
-extern unsigned long hvm_io_bitmap[];
-
enum hvm_translation_result {
HVMTRANS_okay,
HVMTRANS_bad_linear_to_gfn,
diff --git a/xen/arch/x86/include/asm/iocap.h b/xen/arch/x86/include/asm/iocap.h
index f948b7186e95..1083f6171cf7 100644
--- a/xen/arch/x86/include/asm/iocap.h
+++ b/xen/arch/x86/include/asm/iocap.h
@@ -22,6 +22,8 @@
#define cache_flush_permitted(d) \
(has_arch_io_resources(d) || has_arch_pdevs(d))
+int ioports_setup_access(struct domain *d);
+
static inline int ioports_permit_access(struct domain *d, unsigned long s,
unsigned long e)
{
diff --git a/xen/arch/x86/ioport.c b/xen/arch/x86/ioport.c
new file mode 100644
index 000000000000..dbcd52d37a4f
--- /dev/null
+++ b/xen/arch/x86/ioport.c
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Guest I/O port address space configuration.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+#include <xen/domain.h>
+#include <xen/param.h>
+
+#include <asm/amd.h>
+#include <asm/acpi.h>
+#include <asm/io-ports.h>
+#include <asm/iocap.h>
+#include <asm/pv/shim.h>
+#include <asm/setup.h>
+
+static char __initdata opt_dom0_ioports_disable[200] = "";
+string_param("dom0_ioports_disable", opt_dom0_ioports_disable);
+
+/*
+ * The I/O permission bitmap size.
+ * See: comment in nestedhvm_setup()
+ */
+#define HVM_IOBITMAP_SIZE (3 * PAGE_SIZE)
+
+/* Hide user-defined I/O ports from the guest OS. */
+static void process_dom0_ioports_disable(struct domain *dom0)
+{
+ unsigned long io_from, io_to;
+ char *t, *s = opt_dom0_ioports_disable;
+ const char *u;
+
+ if ( *s == '\0' )
+ return;
+
+ while ( (t = strsep(&s, ",")) != NULL )
+ {
+ io_from = simple_strtoul(t, &u, 16);
+ if ( u == t )
+ {
+ parse_error:
+ printk("Invalid ioport range <%s> "
+ "in dom0_ioports_disable, skipping\n", t);
+ continue;
+ }
+
+ if ( *u == '\0' )
+ io_to = io_from;
+ else if ( *u == '-' )
+ io_to = simple_strtoul(u + 1, &u, 16);
+ else
+ goto parse_error;
+
+ if ( (*u != '\0') || (io_to < io_from) || (io_to >= 65536) )
+ goto parse_error;
+
+ printk("Disabling dom0 access to ioport range %04lx-%04lx\n",
+ io_from, io_to);
+
+ if ( ioports_deny_access(dom0, io_from, io_to) != 0 )
+ BUG();
+ }
+}
+
+/* Set the default IO Bitmap. */
+int ioports_setup_access(struct domain *d)
+{
+ unsigned int i, offs;
+ int rc;
+
+ if ( pv_shim )
+ return 0;
+
+#ifdef CONFIG_HVM
+ d->arch.hvm.io_bitmap = _xmalloc(HVM_IOBITMAP_SIZE, PAGE_SIZE);
+ if ( d->arch.hvm.io_bitmap == NULL )
+ return -ENOMEM;
+
+ memset(d->arch.hvm.io_bitmap, ~0, HVM_IOBITMAP_SIZE);
+
+ if ( !is_hardware_domain(d) )
+ {
+ /*
+ * Allow direct access to the PC debug ports 0x80 and 0xed (they are
+ * often used for I/O delays, but the vmexits simply slow things down).
+ */
+ if ( hvm_port80_allowed )
+ __clear_bit(0x80, d->arch.hvm.io_bitmap);
+
+ __clear_bit(0xed, d->arch.hvm.io_bitmap);
+
+ return 0;
+ }
+#endif
+
+ /* The hardware domain is initially permitted full I/O capabilities. */
+ rc = ioports_permit_access(d, 0, 0xFFFF);
+
+ /* Modify I/O port access permissions. */
+
+ for ( offs = 0, i = ISOLATE_LSB(i8259A_alias_mask) ?: 2;
+ offs <= i8259A_alias_mask; offs += i )
+ {
+ if ( offs & ~i8259A_alias_mask )
+ continue;
+ /* Master Interrupt Controller (PIC). */
+ rc |= ioports_deny_access(d, 0x20 + offs, 0x21 + offs);
+ /* Slave Interrupt Controller (PIC). */
+ rc |= ioports_deny_access(d, 0xA0 + offs, 0xA1 + offs);
+ }
+
+ /* ELCR of both PICs. */
+ rc |= ioports_deny_access(d, 0x4D0, 0x4D1);
+
+ /* Interval Timer (PIT). */
+ for ( offs = 0, i = ISOLATE_LSB(pit_alias_mask) ?: 4;
+ offs <= pit_alias_mask; offs += i )
+ if ( !(offs & ~pit_alias_mask) )
+ rc |= ioports_deny_access(d, PIT_CH0 + offs, PIT_MODE + offs);
+
+ /* PIT Channel 2 / PC Speaker Control. */
+ rc |= ioports_deny_access(d, 0x61, 0x61);
+
+ /* INIT# and alternative A20M# control. */
+ rc |= ioports_deny_access(d, 0x92, 0x92);
+
+ /* IGNNE# control. */
+ rc |= ioports_deny_access(d, 0xF0, 0xF0);
+
+ /* ACPI PM Timer. */
+ if ( pmtmr_ioport )
+ rc |= ioports_deny_access(d, pmtmr_ioport, pmtmr_ioport + 3);
+
+ /* Reset control. */
+ rc |= ioports_deny_access(d, 0xCF9, 0xCF9);
+
+ /* PCI configuration space (NB. 0xCF8 has special treatment). */
+ rc |= ioports_deny_access(d, 0xCFC, 0xCFF);
+
+#ifdef CONFIG_HVM
+ if ( is_hvm_domain(d) )
+ {
+ /* ISA DMA controller, channels 0-3 (incl possible aliases). */
+ rc |= ioports_deny_access(d, 0x00, 0x1F);
+ /* ISA DMA controller, page registers (incl various reserved ones). */
+ rc |= ioports_deny_access(d, 0x80 + !!hvm_port80_allowed, 0x8F);
+ /* ISA DMA controller, channels 4-7 (incl usual aliases). */
+ rc |= ioports_deny_access(d, 0xC0, 0xDF);
+
+ /* HVM debug console IO port. */
+ rc |= ioports_deny_access(d, XEN_HVM_DEBUGCONS_IOPORT,
+ XEN_HVM_DEBUGCONS_IOPORT);
+ if ( amd_acpi_c1e_quirk )
+ rc |= ioports_deny_access(d, acpi_smi_cmd, acpi_smi_cmd);
+ }
+#endif
+
+ /* Command-line I/O ranges. */
+ process_dom0_ioports_disable(d);
+
+ return rc;
+}
diff --git a/xen/arch/x86/pv/dom0_build.c b/xen/arch/x86/pv/dom0_build.c
index 21158ce1812e..2b8b4d869ee7 100644
--- a/xen/arch/x86/pv/dom0_build.c
+++ b/xen/arch/x86/pv/dom0_build.c
@@ -17,6 +17,7 @@
#include <asm/bootinfo.h>
#include <asm/bzimage.h>
#include <asm/dom0_build.h>
+#include <asm/iocap.h>
#include <asm/guest.h>
#include <asm/page.h>
#include <asm/pv/mm.h>
@@ -1033,6 +1034,9 @@ static int __init dom0_construct(const struct boot_domain *bd)
if ( test_bit(XENFEAT_supervisor_mode_kernel, parms.f_required) )
panic("Dom0 requires supervisor-mode execution\n");
+ rc = ioports_setup_access(d);
+ BUG_ON(rc != 0);
+
rc = dom0_setup_permissions(d);
BUG_ON(rc != 0);
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index a4fa3a9be713..7c4affe98533 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -792,9 +792,20 @@ static int ns16x50_init(void *arg)
struct vuart_ns16x50 *vdev = arg;
const struct vuart_info *info = vdev->info;
struct domain *d = vdev->owner;
+ int rc;
ASSERT(vdev);
+ /* Disallow sharing physical I/O port */
+ rc = ioports_deny_access(d, info->base_addr,
+ info->base_addr + info->size - 1);
+ if ( rc )
+ {
+ ns16x50_err(info, " virtual I/O port range [0x%04lx..0x%04lx]: conflict w/ physical range\n",
+ info->base_addr, info->base_addr + info->size - 1);
+ return rc;
+ }
+
/* NB: report 115200 baud rate. */
vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 14/15] xen/domain: allocate d->irq_caps before arch-specific initialization
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (12 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 13/15] x86/domain: enable per-domain I/O port bitmaps dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 15/15] emul/ns16x50: implement IRQ emulation via vIOAPIC dmukhin
` (2 subsequent siblings)
16 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
Make sure that NS16550 emulator does not share virtual device IRQ with the
physical one. This is needed for enabling NS16550 emulator for PVH hwdom
(dom0).
To do that, move per-domain interrupt rangeset allocation before arch-specific
code. Add irqs_setup_access() to setup the initial rangeset.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- n/a
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-15-dmukhin@ford.com/
---
xen/arch/x86/dom0_build.c | 1 -
xen/arch/x86/hvm/dom0_build.c | 7 +++++++
xen/arch/x86/include/asm/irq.h | 2 ++
xen/arch/x86/irq.c | 8 ++++++++
xen/arch/x86/pv/dom0_build.c | 3 +++
xen/common/domain.c | 8 ++++++--
xen/common/emul/vuart/ns16x50.c | 9 +++++++++
7 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/xen/arch/x86/dom0_build.c b/xen/arch/x86/dom0_build.c
index 26202b33345c..9dc87efbf3e8 100644
--- a/xen/arch/x86/dom0_build.c
+++ b/xen/arch/x86/dom0_build.c
@@ -442,7 +442,6 @@ int __init dom0_setup_permissions(struct domain *d)
rc |= iomem_permit_access(d, 0UL,
PFN_DOWN(1UL << domain_max_paddr_bits(d)) - 1);
- rc |= irqs_permit_access(d, 1, nr_irqs_gsi - 1);
/* Local APIC. */
if ( mp_lapic_addr != 0 )
diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
index 5551f9044836..245a42dec9aa 100644
--- a/xen/arch/x86/hvm/dom0_build.c
+++ b/xen/arch/x86/hvm/dom0_build.c
@@ -1348,6 +1348,13 @@ int __init dom0_construct_pvh(const struct boot_domain *bd)
*/
pvh_setup_mmcfg(d);
+ rc = irqs_setup_access(d);
+ if ( rc )
+ {
+ printk("%pd unable to setup IRQ rangeset: %d\n", d, rc);
+ return rc;
+ }
+
/*
* Setup permissions early so that calls to add MMIO regions to the
* p2m as part of vPCI setup don't fail due to permission checks.
diff --git a/xen/arch/x86/include/asm/irq.h b/xen/arch/x86/include/asm/irq.h
index 8c81f66434a8..8bffec3bbfee 100644
--- a/xen/arch/x86/include/asm/irq.h
+++ b/xen/arch/x86/include/asm/irq.h
@@ -231,4 +231,6 @@ int allocate_and_map_gsi_pirq(struct domain *d, int index, int *pirq_p);
int allocate_and_map_msi_pirq(struct domain *d, int index, int *pirq_p,
int type, struct msi_info *msi);
+int irqs_setup_access(struct domain *d);
+
#endif /* _ASM_HW_IRQ_H */
diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c
index 556134f85aa0..079277be719d 100644
--- a/xen/arch/x86/irq.c
+++ b/xen/arch/x86/irq.c
@@ -3046,3 +3046,11 @@ int allocate_and_map_msi_pirq(struct domain *d, int index, int *pirq_p,
return ret;
}
+
+int irqs_setup_access(struct domain *d)
+{
+ if ( is_hardware_domain(d) )
+ return irqs_permit_access(d, 1, nr_irqs_gsi - 1);
+
+ return 0;
+}
diff --git a/xen/arch/x86/pv/dom0_build.c b/xen/arch/x86/pv/dom0_build.c
index 2b8b4d869ee7..1a092b802833 100644
--- a/xen/arch/x86/pv/dom0_build.c
+++ b/xen/arch/x86/pv/dom0_build.c
@@ -1037,6 +1037,9 @@ static int __init dom0_construct(const struct boot_domain *bd)
rc = ioports_setup_access(d);
BUG_ON(rc != 0);
+ rc = irqs_setup_access(d);
+ BUG_ON(rc != 0);
+
rc = dom0_setup_permissions(d);
BUG_ON(rc != 0);
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 775c33928585..edf76b02e1a1 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -952,6 +952,11 @@ struct domain *domain_create(domid_t domid,
radix_tree_init(&d->pirq_tree);
#endif
+ err = -ENOMEM;
+ d->irq_caps = rangeset_new(d, "Interrupts", 0);
+ if ( !d->irq_caps )
+ goto fail;
+
if ( (err = arch_domain_create(d, config, flags)) != 0 )
goto fail;
init_status |= INIT_arch;
@@ -961,8 +966,7 @@ struct domain *domain_create(domid_t domid,
err = -ENOMEM;
d->iomem_caps = rangeset_new(d, "I/O Memory", RANGESETF_prettyprint_hex);
- d->irq_caps = rangeset_new(d, "Interrupts", 0);
- if ( !d->iomem_caps || !d->irq_caps )
+ if ( !d->iomem_caps )
goto fail;
if ( (err = xsm_domain_create(XSM_HOOK, d, config->ssidref)) != 0 )
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index 7c4affe98533..bcbd765b815d 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -806,6 +806,15 @@ static int ns16x50_init(void *arg)
return rc;
}
+ /* Disallow sharing physical IRQ */
+ rc = irq_deny_access(d, info->irq);
+ if ( rc )
+ {
+ ns16x50_err(info, "virtual IRQ#%d: conflict w/ physical IRQ: %d\n",
+ info->irq, rc);
+ return rc;
+ }
+
/* NB: report 115200 baud rate. */
vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v6 15/15] emul/ns16x50: implement IRQ emulation via vIOAPIC
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (13 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 14/15] xen/domain: allocate d->irq_caps before arch-specific initialization dmukhin
@ 2025-09-05 23:27 ` dmukhin
2025-09-06 0:33 ` [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
2025-09-06 2:01 ` Stefano Stabellini
16 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-05 23:27 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
From: Denis Mukhin <dmukhin@ford.com>
PVH domains use vIOAPIC, not vPIC and NS16550 emulates ISA IRQs which cannot
be asserted on vIOAPIC.
{map,unmap}_domain_pirq_emuirq() infrastructure is modified by adding new
type of interrupt resources 'IRQ_EMU' which means 'emulated device IRQ'
(similarly to IRQ_MSI_EMU).
This is necessary to for IOAPIC emulation code to skip IRQ->PIRQ mapping
(vioapic_hwdom_map_gsi()) when guest OS unmasks vIOAPIC pin corresponding to
virtual device's IRQ.
Also, hvm_gsi_eoi() is modified to trigger assertion in hvm_gsi_deassert()
path for ISA IRQs.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v5:
- did cosmetic renaming and dropped unneeded changes
- fixed ns16x50_irq_assert() and ns16x50_irq_deassert() as per Jan's
suggestion in v4 (missed to address in v5)
- fixed __hvm_dpci_eoi()
- Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-16-dmukhin@ford.com/
---
xen/arch/x86/hvm/vioapic.c | 10 ++++++++++
xen/arch/x86/include/asm/irq.h | 6 ++++++
xen/common/emul/vuart/ns16x50.c | 28 ++++++++++++++++++++++++++++
xen/drivers/passthrough/x86/hvm.c | 11 ++++++++++-
4 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
index 7c725f9e471f..6314874b64f7 100644
--- a/xen/arch/x86/hvm/vioapic.c
+++ b/xen/arch/x86/hvm/vioapic.c
@@ -177,6 +177,16 @@ static int vioapic_hwdom_map_gsi(unsigned int gsi, unsigned int trig,
ASSERT(is_hardware_domain(currd));
+ /*
+ * Interrupt is claimed by one of the platform virtual devices (e.g.
+ * NS16550); do nothing.
+ */
+ write_lock(&currd->event_lock);
+ ret = is_domain_emuirq_claimed(currd, gsi);
+ write_unlock(&currd->event_lock);
+ if ( ret )
+ return 0;
+
/* Interrupt has been unmasked, bind it now. */
ret = mp_register_gsi(gsi, trig, pol);
if ( ret == -EEXIST )
diff --git a/xen/arch/x86/include/asm/irq.h b/xen/arch/x86/include/asm/irq.h
index 8bffec3bbfee..bdbe700274e9 100644
--- a/xen/arch/x86/include/asm/irq.h
+++ b/xen/arch/x86/include/asm/irq.h
@@ -168,6 +168,11 @@ void free_domain_pirqs(struct domain *d);
int map_domain_emuirq_pirq(struct domain *d, int pirq, int emuirq);
int unmap_domain_pirq_emuirq(struct domain *d, int pirq);
+#define domain_emuirq_claim(d, irq) map_domain_emuirq_pirq(d, irq, IRQ_EMU)
+#define domain_emuirq_unclaim(d, irq) unmap_domain_pirq_emuirq(d, irq)
+#define is_domain_emuirq_claimed(d, irq) \
+ (domain_pirq_to_emuirq(d, irq) != IRQ_UNBOUND)
+
/* Evacuate interrupts assigned to CPUs not present in the CPU online map. */
void fixup_irqs(void);
void fixup_eoi(void);
@@ -221,6 +226,7 @@ void cleanup_domain_irq_mapping(struct domain *d);
#define IRQ_UNBOUND (-1)
#define IRQ_PT (-2)
#define IRQ_MSI_EMU (-3)
+#define IRQ_EMU (-4)
bool cpu_has_pending_apic_eoi(void);
diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
index bcbd765b815d..723b1b0bb55d 100644
--- a/xen/common/emul/vuart/ns16x50.c
+++ b/xen/common/emul/vuart/ns16x50.c
@@ -292,6 +292,8 @@ static void ns16x50_irq_assert(const struct vuart_ns16x50 *vdev)
if ( has_vpic(d) )
vector = hvm_isa_irq_assert(d, info->irq, vioapic_get_vector);
+ else if ( has_vioapic(d) )
+ vector = hvm_ioapic_assert(d, info->irq, false);
else
ASSERT_UNREACHABLE();
@@ -305,6 +307,8 @@ static void ns16x50_irq_deassert(const struct vuart_ns16x50 *vdev)
if ( has_vpic(d) )
hvm_isa_irq_deassert(d, info->irq);
+ else if ( has_vioapic(d) )
+ hvm_ioapic_deassert(d, info->irq);
else
ASSERT_UNREACHABLE();
@@ -815,6 +819,17 @@ static int ns16x50_init(void *arg)
return rc;
}
+ /* Claim virtual IRQ */
+ write_lock(&d->event_lock);
+ rc = domain_emuirq_claim(d, info->irq);
+ write_unlock(&d->event_lock);
+ if ( rc )
+ {
+ ns16x50_err(info, "virtual IRQ#%d: cannot claim: %d\n",
+ info->irq, rc);
+ return rc;
+ }
+
/* NB: report 115200 baud rate. */
vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
@@ -834,9 +849,22 @@ static int ns16x50_init(void *arg)
static void cf_check ns16x50_deinit(void *arg)
{
struct vuart_ns16x50 *vdev = arg;
+ const struct vuart_info *info;
+ struct domain *d;
+ int rc;
ASSERT(vdev);
+ d = vdev->owner;
+ info = vdev->info;
+
+ write_lock(&d->event_lock);
+ rc = domain_emuirq_unclaim(d, info->irq);
+ write_unlock(&d->event_lock);
+ if ( rc )
+ ns16x50_err(vdev, "virtual IRQ#%d: cannot unclaim: %d\n",
+ info->irq, rc);
+
spin_lock(&vdev->lock);
ns16x50_fifo_tx_flush(vdev);
spin_unlock(&vdev->lock);
diff --git a/xen/drivers/passthrough/x86/hvm.c b/xen/drivers/passthrough/x86/hvm.c
index a2ca7e0e570c..20641194561f 100644
--- a/xen/drivers/passthrough/x86/hvm.c
+++ b/xen/drivers/passthrough/x86/hvm.c
@@ -922,7 +922,16 @@ static void __hvm_dpci_eoi(struct domain *d,
static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
{
- struct pirq *pirq = pirq_info(d, gsi);
+ struct pirq *pirq;
+
+ /* Check if GSI is claimed by one of the virtual devices. */
+ if ( is_domain_emuirq_claimed(d, gsi) )
+ {
+ hvm_gsi_deassert(d, gsi);
+ return;
+ }
+
+ pirq = pirq_info(d, gsi);
/* Check if GSI is actually mapped. */
if ( !pirq_dpci(pirq) )
--
2.51.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (14 preceding siblings ...)
2025-09-05 23:27 ` [PATCH v6 15/15] emul/ns16x50: implement IRQ emulation via vIOAPIC dmukhin
@ 2025-09-06 0:33 ` dmukhin
2025-09-06 2:01 ` Stefano Stabellini
16 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-06 0:33 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
On Fri, Sep 05, 2025 at 04:26:59PM -0700, dmukhin@xen.org wrote:
>
> HVM
> ---
> Tested only boot of HVM linux guest with OVMF as the virtual firmware.
> SeaBIOS as a virtual firmware is not tested.
Sorry, current series will enable the emulator for hwdom PVH only.
To enable the emulator for HVM, an extra change is neeed.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
@ 2025-09-06 0:34 ` dmukhin
2025-09-06 2:03 ` Stefano Stabellini
` (2 subsequent siblings)
3 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-06 0:34 UTC (permalink / raw)
To: xen-devel
Cc: andrew.cooper3, anthony.perard, jbeulich, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
On Fri, Sep 05, 2025 at 04:27:02PM -0700, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
[..]
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,23 @@ config VUART_FRAMEWORK
>
> menu "UART Emulation"
>
> +config VUART_NS16X50
> + bool "NS16550-compatible UART Emulator" if EXPERT
> + depends on X86 && HVM
> + select VUART_FRAMEWORK
> + default n
Forgot to drop 'default n'
> + help
> + In-hypervisor NS16x50 UART emulation.
> +
> + Only legacy PC COM2 port is emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers
2025-09-05 23:27 ` [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers dmukhin
@ 2025-09-06 1:42 ` Stefano Stabellini
2025-09-08 2:37 ` dmukhin
0 siblings, 1 reply; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 1:42 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> +/*
> + * Get the interrupt identity reason.
> + *
> + * IIR is re-calculated once called, because ns16x50 always reports high
> + * priority events first.
> + * regs[NS16X50_REGS_NUM + UART_IIR] is used to store THR reason only.
> + */
> +static uint8_t ns16x50_iir_get(const struct vuart_ns16x50 *vdev)
> +{
> + /*
> + * Interrupt identity reasons by priority.
> + * NB: high priority are at lower indexes below.
> + */
> + static const struct {
> + bool (*check)(const struct vuart_ns16x50 *vdev);
> + uint8_t ier;
> + uint8_t iir;
> + } iir_by_prio[] = {
> + [0] = { ns16x50_iir_check_lsi, UART_IER_ELSI, UART_IIR_LSI },
> + [1] = { ns16x50_iir_check_rda, UART_IER_ERDAI, UART_IIR_RDA },
> + [2] = { ns16x50_iir_check_thr, UART_IER_ETHREI, UART_IIR_THR },
> + [3] = { ns16x50_iir_check_msi, UART_IER_EMSI, UART_IIR_MSI },
> + };
> + const uint8_t *regs = vdev->regs;
> + uint8_t iir = 0;
> + unsigned int i;
> +
> + /*
> + * NB: every interaction w/ ns16x50 registers (except DLAB=1) goes
> + * through that call.
> + */
> + ASSERT(spin_is_locked(&vdev->lock));
> +
> + for ( i = 0; i < ARRAY_SIZE(iir_by_prio); i++ )
> + {
> + if ( (regs[UART_IER] & iir_by_prio[i].ier) &&
> + iir_by_prio[i].check(vdev) )
> + break;
> +
> + }
> + if ( i == ARRAY_SIZE(iir_by_prio) )
> + iir |= UART_IIR_NOINT;
> + else
> + iir |= iir_by_prio[i].iir;
> +
> + if ( regs[UART_FCR] & UART_FCR_ENABLE )
> + iir |= UART_IIR_FE;
> +
> + return iir;
> +}
> +
> +static void ns16x50_irq_assert(const struct vuart_ns16x50 *vdev)
> +{
> + struct domain *d = vdev->owner;
> + const struct vuart_info *info = vdev->info;
> + int vector;
> +
> + if ( has_vpic(d) )
> + vector = hvm_isa_irq_assert(d, info->irq, vioapic_get_vector);
> + else
> + ASSERT_UNREACHABLE();
This seems dangerous as there are guests without vpic
> + ns16x50_debug(vdev, "IRQ#%d vector %d assert\n", info->irq, vector);
> +}
> +
> +static void ns16x50_irq_deassert(const struct vuart_ns16x50 *vdev)
> +{
> + struct domain *d = vdev->owner;
> + const struct vuart_info *info = vdev->info;
> +
> + if ( has_vpic(d) )
> + hvm_isa_irq_deassert(d, info->irq);
> + else
> + ASSERT_UNREACHABLE();
also here
> + ns16x50_debug(vdev, "IRQ#%d deassert\n", info->irq);
> +}
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers
2025-09-05 23:27 ` [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers dmukhin
@ 2025-09-06 1:42 ` Stefano Stabellini
2025-09-08 2:35 ` dmukhin
0 siblings, 1 reply; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 1:42 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add MCR/MSR registers emulation to the I/O port handler.
>
> Add implementation of ns16x50_iir_check_msi().
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes in v5:
> - Moved earlier in the series
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-10-dmukhin@ford.com/
> ---
> xen/common/emul/vuart/ns16x50.c | 62 ++++++++++++++++++++++++++++++++-
> 1 file changed, 61 insertions(+), 1 deletion(-)
>
> diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> index 9d1fe0284362..a8ec9f6c3a04 100644
> --- a/xen/common/emul/vuart/ns16x50.c
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -108,7 +108,7 @@ static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
>
> static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
> {
> - return false;
> + return vdev->regs[UART_MSR] & UART_MSR_CHANGE;
> }
>
> /*
> @@ -230,12 +230,63 @@ static int ns16x50_io_write8(
> regs[UART_LCR] = val;
> break;
>
> + case UART_MCR: {
> + uint8_t msr_curr, msr_next, msr_delta;
> +
> + msr_curr = regs[UART_MSR];
> + msr_next = 0;
> + msr_delta = 0;
> +
> + if ( val & UART_MCR_RESERVED0 )
> + ns16x50_warn(vdev, "MCR: attempt to set reserved bit: %x\n",
> + UART_MCR_RESERVED0);
> +
> + if ( val & UART_MCR_TCRTLR )
> + ns16x50_warn(vdev, "MCR: not supported: %x\n",
> + UART_MCR_TCRTLR);
> +
> + if ( val & UART_MCR_RESERVED1 )
> + ns16x50_warn(vdev, "MCR: attempt to set reserved bit: %x\n",
> + UART_MCR_RESERVED1);
> +
> + /* Set modem status */
> + if ( val & UART_MCR_LOOP )
> + {
> + if ( val & UART_MCR_DTR )
> + msr_next |= UART_MSR_DSR;
> + if ( val & UART_MCR_RTS )
> + msr_next |= UART_MSR_CTS;
> + if ( val & UART_MCR_OUT1 )
> + msr_next |= UART_MSR_RI;
> + if ( val & UART_MCR_OUT2 )
> + msr_next |= UART_MSR_DCD;
> + }
> + else
> + msr_next |= UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
> +
> + /* Calculate changes in modem status */
> + if ( (msr_curr & UART_MSR_CTS) ^ (msr_next & UART_MSR_CTS) )
> + msr_delta |= UART_MSR_DCTS;
> + if ( (msr_curr & UART_MSR_DSR) ^ (msr_next & UART_MSR_DSR) )
> + msr_delta |= UART_MSR_DDSR;
> + if ( (msr_curr & UART_MSR_RI) & (msr_next & UART_MSR_RI) )
> + msr_delta |= UART_MSR_TERI;
Should this be:
if ( (msr_curr & UART_MSR_RI) && !(msr_next & UART_MSR_RI) )
msr_delta |= UART_MSR_TERI;
?
> + if ( (msr_curr & UART_MSR_DCD) ^ (msr_next & UART_MSR_DCD) )
> + msr_delta |= UART_MSR_DDCD;
> +
> + regs[UART_MCR] = val & UART_MCR_MASK;
> + regs[UART_MSR] = msr_next | msr_delta;
> +
> + break;
> + }
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 10/15] emul/ns16x50: implement THR register
2025-09-05 23:27 ` [PATCH v6 10/15] emul/ns16x50: implement THR register dmukhin
@ 2025-09-06 1:59 ` Stefano Stabellini
2025-09-08 2:50 ` dmukhin
0 siblings, 1 reply; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 1:59 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add THR register emulation to the I/O port handlder.
>
> Add TX FIFO management code since THR depends on TX FIFO.
>
> TX FIFOs is not emulated as per UART specs for simplicity (not need to emulate
> baud rate). Emulator does not emulate NS8250 (no FIFO), NS16550a (16 bytes) or
> NS16750 (64 bytes).
>
> TX FIFOs is emulated by using xencons_interface which conveniently provides
> primitives for buffer management and later can be used for inter-domain
> communication similarly to vpl011.
>
> Add UART_IIR_THR interrupt reason handling since it depends on THR register
> access.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - new patch
> - Link to v5 (both THR/RBR): https://lore.kernel.org/xen-devel/20250828235409.2835815-7-dmukhin@ford.com/
> ---
> xen/common/emul/vuart/ns16x50.c | 103 +++++++++++++++++++++++++++++++-
> 1 file changed, 102 insertions(+), 1 deletion(-)
>
> diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> index cac5128f0573..987d4c06e23b 100644
> --- a/xen/common/emul/vuart/ns16x50.c
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -148,6 +148,66 @@ static int ns16x50_fifo_rx_putchar(struct vuart_ns16x50 *vdev, char c)
> return rc;
> }
>
> +static bool ns16x50_fifo_tx_full(const struct vuart_ns16x50 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->out_prod - cons->out_cons == ARRAY_SIZE(cons->out);
> +}
> +
> +static void ns16x50_fifo_tx_reset(struct vuart_ns16x50 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + cons->out_cons = cons->out_prod;
> +}
> +
> +/*
> + * Flush cached output to Xen console.
> + */
> +static void ns16x50_fifo_tx_flush(struct vuart_ns16x50 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> + struct domain *d = vdev->owner;
> + XENCONS_RING_IDX i, n, len = cons->out_prod - cons->out_cons;
> +
> + ASSERT(len <= ARRAY_SIZE(cons->out));
> + if ( !len )
> + return;
> +
> + i = MASK_XENCONS_IDX(cons->out_cons, cons->out);
> + n = min_t(XENCONS_RING_IDX, len, ARRAY_SIZE(cons->out) - i);
> + if ( n )
> + guest_printk(d, guest_prefix "%.*s", n, &cons->out[i]);
> +
> + i = 0;
> + n = len - n;
> + if ( n )
> + guest_printk(d, guest_prefix "%.*s", n, &cons->out[i]);
> +
> + cons->out_cons += len;
> +}
> +
> +/*
> + * Accumulate guest OS output before sending to Xen console.
> + */
> +static void ns16x50_fifo_tx_putchar(struct vuart_ns16x50 *vdev, char ch)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + if ( !is_console_printable(ch) )
> + return;
> +
> + if ( !ns16x50_fifo_tx_full(vdev) )
> + {
> + cons->out[MASK_XENCONS_IDX(cons->out_prod, cons->out)] = ch;
> + cons->out_prod++;
> + }
> +
> + if ( ch == '\n' || ch == '\0' || ns16x50_fifo_tx_full(vdev) )
> + ns16x50_fifo_tx_flush(vdev);
> +}
> +
> static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
> {
> return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
> @@ -165,7 +225,7 @@ static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev)
>
> static bool cf_check ns16x50_iir_check_thr(const struct vuart_ns16x50 *vdev)
> {
> - return false;
> + return vdev->regs[NS16X50_REGS_NUM + UART_IIR] & UART_IIR_THR;
> }
>
> static bool cf_check ns16x50_iir_check_msi(const struct vuart_ns16x50 *vdev)
> @@ -284,7 +344,31 @@ static int ns16x50_io_write8(
> {
> switch ( reg )
> {
> + case UART_THR:
> + if ( regs[UART_MCR] & UART_MCR_LOOP )
> + {
> + if ( ns16x50_fifo_rx_putchar(vdev, val) )
> + regs[UART_LSR] |= UART_LSR_OE;
> +
> + regs[UART_LSR] |= UART_LSR_DR;
> + }
> + else
> + {
> + ns16x50_fifo_tx_putchar(vdev, val);
> + regs[NS16X50_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> + }
> + break;
> +
> case UART_IER:
> + /*
> + * NB: Make sure THR interrupt is re-triggered once guest OS
> + * re-enables ETHREI in IER since all THR writes are immediate,
> + * there's no baud rate emulation.
> + */
> + if ( val & regs[UART_IER] & UART_IER_ETHREI )
> + regs[NS16X50_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> regs[UART_IER] = val & UART_IER_MASK;
> break;
>
> @@ -439,6 +523,16 @@ static int ns16x50_io_read8(
>
> case UART_IIR: /* RO */
> val = ns16x50_iir_get(vdev);
> +
> + /*
> + * Since there's no baud rate emulation, transmits are immediate
> + * to the guest. Clear IIR scratch location to make sure there
> + * will be interrupt generated once guest re-enabled ETHREI in
> + * IER.
> + */
> + if ( val & UART_IIR_THR )
> + regs[NS16X50_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
Why clear UART_IIR_THR here?
UART_IIR_THR should be set if the out buffer is not full and should not
be set of the out buffer is full?
Given that the only function adding to out is ns16x50_fifo_tx_putchar,
and given that ns16x50_fifo_tx_putchar clears the out buffer when full
by calling ns16x50_fifo_tx_flush if ns16x50_fifo_tx_full, then basically
we can keep UART_IIR_THR set all the time?
> break;
>
> case UART_LCR:
> @@ -620,6 +714,9 @@ static int ns16x50_init(void *arg)
> vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
> vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
>
> + /* Report UART is ready to transmit. */
> + vdev->regs[NS16X50_REGS_NUM + UART_IIR] = UART_IIR_THR;
> +
> register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
>
> spin_lock(&vdev->lock);
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
` (15 preceding siblings ...)
2025-09-06 0:33 ` [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
@ 2025-09-06 2:01 ` Stefano Stabellini
2025-09-08 9:04 ` Mykola Kvach
16 siblings, 1 reply; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:01 UTC (permalink / raw)
To: oleksii.kurochko
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin, dmukhin
Oleksii and all,
I would like to consider patches 1-12 of this patch series for 4.21,
pending the few minor comments I made addressed.
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> x86 port of Xen lacks vUART facility similar to Arm's vpl011 to support x86
> guest OS bring up in the embedded setups.
>
> This patch series introduces initial in-hypervisor emulator for
> NS8250/NS16x50-compatible UARTs under CONFIG_VUART_NS16X50.
>
> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> early guest firmware and OS bringup debugging, because it eliminates
> dependency on the external emulator (qemu) being operational by the time
> domains are created.
>
> The emulator also allows to forward the physical console input to the x86
> domain which is useful when a system has only one physical UART for early
> debugging and this UART is owned by Xen.
>
> By default, CONFIG_VUART_NS16X50 enables emulation of NS16550 at I/O port
> 0x2f8, IRQ#3 in guest OS (legacy COM2). Legacy COM resources cannot be
> selected at built-time or via per-domain xl configuration in this initial
> submission.
>
> CONFIG_VUART_NS16X50_DEBUG enables some extra debugging facilities useful
> for NS16550 emulator development/debugging (disabled by default).
>
> The NS16550 emulator is disabled in default x86 configuration and goes under
> CONFIG_EXPERT in Kconfig.
>
> Limitations
> ===========
> - Only x86;
> - Only legacy COM2 resources, custom I/O ports/IRQs are not supported;
> - 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 toolstack integration;
> - 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 MMIO-based UART emulation.
>
> Series
> ======
>
> Patch 1 introduces the new vUART framework, that is the code originally
> posted here:
> https://lore.kernel.org/xen-devel/20250624035443.344099-16-dmukhin@ford.com/
> Required for emulator.
>
> Patch 2 adds missing NS16550 definitions, required for emulator.
>
> Patch 3 introduces the basic emulator skeleton - state machine
> initialization stubs, I/O port handler stub, logging, etc.
>
> Patches 4-11 incrementally populate the minimal NS16550 register emulation.
>
> Patch 12 hooks vUART state debugging (disabled by default).
>
> Pathes 13-15 introduce necessary changes to enable NS16550 on dom0 (and PVH).
>
> Link to CI: https://gitlab.com/xen-project/people/dmukhin/xen/-/pipelines/2024756493
> Link to branch: https://gitlab.com/xen-project/people/dmukhin/xen/-/tree/vuart-ns8250-v6?ref_type=heads
>
> Testing
> =======
>
> ```shell
> echo CONFIG_EXPERT=y >> .config
> echo CONFIG_VUART_NS16X50=y >> .config
> make olddefconfig
> ```
> COM2 (0x2f8) resources are used by default.
>
> To test w/ virtual COM2, the guest kernel parameters should contain
> something like the following:
> earlycon=uart,io,0x2f8,115200n8 console=uart,io,0x2f8,115200n8
>
> HVM
> ---
> Tested only boot of HVM linux guest with OVMF as the virtual firmware.
> SeaBIOS as a virtual firmware is not tested.
>
> PVH (dom0)
> ----------
> Xen is able to forward physical console input to the domain with virtual
> NS16550. To switch the console focus press Ctrl+aaa.
> Console switch is limited on x86 to dom0 and Xen (fixes pending).
>
> Changes since v5:
> - Split THR/RBR into two separate patches.
> - Addressed feedback from v5.
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-1-dmukhin@ford.com/
>
> Changes since v4:
> - Split the series to make it simpler to review.
> - Addressed feedback from v4.
> - Dropped xl changes, which I will submit separately.
> - Link to v4: https://lore.kernel.org/xen-devel/20250731192130.3948419-1-dmukhin@ford.com/
>
> Changes since v3:
> - Reduced the blast radius of the series, thanks to reviews, individual
> aspects (like console focus) touched in v3 moved to separate threads.
> - Kept the UART emulator framework since I need to redo some of emulator code
> and there's more-or-less agreement on it (where to place, naming, scope).
> - Applied the feedback from
> https://lore.kernel.org/xen-devel/20250624035443.344099-1-dmukhin@ford.com/
> - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-0-c5d36b31d66c@ford.com/
>
> Changes since v2:
> - renamed emulator s/NS8250/NS16550/g
> - reduced the patch series after addressing v2 feedback
> - introduced driver framework for UART emulators
> - unified guest OS printouts across all available UART emulators
> - Link to v2: https://lore.kernel.org/xen-devel/20241205-vuart-ns8250-v1-0-e9aa923127eb@ford.com/
>
> Changes since v1:
> - dropped kmalloc/kfree aliases
> - fixed ECLAIR jobs (thanks Andrew Cooper)
> - addressed console forwarding on arm32 and arm64 (thanks to Luca Fancellu)
> - moved NS8250 debugging stubs into its own patch
> - added fix for https://gitlab.com/xen-project/xen/-/issues/184
> - Link to v1: https://lore.kernel.org/r/20241126-vuart-ns8250-v1-v1-0-87b9a8375b7a@ford.com
>
> Denis Mukhin (15):
> emul/vuart: introduce framework for UART emulators
> xen/8250-uart: update definitions
> emul/ns16x50: implement emulator stub
> emul/ns16x50: implement DLL/DLM registers
> emul/ns16x50: implement SCR register
> emul/ns16x50: implement IER/IIR registers
> emul/ns16x50: implement LCR/LSR registers
> emul/ns16x50: implement MCR/MSR registers
> emul/ns16x50: implement RBR register
> emul/ns16x50: implement THR register
> emul/ns16x50: implement FCR register (write-only)
> emul/ns16550: implement dump_state() hook
> x86/domain: enable per-domain I/O port bitmaps
> xen/domain: allocate d->irq_caps before arch-specific initialization
> emul/ns16x50: implement IRQ emulation via vIOAPIC
>
> xen/arch/arm/xen.lds.S | 1 +
> xen/arch/ppc/xen.lds.S | 1 +
> xen/arch/riscv/xen.lds.S | 1 +
> xen/arch/x86/Makefile | 1 +
> xen/arch/x86/dom0_build.c | 112 +--
> xen/arch/x86/hvm/dom0_build.c | 7 +
> xen/arch/x86/hvm/hvm.c | 56 +-
> xen/arch/x86/hvm/nestedhvm.c | 8 +-
> xen/arch/x86/hvm/quirks.c | 3 -
> xen/arch/x86/hvm/svm/nestedsvm.c | 2 +-
> xen/arch/x86/hvm/vioapic.c | 10 +
> xen/arch/x86/hvm/vmx/vvmx.c | 4 +-
> xen/arch/x86/include/asm/hvm/nestedhvm.h | 3 +-
> xen/arch/x86/include/asm/hvm/support.h | 2 -
> xen/arch/x86/include/asm/iocap.h | 2 +
> xen/arch/x86/include/asm/irq.h | 8 +
> xen/arch/x86/ioport.c | 163 ++++
> xen/arch/x86/irq.c | 8 +
> xen/arch/x86/pv/dom0_build.c | 7 +
> xen/arch/x86/xen.lds.S | 1 +
> xen/common/Kconfig | 2 +
> xen/common/Makefile | 1 +
> xen/common/domain.c | 8 +-
> xen/common/emul/Kconfig | 6 +
> xen/common/emul/Makefile | 1 +
> xen/common/emul/vuart/Kconfig | 25 +
> xen/common/emul/vuart/Makefile | 2 +
> xen/common/emul/vuart/ns16x50.c | 984 +++++++++++++++++++++++
> xen/common/emul/vuart/vuart.c | 157 ++++
> xen/common/keyhandler.c | 3 +
> xen/drivers/char/console.c | 6 +-
> xen/drivers/char/ns16550.c | 16 +-
> xen/drivers/passthrough/x86/hvm.c | 11 +-
> xen/include/xen/8250-uart.h | 50 +-
> xen/include/xen/sched.h | 4 +
> xen/include/xen/serial.h | 3 +
> xen/include/xen/vuart.h | 116 +++
> xen/include/xen/xen.lds.h | 10 +
> 38 files changed, 1634 insertions(+), 171 deletions(-)
> create mode 100644 xen/arch/x86/ioport.c
> create mode 100644 xen/common/emul/Kconfig
> create mode 100644 xen/common/emul/Makefile
> create mode 100644 xen/common/emul/vuart/Kconfig
> create mode 100644 xen/common/emul/vuart/Makefile
> create mode 100644 xen/common/emul/vuart/ns16x50.c
> create mode 100644 xen/common/emul/vuart/vuart.c
> create mode 100644 xen/include/xen/vuart.h
>
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
@ 2025-09-06 2:02 ` Stefano Stabellini
2025-09-06 9:43 ` Mykola Kvach
2025-09-08 8:29 ` Mykola Kvach
2 siblings, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:02 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> 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_VUART_FRAMEWORK, which will be
> automatically enabled once the user enables any UART emulator.
>
> Current implementation supports maximum of one vUART of each kind per domain.
>
> Use new domain_has_vuart() in the console driver code to check whether to
> forward console input to the domain using vUART.
>
> Enable console forwarding over vUART for hardware domains with a vUART. That
> enables console forwarding to dom0 on x86, since console can be forwarded only
> to Xen, dom0 and pvshim on x86 as of now.
>
> Note: existing vUARTs are deliberately *not* hooked to the new framework to
> minimize the scope of the patch: vpl011 (i.e. SBSA) emulator and "vuart" (i.e.
> minimalistic MMIO-mapped dtuart for hwdoms on Arm) are kept unmodified.
>
> No functional changes for non-x86 architectures.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 02/15] xen/8250-uart: update definitions
2025-09-05 23:27 ` [PATCH v6 02/15] xen/8250-uart: update definitions dmukhin
@ 2025-09-06 2:02 ` Stefano Stabellini
2025-09-06 14:57 ` Mykola Kvach
1 sibling, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:02 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Added missing definitions needed for NS16550 UART emulator.
>
> Newly introduced MSR definitions re-used in the existing ns16550 driver.
>
> Also, corrected FCR DMA definition bit#3 (0x08) as per:
> https://www.ti.com/lit/ds/symlink/tl16c550c.pdf
> See "7.7.2 FIFO Control Register (FCR)".
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
2025-09-06 0:34 ` dmukhin
@ 2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 4:18 ` dmukhin
2025-09-06 20:37 ` Mykola Kvach
2025-09-08 8:37 ` Mykola Kvach
3 siblings, 1 reply; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:03 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> The change is the first on the way on introducing minimally functional
> NS16550-compatible UART emulator.
>
> Define UART state and a set of emulated registers.
>
> Implement alloc/free vUART hooks.
>
> Stub out I/O port handler.
>
> Add initialization of the NS16x50-compatible UART emulator state machine.
>
> Plumb debug logging.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - v5 feedback
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-4-dmukhin@ford.com/
> ---
> xen/arch/x86/hvm/hvm.c | 21 ++
> xen/common/emul/vuart/Kconfig | 19 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/ns16x50.c | 366 ++++++++++++++++++++++++++++++++
> 4 files changed, 407 insertions(+)
> create mode 100644 xen/common/emul/vuart/ns16x50.c
>
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index 23bd7f078a1d..91c971f11e14 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -28,6 +28,7 @@
> #include <xen/softirq.h>
> #include <xen/trace.h>
> #include <xen/vm_event.h>
> +#include <xen/vuart.h>
> #include <xen/vpci.h>
> #include <xen/wait.h>
> #include <xen/warning.h>
> @@ -689,6 +690,22 @@ int hvm_domain_initialise(struct domain *d,
> if ( rc != 0 )
> goto fail1;
>
> + /* Limit NS16550 emulator for dom0 only for now. */
> + if ( IS_ENABLED(CONFIG_VUART_NS16X50) && is_hardware_domain(d) )
> + {
> + struct vuart_info info = {
> + .name = "COM2",
> + .compatible = "ns16550",
> + .base_addr = 0x2f8,
> + .size = 8,
> + .irq = 3,
> + };
> +
> + rc = vuart_init(d, &info);
> + if ( rc )
> + goto out_vioapic_deinit;
> + }
> +
> stdvga_init(d);
>
> rtc_init(d);
> @@ -712,6 +729,8 @@ int hvm_domain_initialise(struct domain *d,
> return 0;
>
> fail2:
> + vuart_deinit(d);
> + out_vioapic_deinit:
> vioapic_deinit(d);
> fail1:
> if ( is_hardware_domain(d) )
> @@ -774,6 +793,8 @@ void hvm_domain_destroy(struct domain *d)
> if ( hvm_funcs.domain_destroy )
> alternative_vcall(hvm_funcs.domain_destroy, d);
>
> + vuart_deinit(d);
> +
> vioapic_deinit(d);
>
> XFREE(d->arch.hvm.pl_time);
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index ce1b976b7da7..a27d7ca135af 100644
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,23 @@ config VUART_FRAMEWORK
>
> menu "UART Emulation"
>
> +config VUART_NS16X50
> + bool "NS16550-compatible UART Emulator" if EXPERT
> + depends on X86 && HVM
> + select VUART_FRAMEWORK
> + default n
> + help
> + In-hypervisor NS16x50 UART emulation.
> +
> + Only legacy PC COM2 port is emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
> +config VUART_NS16X50_DEBUG
> + bool "NS16550-compatible UART Emulator Debugging"
> + depends on VUART_NS16X50 && DEBUG
> + help
> + Enable development debugging.
There is a question about adding the kconfig option early in the series.
I think it would be best to add it as last patch
> endmenu
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers
2025-09-05 23:27 ` [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers dmukhin
@ 2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 21:12 ` Mykola Kvach
1 sibling, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:03 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add DLL/DLM registers emulation.
>
> DLL/DLM registers report hardcoded 115200 baud rate to the guest OS.
>
> Add stub for ns16x50_dlab_get() helper.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 05/15] emul/ns16x50: implement SCR register
2025-09-05 23:27 ` [PATCH v6 05/15] emul/ns16x50: implement SCR register dmukhin
@ 2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 21:24 ` Mykola Kvach
1 sibling, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:03 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add SCR register emulation to the I/O port handler.
> Firmware (e.g. OVMF) may use SCR during the guest OS boot.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 07/15] emul/ns16x50: implement LCR/LSR registers
2025-09-05 23:27 ` [PATCH v6 07/15] emul/ns16x50: implement LCR/LSR registers dmukhin
@ 2025-09-06 2:04 ` Stefano Stabellini
0 siblings, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:04 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add LCR/LSR registers implementation to the I/O port handler.
>
> Add implementation of ns16x50_dlab_get() and ns16x50_iir_check_lsi().
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - Moved earlier in the series so follow on patches (THR/RBR) could make use of
> LSR bits more naturally
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-9-dmukhin@ford.com/
> ---
> xen/common/emul/vuart/ns16x50.c | 18 ++++++++++++++++--
> 1 file changed, 16 insertions(+), 2 deletions(-)
>
> diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> index a7429c84c36e..9d1fe0284362 100644
> --- a/xen/common/emul/vuart/ns16x50.c
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -88,12 +88,12 @@ struct vuart_ns16x50 {
>
> static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
> {
> - return 0;
> + return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
> }
>
> static bool cf_check ns16x50_iir_check_lsi(const struct vuart_ns16x50 *vdev)
> {
> - return false;
> + return vdev->regs[UART_LSR] & UART_LSR_MASK;
> }
>
> static bool cf_check ns16x50_iir_check_rda(const struct vuart_ns16x50 *vdev)
> @@ -226,11 +226,16 @@ static int ns16x50_io_write8(
> regs[UART_IER] = val & UART_IER_MASK;
> break;
>
> + case UART_LCR:
> + regs[UART_LCR] = val;
> + break;
> +
> /* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
> case UART_SCR:
> regs[UART_SCR] = val;
> break;
>
> + case UART_LSR: /* RO */
> default:
> rc = -EINVAL;
> break;
> @@ -312,6 +317,15 @@ static int ns16x50_io_read8(
> val = ns16x50_iir_get(vdev);
> break;
>
> + case UART_LCR:
> + val = regs[UART_LCR];
> + break;
> +
> + case UART_LSR:
> + val = regs[UART_LSR] | UART_LSR_THRE | UART_LSR_TEMT;
> + regs[UART_LSR] = val & ~UART_LSR_MASK;
> + break;
> +
> case UART_SCR:
> val = regs[UART_SCR];
> break;
> --
> 2.51.0
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 09/15] emul/ns16x50: implement RBR register
2025-09-05 23:27 ` [PATCH v6 09/15] emul/ns16x50: implement RBR register dmukhin
@ 2025-09-06 2:04 ` Stefano Stabellini
0 siblings, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:04 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add RBR register emulation to the I/O port handlder.
>
> Add RX FIFO management code since RBR depends on RX FIFO.
>
> RX FIFO is not emulated as per UART specs for simplicity (not need to emulate
> baud rate). Emulator does not emulate NS8250 (no FIFO), NS16550a (16 bytes) or
> NS16750 (64 bytes).
>
> RX FIFO is emulated by means of using xencons_interface which conveniently
> provides primitives for buffer management and later can be used for
> inter-domain communication similarly to vpl011.
>
> Add UART_LSR_DR handling since it depends on RBR register access.
>
> Finally, implement put_rx() vUART hook for placing a character into the
> emulated RX FIFO from console driver. That implements physical console
> forwarding to the guest OS over emulated NS16550.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 11/15] emul/ns16x50: implement FCR register (write-only)
2025-09-05 23:27 ` [PATCH v6 11/15] emul/ns16x50: implement FCR register (write-only) dmukhin
@ 2025-09-06 2:04 ` Stefano Stabellini
0 siblings, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-06 2:04 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add emulation logic for FCR register.
>
> Note, that does not hook FIFO interrupt moderation to the FIFO management
> code for simplicity.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-06 2:03 ` Stefano Stabellini
@ 2025-09-06 4:18 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-06 4:18 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Fri, Sep 05, 2025 at 07:03:19PM -0700, Stefano Stabellini wrote:
> On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
[..]
> > --- a/xen/common/emul/vuart/Kconfig
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -3,4 +3,23 @@ config VUART_FRAMEWORK
> >
> > menu "UART Emulation"
> >
> > +config VUART_NS16X50
> > + bool "NS16550-compatible UART Emulator" if EXPERT
> > + depends on X86 && HVM
> > + select VUART_FRAMEWORK
> > + default n
> > + help
> > + In-hypervisor NS16x50 UART emulation.
> > +
> > + Only legacy PC COM2 port is emulated.
> > +
> > + This is strictly for testing purposes (such as early HVM guest console),
> > + and not appropriate for use in production.
> > +
> > +config VUART_NS16X50_DEBUG
> > + bool "NS16550-compatible UART Emulator Debugging"
> > + depends on VUART_NS16X50 && DEBUG
> > + help
> > + Enable development debugging.
>
> There is a question about adding the kconfig option early in the series.
> I think it would be best to add it as last patch
Will do.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
2025-09-06 2:02 ` Stefano Stabellini
@ 2025-09-06 9:43 ` Mykola Kvach
2025-09-08 7:16 ` dmukhin
2025-09-08 8:29 ` Mykola Kvach
2 siblings, 1 reply; 54+ messages in thread
From: Mykola Kvach @ 2025-09-06 9:43 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
Hi Denis,
On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> 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_VUART_FRAMEWORK, which will be
> automatically enabled once the user enables any UART emulator.
>
> Current implementation supports maximum of one vUART of each kind per domain.
>
> Use new domain_has_vuart() in the console driver code to check whether to
> forward console input to the domain using vUART.
>
> Enable console forwarding over vUART for hardware domains with a vUART. That
> enables console forwarding to dom0 on x86, since console can be forwarded only
> to Xen, dom0 and pvshim on x86 as of now.
>
> Note: existing vUARTs are deliberately *not* hooked to the new framework to
> minimize the scope of the patch: vpl011 (i.e. SBSA) emulator and "vuart" (i.e.
> minimalistic MMIO-mapped dtuart for hwdoms on Arm) are kept unmodified.
>
> No functional changes for non-x86 architectures.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - addressed v5 feedback
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-2-dmukhin@ford.com/
> ---
> xen/arch/arm/xen.lds.S | 1 +
> xen/arch/ppc/xen.lds.S | 1 +
> xen/arch/riscv/xen.lds.S | 1 +
> xen/arch/x86/xen.lds.S | 1 +
> xen/common/Kconfig | 2 +
> xen/common/Makefile | 1 +
> xen/common/emul/Kconfig | 6 ++
> xen/common/emul/Makefile | 1 +
> xen/common/emul/vuart/Kconfig | 6 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/vuart.c | 157 +++++++++++++++++++++++++++++++++
> xen/common/keyhandler.c | 3 +
> xen/drivers/char/console.c | 6 +-
> xen/include/xen/sched.h | 4 +
> xen/include/xen/serial.h | 3 +
> xen/include/xen/vuart.h | 116 ++++++++++++++++++++++++
> xen/include/xen/xen.lds.h | 10 +++
> 17 files changed, 319 insertions(+), 1 deletion(-)
> create mode 100644 xen/common/emul/Kconfig
> create mode 100644 xen/common/emul/Makefile
> create mode 100644 xen/common/emul/vuart/Kconfig
> create mode 100644 xen/common/emul/vuart/Makefile
> create mode 100644 xen/common/emul/vuart/vuart.c
> create mode 100644 xen/include/xen/vuart.h
>
> diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
> index db17ff1efa98..cd05b18770f4 100644
> --- a/xen/arch/arm/xen.lds.S
> +++ b/xen/arch/arm/xen.lds.S
> @@ -58,6 +58,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S
> index 1de0b77fc6b9..f9d4e5b0dcd8 100644
> --- a/xen/arch/ppc/xen.lds.S
> +++ b/xen/arch/ppc/xen.lds.S
> @@ -52,6 +52,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S
> index edcadff90bfe..59dcaa5fef9a 100644
> --- a/xen/arch/riscv/xen.lds.S
> +++ b/xen/arch/riscv/xen.lds.S
> @@ -47,6 +47,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
> index 966e514f2034..d877b93a6964 100644
> --- a/xen/arch/x86/xen.lds.S
> +++ b/xen/arch/x86/xen.lds.S
> @@ -132,6 +132,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/common/Kconfig b/xen/common/Kconfig
> index 76f9ce705f7a..78a32b69e2b2 100644
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -676,4 +676,6 @@ config PM_STATS
> Enable collection of performance management statistics to aid in
> analyzing and tuning power/performance characteristics of the system
>
> +source "common/emul/Kconfig"
> +
> endmenu
> diff --git a/xen/common/Makefile b/xen/common/Makefile
> index 0c7d0f5d46e1..8c8462565050 100644
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> obj-$(CONFIG_IOREQ_SERVER) += dm.o
> obj-y += domain.o
> obj-y += domid.o
> +obj-y += emul/
> obj-y += event_2l.o
> obj-y += event_channel.o
> obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o
> diff --git a/xen/common/emul/Kconfig b/xen/common/emul/Kconfig
> new file mode 100644
> index 000000000000..7c6764d1756b
> --- /dev/null
> +++ b/xen/common/emul/Kconfig
> @@ -0,0 +1,6 @@
> +menu "Domain Emulation Features"
> + visible if EXPERT
> +
> +source "common/emul/vuart/Kconfig"
> +
> +endmenu
> diff --git a/xen/common/emul/Makefile b/xen/common/emul/Makefile
> new file mode 100644
> index 000000000000..ae0b575c3901
> --- /dev/null
> +++ b/xen/common/emul/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_VUART_FRAMEWORK) += vuart/
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> new file mode 100644
> index 000000000000..ce1b976b7da7
> --- /dev/null
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -0,0 +1,6 @@
> +config VUART_FRAMEWORK
> + bool
> +
> +menu "UART Emulation"
> +
> +endmenu
> diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
> new file mode 100644
> index 000000000000..97f792dc6641
> --- /dev/null
> +++ b/xen/common/emul/vuart/Makefile
> @@ -0,0 +1 @@
> +obj-y += vuart.o
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> new file mode 100644
> index 000000000000..3dfcba217248
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -0,0 +1,157 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#include <xen/err.h>
> +#include <xen/sched.h>
> +#include <xen/vuart.h>
> +#include <xen/xvmalloc.h>
> +
> +#define for_each_emulator(e) \
> + for ( e = vuart_array_start; e < vuart_array_end; e++ )
> +
> +extern const struct vuart_emulator vuart_array_start[];
> +extern const struct vuart_emulator vuart_array_end[];
> +
> +static const struct vuart_emulator *
> +vuart_match_by_compatible(const struct domain *d, const char *compat)
> +{
> + const struct vuart_emulator *emulator;
> +
> + for_each_emulator(emulator)
> + if ( emulator->compatible &&
> + !strncmp(compat, emulator->compatible,
> + strlen(emulator->compatible)) )
> + return emulator;
> +
> + return NULL;
> +}
> +
> +const static struct vuart *
> +vuart_find_by_console_permission(const struct domain *d)
Other functions that search for a console (e.g., by compatible string or IO
range) take an argument to specify what to search by. Here,
vuart_find_by_console_permission takes no argument and just checks a single
flag. It might be clearer to either add a flags argument to make it general,
or rename the function to reflect that it checks only this one permission flag.
> +{
> + const struct vuart *vuart = d->console.vuart;
> +
> + if ( !vuart || !vuart->emulator || !vuart->emulator->put_rx ||
Looking at vuart_init, vuart->emulator is always set when vuart is valid.
So the vuart->emulator check seems redundant.
If it is truly needed, the same check should also appear in
vuart_dump_state and vuart_deinit. Otherwise, for consistency we
could safely assume vuart->emulator is non-NULL after vuart_init.
> + !(vuart->flags & VUART_CONSOLE_INPUT))
> + return NULL;
> +
> + return vuart;
> +}
> +
> +struct vuart *vuart_find_by_io_range(struct domain *d, unsigned long addr,
> + unsigned long size)
> +{
> + struct vuart *vuart = d->console.vuart;
> +
> + if ( !vuart || !vuart->info )
Is it possible to have a valid vuart pointer without a valid info pointer?
> + return NULL;
> +
> + if ( addr >= vuart->info->base_addr &&
> + addr + size - 1 <= vuart->info->base_addr + vuart->info->size - 1 )
> + return vuart;
> +
> + return NULL;
> +}
> +
> +int vuart_init(struct domain *d, struct vuart_info *info)
> +{
> + const struct vuart_emulator *emulator;
> + struct vuart *vuart;
> + int rc;
> +
> + if ( d->console.vuart )
> + return -EBUSY;
> +
> + emulator = vuart_match_by_compatible(d, info->compatible);
> + if ( !emulator )
> + return -ENODEV;
> +
> + vuart = xzalloc(typeof(*vuart));
> + if ( !vuart )
> + return -ENOMEM;
> +
> + vuart->info = xvzalloc(typeof(*info));
> + if ( !vuart->info )
> + {
> + rc = -ENOMEM;
> + goto err_out;
> + }
> + memcpy(vuart->info, info, sizeof(*info));
> +
> + vuart->vdev = emulator->alloc(d, vuart->info);
> + if ( IS_ERR(vuart->vdev) )
> + {
> + rc = PTR_ERR(vuart->vdev);
> + goto err_out;
> + }
> +
> + vuart->emulator = emulator;
> + vuart->owner = d;
> + vuart->flags |= VUART_CONSOLE_INPUT;
> +
> + d->console.input_allowed = true;
> + d->console.vuart = vuart;
> +
> + return 0;
> +
> + err_out:
> + if ( vuart )
As far as I can see, it isn’t possible to reach this point when vuart
is NULL. The err_out label is only jumped to after vuart has been
successfully allocated, so the check if (vuart) is redundant.
> + xvfree(vuart->info);
> + xvfree(vuart);
> +
> + return rc;
> +}
> +
> +/*
> + * Release any resources taken by UART emulators.
> + *
> + * NB: no flags are cleared, since currently exit() is called only during
> + * domain destroy.
> + */
> +void vuart_deinit(struct domain *d)
> +{
> + struct vuart *vuart = d->console.vuart;
> +
> + if ( vuart )
> + {
> + vuart->emulator->free(vuart->vdev);
> + xvfree(vuart->info);
> + }
> + XVFREE(d->console.vuart);
> +}
> +
> +void vuart_dump_state(const struct domain *d)
> +{
> + struct vuart *vuart = d->console.vuart;
> +
> + if ( vuart )
> + vuart->emulator->dump_state(vuart->vdev);
> +}
> +
> +/*
> + * Put character to the *first* suitable emulated UART's FIFO.
> + */
This comment could be a single line since it doesn’t exceed 80 characters.
> +int vuart_put_rx(struct domain *d, char c)
> +{
> + const struct vuart *vuart = vuart_find_by_console_permission(d);
If vuart_deinit has already been called, is it possible that vuart
points to freed memory here or in other places?
Should we add reference counting or locks to protect against such
use-after-free, or are we relying on higher-level mechanisms to
guarantee that these structs aren’t freed while vuart is accessed?
Should we also check whether the domain is currently being
destroyed and avoid putting new characters into the emulated UART
in that case?
If we are relying on some upper-level mechanism, I think it deserves a
comment somewhere to make that guarantee explicit.
> +
> + return vuart ? vuart->emulator->put_rx(vuart->vdev, c) : -ENODEV;
> +}
> +
> +bool domain_has_vuart(const struct domain *d)
> +{
> + return vuart_find_by_console_permission(d);
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
> index cb6df2823b00..156e64d9eb58 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;
> @@ -352,6 +353,8 @@ static void cf_check dump_domains(unsigned char key)
> v->periodic_period / 1000000);
> }
> }
> +
> + vuart_dump_state(d);
> }
>
> for_each_domain ( d )
> diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> index 9bd5b4825da6..d5164897a776 100644
> --- a/xen/drivers/char/console.c
> +++ b/xen/drivers/char/console.c
> @@ -33,6 +33,7 @@
> #include <asm/setup.h>
> #include <xen/sections.h>
> #include <xen/consoled.h>
> +#include <xen/vuart.h>
>
> #ifdef CONFIG_X86
> #include <asm/guest.h>
> @@ -596,11 +597,12 @@ static void __serial_rx(char c)
> if ( !d )
> return;
>
> - if ( is_hardware_domain(d) )
> + if ( is_hardware_domain(d) && !domain_has_vuart(d) )
> {
> /*
> * 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;
> @@ -611,6 +613,8 @@ static void __serial_rx(char c)
> */
> send_global_virq(VIRQ_CONSOLE);
> }
> + else if ( domain_has_vuart(d) )
> + rc = vuart_put_rx(d, c);
> #ifdef CONFIG_SBSA_VUART_CONSOLE
> else
> /* Deliver input to the emulated UART. */
> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
> index 02bdc256ce37..613f4596e33d 100644
> --- a/xen/include/xen/sched.h
> +++ b/xen/include/xen/sched.h
> @@ -23,6 +23,7 @@
> #include <asm/atomic.h>
> #include <asm/current.h>
> #include <xen/vpci.h>
> +#include <xen/vuart.h>
> #include <xen/wait.h>
> #include <public/xen.h>
> #include <public/domctl.h>
> @@ -660,6 +661,9 @@ struct domain
> struct {
> /* Permission to take ownership of the physical console input. */
> bool input_allowed;
> +#ifdef CONFIG_VUART_FRAMEWORK
> + struct vuart *vuart;
> +#endif
> } console;
> } __aligned(PAGE_SIZE);
>
> diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
> index 8e1844555208..123eee67df35 100644
> --- a/xen/include/xen/serial.h
> +++ b/xen/include/xen/serial.h
> @@ -36,6 +36,9 @@ struct vuart_info {
> unsigned long data_off; /* Data register offset */
> unsigned long status_off; /* Status register offset */
> unsigned long status; /* Ready status value */
> + unsigned int irq; /* Interrupt */
> + char compatible[16]; /* Compatible string */
> + char name[16]; /* User-friendly name */
> };
>
> struct serial_port {
> diff --git a/xen/include/xen/vuart.h b/xen/include/xen/vuart.h
> new file mode 100644
> index 000000000000..54f2f29f3f4a
> --- /dev/null
> +++ b/xen/include/xen/vuart.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#ifndef XEN_VUART_H
> +#define XEN_VUART_H
> +
> +#include <xen/serial.h>
> +#include <public/xen.h>
> +
> +struct vuart_emulator;
> +
> +enum {
> + VUART_CONSOLE_INPUT = BIT(0, U), /* Physical console input forwarding. */
> +};
> +
> +
> +/*
> + * FIXME: #ifdef is temporary to avoid clash with
> + * arch/arm/include/asm/domain.h
> + */
> +#ifdef CONFIG_VUART_FRAMEWORK
> +struct vuart {
> + const struct vuart_emulator *emulator;
> + struct vuart_info *info;
> + struct domain *owner;
> + uint32_t flags;
> + void *vdev;
> +};
> +#endif
> +
> +struct vuart_emulator {
> + /* UART compatible string. Cannot be NULL or empty. */
> + const char *compatible;
> +
> + /*
> + * Allocate emulated UART state (RX/TX FIFOs, locks, initialize registers,
> + * hook I/O handlers, etc.)
> + * Cannot be NULL.
> + */
> + void *(*alloc)(struct domain *d, const struct vuart_info *info);
> +
> + /*
> + * Release resources used to emulate UART state (flush RX/TX FIFOs, unhook
> + * I/O handlers, etc.).
> + * Cannot be NULL.
> + */
> + void (*free)(void *arg);
> +
> + /*
> + * Print emulated UART state, including registers, on the console.
> + * Can be NULL.
> + */
> + void (*dump_state)(void *arg);
> +
> + /*
> + * Place character to the emulated RX FIFO.
> + * Used to forward physical console input to the guest OS.
> + * Can be NULL.
> + */
> + int (*put_rx)(void *arg, char c);
> +};
> +
> +#define VUART_REGISTER(name, x) \
> + static const struct vuart_emulator name##_entry \
> + __used_section(".data.rel.ro.vuart") = x
> +
> +struct vuart *vuart_find_by_io_range(struct domain *d,
> + unsigned long base_addr,
> + unsigned long size);
> +
> +int vuart_put_rx(struct domain *d, char c);
> +
> +#ifdef CONFIG_VUART_FRAMEWORK
> +
> +int vuart_init(struct domain *d, struct vuart_info *info);
> +void vuart_deinit(struct domain *d);
> +void vuart_dump_state(const struct domain *d);
> +bool domain_has_vuart(const struct domain *d);
> +
> +#else
> +
> +static inline int vuart_init(struct domain *d, struct vuart_info *info)
> +{
> + return 0;
> +}
> +
> +static inline void vuart_deinit(struct domain *d)
> +{
> +}
> +
> +static inline void vuart_dump_state(const struct domain *d)
> +{
> +}
> +
> +static inline bool domain_has_vuart(const struct domain *d)
> +{
> + return false;
> +}
> +
> +#endif /* CONFIG_VUART_FRAMEWORK */
> +
> +#endif /* XEN_VUART_H */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> +
> diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
> index b126dfe88792..2d65f32ddad3 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_VUART_FRAMEWORK
> +#define VUART_ARRAY \
> + . = ALIGN(POINTER_ALIGN); \
> + vuart_array_start = .; \
> + *(.data.rel.ro.vuart) \
> + vuart_array_end = .;
> +#else
> +#define VUART_ARRAY
> +#endif
> +
> #endif /* __XEN_LDS_H__ */
> --
> 2.51.0
>
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 02/15] xen/8250-uart: update definitions
2025-09-05 23:27 ` [PATCH v6 02/15] xen/8250-uart: update definitions dmukhin
2025-09-06 2:02 ` Stefano Stabellini
@ 2025-09-06 14:57 ` Mykola Kvach
2025-09-08 8:36 ` dmukhin
1 sibling, 1 reply; 54+ messages in thread
From: Mykola Kvach @ 2025-09-06 14:57 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
Hi Denis,
On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> Added missing definitions needed for NS16550 UART emulator.
>
> Newly introduced MSR definitions re-used in the existing ns16550 driver.
>
> Also, corrected FCR DMA definition bit#3 (0x08) as per:
> https://www.ti.com/lit/ds/symlink/tl16c550c.pdf
> See "7.7.2 FIFO Control Register (FCR)".
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - fixed commentaries
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-3-dmukhin@ford.com/
> ---
> xen/drivers/char/ns16550.c | 16 ++++++------
> xen/include/xen/8250-uart.h | 50 ++++++++++++++++++++++++++++++-------
> 2 files changed, 49 insertions(+), 17 deletions(-)
>
> diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
> index df7fff7f81df..0e80fadbb894 100644
> --- a/xen/drivers/char/ns16550.c
> +++ b/xen/drivers/char/ns16550.c
> @@ -388,7 +388,7 @@ static void __init cf_check ns16550_init_preirq(struct serial_port *port)
>
> /* Check this really is a 16550+. Otherwise we have no FIFOs. */
> if ( uart->fifo_size <= 1 &&
> - ((ns_read_reg(uart, UART_IIR) & 0xc0) == 0xc0) &&
> + ((ns_read_reg(uart, UART_IIR) & UART_IIR_FE) == UART_IIR_FE) &&
> ((ns_read_reg(uart, UART_FCR) & UART_FCR_TRG14) == UART_FCR_TRG14) )
> uart->fifo_size = 16;
> }
> @@ -728,20 +728,20 @@ static int __init check_existence(struct ns16550 *uart)
> * Mask out IER[7:4] bits for test as some UARTs (e.g. TL
> * 16C754B) allow only to modify them if an EFR bit is set.
> */
> - scratch2 = ns_read_reg(uart, UART_IER) & 0x0f;
> - ns_write_reg(uart,UART_IER, 0x0F);
> - scratch3 = ns_read_reg(uart, UART_IER) & 0x0f;
> + scratch2 = ns_read_reg(uart, UART_IER) & UART_IER_MASK;
> + ns_write_reg(uart, UART_IER, UART_IER_MASK);
> + scratch3 = ns_read_reg(uart, UART_IER) & UART_IER_MASK;
> ns_write_reg(uart, UART_IER, scratch);
> - if ( (scratch2 != 0) || (scratch3 != 0x0F) )
> + if ( (scratch2 != 0) || (scratch3 != UART_IER_MASK) )
> return 0;
>
> /*
> * Check to see if a UART is really there.
> * Use loopback test mode.
> */
> - ns_write_reg(uart, UART_MCR, UART_MCR_LOOP | 0x0A);
> - status = ns_read_reg(uart, UART_MSR) & 0xF0;
> - return (status == 0x90);
> + ns_write_reg(uart, UART_MCR, UART_MCR_LOOP | UART_MCR_RTS | UART_MCR_OUT2);
> + status = ns_read_reg(uart, UART_MSR) & UART_MSR_STATUS;
> + return (status == (UART_MSR_CTS | UART_MSR_DCD));
> }
>
> #ifdef CONFIG_HAS_PCI
> diff --git a/xen/include/xen/8250-uart.h b/xen/include/xen/8250-uart.h
> index d13352940c13..bbbffb14d320 100644
> --- a/xen/include/xen/8250-uart.h
> +++ b/xen/include/xen/8250-uart.h
> @@ -32,6 +32,7 @@
> #define UART_MCR 0x04 /* Modem control */
> #define UART_LSR 0x05 /* line status */
> #define UART_MSR 0x06 /* Modem status */
> +#define UART_SCR 0x07 /* Scratch pad */
> #define UART_USR 0x1f /* Status register (DW) */
> #define UART_DLL 0x00 /* divisor latch (ls) (DLAB=1) */
> #define UART_DLM 0x01 /* divisor latch (ms) (DLAB=1) */
> @@ -42,6 +43,8 @@
> #define UART_IER_ETHREI 0x02 /* tx reg. empty */
> #define UART_IER_ELSI 0x04 /* rx line status */
> #define UART_IER_EMSI 0x08 /* MODEM status */
> +#define UART_IER_MASK \
> + (UART_IER_ERDAI | UART_IER_ETHREI | UART_IER_ELSI | UART_IER_EMSI)
>
> /* Interrupt Identification Register */
> #define UART_IIR_NOINT 0x01 /* no interrupt pending */
> @@ -51,12 +54,19 @@
> #define UART_IIR_THR 0x02 /* - tx reg. empty */
> #define UART_IIR_MSI 0x00 /* - MODEM status */
> #define UART_IIR_BSY 0x07 /* - busy detect (DW) */
> +#define UART_IIR_FE 0xc0 /* FIFO enabled (2 bits) */
>
> /* FIFO Control Register */
> -#define UART_FCR_ENABLE 0x01 /* enable FIFO */
> -#define UART_FCR_CLRX 0x02 /* clear Rx FIFO */
> -#define UART_FCR_CLTX 0x04 /* clear Tx FIFO */
> -#define UART_FCR_DMA 0x10 /* enter DMA mode */
> +#define UART_FCR_ENABLE BIT(0, U) /* enable FIFO */
> +#define UART_FCR_CLRX BIT(1, U) /* clear Rx FIFO */
> +#define UART_FCR_CLTX BIT(2, U) /* clear Tx FIFO */
> +#define UART_FCR_DMA BIT(3, U) /* enter DMA mode */
> +#define UART_FCR_RESERVED0 BIT(4, U) /* reserved; always 0 */
> +#define UART_FCR_RESERVED1 BIT(5, U) /* reserved; always 0 */
> +#define UART_FCR_RTB0 BIT(6, U) /* receiver trigger bit #0 */
> +#define UART_FCR_RTB1 BIT(7, U) /* receiver trigger bit #1 */
> +#define UART_FCR_TRG_MASK (UART_FCR_RTB0 | UART_FCR_RTB1)
Thanks for the patch. I noticed that in this changeset some bit
definitions (e.g. UART_FCR_*) were rewritten using the BIT(n, U)
macro, while others (e.g. UART_IER_* and rest of UART_FCR_*) are
still left as plain hex values (0x01, 0x02, etc.), even though they
are also powers of two.
Could you clarify the reasoning behind this choice? From a reader’s
perspective it looks inconsistent. Would it make sense to either:
- update all of them to use BIT() for consistency, or
- keep the existing style unchanged in this patch and move a full
conversion to BIT() into a separate cleanup patch?
This would make the codebase easier to follow.
> +
> #define UART_FCR_TRG1 0x00 /* Rx FIFO trig lev 1 */
> #define UART_FCR_TRG4 0x40 /* Rx FIFO trig lev 4 */
> #define UART_FCR_TRG8 0x80 /* Rx FIFO trig lev 8 */
> @@ -96,11 +106,32 @@
> #define UART_LCR_CONF_MODE_B 0xBF /* Configuration mode B */
>
> /* Modem Control Register */
> -#define UART_MCR_DTR 0x01 /* Data Terminal Ready */
> -#define UART_MCR_RTS 0x02 /* Request to Send */
> -#define UART_MCR_OUT2 0x08 /* OUT2: interrupt mask */
> -#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
> -#define UART_MCR_TCRTLR 0x40 /* Access TCR/TLR (TI16C752, EFR[4]=1) */
> +#define UART_MCR_DTR BIT(0, U) /* Data Terminal Ready */
> +#define UART_MCR_RTS BIT(1, U) /* Request to Send */
> +#define UART_MCR_OUT1 BIT(2, U) /* Output #1 */
> +#define UART_MCR_OUT2 BIT(3, U) /* Output #2 */
> +#define UART_MCR_LOOP BIT(4, U) /* Enable loopback test mode */
> +#define UART_MCR_RESERVED0 BIT(5, U) /* Reserved #0 */
> +#define UART_MCR_TCRTLR BIT(6, U) /* Access TCR/TLR (TI16C752, EFR[4]=1) */
> +#define UART_MCR_RESERVED1 BIT(7, U) /* Reserved #1 */
> +#define UART_MCR_MASK \
> + (UART_MCR_DTR | UART_MCR_RTS | \
> + UART_MCR_OUT1 | UART_MCR_OUT2 | \
> + UART_MCR_LOOP | UART_MCR_TCRTLR)
> +
> +/* Modem Status Register */
> +#define UART_MSR_DCTS BIT(0, U) /* Change in CTS */
> +#define UART_MSR_DDSR BIT(1, U) /* Change in DSR */
> +#define UART_MSR_TERI BIT(2, U) /* Change in RI */
> +#define UART_MSR_DDCD BIT(3, U) /* Change in DCD */
> +#define UART_MSR_CTS BIT(4, U)
> +#define UART_MSR_DSR BIT(5, U)
> +#define UART_MSR_RI BIT(6, U)
> +#define UART_MSR_DCD BIT(7, U)
> +#define UART_MSR_CHANGE \
> + (UART_MSR_DCTS | UART_MSR_DDSR | UART_MSR_TERI | UART_MSR_DDCD)
> +#define UART_MSR_STATUS \
> + (UART_MSR_CTS | UART_MSR_DSR | UART_MSR_RI | UART_MSR_DCD)
>
> /* Line Status Register */
> #define UART_LSR_DR 0x01 /* Data ready */
> @@ -111,6 +142,7 @@
> #define UART_LSR_THRE 0x20 /* Xmit hold reg empty */
> #define UART_LSR_TEMT 0x40 /* Xmitter empty */
> #define UART_LSR_ERR 0x80 /* Error */
> +#define UART_LSR_MASK (UART_LSR_OE | UART_LSR_BI)
>
> /* These parity settings can be ORed directly into the LCR. */
> #define UART_PARITY_NONE (0<<3)
> --
> 2.51.0
>
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
2025-09-06 0:34 ` dmukhin
2025-09-06 2:03 ` Stefano Stabellini
@ 2025-09-06 20:37 ` Mykola Kvach
2025-09-08 8:32 ` dmukhin
2025-09-08 8:37 ` Mykola Kvach
3 siblings, 1 reply; 54+ messages in thread
From: Mykola Kvach @ 2025-09-06 20:37 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
Hi Denis,
On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> The change is the first on the way on introducing minimally functional
> NS16550-compatible UART emulator.
>
> Define UART state and a set of emulated registers.
>
> Implement alloc/free vUART hooks.
>
> Stub out I/O port handler.
>
> Add initialization of the NS16x50-compatible UART emulator state machine.
>
> Plumb debug logging.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - v5 feedback
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-4-dmukhin@ford.com/
> ---
> xen/arch/x86/hvm/hvm.c | 21 ++
> xen/common/emul/vuart/Kconfig | 19 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/ns16x50.c | 366 ++++++++++++++++++++++++++++++++
> 4 files changed, 407 insertions(+)
> create mode 100644 xen/common/emul/vuart/ns16x50.c
>
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index 23bd7f078a1d..91c971f11e14 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -28,6 +28,7 @@
> #include <xen/softirq.h>
> #include <xen/trace.h>
> #include <xen/vm_event.h>
I noticed that this include ...
> +#include <xen/vuart.h>
... is out of alphabetical order. All other includes in this block
are correctly sorted.
> #include <xen/vpci.h>
> #include <xen/wait.h>
> #include <xen/warning.h>
> @@ -689,6 +690,22 @@ int hvm_domain_initialise(struct domain *d,
> if ( rc != 0 )
> goto fail1;
>
> + /* Limit NS16550 emulator for dom0 only for now. */
> + if ( IS_ENABLED(CONFIG_VUART_NS16X50) && is_hardware_domain(d) )
Currently, the Xen glossary defines a "hardware domain" as:
A domain, commonly dom0, which shares responsibility with Xen
about the system as a whole.
It has been historically correct to treat is_hardware_domain(d) as
equivalent to dom0. However, according to patch series [1], this is
no longer guaranteed:
"Setting hardware domain as domid 0 is removed."
After these changes, the assumption that hardware domain always equals
dom0 may not hold. As a result, the above comment in the code could
become misleading. Consider updating it to something like:
/* Limit NS16550 emulator to the hardware domain only for now */
to reflect the new semantics.
> + {
> + struct vuart_info info = {
> + .name = "COM2",
> + .compatible = "ns16550",
> + .base_addr = 0x2f8,
> + .size = 8,
> + .irq = 3,
> + };
Consider defining COM2 base address and IRQ in a shared header
(e.g., VUART_COM2_BASE and VUART_COM2_IRQ) rather than using
the magic numbers 0x2f8 and 3 here. This would allow reuse in
`__start_xen` and other places, and makes the code clearer and
easier to maintain.
> +
> + rc = vuart_init(d, &info);
> + if ( rc )
> + goto out_vioapic_deinit;
> + }
> +
> stdvga_init(d);
>
> rtc_init(d);
> @@ -712,6 +729,8 @@ int hvm_domain_initialise(struct domain *d,
> return 0;
>
> fail2:
> + vuart_deinit(d);
> + out_vioapic_deinit:
> vioapic_deinit(d);
> fail1:
> if ( is_hardware_domain(d) )
> @@ -774,6 +793,8 @@ void hvm_domain_destroy(struct domain *d)
> if ( hvm_funcs.domain_destroy )
> alternative_vcall(hvm_funcs.domain_destroy, d);
>
> + vuart_deinit(d);
> +
> vioapic_deinit(d);
>
> XFREE(d->arch.hvm.pl_time);
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index ce1b976b7da7..a27d7ca135af 100644
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,23 @@ config VUART_FRAMEWORK
>
> menu "UART Emulation"
>
> +config VUART_NS16X50
> + bool "NS16550-compatible UART Emulator" if EXPERT
> + depends on X86 && HVM
> + select VUART_FRAMEWORK
> + default n
> + help
> + In-hypervisor NS16x50 UART emulation.
> +
> + Only legacy PC COM2 port is emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
> +config VUART_NS16X50_DEBUG
> + bool "NS16550-compatible UART Emulator Debugging"
> + depends on VUART_NS16X50 && DEBUG
> + help
> + Enable development debugging.
> +
> endmenu
> 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..0462a961e785
> --- /dev/null
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -0,0 +1,366 @@
> +/* 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
> + *
> + * 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 <xen/8250-uart.h>
> +#include <xen/console.h>
> +#include <xen/err.h>
> +#include <xen/iocap.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
> + * - R/O, IIR (IIR_THR) = NS16X50_REGS_NUM + 2
> + */
> +#define NS16X50_EMU_REGS_NUM (NS16X50_REGS_NUM + 3)
> +
> +/*
> + * 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 uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
> +{
> + return 0;
> +}
> +
> +/*
> + * 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 = 0xff;
Instead of hardcoding 0xff here, consider using UINT8_MAX. This makes
it clear that the value is the maximum for uint8_t and avoids magic
numbers.
Similarly, in other places where constants for 16-bit or 32-bit
unsigned integers are used, it would be good to replace them with
UINT16_MAX and UINT32_MAX respectively for consistency and clarity.
> + 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 = 0xffff;
> + 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 = 0xffffffff;
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * 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)
> +{
> +#define op(dir) (((dir) == IOREQ_WRITE) ? 'W' : 'R')
Instead of defining the op(dir) macro, it might be cleaner to compute
the direction character once at the beginning, e.g.:
const char dir_char = (dir == IOREQ_WRITE) ? 'W' : 'R';
and then use dir_char in printk()/debug. This avoids the local macro
and makes the code easier to follow.
> + struct domain *d = rcu_lock_current_domain();
> + struct vuart *vuart = vuart_find_by_io_range(d, addr, size);
> + struct vuart_ns16x50 *vdev;
> + const struct domain *owner;
> + const struct vuart_info *info;
> + uint32_t reg;
> + unsigned dlab;
> + int rc;
> +
> + if ( !vuart || !vuart->vdev )
> + {
> + printk(XENLOG_ERR "%c io 0x%04x %d: not initialized\n",
> + op(dir), addr, size);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> + vdev = vuart->vdev;
> +
> + owner = vuart->owner;
> + ASSERT(owner);
> + if ( d != owner )
> + {
> + ns16x50_err(vdev, "%c io 0x%04x %d: does not match current domain %pv\n",
> + op(dir), addr, size, d);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> +
> + info = vuart->info;
> + ASSERT(info);
> + reg = addr - info->base_addr;
> + if ( !IS_ALIGNED(reg, size) )
> + {
> + ns16x50_err(vdev, "%c 0x%04x %d: unaligned access\n",
> + op(dir), addr, size);
> + goto out;
> + }
> +
> + dlab = ns16x50_dlab_get(vdev);
> + if ( reg >= NS16X50_REGS_NUM )
> + {
> + ns16x50_err(vdev, "%c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
> + op(dir), addr, size, dlab, reg, *data);
> + goto out;
> + }
> +
> + spin_lock(&vdev->lock);
> +
> + if ( dir == IOREQ_WRITE )
> + {
> + ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + op(dir), addr, size, dlab, reg, *data);
> + rc = ns16x50_io_write(vdev, reg, size, data);
AFAICT ns16x50_io_read() and ns16x50_io_write() have identical
signatures. Would it be cleaner to store them in an array of
function pointers and call through that, e.g.:
rc = ns16x50_io_op[dir == IOREQ_WRITE](vdev, reg, size, data);
This would avoid the if/else block here.
> + }
> + else
> + {
> + rc = ns16x50_io_read(vdev, reg, size, data);
> + ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + op(dir), addr, size, dlab, reg, *data);
The ns16x50_debug() call is duplicated in both branches of the
IOREQ_WRITE check, differing only in whether it's placed before or
after the access. Would it make sense to move this debug print
outside the condition, so the same code is used for both read and
write paths (assuming the "data" is not modified during a write)?
> + }
> + if ( rc < 0 )
> + ns16x50_err(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
> + op(dir), addr, size, dlab, reg, *data);
The ns16x50_err() call doesn’t require holding vdev->lock, so it would
be cleaner to move it after spin_unlock(). That way the critical section
is shorter.
> +
> + spin_unlock(&vdev->lock);
> +
> +out:
> + rcu_unlock_domain(d);
> +
> + return X86EMUL_OKAY;
> +#undef op
> +}
> +
> +static int ns16x50_init(void *arg)
> +{
> + struct vuart_ns16x50 *vdev = arg;
> + const struct vuart_info *info = vdev->info;
> + struct domain *d = vdev->owner;
> +
> + ASSERT(vdev);
> +
> + register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
> +
> + 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 ( !info )
> + return ERR_PTR(-EINVAL);
> +
> + if ( vuart_find_by_io_range(d, info->base_addr, info->size) )
> + {
> + ns16x50_err(info, "already registered\n");
> + return ERR_PTR(-EBUSY);
> + }
> +
> + if ( !is_hvm_domain(d) )
Currently, ns16x50_alloc() first calls vuart_find_by_io_range() and
only afterwards checks if the domain is an HVM domain. Wouldn’t it
be more logical to perform the HVM check first? If the console is
only available for HVM domains, the extra check for an uninitialized
vuart field seems unnecessary.
> + {
> + ns16x50_err(info, "not an HVM domain\n");
> + return ERR_PTR(-ENOSYS);
> + }
> +
> + vdev = xvzalloc(typeof(*vdev));
> + if ( !vdev )
> + {
> + ns16x50_err(info, "failed to allocate memory\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + spin_lock_init(&vdev->lock);
> + vdev->name = info->name;
> + vdev->owner = d;
> + vdev->info = info;
> +
> + rc = ns16x50_init(vdev);
> + if ( rc )
If ns16x50_init(vdev) fails, vdev should be freed with xvfree() to
avoid a memory leak.
Currently, ns16x50_init() always returns 0. If it is not planned to
return other values in the future, it may be simpler to make the
function return void, avoiding the need for the rc variable and
conditional checks.
> + 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, \
dump_state is set to NULL, but vuart_dump_state() in the previous
commit does not check this pointer. If all commits up to this one
are applied and domain state is dumped, this could result in a
NULL pointer dereference and crash the hypervisor.
Consider adding a NULL check in vuart_dump_state() or initializing
dump_state properly.
> + .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.51.0
>
>
[1] https://patchew.org/Xen/20250416212911.410946-1-jason.andryuk@amd.com/
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers
2025-09-05 23:27 ` [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers dmukhin
2025-09-06 2:03 ` Stefano Stabellini
@ 2025-09-06 21:12 ` Mykola Kvach
2025-09-08 6:41 ` dmukhin
1 sibling, 1 reply; 54+ messages in thread
From: Mykola Kvach @ 2025-09-06 21:12 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
Hi Denis,
On Sat, Sep 6, 2025 at 3:11 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add DLL/DLM registers emulation.
>
> DLL/DLM registers report hardcoded 115200 baud rate to the guest OS.
>
> Add stub for ns16x50_dlab_get() helper.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - dropped ns16x50_dlab_get() hunk (moved to emulator stub)
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-5-dmukhin@ford.com/
> ---
> xen/common/emul/vuart/ns16x50.c | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> index 0462a961e785..7f479a5be4a2 100644
> --- a/xen/common/emul/vuart/ns16x50.c
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -97,8 +97,13 @@ static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
> static int ns16x50_io_write8(
> struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
> {
> + uint8_t *regs = vdev->regs;
> + uint8_t val = *data;
> int rc = 0;
>
> + if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> + regs[NS16X50_REGS_NUM + reg] = val;
Some documentation (e.g., DesignWare DW_apb_uart Databook, v3.04a)
notes that if the Divisor Latch Registers (DLL and DLH) are set to
zero, the baud clock is disabled and no serial communications occur.
Should we handle the zero-divisor case to emulate this behavior more
accurately?
> +
> return rc;
> }
>
> @@ -109,8 +114,16 @@ static int ns16x50_io_write8(
> static int ns16x50_io_write16(
> struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data)
> {
> + uint16_t val = *data;
> int rc = -EINVAL;
>
> + if ( ns16x50_dlab_get(vdev) && reg == UART_DLL )
> + {
> + vdev->regs[NS16X50_REGS_NUM + UART_DLL] = val & 0xff;
> + vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
Instead of hardcoding 0xff here (and in similar lines below), consider
using UINT8_MAX. This makes it explicit that the value is the maximum
for a uint8_t and avoids magic numbers.
> + rc = 0;
> + }
> +
> return rc;
> }
>
> @@ -146,9 +159,13 @@ static int ns16x50_io_write(
> static int ns16x50_io_read8(
> struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
> {
> + uint8_t *regs = vdev->regs;
> uint8_t val = 0xff;
> int rc = 0;
>
> + if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> + val = regs[NS16X50_REGS_NUM + reg];
> +
> *data = val;
>
> return rc;
> @@ -163,6 +180,13 @@ static int ns16x50_io_read16(
> uint16_t val = 0xffff;
> int rc = -EINVAL;
>
> + if ( ns16x50_dlab_get(vdev) && reg == UART_DLL )
> + {
> + val = vdev->regs[NS16X50_REGS_NUM + UART_DLM] << 8 |
> + vdev->regs[NS16X50_REGS_NUM + UART_DLL];
> + rc = 0;
> + }
> +
> *data = val;
>
> return rc;
> @@ -280,12 +304,17 @@ out:
>
> static int ns16x50_init(void *arg)
> {
> + const uint16_t divisor = (UART_CLOCK_HZ / 115200) >> 4;
> struct vuart_ns16x50 *vdev = arg;
> const struct vuart_info *info = vdev->info;
> struct domain *d = vdev->owner;
>
> ASSERT(vdev);
>
> + /* NB: report 115200 baud rate. */
> + vdev->regs[NS16X50_REGS_NUM + UART_DLL] = divisor & 0xff;
> + vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
> +
> register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
>
> return 0;
> --
> 2.51.0
>
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 05/15] emul/ns16x50: implement SCR register
2025-09-05 23:27 ` [PATCH v6 05/15] emul/ns16x50: implement SCR register dmukhin
2025-09-06 2:03 ` Stefano Stabellini
@ 2025-09-06 21:24 ` Mykola Kvach
2025-09-08 3:37 ` dmukhin
1 sibling, 1 reply; 54+ messages in thread
From: Mykola Kvach @ 2025-09-06 21:24 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
Hi Denis,
On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add SCR register emulation to the I/O port handler.
> Firmware (e.g. OVMF) may use SCR during the guest OS boot.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - moved earlier in the series to simplify I/O handler population in
> the follow on patches
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-11-dmukhin@ford.com/
> ---
> xen/common/emul/vuart/ns16x50.c | 27 +++++++++++++++++++++++++++
> 1 file changed, 27 insertions(+)
>
> diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> index 7f479a5be4a2..51ec85e57627 100644
> --- a/xen/common/emul/vuart/ns16x50.c
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -103,6 +103,20 @@ static int ns16x50_io_write8(
>
> if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> regs[NS16X50_REGS_NUM + reg] = val;
> + else
> + {
> + switch ( reg )
> + {
> + /* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
> + case UART_SCR:
> + regs[UART_SCR] = val;
> + break;
> +
> + default:
> + rc = -EINVAL;
In the previous commit, when ns16x50_dlab_get() was zero and UART_DLL
or UART_DLM was accessed, the function returned 0. With this commit,
the behavior changes: now an -EINVAL error is returned for both DLL
and DLM when ns16x50_dlab_get() is zero.
Should this be fixed in the previous commit, or is this change
intentional in this one? Note that for 16-bit accesses you already
return an error when ns16x50_dlab_get() is zero, so the behavior is
inconsistent for 8-bit accesses to DLL/DLM.
> + break;
> + }
> + }
>
> return rc;
> }
> @@ -165,6 +179,19 @@ static int ns16x50_io_read8(
>
> if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> val = regs[NS16X50_REGS_NUM + reg];
> + else
> + {
> + switch ( reg )
> + {
> + case UART_SCR:
> + val = regs[UART_SCR];
> + break;
> +
> + default:
> + rc = -EINVAL;
> + break;
> + }
> + }
>
> *data = val;
>
> --
> 2.51.0
>
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers
2025-09-06 1:42 ` Stefano Stabellini
@ 2025-09-08 2:35 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 2:35 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Fri, Sep 05, 2025 at 06:42:23PM -0700, Stefano Stabellini wrote:
> On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
[..]
> > + /* Calculate changes in modem status */
> > + if ( (msr_curr & UART_MSR_CTS) ^ (msr_next & UART_MSR_CTS) )
> > + msr_delta |= UART_MSR_DCTS;
> > + if ( (msr_curr & UART_MSR_DSR) ^ (msr_next & UART_MSR_DSR) )
> > + msr_delta |= UART_MSR_DDSR;
> > + if ( (msr_curr & UART_MSR_RI) & (msr_next & UART_MSR_RI) )
> > + msr_delta |= UART_MSR_TERI;
>
> Should this be:
>
> if ( (msr_curr & UART_MSR_RI) && !(msr_next & UART_MSR_RI) )
> msr_delta |= UART_MSR_TERI;
>
> ?
Thanks for the catch!
TL16C550C spec (7.7.10 Modem Status Register (MSR)) says TERI is set on
RI's 0->1 transition:
if ( !(msr_curr & UART_MSR_RI) && (msr_next & UART_MSR_RI) )
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers
2025-09-06 1:42 ` Stefano Stabellini
@ 2025-09-08 2:37 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 2:37 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Fri, Sep 05, 2025 at 06:42:13PM -0700, Stefano Stabellini wrote:
> On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
[..]
> > +static void ns16x50_irq_assert(const struct vuart_ns16x50 *vdev)
> > +{
> > + struct domain *d = vdev->owner;
> > + const struct vuart_info *info = vdev->info;
> > + int vector;
> > +
> > + if ( has_vpic(d) )
> > + vector = hvm_isa_irq_assert(d, info->irq, vioapic_get_vector);
> > + else
> > + ASSERT_UNREACHABLE();
>
> This seems dangerous as there are guests without vpic
Agreed; will update.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 10/15] emul/ns16x50: implement THR register
2025-09-06 1:59 ` Stefano Stabellini
@ 2025-09-08 2:50 ` dmukhin
2025-09-08 3:15 ` dmukhin
0 siblings, 1 reply; 54+ messages in thread
From: dmukhin @ 2025-09-08 2:50 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Fri, Sep 05, 2025 at 06:59:30PM -0700, Stefano Stabellini wrote:
> On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
[..]
> > @@ -439,6 +523,16 @@ static int ns16x50_io_read8(
> >
> > case UART_IIR: /* RO */
> > val = ns16x50_iir_get(vdev);
> > +
> > + /*
> > + * Since there's no baud rate emulation, transmits are immediate
> > + * to the guest. Clear IIR scratch location to make sure there
> > + * will be interrupt generated once guest re-enabled ETHREI in
> > + * IER.
> > + */
> > + if ( val & UART_IIR_THR )
> > + regs[NS16X50_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
>
> Why clear UART_IIR_THR here?
>
> UART_IIR_THR should be set if the out buffer is not full and should not
> be set of the out buffer is full?
Now that the THR/FCR register emulation _may_ clear UART_IIR_THR, clearing
UART_IIR_THR here is not needed.
Thanks for the catch!
>
> Given that the only function adding to out is ns16x50_fifo_tx_putchar,
> and given that ns16x50_fifo_tx_putchar clears the out buffer when full
> by calling ns16x50_fifo_tx_flush if ns16x50_fifo_tx_full, then basically
> we can keep UART_IIR_THR set all the time?
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 10/15] emul/ns16x50: implement THR register
2025-09-08 2:50 ` dmukhin
@ 2025-09-08 3:15 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 3:15 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Sun, Sep 07, 2025 at 07:50:34PM -0700, dmukhin@xen.org wrote:
> On Fri, Sep 05, 2025 at 06:59:30PM -0700, Stefano Stabellini wrote:
> > On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> > > From: Denis Mukhin <dmukhin@ford.com>
> [..]
> > > @@ -439,6 +523,16 @@ static int ns16x50_io_read8(
> > >
> > > case UART_IIR: /* RO */
> > > val = ns16x50_iir_get(vdev);
> > > +
> > > + /*
> > > + * Since there's no baud rate emulation, transmits are immediate
> > > + * to the guest. Clear IIR scratch location to make sure there
> > > + * will be interrupt generated once guest re-enabled ETHREI in
> > > + * IER.
> > > + */
> > > + if ( val & UART_IIR_THR )
> > > + regs[NS16X50_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
> >
> > Why clear UART_IIR_THR here?
> >
> > UART_IIR_THR should be set if the out buffer is not full and should not
> > be set of the out buffer is full?
>
> Now that the THR/FCR register emulation _may_ clear UART_IIR_THR, clearing
> UART_IIR_THR here is not needed.
>
> Thanks for the catch!
Clarification: I reworked the code to report UART_IIR_THR based on
ns16x50_fifo_tx_full() status.
>
> >
> > Given that the only function adding to out is ns16x50_fifo_tx_putchar,
> > and given that ns16x50_fifo_tx_putchar clears the out buffer when full
> > by calling ns16x50_fifo_tx_flush if ns16x50_fifo_tx_full, then basically
> > we can keep UART_IIR_THR set all the time?
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 05/15] emul/ns16x50: implement SCR register
2025-09-06 21:24 ` Mykola Kvach
@ 2025-09-08 3:37 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 3:37 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Sun, Sep 07, 2025 at 12:24:24AM +0300, Mykola Kvach wrote:
> Hi Denis,
>
> On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
> >
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Add SCR register emulation to the I/O port handler.
> > Firmware (e.g. OVMF) may use SCR during the guest OS boot.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Changes since v5:
> > - moved earlier in the series to simplify I/O handler population in
> > the follow on patches
> > - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-11-dmukhin@ford.com/
> > ---
> > xen/common/emul/vuart/ns16x50.c | 27 +++++++++++++++++++++++++++
> > 1 file changed, 27 insertions(+)
> >
> > diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> > index 7f479a5be4a2..51ec85e57627 100644
> > --- a/xen/common/emul/vuart/ns16x50.c
> > +++ b/xen/common/emul/vuart/ns16x50.c
> > @@ -103,6 +103,20 @@ static int ns16x50_io_write8(
> >
> > if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> > regs[NS16X50_REGS_NUM + reg] = val;
> > + else
> > + {
> > + switch ( reg )
> > + {
> > + /* NB: Firmware (e.g. OVMF) may rely on SCR presence. */
> > + case UART_SCR:
> > + regs[UART_SCR] = val;
> > + break;
> > +
> > + default:
> > + rc = -EINVAL;
>
> In the previous commit, when ns16x50_dlab_get() was zero and UART_DLL
> or UART_DLM was accessed, the function returned 0. With this commit,
> the behavior changes: now an -EINVAL error is returned for both DLL
> and DLM when ns16x50_dlab_get() is zero.
>
> Should this be fixed in the previous commit, or is this change
> intentional in this one? Note that for 16-bit accesses you already
> return an error when ns16x50_dlab_get() is zero, so the behavior is
> inconsistent for 8-bit accesses to DLL/DLM.
I agree, it makes sense to move the switch() block with default register
handling to the previous DLL/DLM commit; will update.
Thanks!
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers
2025-09-06 21:12 ` Mykola Kvach
@ 2025-09-08 6:41 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 6:41 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Sun, Sep 07, 2025 at 12:12:08AM +0300, Mykola Kvach wrote:
> Hi Denis,
>
> On Sat, Sep 6, 2025 at 3:11 AM <dmukhin@xen.org> wrote:
> >
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Add DLL/DLM registers emulation.
> >
> > DLL/DLM registers report hardcoded 115200 baud rate to the guest OS.
> >
> > Add stub for ns16x50_dlab_get() helper.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Changes since v5:
> > - dropped ns16x50_dlab_get() hunk (moved to emulator stub)
> > - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-5-dmukhin@ford.com/
> > ---
> > xen/common/emul/vuart/ns16x50.c | 29 +++++++++++++++++++++++++++++
> > 1 file changed, 29 insertions(+)
> >
> > diff --git a/xen/common/emul/vuart/ns16x50.c b/xen/common/emul/vuart/ns16x50.c
> > index 0462a961e785..7f479a5be4a2 100644
> > --- a/xen/common/emul/vuart/ns16x50.c
> > +++ b/xen/common/emul/vuart/ns16x50.c
> > @@ -97,8 +97,13 @@ static uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
> > static int ns16x50_io_write8(
> > struct vuart_ns16x50 *vdev, uint32_t reg, uint8_t *data)
> > {
> > + uint8_t *regs = vdev->regs;
> > + uint8_t val = *data;
> > int rc = 0;
> >
> > + if ( ns16x50_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> > + regs[NS16X50_REGS_NUM + reg] = val;
>
> Some documentation (e.g., DesignWare DW_apb_uart Databook, v3.04a)
> notes that if the Divisor Latch Registers (DLL and DLH) are set to
> zero, the baud clock is disabled and no serial communications occur.
>
> Should we handle the zero-divisor case to emulate this behavior more
> accurately?
Good idea, thanks!
I will plumb zero-divisor logic into RBR/THR handling.
>
> > +
> > return rc;
> > }
> >
> > @@ -109,8 +114,16 @@ static int ns16x50_io_write8(
> > static int ns16x50_io_write16(
> > struct vuart_ns16x50 *vdev, uint32_t reg, uint16_t *data)
> > {
> > + uint16_t val = *data;
> > int rc = -EINVAL;
> >
> > + if ( ns16x50_dlab_get(vdev) && reg == UART_DLL )
> > + {
> > + vdev->regs[NS16X50_REGS_NUM + UART_DLL] = val & 0xff;
> > + vdev->regs[NS16X50_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
>
> Instead of hardcoding 0xff here (and in similar lines below), consider
> using UINT8_MAX. This makes it explicit that the value is the maximum
> for a uint8_t and avoids magic numbers.
Thanks for suggestion!
Will update.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-06 9:43 ` Mykola Kvach
@ 2025-09-08 7:16 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 7:16 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Sat, Sep 06, 2025 at 12:43:01PM +0300, Mykola Kvach wrote:
> Hi Denis,
>
> On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
[..]
> > +
> > +const static struct vuart *
> > +vuart_find_by_console_permission(const struct domain *d)
>
> Other functions that search for a console (e.g., by compatible string or IO
> range) take an argument to specify what to search by. Here,
> vuart_find_by_console_permission takes no argument and just checks a single
> flag. It might be clearer to either add a flags argument to make it general,
> or rename the function to reflect that it checks only this one permission flag.
Agreed, will update.
Thanks for the suggestion.
>
> > +{
> > + const struct vuart *vuart = d->console.vuart;
> > +
> > + if ( !vuart || !vuart->emulator || !vuart->emulator->put_rx ||
>
> Looking at vuart_init, vuart->emulator is always set when vuart is valid.
> So the vuart->emulator check seems redundant.
Ack.
>
> If it is truly needed, the same check should also appear in
> vuart_dump_state and vuart_deinit. Otherwise, for consistency we
> could safely assume vuart->emulator is non-NULL after vuart_init.
Agreed, will update.
>
> > + !(vuart->flags & VUART_CONSOLE_INPUT))
> > + return NULL;
> > +
> > + return vuart;
> > +}
> > +
> > +struct vuart *vuart_find_by_io_range(struct domain *d, unsigned long addr,
> > + unsigned long size)
> > +{
> > + struct vuart *vuart = d->console.vuart;
> > +
> > + if ( !vuart || !vuart->info )
>
> Is it possible to have a valid vuart pointer without a valid info pointer?
Yes, the vuart->info check is redundant, will drop.
>
> > + return NULL;
> > +
> > + if ( addr >= vuart->info->base_addr &&
> > + addr + size - 1 <= vuart->info->base_addr + vuart->info->size - 1 )
> > + return vuart;
> > +
> > + return NULL;
> > +}
> > +
> > +int vuart_init(struct domain *d, struct vuart_info *info)
> > +{
> > + const struct vuart_emulator *emulator;
> > + struct vuart *vuart;
> > + int rc;
> > +
> > + if ( d->console.vuart )
> > + return -EBUSY;
> > +
> > + emulator = vuart_match_by_compatible(d, info->compatible);
> > + if ( !emulator )
> > + return -ENODEV;
> > +
> > + vuart = xzalloc(typeof(*vuart));
> > + if ( !vuart )
> > + return -ENOMEM;
> > +
> > + vuart->info = xvzalloc(typeof(*info));
> > + if ( !vuart->info )
> > + {
> > + rc = -ENOMEM;
> > + goto err_out;
> > + }
> > + memcpy(vuart->info, info, sizeof(*info));
> > +
> > + vuart->vdev = emulator->alloc(d, vuart->info);
> > + if ( IS_ERR(vuart->vdev) )
> > + {
> > + rc = PTR_ERR(vuart->vdev);
> > + goto err_out;
> > + }
> > +
> > + vuart->emulator = emulator;
> > + vuart->owner = d;
> > + vuart->flags |= VUART_CONSOLE_INPUT;
> > +
> > + d->console.input_allowed = true;
> > + d->console.vuart = vuart;
> > +
> > + return 0;
> > +
> > + err_out:
> > + if ( vuart )
>
> As far as I can see, it isn’t possible to reach this point when vuart
> is NULL. The err_out label is only jumped to after vuart has been
> successfully allocated, so the check if (vuart) is redundant.
Right, thanks.
>
> > + xvfree(vuart->info);
> > + xvfree(vuart);
> > +
> > + return rc;
> > +}
> > +
> > +/*
> > + * Release any resources taken by UART emulators.
> > + *
> > + * NB: no flags are cleared, since currently exit() is called only during
> > + * domain destroy.
> > + */
> > +void vuart_deinit(struct domain *d)
> > +{
> > + struct vuart *vuart = d->console.vuart;
> > +
> > + if ( vuart )
> > + {
> > + vuart->emulator->free(vuart->vdev);
> > + xvfree(vuart->info);
> > + }
> > + XVFREE(d->console.vuart);
> > +}
> > +
> > +void vuart_dump_state(const struct domain *d)
> > +{
> > + struct vuart *vuart = d->console.vuart;
> > +
> > + if ( vuart )
> > + vuart->emulator->dump_state(vuart->vdev);
> > +}
> > +
> > +/*
> > + * Put character to the *first* suitable emulated UART's FIFO.
> > + */
>
> This comment could be a single line since it doesn’t exceed 80 characters.
I will update the comment.
>
> > +int vuart_put_rx(struct domain *d, char c)
> > +{
> > + const struct vuart *vuart = vuart_find_by_console_permission(d);
>
> If vuart_deinit has already been called, is it possible that vuart
> points to freed memory here or in other places?
>
> Should we add reference counting or locks to protect against such
> use-after-free, or are we relying on higher-level mechanisms to
> guarantee that these structs aren’t freed while vuart is accessed?
That should be covered with rcu_{un,}lock_domain() calls.
But a dedicated vUART lock will be needed in the future series (vpl011 and
hwdom vuart plumbing into the new framework).
>
> Should we also check whether the domain is currently being
> destroyed and avoid putting new characters into the emulated UART
> in that case?
There's only one callsite currently (in the console driver) and it is
guaranteed that domain will not be destroyed during the call.
>
> If we are relying on some upper-level mechanism, I think it deserves a
> comment somewhere to make that guarantee explicit.
Agree, will add some explanations.
Thanks!
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
2025-09-06 2:02 ` Stefano Stabellini
2025-09-06 9:43 ` Mykola Kvach
@ 2025-09-08 8:29 ` Mykola Kvach
2025-09-08 8:43 ` Jan Beulich
2025-09-08 9:45 ` dmukhin
2 siblings, 2 replies; 54+ messages in thread
From: Mykola Kvach @ 2025-09-08 8:29 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> 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_VUART_FRAMEWORK, which will be
> automatically enabled once the user enables any UART emulator.
>
> Current implementation supports maximum of one vUART of each kind per domain.
>
> Use new domain_has_vuart() in the console driver code to check whether to
> forward console input to the domain using vUART.
>
> Enable console forwarding over vUART for hardware domains with a vUART. That
> enables console forwarding to dom0 on x86, since console can be forwarded only
> to Xen, dom0 and pvshim on x86 as of now.
>
> Note: existing vUARTs are deliberately *not* hooked to the new framework to
> minimize the scope of the patch: vpl011 (i.e. SBSA) emulator and "vuart" (i.e.
> minimalistic MMIO-mapped dtuart for hwdoms on Arm) are kept unmodified.
>
> No functional changes for non-x86 architectures.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - addressed v5 feedback
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-2-dmukhin@ford.com/
> ---
> xen/arch/arm/xen.lds.S | 1 +
> xen/arch/ppc/xen.lds.S | 1 +
> xen/arch/riscv/xen.lds.S | 1 +
> xen/arch/x86/xen.lds.S | 1 +
> xen/common/Kconfig | 2 +
> xen/common/Makefile | 1 +
> xen/common/emul/Kconfig | 6 ++
> xen/common/emul/Makefile | 1 +
> xen/common/emul/vuart/Kconfig | 6 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/vuart.c | 157 +++++++++++++++++++++++++++++++++
> xen/common/keyhandler.c | 3 +
> xen/drivers/char/console.c | 6 +-
> xen/include/xen/sched.h | 4 +
> xen/include/xen/serial.h | 3 +
> xen/include/xen/vuart.h | 116 ++++++++++++++++++++++++
> xen/include/xen/xen.lds.h | 10 +++
> 17 files changed, 319 insertions(+), 1 deletion(-)
> create mode 100644 xen/common/emul/Kconfig
> create mode 100644 xen/common/emul/Makefile
> create mode 100644 xen/common/emul/vuart/Kconfig
> create mode 100644 xen/common/emul/vuart/Makefile
> create mode 100644 xen/common/emul/vuart/vuart.c
> create mode 100644 xen/include/xen/vuart.h
>
> diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
> index db17ff1efa98..cd05b18770f4 100644
> --- a/xen/arch/arm/xen.lds.S
> +++ b/xen/arch/arm/xen.lds.S
> @@ -58,6 +58,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/arch/ppc/xen.lds.S b/xen/arch/ppc/xen.lds.S
> index 1de0b77fc6b9..f9d4e5b0dcd8 100644
> --- a/xen/arch/ppc/xen.lds.S
> +++ b/xen/arch/ppc/xen.lds.S
> @@ -52,6 +52,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S
> index edcadff90bfe..59dcaa5fef9a 100644
> --- a/xen/arch/riscv/xen.lds.S
> +++ b/xen/arch/riscv/xen.lds.S
> @@ -47,6 +47,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
> index 966e514f2034..d877b93a6964 100644
> --- a/xen/arch/x86/xen.lds.S
> +++ b/xen/arch/x86/xen.lds.S
> @@ -132,6 +132,7 @@ SECTIONS
> *(.rodata)
> *(.rodata.*)
> VPCI_ARRAY
> + VUART_ARRAY
> *(.data.rel.ro)
> *(.data.rel.ro.*)
>
> diff --git a/xen/common/Kconfig b/xen/common/Kconfig
> index 76f9ce705f7a..78a32b69e2b2 100644
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -676,4 +676,6 @@ config PM_STATS
> Enable collection of performance management statistics to aid in
> analyzing and tuning power/performance characteristics of the system
>
> +source "common/emul/Kconfig"
> +
> endmenu
> diff --git a/xen/common/Makefile b/xen/common/Makefile
> index 0c7d0f5d46e1..8c8462565050 100644
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> obj-$(CONFIG_IOREQ_SERVER) += dm.o
> obj-y += domain.o
> obj-y += domid.o
> +obj-y += emul/
> obj-y += event_2l.o
> obj-y += event_channel.o
> obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o
> diff --git a/xen/common/emul/Kconfig b/xen/common/emul/Kconfig
> new file mode 100644
> index 000000000000..7c6764d1756b
> --- /dev/null
> +++ b/xen/common/emul/Kconfig
> @@ -0,0 +1,6 @@
> +menu "Domain Emulation Features"
> + visible if EXPERT
> +
> +source "common/emul/vuart/Kconfig"
> +
> +endmenu
> diff --git a/xen/common/emul/Makefile b/xen/common/emul/Makefile
> new file mode 100644
> index 000000000000..ae0b575c3901
> --- /dev/null
> +++ b/xen/common/emul/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_VUART_FRAMEWORK) += vuart/
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> new file mode 100644
> index 000000000000..ce1b976b7da7
> --- /dev/null
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -0,0 +1,6 @@
> +config VUART_FRAMEWORK
> + bool
If VUART_FRAMEWORK has no dependencies, it can be enabled on any
architecture. For example, I tried enabling it on arm64 and the build
fails:
./include/xen/vuart.h:26:8: error: redefinition of ‘struct vuart’
Should this config be restricted (e.g. "depends on X86") or the code
adjusted to handle non-x86 architectures properly?
> +
> +menu "UART Emulation"
> +
> +endmenu
> diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
> new file mode 100644
> index 000000000000..97f792dc6641
> --- /dev/null
> +++ b/xen/common/emul/vuart/Makefile
> @@ -0,0 +1 @@
> +obj-y += vuart.o
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> new file mode 100644
> index 000000000000..3dfcba217248
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -0,0 +1,157 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#include <xen/err.h>
> +#include <xen/sched.h>
> +#include <xen/vuart.h>
> +#include <xen/xvmalloc.h>
> +
> +#define for_each_emulator(e) \
> + for ( e = vuart_array_start; e < vuart_array_end; e++ )
> +
> +extern const struct vuart_emulator vuart_array_start[];
> +extern const struct vuart_emulator vuart_array_end[];
> +
> +static const struct vuart_emulator *
> +vuart_match_by_compatible(const struct domain *d, const char *compat)
> +{
> + const struct vuart_emulator *emulator;
> +
> + for_each_emulator(emulator)
> + if ( emulator->compatible &&
> + !strncmp(compat, emulator->compatible,
> + strlen(emulator->compatible)) )
> + return emulator;
> +
> + return NULL;
> +}
> +
> +const static struct vuart *
> +vuart_find_by_console_permission(const struct domain *d)
During the first review of this patch I thought you planned to add a
searching procedure into this and the next function in one of the later
commits. However, looking at the series now, it seems these functions
remain unchanged and don’t actually perform any searching.
Do you think the current names are accurate, or would it make sense to
rename them to better reflect their purpose?
> +{
> + const struct vuart *vuart = d->console.vuart;
> +
> + if ( !vuart || !vuart->emulator || !vuart->emulator->put_rx ||
> + !(vuart->flags & VUART_CONSOLE_INPUT))
> + return NULL;
> +
> + return vuart;
> +}
> +
> +struct vuart *vuart_find_by_io_range(struct domain *d, unsigned long addr,
> + unsigned long size)
> +{
> + struct vuart *vuart = d->console.vuart;
> +
> + if ( !vuart || !vuart->info )
> + return NULL;
> +
> + if ( addr >= vuart->info->base_addr &&
> + addr + size - 1 <= vuart->info->base_addr + vuart->info->size - 1 )
> + return vuart;
> +
> + return NULL;
> +}
> +
> +int vuart_init(struct domain *d, struct vuart_info *info)
> +{
> + const struct vuart_emulator *emulator;
> + struct vuart *vuart;
> + int rc;
> +
> + if ( d->console.vuart )
> + return -EBUSY;
> +
> + emulator = vuart_match_by_compatible(d, info->compatible);
> + if ( !emulator )
> + return -ENODEV;
> +
> + vuart = xzalloc(typeof(*vuart));
> + if ( !vuart )
> + return -ENOMEM;
> +
> + vuart->info = xvzalloc(typeof(*info));
> + if ( !vuart->info )
> + {
> + rc = -ENOMEM;
> + goto err_out;
> + }
> + memcpy(vuart->info, info, sizeof(*info));
> +
> + vuart->vdev = emulator->alloc(d, vuart->info);
> + if ( IS_ERR(vuart->vdev) )
> + {
> + rc = PTR_ERR(vuart->vdev);
> + goto err_out;
> + }
> +
> + vuart->emulator = emulator;
> + vuart->owner = d;
> + vuart->flags |= VUART_CONSOLE_INPUT;
> +
> + d->console.input_allowed = true;
> + d->console.vuart = vuart;
> +
> + return 0;
> +
> + err_out:
> + if ( vuart )
> + xvfree(vuart->info);
> + xvfree(vuart);
> +
> + return rc;
> +}
> +
> +/*
> + * Release any resources taken by UART emulators.
> + *
> + * NB: no flags are cleared, since currently exit() is called only during
> + * domain destroy.
> + */
> +void vuart_deinit(struct domain *d)
> +{
> + struct vuart *vuart = d->console.vuart;
> +
> + if ( vuart )
> + {
> + vuart->emulator->free(vuart->vdev);
> + xvfree(vuart->info);
> + }
> + XVFREE(d->console.vuart);
> +}
> +
> +void vuart_dump_state(const struct domain *d)
> +{
> + struct vuart *vuart = d->console.vuart;
> +
> + if ( vuart )
> + vuart->emulator->dump_state(vuart->vdev);
> +}
> +
> +/*
> + * Put character to the *first* suitable emulated UART's FIFO.
> + */
> +int vuart_put_rx(struct domain *d, char c)
> +{
> + const struct vuart *vuart = vuart_find_by_console_permission(d);
> +
> + return vuart ? vuart->emulator->put_rx(vuart->vdev, c) : -ENODEV;
> +}
> +
> +bool domain_has_vuart(const struct domain *d)
> +{
> + return vuart_find_by_console_permission(d);
> +}
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
> index cb6df2823b00..156e64d9eb58 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;
> @@ -352,6 +353,8 @@ static void cf_check dump_domains(unsigned char key)
> v->periodic_period / 1000000);
> }
> }
> +
> + vuart_dump_state(d);
> }
>
> for_each_domain ( d )
> diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> index 9bd5b4825da6..d5164897a776 100644
> --- a/xen/drivers/char/console.c
> +++ b/xen/drivers/char/console.c
> @@ -33,6 +33,7 @@
> #include <asm/setup.h>
> #include <xen/sections.h>
> #include <xen/consoled.h>
> +#include <xen/vuart.h>
>
> #ifdef CONFIG_X86
> #include <asm/guest.h>
> @@ -596,11 +597,12 @@ static void __serial_rx(char c)
> if ( !d )
> return;
>
> - if ( is_hardware_domain(d) )
> + if ( is_hardware_domain(d) && !domain_has_vuart(d) )
> {
> /*
> * 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;
> @@ -611,6 +613,8 @@ static void __serial_rx(char c)
> */
> send_global_virq(VIRQ_CONSOLE);
> }
> + else if ( domain_has_vuart(d) )
> + rc = vuart_put_rx(d, c);
> #ifdef CONFIG_SBSA_VUART_CONSOLE
> else
> /* Deliver input to the emulated UART. */
> diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
> index 02bdc256ce37..613f4596e33d 100644
> --- a/xen/include/xen/sched.h
> +++ b/xen/include/xen/sched.h
> @@ -23,6 +23,7 @@
> #include <asm/atomic.h>
> #include <asm/current.h>
> #include <xen/vpci.h>
> +#include <xen/vuart.h>
> #include <xen/wait.h>
> #include <public/xen.h>
> #include <public/domctl.h>
> @@ -660,6 +661,9 @@ struct domain
> struct {
> /* Permission to take ownership of the physical console input. */
> bool input_allowed;
> +#ifdef CONFIG_VUART_FRAMEWORK
> + struct vuart *vuart;
> +#endif
> } console;
> } __aligned(PAGE_SIZE);
>
> diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h
> index 8e1844555208..123eee67df35 100644
> --- a/xen/include/xen/serial.h
> +++ b/xen/include/xen/serial.h
> @@ -36,6 +36,9 @@ struct vuart_info {
> unsigned long data_off; /* Data register offset */
> unsigned long status_off; /* Status register offset */
> unsigned long status; /* Ready status value */
> + unsigned int irq; /* Interrupt */
> + char compatible[16]; /* Compatible string */
> + char name[16]; /* User-friendly name */
> };
>
> struct serial_port {
> diff --git a/xen/include/xen/vuart.h b/xen/include/xen/vuart.h
> new file mode 100644
> index 000000000000..54f2f29f3f4a
> --- /dev/null
> +++ b/xen/include/xen/vuart.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#ifndef XEN_VUART_H
> +#define XEN_VUART_H
> +
> +#include <xen/serial.h>
> +#include <public/xen.h>
> +
> +struct vuart_emulator;
> +
> +enum {
> + VUART_CONSOLE_INPUT = BIT(0, U), /* Physical console input forwarding. */
> +};
> +
> +
> +/*
> + * FIXME: #ifdef is temporary to avoid clash with
> + * arch/arm/include/asm/domain.h
> + */
> +#ifdef CONFIG_VUART_FRAMEWORK
> +struct vuart {
> + const struct vuart_emulator *emulator;
> + struct vuart_info *info;
> + struct domain *owner;
> + uint32_t flags;
> + void *vdev;
> +};
> +#endif
> +
> +struct vuart_emulator {
> + /* UART compatible string. Cannot be NULL or empty. */
> + const char *compatible;
> +
> + /*
> + * Allocate emulated UART state (RX/TX FIFOs, locks, initialize registers,
> + * hook I/O handlers, etc.)
> + * Cannot be NULL.
> + */
> + void *(*alloc)(struct domain *d, const struct vuart_info *info);
> +
> + /*
> + * Release resources used to emulate UART state (flush RX/TX FIFOs, unhook
> + * I/O handlers, etc.).
> + * Cannot be NULL.
> + */
> + void (*free)(void *arg);
> +
> + /*
> + * Print emulated UART state, including registers, on the console.
> + * Can be NULL.
> + */
> + void (*dump_state)(void *arg);
> +
> + /*
> + * Place character to the emulated RX FIFO.
> + * Used to forward physical console input to the guest OS.
> + * Can be NULL.
> + */
> + int (*put_rx)(void *arg, char c);
> +};
> +
> +#define VUART_REGISTER(name, x) \
> + static const struct vuart_emulator name##_entry \
> + __used_section(".data.rel.ro.vuart") = x
> +
> +struct vuart *vuart_find_by_io_range(struct domain *d,
> + unsigned long base_addr,
> + unsigned long size);
> +
> +int vuart_put_rx(struct domain *d, char c);
> +
> +#ifdef CONFIG_VUART_FRAMEWORK
> +
> +int vuart_init(struct domain *d, struct vuart_info *info);
> +void vuart_deinit(struct domain *d);
> +void vuart_dump_state(const struct domain *d);
> +bool domain_has_vuart(const struct domain *d);
> +
> +#else
> +
> +static inline int vuart_init(struct domain *d, struct vuart_info *info)
> +{
> + return 0;
> +}
> +
> +static inline void vuart_deinit(struct domain *d)
> +{
> +}
> +
> +static inline void vuart_dump_state(const struct domain *d)
> +{
> +}
> +
> +static inline bool domain_has_vuart(const struct domain *d)
> +{
> + return false;
> +}
> +
> +#endif /* CONFIG_VUART_FRAMEWORK */
> +
> +#endif /* XEN_VUART_H */
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> +
> diff --git a/xen/include/xen/xen.lds.h b/xen/include/xen/xen.lds.h
> index b126dfe88792..2d65f32ddad3 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_VUART_FRAMEWORK
> +#define VUART_ARRAY \
> + . = ALIGN(POINTER_ALIGN); \
> + vuart_array_start = .; \
> + *(.data.rel.ro.vuart) \
> + vuart_array_end = .;
> +#else
> +#define VUART_ARRAY
> +#endif
> +
> #endif /* __XEN_LDS_H__ */
> --
> 2.51.0
>
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-06 20:37 ` Mykola Kvach
@ 2025-09-08 8:32 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 8:32 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Sat, Sep 06, 2025 at 11:37:28PM +0300, Mykola Kvach wrote:
> Hi Denis,
>
> On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
> >
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > The change is the first on the way on introducing minimally functional
> > NS16550-compatible UART emulator.
> >
> > Define UART state and a set of emulated registers.
> >
> > Implement alloc/free vUART hooks.
> >
> > Stub out I/O port handler.
> >
> > Add initialization of the NS16x50-compatible UART emulator state machine.
> >
> > Plumb debug logging.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Changes since v5:
> > - v5 feedback
> > - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-4-dmukhin@ford.com/
> > ---
> > xen/arch/x86/hvm/hvm.c | 21 ++
> > xen/common/emul/vuart/Kconfig | 19 ++
> > xen/common/emul/vuart/Makefile | 1 +
> > xen/common/emul/vuart/ns16x50.c | 366 ++++++++++++++++++++++++++++++++
> > 4 files changed, 407 insertions(+)
> > create mode 100644 xen/common/emul/vuart/ns16x50.c
> >
> > diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> > index 23bd7f078a1d..91c971f11e14 100644
> > --- a/xen/arch/x86/hvm/hvm.c
> > +++ b/xen/arch/x86/hvm/hvm.c
> > @@ -28,6 +28,7 @@
> > #include <xen/softirq.h>
> > #include <xen/trace.h>
> > #include <xen/vm_event.h>
>
> I noticed that this include ...
>
> > +#include <xen/vuart.h>
>
> ... is out of alphabetical order. All other includes in this block
> are correctly sorted.
Thanks, will update.
>
> > #include <xen/vpci.h>
> > #include <xen/wait.h>
> > #include <xen/warning.h>
> > @@ -689,6 +690,22 @@ int hvm_domain_initialise(struct domain *d,
> > if ( rc != 0 )
> > goto fail1;
> >
> > + /* Limit NS16550 emulator for dom0 only for now. */
> > + if ( IS_ENABLED(CONFIG_VUART_NS16X50) && is_hardware_domain(d) )
>
> Currently, the Xen glossary defines a "hardware domain" as:
>
> A domain, commonly dom0, which shares responsibility with Xen
> about the system as a whole.
>
> It has been historically correct to treat is_hardware_domain(d) as
> equivalent to dom0. However, according to patch series [1], this is
> no longer guaranteed:
>
> "Setting hardware domain as domid 0 is removed."
>
> After these changes, the assumption that hardware domain always equals
> dom0 may not hold. As a result, the above comment in the code could
> become misleading. Consider updating it to something like:
>
> /* Limit NS16550 emulator to the hardware domain only for now */
>
> to reflect the new semantics.
I adjusted the code a bit so that it is possible to test vUART for
either hwdom or domU; will remove the comment in v7.
>
> > + {
> > + struct vuart_info info = {
> > + .name = "COM2",
> > + .compatible = "ns16550",
> > + .base_addr = 0x2f8,
> > + .size = 8,
> > + .irq = 3,
> > + };
>
> Consider defining COM2 base address and IRQ in a shared header
> (e.g., VUART_COM2_BASE and VUART_COM2_IRQ) rather than using
> the magic numbers 0x2f8 and 3 here. This would allow reuse in
> `__start_xen` and other places, and makes the code clearer and
> easier to maintain.
PC platform resources are standard and not going to change, so
I think it is satisfactory to use hardcoded numbers here:
COM1 - 0x3f8 / IRQ#4
COM2 - 0x2f8 / IRQ#3
COM3 - 0x3fe / IRQ#4
COM4 - 0x2fe / IRQ#3
Other PC I/O ports still use hardcoded values, e.g. dom0_setup_permissions().
Anyway, this code will change again, once xl support is plumbed.
So, I would keep that as is for now...
[..]
> > +/*
> > + * 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 = 0xff;
>
> Instead of hardcoding 0xff here, consider using UINT8_MAX. This makes
> it clear that the value is the maximum for uint8_t and avoids magic
> numbers.
>
> Similarly, in other places where constants for 16-bit or 32-bit
> unsigned integers are used, it would be good to replace them with
> UINT16_MAX and UINT32_MAX respectively for consistency and clarity.
Agreed, will do.
Thanks.
[..]
> > +/*
> > + * 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)
> > +{
> > +#define op(dir) (((dir) == IOREQ_WRITE) ? 'W' : 'R')
>
> Instead of defining the op(dir) macro, it might be cleaner to compute
> the direction character once at the beginning, e.g.:
>
> const char dir_char = (dir == IOREQ_WRITE) ? 'W' : 'R';
>
> and then use dir_char in printk()/debug. This avoids the local macro
> and makes the code easier to follow.
Thanks for suggestion.
Will update.
>
> > + struct domain *d = rcu_lock_current_domain();
> > + struct vuart *vuart = vuart_find_by_io_range(d, addr, size);
> > + struct vuart_ns16x50 *vdev;
> > + const struct domain *owner;
> > + const struct vuart_info *info;
> > + uint32_t reg;
> > + unsigned dlab;
> > + int rc;
> > +
> > + if ( !vuart || !vuart->vdev )
> > + {
> > + printk(XENLOG_ERR "%c io 0x%04x %d: not initialized\n",
> > + op(dir), addr, size);
> > +
> > + ASSERT_UNREACHABLE();
> > + goto out;
> > + }
> > + vdev = vuart->vdev;
> > +
> > + owner = vuart->owner;
> > + ASSERT(owner);
> > + if ( d != owner )
> > + {
> > + ns16x50_err(vdev, "%c io 0x%04x %d: does not match current domain %pv\n",
> > + op(dir), addr, size, d);
> > +
> > + ASSERT_UNREACHABLE();
> > + goto out;
> > + }
> > +
> > + info = vuart->info;
> > + ASSERT(info);
> > + reg = addr - info->base_addr;
> > + if ( !IS_ALIGNED(reg, size) )
> > + {
> > + ns16x50_err(vdev, "%c 0x%04x %d: unaligned access\n",
> > + op(dir), addr, size);
> > + goto out;
> > + }
> > +
> > + dlab = ns16x50_dlab_get(vdev);
> > + if ( reg >= NS16X50_REGS_NUM )
> > + {
> > + ns16x50_err(vdev, "%c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
> > + op(dir), addr, size, dlab, reg, *data);
> > + goto out;
> > + }
> > +
> > + spin_lock(&vdev->lock);
> > +
> > + if ( dir == IOREQ_WRITE )
> > + {
> > + ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> > + op(dir), addr, size, dlab, reg, *data);
> > + rc = ns16x50_io_write(vdev, reg, size, data);
>
> AFAICT ns16x50_io_read() and ns16x50_io_write() have identical
> signatures. Would it be cleaner to store them in an array of
> function pointers and call through that, e.g.:
>
> rc = ns16x50_io_op[dir == IOREQ_WRITE](vdev, reg, size, data);
>
> This would avoid the if/else block here.
I remember I tried this way, but eventually decided to keep just direct calls
for simplicity.
>
> > + }
> > + else
> > + {
> > + rc = ns16x50_io_read(vdev, reg, size, data);
> > + ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> > + op(dir), addr, size, dlab, reg, *data);
>
> The ns16x50_debug() call is duplicated in both branches of the
> IOREQ_WRITE check, differing only in whether it's placed before or
> after the access. Would it make sense to move this debug print
> outside the condition, so the same code is used for both read and
> write paths (assuming the "data" is not modified during a write)?
Yep, that will work, thanks!
>
> > + }
> > + if ( rc < 0 )
> > + ns16x50_err(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
> > + op(dir), addr, size, dlab, reg, *data);
>
> The ns16x50_err() call doesn’t require holding vdev->lock, so it would
> be cleaner to move it after spin_unlock(). That way the critical section
> is shorter.
Ack.
>
> > +
> > + spin_unlock(&vdev->lock);
> > +
> > +out:
> > + rcu_unlock_domain(d);
> > +
> > + return X86EMUL_OKAY;
> > +#undef op
> > +}
> > +
> > +static int ns16x50_init(void *arg)
> > +{
> > + struct vuart_ns16x50 *vdev = arg;
> > + const struct vuart_info *info = vdev->info;
> > + struct domain *d = vdev->owner;
> > +
> > + ASSERT(vdev);
> > +
> > + register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
> > +
> > + 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 ( !info )
> > + return ERR_PTR(-EINVAL);
> > +
> > + if ( vuart_find_by_io_range(d, info->base_addr, info->size) )
> > + {
> > + ns16x50_err(info, "already registered\n");
> > + return ERR_PTR(-EBUSY);
> > + }
> > +
> > + if ( !is_hvm_domain(d) )
>
> Currently, ns16x50_alloc() first calls vuart_find_by_io_range() and
> only afterwards checks if the domain is an HVM domain. Wouldn’t it
> be more logical to perform the HVM check first? If the console is
> only available for HVM domains, the extra check for an uninitialized
> vuart field seems unnecessary.
Ack.
>
> > + {
> > + ns16x50_err(info, "not an HVM domain\n");
> > + return ERR_PTR(-ENOSYS);
> > + }
> > +
> > + vdev = xvzalloc(typeof(*vdev));
> > + if ( !vdev )
> > + {
> > + ns16x50_err(info, "failed to allocate memory\n");
> > + return ERR_PTR(-ENOMEM);
> > + }
> > +
> > + spin_lock_init(&vdev->lock);
> > + vdev->name = info->name;
> > + vdev->owner = d;
> > + vdev->info = info;
> > +
> > + rc = ns16x50_init(vdev);
> > + if ( rc )
>
> If ns16x50_init(vdev) fails, vdev should be freed with xvfree() to
> avoid a memory leak.
Whoops, thanks.
>
> Currently, ns16x50_init() always returns 0. If it is not planned to
> return other values in the future, it may be simpler to make the
> function return void, avoiding the need for the rc variable and
> conditional checks.
There will be non-zero return values in the future.
>
> > + 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, \
>
> dump_state is set to NULL, but vuart_dump_state() in the previous
> commit does not check this pointer. If all commits up to this one
> are applied and domain state is dumped, this could result in a
> NULL pointer dereference and crash the hypervisor.
Thanks; will fix vuart.c
>
> Consider adding a NULL check in vuart_dump_state() or initializing
> dump_state properly.
>
> > + .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.51.0
> >
> >
>
> [1] https://patchew.org/Xen/20250416212911.410946-1-jason.andryuk@amd.com/
>
> Best regards,
> Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 02/15] xen/8250-uart: update definitions
2025-09-06 14:57 ` Mykola Kvach
@ 2025-09-08 8:36 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 8:36 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Sat, Sep 06, 2025 at 05:57:02PM +0300, Mykola Kvach wrote:
> Hi Denis,
>
> On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
[..]
> > /* FIFO Control Register */
> > -#define UART_FCR_ENABLE 0x01 /* enable FIFO */
> > -#define UART_FCR_CLRX 0x02 /* clear Rx FIFO */
> > -#define UART_FCR_CLTX 0x04 /* clear Tx FIFO */
> > -#define UART_FCR_DMA 0x10 /* enter DMA mode */
> > +#define UART_FCR_ENABLE BIT(0, U) /* enable FIFO */
> > +#define UART_FCR_CLRX BIT(1, U) /* clear Rx FIFO */
> > +#define UART_FCR_CLTX BIT(2, U) /* clear Tx FIFO */
> > +#define UART_FCR_DMA BIT(3, U) /* enter DMA mode */
> > +#define UART_FCR_RESERVED0 BIT(4, U) /* reserved; always 0 */
> > +#define UART_FCR_RESERVED1 BIT(5, U) /* reserved; always 0 */
> > +#define UART_FCR_RTB0 BIT(6, U) /* receiver trigger bit #0 */
> > +#define UART_FCR_RTB1 BIT(7, U) /* receiver trigger bit #1 */
> > +#define UART_FCR_TRG_MASK (UART_FCR_RTB0 | UART_FCR_RTB1)
>
> Thanks for the patch. I noticed that in this changeset some bit
> definitions (e.g. UART_FCR_*) were rewritten using the BIT(n, U)
> macro, while others (e.g. UART_IER_* and rest of UART_FCR_*) are
> still left as plain hex values (0x01, 0x02, etc.), even though they
> are also powers of two.
>
> Could you clarify the reasoning behind this choice? From a reader’s
> perspective it looks inconsistent. Would it make sense to either:
>
> - update all of them to use BIT() for consistency, or
> - keep the existing style unchanged in this patch and move a full
> conversion to BIT() into a separate cleanup patch?
>
> This would make the codebase easier to follow.
I find BIT() notation is more readable than raw bitmasks.
But I agree that definitions should be consistently updated.
Will update to the raw masks instead of BIT() in v7.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
` (2 preceding siblings ...)
2025-09-06 20:37 ` Mykola Kvach
@ 2025-09-08 8:37 ` Mykola Kvach
2025-09-08 9:59 ` dmukhin
3 siblings, 1 reply; 54+ messages in thread
From: Mykola Kvach @ 2025-09-08 8:37 UTC (permalink / raw)
To: dmukhin
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
Hi Denis,
On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>
> From: Denis Mukhin <dmukhin@ford.com>
>
> The change is the first on the way on introducing minimally functional
> NS16550-compatible UART emulator.
>
> Define UART state and a set of emulated registers.
>
> Implement alloc/free vUART hooks.
>
> Stub out I/O port handler.
>
> Add initialization of the NS16x50-compatible UART emulator state machine.
>
> Plumb debug logging.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v5:
> - v5 feedback
> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-4-dmukhin@ford.com/
> ---
> xen/arch/x86/hvm/hvm.c | 21 ++
> xen/common/emul/vuart/Kconfig | 19 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/ns16x50.c | 366 ++++++++++++++++++++++++++++++++
> 4 files changed, 407 insertions(+)
> create mode 100644 xen/common/emul/vuart/ns16x50.c
>
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index 23bd7f078a1d..91c971f11e14 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -28,6 +28,7 @@
> #include <xen/softirq.h>
> #include <xen/trace.h>
> #include <xen/vm_event.h>
> +#include <xen/vuart.h>
> #include <xen/vpci.h>
> #include <xen/wait.h>
> #include <xen/warning.h>
> @@ -689,6 +690,22 @@ int hvm_domain_initialise(struct domain *d,
> if ( rc != 0 )
> goto fail1;
>
> + /* Limit NS16550 emulator for dom0 only for now. */
> + if ( IS_ENABLED(CONFIG_VUART_NS16X50) && is_hardware_domain(d) )
> + {
> + struct vuart_info info = {
> + .name = "COM2",
> + .compatible = "ns16550",
> + .base_addr = 0x2f8,
> + .size = 8,
> + .irq = 3,
> + };
> +
> + rc = vuart_init(d, &info);
> + if ( rc )
> + goto out_vioapic_deinit;
> + }
> +
> stdvga_init(d);
>
> rtc_init(d);
> @@ -712,6 +729,8 @@ int hvm_domain_initialise(struct domain *d,
> return 0;
>
> fail2:
> + vuart_deinit(d);
> + out_vioapic_deinit:
> vioapic_deinit(d);
> fail1:
> if ( is_hardware_domain(d) )
> @@ -774,6 +793,8 @@ void hvm_domain_destroy(struct domain *d)
> if ( hvm_funcs.domain_destroy )
> alternative_vcall(hvm_funcs.domain_destroy, d);
>
> + vuart_deinit(d);
> +
> vioapic_deinit(d);
>
> XFREE(d->arch.hvm.pl_time);
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index ce1b976b7da7..a27d7ca135af 100644
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,23 @@ config VUART_FRAMEWORK
>
> menu "UART Emulation"
>
> +config VUART_NS16X50
> + bool "NS16550-compatible UART Emulator" if EXPERT
> + depends on X86 && HVM
Currently VUART_NS16X50 depends on X86, so the code is only
usable on x86. Are there any plans to support this vUART on non-x86
architectures (e.g. ARM, where some UARTs are also ns16550-compatible)?
If not, wouldn’t it be more appropriate to move the ns16x50-specific
implementation into x86-specific directories instead of common?
> + select VUART_FRAMEWORK
> + default n
> + help
> + In-hypervisor NS16x50 UART emulation.
> +
> + Only legacy PC COM2 port is emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
> +config VUART_NS16X50_DEBUG
> + bool "NS16550-compatible UART Emulator Debugging"
> + depends on VUART_NS16X50 && DEBUG
> + help
> + Enable development debugging.
> +
> endmenu
> 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..0462a961e785
> --- /dev/null
> +++ b/xen/common/emul/vuart/ns16x50.c
> @@ -0,0 +1,366 @@
> +/* 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
> + *
> + * 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 <xen/8250-uart.h>
> +#include <xen/console.h>
> +#include <xen/err.h>
> +#include <xen/iocap.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
> + * - R/O, IIR (IIR_THR) = NS16X50_REGS_NUM + 2
> + */
> +#define NS16X50_EMU_REGS_NUM (NS16X50_REGS_NUM + 3)
> +
> +/*
> + * 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 uint8_t ns16x50_dlab_get(const struct vuart_ns16x50 *vdev)
> +{
> + return 0;
> +}
> +
> +/*
> + * 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 = 0xff;
> + 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 = 0xffff;
> + 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 = 0xffffffff;
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * 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)
> +{
> +#define op(dir) (((dir) == IOREQ_WRITE) ? 'W' : 'R')
> + struct domain *d = rcu_lock_current_domain();
> + struct vuart *vuart = vuart_find_by_io_range(d, addr, size);
> + struct vuart_ns16x50 *vdev;
> + const struct domain *owner;
> + const struct vuart_info *info;
> + uint32_t reg;
> + unsigned dlab;
> + int rc;
> +
> + if ( !vuart || !vuart->vdev )
> + {
> + printk(XENLOG_ERR "%c io 0x%04x %d: not initialized\n",
> + op(dir), addr, size);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> + vdev = vuart->vdev;
> +
> + owner = vuart->owner;
> + ASSERT(owner);
> + if ( d != owner )
> + {
> + ns16x50_err(vdev, "%c io 0x%04x %d: does not match current domain %pv\n",
> + op(dir), addr, size, d);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> +
> + info = vuart->info;
> + ASSERT(info);
> + reg = addr - info->base_addr;
> + if ( !IS_ALIGNED(reg, size) )
> + {
> + ns16x50_err(vdev, "%c 0x%04x %d: unaligned access\n",
> + op(dir), addr, size);
> + goto out;
> + }
> +
> + dlab = ns16x50_dlab_get(vdev);
> + if ( reg >= NS16X50_REGS_NUM )
> + {
> + ns16x50_err(vdev, "%c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
> + op(dir), addr, size, dlab, reg, *data);
> + goto out;
> + }
> +
> + spin_lock(&vdev->lock);
> +
> + if ( dir == IOREQ_WRITE )
> + {
> + ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + op(dir), addr, size, dlab, reg, *data);
> + rc = ns16x50_io_write(vdev, reg, size, data);
> + }
> + else
> + {
> + rc = ns16x50_io_read(vdev, reg, size, data);
> + ns16x50_debug(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + op(dir), addr, size, dlab, reg, *data);
> + }
> + if ( rc < 0 )
> + ns16x50_err(vdev, "%c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
> + op(dir), addr, size, dlab, reg, *data);
> +
> + spin_unlock(&vdev->lock);
> +
> +out:
> + rcu_unlock_domain(d);
> +
> + return X86EMUL_OKAY;
> +#undef op
> +}
> +
> +static int ns16x50_init(void *arg)
> +{
> + struct vuart_ns16x50 *vdev = arg;
> + const struct vuart_info *info = vdev->info;
> + struct domain *d = vdev->owner;
> +
> + ASSERT(vdev);
> +
> + register_portio_handler(d, info->base_addr, info->size, ns16x50_io_handle);
> +
> + 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 ( !info )
> + return ERR_PTR(-EINVAL);
> +
> + if ( vuart_find_by_io_range(d, info->base_addr, info->size) )
> + {
> + ns16x50_err(info, "already registered\n");
> + return ERR_PTR(-EBUSY);
> + }
> +
> + if ( !is_hvm_domain(d) )
> + {
> + ns16x50_err(info, "not an HVM domain\n");
> + return ERR_PTR(-ENOSYS);
> + }
> +
> + vdev = xvzalloc(typeof(*vdev));
> + if ( !vdev )
> + {
> + ns16x50_err(info, "failed to allocate memory\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + spin_lock_init(&vdev->lock);
> + vdev->name = info->name;
> + vdev->owner = d;
> + vdev->info = info;
> +
> + rc = ns16x50_init(vdev);
> + if ( rc )
> + 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.51.0
>
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-08 8:29 ` Mykola Kvach
@ 2025-09-08 8:43 ` Jan Beulich
2025-09-08 9:45 ` dmukhin
1 sibling, 0 replies; 54+ messages in thread
From: Jan Beulich @ 2025-09-08 8:43 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, julien, michal.orzel,
roger.pau, sstabellini, dmukhin, dmukhin
On 08.09.2025 10:29, Mykola Kvach wrote:
> On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
>> --- /dev/null
>> +++ b/xen/common/emul/vuart/Kconfig
>> @@ -0,0 +1,6 @@
>> +config VUART_FRAMEWORK
>> + bool
>
> If VUART_FRAMEWORK has no dependencies, it can be enabled on any
> architecture. For example, I tried enabling it on arm64 and the build
> fails:
>
> ./include/xen/vuart.h:26:8: error: redefinition of ‘struct vuart’
>
> Should this config be restricted (e.g. "depends on X86") or the code
> adjusted to handle non-x86 architectures properly?
"depends on" isn't very useful when there's no prompt. An arch selecting
such an option will need to make sure things actually build when enabled.
Jan
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
2025-09-06 2:01 ` Stefano Stabellini
@ 2025-09-08 9:04 ` Mykola Kvach
2025-09-08 14:19 ` Oleksii Kurochko
2025-09-08 14:23 ` Oleksii Kurochko
0 siblings, 2 replies; 54+ messages in thread
From: Mykola Kvach @ 2025-09-08 9:04 UTC (permalink / raw)
To: Stefano Stabellini
Cc: oleksii.kurochko, xen-devel, andrew.cooper3, anthony.perard,
jbeulich, julien, michal.orzel, roger.pau, dmukhin, dmukhin
Hi Denis and Stefano
I’d like to acknowledge the significant effort that went into this patch
series -- it’s clear that a lot of work has been invested.
On Sat, Sep 6, 2025 at 5:02 AM Stefano Stabellini
<sstabellini@kernel.org> wrote:
>
> Oleksii and all,
>
> I would like to consider patches 1-12 of this patch series for 4.21,
> pending the few minor comments I made addressed.
Although I am neither a maintainer nor an official reviewer for this
project, I have looked over some of the first patches in the series. In my
opinion, the series is not yet ready for merging.
Even if my review is set aside, the changes are largely x86-specific and
produce the most impact on this architecture. I believe that before
merging, one of the x86 maintainers (or at least a trusted reviewer for
x86, if available) should carefully review these patches.
>
>
> On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
> > x86 port of Xen lacks vUART facility similar to Arm's vpl011 to support x86
> > guest OS bring up in the embedded setups.
> >
> > This patch series introduces initial in-hypervisor emulator for
> > NS8250/NS16x50-compatible UARTs under CONFIG_VUART_NS16X50.
> >
> > In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> > early guest firmware and OS bringup debugging, because it eliminates
> > dependency on the external emulator (qemu) being operational by the time
> > domains are created.
> >
> > The emulator also allows to forward the physical console input to the x86
> > domain which is useful when a system has only one physical UART for early
> > debugging and this UART is owned by Xen.
> >
> > By default, CONFIG_VUART_NS16X50 enables emulation of NS16550 at I/O port
> > 0x2f8, IRQ#3 in guest OS (legacy COM2). Legacy COM resources cannot be
> > selected at built-time or via per-domain xl configuration in this initial
> > submission.
> >
> > CONFIG_VUART_NS16X50_DEBUG enables some extra debugging facilities useful
> > for NS16550 emulator development/debugging (disabled by default).
> >
> > The NS16550 emulator is disabled in default x86 configuration and goes under
> > CONFIG_EXPERT in Kconfig.
> >
> > Limitations
> > ===========
> > - Only x86;
> > - Only legacy COM2 resources, custom I/O ports/IRQs are not supported;
> > - 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 toolstack integration;
> > - 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 MMIO-based UART emulation.
> >
> > Series
> > ======
> >
> > Patch 1 introduces the new vUART framework, that is the code originally
> > posted here:
> > https://lore.kernel.org/xen-devel/20250624035443.344099-16-dmukhin@ford.com/
> > Required for emulator.
> >
> > Patch 2 adds missing NS16550 definitions, required for emulator.
> >
> > Patch 3 introduces the basic emulator skeleton - state machine
> > initialization stubs, I/O port handler stub, logging, etc.
> >
> > Patches 4-11 incrementally populate the minimal NS16550 register emulation.
> >
> > Patch 12 hooks vUART state debugging (disabled by default).
> >
> > Pathes 13-15 introduce necessary changes to enable NS16550 on dom0 (and PVH).
> >
> > Link to CI: https://gitlab.com/xen-project/people/dmukhin/xen/-/pipelines/2024756493
> > Link to branch: https://gitlab.com/xen-project/people/dmukhin/xen/-/tree/vuart-ns8250-v6?ref_type=heads
> >
> > Testing
> > =======
> >
> > ```shell
> > echo CONFIG_EXPERT=y >> .config
> > echo CONFIG_VUART_NS16X50=y >> .config
> > make olddefconfig
> > ```
> > COM2 (0x2f8) resources are used by default.
> >
> > To test w/ virtual COM2, the guest kernel parameters should contain
> > something like the following:
> > earlycon=uart,io,0x2f8,115200n8 console=uart,io,0x2f8,115200n8
> >
> > HVM
> > ---
> > Tested only boot of HVM linux guest with OVMF as the virtual firmware.
> > SeaBIOS as a virtual firmware is not tested.
> >
> > PVH (dom0)
> > ----------
> > Xen is able to forward physical console input to the domain with virtual
> > NS16550. To switch the console focus press Ctrl+aaa.
> > Console switch is limited on x86 to dom0 and Xen (fixes pending).
> >
> > Changes since v5:
> > - Split THR/RBR into two separate patches.
> > - Addressed feedback from v5.
> > - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-1-dmukhin@ford.com/
> >
> > Changes since v4:
> > - Split the series to make it simpler to review.
> > - Addressed feedback from v4.
> > - Dropped xl changes, which I will submit separately.
> > - Link to v4: https://lore.kernel.org/xen-devel/20250731192130.3948419-1-dmukhin@ford.com/
> >
> > Changes since v3:
> > - Reduced the blast radius of the series, thanks to reviews, individual
> > aspects (like console focus) touched in v3 moved to separate threads.
> > - Kept the UART emulator framework since I need to redo some of emulator code
> > and there's more-or-less agreement on it (where to place, naming, scope).
> > - Applied the feedback from
> > https://lore.kernel.org/xen-devel/20250624035443.344099-1-dmukhin@ford.com/
> > - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-0-c5d36b31d66c@ford.com/
> >
> > Changes since v2:
> > - renamed emulator s/NS8250/NS16550/g
> > - reduced the patch series after addressing v2 feedback
> > - introduced driver framework for UART emulators
> > - unified guest OS printouts across all available UART emulators
> > - Link to v2: https://lore.kernel.org/xen-devel/20241205-vuart-ns8250-v1-0-e9aa923127eb@ford.com/
> >
> > Changes since v1:
> > - dropped kmalloc/kfree aliases
> > - fixed ECLAIR jobs (thanks Andrew Cooper)
> > - addressed console forwarding on arm32 and arm64 (thanks to Luca Fancellu)
> > - moved NS8250 debugging stubs into its own patch
> > - added fix for https://gitlab.com/xen-project/xen/-/issues/184
> > - Link to v1: https://lore.kernel.org/r/20241126-vuart-ns8250-v1-v1-0-87b9a8375b7a@ford.com
> >
> > Denis Mukhin (15):
> > emul/vuart: introduce framework for UART emulators
> > xen/8250-uart: update definitions
> > emul/ns16x50: implement emulator stub
> > emul/ns16x50: implement DLL/DLM registers
> > emul/ns16x50: implement SCR register
> > emul/ns16x50: implement IER/IIR registers
> > emul/ns16x50: implement LCR/LSR registers
> > emul/ns16x50: implement MCR/MSR registers
> > emul/ns16x50: implement RBR register
> > emul/ns16x50: implement THR register
> > emul/ns16x50: implement FCR register (write-only)
> > emul/ns16550: implement dump_state() hook
> > x86/domain: enable per-domain I/O port bitmaps
> > xen/domain: allocate d->irq_caps before arch-specific initialization
> > emul/ns16x50: implement IRQ emulation via vIOAPIC
> >
> > xen/arch/arm/xen.lds.S | 1 +
> > xen/arch/ppc/xen.lds.S | 1 +
> > xen/arch/riscv/xen.lds.S | 1 +
> > xen/arch/x86/Makefile | 1 +
> > xen/arch/x86/dom0_build.c | 112 +--
> > xen/arch/x86/hvm/dom0_build.c | 7 +
> > xen/arch/x86/hvm/hvm.c | 56 +-
> > xen/arch/x86/hvm/nestedhvm.c | 8 +-
> > xen/arch/x86/hvm/quirks.c | 3 -
> > xen/arch/x86/hvm/svm/nestedsvm.c | 2 +-
> > xen/arch/x86/hvm/vioapic.c | 10 +
> > xen/arch/x86/hvm/vmx/vvmx.c | 4 +-
> > xen/arch/x86/include/asm/hvm/nestedhvm.h | 3 +-
> > xen/arch/x86/include/asm/hvm/support.h | 2 -
> > xen/arch/x86/include/asm/iocap.h | 2 +
> > xen/arch/x86/include/asm/irq.h | 8 +
> > xen/arch/x86/ioport.c | 163 ++++
> > xen/arch/x86/irq.c | 8 +
> > xen/arch/x86/pv/dom0_build.c | 7 +
> > xen/arch/x86/xen.lds.S | 1 +
> > xen/common/Kconfig | 2 +
> > xen/common/Makefile | 1 +
> > xen/common/domain.c | 8 +-
> > xen/common/emul/Kconfig | 6 +
> > xen/common/emul/Makefile | 1 +
> > xen/common/emul/vuart/Kconfig | 25 +
> > xen/common/emul/vuart/Makefile | 2 +
> > xen/common/emul/vuart/ns16x50.c | 984 +++++++++++++++++++++++
> > xen/common/emul/vuart/vuart.c | 157 ++++
> > xen/common/keyhandler.c | 3 +
> > xen/drivers/char/console.c | 6 +-
> > xen/drivers/char/ns16550.c | 16 +-
> > xen/drivers/passthrough/x86/hvm.c | 11 +-
> > xen/include/xen/8250-uart.h | 50 +-
> > xen/include/xen/sched.h | 4 +
> > xen/include/xen/serial.h | 3 +
> > xen/include/xen/vuart.h | 116 +++
> > xen/include/xen/xen.lds.h | 10 +
> > 38 files changed, 1634 insertions(+), 171 deletions(-)
> > create mode 100644 xen/arch/x86/ioport.c
> > create mode 100644 xen/common/emul/Kconfig
> > create mode 100644 xen/common/emul/Makefile
> > create mode 100644 xen/common/emul/vuart/Kconfig
> > create mode 100644 xen/common/emul/vuart/Makefile
> > create mode 100644 xen/common/emul/vuart/ns16x50.c
> > create mode 100644 xen/common/emul/vuart/vuart.c
> > create mode 100644 xen/include/xen/vuart.h
> >
> > --
> > 2.51.0
> >
>
Best regards,
Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators
2025-09-08 8:29 ` Mykola Kvach
2025-09-08 8:43 ` Jan Beulich
@ 2025-09-08 9:45 ` dmukhin
1 sibling, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 9:45 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Mon, Sep 08, 2025 at 11:29:03AM +0300, Mykola Kvach wrote:
> On Sat, Sep 6, 2025 at 2:27 AM <dmukhin@xen.org> wrote:
[..]
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -0,0 +1,6 @@
> > +config VUART_FRAMEWORK
> > + bool
>
> If VUART_FRAMEWORK has no dependencies, it can be enabled on any
> architecture. For example, I tried enabling it on arm64 and the build
> fails:
>
> ./include/xen/vuart.h:26:8: error: redefinition of ‘struct vuart’
>
> Should this config be restricted (e.g. "depends on X86") or the code
> adjusted to handle non-x86 architectures properly?
Yes, missed that; the code is for x86 only for now.
`struct vuart` on Arm corresponds to simple MMIO-based UART emulator for
hwdom's earlyprintk.
Arm part is pending:
https://lore.kernel.org/xen-devel/20250624035443.344099-1-dmukhin@ford.com/
Most of the feedback resolved locally, but I need to wait for the other series to land
[..]
> > +static const struct vuart_emulator *
> > +vuart_match_by_compatible(const struct domain *d, const char *compat)
> > +{
> > + const struct vuart_emulator *emulator;
> > +
> > + for_each_emulator(emulator)
> > + if ( emulator->compatible &&
> > + !strncmp(compat, emulator->compatible,
> > + strlen(emulator->compatible)) )
> > + return emulator;
> > +
> > + return NULL;
> > +}
> > +
> > +const static struct vuart *
> > +vuart_find_by_console_permission(const struct domain *d)
>
> During the first review of this patch I thought you planned to add a
> searching procedure into this and the next function in one of the later
> commits. However, looking at the series now, it seems these functions
> remain unchanged and don’t actually perform any searching.
>
> Do you think the current names are accurate, or would it make sense to
> rename them to better reflect their purpose?
Arm has two vUART emulators enabled by default, so there will be at least two
vUARTs and some search in vuart.c.
I scoped x86 portion of the change into the current series, Arm is to follow,
since I have pending series to plumb vpl011 (i.e. SBSA) and hwdom vuart (i.e.
early dtuart) into that new framework. There will be some adjustments in
vuart.c
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 03/15] emul/ns16x50: implement emulator stub
2025-09-08 8:37 ` Mykola Kvach
@ 2025-09-08 9:59 ` dmukhin
0 siblings, 0 replies; 54+ messages in thread
From: dmukhin @ 2025-09-08 9:59 UTC (permalink / raw)
To: Mykola Kvach
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Mon, Sep 08, 2025 at 11:37:03AM +0300, Mykola Kvach wrote:
> > --- a/xen/common/emul/vuart/Kconfig
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -3,4 +3,23 @@ config VUART_FRAMEWORK
> >
> > menu "UART Emulation"
> >
> > +config VUART_NS16X50
> > + bool "NS16550-compatible UART Emulator" if EXPERT
> > + depends on X86 && HVM
>
> Currently VUART_NS16X50 depends on X86, so the code is only
> usable on x86. Are there any plans to support this vUART on non-x86
> architectures (e.g. ARM, where some UARTs are also ns16550-compatible)?
The plan is to add MMIO-based implementation for x86 first.
>
> If not, wouldn’t it be more appropriate to move the ns16x50-specific
> implementation into x86-specific directories instead of common?
There was discussion on that and the agreement is to move all vUARTs into
common/emul/vuart
and start using common/emul as a placeholder for common emulation code,
including vPCI.
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
2025-09-08 9:04 ` Mykola Kvach
@ 2025-09-08 14:19 ` Oleksii Kurochko
2025-09-08 14:23 ` Oleksii Kurochko
1 sibling, 0 replies; 54+ messages in thread
From: Oleksii Kurochko @ 2025-09-08 14:19 UTC (permalink / raw)
To: Mykola Kvach, Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin, dmukhin
On 9/8/25 11:04 AM, Mykola Kvach wrote:
> Hi Denis and Stefano
>
> I’d like to acknowledge the significant effort that went into this patch
> series -- it’s clear that a lot of work has been invested.
>
> On Sat, Sep 6, 2025 at 5:02 AM Stefano Stabellini
> <sstabellini@kernel.org> wrote:
>> Oleksii and all,
>>
>> I would like to consider patches 1-12 of this patch series for 4.21,
>> pending the few minor comments I made addressed.
> Although I am neither a maintainer nor an official reviewer for this
> project, I have looked over some of the first patches in the series. In my
> opinion, the series is not yet ready for merging.
>
> Even if my review is set aside, the changes are largely x86-specific and
> produce the most impact on this architecture. I believe that before
> merging, one of the x86 maintainers (or at least a trusted reviewer for
> x86, if available) should carefully review these patches.
>
>>
>> On Fri, 5 Sep 2025, dmukhin@xen.org wrote:
>>> x86 port of Xen lacks vUART facility similar to Arm's vpl011 to support x86
>>> guest OS bring up in the embedded setups.
>>>
>>> This patch series introduces initial in-hypervisor emulator for
>>> NS8250/NS16x50-compatible UARTs under CONFIG_VUART_NS16X50.
>>>
>>> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
>>> early guest firmware and OS bringup debugging, because it eliminates
>>> dependency on the external emulator (qemu) being operational by the time
>>> domains are created.
>>>
>>> The emulator also allows to forward the physical console input to the x86
>>> domain which is useful when a system has only one physical UART for early
>>> debugging and this UART is owned by Xen.
>>>
>>> By default, CONFIG_VUART_NS16X50 enables emulation of NS16550 at I/O port
>>> 0x2f8, IRQ#3 in guest OS (legacy COM2). Legacy COM resources cannot be
>>> selected at built-time or via per-domain xl configuration in this initial
>>> submission.
>>>
>>> CONFIG_VUART_NS16X50_DEBUG enables some extra debugging facilities useful
>>> for NS16550 emulator development/debugging (disabled by default).
>>>
>>> The NS16550 emulator is disabled in default x86 configuration and goes under
>>> CONFIG_EXPERT in Kconfig.
>>>
>>> Limitations
>>> ===========
>>> - Only x86;
>>> - Only legacy COM2 resources, custom I/O ports/IRQs are not supported;
>>> - 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 toolstack integration;
>>> - 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 MMIO-based UART emulation.
>>>
>>> Series
>>> ======
>>>
>>> Patch 1 introduces the new vUART framework, that is the code originally
>>> posted here:
>>> https://lore.kernel.org/xen-devel/20250624035443.344099-16-dmukhin@ford.com/
>>> Required for emulator.
>>>
>>> Patch 2 adds missing NS16550 definitions, required for emulator.
>>>
>>> Patch 3 introduces the basic emulator skeleton - state machine
>>> initialization stubs, I/O port handler stub, logging, etc.
>>>
>>> Patches 4-11 incrementally populate the minimal NS16550 register emulation.
>>>
>>> Patch 12 hooks vUART state debugging (disabled by default).
>>>
>>> Pathes 13-15 introduce necessary changes to enable NS16550 on dom0 (and PVH).
>>>
>>> Link to CI: https://gitlab.com/xen-project/people/dmukhin/xen/-/pipelines/2024756493
>>> Link to branch: https://gitlab.com/xen-project/people/dmukhin/xen/-/tree/vuart-ns8250-v6?ref_type=heads
>>>
>>> Testing
>>> =======
>>>
>>> ```shell
>>> echo CONFIG_EXPERT=y >> .config
>>> echo CONFIG_VUART_NS16X50=y >> .config
>>> make olddefconfig
>>> ```
>>> COM2 (0x2f8) resources are used by default.
>>>
>>> To test w/ virtual COM2, the guest kernel parameters should contain
>>> something like the following:
>>> earlycon=uart,io,0x2f8,115200n8 console=uart,io,0x2f8,115200n8
>>>
>>> HVM
>>> ---
>>> Tested only boot of HVM linux guest with OVMF as the virtual firmware.
>>> SeaBIOS as a virtual firmware is not tested.
>>>
>>> PVH (dom0)
>>> ----------
>>> Xen is able to forward physical console input to the domain with virtual
>>> NS16550. To switch the console focus press Ctrl+aaa.
>>> Console switch is limited on x86 to dom0 and Xen (fixes pending).
>>>
>>> Changes since v5:
>>> - Split THR/RBR into two separate patches.
>>> - Addressed feedback from v5.
>>> - Link to v5: https://lore.kernel.org/xen-devel/20250828235409.2835815-1-dmukhin@ford.com/
>>>
>>> Changes since v4:
>>> - Split the series to make it simpler to review.
>>> - Addressed feedback from v4.
>>> - Dropped xl changes, which I will submit separately.
>>> - Link to v4: https://lore.kernel.org/xen-devel/20250731192130.3948419-1-dmukhin@ford.com/
>>>
>>> Changes since v3:
>>> - Reduced the blast radius of the series, thanks to reviews, individual
>>> aspects (like console focus) touched in v3 moved to separate threads.
>>> - Kept the UART emulator framework since I need to redo some of emulator code
>>> and there's more-or-less agreement on it (where to place, naming, scope).
>>> - Applied the feedback from
>>> https://lore.kernel.org/xen-devel/20250624035443.344099-1-dmukhin@ford.com/
>>> - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-0-c5d36b31d66c@ford.com/
>>>
>>> Changes since v2:
>>> - renamed emulator s/NS8250/NS16550/g
>>> - reduced the patch series after addressing v2 feedback
>>> - introduced driver framework for UART emulators
>>> - unified guest OS printouts across all available UART emulators
>>> - Link to v2: https://lore.kernel.org/xen-devel/20241205-vuart-ns8250-v1-0-e9aa923127eb@ford.com/
>>>
>>> Changes since v1:
>>> - dropped kmalloc/kfree aliases
>>> - fixed ECLAIR jobs (thanks Andrew Cooper)
>>> - addressed console forwarding on arm32 and arm64 (thanks to Luca Fancellu)
>>> - moved NS8250 debugging stubs into its own patch
>>> - added fix for https://gitlab.com/xen-project/xen/-/issues/184
>>> - Link to v1: https://lore.kernel.org/r/20241126-vuart-ns8250-v1-v1-0-87b9a8375b7a@ford.com
>>>
>>> Denis Mukhin (15):
>>> emul/vuart: introduce framework for UART emulators
>>> xen/8250-uart: update definitions
>>> emul/ns16x50: implement emulator stub
>>> emul/ns16x50: implement DLL/DLM registers
>>> emul/ns16x50: implement SCR register
>>> emul/ns16x50: implement IER/IIR registers
>>> emul/ns16x50: implement LCR/LSR registers
>>> emul/ns16x50: implement MCR/MSR registers
>>> emul/ns16x50: implement RBR register
>>> emul/ns16x50: implement THR register
>>> emul/ns16x50: implement FCR register (write-only)
>>> emul/ns16550: implement dump_state() hook
>>> x86/domain: enable per-domain I/O port bitmaps
>>> xen/domain: allocate d->irq_caps before arch-specific initialization
>>> emul/ns16x50: implement IRQ emulation via vIOAPIC
>>>
>>> xen/arch/arm/xen.lds.S | 1 +
>>> xen/arch/ppc/xen.lds.S | 1 +
>>> xen/arch/riscv/xen.lds.S | 1 +
>>> xen/arch/x86/Makefile | 1 +
>>> xen/arch/x86/dom0_build.c | 112 +--
>>> xen/arch/x86/hvm/dom0_build.c | 7 +
>>> xen/arch/x86/hvm/hvm.c | 56 +-
>>> xen/arch/x86/hvm/nestedhvm.c | 8 +-
>>> xen/arch/x86/hvm/quirks.c | 3 -
>>> xen/arch/x86/hvm/svm/nestedsvm.c | 2 +-
>>> xen/arch/x86/hvm/vioapic.c | 10 +
>>> xen/arch/x86/hvm/vmx/vvmx.c | 4 +-
>>> xen/arch/x86/include/asm/hvm/nestedhvm.h | 3 +-
>>> xen/arch/x86/include/asm/hvm/support.h | 2 -
>>> xen/arch/x86/include/asm/iocap.h | 2 +
>>> xen/arch/x86/include/asm/irq.h | 8 +
>>> xen/arch/x86/ioport.c | 163 ++++
>>> xen/arch/x86/irq.c | 8 +
>>> xen/arch/x86/pv/dom0_build.c | 7 +
>>> xen/arch/x86/xen.lds.S | 1 +
>>> xen/common/Kconfig | 2 +
>>> xen/common/Makefile | 1 +
>>> xen/common/domain.c | 8 +-
>>> xen/common/emul/Kconfig | 6 +
>>> xen/common/emul/Makefile | 1 +
>>> xen/common/emul/vuart/Kconfig | 25 +
>>> xen/common/emul/vuart/Makefile | 2 +
>>> xen/common/emul/vuart/ns16x50.c | 984 +++++++++++++++++++++++
>>> xen/common/emul/vuart/vuart.c | 157 ++++
>>> xen/common/keyhandler.c | 3 +
>>> xen/drivers/char/console.c | 6 +-
>>> xen/drivers/char/ns16550.c | 16 +-
>>> xen/drivers/passthrough/x86/hvm.c | 11 +-
>>> xen/include/xen/8250-uart.h | 50 +-
>>> xen/include/xen/sched.h | 4 +
>>> xen/include/xen/serial.h | 3 +
>>> xen/include/xen/vuart.h | 116 +++
>>> xen/include/xen/xen.lds.h | 10 +
>>> 38 files changed, 1634 insertions(+), 171 deletions(-)
>>> create mode 100644 xen/arch/x86/ioport.c
>>> create mode 100644 xen/common/emul/Kconfig
>>> create mode 100644 xen/common/emul/Makefile
>>> create mode 100644 xen/common/emul/vuart/Kconfig
>>> create mode 100644 xen/common/emul/vuart/Makefile
>>> create mode 100644 xen/common/emul/vuart/ns16x50.c
>>> create mode 100644 xen/common/emul/vuart/vuart.c
>>> create mode 100644 xen/include/xen/vuart.h
>>>
>>> --
>>> 2.51.0
>>>
> Best regards,
> Mykola
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
2025-09-08 9:04 ` Mykola Kvach
2025-09-08 14:19 ` Oleksii Kurochko
@ 2025-09-08 14:23 ` Oleksii Kurochko
2025-09-08 23:51 ` Stefano Stabellini
1 sibling, 1 reply; 54+ messages in thread
From: Oleksii Kurochko @ 2025-09-08 14:23 UTC (permalink / raw)
To: Mykola Kvach, Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin, dmukhin
[-- Attachment #1: Type: text/plain, Size: 10201 bytes --]
Hello Everyone,
On 9/8/25 11:04 AM, Mykola Kvach wrote:
> Hi Denis and Stefano
>
> I’d like to acknowledge the significant effort that went into this patch
> series -- it’s clear that a lot of work has been invested.
>
> On Sat, Sep 6, 2025 at 5:02 AM Stefano Stabellini
> <sstabellini@kernel.org> wrote:
>> Oleksii and all,
>>
>> I would like to consider patches 1-12 of this patch series for 4.21,
>> pending the few minor comments I made addressed.
> Although I am neither a maintainer nor an official reviewer for this
> project, I have looked over some of the first patches in the series. In my
> opinion, the series is not yet ready for merging.
>
> Even if my review is set aside, the changes are largely x86-specific and
> produce the most impact on this architecture. I believe that before
> merging, one of the x86 maintainers (or at least a trusted reviewer for
> x86, if available) should carefully review these patches.
I agree with this point. Considering that this part is being moved to
common code, it would be helpful to get some input from the x86 maintainers.
Also, since the entire patch series is not yet ready, I think it makes
sense at this stage of development to either have the whole series reviewed
or postpone it to 4.22. (The last one is preferred at the current stage of
development)
~ Oleksii
>>
>> On Fri, 5 Sep 2025,dmukhin@xen.org wrote:
>>> x86 port of Xen lacks vUART facility similar to Arm's vpl011 to support x86
>>> guest OS bring up in the embedded setups.
>>>
>>> This patch series introduces initial in-hypervisor emulator for
>>> NS8250/NS16x50-compatible UARTs under CONFIG_VUART_NS16X50.
>>>
>>> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
>>> early guest firmware and OS bringup debugging, because it eliminates
>>> dependency on the external emulator (qemu) being operational by the time
>>> domains are created.
>>>
>>> The emulator also allows to forward the physical console input to the x86
>>> domain which is useful when a system has only one physical UART for early
>>> debugging and this UART is owned by Xen.
>>>
>>> By default, CONFIG_VUART_NS16X50 enables emulation of NS16550 at I/O port
>>> 0x2f8, IRQ#3 in guest OS (legacy COM2). Legacy COM resources cannot be
>>> selected at built-time or via per-domain xl configuration in this initial
>>> submission.
>>>
>>> CONFIG_VUART_NS16X50_DEBUG enables some extra debugging facilities useful
>>> for NS16550 emulator development/debugging (disabled by default).
>>>
>>> The NS16550 emulator is disabled in default x86 configuration and goes under
>>> CONFIG_EXPERT in Kconfig.
>>>
>>> Limitations
>>> ===========
>>> - Only x86;
>>> - Only legacy COM2 resources, custom I/O ports/IRQs are not supported;
>>> - 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 toolstack integration;
>>> - 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 MMIO-based UART emulation.
>>>
>>> Series
>>> ======
>>>
>>> Patch 1 introduces the new vUART framework, that is the code originally
>>> posted here:
>>> https://lore.kernel.org/xen-devel/20250624035443.344099-16-dmukhin@ford.com/
>>> Required for emulator.
>>>
>>> Patch 2 adds missing NS16550 definitions, required for emulator.
>>>
>>> Patch 3 introduces the basic emulator skeleton - state machine
>>> initialization stubs, I/O port handler stub, logging, etc.
>>>
>>> Patches 4-11 incrementally populate the minimal NS16550 register emulation.
>>>
>>> Patch 12 hooks vUART state debugging (disabled by default).
>>>
>>> Pathes 13-15 introduce necessary changes to enable NS16550 on dom0 (and PVH).
>>>
>>> Link to CI:https://gitlab.com/xen-project/people/dmukhin/xen/-/pipelines/2024756493
>>> Link to branch:https://gitlab.com/xen-project/people/dmukhin/xen/-/tree/vuart-ns8250-v6?ref_type=heads
>>>
>>> Testing
>>> =======
>>>
>>> ```shell
>>> echo CONFIG_EXPERT=y >> .config
>>> echo CONFIG_VUART_NS16X50=y >> .config
>>> make olddefconfig
>>> ```
>>> COM2 (0x2f8) resources are used by default.
>>>
>>> To test w/ virtual COM2, the guest kernel parameters should contain
>>> something like the following:
>>> earlycon=uart,io,0x2f8,115200n8 console=uart,io,0x2f8,115200n8
>>>
>>> HVM
>>> ---
>>> Tested only boot of HVM linux guest with OVMF as the virtual firmware.
>>> SeaBIOS as a virtual firmware is not tested.
>>>
>>> PVH (dom0)
>>> ----------
>>> Xen is able to forward physical console input to the domain with virtual
>>> NS16550. To switch the console focus press Ctrl+aaa.
>>> Console switch is limited on x86 to dom0 and Xen (fixes pending).
>>>
>>> Changes since v5:
>>> - Split THR/RBR into two separate patches.
>>> - Addressed feedback from v5.
>>> - Link to v5:https://lore.kernel.org/xen-devel/20250828235409.2835815-1-dmukhin@ford.com/
>>>
>>> Changes since v4:
>>> - Split the series to make it simpler to review.
>>> - Addressed feedback from v4.
>>> - Dropped xl changes, which I will submit separately.
>>> - Link to v4:https://lore.kernel.org/xen-devel/20250731192130.3948419-1-dmukhin@ford.com/
>>>
>>> Changes since v3:
>>> - Reduced the blast radius of the series, thanks to reviews, individual
>>> aspects (like console focus) touched in v3 moved to separate threads.
>>> - Kept the UART emulator framework since I need to redo some of emulator code
>>> and there's more-or-less agreement on it (where to place, naming, scope).
>>> - Applied the feedback from
>>> https://lore.kernel.org/xen-devel/20250624035443.344099-1-dmukhin@ford.com/
>>> - Link to v3:https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-0-c5d36b31d66c@ford.com/
>>>
>>> Changes since v2:
>>> - renamed emulator s/NS8250/NS16550/g
>>> - reduced the patch series after addressing v2 feedback
>>> - introduced driver framework for UART emulators
>>> - unified guest OS printouts across all available UART emulators
>>> - Link to v2:https://lore.kernel.org/xen-devel/20241205-vuart-ns8250-v1-0-e9aa923127eb@ford.com/
>>>
>>> Changes since v1:
>>> - dropped kmalloc/kfree aliases
>>> - fixed ECLAIR jobs (thanks Andrew Cooper)
>>> - addressed console forwarding on arm32 and arm64 (thanks to Luca Fancellu)
>>> - moved NS8250 debugging stubs into its own patch
>>> - added fix forhttps://gitlab.com/xen-project/xen/-/issues/184
>>> - Link to v1:https://lore.kernel.org/r/20241126-vuart-ns8250-v1-v1-0-87b9a8375b7a@ford.com
>>>
>>> Denis Mukhin (15):
>>> emul/vuart: introduce framework for UART emulators
>>> xen/8250-uart: update definitions
>>> emul/ns16x50: implement emulator stub
>>> emul/ns16x50: implement DLL/DLM registers
>>> emul/ns16x50: implement SCR register
>>> emul/ns16x50: implement IER/IIR registers
>>> emul/ns16x50: implement LCR/LSR registers
>>> emul/ns16x50: implement MCR/MSR registers
>>> emul/ns16x50: implement RBR register
>>> emul/ns16x50: implement THR register
>>> emul/ns16x50: implement FCR register (write-only)
>>> emul/ns16550: implement dump_state() hook
>>> x86/domain: enable per-domain I/O port bitmaps
>>> xen/domain: allocate d->irq_caps before arch-specific initialization
>>> emul/ns16x50: implement IRQ emulation via vIOAPIC
>>>
>>> xen/arch/arm/xen.lds.S | 1 +
>>> xen/arch/ppc/xen.lds.S | 1 +
>>> xen/arch/riscv/xen.lds.S | 1 +
>>> xen/arch/x86/Makefile | 1 +
>>> xen/arch/x86/dom0_build.c | 112 +--
>>> xen/arch/x86/hvm/dom0_build.c | 7 +
>>> xen/arch/x86/hvm/hvm.c | 56 +-
>>> xen/arch/x86/hvm/nestedhvm.c | 8 +-
>>> xen/arch/x86/hvm/quirks.c | 3 -
>>> xen/arch/x86/hvm/svm/nestedsvm.c | 2 +-
>>> xen/arch/x86/hvm/vioapic.c | 10 +
>>> xen/arch/x86/hvm/vmx/vvmx.c | 4 +-
>>> xen/arch/x86/include/asm/hvm/nestedhvm.h | 3 +-
>>> xen/arch/x86/include/asm/hvm/support.h | 2 -
>>> xen/arch/x86/include/asm/iocap.h | 2 +
>>> xen/arch/x86/include/asm/irq.h | 8 +
>>> xen/arch/x86/ioport.c | 163 ++++
>>> xen/arch/x86/irq.c | 8 +
>>> xen/arch/x86/pv/dom0_build.c | 7 +
>>> xen/arch/x86/xen.lds.S | 1 +
>>> xen/common/Kconfig | 2 +
>>> xen/common/Makefile | 1 +
>>> xen/common/domain.c | 8 +-
>>> xen/common/emul/Kconfig | 6 +
>>> xen/common/emul/Makefile | 1 +
>>> xen/common/emul/vuart/Kconfig | 25 +
>>> xen/common/emul/vuart/Makefile | 2 +
>>> xen/common/emul/vuart/ns16x50.c | 984 +++++++++++++++++++++++
>>> xen/common/emul/vuart/vuart.c | 157 ++++
>>> xen/common/keyhandler.c | 3 +
>>> xen/drivers/char/console.c | 6 +-
>>> xen/drivers/char/ns16550.c | 16 +-
>>> xen/drivers/passthrough/x86/hvm.c | 11 +-
>>> xen/include/xen/8250-uart.h | 50 +-
>>> xen/include/xen/sched.h | 4 +
>>> xen/include/xen/serial.h | 3 +
>>> xen/include/xen/vuart.h | 116 +++
>>> xen/include/xen/xen.lds.h | 10 +
>>> 38 files changed, 1634 insertions(+), 171 deletions(-)
>>> create mode 100644 xen/arch/x86/ioport.c
>>> create mode 100644 xen/common/emul/Kconfig
>>> create mode 100644 xen/common/emul/Makefile
>>> create mode 100644 xen/common/emul/vuart/Kconfig
>>> create mode 100644 xen/common/emul/vuart/Makefile
>>> create mode 100644 xen/common/emul/vuart/ns16x50.c
>>> create mode 100644 xen/common/emul/vuart/vuart.c
>>> create mode 100644 xen/include/xen/vuart.h
>>>
>>> --
>>> 2.51.0
>>>
> Best regards,
> Mykola
[-- Attachment #2: Type: text/html, Size: 11824 bytes --]
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator
2025-09-08 14:23 ` Oleksii Kurochko
@ 2025-09-08 23:51 ` Stefano Stabellini
0 siblings, 0 replies; 54+ messages in thread
From: Stefano Stabellini @ 2025-09-08 23:51 UTC (permalink / raw)
To: Oleksii Kurochko
Cc: Mykola Kvach, Stefano Stabellini, xen-devel, andrew.cooper3,
anthony.perard, jbeulich, julien, michal.orzel, roger.pau,
dmukhin, dmukhin
[-- Attachment #1: Type: text/plain, Size: 1537 bytes --]
On Mon, 8 Sep 2025, Oleksii Kurochko wrote:
> Hello Everyone,
>
> On 9/8/25 11:04 AM, Mykola Kvach wrote:
>
> Hi Denis and Stefano
>
> I’d like to acknowledge the significant effort that went into this patch
> series -- it’s clear that a lot of work has been invested.
>
> On Sat, Sep 6, 2025 at 5:02 AM Stefano Stabellini
> <sstabellini@kernel.org> wrote:
>
> Oleksii and all,
>
> I would like to consider patches 1-12 of this patch series for 4.21,
> pending the few minor comments I made addressed.
>
> Although I am neither a maintainer nor an official reviewer for this
> project, I have looked over some of the first patches in the series. In my
> opinion, the series is not yet ready for merging.
>
> Even if my review is set aside, the changes are largely x86-specific and
> produce the most impact on this architecture. I believe that before
> merging, one of the x86 maintainers (or at least a trusted reviewer for
> x86, if available) should carefully review these patches.
>
> I agree with this point. Considering that this part is being moved to
> common code, it would be helpful to get some input from the x86 maintainers.
>
> Also, since the entire patch series is not yet ready, I think it makes
> sense at this stage of development to either have the whole series reviewed
> or postpone it to 4.22. (The last one is preferred at the current stage of
> development)
Even with x86 review, it would be difficult to get the whole series
merged now. If it is all or nothing, then I suggest we wait for 4.22.
^ permalink raw reply [flat|nested] 54+ messages in thread
end of thread, other threads:[~2025-09-08 23:52 UTC | newest]
Thread overview: 54+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-05 23:26 [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
2025-09-05 23:27 ` [PATCH v6 01/15] emul/vuart: introduce framework for UART emulators dmukhin
2025-09-06 2:02 ` Stefano Stabellini
2025-09-06 9:43 ` Mykola Kvach
2025-09-08 7:16 ` dmukhin
2025-09-08 8:29 ` Mykola Kvach
2025-09-08 8:43 ` Jan Beulich
2025-09-08 9:45 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 02/15] xen/8250-uart: update definitions dmukhin
2025-09-06 2:02 ` Stefano Stabellini
2025-09-06 14:57 ` Mykola Kvach
2025-09-08 8:36 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 03/15] emul/ns16x50: implement emulator stub dmukhin
2025-09-06 0:34 ` dmukhin
2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 4:18 ` dmukhin
2025-09-06 20:37 ` Mykola Kvach
2025-09-08 8:32 ` dmukhin
2025-09-08 8:37 ` Mykola Kvach
2025-09-08 9:59 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 04/15] emul/ns16x50: implement DLL/DLM registers dmukhin
2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 21:12 ` Mykola Kvach
2025-09-08 6:41 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 05/15] emul/ns16x50: implement SCR register dmukhin
2025-09-06 2:03 ` Stefano Stabellini
2025-09-06 21:24 ` Mykola Kvach
2025-09-08 3:37 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 06/15] emul/ns16x50: implement IER/IIR registers dmukhin
2025-09-06 1:42 ` Stefano Stabellini
2025-09-08 2:37 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 07/15] emul/ns16x50: implement LCR/LSR registers dmukhin
2025-09-06 2:04 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 08/15] emul/ns16x50: implement MCR/MSR registers dmukhin
2025-09-06 1:42 ` Stefano Stabellini
2025-09-08 2:35 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 09/15] emul/ns16x50: implement RBR register dmukhin
2025-09-06 2:04 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 10/15] emul/ns16x50: implement THR register dmukhin
2025-09-06 1:59 ` Stefano Stabellini
2025-09-08 2:50 ` dmukhin
2025-09-08 3:15 ` dmukhin
2025-09-05 23:27 ` [PATCH v6 11/15] emul/ns16x50: implement FCR register (write-only) dmukhin
2025-09-06 2:04 ` Stefano Stabellini
2025-09-05 23:27 ` [PATCH v6 12/15] emul/ns16550: implement dump_state() hook dmukhin
2025-09-05 23:27 ` [PATCH v6 13/15] x86/domain: enable per-domain I/O port bitmaps dmukhin
2025-09-05 23:27 ` [PATCH v6 14/15] xen/domain: allocate d->irq_caps before arch-specific initialization dmukhin
2025-09-05 23:27 ` [PATCH v6 15/15] emul/ns16x50: implement IRQ emulation via vIOAPIC dmukhin
2025-09-06 0:33 ` [PATCH v6 00/15] x86: introduce NS16550-compatible UART emulator dmukhin
2025-09-06 2:01 ` Stefano Stabellini
2025-09-08 9:04 ` Mykola Kvach
2025-09-08 14:19 ` Oleksii Kurochko
2025-09-08 14:23 ` Oleksii Kurochko
2025-09-08 23:51 ` Stefano Stabellini
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.