* [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
@ 2025-07-31 19:21 ` dmkhn
2025-08-04 9:46 ` Jan Beulich
2025-07-31 19:21 ` [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators dmkhn
` (7 subsequent siblings)
8 siblings, 1 reply; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:21 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 common emulation_flags for configuring domain emulation features.
Print d->emulation_flags from 'q' keyhandler for better traceability while
debugging.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
---
Original patch: https://lore.kernel.org/xen-devel/20250624035443.344099-12-dmukhin@ford.com/
---
xen/arch/x86/domain.c | 2 +-
xen/arch/x86/domctl.c | 2 +-
xen/arch/x86/include/asm/domain.h | 25 +++++++++++--------------
xen/common/keyhandler.c | 1 +
xen/include/xen/sched.h | 2 ++
5 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 56c381618712..7fd4f7a831dc 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -863,7 +863,7 @@ int arch_domain_create(struct domain *d,
emflags);
return -EOPNOTSUPP;
}
- d->arch.emulation_flags = emflags;
+ d->emulation_flags = emflags;
#ifdef CONFIG_PV32
HYPERVISOR_COMPAT_VIRT_START(d) =
diff --git a/xen/arch/x86/domctl.c b/xen/arch/x86/domctl.c
index 3044f706de1c..37d848f68339 100644
--- a/xen/arch/x86/domctl.c
+++ b/xen/arch/x86/domctl.c
@@ -144,7 +144,7 @@ void arch_get_domain_info(const struct domain *d,
if ( paging_mode_hap(d) )
info->flags |= XEN_DOMINF_hap;
- info->arch_config.emulation_flags = d->arch.emulation_flags;
+ info->arch_config.emulation_flags = d->emulation_flags;
info->gpaddr_bits = hap_paddr_bits;
}
diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h
index 8c0dea12a526..eafd5cfc903d 100644
--- a/xen/arch/x86/include/asm/domain.h
+++ b/xen/arch/x86/include/asm/domain.h
@@ -455,9 +455,6 @@ struct arch_domain
/* Don't unconditionally inject #GP for unhandled MSRs. */
bool msr_relaxed;
-
- /* Emulated devices enabled bitmap. */
- uint32_t emulation_flags;
} __cacheline_aligned;
#ifdef CONFIG_HVM
@@ -494,17 +491,17 @@ struct arch_domain
X86_EMU_PIT | X86_EMU_USE_PIRQ | \
X86_EMU_VPCI)
-#define has_vlapic(d) (!!((d)->arch.emulation_flags & X86_EMU_LAPIC))
-#define has_vhpet(d) (!!((d)->arch.emulation_flags & X86_EMU_HPET))
-#define has_vpm(d) (!!((d)->arch.emulation_flags & X86_EMU_PM))
-#define has_vrtc(d) (!!((d)->arch.emulation_flags & X86_EMU_RTC))
-#define has_vioapic(d) (!!((d)->arch.emulation_flags & X86_EMU_IOAPIC))
-#define has_vpic(d) (!!((d)->arch.emulation_flags & X86_EMU_PIC))
-#define has_vvga(d) (!!((d)->arch.emulation_flags & X86_EMU_VGA))
-#define has_viommu(d) (!!((d)->arch.emulation_flags & X86_EMU_IOMMU))
-#define has_vpit(d) (!!((d)->arch.emulation_flags & X86_EMU_PIT))
-#define has_pirq(d) (!!((d)->arch.emulation_flags & X86_EMU_USE_PIRQ))
-#define has_vpci(d) (!!((d)->arch.emulation_flags & X86_EMU_VPCI))
+#define has_vlapic(d) (!!((d)->emulation_flags & X86_EMU_LAPIC))
+#define has_vhpet(d) (!!((d)->emulation_flags & X86_EMU_HPET))
+#define has_vpm(d) (!!((d)->emulation_flags & X86_EMU_PM))
+#define has_vrtc(d) (!!((d)->emulation_flags & X86_EMU_RTC))
+#define has_vioapic(d) (!!((d)->emulation_flags & X86_EMU_IOAPIC))
+#define has_vpic(d) (!!((d)->emulation_flags & X86_EMU_PIC))
+#define has_vvga(d) (!!((d)->emulation_flags & X86_EMU_VGA))
+#define has_viommu(d) (!!((d)->emulation_flags & X86_EMU_IOMMU))
+#define has_vpit(d) (!!((d)->emulation_flags & X86_EMU_PIT))
+#define has_pirq(d) (!!((d)->emulation_flags & X86_EMU_USE_PIRQ))
+#define has_vpci(d) (!!((d)->emulation_flags & X86_EMU_VPCI))
#define gdt_ldt_pt_idx(v) \
((v)->vcpu_id >> (PAGETABLE_ORDER - GDT_LDT_VCPU_SHIFT))
diff --git a/xen/common/keyhandler.c b/xen/common/keyhandler.c
index b0a2051408d5..eccd97c565c6 100644
--- a/xen/common/keyhandler.c
+++ b/xen/common/keyhandler.c
@@ -306,6 +306,7 @@ static void cf_check dump_domains(unsigned char key)
if ( test_bit(i, &d->watchdog_inuse_map) )
printk(" watchdog %d expires in %d seconds\n",
i, (u32)((d->watchdog_timer[i].expires - NOW()) >> 30));
+ printk(" emulation_flags %#x\n", d->emulation_flags);
arch_dump_domain_info(d);
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index fd5c9f933373..f423a0ef714c 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -652,6 +652,8 @@ struct domain
unsigned int *llc_colors;
#endif
+ uint32_t emulation_flags;
+
/* Console settings. */
struct {
/* Permission to take ownership of the physical console input. */
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-07-31 19:21 ` [PATCH v4 1/8] xen/domain: introduce common emulation flags dmkhn
@ 2025-08-04 9:46 ` Jan Beulich
2025-08-05 0:54 ` dmkhn
0 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 9:46 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:21, dmkhn@proton.me wrote:
> --- a/xen/include/xen/sched.h
> +++ b/xen/include/xen/sched.h
> @@ -652,6 +652,8 @@ struct domain
> unsigned int *llc_colors;
> #endif
>
> + uint32_t emulation_flags;
Just one further remark: The field probably never should have been of this
type; unsigned int will do, and imo will want switching to while the field
is being moved. (Before giving an x86 ack, I want to convince myself though
that this is moving us in the right direction.)
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-08-04 9:46 ` Jan Beulich
@ 2025-08-05 0:54 ` dmkhn
2025-08-06 13:56 ` Roger Pau Monné
2025-08-07 14:28 ` Oleksii Kurochko
0 siblings, 2 replies; 61+ messages in thread
From: dmkhn @ 2025-08-05 0:54 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, oleksii.kurochko, dmukhin, xen-devel
+ Cc: Oleskii
On Mon, Aug 04, 2025 at 11:46:36AM +0200, Jan Beulich wrote:
> On 31.07.2025 21:21, dmkhn@proton.me wrote:
> > --- a/xen/include/xen/sched.h
> > +++ b/xen/include/xen/sched.h
> > @@ -652,6 +652,8 @@ struct domain
> > unsigned int *llc_colors;
> > #endif
> >
> > + uint32_t emulation_flags;
>
> Just one further remark: The field probably never should have been of this
> type; unsigned int will do, and imo will want switching to while the field
> is being moved. (Before giving an x86 ack, I want to convince myself though
> that this is moving us in the right direction.)
Hi Jan,
I can definitely use different mechanism for virt ns16550: add a new field in
xen_arch_domainconfig. That will also simplify some of the emulation_flags
checks on x86 and will be more flexible wrt emulator configuration (e.g. I can
allow passing I/O ports ranges).
Hi Arm/RISC-V maintainers,
Do you foresee any need in bubbling up emulation_flags to the common `struct
domain`?
--
Denis
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-08-05 0:54 ` dmkhn
@ 2025-08-06 13:56 ` Roger Pau Monné
2025-08-07 17:54 ` dmkhn
2025-08-07 14:28 ` Oleksii Kurochko
1 sibling, 1 reply; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 13:56 UTC (permalink / raw)
To: dmkhn
Cc: Jan Beulich, andrew.cooper3, anthony.perard, julien, michal.orzel,
sstabellini, oleksii.kurochko, dmukhin, xen-devel
On Tue, Aug 05, 2025 at 12:54:31AM +0000, dmkhn@proton.me wrote:
> + Cc: Oleskii
>
> On Mon, Aug 04, 2025 at 11:46:36AM +0200, Jan Beulich wrote:
> > On 31.07.2025 21:21, dmkhn@proton.me wrote:
> > > --- a/xen/include/xen/sched.h
> > > +++ b/xen/include/xen/sched.h
> > > @@ -652,6 +652,8 @@ struct domain
> > > unsigned int *llc_colors;
> > > #endif
> > >
> > > + uint32_t emulation_flags;
> >
> > Just one further remark: The field probably never should have been of this
> > type; unsigned int will do, and imo will want switching to while the field
> > is being moved. (Before giving an x86 ack, I want to convince myself though
> > that this is moving us in the right direction.)
>
> Hi Jan,
>
> I can definitely use different mechanism for virt ns16550: add a new field in
> xen_arch_domainconfig. That will also simplify some of the emulation_flags
> checks on x86 and will be more flexible wrt emulator configuration (e.g. I can
> allow passing I/O ports ranges).
For the time being, I would leave emulation_flags in
xen_arch_domainconfig. The set of emulated devices is
architecture-specific, and pulling it to the generic struct is IMO not
specially helpful, as you then have the definition of the flags
decoupled from the field definition.
For the emulated UART, I don't think you need a new field in
xen_arch_domainconfig, just a new bit in emulation_flags? IOW:
#define _XEN_X86_EMU_VUART 11
#define XEN_X86_EMU_VUART (1U << _XEN_X86_EMU_VUART)
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-08-06 13:56 ` Roger Pau Monné
@ 2025-08-07 17:54 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 17:54 UTC (permalink / raw)
To: Roger Pau Monné
Cc: Jan Beulich, andrew.cooper3, anthony.perard, julien, michal.orzel,
sstabellini, oleksii.kurochko, dmukhin, xen-devel
On Wed, Aug 06, 2025 at 03:56:42PM +0200, Roger Pau Monné wrote:
> On Tue, Aug 05, 2025 at 12:54:31AM +0000, dmkhn@proton.me wrote:
> > + Cc: Oleskii
> >
> > On Mon, Aug 04, 2025 at 11:46:36AM +0200, Jan Beulich wrote:
> > > On 31.07.2025 21:21, dmkhn@proton.me wrote:
> > > > --- a/xen/include/xen/sched.h
> > > > +++ b/xen/include/xen/sched.h
> > > > @@ -652,6 +652,8 @@ struct domain
> > > > unsigned int *llc_colors;
> > > > #endif
> > > >
> > > > + uint32_t emulation_flags;
> > >
> > > Just one further remark: The field probably never should have been of this
> > > type; unsigned int will do, and imo will want switching to while the field
> > > is being moved. (Before giving an x86 ack, I want to convince myself though
> > > that this is moving us in the right direction.)
> >
> > Hi Jan,
> >
> > I can definitely use different mechanism for virt ns16550: add a new field in
> > xen_arch_domainconfig. That will also simplify some of the emulation_flags
> > checks on x86 and will be more flexible wrt emulator configuration (e.g. I can
> > allow passing I/O ports ranges).
>
> For the time being, I would leave emulation_flags in
> xen_arch_domainconfig.
Ack; agreed.
> The set of emulated devices is
> architecture-specific, and pulling it to the generic struct is IMO not
> specially helpful, as you then have the definition of the flags
> decoupled from the field definition.
NS16550 is not an arch-specific device though.
But yes, current implementation _is_ arch-specific.
>
> For the emulated UART, I don't think you need a new field in
> xen_arch_domainconfig, just a new bit in emulation_flags? IOW:
I think new field is needed, since there are four legacy PC UART resource
sets selectable by the user. I'm dropping COM port build-time selection
and reworking it to be xl-configurable. That will be more flexible and
there will be less in-hypervisor changes.
>
> #define _XEN_X86_EMU_VUART 11
> #define XEN_X86_EMU_VUART (1U << _XEN_X86_EMU_VUART)
>
> Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-08-05 0:54 ` dmkhn
2025-08-06 13:56 ` Roger Pau Monné
@ 2025-08-07 14:28 ` Oleksii Kurochko
2025-08-07 17:43 ` dmkhn
1 sibling, 1 reply; 61+ messages in thread
From: Oleksii Kurochko @ 2025-08-07 14:28 UTC (permalink / raw)
To: dmkhn, Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
[-- Attachment #1: Type: text/plain, Size: 1230 bytes --]
On 8/5/25 2:54 AM, dmkhn@proton.me wrote:
> + Cc: Oleskii
>
> On Mon, Aug 04, 2025 at 11:46:36AM +0200, Jan Beulich wrote:
>> On 31.07.2025 21:21,dmkhn@proton.me wrote:
>>> --- a/xen/include/xen/sched.h
>>> +++ b/xen/include/xen/sched.h
>>> @@ -652,6 +652,8 @@ struct domain
>>> unsigned int *llc_colors;
>>> #endif
>>>
>>> + uint32_t emulation_flags;
>> Just one further remark: The field probably never should have been of this
>> type; unsigned int will do, and imo will want switching to while the field
>> is being moved. (Before giving an x86 ack, I want to convince myself though
>> that this is moving us in the right direction.)
> Hi Jan,
>
> I can definitely use different mechanism for virt ns16550: add a new field in
> xen_arch_domainconfig. That will also simplify some of the emulation_flags
> checks on x86 and will be more flexible wrt emulator configuration (e.g. I can
> allow passing I/O ports ranges).
>
> Hi Arm/RISC-V maintainers,
>
> Do you foresee any need in bubbling up emulation_flags to the common `struct
> domain`?
At the moment, in RISC-V's downstream branches emulation_flags isn't used. And,
for now, I am not really sure that it's going to be used in the nearest future.
~ Oleksii
[-- Attachment #2: Type: text/html, Size: 1970 bytes --]
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 1/8] xen/domain: introduce common emulation flags
2025-08-07 14:28 ` Oleksii Kurochko
@ 2025-08-07 17:43 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 17:43 UTC (permalink / raw)
To: Oleksii Kurochko
Cc: Jan Beulich, andrew.cooper3, anthony.perard, julien, michal.orzel,
roger.pau, sstabellini, dmukhin, xen-devel
On Thu, Aug 07, 2025 at 04:28:20PM +0200, Oleksii Kurochko wrote:
[..]
> Hi Arm/RISC-V maintainers,
>
> Do you foresee any need in bubbling up emulation_flags to the common `struct
> domain`?
> </pre>
> </blockquote>
> <pre>At the moment, in RISC-V's downstream branches emulation_flags isn't used. And,
> for now, I am not really sure that it's going to be used in the nearest future.
Thank you, I'll drop that patch.
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
2025-07-31 19:21 ` [PATCH v4 1/8] xen/domain: introduce common emulation flags dmkhn
@ 2025-07-31 19:21 ` dmkhn
2025-08-01 0:08 ` Stefano Stabellini
` (2 more replies)
2025-07-31 19:21 ` [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization dmkhn
` (6 subsequent siblings)
8 siblings, 3 replies; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:21 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_HAS_VUART, 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.
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.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v3:
- new patch
- original patch from ML: https://lore.kernel.org/xen-devel/20250624035443.344099-16-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 | 112 +++++++++++++++++++++++++++++++++
xen/common/keyhandler.c | 3 +
xen/drivers/char/console.c | 4 ++
xen/include/xen/vuart.h | 84 +++++++++++++++++++++++++
xen/include/xen/xen.lds.h | 10 +++
15 files changed, 234 insertions(+)
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 9f30c3a13ed1..bdba7eaa4f65 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 8e9cac75b09e..43426df33092 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -136,6 +136,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 16936418a6e6..4e0bd524dc43 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -1,6 +1,8 @@
menu "Common Features"
+source "common/emul/Kconfig"
+
config COMPAT
bool
help
diff --git a/xen/common/Makefile b/xen/common/Makefile
index c316957fcb36..c0734480ee4b 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
obj-$(CONFIG_IOREQ_SERVER) += dm.o
obj-y += domain.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..670682102c13
--- /dev/null
+++ b/xen/common/emul/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HAS_VUART) += vuart/
diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
new file mode 100644
index 000000000000..02f7dd6dc1a1
--- /dev/null
+++ b/xen/common/emul/vuart/Kconfig
@@ -0,0 +1,6 @@
+config HAS_VUART
+ 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..c6400b001e85
--- /dev/null
+++ b/xen/common/emul/vuart/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HAS_VUART) += vuart.o
diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
new file mode 100644
index 000000000000..14a7f8bd8b79
--- /dev/null
+++ b/xen/common/emul/vuart/vuart.c
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UART emulator framework.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+#include <xen/errno.h>
+#include <xen/sched.h>
+#include <xen/vuart.h>
+
+#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
+
+#define for_each_vuart(vdev) \
+ for (unsigned __i = 0; \
+ __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
+ __i++)
+
+extern const struct vuart_ops *const __start_vuart_array[];
+extern const struct vuart_ops *const __start_vuart_end[];
+
+int vuart_add_node(struct domain *d, const void *node)
+{
+ const struct vuart_ops *vdev;
+ int rc;
+
+ for_each_vuart(vdev)
+ {
+ if ( !vdev->add_node )
+ continue;
+
+ rc = vdev->add_node(d, node);
+ if ( rc )
+ return rc;
+ }
+
+ return 0;
+}
+
+int vuart_init(struct domain *d, struct vuart_params *params)
+{
+ const struct vuart_ops *vdev;
+ int rc;
+
+ if ( !domain_has_vuart(d) )
+ return 0;
+
+ for_each_vuart(vdev)
+ {
+ rc = vdev->init(d, params);
+ if ( rc )
+ return rc;
+ }
+
+ d->console.input_allowed = true;
+
+ return 0;
+}
+
+/*
+ * Release any resources taken by UART emulators.
+ *
+ * NB: no flags are cleared, since currently exit() is called only during
+ * domain destroy.
+ */
+void vuart_deinit(struct domain *d)
+{
+ const struct vuart_ops *vdev;
+
+ for_each_vuart(vdev)
+ vdev->deinit(d);
+}
+
+void vuart_dump_state(const struct domain *d)
+{
+ const struct vuart_ops *vdev;
+
+ for_each_vuart(vdev)
+ vdev->dump_state(d);
+}
+
+/*
+ * Put character to the first suitable emulated UART's FIFO.
+ */
+int vuart_put_rx(struct domain *d, char c)
+{
+ const struct vuart_ops *vdev = NULL;
+
+ ASSERT(domain_has_vuart(d));
+
+ for_each_vuart(vdev)
+ if ( vdev->put_rx )
+ break;
+
+ return vdev ? vdev->put_rx(d, c) : -ENODEV;
+}
+
+bool domain_has_vuart(const struct domain *d)
+{
+ uint32_t mask = 0;
+
+ return !!(d->emulation_flags & mask);
+}
+
+/*
+ * 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 eccd97c565c6..af427d25dc0d 100644
--- a/xen/common/keyhandler.c
+++ b/xen/common/keyhandler.c
@@ -22,6 +22,7 @@
#include <xen/mm.h>
#include <xen/watchdog.h>
#include <xen/init.h>
+#include <xen/vuart.h>
#include <asm/div64.h>
static unsigned char keypress_key;
@@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
v->periodic_period / 1000000);
}
}
+
+ vuart_dump_state(d);
}
for_each_domain ( d )
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 963c7b043cd8..93254979817b 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>
@@ -601,6 +602,7 @@ static void __serial_rx(char c)
/*
* Deliver input to the hardware domain buffer, unless it is
* already full.
+ * NB: must be the first check: hardware domain may have emulated UART.
*/
if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
@@ -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/vuart.h b/xen/include/xen/vuart.h
new file mode 100644
index 000000000000..e843026df4b1
--- /dev/null
+++ b/xen/include/xen/vuart.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UART emulator framework.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+#ifndef XEN_VUART_H
+#define XEN_VUART_H
+
+#include <public/xen.h>
+#include <public/event_channel.h>
+#include <xen/types.h>
+
+struct vuart_params {
+ domid_t console_domid;
+ gfn_t gfn;
+ evtchn_port_t evtchn;
+};
+
+struct vuart_ops {
+ int (*add_node)(struct domain *d, const void *node);
+ int (*init)(struct domain *d, struct vuart_params *params);
+ void (*deinit)(struct domain *d);
+ void (*dump_state)(const struct domain *d);
+ int (*put_rx)(struct domain *d, char c);
+};
+
+#define VUART_REGISTER(name, x) \
+ static const struct vuart_ops *const __name##_entry \
+ __used_section(".data.vuart." #name) = (x);
+
+#ifdef CONFIG_HAS_VUART
+
+int vuart_add_node(struct domain *d, const void *node);
+int vuart_init(struct domain *d, struct vuart_params *params);
+void vuart_deinit(struct domain *d);
+void vuart_dump_state(const struct domain *d);
+int vuart_put_rx(struct domain *d, char c);
+bool domain_has_vuart(const struct domain *d);
+
+#else
+
+static inline int vuart_add_node(struct domain *d, const void *node)
+{
+ return 0;
+}
+
+static inline int vuart_init(struct domain *d, struct vuart_params *params)
+{
+ return 0;
+}
+
+static inline void vuart_deinit(struct domain *d)
+{
+}
+
+static inline void vuart_dump_state(const struct domain *d)
+{
+}
+
+static inline int vuart_put_rx(struct domain *d, char c)
+{
+ ASSERT_UNREACHABLE();
+ return -ENODEV;
+}
+
+static inline bool domain_has_vuart(const struct domain *d)
+{
+ return false;
+}
+
+#endif /* CONFIG_HAS_VUART */
+
+#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..c2da180948ca 100644
--- a/xen/include/xen/xen.lds.h
+++ b/xen/include/xen/xen.lds.h
@@ -194,4 +194,14 @@
#define VPCI_ARRAY
#endif
+#ifdef CONFIG_HAS_VUART
+#define VUART_ARRAY \
+ . = ALIGN(POINTER_ALIGN); \
+ __start_vuart_array = .; \
+ *(SORT(.data.vuart.*)) \
+ __start_vuart_end = .;
+#else
+#define VUART_ARRAY
+#endif
+
#endif /* __XEN_LDS_H__ */
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-07-31 19:21 ` [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators dmkhn
@ 2025-08-01 0:08 ` Stefano Stabellini
2025-08-01 2:54 ` dmkhn
2025-08-04 10:11 ` Jan Beulich
2025-08-06 14:24 ` Roger Pau Monné
2 siblings, 1 reply; 61+ messages in thread
From: Stefano Stabellini @ 2025-08-01 0:08 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, 31 Jul 2025, dmkhn@proton.me 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_HAS_VUART, 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.
>
> 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.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v3:
> - new patch
> - original patch from ML: https://lore.kernel.org/xen-devel/20250624035443.344099-16-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 | 112 +++++++++++++++++++++++++++++++++
> xen/common/keyhandler.c | 3 +
> xen/drivers/char/console.c | 4 ++
> xen/include/xen/vuart.h | 84 +++++++++++++++++++++++++
> xen/include/xen/xen.lds.h | 10 +++
> 15 files changed, 234 insertions(+)
> 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 9f30c3a13ed1..bdba7eaa4f65 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 8e9cac75b09e..43426df33092 100644
> --- a/xen/arch/x86/xen.lds.S
> +++ b/xen/arch/x86/xen.lds.S
> @@ -136,6 +136,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 16936418a6e6..4e0bd524dc43 100644
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -1,6 +1,8 @@
>
> menu "Common Features"
>
> +source "common/emul/Kconfig"
> +
> config COMPAT
> bool
> help
> diff --git a/xen/common/Makefile b/xen/common/Makefile
> index c316957fcb36..c0734480ee4b 100644
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> obj-$(CONFIG_IOREQ_SERVER) += dm.o
> obj-y += domain.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..670682102c13
> --- /dev/null
> +++ b/xen/common/emul/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HAS_VUART) += vuart/
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> new file mode 100644
> index 000000000000..02f7dd6dc1a1
> --- /dev/null
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -0,0 +1,6 @@
> +config HAS_VUART
> + 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..c6400b001e85
> --- /dev/null
> +++ b/xen/common/emul/vuart/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HAS_VUART) += vuart.o
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> new file mode 100644
> index 000000000000..14a7f8bd8b79
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -0,0 +1,112 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#include <xen/errno.h>
> +#include <xen/sched.h>
> +#include <xen/vuart.h>
> +
> +#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
> +
> +#define for_each_vuart(vdev) \
> + for (unsigned __i = 0; \
> + __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
> + __i++)
> +
> +extern const struct vuart_ops *const __start_vuart_array[];
> +extern const struct vuart_ops *const __start_vuart_end[];
> +
> +int vuart_add_node(struct domain *d, const void *node)
> +{
> + const struct vuart_ops *vdev;
> + int rc;
> +
> + for_each_vuart(vdev)
> + {
> + if ( !vdev->add_node )
> + continue;
> +
> + rc = vdev->add_node(d, node);
> + if ( rc )
> + return rc;
> + }
> +
> + return 0;
> +}
Maybe skip this function until we needed? Without the reference
implementation of vuart-ns16550.c it is hard to tell what it is supposed
to do.
> +int vuart_init(struct domain *d, struct vuart_params *params)
> +{
> + const struct vuart_ops *vdev;
> + int rc;
> +
> + if ( !domain_has_vuart(d) )
> + return 0;
> +
> + for_each_vuart(vdev)
> + {
> + rc = vdev->init(d, params);
> + if ( rc )
> + return rc;
> + }
> +
> + d->console.input_allowed = true;
This works because there is only one emulator (NS16550) but if there
were multiple possible emulators, I think we would want to only
initialize the emulator enabled in the specific domain.
One domain could only have NS16550 and another domain could only have
PL011, while both NS16550 and PL011 might be possible.
I think it is OK for now and this function can be fixed/improved when
adding the second emulator.
> + return 0;
> +}
> +
> +/*
> + * Release any resources taken by UART emulators.
> + *
> + * NB: no flags are cleared, since currently exit() is called only during
> + * domain destroy.
> + */
> +void vuart_deinit(struct domain *d)
> +{
> + const struct vuart_ops *vdev;
> +
> + for_each_vuart(vdev)
> + vdev->deinit(d);
> +}
> +
> +void vuart_dump_state(const struct domain *d)
> +{
> + const struct vuart_ops *vdev;
> +
> + for_each_vuart(vdev)
> + vdev->dump_state(d);
> +}
> +
> +/*
> + * Put character to the first suitable emulated UART's FIFO.
> + */
> +int vuart_put_rx(struct domain *d, char c)
> +{
> + const struct vuart_ops *vdev = NULL;
> +
> + ASSERT(domain_has_vuart(d));
> +
> + for_each_vuart(vdev)
> + if ( vdev->put_rx )
> + break;
> +
> + return vdev ? vdev->put_rx(d, c) : -ENODEV;
I don't think this would work with multiple emulators possible, maybe
enable or maybe not, for the same domain.
In a situation where there is both PL011 and NS16550 enable in the Xen
kconfig, but only NS16550 enabled for this specific domain,
for_each_vuart might find PL011 as the first emulator with a put_rx
implementation, but it is not actually the one the domain can use.
I think this is OK for now, but it would have to be fixed when adding a
second emulator.
> +}
> +
> +bool domain_has_vuart(const struct domain *d)
> +{
> + uint32_t mask = 0;
> +
> + return !!(d->emulation_flags & mask);
> +}
> +
> +/*
> + * 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 eccd97c565c6..af427d25dc0d 100644
> --- a/xen/common/keyhandler.c
> +++ b/xen/common/keyhandler.c
> @@ -22,6 +22,7 @@
> #include <xen/mm.h>
> #include <xen/watchdog.h>
> #include <xen/init.h>
> +#include <xen/vuart.h>
> #include <asm/div64.h>
>
> static unsigned char keypress_key;
> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> v->periodic_period / 1000000);
> }
> }
> +
> + vuart_dump_state(d);
> }
>
> for_each_domain ( d )
> diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> index 963c7b043cd8..93254979817b 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>
> @@ -601,6 +602,7 @@ static void __serial_rx(char c)
> /*
> * Deliver input to the hardware domain buffer, unless it is
> * already full.
> + * NB: must be the first check: hardware domain may have emulated UART.
> */
> if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
> serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
> @@ -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/vuart.h b/xen/include/xen/vuart.h
> new file mode 100644
> index 000000000000..e843026df4b1
> --- /dev/null
> +++ b/xen/include/xen/vuart.h
> @@ -0,0 +1,84 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#ifndef XEN_VUART_H
> +#define XEN_VUART_H
> +
> +#include <public/xen.h>
> +#include <public/event_channel.h>
> +#include <xen/types.h>
> +
> +struct vuart_params {
> + domid_t console_domid;
> + gfn_t gfn;
> + evtchn_port_t evtchn;
> +};
> +
> +struct vuart_ops {
> + int (*add_node)(struct domain *d, const void *node);
> + int (*init)(struct domain *d, struct vuart_params *params);
> + void (*deinit)(struct domain *d);
> + void (*dump_state)(const struct domain *d);
> + int (*put_rx)(struct domain *d, char c);
> +};
> +
> +#define VUART_REGISTER(name, x) \
> + static const struct vuart_ops *const __name##_entry \
> + __used_section(".data.vuart." #name) = (x);
> +
> +#ifdef CONFIG_HAS_VUART
> +
> +int vuart_add_node(struct domain *d, const void *node);
> +int vuart_init(struct domain *d, struct vuart_params *params);
> +void vuart_deinit(struct domain *d);
> +void vuart_dump_state(const struct domain *d);
> +int vuart_put_rx(struct domain *d, char c);
> +bool domain_has_vuart(const struct domain *d);
> +
> +#else
> +
> +static inline int vuart_add_node(struct domain *d, const void *node)
> +{
> + return 0;
> +}
> +
> +static inline int vuart_init(struct domain *d, struct vuart_params *params)
> +{
> + return 0;
> +}
> +
> +static inline void vuart_deinit(struct domain *d)
> +{
> +}
> +
> +static inline void vuart_dump_state(const struct domain *d)
> +{
> +}
> +
> +static inline int vuart_put_rx(struct domain *d, char c)
> +{
> + ASSERT_UNREACHABLE();
> + return -ENODEV;
> +}
> +
> +static inline bool domain_has_vuart(const struct domain *d)
> +{
> + return false;
> +}
> +
> +#endif /* CONFIG_HAS_VUART */
> +
> +#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..c2da180948ca 100644
> --- a/xen/include/xen/xen.lds.h
> +++ b/xen/include/xen/xen.lds.h
> @@ -194,4 +194,14 @@
> #define VPCI_ARRAY
> #endif
>
> +#ifdef CONFIG_HAS_VUART
> +#define VUART_ARRAY \
> + . = ALIGN(POINTER_ALIGN); \
> + __start_vuart_array = .; \
> + *(SORT(.data.vuart.*)) \
> + __start_vuart_end = .;
> +#else
> +#define VUART_ARRAY
> +#endif
> +
> #endif /* __XEN_LDS_H__ */
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-01 0:08 ` Stefano Stabellini
@ 2025-08-01 2:54 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-01 2:54 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Thu, Jul 31, 2025 at 05:08:12PM -0700, Stefano Stabellini wrote:
> On Thu, 31 Jul 2025, dmkhn@proton.me 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_HAS_VUART, 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.
> >
> > 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.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Changes since v3:
> > - new patch
> > - original patch from ML: https://lore.kernel.org/xen-devel/20250624035443.344099-16-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 | 112 +++++++++++++++++++++++++++++++++
> > xen/common/keyhandler.c | 3 +
> > xen/drivers/char/console.c | 4 ++
> > xen/include/xen/vuart.h | 84 +++++++++++++++++++++++++
> > xen/include/xen/xen.lds.h | 10 +++
> > 15 files changed, 234 insertions(+)
> > 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 9f30c3a13ed1..bdba7eaa4f65 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 8e9cac75b09e..43426df33092 100644
> > --- a/xen/arch/x86/xen.lds.S
> > +++ b/xen/arch/x86/xen.lds.S
> > @@ -136,6 +136,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 16936418a6e6..4e0bd524dc43 100644
> > --- a/xen/common/Kconfig
> > +++ b/xen/common/Kconfig
> > @@ -1,6 +1,8 @@
> >
> > menu "Common Features"
> >
> > +source "common/emul/Kconfig"
> > +
> > config COMPAT
> > bool
> > help
> > diff --git a/xen/common/Makefile b/xen/common/Makefile
> > index c316957fcb36..c0734480ee4b 100644
> > --- a/xen/common/Makefile
> > +++ b/xen/common/Makefile
> > @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> > obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> > obj-$(CONFIG_IOREQ_SERVER) += dm.o
> > obj-y += domain.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..670682102c13
> > --- /dev/null
> > +++ b/xen/common/emul/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_HAS_VUART) += vuart/
> > diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> > new file mode 100644
> > index 000000000000..02f7dd6dc1a1
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -0,0 +1,6 @@
> > +config HAS_VUART
> > + 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..c6400b001e85
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_HAS_VUART) += vuart.o
> > diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> > new file mode 100644
> > index 000000000000..14a7f8bd8b79
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/vuart.c
> > @@ -0,0 +1,112 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#include <xen/errno.h>
> > +#include <xen/sched.h>
> > +#include <xen/vuart.h>
> > +
> > +#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
> > +
> > +#define for_each_vuart(vdev) \
> > + for (unsigned __i = 0; \
> > + __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
> > + __i++)
> > +
> > +extern const struct vuart_ops *const __start_vuart_array[];
> > +extern const struct vuart_ops *const __start_vuart_end[];
> > +
> > +int vuart_add_node(struct domain *d, const void *node)
> > +{
> > + const struct vuart_ops *vdev;
> > + int rc;
> > +
> > + for_each_vuart(vdev)
> > + {
> > + if ( !vdev->add_node )
> > + continue;
> > +
> > + rc = vdev->add_node(d, node);
> > + if ( rc )
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
>
> Maybe skip this function until we needed? Without the reference
> implementation of vuart-ns16550.c it is hard to tell what it is supposed
> to do.
Ack
>
>
> > +int vuart_init(struct domain *d, struct vuart_params *params)
> > +{
> > + const struct vuart_ops *vdev;
> > + int rc;
> > +
> > + if ( !domain_has_vuart(d) )
> > + return 0;
> > +
> > + for_each_vuart(vdev)
> > + {
> > + rc = vdev->init(d, params);
> > + if ( rc )
> > + return rc;
> > + }
> > +
> > + d->console.input_allowed = true;
>
> This works because there is only one emulator (NS16550) but if there
> were multiple possible emulators, I think we would want to only
> initialize the emulator enabled in the specific domain.
There are two emulators simultenously on Arm: "vuart", i.e. hwdom
dtuart for earlycon and "vpl011", i.e. SBSA.
>
> One domain could only have NS16550 and another domain could only have
> PL011, while both NS16550 and PL011 might be possible.
>
> I think it is OK for now and this function can be fixed/improved when
> adding the second emulator.
>
>
> > + return 0;
> > +}
> > +
> > +/*
> > + * Release any resources taken by UART emulators.
> > + *
> > + * NB: no flags are cleared, since currently exit() is called only during
> > + * domain destroy.
> > + */
> > +void vuart_deinit(struct domain *d)
> > +{
> > + const struct vuart_ops *vdev;
> > +
> > + for_each_vuart(vdev)
> > + vdev->deinit(d);
> > +}
> > +
> > +void vuart_dump_state(const struct domain *d)
> > +{
> > + const struct vuart_ops *vdev;
> > +
> > + for_each_vuart(vdev)
> > + vdev->dump_state(d);
> > +}
> > +
> > +/*
> > + * Put character to the first suitable emulated UART's FIFO.
> > + */
> > +int vuart_put_rx(struct domain *d, char c)
> > +{
> > + const struct vuart_ops *vdev = NULL;
> > +
> > + ASSERT(domain_has_vuart(d));
> > +
> > + for_each_vuart(vdev)
> > + if ( vdev->put_rx )
> > + break;
> > +
> > + return vdev ? vdev->put_rx(d, c) : -ENODEV;
>
> I don't think this would work with multiple emulators possible, maybe
> enable or maybe not, for the same domain.
>
> In a situation where there is both PL011 and NS16550 enable in the Xen
> kconfig, but only NS16550 enabled for this specific domain,
> for_each_vuart might find PL011 as the first emulator with a put_rx
> implementation, but it is not actually the one the domain can use.
Yes, multiple vUARTs will need more work.
>
> I think this is OK for now, but it would have to be fixed when adding a
> second emulator.
>
>
> > +}
> > +
> > +bool domain_has_vuart(const struct domain *d)
> > +{
> > + uint32_t mask = 0;
> > +
> > + return !!(d->emulation_flags & mask);
> > +}
> > +
> > +/*
> > + * 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 eccd97c565c6..af427d25dc0d 100644
> > --- a/xen/common/keyhandler.c
> > +++ b/xen/common/keyhandler.c
> > @@ -22,6 +22,7 @@
> > #include <xen/mm.h>
> > #include <xen/watchdog.h>
> > #include <xen/init.h>
> > +#include <xen/vuart.h>
> > #include <asm/div64.h>
> >
> > static unsigned char keypress_key;
> > @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> > v->periodic_period / 1000000);
> > }
> > }
> > +
> > + vuart_dump_state(d);
> > }
> >
> > for_each_domain ( d )
> > diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> > index 963c7b043cd8..93254979817b 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>
> > @@ -601,6 +602,7 @@ static void __serial_rx(char c)
> > /*
> > * Deliver input to the hardware domain buffer, unless it is
> > * already full.
> > + * NB: must be the first check: hardware domain may have emulated UART.
> > */
> > if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
> > serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
> > @@ -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/vuart.h b/xen/include/xen/vuart.h
> > new file mode 100644
> > index 000000000000..e843026df4b1
> > --- /dev/null
> > +++ b/xen/include/xen/vuart.h
> > @@ -0,0 +1,84 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#ifndef XEN_VUART_H
> > +#define XEN_VUART_H
> > +
> > +#include <public/xen.h>
> > +#include <public/event_channel.h>
> > +#include <xen/types.h>
> > +
> > +struct vuart_params {
> > + domid_t console_domid;
> > + gfn_t gfn;
> > + evtchn_port_t evtchn;
> > +};
> > +
> > +struct vuart_ops {
> > + int (*add_node)(struct domain *d, const void *node);
> > + int (*init)(struct domain *d, struct vuart_params *params);
> > + void (*deinit)(struct domain *d);
> > + void (*dump_state)(const struct domain *d);
> > + int (*put_rx)(struct domain *d, char c);
> > +};
> > +
> > +#define VUART_REGISTER(name, x) \
> > + static const struct vuart_ops *const __name##_entry \
> > + __used_section(".data.vuart." #name) = (x);
> > +
> > +#ifdef CONFIG_HAS_VUART
> > +
> > +int vuart_add_node(struct domain *d, const void *node);
> > +int vuart_init(struct domain *d, struct vuart_params *params);
> > +void vuart_deinit(struct domain *d);
> > +void vuart_dump_state(const struct domain *d);
> > +int vuart_put_rx(struct domain *d, char c);
> > +bool domain_has_vuart(const struct domain *d);
> > +
> > +#else
> > +
> > +static inline int vuart_add_node(struct domain *d, const void *node)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int vuart_init(struct domain *d, struct vuart_params *params)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void vuart_deinit(struct domain *d)
> > +{
> > +}
> > +
> > +static inline void vuart_dump_state(const struct domain *d)
> > +{
> > +}
> > +
> > +static inline int vuart_put_rx(struct domain *d, char c)
> > +{
> > + ASSERT_UNREACHABLE();
> > + return -ENODEV;
> > +}
> > +
> > +static inline bool domain_has_vuart(const struct domain *d)
> > +{
> > + return false;
> > +}
> > +
> > +#endif /* CONFIG_HAS_VUART */
> > +
> > +#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..c2da180948ca 100644
> > --- a/xen/include/xen/xen.lds.h
> > +++ b/xen/include/xen/xen.lds.h
> > @@ -194,4 +194,14 @@
> > #define VPCI_ARRAY
> > #endif
> >
> > +#ifdef CONFIG_HAS_VUART
> > +#define VUART_ARRAY \
> > + . = ALIGN(POINTER_ALIGN); \
> > + __start_vuart_array = .; \
> > + *(SORT(.data.vuart.*)) \
> > + __start_vuart_end = .;
> > +#else
> > +#define VUART_ARRAY
> > +#endif
> > +
> > #endif /* __XEN_LDS_H__ */
> > --
> > 2.34.1
> >
> >
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-07-31 19:21 ` [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators dmkhn
2025-08-01 0:08 ` Stefano Stabellini
@ 2025-08-04 10:11 ` Jan Beulich
2025-08-09 18:55 ` dmkhn
2025-08-06 14:24 ` Roger Pau Monné
2 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 10:11 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:21, dmkhn@proton.me 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_HAS_VUART, which will be automatically
> enabled once the user enables any UART emulator.
Yet then still - why "HAS"? Call it just VUART or VUART_FRAMEWORK or some such.
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -1,6 +1,8 @@
>
> menu "Common Features"
>
> +source "common/emul/Kconfig"
> +
> config COMPAT
Why at the very top?
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> obj-$(CONFIG_IOREQ_SERVER) += dm.o
> obj-y += domain.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..670682102c13
> --- /dev/null
> +++ b/xen/common/emul/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HAS_VUART) += vuart/
With this you can ...
> --- /dev/null
> +++ b/xen/common/emul/vuart/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HAS_VUART) += vuart.o
... use the simpler obj-y here.
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -0,0 +1,112 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#include <xen/errno.h>
> +#include <xen/sched.h>
> +#include <xen/vuart.h>
> +
> +#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
> +
> +#define for_each_vuart(vdev) \
> + for (unsigned __i = 0; \
> + __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
> + __i++)
Nit: Xen style please. Any preferably no leading underscores; in no case
two of them.
> +extern const struct vuart_ops *const __start_vuart_array[];
> +extern const struct vuart_ops *const __start_vuart_end[];
Is there an actual need for this extra level of indirection? It is in the
process of being done away with for vPCI.
> +int vuart_add_node(struct domain *d, const void *node)
> +{
> + const struct vuart_ops *vdev;
> + int rc;
> +
> + for_each_vuart(vdev)
> + {
> + if ( !vdev->add_node )
> + continue;
> +
> + rc = vdev->add_node(d, node);
Here and below - shouldn't you call hooks only when the kind of driver is
actually enabled for the domkain in question?
> + if ( rc )
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int vuart_init(struct domain *d, struct vuart_params *params)
> +{
> + const struct vuart_ops *vdev;
> + int rc;
> +
> + if ( !domain_has_vuart(d) )
> + return 0;
> +
> + for_each_vuart(vdev)
> + {
> + rc = vdev->init(d, params);
> + if ( rc )
> + return rc;
> + }
> +
> + d->console.input_allowed = true;
Unconditionally?
> +void vuart_deinit(struct domain *d)
> +{
> + const struct vuart_ops *vdev;
> +
> + for_each_vuart(vdev)
> + vdev->deinit(d);
> +}
I can perhaps see why this hook wants to uniformly be set, but ...
> +void vuart_dump_state(const struct domain *d)
> +{
> + const struct vuart_ops *vdev;
> +
> + for_each_vuart(vdev)
> + vdev->dump_state(d);
> +}
... state dumping pretty surely wants to be optional?
> +/*
> + * Put character to the first suitable emulated UART's FIFO.
> + */
What's "suitable"? Along the lines of the earlier remark, what if the domain
has vUART kind A configured, ...
> +int vuart_put_rx(struct domain *d, char c)
> +{
> + const struct vuart_ops *vdev = NULL;
> +
> + ASSERT(domain_has_vuart(d));
> +
> + for_each_vuart(vdev)
> + if ( vdev->put_rx )
... but only kind B offers this hook?
> + break;
> +
> + return vdev ? vdev->put_rx(d, c) : -ENODEV;
The check for NULL helps for the "no vUART drivers" case, but it won't
help if you exhausted the array without finding a driver with the wanted
hook.
> +}
> +
> +bool domain_has_vuart(const struct domain *d)
> +{
> + uint32_t mask = 0;
unsigned int?
> --- a/xen/common/keyhandler.c
> +++ b/xen/common/keyhandler.c
> @@ -22,6 +22,7 @@
> #include <xen/mm.h>
> #include <xen/watchdog.h>
> #include <xen/init.h>
> +#include <xen/vuart.h>
> #include <asm/div64.h>
>
> static unsigned char keypress_key;
> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> v->periodic_period / 1000000);
> }
> }
> +
> + vuart_dump_state(d);
How verbose is this going to get?
> --- /dev/null
> +++ b/xen/include/xen/vuart.h
> @@ -0,0 +1,84 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#ifndef XEN_VUART_H
> +#define XEN_VUART_H
> +
> +#include <public/xen.h>
> +#include <public/event_channel.h>
> +#include <xen/types.h>
The order is wrong - types must be available before public headers are included.
> +struct vuart_params {
> + domid_t console_domid;
> + gfn_t gfn;
> + evtchn_port_t evtchn;
> +};
> +
> +struct vuart_ops {
> + int (*add_node)(struct domain *d, const void *node);
> + int (*init)(struct domain *d, struct vuart_params *params);
> + void (*deinit)(struct domain *d);
> + void (*dump_state)(const struct domain *d);
> + int (*put_rx)(struct domain *d, char c);
> +};
> +
> +#define VUART_REGISTER(name, x) \
> + static const struct vuart_ops *const __name##_entry \
> + __used_section(".data.vuart." #name) = (x);
> +
> +#ifdef CONFIG_HAS_VUART
> +
> +int vuart_add_node(struct domain *d, const void *node);
> +int vuart_init(struct domain *d, struct vuart_params *params);
> +void vuart_deinit(struct domain *d);
> +void vuart_dump_state(const struct domain *d);
> +int vuart_put_rx(struct domain *d, char c);
> +bool domain_has_vuart(const struct domain *d);
> +
> +#else
> +
> +static inline int vuart_add_node(struct domain *d, const void *node)
> +{
> + return 0;
> +}
> +
> +static inline int vuart_init(struct domain *d, struct vuart_params *params)
> +{
> + return 0;
> +}
> +
> +static inline void vuart_deinit(struct domain *d)
> +{
> +}
> +
> +static inline void vuart_dump_state(const struct domain *d)
> +{
> +}
> +
> +static inline int vuart_put_rx(struct domain *d, char c)
> +{
> + ASSERT_UNREACHABLE();
> + return -ENODEV;
> +}
> +
> +static inline bool domain_has_vuart(const struct domain *d)
> +{
> + return false;
> +}
With this, some of the other stubs should not be necessary. Declarations
will suffice, e.g. for vuart_put_rx().
> --- a/xen/include/xen/xen.lds.h
> +++ b/xen/include/xen/xen.lds.h
> @@ -194,4 +194,14 @@
> #define VPCI_ARRAY
> #endif
>
> +#ifdef CONFIG_HAS_VUART
> +#define VUART_ARRAY \
> + . = ALIGN(POINTER_ALIGN); \
> + __start_vuart_array = .; \
> + *(SORT(.data.vuart.*)) \
This is r/o data afaict, so would want naming .rodata.vuart.*. Which in
turn means the uses of the macros need to move up in the linker scripts.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-04 10:11 ` Jan Beulich
@ 2025-08-09 18:55 ` dmkhn
2025-08-11 7:34 ` Jan Beulich
0 siblings, 1 reply; 61+ messages in thread
From: dmkhn @ 2025-08-09 18:55 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 04, 2025 at 12:11:03PM +0200, Jan Beulich wrote:
> On 31.07.2025 21:21, dmkhn@proton.me 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_HAS_VUART, which will be automatically
> > enabled once the user enables any UART emulator.
>
> Yet then still - why "HAS"? Call it just VUART or VUART_FRAMEWORK or some such.
>
> > --- a/xen/common/Kconfig
> > +++ b/xen/common/Kconfig
> > @@ -1,6 +1,8 @@
> >
> > menu "Common Features"
> >
> > +source "common/emul/Kconfig"
> > +
> > config COMPAT
>
> Why at the very top?
I did not find a better place, since the settings are not sorted and to me it
makes sense to list emulation capabilities first...
Where would be the best location for that submenu?
Close to another submenu `source "common/sched/Kconfig"`?
>
> > --- a/xen/common/Makefile
> > +++ b/xen/common/Makefile
> > @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> > obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> > obj-$(CONFIG_IOREQ_SERVER) += dm.o
> > obj-y += domain.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..670682102c13
> > --- /dev/null
> > +++ b/xen/common/emul/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_HAS_VUART) += vuart/
>
> With this you can ...
>
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_HAS_VUART) += vuart.o
>
> ... use the simpler obj-y here.
Thanks.
>
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/vuart.c
> > @@ -0,0 +1,112 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#include <xen/errno.h>
> > +#include <xen/sched.h>
> > +#include <xen/vuart.h>
> > +
> > +#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
> > +
> > +#define for_each_vuart(vdev) \
> > + for (unsigned __i = 0; \
> > + __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
> > + __i++)
>
> Nit: Xen style please. Any preferably no leading underscores; in no case
> two of them.
Ack.
>
> > +extern const struct vuart_ops *const __start_vuart_array[];
> > +extern const struct vuart_ops *const __start_vuart_end[];
>
> Is there an actual need for this extra level of indirection? It is in the
> process of being done away with for vPCI.
Ack.
>
> > +int vuart_add_node(struct domain *d, const void *node)
> > +{
> > + const struct vuart_ops *vdev;
> > + int rc;
> > +
> > + for_each_vuart(vdev)
> > + {
> > + if ( !vdev->add_node )
> > + continue;
> > +
> > + rc = vdev->add_node(d, node);
>
> Here and below - shouldn't you call hooks only when the kind of driver is
> actually enabled for the domkain in question?
Thanks; this looks a bit raw...
I will rework that in the follow on change. I will drop vuart_add_node()
from NS16550 series since it is not used yet.
>
> > + if ( rc )
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int vuart_init(struct domain *d, struct vuart_params *params)
> > +{
> > + const struct vuart_ops *vdev;
> > + int rc;
> > +
> > + if ( !domain_has_vuart(d) )
> > + return 0;
> > +
> > + for_each_vuart(vdev)
> > + {
> > + rc = vdev->init(d, params);
> > + if ( rc )
> > + return rc;
> > + }
> > +
> > + d->console.input_allowed = true;
>
> Unconditionally?
Thanks.
That should be a least under rc == 0.
>
> > +void vuart_deinit(struct domain *d)
> > +{
> > + const struct vuart_ops *vdev;
> > +
> > + for_each_vuart(vdev)
> > + vdev->deinit(d);
> > +}
>
> I can perhaps see why this hook wants to uniformly be set, but ...
>
> > +void vuart_dump_state(const struct domain *d)
> > +{
> > + const struct vuart_ops *vdev;
> > +
> > + for_each_vuart(vdev)
> > + vdev->dump_state(d);
> > +}
>
> ... state dumping pretty surely wants to be optional?
Ack.
>
> > +/*
> > + * Put character to the first suitable emulated UART's FIFO.
> > + */
>
> What's "suitable"? Along the lines of the earlier remark, what if the domain
> has vUART kind A configured, ...
"suitable" is meant to be the first emulator with put_rx != NULL.
I will update that.
>
> > +int vuart_put_rx(struct domain *d, char c)
> > +{
> > + const struct vuart_ops *vdev = NULL;
> > +
> > + ASSERT(domain_has_vuart(d));
> > +
> > + for_each_vuart(vdev)
> > + if ( vdev->put_rx )
>
> ... but only kind B offers this hook?
>
> > + break;
> > +
> > + return vdev ? vdev->put_rx(d, c) : -ENODEV;
>
> The check for NULL helps for the "no vUART drivers" case, but it won't
> help if you exhausted the array without finding a driver with the wanted
> hook.
Ack.
>
> > +}
> > +
> > +bool domain_has_vuart(const struct domain *d)
> > +{
> > + uint32_t mask = 0;
>
> unsigned int?
Ack.
>
> > --- a/xen/common/keyhandler.c
> > +++ b/xen/common/keyhandler.c
> > @@ -22,6 +22,7 @@
> > #include <xen/mm.h>
> > #include <xen/watchdog.h>
> > #include <xen/init.h>
> > +#include <xen/vuart.h>
> > #include <asm/div64.h>
> >
> > static unsigned char keypress_key;
> > @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> > v->periodic_period / 1000000);
> > }
> > }
> > +
> > + vuart_dump_state(d);
>
> How verbose is this going to get?
Looks something like this:
```
(XEN) [ 88.334893] 'q' pressed -> dumping domain info (now = 88334828303)
[..]
(XEN) [ 88.335673] Virtual ns16550 (COM2) I/O port 0x02f8 IRQ#3 owner d0
(XEN) [ 88.335681] RX FIFO size 1024 in_prod 258 in_cons 258 used 0
(XEN) [ 88.335689] TX FIFO size 2048 out_prod 15 out_cons 0 used 15
(XEN) [ 88.335696] 00 RBR 02 THR 6f DLL 01 DLM 00
(XEN) [ 88.335703] 01 IER 05
(XEN) [ 88.335709] 02 FCR 81 IIR c1
(XEN) [ 88.335715] 03 LCR 13
(XEN) [ 88.335720] 04 MCR 0b
(XEN) [ 88.335726] 05 LSR 60
(XEN) [ 88.335731] 06 MSR b0
(XEN) [ 88.335736] 07 SCR 00
```
>
> > --- /dev/null
> > +++ b/xen/include/xen/vuart.h
> > @@ -0,0 +1,84 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#ifndef XEN_VUART_H
> > +#define XEN_VUART_H
> > +
> > +#include <public/xen.h>
> > +#include <public/event_channel.h>
> > +#include <xen/types.h>
>
> The order is wrong - types must be available before public headers are included.
Ack.
>
> > +struct vuart_params {
> > + domid_t console_domid;
> > + gfn_t gfn;
> > + evtchn_port_t evtchn;
> > +};
> > +
> > +struct vuart_ops {
> > + int (*add_node)(struct domain *d, const void *node);
> > + int (*init)(struct domain *d, struct vuart_params *params);
> > + void (*deinit)(struct domain *d);
> > + void (*dump_state)(const struct domain *d);
> > + int (*put_rx)(struct domain *d, char c);
> > +};
> > +
> > +#define VUART_REGISTER(name, x) \
> > + static const struct vuart_ops *const __name##_entry \
> > + __used_section(".data.vuart." #name) = (x);
> > +
> > +#ifdef CONFIG_HAS_VUART
> > +
> > +int vuart_add_node(struct domain *d, const void *node);
> > +int vuart_init(struct domain *d, struct vuart_params *params);
> > +void vuart_deinit(struct domain *d);
> > +void vuart_dump_state(const struct domain *d);
> > +int vuart_put_rx(struct domain *d, char c);
> > +bool domain_has_vuart(const struct domain *d);
> > +
> > +#else
> > +
> > +static inline int vuart_add_node(struct domain *d, const void *node)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline int vuart_init(struct domain *d, struct vuart_params *params)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void vuart_deinit(struct domain *d)
> > +{
> > +}
> > +
> > +static inline void vuart_dump_state(const struct domain *d)
> > +{
> > +}
> > +
> > +static inline int vuart_put_rx(struct domain *d, char c)
> > +{
> > + ASSERT_UNREACHABLE();
> > + return -ENODEV;
> > +}
> > +
> > +static inline bool domain_has_vuart(const struct domain *d)
> > +{
> > + return false;
> > +}
>
> With this, some of the other stubs should not be necessary. Declarations
> will suffice, e.g. for vuart_put_rx().
Thanks, will update.
>
> > --- a/xen/include/xen/xen.lds.h
> > +++ b/xen/include/xen/xen.lds.h
> > @@ -194,4 +194,14 @@
> > #define VPCI_ARRAY
> > #endif
> >
> > +#ifdef CONFIG_HAS_VUART
> > +#define VUART_ARRAY \
> > + . = ALIGN(POINTER_ALIGN); \
> > + __start_vuart_array = .; \
> > + *(SORT(.data.vuart.*)) \
>
> This is r/o data afaict, so would want naming .rodata.vuart.*. Which in
> turn means the uses of the macros need to move up in the linker scripts.
Ack.
>
> Jan
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-09 18:55 ` dmkhn
@ 2025-08-11 7:34 ` Jan Beulich
2025-08-11 23:55 ` dmkhn
0 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-11 7:34 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 09.08.2025 20:55, dmkhn@proton.me wrote:
> On Mon, Aug 04, 2025 at 12:11:03PM +0200, Jan Beulich wrote:
>> On 31.07.2025 21:21, dmkhn@proton.me wrote:
>>> --- a/xen/common/Kconfig
>>> +++ b/xen/common/Kconfig
>>> @@ -1,6 +1,8 @@
>>>
>>> menu "Common Features"
>>>
>>> +source "common/emul/Kconfig"
>>> +
>>> config COMPAT
>>
>> Why at the very top?
>
> I did not find a better place, since the settings are not sorted and to me it
> makes sense to list emulation capabilities first...
>
> Where would be the best location for that submenu?
> Close to another submenu `source "common/sched/Kconfig"`?
At least below there. Possibly yet further down.
>>> +int vuart_init(struct domain *d, struct vuart_params *params)
>>> +{
>>> + const struct vuart_ops *vdev;
>>> + int rc;
>>> +
>>> + if ( !domain_has_vuart(d) )
>>> + return 0;
>>> +
>>> + for_each_vuart(vdev)
>>> + {
>>> + rc = vdev->init(d, params);
>>> + if ( rc )
>>> + return rc;
>>> + }
>>> +
>>> + d->console.input_allowed = true;
>>
>> Unconditionally?
>
> Thanks.
> That should be a least under rc == 0.
You only ever make it there with rc == 0, though. (In fact that variable's
scope would better be just the loop body.)
>>> +/*
>>> + * Put character to the first suitable emulated UART's FIFO.
>>> + */
>>
>> What's "suitable"? Along the lines of the earlier remark, what if the domain
>> has vUART kind A configured, ...
>
> "suitable" is meant to be the first emulator with put_rx != NULL.
> I will update that.
Except that, as iirc Roger also pointed out, "first emulator with put_rx != NULL"
is a questionable condition.
>>> --- a/xen/common/keyhandler.c
>>> +++ b/xen/common/keyhandler.c
>>> @@ -22,6 +22,7 @@
>>> #include <xen/mm.h>
>>> #include <xen/watchdog.h>
>>> #include <xen/init.h>
>>> +#include <xen/vuart.h>
>>> #include <asm/div64.h>
>>>
>>> static unsigned char keypress_key;
>>> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
>>> v->periodic_period / 1000000);
>>> }
>>> }
>>> +
>>> + vuart_dump_state(d);
>>
>> How verbose is this going to get?
>
> Looks something like this:
> ```
> (XEN) [ 88.334893] 'q' pressed -> dumping domain info (now = 88334828303)
> [..]
> (XEN) [ 88.335673] Virtual ns16550 (COM2) I/O port 0x02f8 IRQ#3 owner d0
> (XEN) [ 88.335681] RX FIFO size 1024 in_prod 258 in_cons 258 used 0
> (XEN) [ 88.335689] TX FIFO size 2048 out_prod 15 out_cons 0 used 15
> (XEN) [ 88.335696] 00 RBR 02 THR 6f DLL 01 DLM 00
> (XEN) [ 88.335703] 01 IER 05
> (XEN) [ 88.335709] 02 FCR 81 IIR c1
> (XEN) [ 88.335715] 03 LCR 13
> (XEN) [ 88.335720] 04 MCR 0b
> (XEN) [ 88.335726] 05 LSR 60
> (XEN) [ 88.335731] 06 MSR b0
> (XEN) [ 88.335736] 07 SCR 00
>
> ```
Definitely too much (for my taste) to put under 'q'.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-11 7:34 ` Jan Beulich
@ 2025-08-11 23:55 ` dmkhn
2025-08-12 6:52 ` Jan Beulich
0 siblings, 1 reply; 61+ messages in thread
From: dmkhn @ 2025-08-11 23:55 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 11, 2025 at 09:34:58AM +0200, Jan Beulich wrote:
> On 09.08.2025 20:55, dmkhn@proton.me wrote:
> > On Mon, Aug 04, 2025 at 12:11:03PM +0200, Jan Beulich wrote:
> >> On 31.07.2025 21:21, dmkhn@proton.me wrote:
> >>> --- a/xen/common/Kconfig
> >>> +++ b/xen/common/Kconfig
> >>> @@ -1,6 +1,8 @@
> >>>
> >>> menu "Common Features"
> >>>
> >>> +source "common/emul/Kconfig"
> >>> +
> >>> config COMPAT
> >>
> >> Why at the very top?
> >
> > I did not find a better place, since the settings are not sorted and to me it
> > makes sense to list emulation capabilities first...
> >
> > Where would be the best location for that submenu?
> > Close to another submenu `source "common/sched/Kconfig"`?
>
> At least below there. Possibly yet further down.
>
> >>> +int vuart_init(struct domain *d, struct vuart_params *params)
> >>> +{
> >>> + const struct vuart_ops *vdev;
> >>> + int rc;
> >>> +
> >>> + if ( !domain_has_vuart(d) )
> >>> + return 0;
> >>> +
> >>> + for_each_vuart(vdev)
> >>> + {
> >>> + rc = vdev->init(d, params);
> >>> + if ( rc )
> >>> + return rc;
> >>> + }
> >>> +
> >>> + d->console.input_allowed = true;
> >>
> >> Unconditionally?
> >
> > Thanks.
> > That should be a least under rc == 0.
>
> You only ever make it there with rc == 0, though. (In fact that variable's
> scope would better be just the loop body.)
>
> >>> +/*
> >>> + * Put character to the first suitable emulated UART's FIFO.
> >>> + */
> >>
> >> What's "suitable"? Along the lines of the earlier remark, what if the domain
> >> has vUART kind A configured, ...
> >
> > "suitable" is meant to be the first emulator with put_rx != NULL.
> > I will update that.
>
> Except that, as iirc Roger also pointed out, "first emulator with put_rx != NULL"
> is a questionable condition.
>
> >>> --- a/xen/common/keyhandler.c
> >>> +++ b/xen/common/keyhandler.c
> >>> @@ -22,6 +22,7 @@
> >>> #include <xen/mm.h>
> >>> #include <xen/watchdog.h>
> >>> #include <xen/init.h>
> >>> +#include <xen/vuart.h>
> >>> #include <asm/div64.h>
> >>>
> >>> static unsigned char keypress_key;
> >>> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> >>> v->periodic_period / 1000000);
> >>> }
> >>> }
> >>> +
> >>> + vuart_dump_state(d);
> >>
> >> How verbose is this going to get?
> >
> > Looks something like this:
> > ```
> > (XEN) [ 88.334893] 'q' pressed -> dumping domain info (now = 88334828303)
> > [..]
> > (XEN) [ 88.335673] Virtual ns16550 (COM2) I/O port 0x02f8 IRQ#3 owner d0
> > (XEN) [ 88.335681] RX FIFO size 1024 in_prod 258 in_cons 258 used 0
> > (XEN) [ 88.335689] TX FIFO size 2048 out_prod 15 out_cons 0 used 15
> > (XEN) [ 88.335696] 00 RBR 02 THR 6f DLL 01 DLM 00
> > (XEN) [ 88.335703] 01 IER 05
> > (XEN) [ 88.335709] 02 FCR 81 IIR c1
> > (XEN) [ 88.335715] 03 LCR 13
> > (XEN) [ 88.335720] 04 MCR 0b
> > (XEN) [ 88.335726] 05 LSR 60
> > (XEN) [ 88.335731] 06 MSR b0
> > (XEN) [ 88.335736] 07 SCR 00
> >
> > ```
>
> Definitely too much (for my taste) to put under 'q'.
I'll try to limit the number of printed lines; register dump can be made
compact for sure.
>
> Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-11 23:55 ` dmkhn
@ 2025-08-12 6:52 ` Jan Beulich
2025-08-14 6:32 ` dmkhn
0 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-12 6:52 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 12.08.2025 01:55, dmkhn@proton.me wrote:
> On Mon, Aug 11, 2025 at 09:34:58AM +0200, Jan Beulich wrote:
>> On 09.08.2025 20:55, dmkhn@proton.me wrote:
>>> On Mon, Aug 04, 2025 at 12:11:03PM +0200, Jan Beulich wrote:
>>>> On 31.07.2025 21:21, dmkhn@proton.me wrote:
>>>>> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
>>>>> v->periodic_period / 1000000);
>>>>> }
>>>>> }
>>>>> +
>>>>> + vuart_dump_state(d);
>>>>
>>>> How verbose is this going to get?
>>>
>>> Looks something like this:
>>> ```
>>> (XEN) [ 88.334893] 'q' pressed -> dumping domain info (now = 88334828303)
>>> [..]
>>> (XEN) [ 88.335673] Virtual ns16550 (COM2) I/O port 0x02f8 IRQ#3 owner d0
>>> (XEN) [ 88.335681] RX FIFO size 1024 in_prod 258 in_cons 258 used 0
>>> (XEN) [ 88.335689] TX FIFO size 2048 out_prod 15 out_cons 0 used 15
>>> (XEN) [ 88.335696] 00 RBR 02 THR 6f DLL 01 DLM 00
>>> (XEN) [ 88.335703] 01 IER 05
>>> (XEN) [ 88.335709] 02 FCR 81 IIR c1
>>> (XEN) [ 88.335715] 03 LCR 13
>>> (XEN) [ 88.335720] 04 MCR 0b
>>> (XEN) [ 88.335726] 05 LSR 60
>>> (XEN) [ 88.335731] 06 MSR b0
>>> (XEN) [ 88.335736] 07 SCR 00
>>>
>>> ```
>>
>> Definitely too much (for my taste) to put under 'q'.
>
> I'll try to limit the number of printed lines; register dump can be made
> compact for sure.
Yet even then I'm inclined to say that device specific data simply doesn't
belong here.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-12 6:52 ` Jan Beulich
@ 2025-08-14 6:32 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-14 6:32 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Tue, Aug 12, 2025 at 08:52:18AM +0200, Jan Beulich wrote:
> On 12.08.2025 01:55, dmkhn@proton.me wrote:
> > On Mon, Aug 11, 2025 at 09:34:58AM +0200, Jan Beulich wrote:
> >> On 09.08.2025 20:55, dmkhn@proton.me wrote:
> >>> On Mon, Aug 04, 2025 at 12:11:03PM +0200, Jan Beulich wrote:
> >>>> On 31.07.2025 21:21, dmkhn@proton.me wrote:
> >>>>> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> >>>>> v->periodic_period / 1000000);
> >>>>> }
> >>>>> }
> >>>>> +
> >>>>> + vuart_dump_state(d);
> >>>>
> >>>> How verbose is this going to get?
> >>>
> >>> Looks something like this:
> >>> ```
> >>> (XEN) [ 88.334893] 'q' pressed -> dumping domain info (now = 88334828303)
> >>> [..]
> >>> (XEN) [ 88.335673] Virtual ns16550 (COM2) I/O port 0x02f8 IRQ#3 owner d0
> >>> (XEN) [ 88.335681] RX FIFO size 1024 in_prod 258 in_cons 258 used 0
> >>> (XEN) [ 88.335689] TX FIFO size 2048 out_prod 15 out_cons 0 used 15
> >>> (XEN) [ 88.335696] 00 RBR 02 THR 6f DLL 01 DLM 00
> >>> (XEN) [ 88.335703] 01 IER 05
> >>> (XEN) [ 88.335709] 02 FCR 81 IIR c1
> >>> (XEN) [ 88.335715] 03 LCR 13
> >>> (XEN) [ 88.335720] 04 MCR 0b
> >>> (XEN) [ 88.335726] 05 LSR 60
> >>> (XEN) [ 88.335731] 06 MSR b0
> >>> (XEN) [ 88.335736] 07 SCR 00
> >>>
> >>> ```
> >>
> >> Definitely too much (for my taste) to put under 'q'.
> >
> > I'll try to limit the number of printed lines; register dump can be made
> > compact for sure.
>
> Yet even then I'm inclined to say that device specific data simply doesn't
> belong here.
Ability to produce such traces was helpful during debug, TBH.
I will hide per-vuart dump_state behind CONFIG_VUART_DUMP_STATE (or alike)
so default config does not enable it.
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-07-31 19:21 ` [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators dmkhn
2025-08-01 0:08 ` Stefano Stabellini
2025-08-04 10:11 ` Jan Beulich
@ 2025-08-06 14:24 ` Roger Pau Monné
2025-08-07 19:12 ` dmkhn
2 siblings, 1 reply; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 14:24 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Thu, Jul 31, 2025 at 07:21:49PM +0000, dmkhn@proton.me 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_HAS_VUART, 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.
>
> 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.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v3:
> - new patch
> - original patch from ML: https://lore.kernel.org/xen-devel/20250624035443.344099-16-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 | 112 +++++++++++++++++++++++++++++++++
> xen/common/keyhandler.c | 3 +
> xen/drivers/char/console.c | 4 ++
> xen/include/xen/vuart.h | 84 +++++++++++++++++++++++++
> xen/include/xen/xen.lds.h | 10 +++
> 15 files changed, 234 insertions(+)
> 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 9f30c3a13ed1..bdba7eaa4f65 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 8e9cac75b09e..43426df33092 100644
> --- a/xen/arch/x86/xen.lds.S
> +++ b/xen/arch/x86/xen.lds.S
> @@ -136,6 +136,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 16936418a6e6..4e0bd524dc43 100644
> --- a/xen/common/Kconfig
> +++ b/xen/common/Kconfig
> @@ -1,6 +1,8 @@
>
> menu "Common Features"
>
> +source "common/emul/Kconfig"
> +
> config COMPAT
> bool
> help
> diff --git a/xen/common/Makefile b/xen/common/Makefile
> index c316957fcb36..c0734480ee4b 100644
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> obj-$(CONFIG_IOREQ_SERVER) += dm.o
> obj-y += domain.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..670682102c13
> --- /dev/null
> +++ b/xen/common/emul/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HAS_VUART) += vuart/
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> new file mode 100644
> index 000000000000..02f7dd6dc1a1
> --- /dev/null
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -0,0 +1,6 @@
> +config HAS_VUART
> + 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..c6400b001e85
> --- /dev/null
> +++ b/xen/common/emul/vuart/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_HAS_VUART) += vuart.o
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> new file mode 100644
> index 000000000000..14a7f8bd8b79
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -0,0 +1,112 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#include <xen/errno.h>
> +#include <xen/sched.h>
> +#include <xen/vuart.h>
> +
> +#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
> +
> +#define for_each_vuart(vdev) \
> + for (unsigned __i = 0; \
> + __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
> + __i++)
Could you possibly do:
#define for_each_vuart(vdev) \
for ( vdev = __start_vuart_array; vdev < __start_vuart_end: vdev++ )
To avoid the extra __i variable in the inner scope?
> +
> +extern const struct vuart_ops *const __start_vuart_array[];
> +extern const struct vuart_ops *const __start_vuart_end[];
Naming here looks weird, why not __vuart_{start,end}? Or
__{start,end}_vuart_array.
> +
> +int vuart_add_node(struct domain *d, const void *node)
What's the purpose of this function? There's no comment here or in
the declaration to figure out what's the purpose. It's also not being
called, which makes it unreachable code. MISRA will likely complain
about it?
> +{
> + const struct vuart_ops *vdev;
> + int rc;
> +
> + for_each_vuart(vdev)
> + {
> + if ( !vdev->add_node )
> + continue;
> +
> + rc = vdev->add_node(d, node);
> + if ( rc )
> + return rc;
> + }
> +
> + return 0;
> +}
> +
> +int vuart_init(struct domain *d, struct vuart_params *params)
> +{
> + const struct vuart_ops *vdev;
> + int rc;
> +
> + if ( !domain_has_vuart(d) )
> + return 0;
Don't you need the domain_has_vuart() checks in all the handlers?
Otherwise you are pointlessly iterating and calling handlers that
won't do anything?
> +
> + for_each_vuart(vdev)
> + {
> + rc = vdev->init(d, params);
> + if ( rc )
> + return rc;
> + }
> +
> + d->console.input_allowed = true;
> +
> + return 0;
> +}
> +
> +/*
> + * Release any resources taken by UART emulators.
> + *
> + * NB: no flags are cleared, since currently exit() is called only during
> + * domain destroy.
> + */
> +void vuart_deinit(struct domain *d)
> +{
> + const struct vuart_ops *vdev;
> +
> + for_each_vuart(vdev)
> + vdev->deinit(d);
> +}
> +
> +void vuart_dump_state(const struct domain *d)
> +{
> + const struct vuart_ops *vdev;
> +
> + for_each_vuart(vdev)
> + vdev->dump_state(d);
> +}
> +
> +/*
> + * Put character to the first suitable emulated UART's FIFO.
> + */
> +int vuart_put_rx(struct domain *d, char c)
> +{
> + const struct vuart_ops *vdev = NULL;
> +
> + ASSERT(domain_has_vuart(d));
> +
> + for_each_vuart(vdev)
> + if ( vdev->put_rx )
> + break;
> +
> + return vdev ? vdev->put_rx(d, c) : -ENODEV;
The above functions seems to be designed to deal with multiple vUARTs
in-use by the same domain, while the put_rx code gives up as soon as
it finds an implementation that has the ->put_rx() hook set.
> +}
> +
> +bool domain_has_vuart(const struct domain *d)
> +{
> + uint32_t mask = 0;
> +
> + return !!(d->emulation_flags & mask);
> +}
> +
> +/*
> + * 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 eccd97c565c6..af427d25dc0d 100644
> --- a/xen/common/keyhandler.c
> +++ b/xen/common/keyhandler.c
> @@ -22,6 +22,7 @@
> #include <xen/mm.h>
> #include <xen/watchdog.h>
> #include <xen/init.h>
> +#include <xen/vuart.h>
> #include <asm/div64.h>
>
> static unsigned char keypress_key;
> @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> v->periodic_period / 1000000);
> }
> }
> +
> + vuart_dump_state(d);
> }
>
> for_each_domain ( d )
> diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> index 963c7b043cd8..93254979817b 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>
> @@ -601,6 +602,7 @@ static void __serial_rx(char c)
> /*
> * Deliver input to the hardware domain buffer, unless it is
> * already full.
> + * NB: must be the first check: hardware domain may have emulated UART.
> */
> if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
> serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
> @@ -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/vuart.h b/xen/include/xen/vuart.h
> new file mode 100644
> index 000000000000..e843026df4b1
> --- /dev/null
> +++ b/xen/include/xen/vuart.h
> @@ -0,0 +1,84 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * UART emulator framework.
> + *
> + * Copyright 2025 Ford Motor Company
> + */
> +
> +#ifndef XEN_VUART_H
> +#define XEN_VUART_H
> +
> +#include <public/xen.h>
> +#include <public/event_channel.h>
> +#include <xen/types.h>
> +
> +struct vuart_params {
> + domid_t console_domid;
> + gfn_t gfn;
> + evtchn_port_t evtchn;
I think this should be empty initially, as there's no implementation
that uses the hooks it's completely opaque to me what should be placed
in vuart_params.
> +};
> +
> +struct vuart_ops {
> + int (*add_node)(struct domain *d, const void *node);
> + int (*init)(struct domain *d, struct vuart_params *params);
> + void (*deinit)(struct domain *d);
> + void (*dump_state)(const struct domain *d);
> + int (*put_rx)(struct domain *d, char c);
We haven't been very good at this, but ideally hooks should be
documented as to which task they are expected to perform, so that
future implementations have some initial help in understanding how
this is supposed to work.
> +};
> +
> +#define VUART_REGISTER(name, x) \
> + static const struct vuart_ops *const __name##_entry \
> + __used_section(".data.vuart." #name) = (x);
For vPCI we are moving this to a different section, I think you want
to use ".data.rel.ro.vuart" here.
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators
2025-08-06 14:24 ` Roger Pau Monné
@ 2025-08-07 19:12 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 19:12 UTC (permalink / raw)
To: Roger Pau Monné
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Wed, Aug 06, 2025 at 04:24:52PM +0200, Roger Pau Monné wrote:
> On Thu, Jul 31, 2025 at 07:21:49PM +0000, dmkhn@proton.me 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_HAS_VUART, 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.
> >
> > 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.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Changes since v3:
> > - new patch
> > - original patch from ML: https://lore.kernel.org/xen-devel/20250624035443.344099-16-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 | 112 +++++++++++++++++++++++++++++++++
> > xen/common/keyhandler.c | 3 +
> > xen/drivers/char/console.c | 4 ++
> > xen/include/xen/vuart.h | 84 +++++++++++++++++++++++++
> > xen/include/xen/xen.lds.h | 10 +++
> > 15 files changed, 234 insertions(+)
> > 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 9f30c3a13ed1..bdba7eaa4f65 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 8e9cac75b09e..43426df33092 100644
> > --- a/xen/arch/x86/xen.lds.S
> > +++ b/xen/arch/x86/xen.lds.S
> > @@ -136,6 +136,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 16936418a6e6..4e0bd524dc43 100644
> > --- a/xen/common/Kconfig
> > +++ b/xen/common/Kconfig
> > @@ -1,6 +1,8 @@
> >
> > menu "Common Features"
> >
> > +source "common/emul/Kconfig"
> > +
> > config COMPAT
> > bool
> > help
> > diff --git a/xen/common/Makefile b/xen/common/Makefile
> > index c316957fcb36..c0734480ee4b 100644
> > --- a/xen/common/Makefile
> > +++ b/xen/common/Makefile
> > @@ -11,6 +11,7 @@ obj-$(filter-out $(CONFIG_X86),$(CONFIG_ACPI)) += device.o
> > obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
> > obj-$(CONFIG_IOREQ_SERVER) += dm.o
> > obj-y += domain.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..670682102c13
> > --- /dev/null
> > +++ b/xen/common/emul/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_HAS_VUART) += vuart/
> > diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> > new file mode 100644
> > index 000000000000..02f7dd6dc1a1
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -0,0 +1,6 @@
> > +config HAS_VUART
> > + 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..c6400b001e85
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_HAS_VUART) += vuart.o
> > diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> > new file mode 100644
> > index 000000000000..14a7f8bd8b79
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/vuart.c
> > @@ -0,0 +1,112 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#include <xen/errno.h>
> > +#include <xen/sched.h>
> > +#include <xen/vuart.h>
> > +
> > +#define VUART_ARRAY_SIZE (__start_vuart_end - __start_vuart_array)
> > +
> > +#define for_each_vuart(vdev) \
> > + for (unsigned __i = 0; \
> > + __i < VUART_ARRAY_SIZE && (vdev = __start_vuart_array[__i], 1); \
> > + __i++)
>
> Could you possibly do:
>
> #define for_each_vuart(vdev) \
> for ( vdev = __start_vuart_array; vdev < __start_vuart_end: vdev++ )
>
> To avoid the extra __i variable in the inner scope?
>
> > +
> > +extern const struct vuart_ops *const __start_vuart_array[];
> > +extern const struct vuart_ops *const __start_vuart_end[];
>
> Naming here looks weird, why not __vuart_{start,end}? Or
> __{start,end}_vuart_array.
I overlooked that, thanks.
>
> > +
> > +int vuart_add_node(struct domain *d, const void *node)
>
> What's the purpose of this function? There's no comment here or in
> the declaration to figure out what's the purpose. It's also not being
> called, which makes it unreachable code. MISRA will likely complain
> about it?
That supposed to ge a hook to generate virtual firmware description for the
guest OS. On Arm/RISC-V that would be DT, on x86 that would be DSDT record in
guest's ACPI.
Stefano already asked to drop that until it is actually used, like for Arm's
vUARTs.
Will remove in v5.
>
> > +{
> > + const struct vuart_ops *vdev;
> > + int rc;
> > +
> > + for_each_vuart(vdev)
> > + {
> > + if ( !vdev->add_node )
> > + continue;
> > +
> > + rc = vdev->add_node(d, node);
> > + if ( rc )
> > + return rc;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int vuart_init(struct domain *d, struct vuart_params *params)
> > +{
> > + const struct vuart_ops *vdev;
> > + int rc;
> > +
> > + if ( !domain_has_vuart(d) )
> > + return 0;
>
> Don't you need the domain_has_vuart() checks in all the handlers?
> Otherwise you are pointlessly iterating and calling handlers that
> won't do anything?
All handlers should check for vuart handler (per-vUART data structure)
not being NULL. Will update that code in v5.
>
> > +
> > + for_each_vuart(vdev)
> > + {
> > + rc = vdev->init(d, params);
> > + if ( rc )
> > + return rc;
> > + }
> > +
> > + d->console.input_allowed = true;
> > +
> > + return 0;
> > +}
> > +
> > +/*
> > + * Release any resources taken by UART emulators.
> > + *
> > + * NB: no flags are cleared, since currently exit() is called only during
> > + * domain destroy.
> > + */
> > +void vuart_deinit(struct domain *d)
> > +{
> > + const struct vuart_ops *vdev;
> > +
> > + for_each_vuart(vdev)
> > + vdev->deinit(d);
> > +}
> > +
> > +void vuart_dump_state(const struct domain *d)
> > +{
> > + const struct vuart_ops *vdev;
> > +
> > + for_each_vuart(vdev)
> > + vdev->dump_state(d);
> > +}
> > +
> > +/*
> > + * Put character to the first suitable emulated UART's FIFO.
> > + */
> > +int vuart_put_rx(struct domain *d, char c)
> > +{
> > + const struct vuart_ops *vdev = NULL;
> > +
> > + ASSERT(domain_has_vuart(d));
> > +
> > + for_each_vuart(vdev)
> > + if ( vdev->put_rx )
> > + break;
> > +
> > + return vdev ? vdev->put_rx(d, c) : -ENODEV;
>
> The above functions seems to be designed to deal with multiple vUARTs
> in-use by the same domain, while the put_rx code gives up as soon as
> it finds an implementation that has the ->put_rx() hook set.
Correct, that is to account for hwdom vUART (or just "vuart" on Arm) which by
design has no RX buffer/register emulation.
>
> > +}
> > +
> > +bool domain_has_vuart(const struct domain *d)
> > +{
> > + uint32_t mask = 0;
> > +
> > + return !!(d->emulation_flags & mask);
> > +}
> > +
> > +/*
> > + * 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 eccd97c565c6..af427d25dc0d 100644
> > --- a/xen/common/keyhandler.c
> > +++ b/xen/common/keyhandler.c
> > @@ -22,6 +22,7 @@
> > #include <xen/mm.h>
> > #include <xen/watchdog.h>
> > #include <xen/init.h>
> > +#include <xen/vuart.h>
> > #include <asm/div64.h>
> >
> > static unsigned char keypress_key;
> > @@ -354,6 +355,8 @@ static void cf_check dump_domains(unsigned char key)
> > v->periodic_period / 1000000);
> > }
> > }
> > +
> > + vuart_dump_state(d);
> > }
> >
> > for_each_domain ( d )
> > diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> > index 963c7b043cd8..93254979817b 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>
> > @@ -601,6 +602,7 @@ static void __serial_rx(char c)
> > /*
> > * Deliver input to the hardware domain buffer, unless it is
> > * already full.
> > + * NB: must be the first check: hardware domain may have emulated UART.
> > */
> > if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
> > serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
> > @@ -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/vuart.h b/xen/include/xen/vuart.h
> > new file mode 100644
> > index 000000000000..e843026df4b1
> > --- /dev/null
> > +++ b/xen/include/xen/vuart.h
> > @@ -0,0 +1,84 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * UART emulator framework.
> > + *
> > + * Copyright 2025 Ford Motor Company
> > + */
> > +
> > +#ifndef XEN_VUART_H
> > +#define XEN_VUART_H
> > +
> > +#include <public/xen.h>
> > +#include <public/event_channel.h>
> > +#include <xen/types.h>
> > +
> > +struct vuart_params {
> > + domid_t console_domid;
> > + gfn_t gfn;
> > + evtchn_port_t evtchn;
>
> I think this should be empty initially, as there's no implementation
> that uses the hooks it's completely opaque to me what should be placed
> in vuart_params.
Good point, will do.
>
> > +};
> > +
> > +struct vuart_ops {
> > + int (*add_node)(struct domain *d, const void *node);
> > + int (*init)(struct domain *d, struct vuart_params *params);
> > + void (*deinit)(struct domain *d);
> > + void (*dump_state)(const struct domain *d);
> > + int (*put_rx)(struct domain *d, char c);
>
> We haven't been very good at this, but ideally hooks should be
> documented as to which task they are expected to perform, so that
> future implementations have some initial help in understanding how
> this is supposed to work.
Agreed.
>
> > +};
> > +
> > +#define VUART_REGISTER(name, x) \
> > + static const struct vuart_ops *const __name##_entry \
> > + __used_section(".data.vuart." #name) = (x);
>
> For vPCI we are moving this to a different section, I think you want
> to use ".data.rel.ro.vuart" here.
Ack.
>
> Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
2025-07-31 19:21 ` [PATCH v4 1/8] xen/domain: introduce common emulation flags dmkhn
2025-07-31 19:21 ` [PATCH v4 2/8] emul/vuart: introduce framework for UART emulators dmkhn
@ 2025-07-31 19:21 ` dmkhn
2025-07-31 19:52 ` Grygorii Strashko
` (3 more replies)
2025-07-31 19:22 ` [PATCH v4 4/8] xen/8250-uart: update definitions dmkhn
` (5 subsequent siblings)
8 siblings, 4 replies; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:21 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>
Move IRQ/IOMEM rangesets allocation before arch_domain_create().
That guarantees that arch-specific code could access those rangesets to
register traps for emulation.
It is necessary for those emulators registering trap handlers and ensuring
that emulated IRQs are not shared with the physical IRQs.
Move dom0_setup_permissions() call right after I/O rangesets are allocated.
Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
where MMCFG ranges are initialized.
Not a functional change.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Chanhes since v3:
- new patch
---
xen/arch/x86/dom0_build.c | 26 +++++++++++++++++++++++
xen/arch/x86/hvm/dom0_build.c | 39 -----------------------------------
xen/arch/x86/hvm/hvm.c | 16 ++++++++++++++
xen/common/domain.c | 12 +++++------
4 files changed, 48 insertions(+), 45 deletions(-)
diff --git a/xen/arch/x86/dom0_build.c b/xen/arch/x86/dom0_build.c
index 0b467fd4a4fc..e965f506a3c8 100644
--- a/xen/arch/x86/dom0_build.c
+++ b/xen/arch/x86/dom0_build.c
@@ -471,6 +471,24 @@ static void __init process_dom0_ioports_disable(struct domain *dom0)
}
}
+static void __hwdom_init setup_mmcfg(struct domain *d)
+{
+ unsigned int i;
+ int rc;
+
+ for ( i = 0; i < pci_mmcfg_config_num; i++ )
+ {
+ rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
+ pci_mmcfg_config[i].start_bus_number,
+ pci_mmcfg_config[i].end_bus_number,
+ pci_mmcfg_config[i].pci_segment);
+ if ( rc )
+ printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
+ pci_mmcfg_config[i].address,
+ pci_mmcfg_config[i].pci_segment);
+ }
+}
+
int __init dom0_setup_permissions(struct domain *d)
{
unsigned long mfn;
@@ -480,6 +498,14 @@ int __init dom0_setup_permissions(struct domain *d)
if ( pv_shim )
return 0;
+ /*
+ * MMCFG initialization must be performed before setting domain
+ * permissions, as the MCFG areas must not be part of the domain IOMEM
+ * accessible regions.
+ */
+ if ( is_hvm_domain(d) )
+ setup_mmcfg(d);
+
/* The hardware domain is initially permitted full I/O capabilities. */
rc = ioports_permit_access(d, 0, 0xFFFF);
rc |= iomem_permit_access(d, 0UL,
diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
index 5551f9044836..6f47c9eeeaa6 100644
--- a/xen/arch/x86/hvm/dom0_build.c
+++ b/xen/arch/x86/hvm/dom0_build.c
@@ -1310,24 +1310,6 @@ static int __init pvh_setup_acpi(struct domain *d, paddr_t start_info)
return 0;
}
-static void __hwdom_init pvh_setup_mmcfg(struct domain *d)
-{
- unsigned int i;
- int rc;
-
- for ( i = 0; i < pci_mmcfg_config_num; i++ )
- {
- rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
- pci_mmcfg_config[i].start_bus_number,
- pci_mmcfg_config[i].end_bus_number,
- pci_mmcfg_config[i].pci_segment);
- if ( rc )
- printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
- pci_mmcfg_config[i].address,
- pci_mmcfg_config[i].pci_segment);
- }
-}
-
int __init dom0_construct_pvh(const struct boot_domain *bd)
{
paddr_t entry, start_info;
@@ -1339,27 +1321,6 @@ int __init dom0_construct_pvh(const struct boot_domain *bd)
if ( bd->kernel == NULL )
panic("Missing kernel boot module for %pd construction\n", d);
- if ( is_hardware_domain(d) )
- {
- /*
- * MMCFG initialization must be performed before setting domain
- * permissions, as the MCFG areas must not be part of the domain IOMEM
- * accessible regions.
- */
- pvh_setup_mmcfg(d);
-
- /*
- * 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.
- */
- rc = dom0_setup_permissions(d);
- if ( rc )
- {
- printk("%pd unable to setup permissions: %d\n", d, rc);
- return rc;
- }
- }
-
/*
* Craft dom0 physical memory map and set the paging allocation. This must
* be done before the iommu initializion, since iommu initialization code
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index cb8ecd050d41..b7edb1d6555d 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -35,6 +35,7 @@
#include <asm/hap.h>
#include <asm/current.h>
#include <asm/debugreg.h>
+#include <asm/dom0_build.h>
#include <asm/e820.h>
#include <asm/regs.h>
#include <asm/cpufeature.h>
@@ -651,6 +652,17 @@ int hvm_domain_initialise(struct domain *d,
goto fail1;
}
memset(d->arch.hvm.io_bitmap, ~0, HVM_IOBITMAP_SIZE);
+
+ /*
+ * 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.
+ */
+ rc = dom0_setup_permissions(d);
+ if ( rc )
+ {
+ printk("%pd unable to setup permissions: %d\n", d, rc);
+ goto fail1;
+ }
}
else
d->arch.hvm.io_bitmap = hvm_io_bitmap;
@@ -680,6 +692,10 @@ int hvm_domain_initialise(struct domain *d,
break;
}
+ BUG_ON(!d->irq_caps);
+ BUG_ON(!d->iomem_caps);
+ BUG_ON(!d->arch.ioport_caps);
+
vpic_init(d);
rc = vioapic_init(d);
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 5241a1629eeb..187637e8114a 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -944,6 +944,12 @@ struct domain *domain_create(domid_t domid,
radix_tree_init(&d->pirq_tree);
#endif
+ 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 )
+ goto fail;
+
if ( (err = arch_domain_create(d, config, flags)) != 0 )
goto fail;
init_status |= INIT_arch;
@@ -951,12 +957,6 @@ struct domain *domain_create(domid_t domid,
watchdog_domain_init(d);
init_status |= INIT_watchdog;
- 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 )
- goto fail;
-
if ( (err = xsm_domain_create(XSM_HOOK, d, config->ssidref)) != 0 )
goto fail;
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 19:21 ` [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization dmkhn
@ 2025-07-31 19:52 ` Grygorii Strashko
2025-07-31 20:21 ` dmkhn
2025-07-31 23:20 ` Stefano Stabellini
` (2 subsequent siblings)
3 siblings, 1 reply; 61+ messages in thread
From: Grygorii Strashko @ 2025-07-31 19:52 UTC (permalink / raw)
To: xen-devel
Hi Denis,
On 31.07.25 22:21, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Move IRQ/IOMEM rangesets allocation before arch_domain_create().
>
> That guarantees that arch-specific code could access those rangesets to
> register traps for emulation.
>
> It is necessary for those emulators registering trap handlers and ensuring
> that emulated IRQs are not shared with the physical IRQs.
>
> Move dom0_setup_permissions() call right after I/O rangesets are allocated.
>
> Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> where MMCFG ranges are initialized.
>
> Not a functional change.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Chanhes since v3:
> - new patch
> ---
> xen/arch/x86/dom0_build.c | 26 +++++++++++++++++++++++
> xen/arch/x86/hvm/dom0_build.c | 39 -----------------------------------
> xen/arch/x86/hvm/hvm.c | 16 ++++++++++++++
> xen/common/domain.c | 12 +++++------
> 4 files changed, 48 insertions(+), 45 deletions(-)
>
> diff --git a/xen/arch/x86/dom0_build.c b/xen/arch/x86/dom0_build.c
> index 0b467fd4a4fc..e965f506a3c8 100644
> --- a/xen/arch/x86/dom0_build.c
> +++ b/xen/arch/x86/dom0_build.c
> @@ -471,6 +471,24 @@ static void __init process_dom0_ioports_disable(struct domain *dom0)
> }
> }
>
> +static void __hwdom_init setup_mmcfg(struct domain *d)
> +{
> + unsigned int i;
> + int rc;
> +
> + for ( i = 0; i < pci_mmcfg_config_num; i++ )
> + {
> + rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
> + pci_mmcfg_config[i].start_bus_number,
> + pci_mmcfg_config[i].end_bus_number,
> + pci_mmcfg_config[i].pci_segment);
> + if ( rc )
> + printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
> + pci_mmcfg_config[i].address,
> + pci_mmcfg_config[i].pci_segment);
> + }
> +}
> +
> int __init dom0_setup_permissions(struct domain *d)
It could be i'm missing smth, but ^ function is __init while ...
> {
> unsigned long mfn;
> @@ -480,6 +498,14 @@ int __init dom0_setup_permissions(struct domain *d)
> if ( pv_shim )
> return 0;
>
> + /*
> + * MMCFG initialization must be performed before setting domain
> + * permissions, as the MCFG areas must not be part of the domain IOMEM
> + * accessible regions.
> + */
> + if ( is_hvm_domain(d) )
> + setup_mmcfg(d);
> +
> /* The hardware domain is initially permitted full I/O capabilities. */
> rc = ioports_permit_access(d, 0, 0xFFFF);
> rc |= iomem_permit_access(d, 0UL,
> diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
> index 5551f9044836..6f47c9eeeaa6 100644
> --- a/xen/arch/x86/hvm/dom0_build.c
> +++ b/xen/arch/x86/hvm/dom0_build.c
> @@ -1310,24 +1310,6 @@ static int __init pvh_setup_acpi(struct domain *d, paddr_t start_info)
> return 0;
> }
>
> -static void __hwdom_init pvh_setup_mmcfg(struct domain *d)
> -{
> - unsigned int i;
> - int rc;
> -
> - for ( i = 0; i < pci_mmcfg_config_num; i++ )
> - {
> - rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
> - pci_mmcfg_config[i].start_bus_number,
> - pci_mmcfg_config[i].end_bus_number,
> - pci_mmcfg_config[i].pci_segment);
> - if ( rc )
> - printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
> - pci_mmcfg_config[i].address,
> - pci_mmcfg_config[i].pci_segment);
> - }
> -}
> -
> int __init dom0_construct_pvh(const struct boot_domain *bd)
> {
> paddr_t entry, start_info;
> @@ -1339,27 +1321,6 @@ int __init dom0_construct_pvh(const struct boot_domain *bd)
> if ( bd->kernel == NULL )
> panic("Missing kernel boot module for %pd construction\n", d);
>
> - if ( is_hardware_domain(d) )
> - {
> - /*
> - * MMCFG initialization must be performed before setting domain
> - * permissions, as the MCFG areas must not be part of the domain IOMEM
> - * accessible regions.
> - */
> - pvh_setup_mmcfg(d);
> -
> - /*
> - * 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.
> - */
> - rc = dom0_setup_permissions(d);
> - if ( rc )
> - {
> - printk("%pd unable to setup permissions: %d\n", d, rc);
> - return rc;
> - }
> - }
> -
> /*
> * Craft dom0 physical memory map and set the paging allocation. This must
> * be done before the iommu initializion, since iommu initialization code
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index cb8ecd050d41..b7edb1d6555d 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -35,6 +35,7 @@
> #include <asm/hap.h>
> #include <asm/current.h>
> #include <asm/debugreg.h>
> +#include <asm/dom0_build.h>
> #include <asm/e820.h>
> #include <asm/regs.h>
> #include <asm/cpufeature.h>
> @@ -651,6 +652,17 @@ int hvm_domain_initialise(struct domain *d,
> goto fail1;
> }
> memset(d->arch.hvm.io_bitmap, ~0, HVM_IOBITMAP_SIZE);
> +
> + /*
> + * 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.
> + */
> + rc = dom0_setup_permissions(d);
... here hvm_domain_initialise() is not __init?
[...]
--
Best regards,
-grygorii
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 19:52 ` Grygorii Strashko
@ 2025-07-31 20:21 ` dmkhn
2025-08-01 2:57 ` dmkhn
0 siblings, 1 reply; 61+ messages in thread
From: dmkhn @ 2025-07-31 20:21 UTC (permalink / raw)
To: Grygorii Strashko; +Cc: xen-devel
On Thu, Jul 31, 2025 at 10:52:08PM +0300, Grygorii Strashko wrote:
> Hi Denis,
>
> On 31.07.25 22:21, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Move IRQ/IOMEM rangesets allocation before arch_domain_create().
> >
> > That guarantees that arch-specific code could access those rangesets to
> > register traps for emulation.
> >
> > It is necessary for those emulators registering trap handlers and ensuring
> > that emulated IRQs are not shared with the physical IRQs.
> >
> > Move dom0_setup_permissions() call right after I/O rangesets are allocated.
> >
> > Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> > where MMCFG ranges are initialized.
> >
> > Not a functional change.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Chanhes since v3:
> > - new patch
> > ---
> > xen/arch/x86/dom0_build.c | 26 +++++++++++++++++++++++
> > xen/arch/x86/hvm/dom0_build.c | 39 -----------------------------------
> > xen/arch/x86/hvm/hvm.c | 16 ++++++++++++++
> > xen/common/domain.c | 12 +++++------
> > 4 files changed, 48 insertions(+), 45 deletions(-)
> >
> > diff --git a/xen/arch/x86/dom0_build.c b/xen/arch/x86/dom0_build.c
> > index 0b467fd4a4fc..e965f506a3c8 100644
> > --- a/xen/arch/x86/dom0_build.c
> > +++ b/xen/arch/x86/dom0_build.c
> > @@ -471,6 +471,24 @@ static void __init process_dom0_ioports_disable(struct domain *dom0)
> > }
> > }
> >
> > +static void __hwdom_init setup_mmcfg(struct domain *d)
> > +{
> > + unsigned int i;
> > + int rc;
> > +
> > + for ( i = 0; i < pci_mmcfg_config_num; i++ )
> > + {
> > + rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
> > + pci_mmcfg_config[i].start_bus_number,
> > + pci_mmcfg_config[i].end_bus_number,
> > + pci_mmcfg_config[i].pci_segment);
> > + if ( rc )
> > + printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
> > + pci_mmcfg_config[i].address,
> > + pci_mmcfg_config[i].pci_segment);
> > + }
> > +}
> > +
> > int __init dom0_setup_permissions(struct domain *d)
>
> It could be i'm missing smth, but ^ function is __init while ...
>
> > {
> > unsigned long mfn;
> > @@ -480,6 +498,14 @@ int __init dom0_setup_permissions(struct domain *d)
> > if ( pv_shim )
> > return 0;
> >
> > + /*
> > + * MMCFG initialization must be performed before setting domain
> > + * permissions, as the MCFG areas must not be part of the domain IOMEM
> > + * accessible regions.
> > + */
> > + if ( is_hvm_domain(d) )
> > + setup_mmcfg(d);
> > +
> > /* The hardware domain is initially permitted full I/O capabilities. */
> > rc = ioports_permit_access(d, 0, 0xFFFF);
> > rc |= iomem_permit_access(d, 0UL,
> > diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
> > index 5551f9044836..6f47c9eeeaa6 100644
> > --- a/xen/arch/x86/hvm/dom0_build.c
> > +++ b/xen/arch/x86/hvm/dom0_build.c
> > @@ -1310,24 +1310,6 @@ static int __init pvh_setup_acpi(struct domain *d, paddr_t start_info)
> > return 0;
> > }
> >
> > -static void __hwdom_init pvh_setup_mmcfg(struct domain *d)
> > -{
> > - unsigned int i;
> > - int rc;
> > -
> > - for ( i = 0; i < pci_mmcfg_config_num; i++ )
> > - {
> > - rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
> > - pci_mmcfg_config[i].start_bus_number,
> > - pci_mmcfg_config[i].end_bus_number,
> > - pci_mmcfg_config[i].pci_segment);
> > - if ( rc )
> > - printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
> > - pci_mmcfg_config[i].address,
> > - pci_mmcfg_config[i].pci_segment);
> > - }
> > -}
> > -
> > int __init dom0_construct_pvh(const struct boot_domain *bd)
> > {
> > paddr_t entry, start_info;
> > @@ -1339,27 +1321,6 @@ int __init dom0_construct_pvh(const struct boot_domain *bd)
> > if ( bd->kernel == NULL )
> > panic("Missing kernel boot module for %pd construction\n", d);
> >
> > - if ( is_hardware_domain(d) )
> > - {
> > - /*
> > - * MMCFG initialization must be performed before setting domain
> > - * permissions, as the MCFG areas must not be part of the domain IOMEM
> > - * accessible regions.
> > - */
> > - pvh_setup_mmcfg(d);
> > -
> > - /*
> > - * 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.
> > - */
> > - rc = dom0_setup_permissions(d);
> > - if ( rc )
> > - {
> > - printk("%pd unable to setup permissions: %d\n", d, rc);
> > - return rc;
> > - }
> > - }
> > -
> > /*
> > * Craft dom0 physical memory map and set the paging allocation. This must
> > * be done before the iommu initializion, since iommu initialization code
> > diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> > index cb8ecd050d41..b7edb1d6555d 100644
> > --- a/xen/arch/x86/hvm/hvm.c
> > +++ b/xen/arch/x86/hvm/hvm.c
> > @@ -35,6 +35,7 @@
> > #include <asm/hap.h>
> > #include <asm/current.h>
> > #include <asm/debugreg.h>
> > +#include <asm/dom0_build.h>
> > #include <asm/e820.h>
> > #include <asm/regs.h>
> > #include <asm/cpufeature.h>
> > @@ -651,6 +652,17 @@ int hvm_domain_initialise(struct domain *d,
> > goto fail1;
> > }
> > memset(d->arch.hvm.io_bitmap, ~0, HVM_IOBITMAP_SIZE);
> > +
> > + /*
> > + * 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.
> > + */
> > + rc = dom0_setup_permissions(d);
>
> ... here hvm_domain_initialise() is not __init?
No, you're right, I missed this, thanks!
Good catch!
>
> [...]
>
>
> --
> Best regards,
> -grygorii
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 20:21 ` dmkhn
@ 2025-08-01 2:57 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-01 2:57 UTC (permalink / raw)
To: Grygorii Strashko, xen-devel
On Thu, Jul 31, 2025 at 08:21:24PM +0000, dmkhn@proton.me wrote:
> On Thu, Jul 31, 2025 at 10:52:08PM +0300, Grygorii Strashko wrote:
> > Hi Denis,
> >
> > On 31.07.25 22:21, dmkhn@proton.me wrote:
> > > From: Denis Mukhin <dmukhin@ford.com>
> > >
> > > Move IRQ/IOMEM rangesets allocation before arch_domain_create().
> > >
> > > That guarantees that arch-specific code could access those rangesets to
> > > register traps for emulation.
> > >
> > > It is necessary for those emulators registering trap handlers and ensuring
> > > that emulated IRQs are not shared with the physical IRQs.
> > >
> > > Move dom0_setup_permissions() call right after I/O rangesets are allocated.
> > >
> > > Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> > > where MMCFG ranges are initialized.
> > >
> > > Not a functional change.
> > >
> > > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > > ---
> > > Chanhes since v3:
> > > - new patch
> > > ---
> > > xen/arch/x86/dom0_build.c | 26 +++++++++++++++++++++++
> > > xen/arch/x86/hvm/dom0_build.c | 39 -----------------------------------
> > > xen/arch/x86/hvm/hvm.c | 16 ++++++++++++++
> > > xen/common/domain.c | 12 +++++------
> > > 4 files changed, 48 insertions(+), 45 deletions(-)
> > >
> > > diff --git a/xen/arch/x86/dom0_build.c b/xen/arch/x86/dom0_build.c
> > > index 0b467fd4a4fc..e965f506a3c8 100644
> > > --- a/xen/arch/x86/dom0_build.c
> > > +++ b/xen/arch/x86/dom0_build.c
> > > @@ -471,6 +471,24 @@ static void __init process_dom0_ioports_disable(struct domain *dom0)
> > > }
> > > }
> > >
> > > +static void __hwdom_init setup_mmcfg(struct domain *d)
> > > +{
> > > + unsigned int i;
> > > + int rc;
> > > +
> > > + for ( i = 0; i < pci_mmcfg_config_num; i++ )
> > > + {
> > > + rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
> > > + pci_mmcfg_config[i].start_bus_number,
> > > + pci_mmcfg_config[i].end_bus_number,
> > > + pci_mmcfg_config[i].pci_segment);
> > > + if ( rc )
> > > + printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
> > > + pci_mmcfg_config[i].address,
> > > + pci_mmcfg_config[i].pci_segment);
> > > + }
> > > +}
> > > +
> > > int __init dom0_setup_permissions(struct domain *d)
> >
> > It could be i'm missing smth, but ^ function is __init while ...
> >
> > > {
> > > unsigned long mfn;
> > > @@ -480,6 +498,14 @@ int __init dom0_setup_permissions(struct domain *d)
> > > if ( pv_shim )
> > > return 0;
> > >
> > > + /*
> > > + * MMCFG initialization must be performed before setting domain
> > > + * permissions, as the MCFG areas must not be part of the domain IOMEM
> > > + * accessible regions.
> > > + */
> > > + if ( is_hvm_domain(d) )
> > > + setup_mmcfg(d);
> > > +
> > > /* The hardware domain is initially permitted full I/O capabilities. */
> > > rc = ioports_permit_access(d, 0, 0xFFFF);
> > > rc |= iomem_permit_access(d, 0UL,
> > > diff --git a/xen/arch/x86/hvm/dom0_build.c b/xen/arch/x86/hvm/dom0_build.c
> > > index 5551f9044836..6f47c9eeeaa6 100644
> > > --- a/xen/arch/x86/hvm/dom0_build.c
> > > +++ b/xen/arch/x86/hvm/dom0_build.c
> > > @@ -1310,24 +1310,6 @@ static int __init pvh_setup_acpi(struct domain *d, paddr_t start_info)
> > > return 0;
> > > }
> > >
> > > -static void __hwdom_init pvh_setup_mmcfg(struct domain *d)
> > > -{
> > > - unsigned int i;
> > > - int rc;
> > > -
> > > - for ( i = 0; i < pci_mmcfg_config_num; i++ )
> > > - {
> > > - rc = register_vpci_mmcfg_handler(d, pci_mmcfg_config[i].address,
> > > - pci_mmcfg_config[i].start_bus_number,
> > > - pci_mmcfg_config[i].end_bus_number,
> > > - pci_mmcfg_config[i].pci_segment);
> > > - if ( rc )
> > > - printk("Unable to setup MMCFG handler at %#lx for segment %u\n",
> > > - pci_mmcfg_config[i].address,
> > > - pci_mmcfg_config[i].pci_segment);
> > > - }
> > > -}
> > > -
> > > int __init dom0_construct_pvh(const struct boot_domain *bd)
> > > {
> > > paddr_t entry, start_info;
> > > @@ -1339,27 +1321,6 @@ int __init dom0_construct_pvh(const struct boot_domain *bd)
> > > if ( bd->kernel == NULL )
> > > panic("Missing kernel boot module for %pd construction\n", d);
> > >
> > > - if ( is_hardware_domain(d) )
> > > - {
> > > - /*
> > > - * MMCFG initialization must be performed before setting domain
> > > - * permissions, as the MCFG areas must not be part of the domain IOMEM
> > > - * accessible regions.
> > > - */
> > > - pvh_setup_mmcfg(d);
> > > -
> > > - /*
> > > - * 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.
> > > - */
> > > - rc = dom0_setup_permissions(d);
> > > - if ( rc )
> > > - {
> > > - printk("%pd unable to setup permissions: %d\n", d, rc);
> > > - return rc;
> > > - }
> > > - }
> > > -
> > > /*
> > > * Craft dom0 physical memory map and set the paging allocation. This must
> > > * be done before the iommu initializion, since iommu initialization code
> > > diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> > > index cb8ecd050d41..b7edb1d6555d 100644
> > > --- a/xen/arch/x86/hvm/hvm.c
> > > +++ b/xen/arch/x86/hvm/hvm.c
> > > @@ -35,6 +35,7 @@
> > > #include <asm/hap.h>
> > > #include <asm/current.h>
> > > #include <asm/debugreg.h>
> > > +#include <asm/dom0_build.h>
> > > #include <asm/e820.h>
> > > #include <asm/regs.h>
> > > #include <asm/cpufeature.h>
> > > @@ -651,6 +652,17 @@ int hvm_domain_initialise(struct domain *d,
> > > goto fail1;
> > > }
> > > memset(d->arch.hvm.io_bitmap, ~0, HVM_IOBITMAP_SIZE);
> > > +
> > > + /*
> > > + * 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.
> > > + */
> > > + rc = dom0_setup_permissions(d);
> >
> > ... here hvm_domain_initialise() is not __init?
>
> No, you're right, I missed this, thanks!
>
> Good catch!
So addressing it will require some code movement, like
dom0_setup_permissions() outside of arch/x86/dom0_build.c...
>
> >
> > [...]
> >
> >
> > --
> > Best regards,
> > -grygorii
> >
>
>
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 19:21 ` [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization dmkhn
2025-07-31 19:52 ` Grygorii Strashko
@ 2025-07-31 23:20 ` Stefano Stabellini
2025-08-04 10:20 ` Jan Beulich
2025-08-06 14:37 ` Roger Pau Monné
3 siblings, 0 replies; 61+ messages in thread
From: Stefano Stabellini @ 2025-07-31 23:20 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Move IRQ/IOMEM rangesets allocation before arch_domain_create().
>
> That guarantees that arch-specific code could access those rangesets to
> register traps for emulation.
>
> It is necessary for those emulators registering trap handlers and ensuring
> that emulated IRQs are not shared with the physical IRQs.
>
> Move dom0_setup_permissions() call right after I/O rangesets are allocated.
>
> Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> where MMCFG ranges are initialized.
>
> Not a functional change.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 19:21 ` [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization dmkhn
2025-07-31 19:52 ` Grygorii Strashko
2025-07-31 23:20 ` Stefano Stabellini
@ 2025-08-04 10:20 ` Jan Beulich
2025-08-07 18:59 ` dmkhn
2025-08-06 14:37 ` Roger Pau Monné
3 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 10:20 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:21, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Move IRQ/IOMEM rangesets allocation before arch_domain_create().
>
> That guarantees that arch-specific code could access those rangesets to
> register traps for emulation.
>
> It is necessary for those emulators registering trap handlers and ensuring
> that emulated IRQs are not shared with the physical IRQs.
>
> Move dom0_setup_permissions() call right after I/O rangesets are allocated.
>
> Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> where MMCFG ranges are initialized.
And this is just because you like doing so? I ask because first and foremost
I can't make the connection between this and the purpose of this patch.
> --- a/xen/arch/x86/dom0_build.c
> +++ b/xen/arch/x86/dom0_build.c
> @@ -471,6 +471,24 @@ static void __init process_dom0_ioports_disable(struct domain *dom0)
> }
> }
>
> +static void __hwdom_init setup_mmcfg(struct domain *d)
Why __hwdom_init when ...
> @@ -480,6 +498,14 @@ int __init dom0_setup_permissions(struct domain *d)
> if ( pv_shim )
> return 0;
>
> + /*
> + * MMCFG initialization must be performed before setting domain
> + * permissions, as the MCFG areas must not be part of the domain IOMEM
> + * accessible regions.
> + */
> + if ( is_hvm_domain(d) )
> + setup_mmcfg(d);
... the sole caller is __init?
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-08-04 10:20 ` Jan Beulich
@ 2025-08-07 18:59 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 18:59 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 04, 2025 at 12:20:11PM +0200, Jan Beulich wrote:
> On 31.07.2025 21:21, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Move IRQ/IOMEM rangesets allocation before arch_domain_create().
> >
> > That guarantees that arch-specific code could access those rangesets to
> > register traps for emulation.
> >
> > It is necessary for those emulators registering trap handlers and ensuring
> > that emulated IRQs are not shared with the physical IRQs.
> >
> > Move dom0_setup_permissions() call right after I/O rangesets are allocated.
> >
> > Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> > where MMCFG ranges are initialized.
>
> And this is just because you like doing so? I ask because first and foremost
> I can't make the connection between this and the purpose of this patch.
No, that is a bad leftover from the MMIO-based emulator.
>
> > --- a/xen/arch/x86/dom0_build.c
> > +++ b/xen/arch/x86/dom0_build.c
> > @@ -471,6 +471,24 @@ static void __init process_dom0_ioports_disable(struct domain *dom0)
> > }
> > }
> >
> > +static void __hwdom_init setup_mmcfg(struct domain *d)
>
> Why __hwdom_init when ...
>
> > @@ -480,6 +498,14 @@ int __init dom0_setup_permissions(struct domain *d)
> > if ( pv_shim )
> > return 0;
> >
> > + /*
> > + * MMCFG initialization must be performed before setting domain
> > + * permissions, as the MCFG areas must not be part of the domain IOMEM
> > + * accessible regions.
> > + */
> > + if ( is_hvm_domain(d) )
> > + setup_mmcfg(d);
>
> ... the sole caller is __init?
>
> Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-07-31 19:21 ` [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization dmkhn
` (2 preceding siblings ...)
2025-08-04 10:20 ` Jan Beulich
@ 2025-08-06 14:37 ` Roger Pau Monné
2025-08-07 18:57 ` dmkhn
3 siblings, 1 reply; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 14:37 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Thu, Jul 31, 2025 at 07:21:54PM +0000, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Move IRQ/IOMEM rangesets allocation before arch_domain_create().
>
> That guarantees that arch-specific code could access those rangesets to
> register traps for emulation.
>
> It is necessary for those emulators registering trap handlers and ensuring
> that emulated IRQs are not shared with the physical IRQs.
>
> Move dom0_setup_permissions() call right after I/O rangesets are allocated.
>
> Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> where MMCFG ranges are initialized.
I'm a bit puzzled by this, you don't need I/O permission to setup
traps. You can setup traps everywhere, the I/O rangesets control
whether a domain can access the physical resource, not whether
accesses can be emulated.
The dependency between MMCFG registration (pvh_setup_mmcfg()) and
calling dom0_setup_permissions() is because the later consumes the
MMCFG ranges added by the former to mark them as not accessible by
a PVH dom0.
I think you don't need this for vUARTS, as the ports are know at build
time, and hence you can block access to them without requiring the
actual vUART to be initialized.
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization
2025-08-06 14:37 ` Roger Pau Monné
@ 2025-08-07 18:57 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 18:57 UTC (permalink / raw)
To: Roger Pau Monné
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Wed, Aug 06, 2025 at 04:37:23PM +0200, Roger Pau Monné wrote:
> On Thu, Jul 31, 2025 at 07:21:54PM +0000, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Move IRQ/IOMEM rangesets allocation before arch_domain_create().
> >
> > That guarantees that arch-specific code could access those rangesets to
> > register traps for emulation.
> >
> > It is necessary for those emulators registering trap handlers and ensuring
> > that emulated IRQs are not shared with the physical IRQs.
> >
> > Move dom0_setup_permissions() call right after I/O rangesets are allocated.
> >
> > Move pvh_setup_mmcfg() inside dom0_setup_permissions() close to the place
> > where MMCFG ranges are initialized.
>
> I'm a bit puzzled by this, you don't need I/O permission to setup
> traps. You can setup traps everywhere, the I/O rangesets control
> whether a domain can access the physical resource, not whether
> accesses can be emulated.
I do not want to setup traps if there's physical I/O range owned by the same
domain already. Emulator will report a misconfiguration in this case.
>
> The dependency between MMCFG registration (pvh_setup_mmcfg()) and
> calling dom0_setup_permissions() is because the later consumes the
> MMCFG ranges added by the former to mark them as not accessible by
> a PVH dom0.
>
> I think you don't need this for vUARTS, as the ports are know at build
> time, and hence you can block access to them without requiring the
> actual vUART to be initialized.
Oh, I see now why I have it: that spilled from MMIO-based UART.
What I need is I/O ports and IRQs rangesets allocation before emulator
initialization.
>
> Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 4/8] xen/8250-uart: update definitions
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
` (2 preceding siblings ...)
2025-07-31 19:21 ` [PATCH v4 3/8] x86/domain: allocate d->{iomem,irq}_caps before arch-specific initialization dmkhn
@ 2025-07-31 19:22 ` dmkhn
2025-07-31 23:23 ` Stefano Stabellini
2025-08-04 10:23 ` Jan Beulich
2025-07-31 19:22 ` [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86) dmkhn
` (4 subsequent siblings)
8 siblings, 2 replies; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:22 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 v3:
- feedback addressed
- made use of new UART_MCR_XXX bits in ns16550 driver
- Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-19-c5d36b31d66c@ford.com/
---
xen/drivers/char/ns16550.c | 6 ++---
xen/include/xen/8250-uart.h | 50 ++++++++++++++++++++++++++++++-------
2 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index df7fff7f81df..a899711e2a8b 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -739,9 +739,9 @@ static int __init check_existence(struct ns16550 *uart)
* 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..bc11cdc376c9 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) /* OUT1: interrupt mask */
+#define UART_MCR_OUT2 BIT(3, U) /* OUT2: interrupt mask */
+#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 CTS */
+#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.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 4/8] xen/8250-uart: update definitions
2025-07-31 19:22 ` [PATCH v4 4/8] xen/8250-uart: update definitions dmkhn
@ 2025-07-31 23:23 ` Stefano Stabellini
2025-08-04 10:23 ` Jan Beulich
1 sibling, 0 replies; 61+ messages in thread
From: Stefano Stabellini @ 2025-07-31 23:23 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, 31 Jul 2025, dmkhn@proton.me 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] 61+ messages in thread
* Re: [PATCH v4 4/8] xen/8250-uart: update definitions
2025-07-31 19:22 ` [PATCH v4 4/8] xen/8250-uart: update definitions dmkhn
2025-07-31 23:23 ` Stefano Stabellini
@ 2025-08-04 10:23 ` Jan Beulich
2025-08-07 19:41 ` dmkhn
1 sibling, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 10:23 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:22, dmkhn@proton.me 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 v3:
> - feedback addressed
> - made use of new UART_MCR_XXX bits in ns16550 driver
> - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-19-c5d36b31d66c@ford.com/
> ---
> xen/drivers/char/ns16550.c | 6 ++---
> xen/include/xen/8250-uart.h | 50 ++++++++++++++++++++++++++++++-------
> 2 files changed, 44 insertions(+), 12 deletions(-)
>
> diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
> index df7fff7f81df..a899711e2a8b 100644
> --- a/xen/drivers/char/ns16550.c
> +++ b/xen/drivers/char/ns16550.c
> @@ -739,9 +739,9 @@ static int __init check_existence(struct ns16550 *uart)
> * 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..bc11cdc376c9 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)
At the example of this: It having no users here, how are we to know it'll
gain some (and hence be useful)? Adding missing base definitions is imo
fine without immediate users, but for derived ones it's less clear.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 4/8] xen/8250-uart: update definitions
2025-08-04 10:23 ` Jan Beulich
@ 2025-08-07 19:41 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 19:41 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 04, 2025 at 12:23:34PM +0200, Jan Beulich wrote:
> On 31.07.2025 21:22, dmkhn@proton.me 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 v3:
> > - feedback addressed
> > - made use of new UART_MCR_XXX bits in ns16550 driver
> > - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-19-c5d36b31d66c@ford.com/
> > ---
> > xen/drivers/char/ns16550.c | 6 ++---
> > xen/include/xen/8250-uart.h | 50 ++++++++++++++++++++++++++++++-------
> > 2 files changed, 44 insertions(+), 12 deletions(-)
> >
> > diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
> > index df7fff7f81df..a899711e2a8b 100644
> > --- a/xen/drivers/char/ns16550.c
> > +++ b/xen/drivers/char/ns16550.c
> > @@ -739,9 +739,9 @@ static int __init check_existence(struct ns16550 *uart)
> > * 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..bc11cdc376c9 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)
>
> At the example of this: It having no users here, how are we to know it'll
> gain some (and hence be useful)? Adding missing base definitions is imo
> fine without immediate users, but for derived ones it's less clear.
There're UART_IIR and UART_IER bits which could be re-used.
Will update ns16550 driver.
Thanks.
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
` (3 preceding siblings ...)
2025-07-31 19:22 ` [PATCH v4 4/8] xen/8250-uart: update definitions dmkhn
@ 2025-07-31 19:22 ` dmkhn
2025-07-31 23:57 ` Stefano Stabellini
` (2 more replies)
2025-07-31 19:22 ` [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86) dmkhn
` (3 subsequent siblings)
8 siblings, 3 replies; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:22 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 initial in-hypervisor emulator for NS8250/NS16x50-compatible UARTs under
CONFIG_VUART_NS16550 for x86 port of Xen.
x86 port of Xen lacks vUART facility similar to Arm's SBSA emulator to support
x86 guest OS bring up in the embedded setups.
In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
early guest firmware and/or 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. Such functionality is limited to dom0
use currently.
By default, CONFIG_VUART_NS16550 enables emulation of NS16550 at I/O port
0x3f8, IRQ#4 in guest OS (legacy COM1).
Legacy COM resources can be selected at built-time and cannot be configured
per-domain via .cfg or DT yet.
Introduce new emulation flag for virtual UART on x86 and plumb it through
domain creation code so NS16550 emulator can be instantiated properly.
Please refer to the NS16550 emulator code for full list of limitations.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v3:
- feedback addressed
- adjusted to new vUART framework APIs
- Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-21-c5d36b31d66c@ford.com/
---
xen/arch/x86/hvm/hvm.c | 9 +
xen/arch/x86/include/asm/domain.h | 4 +-
xen/arch/x86/include/asm/hvm/domain.h | 4 +
xen/common/emul/vuart/Kconfig | 48 ++
xen/common/emul/vuart/Makefile | 1 +
xen/common/emul/vuart/vuart-ns16550.c | 1009 +++++++++++++++++++++++++
xen/common/emul/vuart/vuart.c | 4 +
xen/include/public/arch-x86/xen.h | 4 +-
xen/include/xen/resource.h | 3 +
9 files changed, 1084 insertions(+), 2 deletions(-)
create mode 100644 xen/common/emul/vuart/vuart-ns16550.c
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index b7edb1d6555d..1156e7ebcc4c 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -31,6 +31,7 @@
#include <xen/nospec.h>
#include <xen/vm_event.h>
#include <xen/console.h>
+#include <xen/vuart.h>
#include <asm/shadow.h>
#include <asm/hap.h>
#include <asm/current.h>
@@ -702,6 +703,10 @@ int hvm_domain_initialise(struct domain *d,
if ( rc != 0 )
goto fail1;
+ rc = vuart_init(d, NULL);
+ if ( rc != 0 )
+ goto out_vioapic_deinit;
+
stdvga_init(d);
rtc_init(d);
@@ -725,6 +730,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) )
@@ -787,6 +794,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/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h
index eafd5cfc903d..1ecc7c2cae32 100644
--- a/xen/arch/x86/include/asm/domain.h
+++ b/xen/arch/x86/include/asm/domain.h
@@ -468,6 +468,7 @@ struct arch_domain
#define X86_EMU_IOMMU XEN_X86_EMU_IOMMU
#define X86_EMU_USE_PIRQ XEN_X86_EMU_USE_PIRQ
#define X86_EMU_VPCI XEN_X86_EMU_VPCI
+#define X86_EMU_NS16550 XEN_X86_EMU_NS16550
#else
#define X86_EMU_LAPIC 0
#define X86_EMU_HPET 0
@@ -479,6 +480,7 @@ struct arch_domain
#define X86_EMU_IOMMU 0
#define X86_EMU_USE_PIRQ 0
#define X86_EMU_VPCI 0
+#define X86_EMU_NS16550 0
#endif
#define X86_EMU_PIT XEN_X86_EMU_PIT
@@ -489,7 +491,7 @@ struct arch_domain
X86_EMU_IOAPIC | X86_EMU_PIC | \
X86_EMU_VGA | X86_EMU_IOMMU | \
X86_EMU_PIT | X86_EMU_USE_PIRQ | \
- X86_EMU_VPCI)
+ X86_EMU_VPCI | X86_EMU_NS16550)
#define has_vlapic(d) (!!((d)->emulation_flags & X86_EMU_LAPIC))
#define has_vhpet(d) (!!((d)->emulation_flags & X86_EMU_HPET))
diff --git a/xen/arch/x86/include/asm/hvm/domain.h b/xen/arch/x86/include/asm/hvm/domain.h
index 333501d5f2ac..9945b16d1a6e 100644
--- a/xen/arch/x86/include/asm/hvm/domain.h
+++ b/xen/arch/x86/include/asm/hvm/domain.h
@@ -149,6 +149,10 @@ struct hvm_domain {
#ifdef CONFIG_MEM_SHARING
struct mem_sharing_domain mem_sharing;
#endif
+
+#ifdef CONFIG_VUART_NS16550
+ void *vuart; /* Virtual UART handle. */
+#endif
};
#endif /* __ASM_X86_HVM_DOMAIN_H__ */
diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
index 02f7dd6dc1a1..ebefd90d913e 100644
--- a/xen/common/emul/vuart/Kconfig
+++ b/xen/common/emul/vuart/Kconfig
@@ -3,4 +3,52 @@ config HAS_VUART
menu "UART Emulation"
+config VUART_NS16550
+ bool "NS16550-compatible UART Emulation" if EXPERT
+ depends on X86 && HVM
+ select HAS_VUART
+ help
+ In-hypervisor NS16550/NS16x50 UART emulation.
+
+ Only legacy PC I/O ports are emulated.
+
+ This is strictly for testing purposes (such as early HVM guest console),
+ and not appropriate for use in production.
+
+choice VUART_NS16550_PC
+ prompt "IBM PC COM resources"
+ depends on VUART_NS16550
+ default VUART_NS16550_PC_COM1
+ help
+ Default emulated NS16550 resources.
+
+config VUART_NS16550_PC_COM1
+ bool "COM1 (I/O port 0x3f8, IRQ#4)"
+
+config VUART_NS16550_PC_COM2
+ bool "COM2 (I/O port 0x2f8, IRQ#3)"
+
+config VUART_NS16550_PC_COM3
+ bool "COM3 (I/O port 0x3e8, IRQ#4)"
+
+config VUART_NS16550_PC_COM4
+ bool "COM4 (I/O port 0x2e8, IRQ#3)"
+
+endchoice
+
+config VUART_NS16550_LOG_LEVEL
+ int "UART emulator verbosity level"
+ range 0 3
+ default "1"
+ depends on VUART_NS16550
+ help
+ Set the default log level of UART emulator.
+ See include/xen/config.h for more details.
+
+config VUART_NS16550_DEBUG
+ bool "UART emulator development debugging"
+ depends on VUART_NS16550
+ help
+ Enable development debugging.
+
endmenu
diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
index c6400b001e85..85650ca5d8ce 100644
--- a/xen/common/emul/vuart/Makefile
+++ b/xen/common/emul/vuart/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_HAS_VUART) += vuart.o
+obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
diff --git a/xen/common/emul/vuart/vuart-ns16550.c b/xen/common/emul/vuart/vuart-ns16550.c
new file mode 100644
index 000000000000..48bbf58264fe
--- /dev/null
+++ b/xen/common/emul/vuart/vuart-ns16550.c
@@ -0,0 +1,1009 @@
+/* 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 HVM domains support (build-time), PVH domains are not supported yet;
+ * - Only legacy COM{1,2,3,4} resources via Kconfig, 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 ISA IRQ sharing allowed;
+ * - No MMIO-based UART emulation.
+ */
+
+#define pr_prefix "ns16550"
+#define pr_fmt(fmt) pr_prefix ": " fmt
+#define pr_log_level CONFIG_VUART_NS16550_LOG_LEVEL
+
+#include <xen/8250-uart.h>
+#include <xen/console.h>
+#include <xen/iocap.h>
+#include <xen/ioreq.h>
+#include <xen/resource.h>
+#include <xen/vuart.h>
+#include <xen/xvmalloc.h>
+
+#include <public/io/console.h>
+
+#define pr_err(fmt, args...) do { \
+ gprintk(KERN_ERR, pr_fmt(fmt), ## args); \
+} while (0)
+
+#define pr_warn(fmt, args...) do { \
+ if ( pr_log_level >= 1) \
+ gprintk(KERN_WARNING, pr_fmt(fmt), ## args); \
+} while (0)
+
+#define pr_info(fmt, args...) do { \
+ if ( pr_log_level >= 2 ) \
+ gprintk(KERN_INFO, pr_fmt(fmt), ## args); \
+} while (0)
+
+#define pr_debug(fmt, args...) do { \
+ if ( pr_log_level >= 3 ) \
+ gprintk(KERN_DEBUG, pr_fmt(fmt), ## args); \
+} while (0)
+
+#if defined(CONFIG_VUART_NS16550_PC_COM1)
+#define X86_PC_UART_IDX 0
+#elif defined(CONFIG_VUART_NS16550_PC_COM2)
+#define X86_PC_UART_IDX 1
+#elif defined(CONFIG_VUART_NS16550_PC_COM3)
+#define X86_PC_UART_IDX 2
+#elif defined(CONFIG_VUART_NS16550_PC_COM4)
+#define X86_PC_UART_IDX 3
+#else
+#error "Unsupported I/O port"
+#endif
+
+#ifdef CONFIG_VUART_NS16550_DEBUG
+#define guest_prefix "FROM GUEST "
+#else
+#define guest_prefix ""
+#endif
+
+/*
+ * Number of supported registers in the UART.
+ */
+#define NS16550_REGS_NUM ( UART_SCR + 1 )
+
+/*
+ * Number of emulated registers.
+ *
+ * - Emulated registers [0..NS16550_REGS_NUM] are R/W registers for DLAB=0.
+ * - DLAB=1, R/W, DLL = NS16550_REGS_NUM + 0
+ * - DLAB=1, R/W, DLM = NS16550_REGS_NUM + 1
+ * - R/O, IIR (IIR_THR) = NS16550_REGS_NUM + 2
+ */
+#define NS16550_EMU_REGS_NUM ( NS16550_REGS_NUM + 3 )
+
+/*
+ * Virtual NS16550 device state.
+ */
+struct vuart_ns16550 {
+ struct xencons_interface cons; /* Emulated RX/TX FIFOs */
+ uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
+ unsigned int irq; /* Emulated IRQ# */
+ uint64_t io_addr; /* Emulated I/O region base address */
+ uint64_t io_size; /* Emulated I/O region size */
+ const char *name; /* Device name */
+ struct domain *owner; /* Owner domain */
+ spinlock_t lock; /* Protection */
+};
+
+/*
+ * Virtual device description.
+ */
+struct virtdev_desc {
+ const char *name;
+ const struct resource *res;
+};
+
+/*
+ * Legacy IBM PC NS16550 resources.
+ * There are only 4 I/O port ranges, hardcoding all of them here.
+ */
+static const struct virtdev_desc x86_pc_uarts[4] = {
+ [0] = {
+ .name = "COM1",
+ .res = (const struct resource[]){
+ { .type = IORESOURCE_IO, .addr = 0x3f8, .size = NS16550_REGS_NUM },
+ { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
+ { .type = IORESOURCE_UNKNOWN },
+ },
+ },
+ [1] = {
+ .name = "COM2",
+ .res = (const struct resource[]){
+ { .type = IORESOURCE_IO, .addr = 0x2f8, .size = NS16550_REGS_NUM },
+ { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
+ { .type = IORESOURCE_UNKNOWN },
+ },
+ },
+ [2] = {
+ .name = "COM3",
+ .res = (const struct resource[]){
+ { .type = IORESOURCE_IO, .addr = 0x3e8, .size = NS16550_REGS_NUM },
+ { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
+ { .type = IORESOURCE_UNKNOWN },
+ },
+ },
+ [3] = {
+ .name = "COM4",
+ .res = (const struct resource[]){
+ { .type = IORESOURCE_IO, .addr = 0x2e8, .size = NS16550_REGS_NUM },
+ { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
+ { .type = IORESOURCE_UNKNOWN },
+ },
+ },
+};
+
+static bool ns16550_fifo_rx_empty(const struct vuart_ns16550 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->in_prod == cons->in_cons;
+}
+
+static bool ns16550_fifo_rx_full(const struct vuart_ns16550 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->in_prod - cons->in_cons == ARRAY_SIZE(cons->in);
+}
+
+static void ns16550_fifo_rx_reset(struct vuart_ns16550 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ cons->in_cons = cons->in_prod;
+}
+
+static int ns16550_fifo_rx_getchar(struct vuart_ns16550 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+ int rc;
+
+ if ( ns16550_fifo_rx_empty(vdev) )
+ {
+ pr_debug("%s: RX FIFO empty\n", vdev->name);
+ rc = -ENODATA;
+ }
+ else
+ {
+ rc = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)];
+ cons->in_cons++;
+ }
+
+ return rc;
+}
+
+static int ns16550_fifo_rx_putchar(struct vuart_ns16550 *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 ( ns16550_fifo_rx_full(vdev) )
+ {
+ pr_debug("%s: RX FIFO full; resetting\n", vdev->name);
+ ns16550_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 bool ns16550_fifo_tx_empty(const struct vuart_ns16550 *vdev)
+{
+ const struct xencons_interface *cons = &vdev->cons;
+
+ return cons->out_prod == cons->out_cons;
+}
+
+static void ns16550_fifo_tx_reset(struct vuart_ns16550 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ cons->out_prod = 0;
+ ASSERT(cons->out_cons == cons->out_prod);
+}
+
+/*
+ * Flush cached output to Xen console.
+ */
+static void ns16550_fifo_tx_flush(struct vuart_ns16550 *vdev)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ if ( ns16550_fifo_tx_empty(vdev) )
+ return;
+
+ ASSERT(cons->out_prod < ARRAY_SIZE(cons->out));
+ cons->out[cons->out_prod] = '\0';
+ cons->out_prod++;
+
+ guest_printk(vdev->owner, guest_prefix "%s", cons->out);
+
+ ns16550_fifo_tx_reset(vdev);
+}
+
+/*
+ * Accumulate guest OS output before sending to Xen console.
+ */
+static void ns16550_fifo_tx_putchar(struct vuart_ns16550 *vdev, char ch)
+{
+ struct xencons_interface *cons = &vdev->cons;
+
+ if ( !is_console_printable(ch) )
+ return;
+
+ if ( ch != '\0' )
+ {
+ cons->out[cons->out_prod] = ch;
+ cons->out_prod++;
+ }
+
+ if ( cons->out_prod == ARRAY_SIZE(cons->out) - 1 ||
+ ch == '\n' || ch == '\0' )
+ ns16550_fifo_tx_flush(vdev);
+}
+
+static inline uint8_t cf_check ns16550_dlab_get(const struct vuart_ns16550 *vdev)
+{
+ return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
+}
+
+static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
+{
+ return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);
+}
+
+static bool cf_check ns16550_iir_check_rda(const struct vuart_ns16550 *vdev)
+{
+ return !ns16550_fifo_rx_empty(vdev);
+}
+
+static bool cf_check ns16550_iir_check_thr(const struct vuart_ns16550 *vdev)
+{
+ return !!(vdev->regs[NS16550_REGS_NUM + UART_IIR] & UART_IIR_THR);
+}
+
+static bool cf_check ns16550_iir_check_msi(const struct vuart_ns16550 *vdev)
+{
+ return !!(vdev->regs[UART_MSR] & UART_MSR_CHANGE);
+}
+
+/*
+ * Get the interrupt identity reason.
+ *
+ * IIR is re-calculated once called, because NS16550 always reports high
+ * priority events first.
+ * regs[NS16550_REGS_NUM + UART_IIR] is used to store THR reason only.
+ */
+static uint8_t ns16550_iir_get(const struct vuart_ns16550 *vdev)
+{
+ /*
+ * Interrupt identity reasons by priority.
+ * NB: high priority are at lower indexes below.
+ */
+ static const struct {
+ bool (*check)(const struct vuart_ns16550 *vdev);
+ uint8_t ier;
+ uint8_t iir;
+ } iir_by_prio[] = {
+ [0] = { ns16550_iir_check_lsi, UART_IER_ELSI, UART_IIR_LSI },
+ [1] = { ns16550_iir_check_rda, UART_IER_ERDAI, UART_IIR_RDA },
+ [2] = { ns16550_iir_check_thr, UART_IER_ETHREI, UART_IIR_THR },
+ [3] = { ns16550_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/ NS16550 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 ns16550_irq_assert(const struct vuart_ns16550 *vdev)
+{
+ struct domain *d = vdev->owner;
+ int vector;
+
+ if ( has_vpic(d) ) /* HVM */
+ vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
+ else
+ ASSERT_UNREACHABLE();
+
+ pr_debug("%s: IRQ#%d vector %d assert\n", vdev->name, vdev->irq, vector);
+}
+
+static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
+{
+ struct domain *d = vdev->owner;
+
+ if ( has_vpic(d) ) /* HVM */
+ hvm_isa_irq_deassert(d, vdev->irq);
+ else
+ ASSERT_UNREACHABLE();
+
+ pr_debug("%s: IRQ#%d deassert\n", vdev->name, vdev->irq);
+}
+
+/*
+ * Assert/deassert virtual NS16550 interrupt line.
+ */
+static void ns16550_irq_check(const struct vuart_ns16550 *vdev)
+{
+ uint8_t iir = ns16550_iir_get(vdev);
+
+ if ( iir & UART_IIR_NOINT )
+ ns16550_irq_assert(vdev);
+ else
+ ns16550_irq_deassert(vdev);
+
+ pr_debug("%s: IRQ#%d IIR 0x%02x %s\n", vdev->name, vdev->irq, iir,
+ (iir & UART_IIR_NOINT) ? "deassert" : "assert");
+}
+
+/*
+ * Emulate 8-bit write access to NS16550 register.
+ */
+static int ns16550_io_write8(
+ struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
+{
+ uint8_t *regs = vdev->regs;
+ uint8_t val = *data;
+ int rc = 0;
+
+ if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
+ regs[NS16550_REGS_NUM + reg] = val;
+ else
+ {
+ switch ( reg )
+ {
+ case UART_THR:
+ if ( regs[UART_MCR] & UART_MCR_LOOP )
+ {
+ (void)ns16550_fifo_rx_putchar(vdev, val);
+ regs[UART_LSR] |= UART_LSR_OE;
+ }
+ else
+ ns16550_fifo_tx_putchar(vdev, val);
+
+ regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
+
+ break;
+
+ case UART_IER:
+ /*
+ * NB: Make sure THR interrupt is re-triggered once guest OS
+ * re-enabled ETHREI in EIR.
+ */
+ if ( val & regs[UART_IER] & UART_IER_ETHREI )
+ regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
+
+ regs[UART_IER] = val & UART_IER_MASK;
+
+ break;
+
+ case UART_FCR: /* WO */
+ if ( val & UART_FCR_RESERVED0 )
+ pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
+ vdev->name, UART_FCR_RESERVED0);
+
+ if ( val & UART_FCR_RESERVED1 )
+ pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
+ vdev->name, UART_FCR_RESERVED1);
+
+ if ( val & UART_FCR_CLRX )
+ ns16550_fifo_rx_reset(vdev);
+
+ if ( val & UART_FCR_CLTX )
+ ns16550_fifo_tx_flush(vdev);
+
+ 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;
+
+ 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 )
+ pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
+ vdev->name, UART_MCR_RESERVED0);
+
+ if ( val & UART_MCR_TCRTLR )
+ pr_warn("%s: MCR: not supported: %x\n",
+ vdev->name, UART_MCR_TCRTLR);
+
+ if ( val & UART_MCR_RESERVED1 )
+ pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
+ vdev->name, 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_MCR_RTS) ^ (msr_next & UART_MCR_RTS) )
+ 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;
+ }
+
+ ns16550_irq_check(vdev);
+ }
+
+ return rc;
+}
+
+/*
+ * Emulate 16-bit write access to NS16550 register.
+ * NB: some guest OSes use outw() to access UART_DLL.
+ */
+static int ns16550_io_write16(
+ struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
+{
+ uint16_t val = *data;
+ int rc;
+
+ if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
+ {
+ vdev->regs[NS16550_REGS_NUM + UART_DLL] = val & 0xff;
+ vdev->regs[NS16550_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
+ rc = 0;
+ }
+ else
+ rc = -EINVAL;
+
+ return rc;
+}
+
+/*
+ * Emulate write access to NS16550 register.
+ */
+static int ns16550_io_write(
+ struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
+{
+ int rc;
+
+ switch ( size )
+ {
+ case 1:
+ rc = ns16550_io_write8(vdev, reg, (uint8_t *)data);
+ break;
+
+ case 2:
+ rc = ns16550_io_write16(vdev, reg, (uint16_t *)data);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Emulate 8-bit read access to NS16550 register.
+ */
+static int ns16550_io_read8(
+ struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
+{
+ uint8_t *regs = vdev->regs;
+ uint8_t val = 0xff;
+ int rc = 0;
+
+ if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
+ val = regs[NS16550_REGS_NUM + reg];
+ else {
+ switch ( reg )
+ {
+ case UART_RBR:
+ /* NB: do not forget to clear overrun condition */
+ regs[UART_LSR] &= ~UART_LSR_OE;
+
+ rc = ns16550_fifo_rx_getchar(vdev);
+ if ( rc >= 0 )
+ val = (uint8_t)rc;
+
+ rc = 0;
+ break;
+
+ case UART_IER:
+ val = regs[UART_IER];
+ break;
+
+ case UART_IIR: /* RO */
+ val = ns16550_iir_get(vdev);
+
+ /* NB: clear IIR scratch location */
+ if ( val & UART_IIR_THR )
+ regs[NS16550_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
+
+ break;
+
+ case UART_LCR:
+ 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;
+ if ( ns16550_fifo_rx_empty(vdev) )
+ val &= ~UART_LSR_DR;
+ else
+ val |= UART_LSR_DR;
+
+ 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;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ ns16550_irq_check(vdev);
+ }
+
+ *data = val;
+
+ return rc;
+}
+
+/*
+ * Emulate 16-bit read access to NS16550 register.
+ */
+static int ns16550_io_read16(
+ struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
+{
+ uint16_t val = 0xffff;
+ int rc = -EINVAL;
+
+ if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
+ {
+ val = vdev->regs[NS16550_REGS_NUM + UART_DLM] << 8 |
+ vdev->regs[NS16550_REGS_NUM + UART_DLL];
+ rc = 0;
+ }
+
+ *data = val;
+
+ return rc;
+}
+
+/*
+ * Emulate read access to NS16550 register.
+ */
+static int ns16550_io_read(
+ struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
+{
+ int rc;
+
+ switch ( size )
+ {
+ case 1:
+ rc = ns16550_io_read8(vdev, reg, (uint8_t *)data);
+ break;
+
+ case 2:
+ rc = ns16550_io_read16(vdev, reg, (uint16_t *)data);
+ break;
+
+ default:
+ *data = 0xffffffff;
+ rc = -EINVAL;
+ break;
+ }
+
+ return rc;
+}
+
+static void cf_check ns16550_dump_state(const struct domain *d)
+{
+ struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
+ 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%04"PRIx64" IRQ#%d owner %pd\n",
+ vdev->name, vdev->io_addr, vdev->irq, vdev->owner);
+
+ 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[NS16550_REGS_NUM + UART_DLL],
+ regs[NS16550_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], ns16550_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);
+}
+
+/*
+ * Emulate I/O access to NS16550 register.
+ * Note, emulation always returns X86EMUL_OKAY, once I/O port trap is enabled.
+ */
+static int cf_check ns16550_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_ns16550 *vdev = d->arch.hvm.vuart;
+ uint32_t reg;
+ unsigned dlab;
+ int rc;
+
+ if ( !vdev )
+ {
+ pr_err("%s: %c io 0x%04x %d: not initialized\n",
+ vdev->name, op(dir), addr, size);
+
+ ASSERT_UNREACHABLE();
+ goto out;
+ }
+
+ if ( d != vdev->owner )
+ {
+ pr_err("%s: %c io 0x%04x %d: does not match current domain %pv\n",
+ vdev->name, op(dir), addr, size, d);
+
+ ASSERT_UNREACHABLE();
+ goto out;
+ }
+
+ reg = addr - vdev->io_addr;
+ if ( !IS_ALIGNED(reg, size) )
+ {
+ pr_err("%s: %c 0x%04x %d: unaligned access\n",
+ vdev->name, op(dir), addr, size);
+ goto out;
+ }
+
+ dlab = ns16550_dlab_get(vdev);
+ if ( reg >= NS16550_REGS_NUM )
+ {
+ pr_err("%s: %c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
+ vdev->name, op(dir), addr, size,
+ dlab, reg, *data);
+ goto out;
+ }
+
+ spin_lock(&vdev->lock);
+
+ if ( dir == IOREQ_WRITE )
+ {
+ pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
+ vdev->name, op(dir), addr, size,
+ dlab, reg, *data);
+ rc = ns16550_io_write(vdev, reg, size, data);
+ }
+ else
+ {
+ rc = ns16550_io_read(vdev, reg, size, data);
+ pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
+ vdev->name, op(dir), addr, size,
+ dlab, reg, *data);
+ }
+ if ( rc < 0 )
+ pr_err("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
+ vdev->name, op(dir), addr, size,
+ dlab, reg, *data);
+
+ spin_unlock(&vdev->lock);
+#ifdef CONFIG_VUART_NS16550_DEBUG
+ ns16550_dump_state(d);
+#endif
+
+out:
+ rcu_unlock_domain(d);
+
+ return X86EMUL_OKAY;
+#undef op
+}
+
+static int cf_check ns16550_init(struct domain *d,
+ struct vuart_params *params)
+{
+ const struct virtdev_desc *desc = &x86_pc_uarts[X86_PC_UART_IDX];
+ const struct resource *r = desc->res;
+ const uint16_t divisor = (UART_CLOCK_HZ / 115200) >> 4;
+ struct vuart_ns16550 *vdev;
+ int rc;
+
+ BUG_ON(d->arch.hvm.vuart);
+
+ if ( !is_hvm_domain(d) )
+ {
+ pr_err("%s: not an HVM domain\n", desc->name);
+ return -ENOSYS;
+ }
+
+ vdev = xvzalloc(typeof(*vdev));
+ if ( !vdev )
+ {
+ pr_err("%s: failed to allocate memory\n", desc->name);
+ return -ENOMEM;
+ }
+
+ for_each_resource(r)
+ {
+ if ( r->type & IORESOURCE_IO )
+ {
+ /* Disallow sharing physical I/O port */
+ rc = ioports_deny_access(d, r->addr, r->addr + r->size - 1);
+ if ( rc )
+ {
+ pr_err("%s: virtual I/O port range [0x%04x"PRIx64"..0x%04x"PRIx64"]: conflict w/ physical range\n",
+ desc->name,
+ (unsigned int)r->addr,
+ (unsigned int)(r->addr + r->size - 1));
+ return rc;
+ }
+
+ register_portio_handler(d, r->addr, r->size, ns16550_io_handle);
+
+ vdev->io_addr = r->addr;
+ vdev->io_size = r->size;
+ }
+ else if ( r->type & IORESOURCE_IRQ )
+ {
+ /* Disallow sharing physical IRQ */
+ rc = irq_deny_access(d, r->addr);
+ if ( rc )
+ {
+ pr_err("%s: virtual IRQ#%"PRIu64": conflict w/ physical IRQ: %d\n",
+ desc->name, r->addr, rc);
+ return rc;
+ }
+
+ vdev->irq = r->addr;
+ }
+ else
+ ASSERT_UNREACHABLE();
+ }
+
+ spin_lock_init(&vdev->lock);
+
+ vdev->owner = d;
+ vdev->name = desc->name;
+
+ /* NB: report 115200 baud rate */
+ vdev->regs[NS16550_REGS_NUM + UART_DLL] = divisor & 0xff;
+ vdev->regs[NS16550_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
+
+ /* NS16550 shall assert UART_IIR_THR whenever transmitter is empty. */
+ vdev->regs[NS16550_REGS_NUM + UART_IIR] = UART_IIR_THR;
+
+ d->arch.hvm.vuart = vdev;
+
+ spin_lock(&vdev->lock);
+ ns16550_irq_check(vdev);
+ spin_unlock(&vdev->lock);
+
+ return 0;
+}
+
+static void cf_check ns16550_deinit(struct domain *d)
+{
+ struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
+
+ if ( !vdev )
+ return;
+
+ spin_lock(&vdev->lock);
+
+ ns16550_fifo_tx_flush(vdev);
+
+ spin_unlock(&vdev->lock);
+
+ XVFREE(d->arch.hvm.vuart);
+}
+
+static int cf_check ns16550_put_rx(struct domain *d, char ch)
+{
+ struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
+ uint8_t *regs;
+ uint8_t dlab;
+ int rc;
+
+ ASSERT(d == vdev->owner);
+ if ( !vdev )
+ return -ENODEV;
+
+ spin_lock(&vdev->lock);
+
+ dlab = ns16550_dlab_get(vdev);
+ regs = vdev->regs;
+
+ if ( dlab )
+ {
+ pr_debug("%s: THR/RBR access disabled: DLAB=1\n", vdev->name);
+ rc = -EBUSY;
+ }
+ else if ( regs[UART_MCR] & UART_MCR_LOOP )
+ {
+ pr_debug("%s: THR/RBR access disabled: loopback mode\n", vdev->name);
+ rc = -EBUSY;
+ }
+ else
+ {
+ uint8_t val = 0;
+
+ rc = ns16550_fifo_rx_putchar(vdev, ch);
+ if ( rc == -ENOSPC )
+ val |= UART_LSR_OE;
+
+ /* NB: UART_LSR_DR is also set when UART_LSR is accessed. */
+ regs[UART_LSR] |= UART_LSR_DR | val;
+
+ /*
+ * Echo the user input on Xen console iff Xen console input is owned
+ * by NS16550 domain.
+ * NB: use 'console_timestamps=none' to disable Xen timestamps.
+ */
+ if ( is_console_printable(ch) )
+ guest_printk(d, "%c", ch);
+
+ /* FIXME: check FCR when to fire an interrupt */
+ ns16550_irq_check(vdev);
+ }
+
+ spin_unlock(&vdev->lock);
+#ifdef CONFIG_VUART_NS16550_DEBUG
+ ns16550_dump_state(d);
+#endif
+
+ return rc;
+}
+
+static const struct vuart_ops ns16550_ops = {
+ .add_node = NULL,
+ .init = ns16550_init,
+ .deinit = ns16550_deinit,
+ .dump_state = ns16550_dump_state,
+ .put_rx = ns16550_put_rx,
+};
+
+VUART_REGISTER(ns16550, &ns16550_ops);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
index 14a7f8bd8b79..7971813a723d 100644
--- a/xen/common/emul/vuart/vuart.c
+++ b/xen/common/emul/vuart/vuart.c
@@ -99,6 +99,10 @@ bool domain_has_vuart(const struct domain *d)
{
uint32_t mask = 0;
+#ifdef CONFIG_VUART_NS16550
+ mask |= XEN_X86_EMU_NS16550;
+#endif
+
return !!(d->emulation_flags & mask);
}
diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h
index fc2487986642..f905e1252c70 100644
--- a/xen/include/public/arch-x86/xen.h
+++ b/xen/include/public/arch-x86/xen.h
@@ -283,13 +283,15 @@ struct xen_arch_domainconfig {
#define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ)
#define _XEN_X86_EMU_VPCI 10
#define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI)
+#define _XEN_X86_EMU_NS16550 11
+#define XEN_X86_EMU_NS16550 (1U<<_XEN_X86_EMU_NS16550)
#define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \
XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \
XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \
XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \
XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\
- XEN_X86_EMU_VPCI)
+ XEN_X86_EMU_VPCI | XEN_X86_EMU_NS16550)
uint32_t emulation_flags;
/*
diff --git a/xen/include/xen/resource.h b/xen/include/xen/resource.h
index 5d103631288d..56fb8101edd6 100644
--- a/xen/include/xen/resource.h
+++ b/xen/include/xen/resource.h
@@ -31,4 +31,7 @@ struct resource {
#define resource_size(res) ((res)->size)
+#define for_each_resource(res) \
+ for ( ; (res) && (res)->type != IORESOURCE_UNKNOWN; (res)++ )
+
#endif /* XEN__RESOURCE_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-07-31 19:22 ` [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86) dmkhn
@ 2025-07-31 23:57 ` Stefano Stabellini
2025-08-01 3:28 ` dmkhn
2025-08-04 10:53 ` Jan Beulich
2025-08-06 15:06 ` Roger Pau Monné
2 siblings, 1 reply; 61+ messages in thread
From: Stefano Stabellini @ 2025-07-31 23:57 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add initial in-hypervisor emulator for NS8250/NS16x50-compatible UARTs under
> CONFIG_VUART_NS16550 for x86 port of Xen.
>
> x86 port of Xen lacks vUART facility similar to Arm's SBSA emulator to support
> x86 guest OS bring up in the embedded setups.
>
> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> early guest firmware and/or 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. Such functionality is limited to dom0
> use currently.
>
> By default, CONFIG_VUART_NS16550 enables emulation of NS16550 at I/O port
> 0x3f8, IRQ#4 in guest OS (legacy COM1).
>
> Legacy COM resources can be selected at built-time and cannot be configured
> per-domain via .cfg or DT yet.
>
> Introduce new emulation flag for virtual UART on x86 and plumb it through
> domain creation code so NS16550 emulator can be instantiated properly.
>
> Please refer to the NS16550 emulator code for full list of limitations.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v3:
> - feedback addressed
> - adjusted to new vUART framework APIs
> - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-21-c5d36b31d66c@ford.com/
> ---
> xen/arch/x86/hvm/hvm.c | 9 +
> xen/arch/x86/include/asm/domain.h | 4 +-
> xen/arch/x86/include/asm/hvm/domain.h | 4 +
> xen/common/emul/vuart/Kconfig | 48 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/vuart-ns16550.c | 1009 +++++++++++++++++++++++++
> xen/common/emul/vuart/vuart.c | 4 +
> xen/include/public/arch-x86/xen.h | 4 +-
> xen/include/xen/resource.h | 3 +
> 9 files changed, 1084 insertions(+), 2 deletions(-)
> create mode 100644 xen/common/emul/vuart/vuart-ns16550.c
>
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index b7edb1d6555d..1156e7ebcc4c 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -31,6 +31,7 @@
> #include <xen/nospec.h>
> #include <xen/vm_event.h>
> #include <xen/console.h>
> +#include <xen/vuart.h>
> #include <asm/shadow.h>
> #include <asm/hap.h>
> #include <asm/current.h>
> @@ -702,6 +703,10 @@ int hvm_domain_initialise(struct domain *d,
> if ( rc != 0 )
> goto fail1;
>
> + rc = vuart_init(d, NULL);
> + if ( rc != 0 )
> + goto out_vioapic_deinit;
> +
> stdvga_init(d);
>
> rtc_init(d);
> @@ -725,6 +730,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) )
> @@ -787,6 +794,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/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h
> index eafd5cfc903d..1ecc7c2cae32 100644
> --- a/xen/arch/x86/include/asm/domain.h
> +++ b/xen/arch/x86/include/asm/domain.h
> @@ -468,6 +468,7 @@ struct arch_domain
> #define X86_EMU_IOMMU XEN_X86_EMU_IOMMU
> #define X86_EMU_USE_PIRQ XEN_X86_EMU_USE_PIRQ
> #define X86_EMU_VPCI XEN_X86_EMU_VPCI
> +#define X86_EMU_NS16550 XEN_X86_EMU_NS16550
> #else
> #define X86_EMU_LAPIC 0
> #define X86_EMU_HPET 0
> @@ -479,6 +480,7 @@ struct arch_domain
> #define X86_EMU_IOMMU 0
> #define X86_EMU_USE_PIRQ 0
> #define X86_EMU_VPCI 0
> +#define X86_EMU_NS16550 0
> #endif
>
> #define X86_EMU_PIT XEN_X86_EMU_PIT
> @@ -489,7 +491,7 @@ struct arch_domain
> X86_EMU_IOAPIC | X86_EMU_PIC | \
> X86_EMU_VGA | X86_EMU_IOMMU | \
> X86_EMU_PIT | X86_EMU_USE_PIRQ | \
> - X86_EMU_VPCI)
> + X86_EMU_VPCI | X86_EMU_NS16550)
>
> #define has_vlapic(d) (!!((d)->emulation_flags & X86_EMU_LAPIC))
> #define has_vhpet(d) (!!((d)->emulation_flags & X86_EMU_HPET))
> diff --git a/xen/arch/x86/include/asm/hvm/domain.h b/xen/arch/x86/include/asm/hvm/domain.h
> index 333501d5f2ac..9945b16d1a6e 100644
> --- a/xen/arch/x86/include/asm/hvm/domain.h
> +++ b/xen/arch/x86/include/asm/hvm/domain.h
> @@ -149,6 +149,10 @@ struct hvm_domain {
> #ifdef CONFIG_MEM_SHARING
> struct mem_sharing_domain mem_sharing;
> #endif
> +
> +#ifdef CONFIG_VUART_NS16550
> + void *vuart; /* Virtual UART handle. */
> +#endif
> };
>
> #endif /* __ASM_X86_HVM_DOMAIN_H__ */
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index 02f7dd6dc1a1..ebefd90d913e 100644
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,52 @@ config HAS_VUART
>
> menu "UART Emulation"
>
> +config VUART_NS16550
> + bool "NS16550-compatible UART Emulation" if EXPERT
> + depends on X86 && HVM
> + select HAS_VUART
> + help
> + In-hypervisor NS16550/NS16x50 UART emulation.
> +
> + Only legacy PC I/O ports are emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
> +choice VUART_NS16550_PC
> + prompt "IBM PC COM resources"
> + depends on VUART_NS16550
> + default VUART_NS16550_PC_COM1
> + help
> + Default emulated NS16550 resources.
> +
> +config VUART_NS16550_PC_COM1
> + bool "COM1 (I/O port 0x3f8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM2
> + bool "COM2 (I/O port 0x2f8, IRQ#3)"
> +
> +config VUART_NS16550_PC_COM3
> + bool "COM3 (I/O port 0x3e8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM4
> + bool "COM4 (I/O port 0x2e8, IRQ#3)"
> +
> +endchoice
> +
> +config VUART_NS16550_LOG_LEVEL
> + int "UART emulator verbosity level"
> + range 0 3
> + default "1"
> + depends on VUART_NS16550
> + help
> + Set the default log level of UART emulator.
> + See include/xen/config.h for more details.
> +
> +config VUART_NS16550_DEBUG
> + bool "UART emulator development debugging"
> + depends on VUART_NS16550
> + help
> + Enable development debugging.
> +
> endmenu
> diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
> index c6400b001e85..85650ca5d8ce 100644
> --- a/xen/common/emul/vuart/Makefile
> +++ b/xen/common/emul/vuart/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_HAS_VUART) += vuart.o
> +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
> diff --git a/xen/common/emul/vuart/vuart-ns16550.c b/xen/common/emul/vuart/vuart-ns16550.c
> new file mode 100644
> index 000000000000..48bbf58264fe
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart-ns16550.c
> @@ -0,0 +1,1009 @@
> +/* 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 HVM domains support (build-time), PVH domains are not supported yet;
> + * - Only legacy COM{1,2,3,4} resources via Kconfig, 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 ISA IRQ sharing allowed;
> + * - No MMIO-based UART emulation.
> + */
> +
> +#define pr_prefix "ns16550"
> +#define pr_fmt(fmt) pr_prefix ": " fmt
> +#define pr_log_level CONFIG_VUART_NS16550_LOG_LEVEL
> +
> +#include <xen/8250-uart.h>
> +#include <xen/console.h>
> +#include <xen/iocap.h>
> +#include <xen/ioreq.h>
> +#include <xen/resource.h>
> +#include <xen/vuart.h>
> +#include <xen/xvmalloc.h>
> +
> +#include <public/io/console.h>
> +
> +#define pr_err(fmt, args...) do { \
> + gprintk(KERN_ERR, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_warn(fmt, args...) do { \
> + if ( pr_log_level >= 1) \
> + gprintk(KERN_WARNING, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_info(fmt, args...) do { \
> + if ( pr_log_level >= 2 ) \
> + gprintk(KERN_INFO, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_debug(fmt, args...) do { \
> + if ( pr_log_level >= 3 ) \
> + gprintk(KERN_DEBUG, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#if defined(CONFIG_VUART_NS16550_PC_COM1)
> +#define X86_PC_UART_IDX 0
> +#elif defined(CONFIG_VUART_NS16550_PC_COM2)
> +#define X86_PC_UART_IDX 1
> +#elif defined(CONFIG_VUART_NS16550_PC_COM3)
> +#define X86_PC_UART_IDX 2
> +#elif defined(CONFIG_VUART_NS16550_PC_COM4)
> +#define X86_PC_UART_IDX 3
> +#else
> +#error "Unsupported I/O port"
> +#endif
> +
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> +#define guest_prefix "FROM GUEST "
> +#else
> +#define guest_prefix ""
> +#endif
> +
> +/*
> + * Number of supported registers in the UART.
> + */
> +#define NS16550_REGS_NUM ( UART_SCR + 1 )
> +
> +/*
> + * Number of emulated registers.
> + *
> + * - Emulated registers [0..NS16550_REGS_NUM] are R/W registers for DLAB=0.
> + * - DLAB=1, R/W, DLL = NS16550_REGS_NUM + 0
> + * - DLAB=1, R/W, DLM = NS16550_REGS_NUM + 1
> + * - R/O, IIR (IIR_THR) = NS16550_REGS_NUM + 2
> + */
> +#define NS16550_EMU_REGS_NUM ( NS16550_REGS_NUM + 3 )
> +
> +/*
> + * Virtual NS16550 device state.
> + */
> +struct vuart_ns16550 {
> + struct xencons_interface cons; /* Emulated RX/TX FIFOs */
> + uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
> + unsigned int irq; /* Emulated IRQ# */
> + uint64_t io_addr; /* Emulated I/O region base address */
> + uint64_t io_size; /* Emulated I/O region size */
> + const char *name; /* Device name */
> + struct domain *owner; /* Owner domain */
> + spinlock_t lock; /* Protection */
> +};
> +
> +/*
> + * Virtual device description.
> + */
> +struct virtdev_desc {
> + const char *name;
> + const struct resource *res;
> +};
> +
> +/*
> + * Legacy IBM PC NS16550 resources.
> + * There are only 4 I/O port ranges, hardcoding all of them here.
> + */
> +static const struct virtdev_desc x86_pc_uarts[4] = {
> + [0] = {
> + .name = "COM1",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x3f8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [1] = {
> + .name = "COM2",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x2f8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [2] = {
> + .name = "COM3",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x3e8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [3] = {
> + .name = "COM4",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x2e8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> +};
> +
> +static bool ns16550_fifo_rx_empty(const struct vuart_ns16550 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->in_prod == cons->in_cons;
> +}
> +
> +static bool ns16550_fifo_rx_full(const struct vuart_ns16550 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->in_prod - cons->in_cons == ARRAY_SIZE(cons->in);
> +}
> +
> +static void ns16550_fifo_rx_reset(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + cons->in_cons = cons->in_prod;
> +}
> +
> +static int ns16550_fifo_rx_getchar(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> + int rc;
> +
> + if ( ns16550_fifo_rx_empty(vdev) )
> + {
> + pr_debug("%s: RX FIFO empty\n", vdev->name);
> + rc = -ENODATA;
> + }
> + else
> + {
> + rc = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)];
> + cons->in_cons++;
> + }
> +
> + return rc;
> +}
> +
> +static int ns16550_fifo_rx_putchar(struct vuart_ns16550 *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 ( ns16550_fifo_rx_full(vdev) )
> + {
> + pr_debug("%s: RX FIFO full; resetting\n", vdev->name);
> + ns16550_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 bool ns16550_fifo_tx_empty(const struct vuart_ns16550 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->out_prod == cons->out_cons;
> +}
> +
> +static void ns16550_fifo_tx_reset(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + cons->out_prod = 0;
> + ASSERT(cons->out_cons == cons->out_prod);
> +}
> +
> +/*
> + * Flush cached output to Xen console.
> + */
> +static void ns16550_fifo_tx_flush(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + if ( ns16550_fifo_tx_empty(vdev) )
> + return;
> +
> + ASSERT(cons->out_prod < ARRAY_SIZE(cons->out));
> + cons->out[cons->out_prod] = '\0';
> + cons->out_prod++;
> +
> + guest_printk(vdev->owner, guest_prefix "%s", cons->out);
> +
> + ns16550_fifo_tx_reset(vdev);
> +}
> +
> +/*
> + * Accumulate guest OS output before sending to Xen console.
> + */
> +static void ns16550_fifo_tx_putchar(struct vuart_ns16550 *vdev, char ch)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + if ( !is_console_printable(ch) )
> + return;
> +
> + if ( ch != '\0' )
> + {
> + cons->out[cons->out_prod] = ch;
> + cons->out_prod++;
> + }
> +
> + if ( cons->out_prod == ARRAY_SIZE(cons->out) - 1 ||
> + ch == '\n' || ch == '\0' )
> + ns16550_fifo_tx_flush(vdev);
> +}
> +
> +static inline uint8_t cf_check ns16550_dlab_get(const struct vuart_ns16550 *vdev)
> +{
> + return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
> +}
> +
> +static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);
> +}
> +
> +static bool cf_check ns16550_iir_check_rda(const struct vuart_ns16550 *vdev)
> +{
> + return !ns16550_fifo_rx_empty(vdev);
> +}
> +
> +static bool cf_check ns16550_iir_check_thr(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[NS16550_REGS_NUM + UART_IIR] & UART_IIR_THR);
> +}
> +
> +static bool cf_check ns16550_iir_check_msi(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[UART_MSR] & UART_MSR_CHANGE);
> +}
> +
> +/*
> + * Get the interrupt identity reason.
> + *
> + * IIR is re-calculated once called, because NS16550 always reports high
> + * priority events first.
> + * regs[NS16550_REGS_NUM + UART_IIR] is used to store THR reason only.
> + */
> +static uint8_t ns16550_iir_get(const struct vuart_ns16550 *vdev)
> +{
> + /*
> + * Interrupt identity reasons by priority.
> + * NB: high priority are at lower indexes below.
> + */
> + static const struct {
> + bool (*check)(const struct vuart_ns16550 *vdev);
> + uint8_t ier;
> + uint8_t iir;
> + } iir_by_prio[] = {
> + [0] = { ns16550_iir_check_lsi, UART_IER_ELSI, UART_IIR_LSI },
> + [1] = { ns16550_iir_check_rda, UART_IER_ERDAI, UART_IIR_RDA },
> + [2] = { ns16550_iir_check_thr, UART_IER_ETHREI, UART_IIR_THR },
> + [3] = { ns16550_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/ NS16550 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 ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> +{
> + struct domain *d = vdev->owner;
> + int vector;
> +
> + if ( has_vpic(d) ) /* HVM */
> + vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
> + else
> + ASSERT_UNREACHABLE();
> +
> + pr_debug("%s: IRQ#%d vector %d assert\n", vdev->name, vdev->irq, vector);
> +}
> +
> +static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> +{
> + struct domain *d = vdev->owner;
> +
> + if ( has_vpic(d) ) /* HVM */
> + hvm_isa_irq_deassert(d, vdev->irq);
> + else
> + ASSERT_UNREACHABLE();
> +
> + pr_debug("%s: IRQ#%d deassert\n", vdev->name, vdev->irq);
> +}
> +
> +/*
> + * Assert/deassert virtual NS16550 interrupt line.
> + */
> +static void ns16550_irq_check(const struct vuart_ns16550 *vdev)
> +{
> + uint8_t iir = ns16550_iir_get(vdev);
> +
> + if ( iir & UART_IIR_NOINT )
> + ns16550_irq_assert(vdev);
> + else
> + ns16550_irq_deassert(vdev);
> +
> + pr_debug("%s: IRQ#%d IIR 0x%02x %s\n", vdev->name, vdev->irq, iir,
> + (iir & UART_IIR_NOINT) ? "deassert" : "assert");
> +}
> +
> +/*
> + * Emulate 8-bit write access to NS16550 register.
> + */
> +static int ns16550_io_write8(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> +{
> + uint8_t *regs = vdev->regs;
> + uint8_t val = *data;
> + int rc = 0;
> +
> + if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> + regs[NS16550_REGS_NUM + reg] = val;
> + else
> + {
> + switch ( reg )
> + {
> + case UART_THR:
> + if ( regs[UART_MCR] & UART_MCR_LOOP )
> + {
> + (void)ns16550_fifo_rx_putchar(vdev, val);
> + regs[UART_LSR] |= UART_LSR_OE;
Why is UART_LSR_OE set unconditionally here instead of checking if
ns16550_fifo_rx_putchar returned -ENOSPC?
> + }
> + else
> + ns16550_fifo_tx_putchar(vdev, val);
> +
> + regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> + break;
> +
> + case UART_IER:
> + /*
> + * NB: Make sure THR interrupt is re-triggered once guest OS
> + * re-enabled ETHREI in EIR.
> + */
> + if ( val & regs[UART_IER] & UART_IER_ETHREI )
> + regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> + regs[UART_IER] = val & UART_IER_MASK;
> +
> + break;
> +
> + case UART_FCR: /* WO */
> + if ( val & UART_FCR_RESERVED0 )
> + pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
> + vdev->name, UART_FCR_RESERVED0);
> +
> + if ( val & UART_FCR_RESERVED1 )
> + pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
> + vdev->name, UART_FCR_RESERVED1);
> +
> + if ( val & UART_FCR_CLRX )
> + ns16550_fifo_rx_reset(vdev);
> +
> + if ( val & UART_FCR_CLTX )
> + ns16550_fifo_tx_flush(vdev);
> +
> + 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;
> +
> + 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 )
> + pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
> + vdev->name, UART_MCR_RESERVED0);
> +
> + if ( val & UART_MCR_TCRTLR )
> + pr_warn("%s: MCR: not supported: %x\n",
> + vdev->name, UART_MCR_TCRTLR);
> +
> + if ( val & UART_MCR_RESERVED1 )
> + pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
> + vdev->name, 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_MCR_RTS) ^ (msr_next & UART_MCR_RTS) )
> + msr_delta |= UART_MSR_DDSR;
Should we check UART_MSR_DSR instead of UART_MCR_RTS to set
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;
> + }
> +
> + ns16550_irq_check(vdev);
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate 16-bit write access to NS16550 register.
> + * NB: some guest OSes use outw() to access UART_DLL.
> + */
> +static int ns16550_io_write16(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
> +{
> + uint16_t val = *data;
> + int rc;
> +
> + if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
> + {
> + vdev->regs[NS16550_REGS_NUM + UART_DLL] = val & 0xff;
> + vdev->regs[NS16550_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
> + rc = 0;
> + }
> + else
> + rc = -EINVAL;
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate write access to NS16550 register.
> + */
> +static int ns16550_io_write(
> + struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
> +{
> + int rc;
> +
> + switch ( size )
> + {
> + case 1:
> + rc = ns16550_io_write8(vdev, reg, (uint8_t *)data);
> + break;
> +
> + case 2:
> + rc = ns16550_io_write16(vdev, reg, (uint16_t *)data);
> + break;
> +
> + default:
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate 8-bit read access to NS16550 register.
> + */
> +static int ns16550_io_read8(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> +{
> + uint8_t *regs = vdev->regs;
> + uint8_t val = 0xff;
> + int rc = 0;
> +
> + if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> + val = regs[NS16550_REGS_NUM + reg];
> + else {
> + switch ( reg )
> + {
> + case UART_RBR:
> + /* NB: do not forget to clear overrun condition */
> + regs[UART_LSR] &= ~UART_LSR_OE;
> +
> + rc = ns16550_fifo_rx_getchar(vdev);
> + if ( rc >= 0 )
> + val = (uint8_t)rc;
> +
> + rc = 0;
> + break;
> +
> + case UART_IER:
> + val = regs[UART_IER];
> + break;
> +
> + case UART_IIR: /* RO */
> + val = ns16550_iir_get(vdev);
> +
> + /* NB: clear IIR scratch location */
> + if ( val & UART_IIR_THR )
> + regs[NS16550_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
> +
> + break;
> +
> + case UART_LCR:
> + 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;
> + if ( ns16550_fifo_rx_empty(vdev) )
> + val &= ~UART_LSR_DR;
> + else
> + val |= UART_LSR_DR;
> +
> + 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;
> +
> + default:
> + rc = -EINVAL;
> + break;
> + }
> +
> + ns16550_irq_check(vdev);
> + }
> +
> + *data = val;
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate 16-bit read access to NS16550 register.
> + */
> +static int ns16550_io_read16(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
> +{
> + uint16_t val = 0xffff;
> + int rc = -EINVAL;
> +
> + if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
> + {
> + val = vdev->regs[NS16550_REGS_NUM + UART_DLM] << 8 |
> + vdev->regs[NS16550_REGS_NUM + UART_DLL];
> + rc = 0;
> + }
> +
> + *data = val;
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate read access to NS16550 register.
> + */
> +static int ns16550_io_read(
> + struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
> +{
> + int rc;
> +
> + switch ( size )
> + {
> + case 1:
> + rc = ns16550_io_read8(vdev, reg, (uint8_t *)data);
> + break;
> +
> + case 2:
> + rc = ns16550_io_read16(vdev, reg, (uint16_t *)data);
> + break;
> +
> + default:
> + *data = 0xffffffff;
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +static void cf_check ns16550_dump_state(const struct domain *d)
> +{
> + struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> + 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%04"PRIx64" IRQ#%d owner %pd\n",
> + vdev->name, vdev->io_addr, vdev->irq, vdev->owner);
> +
> + 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[NS16550_REGS_NUM + UART_DLL],
> + regs[NS16550_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], ns16550_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);
> +}
> +
> +/*
> + * Emulate I/O access to NS16550 register.
> + * Note, emulation always returns X86EMUL_OKAY, once I/O port trap is enabled.
> + */
> +static int cf_check ns16550_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_ns16550 *vdev = d->arch.hvm.vuart;
> + uint32_t reg;
> + unsigned dlab;
> + int rc;
> +
> + if ( !vdev )
> + {
> + pr_err("%s: %c io 0x%04x %d: not initialized\n",
> + vdev->name, op(dir), addr, size);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> +
> + if ( d != vdev->owner )
> + {
> + pr_err("%s: %c io 0x%04x %d: does not match current domain %pv\n",
> + vdev->name, op(dir), addr, size, d);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> +
> + reg = addr - vdev->io_addr;
> + if ( !IS_ALIGNED(reg, size) )
> + {
> + pr_err("%s: %c 0x%04x %d: unaligned access\n",
> + vdev->name, op(dir), addr, size);
> + goto out;
> + }
> +
> + dlab = ns16550_dlab_get(vdev);
> + if ( reg >= NS16550_REGS_NUM )
> + {
> + pr_err("%s: %c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> + goto out;
> + }
> +
> + spin_lock(&vdev->lock);
> +
> + if ( dir == IOREQ_WRITE )
> + {
> + pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> + rc = ns16550_io_write(vdev, reg, size, data);
> + }
> + else
> + {
> + rc = ns16550_io_read(vdev, reg, size, data);
> + pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> + }
> + if ( rc < 0 )
> + pr_err("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> +
> + spin_unlock(&vdev->lock);
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> + ns16550_dump_state(d);
> +#endif
> +
> +out:
> + rcu_unlock_domain(d);
> +
> + return X86EMUL_OKAY;
> +#undef op
> +}
> +
> +static int cf_check ns16550_init(struct domain *d,
> + struct vuart_params *params)
> +{
> + const struct virtdev_desc *desc = &x86_pc_uarts[X86_PC_UART_IDX];
> + const struct resource *r = desc->res;
> + const uint16_t divisor = (UART_CLOCK_HZ / 115200) >> 4;
> + struct vuart_ns16550 *vdev;
> + int rc;
> +
> + BUG_ON(d->arch.hvm.vuart);
> +
> + if ( !is_hvm_domain(d) )
> + {
> + pr_err("%s: not an HVM domain\n", desc->name);
> + return -ENOSYS;
> + }
> +
> + vdev = xvzalloc(typeof(*vdev));
> + if ( !vdev )
> + {
> + pr_err("%s: failed to allocate memory\n", desc->name);
> + return -ENOMEM;
> + }
> +
> + for_each_resource(r)
> + {
> + if ( r->type & IORESOURCE_IO )
> + {
> + /* Disallow sharing physical I/O port */
> + rc = ioports_deny_access(d, r->addr, r->addr + r->size - 1);
> + if ( rc )
> + {
> + pr_err("%s: virtual I/O port range [0x%04x"PRIx64"..0x%04x"PRIx64"]: conflict w/ physical range\n",
> + desc->name,
> + (unsigned int)r->addr,
> + (unsigned int)(r->addr + r->size - 1));
> + return rc;
> + }
> +
> + register_portio_handler(d, r->addr, r->size, ns16550_io_handle);
> +
> + vdev->io_addr = r->addr;
> + vdev->io_size = r->size;
> + }
> + else if ( r->type & IORESOURCE_IRQ )
> + {
> + /* Disallow sharing physical IRQ */
> + rc = irq_deny_access(d, r->addr);
> + if ( rc )
> + {
> + pr_err("%s: virtual IRQ#%"PRIu64": conflict w/ physical IRQ: %d\n",
> + desc->name, r->addr, rc);
> + return rc;
> + }
> +
> + vdev->irq = r->addr;
> + }
> + else
> + ASSERT_UNREACHABLE();
> + }
> +
> + spin_lock_init(&vdev->lock);
> +
> + vdev->owner = d;
> + vdev->name = desc->name;
> +
> + /* NB: report 115200 baud rate */
> + vdev->regs[NS16550_REGS_NUM + UART_DLL] = divisor & 0xff;
> + vdev->regs[NS16550_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
> +
> + /* NS16550 shall assert UART_IIR_THR whenever transmitter is empty. */
> + vdev->regs[NS16550_REGS_NUM + UART_IIR] = UART_IIR_THR;
> +
> + d->arch.hvm.vuart = vdev;
> +
> + spin_lock(&vdev->lock);
> + ns16550_irq_check(vdev);
> + spin_unlock(&vdev->lock);
> +
> + return 0;
> +}
> +
> +static void cf_check ns16550_deinit(struct domain *d)
> +{
> + struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> +
> + if ( !vdev )
> + return;
> +
> + spin_lock(&vdev->lock);
> +
> + ns16550_fifo_tx_flush(vdev);
> +
> + spin_unlock(&vdev->lock);
> +
> + XVFREE(d->arch.hvm.vuart);
> +}
> +
> +static int cf_check ns16550_put_rx(struct domain *d, char ch)
> +{
> + struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> + uint8_t *regs;
> + uint8_t dlab;
> + int rc;
> +
> + ASSERT(d == vdev->owner);
> + if ( !vdev )
> + return -ENODEV;
> +
> + spin_lock(&vdev->lock);
> +
> + dlab = ns16550_dlab_get(vdev);
> + regs = vdev->regs;
> +
> + if ( dlab )
> + {
> + pr_debug("%s: THR/RBR access disabled: DLAB=1\n", vdev->name);
> + rc = -EBUSY;
> + }
> + else if ( regs[UART_MCR] & UART_MCR_LOOP )
> + {
> + pr_debug("%s: THR/RBR access disabled: loopback mode\n", vdev->name);
> + rc = -EBUSY;
> + }
> + else
> + {
> + uint8_t val = 0;
> +
> + rc = ns16550_fifo_rx_putchar(vdev, ch);
> + if ( rc == -ENOSPC )
> + val |= UART_LSR_OE;
> +
> + /* NB: UART_LSR_DR is also set when UART_LSR is accessed. */
> + regs[UART_LSR] |= UART_LSR_DR | val;
> +
> + /*
> + * Echo the user input on Xen console iff Xen console input is owned
> + * by NS16550 domain.
> + * NB: use 'console_timestamps=none' to disable Xen timestamps.
> + */
> + if ( is_console_printable(ch) )
> + guest_printk(d, "%c", ch);
> +
> + /* FIXME: check FCR when to fire an interrupt */
> + ns16550_irq_check(vdev);
> + }
> +
> + spin_unlock(&vdev->lock);
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> + ns16550_dump_state(d);
> +#endif
> +
> + return rc;
> +}
> +
> +static const struct vuart_ops ns16550_ops = {
> + .add_node = NULL,
> + .init = ns16550_init,
> + .deinit = ns16550_deinit,
> + .dump_state = ns16550_dump_state,
> + .put_rx = ns16550_put_rx,
> +};
> +
> +VUART_REGISTER(ns16550, &ns16550_ops);
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> index 14a7f8bd8b79..7971813a723d 100644
> --- a/xen/common/emul/vuart/vuart.c
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -99,6 +99,10 @@ bool domain_has_vuart(const struct domain *d)
> {
> uint32_t mask = 0;
>
> +#ifdef CONFIG_VUART_NS16550
> + mask |= XEN_X86_EMU_NS16550;
> +#endif
> +
> return !!(d->emulation_flags & mask);
> }
>
> diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h
> index fc2487986642..f905e1252c70 100644
> --- a/xen/include/public/arch-x86/xen.h
> +++ b/xen/include/public/arch-x86/xen.h
> @@ -283,13 +283,15 @@ struct xen_arch_domainconfig {
> #define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ)
> #define _XEN_X86_EMU_VPCI 10
> #define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI)
> +#define _XEN_X86_EMU_NS16550 11
> +#define XEN_X86_EMU_NS16550 (1U<<_XEN_X86_EMU_NS16550)
>
> #define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \
> XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \
> XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \
> XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \
> XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\
> - XEN_X86_EMU_VPCI)
> + XEN_X86_EMU_VPCI | XEN_X86_EMU_NS16550)
> uint32_t emulation_flags;
>
> /*
> diff --git a/xen/include/xen/resource.h b/xen/include/xen/resource.h
> index 5d103631288d..56fb8101edd6 100644
> --- a/xen/include/xen/resource.h
> +++ b/xen/include/xen/resource.h
> @@ -31,4 +31,7 @@ struct resource {
>
> #define resource_size(res) ((res)->size)
>
> +#define for_each_resource(res) \
> + for ( ; (res) && (res)->type != IORESOURCE_UNKNOWN; (res)++ )
> +
> #endif /* XEN__RESOURCE_H */
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-07-31 23:57 ` Stefano Stabellini
@ 2025-08-01 3:28 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-01 3:28 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Thu, Jul 31, 2025 at 04:57:00PM -0700, Stefano Stabellini wrote:
> On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
[..]
> > +/*
> > + * Emulate 8-bit write access to NS16550 register.
> > + */
> > +static int ns16550_io_write8(
> > + struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> > +{
> > + uint8_t *regs = vdev->regs;
> > + uint8_t val = *data;
> > + int rc = 0;
> > +
> > + if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> > + regs[NS16550_REGS_NUM + reg] = val;
> > + else
> > + {
> > + switch ( reg )
> > + {
> > + case UART_THR:
> > + if ( regs[UART_MCR] & UART_MCR_LOOP )
> > + {
> > + (void)ns16550_fifo_rx_putchar(vdev, val);
> > + regs[UART_LSR] |= UART_LSR_OE;
>
> Why is UART_LSR_OE set unconditionally here instead of checking if
> ns16550_fifo_rx_putchar returned -ENOSPC?
Thanks!
Yes, overrun condition should be emulated once FIFO is full.
[..]
> > +
> > + /* 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_MCR_RTS) ^ (msr_next & UART_MCR_RTS) )
> > + msr_delta |= UART_MSR_DDSR;
>
> Should we check UART_MSR_DSR instead of UART_MCR_RTS to set
> UART_MSR_DDSR ?
Good catch!
Thanks.
[..]
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-07-31 19:22 ` [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86) dmkhn
2025-07-31 23:57 ` Stefano Stabellini
@ 2025-08-04 10:53 ` Jan Beulich
2025-08-09 18:37 ` dmkhn
2025-08-06 15:06 ` Roger Pau Monné
2 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 10:53 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:22, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add initial in-hypervisor emulator for NS8250/NS16x50-compatible UARTs under
> CONFIG_VUART_NS16550 for x86 port of Xen.
>
> x86 port of Xen lacks vUART facility similar to Arm's SBSA emulator to support
> x86 guest OS bring up in the embedded setups.
>
> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> early guest firmware and/or 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. Such functionality is limited to dom0
> use currently.
>
> By default, CONFIG_VUART_NS16550 enables emulation of NS16550 at I/O port
> 0x3f8, IRQ#4 in guest OS (legacy COM1).
>
> Legacy COM resources can be selected at built-time and cannot be configured
> per-domain via .cfg or DT yet.
>
> Introduce new emulation flag for virtual UART on x86 and plumb it through
> domain creation code so NS16550 emulator can be instantiated properly.
>
> Please refer to the NS16550 emulator code for full list of limitations.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v3:
> - feedback addressed
> - adjusted to new vUART framework APIs
> - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-21-c5d36b31d66c@ford.com/
> ---
> xen/arch/x86/hvm/hvm.c | 9 +
> xen/arch/x86/include/asm/domain.h | 4 +-
> xen/arch/x86/include/asm/hvm/domain.h | 4 +
> xen/common/emul/vuart/Kconfig | 48 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/vuart-ns16550.c | 1009 +++++++++++++++++++++++++
> xen/common/emul/vuart/vuart.c | 4 +
> xen/include/public/arch-x86/xen.h | 4 +-
> xen/include/xen/resource.h | 3 +
> 9 files changed, 1084 insertions(+), 2 deletions(-)
> create mode 100644 xen/common/emul/vuart/vuart-ns16550.c
Overall I think this patch is too large to sensibly review. Surely base structure
and then (incrementally) fleshing out of the hooks can be separated from one
another?
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -31,6 +31,7 @@
> #include <xen/nospec.h>
> #include <xen/vm_event.h>
> #include <xen/console.h>
> +#include <xen/vuart.h>
> #include <asm/shadow.h>
> #include <asm/hap.h>
> #include <asm/current.h>
> @@ -702,6 +703,10 @@ int hvm_domain_initialise(struct domain *d,
> if ( rc != 0 )
> goto fail1;
>
> + rc = vuart_init(d, NULL);
> + if ( rc != 0 )
> + goto out_vioapic_deinit;
> +
> stdvga_init(d);
>
> rtc_init(d);
> @@ -725,6 +730,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) )
Would be better if vuart_deinit() was idempotent, and hence could be called
unconditionally here.
> @@ -787,6 +794,8 @@ void hvm_domain_destroy(struct domain *d)
> if ( hvm_funcs.domain_destroy )
> alternative_vcall(hvm_funcs.domain_destroy, d);
>
> + vuart_deinit(d);
You require a fair level of idempotency already anyway, as a domain may not
have any vUART, so this call already needs to be "capabale" of doing nothing.
> --- a/xen/arch/x86/include/asm/hvm/domain.h
> +++ b/xen/arch/x86/include/asm/hvm/domain.h
> @@ -149,6 +149,10 @@ struct hvm_domain {
> #ifdef CONFIG_MEM_SHARING
> struct mem_sharing_domain mem_sharing;
> #endif
> +
> +#ifdef CONFIG_VUART_NS16550
> + void *vuart; /* Virtual UART handle. */
> +#endif
> };
With your framework you allow for multiple vUART drivers. Either the field
looks misnamed or the CONFIG_* option checked is the wrong one.
Also, why's this x86-specific? NS16550s can exist anywhere, can't they?
(The present, but presumably temporary tying to x86 looks to be the use of
I/O ports.)
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,52 @@ config HAS_VUART
>
> menu "UART Emulation"
>
> +config VUART_NS16550
> + bool "NS16550-compatible UART Emulation" if EXPERT
> + depends on X86 && HVM
> + select HAS_VUART
> + help
> + In-hypervisor NS16550/NS16x50 UART emulation.
> +
> + Only legacy PC I/O ports are emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
> +choice VUART_NS16550_PC
> + prompt "IBM PC COM resources"
> + depends on VUART_NS16550
> + default VUART_NS16550_PC_COM1
> + help
> + Default emulated NS16550 resources.
> +
> +config VUART_NS16550_PC_COM1
> + bool "COM1 (I/O port 0x3f8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM2
> + bool "COM2 (I/O port 0x2f8, IRQ#3)"
> +
> +config VUART_NS16550_PC_COM3
> + bool "COM3 (I/O port 0x3e8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM4
> + bool "COM4 (I/O port 0x2e8, IRQ#3)"
> +
> +endchoice
> +
> +config VUART_NS16550_LOG_LEVEL
> + int "UART emulator verbosity level"
> + range 0 3
> + default "1"
> + depends on VUART_NS16550
> + help
> + Set the default log level of UART emulator.
> + See include/xen/config.h for more details.
For someone merely running kconfig but not otherwise knowing the sources,
this isn't an overly helful pointer. But I question the need for such a
control anyway, and I think I did say so already before.
> +config VUART_NS16550_DEBUG
> + bool "UART emulator development debugging"
> + depends on VUART_NS16550
&& DEBUG ?
> --- a/xen/common/emul/vuart/Makefile
> +++ b/xen/common/emul/vuart/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_HAS_VUART) += vuart.o
> +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
I don't think files in this directory need a vuart- name prefix.
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart-ns16550.c
> @@ -0,0 +1,1009 @@
> +/* 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 HVM domains support (build-time), PVH domains are not supported yet;
> + * - Only legacy COM{1,2,3,4} resources via Kconfig, 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 ISA IRQ sharing allowed;
> + * - No MMIO-based UART emulation.
> + */
> +
> +#define pr_prefix "ns16550"
> +#define pr_fmt(fmt) pr_prefix ": " fmt
> +#define pr_log_level CONFIG_VUART_NS16550_LOG_LEVEL
> +
> +#include <xen/8250-uart.h>
> +#include <xen/console.h>
> +#include <xen/iocap.h>
> +#include <xen/ioreq.h>
> +#include <xen/resource.h>
> +#include <xen/vuart.h>
> +#include <xen/xvmalloc.h>
> +
> +#include <public/io/console.h>
Except for cases where Xen itself runs as a guest, I don't think any of these
headers should be used in Xen sources. If I'm not mistaken, ...
> +/*
> + * Virtual NS16550 device state.
> + */
> +struct vuart_ns16550 {
> + struct xencons_interface cons; /* Emulated RX/TX FIFOs */
... this also isn't to communicate with some remote, but merely to use some
of the fields conveniently.
> + uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
> + unsigned int irq; /* Emulated IRQ# */
> + uint64_t io_addr; /* Emulated I/O region base address */
> + uint64_t io_size; /* Emulated I/O region size */
These are huge; for the size that's true even if considering future MMIO-
based emulation.
> + const char *name; /* Device name */
> + struct domain *owner; /* Owner domain */
> + spinlock_t lock; /* Protection */
> +};
> +
> +/*
> + * Virtual device description.
> + */
> +struct virtdev_desc {
> + const char *name;
> + const struct resource *res;
> +};
> +
> +/*
> + * Legacy IBM PC NS16550 resources.
> + * There are only 4 I/O port ranges, hardcoding all of them here.
> + */
> +static const struct virtdev_desc x86_pc_uarts[4] = {
> + [0] = {
> + .name = "COM1",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x3f8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [1] = {
> + .name = "COM2",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x2f8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [2] = {
> + .name = "COM3",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x3e8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [3] = {
> + .name = "COM4",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x2e8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> +};
The choice of COMn is at build time. Why do we need all four configurations
resident not only in the binary, but even at (post-init) runtime? Also, the
way you do initialization of .res, I think adding __initconst to the main
array wouldn't have the effect of pulling all those inti .init.* as well.
For the time being I simply don't see the need for the extra level of
indirection: All instances have two entries (plus the then likely not
necessary sentinel).
> +static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);
No need for !! (also elsewhere).
> --- a/xen/include/xen/resource.h
> +++ b/xen/include/xen/resource.h
> @@ -31,4 +31,7 @@ struct resource {
>
> #define resource_size(res) ((res)->size)
>
> +#define for_each_resource(res) \
> + for ( ; (res) && (res)->type != IORESOURCE_UNKNOWN; (res)++ )
I'm not sure this is a good generic #define; imo it wants keeping local to
the one file that uses it.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-08-04 10:53 ` Jan Beulich
@ 2025-08-09 18:37 ` dmkhn
2025-08-11 7:39 ` Jan Beulich
0 siblings, 1 reply; 61+ messages in thread
From: dmkhn @ 2025-08-09 18:37 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 04, 2025 at 12:53:36PM +0200, Jan Beulich wrote:
> On 31.07.2025 21:22, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Add initial in-hypervisor emulator for NS8250/NS16x50-compatible UARTs under
> > CONFIG_VUART_NS16550 for x86 port of Xen.
> >
> > x86 port of Xen lacks vUART facility similar to Arm's SBSA emulator to support
> > x86 guest OS bring up in the embedded setups.
> >
> > In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> > early guest firmware and/or 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. Such functionality is limited to dom0
> > use currently.
> >
> > By default, CONFIG_VUART_NS16550 enables emulation of NS16550 at I/O port
> > 0x3f8, IRQ#4 in guest OS (legacy COM1).
> >
> > Legacy COM resources can be selected at built-time and cannot be configured
> > per-domain via .cfg or DT yet.
> >
> > Introduce new emulation flag for virtual UART on x86 and plumb it through
> > domain creation code so NS16550 emulator can be instantiated properly.
> >
> > Please refer to the NS16550 emulator code for full list of limitations.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> > ---
> > Changes since v3:
> > - feedback addressed
> > - adjusted to new vUART framework APIs
> > - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-21-c5d36b31d66c@ford.com/
> > ---
> > xen/arch/x86/hvm/hvm.c | 9 +
> > xen/arch/x86/include/asm/domain.h | 4 +-
> > xen/arch/x86/include/asm/hvm/domain.h | 4 +
> > xen/common/emul/vuart/Kconfig | 48 ++
> > xen/common/emul/vuart/Makefile | 1 +
> > xen/common/emul/vuart/vuart-ns16550.c | 1009 +++++++++++++++++++++++++
> > xen/common/emul/vuart/vuart.c | 4 +
> > xen/include/public/arch-x86/xen.h | 4 +-
> > xen/include/xen/resource.h | 3 +
> > 9 files changed, 1084 insertions(+), 2 deletions(-)
> > create mode 100644 xen/common/emul/vuart/vuart-ns16550.c
>
> Overall I think this patch is too large to sensibly review. Surely base structure
> and then (incrementally) fleshing out of the hooks can be separated from one
> another?
I'll do a split.
>
> > --- a/xen/arch/x86/hvm/hvm.c
> > +++ b/xen/arch/x86/hvm/hvm.c
> > @@ -31,6 +31,7 @@
> > #include <xen/nospec.h>
> > #include <xen/vm_event.h>
> > #include <xen/console.h>
> > +#include <xen/vuart.h>
> > #include <asm/shadow.h>
> > #include <asm/hap.h>
> > #include <asm/current.h>
> > @@ -702,6 +703,10 @@ int hvm_domain_initialise(struct domain *d,
> > if ( rc != 0 )
> > goto fail1;
> >
> > + rc = vuart_init(d, NULL);
> > + if ( rc != 0 )
> > + goto out_vioapic_deinit;
> > +
> > stdvga_init(d);
> >
> > rtc_init(d);
> > @@ -725,6 +730,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) )
>
> Would be better if vuart_deinit() was idempotent, and hence could be called
> unconditionally here.
Agree, vuart_deinit() is idempotent even in this submisson.
Will update.
>
> > @@ -787,6 +794,8 @@ void hvm_domain_destroy(struct domain *d)
> > if ( hvm_funcs.domain_destroy )
> > alternative_vcall(hvm_funcs.domain_destroy, d);
> >
> > + vuart_deinit(d);
>
> You require a fair level of idempotency already anyway, as a domain may not
> have any vUART, so this call already needs to be "capabale" of doing nothing.
>
> > --- a/xen/arch/x86/include/asm/hvm/domain.h
> > +++ b/xen/arch/x86/include/asm/hvm/domain.h
> > @@ -149,6 +149,10 @@ struct hvm_domain {
> > #ifdef CONFIG_MEM_SHARING
> > struct mem_sharing_domain mem_sharing;
> > #endif
> > +
> > +#ifdef CONFIG_VUART_NS16550
> > + void *vuart; /* Virtual UART handle. */
> > +#endif
> > };
>
> With your framework you allow for multiple vUART drivers. Either the field
> looks misnamed or the CONFIG_* option checked is the wrong one.
Agree; will update.
>
> Also, why's this x86-specific? NS16550s can exist anywhere, can't they?
> (The present, but presumably temporary tying to x86 looks to be the use of
> I/O ports.)
struct hvm_domain is arch-specific.
I do not think I need to add NS16550 to, say RISC-V's, hvm_domain without
implementing MMIO part and guest DT-binding generation.
>
> > --- a/xen/common/emul/vuart/Kconfig
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -3,4 +3,52 @@ config HAS_VUART
> >
> > menu "UART Emulation"
> >
> > +config VUART_NS16550
> > + bool "NS16550-compatible UART Emulation" if EXPERT
> > + depends on X86 && HVM
> > + select HAS_VUART
> > + help
> > + In-hypervisor NS16550/NS16x50 UART emulation.
> > +
> > + Only legacy PC I/O ports are emulated.
> > +
> > + This is strictly for testing purposes (such as early HVM guest console),
> > + and not appropriate for use in production.
> > +
> > +choice VUART_NS16550_PC
> > + prompt "IBM PC COM resources"
> > + depends on VUART_NS16550
> > + default VUART_NS16550_PC_COM1
> > + help
> > + Default emulated NS16550 resources.
> > +
> > +config VUART_NS16550_PC_COM1
> > + bool "COM1 (I/O port 0x3f8, IRQ#4)"
> > +
> > +config VUART_NS16550_PC_COM2
> > + bool "COM2 (I/O port 0x2f8, IRQ#3)"
> > +
> > +config VUART_NS16550_PC_COM3
> > + bool "COM3 (I/O port 0x3e8, IRQ#4)"
> > +
> > +config VUART_NS16550_PC_COM4
> > + bool "COM4 (I/O port 0x2e8, IRQ#3)"
> > +
> > +endchoice
> > +
> > +config VUART_NS16550_LOG_LEVEL
> > + int "UART emulator verbosity level"
> > + range 0 3
> > + default "1"
> > + depends on VUART_NS16550
> > + help
> > + Set the default log level of UART emulator.
> > + See include/xen/config.h for more details.
>
> For someone merely running kconfig but not otherwise knowing the sources,
> this isn't an overly helful pointer. But I question the need for such a
> control anyway, and I think I did say so already before.
I'll drop that Kconfig setting.
>
> > +config VUART_NS16550_DEBUG
> > + bool "UART emulator development debugging"
> > + depends on VUART_NS16550
>
> && DEBUG ?
I will drop that Kconfig.
>
> > --- a/xen/common/emul/vuart/Makefile
> > +++ b/xen/common/emul/vuart/Makefile
> > @@ -1 +1,2 @@
> > obj-$(CONFIG_HAS_VUART) += vuart.o
> > +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
>
> I don't think files in this directory need a vuart- name prefix.
Ack.
Hmm, there's already ns16550.c which is UART driver, so it may be confusing to
have two ns16550s (although in different directories).
I do not have a strong preference on the naming here.
>
> > --- /dev/null
> > +++ b/xen/common/emul/vuart/vuart-ns16550.c
> > @@ -0,0 +1,1009 @@
> > +/* 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 HVM domains support (build-time), PVH domains are not supported yet;
> > + * - Only legacy COM{1,2,3,4} resources via Kconfig, 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 ISA IRQ sharing allowed;
> > + * - No MMIO-based UART emulation.
> > + */
> > +
> > +#define pr_prefix "ns16550"
> > +#define pr_fmt(fmt) pr_prefix ": " fmt
> > +#define pr_log_level CONFIG_VUART_NS16550_LOG_LEVEL
> > +
> > +#include <xen/8250-uart.h>
> > +#include <xen/console.h>
> > +#include <xen/iocap.h>
> > +#include <xen/ioreq.h>
> > +#include <xen/resource.h>
> > +#include <xen/vuart.h>
> > +#include <xen/xvmalloc.h>
> > +
> > +#include <public/io/console.h>
>
> Except for cases where Xen itself runs as a guest, I don't think any of these
> headers should be used in Xen sources. If I'm not mistaken, ...
I'll double check, thanks.
>
> > +/*
> > + * Virtual NS16550 device state.
> > + */
> > +struct vuart_ns16550 {
> > + struct xencons_interface cons; /* Emulated RX/TX FIFOs */
>
> ... this also isn't to communicate with some remote, but merely to use some
> of the fields conveniently.
The plan is to add peer-to-peer connection over vUART similarly to existing
vpl011.
>
> > + uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
> > + unsigned int irq; /* Emulated IRQ# */
> > + uint64_t io_addr; /* Emulated I/O region base address */
> > + uint64_t io_size; /* Emulated I/O region size */
>
> These are huge; for the size that's true even if considering future MMIO-
> based emulation.
Ack.
>
> > + const char *name; /* Device name */
> > + struct domain *owner; /* Owner domain */
> > + spinlock_t lock; /* Protection */
> > +};
> > +
> > +/*
> > + * Virtual device description.
> > + */
> > +struct virtdev_desc {
> > + const char *name;
> > + const struct resource *res;
> > +};
> > +
> > +/*
> > + * Legacy IBM PC NS16550 resources.
> > + * There are only 4 I/O port ranges, hardcoding all of them here.
> > + */
> > +static const struct virtdev_desc x86_pc_uarts[4] = {
> > + [0] = {
> > + .name = "COM1",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x3f8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > + [1] = {
> > + .name = "COM2",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x2f8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > + [2] = {
> > + .name = "COM3",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x3e8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > + [3] = {
> > + .name = "COM4",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x2e8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > +};
>
> The choice of COMn is at build time. Why do we need all four configurations
> resident not only in the binary, but even at (post-init) runtime? Also, the
> way you do initialization of .res, I think adding __initconst to the main
> array wouldn't have the effect of pulling all those inti .init.* as well.
> For the time being I simply don't see the need for the extra level of
> indirection: All instances have two entries (plus the then likely not
> necessary sentinel).
Will rework that.
>
> > +static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
> > +{
> > + return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);
>
> No need for !! (also elsewhere).
Ack.
>
> > --- a/xen/include/xen/resource.h
> > +++ b/xen/include/xen/resource.h
> > @@ -31,4 +31,7 @@ struct resource {
> >
> > #define resource_size(res) ((res)->size)
> >
> > +#define for_each_resource(res) \
> > + for ( ; (res) && (res)->type != IORESOURCE_UNKNOWN; (res)++ )
>
> I'm not sure this is a good generic #define; imo it wants keeping local to
> the one file that uses it.
Ack.
>
> Jan
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-08-09 18:37 ` dmkhn
@ 2025-08-11 7:39 ` Jan Beulich
2025-08-12 0:06 ` dmkhn
0 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-11 7:39 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 09.08.2025 20:37, dmkhn@proton.me wrote:
> On Mon, Aug 04, 2025 at 12:53:36PM +0200, Jan Beulich wrote:
>> On 31.07.2025 21:22, dmkhn@proton.me wrote:
>>> --- a/xen/common/emul/vuart/Makefile
>>> +++ b/xen/common/emul/vuart/Makefile
>>> @@ -1 +1,2 @@
>>> obj-$(CONFIG_HAS_VUART) += vuart.o
>>> +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
>>
>> I don't think files in this directory need a vuart- name prefix.
>
> Ack.
>
> Hmm, there's already ns16550.c which is UART driver, so it may be confusing to
> have two ns16550s (although in different directories).
>
> I do not have a strong preference on the naming here.
We have several examples of files with the same name in distinct directories.
As an aside - is it really only 16550-s that you emulate? Otherwise the name
may want to be e.g. ns16x50.c or ns8250.c.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-08-11 7:39 ` Jan Beulich
@ 2025-08-12 0:06 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-12 0:06 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 11, 2025 at 09:39:42AM +0200, Jan Beulich wrote:
> On 09.08.2025 20:37, dmkhn@proton.me wrote:
> > On Mon, Aug 04, 2025 at 12:53:36PM +0200, Jan Beulich wrote:
> >> On 31.07.2025 21:22, dmkhn@proton.me wrote:
> >>> --- a/xen/common/emul/vuart/Makefile
> >>> +++ b/xen/common/emul/vuart/Makefile
> >>> @@ -1 +1,2 @@
> >>> obj-$(CONFIG_HAS_VUART) += vuart.o
> >>> +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
> >>
> >> I don't think files in this directory need a vuart- name prefix.
> >
> > Ack.
> >
> > Hmm, there's already ns16550.c which is UART driver, so it may be confusing to
> > have two ns16550s (although in different directories).
> >
> > I do not have a strong preference on the naming here.
>
> We have several examples of files with the same name in distinct directories.
> As an aside - is it really only 16550-s that you emulate? Otherwise the name
> may want to be e.g. ns16x50.c or ns8250.c.
I'll use ns16x50.c.
>
> Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-07-31 19:22 ` [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86) dmkhn
2025-07-31 23:57 ` Stefano Stabellini
2025-08-04 10:53 ` Jan Beulich
@ 2025-08-06 15:06 ` Roger Pau Monné
2025-08-06 17:24 ` Roger Pau Monné
2025-08-07 18:49 ` dmkhn
2 siblings, 2 replies; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 15:06 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Thu, Jul 31, 2025 at 07:22:06PM +0000, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Add initial in-hypervisor emulator for NS8250/NS16x50-compatible UARTs under
> CONFIG_VUART_NS16550 for x86 port of Xen.
>
> x86 port of Xen lacks vUART facility similar to Arm's SBSA emulator to support
> x86 guest OS bring up in the embedded setups.
>
> In parallel domain creation scenario (hyperlaunch), NS16550 emulator helps
> early guest firmware and/or 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. Such functionality is limited to dom0
> use currently.
>
> By default, CONFIG_VUART_NS16550 enables emulation of NS16550 at I/O port
> 0x3f8, IRQ#4 in guest OS (legacy COM1).
>
> Legacy COM resources can be selected at built-time and cannot be configured
> per-domain via .cfg or DT yet.
>
> Introduce new emulation flag for virtual UART on x86 and plumb it through
> domain creation code so NS16550 emulator can be instantiated properly.
>
> Please refer to the NS16550 emulator code for full list of limitations.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v3:
> - feedback addressed
> - adjusted to new vUART framework APIs
> - Link to v3: https://lore.kernel.org/xen-devel/20250103-vuart-ns8250-v3-v1-21-c5d36b31d66c@ford.com/
> ---
> xen/arch/x86/hvm/hvm.c | 9 +
> xen/arch/x86/include/asm/domain.h | 4 +-
> xen/arch/x86/include/asm/hvm/domain.h | 4 +
> xen/common/emul/vuart/Kconfig | 48 ++
> xen/common/emul/vuart/Makefile | 1 +
> xen/common/emul/vuart/vuart-ns16550.c | 1009 +++++++++++++++++++++++++
> xen/common/emul/vuart/vuart.c | 4 +
> xen/include/public/arch-x86/xen.h | 4 +-
> xen/include/xen/resource.h | 3 +
> 9 files changed, 1084 insertions(+), 2 deletions(-)
> create mode 100644 xen/common/emul/vuart/vuart-ns16550.c
>
> diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
> index b7edb1d6555d..1156e7ebcc4c 100644
> --- a/xen/arch/x86/hvm/hvm.c
> +++ b/xen/arch/x86/hvm/hvm.c
> @@ -31,6 +31,7 @@
> #include <xen/nospec.h>
> #include <xen/vm_event.h>
> #include <xen/console.h>
> +#include <xen/vuart.h>
> #include <asm/shadow.h>
> #include <asm/hap.h>
> #include <asm/current.h>
> @@ -702,6 +703,10 @@ int hvm_domain_initialise(struct domain *d,
> if ( rc != 0 )
> goto fail1;
>
> + rc = vuart_init(d, NULL);
> + if ( rc != 0 )
> + goto out_vioapic_deinit;
> +
> stdvga_init(d);
>
> rtc_init(d);
> @@ -725,6 +730,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) )
> @@ -787,6 +794,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/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h
> index eafd5cfc903d..1ecc7c2cae32 100644
> --- a/xen/arch/x86/include/asm/domain.h
> +++ b/xen/arch/x86/include/asm/domain.h
> @@ -468,6 +468,7 @@ struct arch_domain
> #define X86_EMU_IOMMU XEN_X86_EMU_IOMMU
> #define X86_EMU_USE_PIRQ XEN_X86_EMU_USE_PIRQ
> #define X86_EMU_VPCI XEN_X86_EMU_VPCI
> +#define X86_EMU_NS16550 XEN_X86_EMU_NS16550
> #else
> #define X86_EMU_LAPIC 0
> #define X86_EMU_HPET 0
> @@ -479,6 +480,7 @@ struct arch_domain
> #define X86_EMU_IOMMU 0
> #define X86_EMU_USE_PIRQ 0
> #define X86_EMU_VPCI 0
> +#define X86_EMU_NS16550 0
> #endif
>
> #define X86_EMU_PIT XEN_X86_EMU_PIT
> @@ -489,7 +491,7 @@ struct arch_domain
> X86_EMU_IOAPIC | X86_EMU_PIC | \
> X86_EMU_VGA | X86_EMU_IOMMU | \
> X86_EMU_PIT | X86_EMU_USE_PIRQ | \
> - X86_EMU_VPCI)
> + X86_EMU_VPCI | X86_EMU_NS16550)
>
> #define has_vlapic(d) (!!((d)->emulation_flags & X86_EMU_LAPIC))
> #define has_vhpet(d) (!!((d)->emulation_flags & X86_EMU_HPET))
> diff --git a/xen/arch/x86/include/asm/hvm/domain.h b/xen/arch/x86/include/asm/hvm/domain.h
> index 333501d5f2ac..9945b16d1a6e 100644
> --- a/xen/arch/x86/include/asm/hvm/domain.h
> +++ b/xen/arch/x86/include/asm/hvm/domain.h
> @@ -149,6 +149,10 @@ struct hvm_domain {
> #ifdef CONFIG_MEM_SHARING
> struct mem_sharing_domain mem_sharing;
> #endif
> +
> +#ifdef CONFIG_VUART_NS16550
> + void *vuart; /* Virtual UART handle. */
> +#endif
> };
>
> #endif /* __ASM_X86_HVM_DOMAIN_H__ */
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index 02f7dd6dc1a1..ebefd90d913e 100644
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -3,4 +3,52 @@ config HAS_VUART
>
> menu "UART Emulation"
>
> +config VUART_NS16550
> + bool "NS16550-compatible UART Emulation" if EXPERT
> + depends on X86 && HVM
> + select HAS_VUART
> + help
> + In-hypervisor NS16550/NS16x50 UART emulation.
> +
> + Only legacy PC I/O ports are emulated.
> +
> + This is strictly for testing purposes (such as early HVM guest console),
> + and not appropriate for use in production.
> +
> +choice VUART_NS16550_PC
> + prompt "IBM PC COM resources"
> + depends on VUART_NS16550
> + default VUART_NS16550_PC_COM1
> + help
> + Default emulated NS16550 resources.
> +
> +config VUART_NS16550_PC_COM1
> + bool "COM1 (I/O port 0x3f8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM2
> + bool "COM2 (I/O port 0x2f8, IRQ#3)"
> +
> +config VUART_NS16550_PC_COM3
> + bool "COM3 (I/O port 0x3e8, IRQ#4)"
> +
> +config VUART_NS16550_PC_COM4
> + bool "COM4 (I/O port 0x2e8, IRQ#3)"
> +
> +endchoice
You can turn this into an index define here in Kconfig, using:
config VUART_NS16550_PC_IDX
depends on VUART_NS16550
int
default 3 if VUART_NS16550_PC_COM4
default 2 if VUART_NS16550_PC_COM3
default 1 if VUART_NS16550_PC_COM2
default 0
Or similar, seeing what you do with X86_PC_UART_IDX below.
> +
> +config VUART_NS16550_LOG_LEVEL
> + int "UART emulator verbosity level"
> + range 0 3
> + default "1"
> + depends on VUART_NS16550
> + help
> + Set the default log level of UART emulator.
> + See include/xen/config.h for more details.
> +
> +config VUART_NS16550_DEBUG
> + bool "UART emulator development debugging"
> + depends on VUART_NS16550
> + help
> + Enable development debugging.
> +
> endmenu
> diff --git a/xen/common/emul/vuart/Makefile b/xen/common/emul/vuart/Makefile
> index c6400b001e85..85650ca5d8ce 100644
> --- a/xen/common/emul/vuart/Makefile
> +++ b/xen/common/emul/vuart/Makefile
> @@ -1 +1,2 @@
> obj-$(CONFIG_HAS_VUART) += vuart.o
> +obj-$(CONFIG_VUART_NS16550) += vuart-ns16550.o
> diff --git a/xen/common/emul/vuart/vuart-ns16550.c b/xen/common/emul/vuart/vuart-ns16550.c
> new file mode 100644
> index 000000000000..48bbf58264fe
> --- /dev/null
> +++ b/xen/common/emul/vuart/vuart-ns16550.c
> @@ -0,0 +1,1009 @@
> +/* 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 HVM domains support (build-time), PVH domains are not supported yet;
> + * - Only legacy COM{1,2,3,4} resources via Kconfig, 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 ISA IRQ sharing allowed;
> + * - No MMIO-based UART emulation.
Listing the limitations here might not be the beast approach, this is
likely to get out of sync as it's too far away from the code
implementation.
If anything those comments want to be closer together to where the
feature would otherwise be implemented.
> + */
> +
> +#define pr_prefix "ns16550"
> +#define pr_fmt(fmt) pr_prefix ": " fmt
> +#define pr_log_level CONFIG_VUART_NS16550_LOG_LEVEL
> +
> +#include <xen/8250-uart.h>
> +#include <xen/console.h>
> +#include <xen/iocap.h>
> +#include <xen/ioreq.h>
> +#include <xen/resource.h>
> +#include <xen/vuart.h>
> +#include <xen/xvmalloc.h>
> +
> +#include <public/io/console.h>
> +
> +#define pr_err(fmt, args...) do { \
> + gprintk(KERN_ERR, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_warn(fmt, args...) do { \
> + if ( pr_log_level >= 1) \
> + gprintk(KERN_WARNING, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_info(fmt, args...) do { \
> + if ( pr_log_level >= 2 ) \
> + gprintk(KERN_INFO, pr_fmt(fmt), ## args); \
> +} while (0)
> +
> +#define pr_debug(fmt, args...) do { \
> + if ( pr_log_level >= 3 ) \
> + gprintk(KERN_DEBUG, pr_fmt(fmt), ## args); \
> +} while (0)
We would use the pr_* set of logging functions for code imported from
Linux, but for Xen code we would directly use the gprintk() functions
rather than wrap them as you do.
> +
> +#if defined(CONFIG_VUART_NS16550_PC_COM1)
> +#define X86_PC_UART_IDX 0
> +#elif defined(CONFIG_VUART_NS16550_PC_COM2)
> +#define X86_PC_UART_IDX 1
> +#elif defined(CONFIG_VUART_NS16550_PC_COM3)
> +#define X86_PC_UART_IDX 2
> +#elif defined(CONFIG_VUART_NS16550_PC_COM4)
> +#define X86_PC_UART_IDX 3
> +#else
> +#error "Unsupported I/O port"
> +#endif
> +
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> +#define guest_prefix "FROM GUEST "
> +#else
> +#define guest_prefix ""
> +#endif
> +
> +/*
> + * Number of supported registers in the UART.
> + */
> +#define NS16550_REGS_NUM ( UART_SCR + 1 )
Extra spaces around parentheses? (here and below)
> +
> +/*
> + * Number of emulated registers.
> + *
> + * - Emulated registers [0..NS16550_REGS_NUM] are R/W registers for DLAB=0.
> + * - DLAB=1, R/W, DLL = NS16550_REGS_NUM + 0
> + * - DLAB=1, R/W, DLM = NS16550_REGS_NUM + 1
> + * - R/O, IIR (IIR_THR) = NS16550_REGS_NUM + 2
> + */
> +#define NS16550_EMU_REGS_NUM ( NS16550_REGS_NUM + 3 )
> +
> +/*
> + * Virtual NS16550 device state.
> + */
> +struct vuart_ns16550 {
> + struct xencons_interface cons; /* Emulated RX/TX FIFOs */
> + uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
> + unsigned int irq; /* Emulated IRQ# */
> + uint64_t io_addr; /* Emulated I/O region base address */
> + uint64_t io_size; /* Emulated I/O region size */
> + const char *name; /* Device name */
> + struct domain *owner; /* Owner domain */
> + spinlock_t lock; /* Protection */
> +};
> +
> +/*
> + * Virtual device description.
> + */
> +struct virtdev_desc {
> + const char *name;
> + const struct resource *res;
> +};
> +
> +/*
> + * Legacy IBM PC NS16550 resources.
> + * There are only 4 I/O port ranges, hardcoding all of them here.
> + */
> +static const struct virtdev_desc x86_pc_uarts[4] = {
> + [0] = {
You don't need the explicit array indexes?
> + .name = "COM1",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x3f8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [1] = {
> + .name = "COM2",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x2f8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [2] = {
> + .name = "COM3",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x3e8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> + [3] = {
> + .name = "COM4",
> + .res = (const struct resource[]){
> + { .type = IORESOURCE_IO, .addr = 0x2e8, .size = NS16550_REGS_NUM },
> + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> + { .type = IORESOURCE_UNKNOWN },
> + },
> + },
> +};
> +
> +static bool ns16550_fifo_rx_empty(const struct vuart_ns16550 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->in_prod == cons->in_cons;
> +}
> +
> +static bool ns16550_fifo_rx_full(const struct vuart_ns16550 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->in_prod - cons->in_cons == ARRAY_SIZE(cons->in);
> +}
> +
> +static void ns16550_fifo_rx_reset(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + cons->in_cons = cons->in_prod;
> +}
> +
> +static int ns16550_fifo_rx_getchar(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> + int rc;
> +
> + if ( ns16550_fifo_rx_empty(vdev) )
> + {
> + pr_debug("%s: RX FIFO empty\n", vdev->name);
> + rc = -ENODATA;
> + }
> + else
> + {
> + rc = cons->in[MASK_XENCONS_IDX(cons->in_cons, cons->in)];
> + cons->in_cons++;
> + }
> +
> + return rc;
> +}
> +
> +static int ns16550_fifo_rx_putchar(struct vuart_ns16550 *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 ( ns16550_fifo_rx_full(vdev) )
> + {
> + pr_debug("%s: RX FIFO full; resetting\n", vdev->name);
> + ns16550_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 bool ns16550_fifo_tx_empty(const struct vuart_ns16550 *vdev)
> +{
> + const struct xencons_interface *cons = &vdev->cons;
> +
> + return cons->out_prod == cons->out_cons;
> +}
> +
> +static void ns16550_fifo_tx_reset(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + cons->out_prod = 0;
> + ASSERT(cons->out_cons == cons->out_prod);
> +}
> +
> +/*
> + * Flush cached output to Xen console.
> + */
> +static void ns16550_fifo_tx_flush(struct vuart_ns16550 *vdev)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + if ( ns16550_fifo_tx_empty(vdev) )
> + return;
> +
> + ASSERT(cons->out_prod < ARRAY_SIZE(cons->out));
> + cons->out[cons->out_prod] = '\0';
> + cons->out_prod++;
> +
> + guest_printk(vdev->owner, guest_prefix "%s", cons->out);
> +
> + ns16550_fifo_tx_reset(vdev);
> +}
> +
> +/*
> + * Accumulate guest OS output before sending to Xen console.
> + */
> +static void ns16550_fifo_tx_putchar(struct vuart_ns16550 *vdev, char ch)
> +{
> + struct xencons_interface *cons = &vdev->cons;
> +
> + if ( !is_console_printable(ch) )
> + return;
> +
> + if ( ch != '\0' )
> + {
> + cons->out[cons->out_prod] = ch;
> + cons->out_prod++;
> + }
> +
> + if ( cons->out_prod == ARRAY_SIZE(cons->out) - 1 ||
> + ch == '\n' || ch == '\0' )
> + ns16550_fifo_tx_flush(vdev);
> +}
> +
> +static inline uint8_t cf_check ns16550_dlab_get(const struct vuart_ns16550 *vdev)
> +{
> + return vdev->regs[UART_LCR] & UART_LCR_DLAB ? 1 : 0;
> +}
> +
> +static bool cf_check ns16550_iir_check_lsi(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[UART_LSR] & UART_LSR_MASK);
> +}
> +
> +static bool cf_check ns16550_iir_check_rda(const struct vuart_ns16550 *vdev)
> +{
> + return !ns16550_fifo_rx_empty(vdev);
> +}
> +
> +static bool cf_check ns16550_iir_check_thr(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[NS16550_REGS_NUM + UART_IIR] & UART_IIR_THR);
> +}
> +
> +static bool cf_check ns16550_iir_check_msi(const struct vuart_ns16550 *vdev)
> +{
> + return !!(vdev->regs[UART_MSR] & UART_MSR_CHANGE);
> +}
> +
> +/*
> + * Get the interrupt identity reason.
> + *
> + * IIR is re-calculated once called, because NS16550 always reports high
> + * priority events first.
> + * regs[NS16550_REGS_NUM + UART_IIR] is used to store THR reason only.
> + */
> +static uint8_t ns16550_iir_get(const struct vuart_ns16550 *vdev)
> +{
> + /*
> + * Interrupt identity reasons by priority.
> + * NB: high priority are at lower indexes below.
> + */
> + static const struct {
> + bool (*check)(const struct vuart_ns16550 *vdev);
> + uint8_t ier;
> + uint8_t iir;
> + } iir_by_prio[] = {
> + [0] = { ns16550_iir_check_lsi, UART_IER_ELSI, UART_IIR_LSI },
> + [1] = { ns16550_iir_check_rda, UART_IER_ERDAI, UART_IIR_RDA },
> + [2] = { ns16550_iir_check_thr, UART_IER_ETHREI, UART_IIR_THR },
> + [3] = { ns16550_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/ NS16550 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 ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> +{
> + struct domain *d = vdev->owner;
> + int vector;
> +
> + if ( has_vpic(d) ) /* HVM */
> + vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
> + else
> + ASSERT_UNREACHABLE();
> +
> + pr_debug("%s: IRQ#%d vector %d assert\n", vdev->name, vdev->irq, vector);
> +}
> +
> +static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> +{
> + struct domain *d = vdev->owner;
> +
> + if ( has_vpic(d) ) /* HVM */
> + hvm_isa_irq_deassert(d, vdev->irq);
> + else
> + ASSERT_UNREACHABLE();
> +
> + pr_debug("%s: IRQ#%d deassert\n", vdev->name, vdev->irq);
> +}
> +
> +/*
> + * Assert/deassert virtual NS16550 interrupt line.
> + */
> +static void ns16550_irq_check(const struct vuart_ns16550 *vdev)
> +{
> + uint8_t iir = ns16550_iir_get(vdev);
> +
> + if ( iir & UART_IIR_NOINT )
> + ns16550_irq_assert(vdev);
> + else
> + ns16550_irq_deassert(vdev);
> +
> + pr_debug("%s: IRQ#%d IIR 0x%02x %s\n", vdev->name, vdev->irq, iir,
> + (iir & UART_IIR_NOINT) ? "deassert" : "assert");
> +}
> +
> +/*
> + * Emulate 8-bit write access to NS16550 register.
> + */
> +static int ns16550_io_write8(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> +{
> + uint8_t *regs = vdev->regs;
> + uint8_t val = *data;
> + int rc = 0;
> +
> + if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> + regs[NS16550_REGS_NUM + reg] = val;
> + else
> + {
> + switch ( reg )
> + {
> + case UART_THR:
> + if ( regs[UART_MCR] & UART_MCR_LOOP )
> + {
> + (void)ns16550_fifo_rx_putchar(vdev, val);
> + regs[UART_LSR] |= UART_LSR_OE;
> + }
> + else
> + ns16550_fifo_tx_putchar(vdev, val);
> +
> + regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> + break;
> +
> + case UART_IER:
> + /*
> + * NB: Make sure THR interrupt is re-triggered once guest OS
> + * re-enabled ETHREI in EIR.
> + */
> + if ( val & regs[UART_IER] & UART_IER_ETHREI )
> + regs[NS16550_REGS_NUM + UART_IIR] |= UART_IIR_THR;
> +
> + regs[UART_IER] = val & UART_IER_MASK;
> +
> + break;
> +
> + case UART_FCR: /* WO */
> + if ( val & UART_FCR_RESERVED0 )
> + pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
> + vdev->name, UART_FCR_RESERVED0);
> +
> + if ( val & UART_FCR_RESERVED1 )
> + pr_warn("%s: FCR: attempt to set reserved bit: %x\n",
> + vdev->name, UART_FCR_RESERVED1);
> +
> + if ( val & UART_FCR_CLRX )
> + ns16550_fifo_rx_reset(vdev);
> +
> + if ( val & UART_FCR_CLTX )
> + ns16550_fifo_tx_flush(vdev);
> +
> + 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;
> +
> + 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 )
> + pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
> + vdev->name, UART_MCR_RESERVED0);
> +
> + if ( val & UART_MCR_TCRTLR )
> + pr_warn("%s: MCR: not supported: %x\n",
> + vdev->name, UART_MCR_TCRTLR);
> +
> + if ( val & UART_MCR_RESERVED1 )
> + pr_warn("%s: MCR: attempt to set reserved bit: %x\n",
> + vdev->name, 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_MCR_RTS) ^ (msr_next & UART_MCR_RTS) )
> + 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;
> + }
> +
> + ns16550_irq_check(vdev);
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate 16-bit write access to NS16550 register.
> + * NB: some guest OSes use outw() to access UART_DLL.
> + */
> +static int ns16550_io_write16(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
> +{
> + uint16_t val = *data;
> + int rc;
> +
> + if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
> + {
> + vdev->regs[NS16550_REGS_NUM + UART_DLL] = val & 0xff;
> + vdev->regs[NS16550_REGS_NUM + UART_DLM] = (val >> 8) & 0xff;
> + rc = 0;
> + }
> + else
> + rc = -EINVAL;
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate write access to NS16550 register.
> + */
> +static int ns16550_io_write(
> + struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
> +{
> + int rc;
> +
> + switch ( size )
> + {
> + case 1:
> + rc = ns16550_io_write8(vdev, reg, (uint8_t *)data);
> + break;
> +
> + case 2:
> + rc = ns16550_io_write16(vdev, reg, (uint16_t *)data);
> + break;
> +
> + default:
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate 8-bit read access to NS16550 register.
> + */
> +static int ns16550_io_read8(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint8_t *data)
> +{
> + uint8_t *regs = vdev->regs;
> + uint8_t val = 0xff;
> + int rc = 0;
> +
> + if ( ns16550_dlab_get(vdev) && (reg == UART_DLL || reg == UART_DLM) )
> + val = regs[NS16550_REGS_NUM + reg];
> + else {
> + switch ( reg )
> + {
> + case UART_RBR:
> + /* NB: do not forget to clear overrun condition */
> + regs[UART_LSR] &= ~UART_LSR_OE;
> +
> + rc = ns16550_fifo_rx_getchar(vdev);
> + if ( rc >= 0 )
> + val = (uint8_t)rc;
> +
> + rc = 0;
> + break;
> +
> + case UART_IER:
> + val = regs[UART_IER];
> + break;
> +
> + case UART_IIR: /* RO */
> + val = ns16550_iir_get(vdev);
> +
> + /* NB: clear IIR scratch location */
> + if ( val & UART_IIR_THR )
> + regs[NS16550_REGS_NUM + UART_IIR] &= ~UART_IIR_THR;
> +
> + break;
> +
> + case UART_LCR:
> + 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;
> + if ( ns16550_fifo_rx_empty(vdev) )
> + val &= ~UART_LSR_DR;
> + else
> + val |= UART_LSR_DR;
> +
> + 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;
> +
> + default:
> + rc = -EINVAL;
> + break;
> + }
> +
> + ns16550_irq_check(vdev);
> + }
> +
> + *data = val;
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate 16-bit read access to NS16550 register.
> + */
> +static int ns16550_io_read16(
> + struct vuart_ns16550 *vdev, uint32_t reg, uint16_t *data)
> +{
> + uint16_t val = 0xffff;
> + int rc = -EINVAL;
> +
> + if ( ns16550_dlab_get(vdev) && reg == UART_DLL )
> + {
> + val = vdev->regs[NS16550_REGS_NUM + UART_DLM] << 8 |
> + vdev->regs[NS16550_REGS_NUM + UART_DLL];
> + rc = 0;
> + }
> +
> + *data = val;
> +
> + return rc;
> +}
> +
> +/*
> + * Emulate read access to NS16550 register.
> + */
> +static int ns16550_io_read(
> + struct vuart_ns16550 *vdev, uint8_t reg, uint32_t size, uint32_t *data)
> +{
> + int rc;
> +
> + switch ( size )
> + {
> + case 1:
> + rc = ns16550_io_read8(vdev, reg, (uint8_t *)data);
> + break;
> +
> + case 2:
> + rc = ns16550_io_read16(vdev, reg, (uint16_t *)data);
> + break;
> +
> + default:
> + *data = 0xffffffff;
> + rc = -EINVAL;
> + break;
> + }
> +
> + return rc;
> +}
> +
> +static void cf_check ns16550_dump_state(const struct domain *d)
> +{
> + struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> + 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%04"PRIx64" IRQ#%d owner %pd\n",
> + vdev->name, vdev->io_addr, vdev->irq, vdev->owner);
> +
> + 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[NS16550_REGS_NUM + UART_DLL],
> + regs[NS16550_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], ns16550_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);
> +}
> +
> +/*
> + * Emulate I/O access to NS16550 register.
> + * Note, emulation always returns X86EMUL_OKAY, once I/O port trap is enabled.
> + */
> +static int cf_check ns16550_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_ns16550 *vdev = d->arch.hvm.vuart;
> + uint32_t reg;
> + unsigned dlab;
> + int rc;
> +
> + if ( !vdev )
> + {
> + pr_err("%s: %c io 0x%04x %d: not initialized\n",
> + vdev->name, op(dir), addr, size);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> +
> + if ( d != vdev->owner )
> + {
> + pr_err("%s: %c io 0x%04x %d: does not match current domain %pv\n",
> + vdev->name, op(dir), addr, size, d);
> +
> + ASSERT_UNREACHABLE();
> + goto out;
> + }
> +
> + reg = addr - vdev->io_addr;
> + if ( !IS_ALIGNED(reg, size) )
> + {
> + pr_err("%s: %c 0x%04x %d: unaligned access\n",
> + vdev->name, op(dir), addr, size);
> + goto out;
> + }
> +
> + dlab = ns16550_dlab_get(vdev);
> + if ( reg >= NS16550_REGS_NUM )
> + {
> + pr_err("%s: %c io 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": not implemented\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> + goto out;
> + }
> +
> + spin_lock(&vdev->lock);
> +
> + if ( dir == IOREQ_WRITE )
> + {
> + pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> + rc = ns16550_io_write(vdev, reg, size, data);
> + }
> + else
> + {
> + rc = ns16550_io_read(vdev, reg, size, data);
> + pr_debug("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32"\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> + }
> + if ( rc < 0 )
> + pr_err("%s: %c 0x%04x %d: DLAB=%d %02"PRIx32" 0x%08"PRIx32": unsupported access\n",
> + vdev->name, op(dir), addr, size,
> + dlab, reg, *data);
> +
> + spin_unlock(&vdev->lock);
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> + ns16550_dump_state(d);
> +#endif
> +
> +out:
> + rcu_unlock_domain(d);
> +
> + return X86EMUL_OKAY;
> +#undef op
> +}
> +
> +static int cf_check ns16550_init(struct domain *d,
> + struct vuart_params *params)
> +{
> + const struct virtdev_desc *desc = &x86_pc_uarts[X86_PC_UART_IDX];
> + const struct resource *r = desc->res;
> + const uint16_t divisor = (UART_CLOCK_HZ / 115200) >> 4;
> + struct vuart_ns16550 *vdev;
> + int rc;
> +
> + BUG_ON(d->arch.hvm.vuart);
> +
> + if ( !is_hvm_domain(d) )
> + {
> + pr_err("%s: not an HVM domain\n", desc->name);
> + return -ENOSYS;
> + }
> +
> + vdev = xvzalloc(typeof(*vdev));
> + if ( !vdev )
> + {
> + pr_err("%s: failed to allocate memory\n", desc->name);
> + return -ENOMEM;
> + }
> +
> + for_each_resource(r)
> + {
> + if ( r->type & IORESOURCE_IO )
> + {
> + /* Disallow sharing physical I/O port */
> + rc = ioports_deny_access(d, r->addr, r->addr + r->size - 1);
> + if ( rc )
> + {
> + pr_err("%s: virtual I/O port range [0x%04x"PRIx64"..0x%04x"PRIx64"]: conflict w/ physical range\n",
> + desc->name,
> + (unsigned int)r->addr,
> + (unsigned int)(r->addr + r->size - 1));
> + return rc;
> + }
> +
> + register_portio_handler(d, r->addr, r->size, ns16550_io_handle);
> +
> + vdev->io_addr = r->addr;
> + vdev->io_size = r->size;
> + }
> + else if ( r->type & IORESOURCE_IRQ )
> + {
> + /* Disallow sharing physical IRQ */
> + rc = irq_deny_access(d, r->addr);
> + if ( rc )
> + {
> + pr_err("%s: virtual IRQ#%"PRIu64": conflict w/ physical IRQ: %d\n",
> + desc->name, r->addr, rc);
> + return rc;
> + }
> +
> + vdev->irq = r->addr;
> + }
> + else
> + ASSERT_UNREACHABLE();
> + }
> +
> + spin_lock_init(&vdev->lock);
> +
> + vdev->owner = d;
> + vdev->name = desc->name;
> +
> + /* NB: report 115200 baud rate */
> + vdev->regs[NS16550_REGS_NUM + UART_DLL] = divisor & 0xff;
> + vdev->regs[NS16550_REGS_NUM + UART_DLM] = (divisor >> 8) & 0xff;
> +
> + /* NS16550 shall assert UART_IIR_THR whenever transmitter is empty. */
> + vdev->regs[NS16550_REGS_NUM + UART_IIR] = UART_IIR_THR;
> +
> + d->arch.hvm.vuart = vdev;
> +
> + spin_lock(&vdev->lock);
> + ns16550_irq_check(vdev);
> + spin_unlock(&vdev->lock);
> +
> + return 0;
> +}
> +
> +static void cf_check ns16550_deinit(struct domain *d)
> +{
> + struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> +
> + if ( !vdev )
> + return;
> +
> + spin_lock(&vdev->lock);
> +
> + ns16550_fifo_tx_flush(vdev);
> +
> + spin_unlock(&vdev->lock);
> +
> + XVFREE(d->arch.hvm.vuart);
> +}
> +
> +static int cf_check ns16550_put_rx(struct domain *d, char ch)
> +{
> + struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> + uint8_t *regs;
> + uint8_t dlab;
> + int rc;
> +
> + ASSERT(d == vdev->owner);
> + if ( !vdev )
> + return -ENODEV;
> +
> + spin_lock(&vdev->lock);
> +
> + dlab = ns16550_dlab_get(vdev);
> + regs = vdev->regs;
> +
> + if ( dlab )
> + {
> + pr_debug("%s: THR/RBR access disabled: DLAB=1\n", vdev->name);
> + rc = -EBUSY;
> + }
> + else if ( regs[UART_MCR] & UART_MCR_LOOP )
> + {
> + pr_debug("%s: THR/RBR access disabled: loopback mode\n", vdev->name);
> + rc = -EBUSY;
> + }
> + else
> + {
> + uint8_t val = 0;
> +
> + rc = ns16550_fifo_rx_putchar(vdev, ch);
> + if ( rc == -ENOSPC )
> + val |= UART_LSR_OE;
> +
> + /* NB: UART_LSR_DR is also set when UART_LSR is accessed. */
> + regs[UART_LSR] |= UART_LSR_DR | val;
> +
> + /*
> + * Echo the user input on Xen console iff Xen console input is owned
> + * by NS16550 domain.
> + * NB: use 'console_timestamps=none' to disable Xen timestamps.
> + */
> + if ( is_console_printable(ch) )
> + guest_printk(d, "%c", ch);
> +
> + /* FIXME: check FCR when to fire an interrupt */
> + ns16550_irq_check(vdev);
> + }
> +
> + spin_unlock(&vdev->lock);
> +#ifdef CONFIG_VUART_NS16550_DEBUG
> + ns16550_dump_state(d);
> +#endif
> +
> + return rc;
> +}
> +
> +static const struct vuart_ops ns16550_ops = {
> + .add_node = NULL,
> + .init = ns16550_init,
> + .deinit = ns16550_deinit,
> + .dump_state = ns16550_dump_state,
> + .put_rx = ns16550_put_rx,
> +};
> +
> +VUART_REGISTER(ns16550, &ns16550_ops);
> +
> +/*
> + * Local variables:
> + * mode: C
> + * c-file-style: "BSD"
> + * c-basic-offset: 4
> + * indent-tabs-mode: nil
> + * End:
> + */
> diff --git a/xen/common/emul/vuart/vuart.c b/xen/common/emul/vuart/vuart.c
> index 14a7f8bd8b79..7971813a723d 100644
> --- a/xen/common/emul/vuart/vuart.c
> +++ b/xen/common/emul/vuart/vuart.c
> @@ -99,6 +99,10 @@ bool domain_has_vuart(const struct domain *d)
> {
> uint32_t mask = 0;
>
> +#ifdef CONFIG_VUART_NS16550
> + mask |= XEN_X86_EMU_NS16550;
> +#endif
> +
> return !!(d->emulation_flags & mask);
> }
>
> diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h
> index fc2487986642..f905e1252c70 100644
> --- a/xen/include/public/arch-x86/xen.h
> +++ b/xen/include/public/arch-x86/xen.h
> @@ -283,13 +283,15 @@ struct xen_arch_domainconfig {
> #define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ)
> #define _XEN_X86_EMU_VPCI 10
> #define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI)
> +#define _XEN_X86_EMU_NS16550 11
> +#define XEN_X86_EMU_NS16550 (1U<<_XEN_X86_EMU_NS16550)
>
> #define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \
> XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \
> XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \
> XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \
> XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\
> - XEN_X86_EMU_VPCI)
> + XEN_X86_EMU_VPCI | XEN_X86_EMU_NS16550)
libxl also consumes XEN_X86_EMU_ALL, and with the proposed change here
it will create all HVM domains with XEN_X86_EMU_NS16550, which I don't
think it's indented?
Overall I agree for Jan it would be better if this patch could be
split into somehow smaller units. Is this something feasible? We
don't want a patch for each register handle, but maybe you cna somehow
grup those into functional sections, so that patches can be < 250
lines?
Maybe you have already considered this approach and it wasn't
feasible?
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-08-06 15:06 ` Roger Pau Monné
@ 2025-08-06 17:24 ` Roger Pau Monné
2025-08-07 18:49 ` dmkhn
1 sibling, 0 replies; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 17:24 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Wed, Aug 06, 2025 at 05:06:24PM +0200, Roger Pau Monné wrote:
> On Thu, Jul 31, 2025 at 07:22:06PM +0000, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> Overall I agree for Jan it would be better if this patch could be
> split into somehow smaller units. Is this something feasible? We
> don't want a patch for each register handle, but maybe you cna somehow
> grup those into functional sections, so that patches can be < 250
> lines?
I've been thinking about this, would it be feasible to have a first
patch that introduces all the boilerplate, like adding the domain
create option, wire the libxl bits, but doesn't actually add any
emulation at all. Then further patches could fill in the emulation
slowly, starting with a patch to register the IO port handlers
(initially would be empty functions), and progressing from the basic
parts (uart initialization maybe?) towards the end implementation that
you have here.
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86)
2025-08-06 15:06 ` Roger Pau Monné
2025-08-06 17:24 ` Roger Pau Monné
@ 2025-08-07 18:49 ` dmkhn
1 sibling, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 18:49 UTC (permalink / raw)
To: Roger Pau Monné
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Wed, Aug 06, 2025 at 05:06:24PM +0200, Roger Pau Monné wrote:
[..]
> > @@ -0,0 +1,1009 @@
> > +/* 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 HVM domains support (build-time), PVH domains are not supported yet;
> > + * - Only legacy COM{1,2,3,4} resources via Kconfig, 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 ISA IRQ sharing allowed;
> > + * - No MMIO-based UART emulation.
>
> Listing the limitations here might not be the beast approach, this is
> likely to get out of sync as it's too far away from the code
> implementation.
>
> If anything those comments want to be closer together to where the
> feature would otherwise be implemented.
I wanted to have some rough overview of the emulation capabilities in one
place so it is easy to get a grasp of how emulator can be used.
But I agree that can get out of hand if not maintained; I plan to maintain
that code.
>
> > + */
> > +
> > +#define pr_prefix "ns16550"
> > +#define pr_fmt(fmt) pr_prefix ": " fmt
> > +#define pr_log_level CONFIG_VUART_NS16550_LOG_LEVEL
> > +
> > +#include <xen/8250-uart.h>
> > +#include <xen/console.h>
> > +#include <xen/iocap.h>
> > +#include <xen/ioreq.h>
> > +#include <xen/resource.h>
> > +#include <xen/vuart.h>
> > +#include <xen/xvmalloc.h>
> > +
> > +#include <public/io/console.h>
> > +
> > +#define pr_err(fmt, args...) do { \
> > + gprintk(KERN_ERR, pr_fmt(fmt), ## args); \
> > +} while (0)
> > +
> > +#define pr_warn(fmt, args...) do { \
> > + if ( pr_log_level >= 1) \
> > + gprintk(KERN_WARNING, pr_fmt(fmt), ## args); \
> > +} while (0)
> > +
> > +#define pr_info(fmt, args...) do { \
> > + if ( pr_log_level >= 2 ) \
> > + gprintk(KERN_INFO, pr_fmt(fmt), ## args); \
> > +} while (0)
> > +
> > +#define pr_debug(fmt, args...) do { \
> > + if ( pr_log_level >= 3 ) \
> > + gprintk(KERN_DEBUG, pr_fmt(fmt), ## args); \
> > +} while (0)
>
> We would use the pr_* set of logging functions for code imported from
> Linux, but for Xen code we would directly use the gprintk() functions
> rather than wrap them as you do.
Oh, I see, pr_ is a "reserved namespace".
I will rename these to ns16550_ since those are extremely helpful for
debugging.
>
> > +
> > +#if defined(CONFIG_VUART_NS16550_PC_COM1)
> > +#define X86_PC_UART_IDX 0
> > +#elif defined(CONFIG_VUART_NS16550_PC_COM2)
> > +#define X86_PC_UART_IDX 1
> > +#elif defined(CONFIG_VUART_NS16550_PC_COM3)
> > +#define X86_PC_UART_IDX 2
> > +#elif defined(CONFIG_VUART_NS16550_PC_COM4)
> > +#define X86_PC_UART_IDX 3
> > +#else
> > +#error "Unsupported I/O port"
> > +#endif
> > +
> > +#ifdef CONFIG_VUART_NS16550_DEBUG
> > +#define guest_prefix "FROM GUEST "
> > +#else
> > +#define guest_prefix ""
> > +#endif
> > +
> > +/*
> > + * Number of supported registers in the UART.
> > + */
> > +#define NS16550_REGS_NUM ( UART_SCR + 1 )
>
> Extra spaces around parentheses? (here and below)
Ack.
>
> > +
> > +/*
> > + * Number of emulated registers.
> > + *
> > + * - Emulated registers [0..NS16550_REGS_NUM] are R/W registers for DLAB=0.
> > + * - DLAB=1, R/W, DLL = NS16550_REGS_NUM + 0
> > + * - DLAB=1, R/W, DLM = NS16550_REGS_NUM + 1
> > + * - R/O, IIR (IIR_THR) = NS16550_REGS_NUM + 2
> > + */
> > +#define NS16550_EMU_REGS_NUM ( NS16550_REGS_NUM + 3 )
> > +
> > +/*
> > + * Virtual NS16550 device state.
> > + */
> > +struct vuart_ns16550 {
> > + struct xencons_interface cons; /* Emulated RX/TX FIFOs */
> > + uint8_t regs[NS16550_EMU_REGS_NUM]; /* Emulated registers */
> > + unsigned int irq; /* Emulated IRQ# */
> > + uint64_t io_addr; /* Emulated I/O region base address */
> > + uint64_t io_size; /* Emulated I/O region size */
> > + const char *name; /* Device name */
> > + struct domain *owner; /* Owner domain */
> > + spinlock_t lock; /* Protection */
> > +};
> > +
> > +/*
> > + * Virtual device description.
> > + */
> > +struct virtdev_desc {
> > + const char *name;
> > + const struct resource *res;
> > +};
> > +
> > +/*
> > + * Legacy IBM PC NS16550 resources.
> > + * There are only 4 I/O port ranges, hardcoding all of them here.
> > + */
> > +static const struct virtdev_desc x86_pc_uarts[4] = {
> > + [0] = {
>
> You don't need the explicit array indexes?
Muscle memory; I will nuke that huge static array in v5.
>
> > + .name = "COM1",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x3f8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > + [1] = {
> > + .name = "COM2",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x2f8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > + [2] = {
> > + .name = "COM3",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x3e8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 4, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > + [3] = {
> > + .name = "COM4",
> > + .res = (const struct resource[]){
> > + { .type = IORESOURCE_IO, .addr = 0x2e8, .size = NS16550_REGS_NUM },
> > + { .type = IORESOURCE_IRQ, .addr = 3, .size = 1 },
> > + { .type = IORESOURCE_UNKNOWN },
> > + },
> > + },
> > +};
[..]
> > diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h
> > index fc2487986642..f905e1252c70 100644
> > --- a/xen/include/public/arch-x86/xen.h
> > +++ b/xen/include/public/arch-x86/xen.h
> > @@ -283,13 +283,15 @@ struct xen_arch_domainconfig {
> > #define XEN_X86_EMU_USE_PIRQ (1U<<_XEN_X86_EMU_USE_PIRQ)
> > #define _XEN_X86_EMU_VPCI 10
> > #define XEN_X86_EMU_VPCI (1U<<_XEN_X86_EMU_VPCI)
> > +#define _XEN_X86_EMU_NS16550 11
> > +#define XEN_X86_EMU_NS16550 (1U<<_XEN_X86_EMU_NS16550)
> >
> > #define XEN_X86_EMU_ALL (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET | \
> > XEN_X86_EMU_PM | XEN_X86_EMU_RTC | \
> > XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC | \
> > XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU | \
> > XEN_X86_EMU_PIT | XEN_X86_EMU_USE_PIRQ |\
> > - XEN_X86_EMU_VPCI)
> > + XEN_X86_EMU_VPCI | XEN_X86_EMU_NS16550)
>
> libxl also consumes XEN_X86_EMU_ALL, and with the proposed change here
> it will create all HVM domains with XEN_X86_EMU_NS16550, which I don't
> think it's indented?
>
> Overall I agree for Jan it would be better if this patch could be
> split into somehow smaller units. Is this something feasible? We
> don't want a patch for each register handle, but maybe you cna somehow
> grup those into functional sections, so that patches can be < 250
> lines?
>
> Maybe you have already considered this approach and it wasn't
> feasible?
There __were__ pretty comprehensive reviews of this very emulator code
in the past without raising such concern (and this is v4).
Just in case, the most comprehensive review was this one (thanks Roger!):
https://lore.kernel.org/xen-devel/Z1wd4iAmVzv1ISPZ@macbook.local/
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
` (4 preceding siblings ...)
2025-07-31 19:22 ` [PATCH v4 5/8] emul/vuart-ns16550: introduce NS16550-compatible UART emulator (x86) dmkhn
@ 2025-07-31 19:22 ` dmkhn
2025-08-04 10:54 ` Jan Beulich
` (2 more replies)
2025-07-31 19:22 ` [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86) dmkhn
` (2 subsequent siblings)
8 siblings, 3 replies; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:22 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>
Enable UART emulator to be individually configured per HVM-domain.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v3:
- new patch
---
docs/man/xl.cfg.5.pod.in | 9 ++++--
tools/golang/xenlight/helpers.gen.go | 4 +--
tools/golang/xenlight/types.gen.go | 3 +-
tools/libs/light/libxl_arm.c | 26 ++++++++++++-----
tools/libs/light/libxl_create.c | 2 +-
tools/libs/light/libxl_types.idl | 3 +-
tools/libs/light/libxl_x86.c | 42 ++++++++++++++++++++++++++++
tools/ocaml/libs/xc/xenctrl.ml | 1 +
tools/ocaml/libs/xc/xenctrl.mli | 1 +
tools/xl/xl_parse.c | 2 +-
xen/arch/x86/domain.c | 5 ++--
11 files changed, 80 insertions(+), 18 deletions(-)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
index 5362fb0e9a6f..e1d012274eaf 100644
--- a/docs/man/xl.cfg.5.pod.in
+++ b/docs/man/xl.cfg.5.pod.in
@@ -3032,14 +3032,17 @@ the domain was created.
This requires hardware compatibility with the requested version, either
natively or via hardware backwards compatibility support.
-=item B<vuart="uart">
+=item B<vuart=[ "sbsa_uart", "ns16550" ]>
To enable vuart console, user must specify the following option in the
-VM config file:
+VM config file, e.g:
+```
vuart = "sbsa_uart"
+```
-Currently, only the "sbsa_uart" model is supported for ARM.
+Currently, "sbsa_uart" (ARM) and "ns16550" (x86) are the only supported
+UART models.
=back
diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
index b43aad7d0064..e56af8a8a8c5 100644
--- a/tools/golang/xenlight/helpers.gen.go
+++ b/tools/golang/xenlight/helpers.gen.go
@@ -1160,7 +1160,6 @@ x.TypeUnion = &typePvh
default:
return fmt.Errorf("invalid union key '%v'", x.Type)}
x.ArchArm.GicVersion = GicVersion(xc.arch_arm.gic_version)
-x.ArchArm.Vuart = VuartType(xc.arch_arm.vuart)
x.ArchArm.SveVl = SveType(xc.arch_arm.sve_vl)
x.ArchArm.NrSpis = uint32(xc.arch_arm.nr_spis)
if err := x.ArchX86.MsrRelaxed.fromC(&xc.arch_x86.msr_relaxed);err != nil {
@@ -1169,6 +1168,7 @@ return fmt.Errorf("converting field ArchX86.MsrRelaxed: %v", err)
x.Altp2M = Altp2MMode(xc.altp2m)
x.Altp2MCount = uint32(xc.altp2m_count)
x.VmtraceBufKb = int(xc.vmtrace_buf_kb)
+x.Vuart = VuartType(xc.vuart)
if err := x.Vpmu.fromC(&xc.vpmu);err != nil {
return fmt.Errorf("converting field Vpmu: %v", err)
}
@@ -1695,7 +1695,6 @@ break
default:
return fmt.Errorf("invalid union key '%v'", x.Type)}
xc.arch_arm.gic_version = C.libxl_gic_version(x.ArchArm.GicVersion)
-xc.arch_arm.vuart = C.libxl_vuart_type(x.ArchArm.Vuart)
xc.arch_arm.sve_vl = C.libxl_sve_type(x.ArchArm.SveVl)
xc.arch_arm.nr_spis = C.uint32_t(x.ArchArm.NrSpis)
if err := x.ArchX86.MsrRelaxed.toC(&xc.arch_x86.msr_relaxed); err != nil {
@@ -1704,6 +1703,7 @@ return fmt.Errorf("converting field ArchX86.MsrRelaxed: %v", err)
xc.altp2m = C.libxl_altp2m_mode(x.Altp2M)
xc.altp2m_count = C.uint32_t(x.Altp2MCount)
xc.vmtrace_buf_kb = C.int(x.VmtraceBufKb)
+xc.vuart = C.libxl_vuart_type(x.Vuart)
if err := x.Vpmu.toC(&xc.vpmu); err != nil {
return fmt.Errorf("converting field Vpmu: %v", err)
}
diff --git a/tools/golang/xenlight/types.gen.go b/tools/golang/xenlight/types.gen.go
index 4777f528b52c..2f4153d2510b 100644
--- a/tools/golang/xenlight/types.gen.go
+++ b/tools/golang/xenlight/types.gen.go
@@ -253,6 +253,7 @@ type VuartType int
const(
VuartTypeUnknown VuartType = 0
VuartTypeSbsaUart VuartType = 1
+VuartTypeNs16550 VuartType = 2
)
type VkbBackend int
@@ -596,7 +597,6 @@ Type DomainType
TypeUnion DomainBuildInfoTypeUnion
ArchArm struct {
GicVersion GicVersion
-Vuart VuartType
SveVl SveType
NrSpis uint32
}
@@ -608,6 +608,7 @@ Altp2MCount uint32
VmtraceBufKb int
Vpmu Defbool
TrapUnmappedAccesses Defbool
+Vuart VuartType
}
type DomainBuildInfoTypeUnion interface {
diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
index 4a19a8d22bdf..f4721b24763c 100644
--- a/tools/libs/light/libxl_arm.c
+++ b/tools/libs/light/libxl_arm.c
@@ -92,14 +92,26 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
uint32_t virtio_mmio_irq = GUEST_VIRTIO_MMIO_SPI_FIRST;
int rc;
- /*
- * If pl011 vuart is enabled then increment the nr_spis to allow allocation
- * of SPI VIRQ for pl011.
- */
- if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
+ switch ( d_config->b_info.vuart )
+ {
+ case LIBXL_VUART_TYPE_SBSA_UART:
+ /*
+ * If pl011 vuart is enabled then increment the nr_spis to allow
+ * allocation of SPI VIRQ for pl011.
+ */
nr_spis += (GUEST_VPL011_SPI - 32) + 1;
vuart_irq = GUEST_VPL011_SPI;
vuart_enabled = true;
+ break;
+
+ case LIBXL_VUART_TYPE_NS16550:
+ LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
+ abort();
+ break;
+
+ case LIBXL_VUART_TYPE_UNKNOWN:
+ default:
+ break;
}
for (i = 0; i < d_config->num_disks; i++) {
@@ -1372,7 +1384,7 @@ next_resize:
FDT( make_timer_node(gc, fdt, ainfo, state->clock_frequency) );
FDT( make_hypervisor_node(gc, fdt, vers) );
- if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
+ if (info->vuart == LIBXL_VUART_TYPE_SBSA_UART)
FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
if (info->tee == LIBXL_TEE_TYPE_OPTEE)
@@ -1725,7 +1737,7 @@ int libxl__arch_build_dom_finish(libxl__gc *gc,
{
int rc = 0, ret;
- if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
+ if (info->vuart != LIBXL_VUART_TYPE_SBSA_UART) {
rc = 0;
goto out;
}
diff --git a/tools/libs/light/libxl_create.c b/tools/libs/light/libxl_create.c
index 4042ae1a8957..cfd7e827867a 100644
--- a/tools/libs/light/libxl_create.c
+++ b/tools/libs/light/libxl_create.c
@@ -1815,7 +1815,7 @@ static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
&d_config->vfbs[i]);
}
- if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
+ if (d_config->b_info.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
init_console_info(gc, &vuart, 0);
vuart.backend_domid = state->console_domid;
libxl__device_vuart_add(gc, domid, &vuart, state);
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
index fe251649f346..fd60c2b26764 100644
--- a/tools/libs/light/libxl_types.idl
+++ b/tools/libs/light/libxl_types.idl
@@ -276,6 +276,7 @@ libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
libxl_vuart_type = Enumeration("vuart_type", [
(0, "unknown"),
(1, "sbsa_uart"),
+ (2, "ns16550"),
])
libxl_vkb_backend = Enumeration("vkb_backend", [
@@ -722,7 +723,6 @@ libxl_domain_build_info = Struct("domain_build_info",[
("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
- ("vuart", libxl_vuart_type),
("sve_vl", libxl_sve_type),
("nr_spis", uint32, {'init_val': 'LIBXL_NR_SPIS_DEFAULT'}),
])),
@@ -739,6 +739,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
("vpmu", libxl_defbool),
("trap_unmapped_accesses", libxl_defbool),
+ ("vuart", libxl_vuart_type),
], dir=DIR_IN,
copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
index 60d4e8661c93..0f039ca65a88 100644
--- a/tools/libs/light/libxl_x86.c
+++ b/tools/libs/light/libxl_x86.c
@@ -2,6 +2,45 @@
#include "libxl_arch.h"
#include <xen/arch-x86/cpuid.h>
+static void libxl__arch_domain_vuart_assert(
+ libxl__gc *gc,
+ libxl_domain_config *d_config,
+ struct xen_domctl_createdomain *config)
+{
+ LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
+ abort();
+}
+
+static void libxl__arch_domain_vuart_unsupported(
+ libxl__gc *gc,
+ libxl_domain_config *d_config,
+ struct xen_domctl_createdomain *config)
+{
+ if ( d_config->b_info.vuart != LIBXL_VUART_TYPE_UNKNOWN )
+ libxl__arch_domain_vuart_assert(gc, d_config, config);
+}
+
+static void libxl__arch_domain_vuart_enable(
+ libxl__gc *gc,
+ libxl_domain_config *d_config,
+ struct xen_domctl_createdomain *config)
+{
+ switch ( d_config->b_info.vuart )
+ {
+ case LIBXL_VUART_TYPE_SBSA_UART:
+ libxl__arch_domain_vuart_assert(gc, d_config, config);
+ break;
+
+ case LIBXL_VUART_TYPE_NS16550:
+ config->arch.emulation_flags |= XEN_X86_EMU_NS16550;
+ break;
+
+ case LIBXL_VUART_TYPE_UNKNOWN:
+ default:
+ break;
+ }
+}
+
int libxl__arch_domain_prepare_config(libxl__gc *gc,
libxl_domain_config *d_config,
struct xen_domctl_createdomain *config)
@@ -9,14 +48,17 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
switch(d_config->c_info.type) {
case LIBXL_DOMAIN_TYPE_HVM:
config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
+ libxl__arch_domain_vuart_enable(gc, d_config, config);
if (!libxl_defbool_val(d_config->b_info.u.hvm.pirq))
config->arch.emulation_flags &= ~XEN_X86_EMU_USE_PIRQ;
break;
case LIBXL_DOMAIN_TYPE_PVH:
config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
+ libxl__arch_domain_vuart_unsupported(gc, d_config, config);
break;
case LIBXL_DOMAIN_TYPE_PV:
config->arch.emulation_flags = 0;
+ libxl__arch_domain_vuart_unsupported(gc, d_config, config);
break;
default:
abort();
diff --git a/tools/ocaml/libs/xc/xenctrl.ml b/tools/ocaml/libs/xc/xenctrl.ml
index 7e1aabad6cba..4539e78bb283 100644
--- a/tools/ocaml/libs/xc/xenctrl.ml
+++ b/tools/ocaml/libs/xc/xenctrl.ml
@@ -47,6 +47,7 @@ type x86_arch_emulation_flags =
| X86_EMU_PIT
| X86_EMU_USE_PIRQ
| X86_EMU_VPCI
+ | X86_EMU_NS16550
type x86_arch_misc_flags =
| X86_MSR_RELAXED
diff --git a/tools/ocaml/libs/xc/xenctrl.mli b/tools/ocaml/libs/xc/xenctrl.mli
index f44dba61aeab..66a98180d99b 100644
--- a/tools/ocaml/libs/xc/xenctrl.mli
+++ b/tools/ocaml/libs/xc/xenctrl.mli
@@ -41,6 +41,7 @@ type x86_arch_emulation_flags =
| X86_EMU_PIT
| X86_EMU_USE_PIRQ
| X86_EMU_VPCI
+ | X86_EMU_NS16550
type x86_arch_misc_flags =
| X86_MSR_RELAXED
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index 28cdbf07c213..b0d266b5bf63 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1498,7 +1498,7 @@ void parse_config_data(const char *config_source,
b_info->max_vcpus = l;
if (!xlu_cfg_get_string(config, "vuart", &buf, 0)) {
- if (libxl_vuart_type_from_string(buf, &b_info->arch_arm.vuart)) {
+ if (libxl_vuart_type_from_string(buf, &b_info->vuart)) {
fprintf(stderr, "ERROR: invalid value \"%s\" for \"vuart\"\n",
buf);
exit(1);
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 7fd4f7a831dc..6a010a509a60 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -780,9 +780,10 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
/* HVM domU */
{
.caps = CAP_HVM | CAP_DOMU,
- .min = X86_EMU_ALL & ~(X86_EMU_VPCI | X86_EMU_USE_PIRQ),
+ .min = X86_EMU_ALL & ~(X86_EMU_VPCI | X86_EMU_USE_PIRQ |
+ X86_EMU_NS16550),
/* HVM PIRQ feature is user-selectable. */
- .opt = X86_EMU_USE_PIRQ,
+ .opt = X86_EMU_USE_PIRQ | X86_EMU_NS16550,
},
#endif /* #ifdef CONFIG_HVM */
};
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-07-31 19:22 ` [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86) dmkhn
@ 2025-08-04 10:54 ` Jan Beulich
2025-08-06 15:21 ` Roger Pau Monné
2025-08-25 14:49 ` Anthony PERARD
2 siblings, 0 replies; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 10:54 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:22, dmkhn@proton.me wrote:
> --- a/xen/arch/x86/domain.c
> +++ b/xen/arch/x86/domain.c
> @@ -780,9 +780,10 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
> /* HVM domU */
> {
> .caps = CAP_HVM | CAP_DOMU,
> - .min = X86_EMU_ALL & ~(X86_EMU_VPCI | X86_EMU_USE_PIRQ),
> + .min = X86_EMU_ALL & ~(X86_EMU_VPCI | X86_EMU_USE_PIRQ |
> + X86_EMU_NS16550),
> /* HVM PIRQ feature is user-selectable. */
> - .opt = X86_EMU_USE_PIRQ,
> + .opt = X86_EMU_USE_PIRQ | X86_EMU_NS16550,
Rendering the comment partly stale.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-07-31 19:22 ` [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86) dmkhn
2025-08-04 10:54 ` Jan Beulich
@ 2025-08-06 15:21 ` Roger Pau Monné
2025-08-25 14:49 ` Anthony PERARD
2 siblings, 0 replies; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 15:21 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Enable UART emulator to be individually configured per HVM-domain.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
> ---
> Changes since v3:
> - new patch
> ---
> docs/man/xl.cfg.5.pod.in | 9 ++++--
> tools/golang/xenlight/helpers.gen.go | 4 +--
> tools/golang/xenlight/types.gen.go | 3 +-
> tools/libs/light/libxl_arm.c | 26 ++++++++++++-----
> tools/libs/light/libxl_create.c | 2 +-
> tools/libs/light/libxl_types.idl | 3 +-
> tools/libs/light/libxl_x86.c | 42 ++++++++++++++++++++++++++++
> tools/ocaml/libs/xc/xenctrl.ml | 1 +
> tools/ocaml/libs/xc/xenctrl.mli | 1 +
> tools/xl/xl_parse.c | 2 +-
> xen/arch/x86/domain.c | 5 ++--
> 11 files changed, 80 insertions(+), 18 deletions(-)
>
> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> index 5362fb0e9a6f..e1d012274eaf 100644
> --- a/docs/man/xl.cfg.5.pod.in
> +++ b/docs/man/xl.cfg.5.pod.in
> @@ -3032,14 +3032,17 @@ the domain was created.
> This requires hardware compatibility with the requested version, either
> natively or via hardware backwards compatibility support.
>
> -=item B<vuart="uart">
> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
>
> To enable vuart console, user must specify the following option in the
> -VM config file:
> +VM config file, e.g:
>
> +```
> vuart = "sbsa_uart"
> +```
>
> -Currently, only the "sbsa_uart" model is supported for ARM.
> +Currently, "sbsa_uart" (ARM) and "ns16550" (x86) are the only supported
> +UART models.
>
> =back
>
> diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
> index b43aad7d0064..e56af8a8a8c5 100644
> --- a/tools/golang/xenlight/helpers.gen.go
> +++ b/tools/golang/xenlight/helpers.gen.go
> @@ -1160,7 +1160,6 @@ x.TypeUnion = &typePvh
> default:
> return fmt.Errorf("invalid union key '%v'", x.Type)}
> x.ArchArm.GicVersion = GicVersion(xc.arch_arm.gic_version)
> -x.ArchArm.Vuart = VuartType(xc.arch_arm.vuart)
> x.ArchArm.SveVl = SveType(xc.arch_arm.sve_vl)
> x.ArchArm.NrSpis = uint32(xc.arch_arm.nr_spis)
> if err := x.ArchX86.MsrRelaxed.fromC(&xc.arch_x86.msr_relaxed);err != nil {
> @@ -1169,6 +1168,7 @@ return fmt.Errorf("converting field ArchX86.MsrRelaxed: %v", err)
> x.Altp2M = Altp2MMode(xc.altp2m)
> x.Altp2MCount = uint32(xc.altp2m_count)
> x.VmtraceBufKb = int(xc.vmtrace_buf_kb)
> +x.Vuart = VuartType(xc.vuart)
> if err := x.Vpmu.fromC(&xc.vpmu);err != nil {
> return fmt.Errorf("converting field Vpmu: %v", err)
> }
> @@ -1695,7 +1695,6 @@ break
> default:
> return fmt.Errorf("invalid union key '%v'", x.Type)}
> xc.arch_arm.gic_version = C.libxl_gic_version(x.ArchArm.GicVersion)
> -xc.arch_arm.vuart = C.libxl_vuart_type(x.ArchArm.Vuart)
> xc.arch_arm.sve_vl = C.libxl_sve_type(x.ArchArm.SveVl)
> xc.arch_arm.nr_spis = C.uint32_t(x.ArchArm.NrSpis)
> if err := x.ArchX86.MsrRelaxed.toC(&xc.arch_x86.msr_relaxed); err != nil {
> @@ -1704,6 +1703,7 @@ return fmt.Errorf("converting field ArchX86.MsrRelaxed: %v", err)
> xc.altp2m = C.libxl_altp2m_mode(x.Altp2M)
> xc.altp2m_count = C.uint32_t(x.Altp2MCount)
> xc.vmtrace_buf_kb = C.int(x.VmtraceBufKb)
> +xc.vuart = C.libxl_vuart_type(x.Vuart)
> if err := x.Vpmu.toC(&xc.vpmu); err != nil {
> return fmt.Errorf("converting field Vpmu: %v", err)
> }
> diff --git a/tools/golang/xenlight/types.gen.go b/tools/golang/xenlight/types.gen.go
> index 4777f528b52c..2f4153d2510b 100644
> --- a/tools/golang/xenlight/types.gen.go
> +++ b/tools/golang/xenlight/types.gen.go
> @@ -253,6 +253,7 @@ type VuartType int
> const(
> VuartTypeUnknown VuartType = 0
> VuartTypeSbsaUart VuartType = 1
> +VuartTypeNs16550 VuartType = 2
> )
>
> type VkbBackend int
> @@ -596,7 +597,6 @@ Type DomainType
> TypeUnion DomainBuildInfoTypeUnion
> ArchArm struct {
> GicVersion GicVersion
> -Vuart VuartType
> SveVl SveType
> NrSpis uint32
> }
> @@ -608,6 +608,7 @@ Altp2MCount uint32
> VmtraceBufKb int
> Vpmu Defbool
> TrapUnmappedAccesses Defbool
> +Vuart VuartType
> }
>
> type DomainBuildInfoTypeUnion interface {
> diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> index 4a19a8d22bdf..f4721b24763c 100644
> --- a/tools/libs/light/libxl_arm.c
> +++ b/tools/libs/light/libxl_arm.c
> @@ -92,14 +92,26 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> uint32_t virtio_mmio_irq = GUEST_VIRTIO_MMIO_SPI_FIRST;
> int rc;
>
> - /*
> - * If pl011 vuart is enabled then increment the nr_spis to allow allocation
> - * of SPI VIRQ for pl011.
> - */
> - if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
> + switch ( d_config->b_info.vuart )
> + {
> + case LIBXL_VUART_TYPE_SBSA_UART:
> + /*
> + * If pl011 vuart is enabled then increment the nr_spis to allow
> + * allocation of SPI VIRQ for pl011.
> + */
> nr_spis += (GUEST_VPL011_SPI - 32) + 1;
> vuart_irq = GUEST_VPL011_SPI;
> vuart_enabled = true;
> + break;
> +
> + case LIBXL_VUART_TYPE_NS16550:
> + LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
> + abort();
> + break;
> +
> + case LIBXL_VUART_TYPE_UNKNOWN:
> + default:
> + break;
> }
>
> for (i = 0; i < d_config->num_disks; i++) {
> @@ -1372,7 +1384,7 @@ next_resize:
> FDT( make_timer_node(gc, fdt, ainfo, state->clock_frequency) );
> FDT( make_hypervisor_node(gc, fdt, vers) );
>
> - if (info->arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART)
> + if (info->vuart == LIBXL_VUART_TYPE_SBSA_UART)
> FDT( make_vpl011_uart_node(gc, fdt, ainfo, dom) );
>
> if (info->tee == LIBXL_TEE_TYPE_OPTEE)
> @@ -1725,7 +1737,7 @@ int libxl__arch_build_dom_finish(libxl__gc *gc,
> {
> int rc = 0, ret;
>
> - if (info->arch_arm.vuart != LIBXL_VUART_TYPE_SBSA_UART) {
> + if (info->vuart != LIBXL_VUART_TYPE_SBSA_UART) {
> rc = 0;
> goto out;
> }
> diff --git a/tools/libs/light/libxl_create.c b/tools/libs/light/libxl_create.c
> index 4042ae1a8957..cfd7e827867a 100644
> --- a/tools/libs/light/libxl_create.c
> +++ b/tools/libs/light/libxl_create.c
> @@ -1815,7 +1815,7 @@ static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
> &d_config->vfbs[i]);
> }
>
> - if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
> + if (d_config->b_info.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
> init_console_info(gc, &vuart, 0);
> vuart.backend_domid = state->console_domid;
> libxl__device_vuart_add(gc, domid, &vuart, state);
> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> index fe251649f346..fd60c2b26764 100644
> --- a/tools/libs/light/libxl_types.idl
> +++ b/tools/libs/light/libxl_types.idl
> @@ -276,6 +276,7 @@ libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
> libxl_vuart_type = Enumeration("vuart_type", [
> (0, "unknown"),
> (1, "sbsa_uart"),
> + (2, "ns16550"),
> ])
>
> libxl_vkb_backend = Enumeration("vkb_backend", [
> @@ -722,7 +723,6 @@ libxl_domain_build_info = Struct("domain_build_info",[
>
>
> ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
> - ("vuart", libxl_vuart_type),
> ("sve_vl", libxl_sve_type),
> ("nr_spis", uint32, {'init_val': 'LIBXL_NR_SPIS_DEFAULT'}),
> ])),
> @@ -739,6 +739,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
>
> ("vpmu", libxl_defbool),
> ("trap_unmapped_accesses", libxl_defbool),
> + ("vuart", libxl_vuart_type),
>
> ], dir=DIR_IN,
> copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
> diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
> index 60d4e8661c93..0f039ca65a88 100644
> --- a/tools/libs/light/libxl_x86.c
> +++ b/tools/libs/light/libxl_x86.c
> @@ -2,6 +2,45 @@
> #include "libxl_arch.h"
> #include <xen/arch-x86/cpuid.h>
>
> +static void libxl__arch_domain_vuart_assert(
> + libxl__gc *gc,
> + libxl_domain_config *d_config,
> + struct xen_domctl_createdomain *config)
> +{
> + LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
> + abort();
> +}
> +
> +static void libxl__arch_domain_vuart_unsupported(
> + libxl__gc *gc,
> + libxl_domain_config *d_config,
> + struct xen_domctl_createdomain *config)
> +{
> + if ( d_config->b_info.vuart != LIBXL_VUART_TYPE_UNKNOWN )
> + libxl__arch_domain_vuart_assert(gc, d_config, config);
> +}
> +
> +static void libxl__arch_domain_vuart_enable(
> + libxl__gc *gc,
> + libxl_domain_config *d_config,
> + struct xen_domctl_createdomain *config)
> +{
> + switch ( d_config->b_info.vuart )
> + {
> + case LIBXL_VUART_TYPE_SBSA_UART:
> + libxl__arch_domain_vuart_assert(gc, d_config, config);
> + break;
> +
> + case LIBXL_VUART_TYPE_NS16550:
> + config->arch.emulation_flags |= XEN_X86_EMU_NS16550;
> + break;
> +
> + case LIBXL_VUART_TYPE_UNKNOWN:
> + default:
> + break;
> + }
> +}
> +
> int libxl__arch_domain_prepare_config(libxl__gc *gc,
> libxl_domain_config *d_config,
> struct xen_domctl_createdomain *config)
> @@ -9,14 +48,17 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> switch(d_config->c_info.type) {
> case LIBXL_DOMAIN_TYPE_HVM:
> config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
> + libxl__arch_domain_vuart_enable(gc, d_config, config);
As mentioned in the previous commit, I don't think this works as
expected. You have added XEN_X86_EMU_NS16550 to XEN_X86_EMU_ALL, and
hence you need to subtract it from the mask as it's done with VPCI.
> if (!libxl_defbool_val(d_config->b_info.u.hvm.pirq))
> config->arch.emulation_flags &= ~XEN_X86_EMU_USE_PIRQ;
> break;
> case LIBXL_DOMAIN_TYPE_PVH:
> config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
> + libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> break;
> case LIBXL_DOMAIN_TYPE_PV:
> config->arch.emulation_flags = 0;
> + libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> break;
> default:
> abort();
> diff --git a/tools/ocaml/libs/xc/xenctrl.ml b/tools/ocaml/libs/xc/xenctrl.ml
> index 7e1aabad6cba..4539e78bb283 100644
> --- a/tools/ocaml/libs/xc/xenctrl.ml
> +++ b/tools/ocaml/libs/xc/xenctrl.ml
> @@ -47,6 +47,7 @@ type x86_arch_emulation_flags =
> | X86_EMU_PIT
> | X86_EMU_USE_PIRQ
> | X86_EMU_VPCI
> + | X86_EMU_NS16550
>
> type x86_arch_misc_flags =
> | X86_MSR_RELAXED
> diff --git a/tools/ocaml/libs/xc/xenctrl.mli b/tools/ocaml/libs/xc/xenctrl.mli
> index f44dba61aeab..66a98180d99b 100644
> --- a/tools/ocaml/libs/xc/xenctrl.mli
> +++ b/tools/ocaml/libs/xc/xenctrl.mli
> @@ -41,6 +41,7 @@ type x86_arch_emulation_flags =
> | X86_EMU_PIT
> | X86_EMU_USE_PIRQ
> | X86_EMU_VPCI
> + | X86_EMU_NS16550
>
> type x86_arch_misc_flags =
> | X86_MSR_RELAXED
> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
> index 28cdbf07c213..b0d266b5bf63 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1498,7 +1498,7 @@ void parse_config_data(const char *config_source,
> b_info->max_vcpus = l;
>
> if (!xlu_cfg_get_string(config, "vuart", &buf, 0)) {
> - if (libxl_vuart_type_from_string(buf, &b_info->arch_arm.vuart)) {
> + if (libxl_vuart_type_from_string(buf, &b_info->vuart)) {
> fprintf(stderr, "ERROR: invalid value \"%s\" for \"vuart\"\n",
> buf);
> exit(1);
> diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
> index 7fd4f7a831dc..6a010a509a60 100644
> --- a/xen/arch/x86/domain.c
> +++ b/xen/arch/x86/domain.c
> @@ -780,9 +780,10 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
> /* HVM domU */
> {
> .caps = CAP_HVM | CAP_DOMU,
> - .min = X86_EMU_ALL & ~(X86_EMU_VPCI | X86_EMU_USE_PIRQ),
> + .min = X86_EMU_ALL & ~(X86_EMU_VPCI | X86_EMU_USE_PIRQ |
> + X86_EMU_NS16550),
> /* HVM PIRQ feature is user-selectable. */
> - .opt = X86_EMU_USE_PIRQ,
> + .opt = X86_EMU_USE_PIRQ | X86_EMU_NS16550,
Does this need to be part of the patch that adds X86_EMU_NS16550 into
X86_EMU_ALL, as to not break domain creation in the interim?
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-07-31 19:22 ` [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86) dmkhn
2025-08-04 10:54 ` Jan Beulich
2025-08-06 15:21 ` Roger Pau Monné
@ 2025-08-25 14:49 ` Anthony PERARD
2025-08-25 15:03 ` Jan Beulich
2025-08-26 9:26 ` dmkhn
2 siblings, 2 replies; 61+ messages in thread
From: Anthony PERARD @ 2025-08-25 14:49 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> index 5362fb0e9a6f..e1d012274eaf 100644
> --- a/docs/man/xl.cfg.5.pod.in
> +++ b/docs/man/xl.cfg.5.pod.in
> @@ -3032,14 +3032,17 @@ the domain was created.
> This requires hardware compatibility with the requested version, either
> natively or via hardware backwards compatibility support.
>
> -=item B<vuart="uart">
> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
This syntax here would inditace that `vuart` takes a list of items. You
could write instead:
vuart="UART"
which seems more in line with the rest of the man page. Then you can add
some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
possible to also have a sublist, like the `tee` option have.
> To enable vuart console, user must specify the following option in the
> -VM config file:
> +VM config file, e.g:
>
> +```
This file isn't in markdown, it's in perlpod.
> vuart = "sbsa_uart"
> +```
>
> -Currently, only the "sbsa_uart" model is supported for ARM.
> +Currently, "sbsa_uart" (ARM) and "ns16550" (x86) are the only supported
> +UART models.
>
> =back
>
> diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> index 4a19a8d22bdf..f4721b24763c 100644
> --- a/tools/libs/light/libxl_arm.c
> +++ b/tools/libs/light/libxl_arm.c
> @@ -92,14 +92,26 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> uint32_t virtio_mmio_irq = GUEST_VIRTIO_MMIO_SPI_FIRST;
> int rc;
>
> - /*
> - * If pl011 vuart is enabled then increment the nr_spis to allow allocation
> - * of SPI VIRQ for pl011.
> - */
> - if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
> + switch ( d_config->b_info.vuart )
> + {
> + case LIBXL_VUART_TYPE_SBSA_UART:
> + /*
> + * If pl011 vuart is enabled then increment the nr_spis to allow
> + * allocation of SPI VIRQ for pl011.
> + */
> nr_spis += (GUEST_VPL011_SPI - 32) + 1;
> vuart_irq = GUEST_VPL011_SPI;
> vuart_enabled = true;
> + break;
> +
> + case LIBXL_VUART_TYPE_NS16550:
> + LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
This seems too late in libxl. I think checking if the config value is
correct could be done in one of the *_setdefault() like many other
config check are done. There's
libxl__arch_domain_build_info_setdefault() that could be used.
> + abort();
> + break;
> +
> + case LIBXL_VUART_TYPE_UNKNOWN:
> + default:
> + break;
> }
>
> for (i = 0; i < d_config->num_disks; i++) {
> diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> index fe251649f346..fd60c2b26764 100644
> --- a/tools/libs/light/libxl_types.idl
> +++ b/tools/libs/light/libxl_types.idl
> @@ -276,6 +276,7 @@ libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
> libxl_vuart_type = Enumeration("vuart_type", [
> (0, "unknown"),
> (1, "sbsa_uart"),
> + (2, "ns16550"),
> ])
>
> libxl_vkb_backend = Enumeration("vkb_backend", [
> @@ -722,7 +723,6 @@ libxl_domain_build_info = Struct("domain_build_info",[
>
>
> ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
> - ("vuart", libxl_vuart_type),
arch_arm.vuart is part of libxl's API, it can't be removed. There's some
explanation about "libxl API compatibility" at the top of "libxl.h".
But for this change, you could add `vuart` to `arch_x86`, or if you want
to add `vuart` at the root like you did, you'll need to check that both
`arch_arm.vuart` and `vuart` aren't set at the same time, and have one
of the *_setdefault() function do the work of migrating the option.
You'll need also a LIBXL_HAVE_* macro in libxl.h, probably named
LIBXL_HAVE_VUART_NS16550.
> ("sve_vl", libxl_sve_type),
> ("nr_spis", uint32, {'init_val': 'LIBXL_NR_SPIS_DEFAULT'}),
> ])),
> @@ -739,6 +739,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
>
> ("vpmu", libxl_defbool),
> ("trap_unmapped_accesses", libxl_defbool),
> + ("vuart", libxl_vuart_type),
>
> ], dir=DIR_IN,
> copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
> diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
> index 60d4e8661c93..0f039ca65a88 100644
> --- a/tools/libs/light/libxl_x86.c
> +++ b/tools/libs/light/libxl_x86.c
> @@ -2,6 +2,45 @@
> #include "libxl_arch.h"
> #include <xen/arch-x86/cpuid.h>
>
> +static void libxl__arch_domain_vuart_assert(
> + libxl__gc *gc,
> + libxl_domain_config *d_config,
> + struct xen_domctl_createdomain *config)
> +{
> + LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
> + abort();
The name of the function is wrong. It doens't assert anything, and just
abort...
I don't think this function is useful.
Also, don't abort() for configuration error, you need to return an error
instead.
> +}
> +
> +static void libxl__arch_domain_vuart_unsupported(
> + libxl__gc *gc,
> + libxl_domain_config *d_config,
> + struct xen_domctl_createdomain *config)
> +{
> + if ( d_config->b_info.vuart != LIBXL_VUART_TYPE_UNKNOWN )
> + libxl__arch_domain_vuart_assert(gc, d_config, config);
This function have also a bad name, it doesn't check if a uart is
unsupported.
> +}
> +
> +static void libxl__arch_domain_vuart_enable(
> + libxl__gc *gc,
> + libxl_domain_config *d_config,
> + struct xen_domctl_createdomain *config)
> +{
> + switch ( d_config->b_info.vuart )
> + {
> + case LIBXL_VUART_TYPE_SBSA_UART:
> + libxl__arch_domain_vuart_assert(gc, d_config, config);
> + break;
> +
> + case LIBXL_VUART_TYPE_NS16550:
> + config->arch.emulation_flags |= XEN_X86_EMU_NS16550;
> + break;
> +
> + case LIBXL_VUART_TYPE_UNKNOWN:
> + default:
> + break;
> + }
> +}
> +
> int libxl__arch_domain_prepare_config(libxl__gc *gc,
> libxl_domain_config *d_config,
> struct xen_domctl_createdomain *config)
> @@ -9,14 +48,17 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> switch(d_config->c_info.type) {
> case LIBXL_DOMAIN_TYPE_HVM:
> config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
> + libxl__arch_domain_vuart_enable(gc, d_config, config);
> if (!libxl_defbool_val(d_config->b_info.u.hvm.pirq))
> config->arch.emulation_flags &= ~XEN_X86_EMU_USE_PIRQ;
> break;
> case LIBXL_DOMAIN_TYPE_PVH:
> config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
> + libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> break;
> case LIBXL_DOMAIN_TYPE_PV:
> config->arch.emulation_flags = 0;
> + libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> break;
> default:
> abort();
Thanks,
--
Anthony PERARD
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-08-25 14:49 ` Anthony PERARD
@ 2025-08-25 15:03 ` Jan Beulich
2025-08-25 15:13 ` Anthony PERARD
2025-08-26 9:26 ` dmkhn
1 sibling, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-25 15:03 UTC (permalink / raw)
To: Anthony PERARD, dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, julien, michal.orzel,
roger.pau, sstabellini, dmukhin
On 25.08.2025 16:49, Anthony PERARD wrote:
> On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
>> diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
>> index 5362fb0e9a6f..e1d012274eaf 100644
>> --- a/docs/man/xl.cfg.5.pod.in
>> +++ b/docs/man/xl.cfg.5.pod.in
>> @@ -3032,14 +3032,17 @@ the domain was created.
>> This requires hardware compatibility with the requested version, either
>> natively or via hardware backwards compatibility support.
>>
>> -=item B<vuart="uart">
>> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
>
> This syntax here would inditace that `vuart` takes a list of items. You
> could write instead:
>
> vuart="UART"
>
> which seems more in line with the rest of the man page. Then you can add
> some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
> possible to also have a sublist, like the `tee` option have.
But shouldn't the syntax already now allow for multiple items? Possibly
even multiple ones of the same kind?
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-08-25 15:03 ` Jan Beulich
@ 2025-08-25 15:13 ` Anthony PERARD
2025-08-25 15:27 ` Jan Beulich
0 siblings, 1 reply; 61+ messages in thread
From: Anthony PERARD @ 2025-08-25 15:13 UTC (permalink / raw)
To: Jan Beulich
Cc: dmkhn, xen-devel, andrew.cooper3, anthony.perard, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Mon, Aug 25, 2025 at 05:03:40PM +0200, Jan Beulich wrote:
> On 25.08.2025 16:49, Anthony PERARD wrote:
> > On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
> >> -=item B<vuart="uart">
> >> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
> >
> > This syntax here would inditace that `vuart` takes a list of items. You
> > could write instead:
> >
> > vuart="UART"
> >
> > which seems more in line with the rest of the man page. Then you can add
> > some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
> > possible to also have a sublist, like the `tee` option have.
>
> But shouldn't the syntax already now allow for multiple items? Possibly
> even multiple ones of the same kind?
How does `vuart="uart"` allow for multiple items?
--
Anthony PERARD
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-08-25 15:13 ` Anthony PERARD
@ 2025-08-25 15:27 ` Jan Beulich
2025-08-25 15:39 ` Anthony PERARD
0 siblings, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-25 15:27 UTC (permalink / raw)
To: Anthony PERARD
Cc: dmkhn, xen-devel, andrew.cooper3, anthony.perard, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On 25.08.2025 17:13, Anthony PERARD wrote:
> On Mon, Aug 25, 2025 at 05:03:40PM +0200, Jan Beulich wrote:
>> On 25.08.2025 16:49, Anthony PERARD wrote:
>>> On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
>>>> -=item B<vuart="uart">
>>>> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
>>>
>>> This syntax here would inditace that `vuart` takes a list of items. You
>>> could write instead:
>>>
>>> vuart="UART"
>>>
>>> which seems more in line with the rest of the man page. Then you can add
>>> some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
>>> possible to also have a sublist, like the `tee` option have.
>>
>> But shouldn't the syntax already now allow for multiple items? Possibly
>> even multiple ones of the same kind?
>
> How does `vuart="uart"` allow for multiple items?
Precisely. I would have expected it to be e.g.
vuart = [ "ns16550", "ns16550", "sbsa-uart" ]
(i.e. the square brackets are part of the necessary syntax).
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-08-25 15:27 ` Jan Beulich
@ 2025-08-25 15:39 ` Anthony PERARD
2025-08-25 15:45 ` Jan Beulich
0 siblings, 1 reply; 61+ messages in thread
From: Anthony PERARD @ 2025-08-25 15:39 UTC (permalink / raw)
To: Jan Beulich
Cc: dmkhn, xen-devel, andrew.cooper3, anthony.perard, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Mon, Aug 25, 2025 at 05:27:41PM +0200, Jan Beulich wrote:
> On 25.08.2025 17:13, Anthony PERARD wrote:
> > On Mon, Aug 25, 2025 at 05:03:40PM +0200, Jan Beulich wrote:
> >> On 25.08.2025 16:49, Anthony PERARD wrote:
> >>> On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
> >>>> -=item B<vuart="uart">
> >>>> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
> >>>
> >>> This syntax here would inditace that `vuart` takes a list of items. You
> >>> could write instead:
> >>>
> >>> vuart="UART"
> >>>
> >>> which seems more in line with the rest of the man page. Then you can add
> >>> some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
> >>> possible to also have a sublist, like the `tee` option have.
> >>
> >> But shouldn't the syntax already now allow for multiple items? Possibly
> >> even multiple ones of the same kind?
> >
> > How does `vuart="uart"` allow for multiple items?
>
> Precisely. I would have expected it to be e.g.
>
> vuart = [ "ns16550", "ns16550", "sbsa-uart" ]
This is not an option.
Having mutliple ns16550 would do exactly the same thing as having a
single one. It's not possible to have both ns16550 and sbsa_uart at the
same time, one is for x86, one is for Arm. I don't know if it's possible
to have several sbsa_uart but the code in libxl doesn't have the
capability.
So overall, I don't think having a list in the xl.cfg syntax is useful
right now. It's easy to change later, I think.
> (i.e. the square brackets are part of the necessary syntax).
>
> Jan
--
Anthony PERARD
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-08-25 15:39 ` Anthony PERARD
@ 2025-08-25 15:45 ` Jan Beulich
0 siblings, 0 replies; 61+ messages in thread
From: Jan Beulich @ 2025-08-25 15:45 UTC (permalink / raw)
To: Anthony PERARD
Cc: dmkhn, xen-devel, andrew.cooper3, anthony.perard, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On 25.08.2025 17:39, Anthony PERARD wrote:
> On Mon, Aug 25, 2025 at 05:27:41PM +0200, Jan Beulich wrote:
>> On 25.08.2025 17:13, Anthony PERARD wrote:
>>> On Mon, Aug 25, 2025 at 05:03:40PM +0200, Jan Beulich wrote:
>>>> On 25.08.2025 16:49, Anthony PERARD wrote:
>>>>> On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
>>>>>> -=item B<vuart="uart">
>>>>>> +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
>>>>>
>>>>> This syntax here would inditace that `vuart` takes a list of items. You
>>>>> could write instead:
>>>>>
>>>>> vuart="UART"
>>>>>
>>>>> which seems more in line with the rest of the man page. Then you can add
>>>>> some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
>>>>> possible to also have a sublist, like the `tee` option have.
>>>>
>>>> But shouldn't the syntax already now allow for multiple items? Possibly
>>>> even multiple ones of the same kind?
>>>
>>> How does `vuart="uart"` allow for multiple items?
>>
>> Precisely. I would have expected it to be e.g.
>>
>> vuart = [ "ns16550", "ns16550", "sbsa-uart" ]
>
> This is not an option.
>
> Having mutliple ns16550 would do exactly the same thing as having a
> single one. It's not possible to have both ns16550 and sbsa_uart at the
> same time, one is for x86, one is for Arm. I don't know if it's possible
> to have several sbsa_uart but the code in libxl doesn't have the
> capability.
>
> So overall, I don't think having a list in the xl.cfg syntax is useful
> right now. It's easy to change later, I think.
Well, never mind then.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86)
2025-08-25 14:49 ` Anthony PERARD
2025-08-25 15:03 ` Jan Beulich
@ 2025-08-26 9:26 ` dmkhn
1 sibling, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-26 9:26 UTC (permalink / raw)
To: Anthony PERARD
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Mon, Aug 25, 2025 at 04:49:58PM +0200, Anthony PERARD wrote:
> On Thu, Jul 31, 2025 at 07:22:12PM +0000, dmkhn@proton.me wrote:
> > diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
> > index 5362fb0e9a6f..e1d012274eaf 100644
> > --- a/docs/man/xl.cfg.5.pod.in
> > +++ b/docs/man/xl.cfg.5.pod.in
> > @@ -3032,14 +3032,17 @@ the domain was created.
> > This requires hardware compatibility with the requested version, either
> > natively or via hardware backwards compatibility support.
> >
> > -=item B<vuart="uart">
> > +=item B<vuart=[ "sbsa_uart", "ns16550" ]>
>
> This syntax here would inditace that `vuart` takes a list of items. You
> could write instead:
>
> vuart="UART"
>
> which seems more in line with the rest of the man page. Then you can add
> some thing like "with UART been one of "sbsa_uart" or "ns16550". It's
> possible to also have a sublist, like the `tee` option have.
OK, will do.
>
>
> > To enable vuart console, user must specify the following option in the
> > -VM config file:
> > +VM config file, e.g:
> >
> > +```
>
> This file isn't in markdown, it's in perlpod.
Whoops, muscle memory. Thanks.
>
> > vuart = "sbsa_uart"
> > +```
> >
> > -Currently, only the "sbsa_uart" model is supported for ARM.
> > +Currently, "sbsa_uart" (ARM) and "ns16550" (x86) are the only supported
> > +UART models.
> >
> > =back
> >
> > diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c
> > index 4a19a8d22bdf..f4721b24763c 100644
> > --- a/tools/libs/light/libxl_arm.c
> > +++ b/tools/libs/light/libxl_arm.c
> > @@ -92,14 +92,26 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> > uint32_t virtio_mmio_irq = GUEST_VIRTIO_MMIO_SPI_FIRST;
> > int rc;
> >
> > - /*
> > - * If pl011 vuart is enabled then increment the nr_spis to allow allocation
> > - * of SPI VIRQ for pl011.
> > - */
> > - if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
> > + switch ( d_config->b_info.vuart )
> > + {
> > + case LIBXL_VUART_TYPE_SBSA_UART:
> > + /*
> > + * If pl011 vuart is enabled then increment the nr_spis to allow
> > + * allocation of SPI VIRQ for pl011.
> > + */
> > nr_spis += (GUEST_VPL011_SPI - 32) + 1;
> > vuart_irq = GUEST_VPL011_SPI;
> > vuart_enabled = true;
> > + break;
> > +
> > + case LIBXL_VUART_TYPE_NS16550:
> > + LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
>
> This seems too late in libxl. I think checking if the config value is
> correct could be done in one of the *_setdefault() like many other
> config check are done. There's
> libxl__arch_domain_build_info_setdefault() that could be used.
Thanks for the pointer.
>
> > + abort();
> > + break;
> > +
> > + case LIBXL_VUART_TYPE_UNKNOWN:
> > + default:
> > + break;
> > }
> >
> > for (i = 0; i < d_config->num_disks; i++) {
> > diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
> > index fe251649f346..fd60c2b26764 100644
> > --- a/tools/libs/light/libxl_types.idl
> > +++ b/tools/libs/light/libxl_types.idl
> > @@ -276,6 +276,7 @@ libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
> > libxl_vuart_type = Enumeration("vuart_type", [
> > (0, "unknown"),
> > (1, "sbsa_uart"),
> > + (2, "ns16550"),
> > ])
> >
> > libxl_vkb_backend = Enumeration("vkb_backend", [
> > @@ -722,7 +723,6 @@ libxl_domain_build_info = Struct("domain_build_info",[
> >
> >
> > ("arch_arm", Struct(None, [("gic_version", libxl_gic_version),
> > - ("vuart", libxl_vuart_type),
>
> arch_arm.vuart is part of libxl's API, it can't be removed. There's some
> explanation about "libxl API compatibility" at the top of "libxl.h".
> But for this change, you could add `vuart` to `arch_x86`, or if you want
> to add `vuart` at the root like you did, you'll need to check that both
> `arch_arm.vuart` and `vuart` aren't set at the same time, and have one
> of the *_setdefault() function do the work of migrating the option.
>
> You'll need also a LIBXL_HAVE_* macro in libxl.h, probably named
> LIBXL_HAVE_VUART_NS16550.
Thanks for details, I will proceed with option 2, adding `vuart` at the root.
>
> > ("sve_vl", libxl_sve_type),
> > ("nr_spis", uint32, {'init_val': 'LIBXL_NR_SPIS_DEFAULT'}),
> > ])),
> > @@ -739,6 +739,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
> >
> > ("vpmu", libxl_defbool),
> > ("trap_unmapped_accesses", libxl_defbool),
> > + ("vuart", libxl_vuart_type),
> >
> > ], dir=DIR_IN,
> > copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
> > diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
> > index 60d4e8661c93..0f039ca65a88 100644
> > --- a/tools/libs/light/libxl_x86.c
> > +++ b/tools/libs/light/libxl_x86.c
> > @@ -2,6 +2,45 @@
> > #include "libxl_arch.h"
> > #include <xen/arch-x86/cpuid.h>
> >
> > +static void libxl__arch_domain_vuart_assert(
> > + libxl__gc *gc,
> > + libxl_domain_config *d_config,
> > + struct xen_domctl_createdomain *config)
> > +{
> > + LOG(ERROR, "unsupported UART emulator %d\n", d_config->b_info.vuart);
> > + abort();
>
> The name of the function is wrong. It doens't assert anything, and just
> abort...
> I don't think this function is useful.
Yeah, I mostly added it to avoid typing the same error message several times.
Will remove.
>
> Also, don't abort() for configuration error, you need to return an error
> instead.
Ack.
>
> > +}
> > +
> > +static void libxl__arch_domain_vuart_unsupported(
> > + libxl__gc *gc,
> > + libxl_domain_config *d_config,
> > + struct xen_domctl_createdomain *config)
> > +{
> > + if ( d_config->b_info.vuart != LIBXL_VUART_TYPE_UNKNOWN )
> > + libxl__arch_domain_vuart_assert(gc, d_config, config);
>
> This function have also a bad name, it doesn't check if a uart is
> unsupported.
Will rework.
>
> > +}
> > +
> > +static void libxl__arch_domain_vuart_enable(
> > + libxl__gc *gc,
> > + libxl_domain_config *d_config,
> > + struct xen_domctl_createdomain *config)
> > +{
> > + switch ( d_config->b_info.vuart )
> > + {
> > + case LIBXL_VUART_TYPE_SBSA_UART:
> > + libxl__arch_domain_vuart_assert(gc, d_config, config);
> > + break;
> > +
> > + case LIBXL_VUART_TYPE_NS16550:
> > + config->arch.emulation_flags |= XEN_X86_EMU_NS16550;
> > + break;
> > +
> > + case LIBXL_VUART_TYPE_UNKNOWN:
> > + default:
> > + break;
> > + }
> > +}
> > +
> > int libxl__arch_domain_prepare_config(libxl__gc *gc,
> > libxl_domain_config *d_config,
> > struct xen_domctl_createdomain *config)
> > @@ -9,14 +48,17 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> > switch(d_config->c_info.type) {
> > case LIBXL_DOMAIN_TYPE_HVM:
> > config->arch.emulation_flags = (XEN_X86_EMU_ALL & ~XEN_X86_EMU_VPCI);
> > + libxl__arch_domain_vuart_enable(gc, d_config, config);
> > if (!libxl_defbool_val(d_config->b_info.u.hvm.pirq))
> > config->arch.emulation_flags &= ~XEN_X86_EMU_USE_PIRQ;
> > break;
> > case LIBXL_DOMAIN_TYPE_PVH:
> > config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
> > + libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> > break;
> > case LIBXL_DOMAIN_TYPE_PV:
> > config->arch.emulation_flags = 0;
> > + libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> > break;
> > default:
> > abort();
>
> Thanks,
Thanks for review!
--
Denis
>
> --
> Anthony PERARD
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86)
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
` (5 preceding siblings ...)
2025-07-31 19:22 ` [PATCH v4 6/8] tools/xl: enable NS16550-compatible UART emulator for HVM (x86) dmkhn
@ 2025-07-31 19:22 ` dmkhn
2025-08-01 0:46 ` Stefano Stabellini
2025-08-04 11:06 ` Jan Beulich
2025-07-31 19:22 ` [PATCH v4 8/8] emul/vuart: introduce console forwarding enforcement via vUART dmkhn
2025-08-06 13:48 ` [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator Roger Pau Monné
8 siblings, 2 replies; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:22 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>
Enable virtual NS16550 for PVH domains in xl.
{map,unmap}_domain_emuirq_pirq() 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 v3:
- new patch
---
tools/libs/light/libxl_x86.c | 2 +-
xen/arch/x86/domain.c | 2 ++
xen/arch/x86/hvm/vioapic.c | 10 ++++++++++
xen/arch/x86/include/asm/irq.h | 1 +
xen/common/emul/vuart/vuart-ns16550.c | 27 +++++++++++++++++++++++++--
xen/drivers/passthrough/x86/hvm.c | 9 ++++-----
6 files changed, 43 insertions(+), 8 deletions(-)
diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
index 0f039ca65a88..a40647c06cb9 100644
--- a/tools/libs/light/libxl_x86.c
+++ b/tools/libs/light/libxl_x86.c
@@ -54,7 +54,7 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
break;
case LIBXL_DOMAIN_TYPE_PVH:
config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
- libxl__arch_domain_vuart_unsupported(gc, d_config, config);
+ libxl__arch_domain_vuart_enable(gc, d_config, config);
break;
case LIBXL_DOMAIN_TYPE_PV:
config->arch.emulation_flags = 0;
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 6a010a509a60..39b0c0b199b9 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -769,12 +769,14 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
{
.caps = CAP_HVM | CAP_HWDOM,
.min = X86_EMU_LAPIC | X86_EMU_IOAPIC | X86_EMU_VPCI,
+ .opt = X86_EMU_NS16550,
},
/* PVH domU */
{
.caps = CAP_HVM | CAP_DOMU,
.min = X86_EMU_LAPIC,
+ .opt = X86_EMU_NS16550,
},
/* HVM domU */
diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
index 7c725f9e471f..86fe3aa4a201 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.
+ */
+ read_lock(&currd->event_lock);
+ ret = domain_pirq_to_emuirq(currd, gsi);
+ read_unlock(&currd->event_lock);
+ if ( ret != IRQ_UNBOUND )
+ 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 8c81f66434a8..731d2bbbb1b4 100644
--- a/xen/arch/x86/include/asm/irq.h
+++ b/xen/arch/x86/include/asm/irq.h
@@ -221,6 +221,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/vuart-ns16550.c b/xen/common/emul/vuart/vuart-ns16550.c
index 48bbf58264fe..9ec9aed2c594 100644
--- a/xen/common/emul/vuart/vuart-ns16550.c
+++ b/xen/common/emul/vuart/vuart-ns16550.c
@@ -355,7 +355,9 @@ static void ns16550_irq_assert(const struct vuart_ns16550 *vdev)
struct domain *d = vdev->owner;
int vector;
- if ( has_vpic(d) ) /* HVM */
+ if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
+ vector = hvm_ioapic_assert(d, vdev->irq, false);
+ else if ( has_vpic(d) ) /* HVM */
vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
else
ASSERT_UNREACHABLE();
@@ -367,7 +369,9 @@ static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
{
struct domain *d = vdev->owner;
- if ( has_vpic(d) ) /* HVM */
+ if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
+ hvm_ioapic_deassert(d, vdev->irq);
+ else if ( has_vpic(d) ) /* HVM */
hvm_isa_irq_deassert(d, vdev->irq);
else
ASSERT_UNREACHABLE();
@@ -889,6 +893,17 @@ static int cf_check ns16550_init(struct domain *d,
return rc;
}
+ /* Claim virtual IRQ */
+ write_lock(&d->event_lock);
+ rc = map_domain_emuirq_pirq(d, r->addr, IRQ_EMU);
+ write_unlock(&d->event_lock);
+ if ( rc )
+ {
+ pr_err("%s: virtual IRQ#%"PRIu64": cannot claim: %d\n",
+ desc->name, r->addr, rc);
+ return rc;
+ }
+
vdev->irq = r->addr;
}
else
@@ -919,12 +934,20 @@ static int cf_check ns16550_init(struct domain *d,
static void cf_check ns16550_deinit(struct domain *d)
{
struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
+ int rc;
if ( !vdev )
return;
spin_lock(&vdev->lock);
+ rc = unmap_domain_pirq_emuirq(vdev->owner, vdev->irq);
+ if ( rc )
+ {
+ pr_err("%s: virtual IRQ#%d: cannot unclaim: %d\n",
+ vdev->name, vdev->irq, rc);
+ }
+
ns16550_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..22905cd86f95 100644
--- a/xen/drivers/passthrough/x86/hvm.c
+++ b/xen/drivers/passthrough/x86/hvm.c
@@ -924,12 +924,11 @@ static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
{
struct pirq *pirq = pirq_info(d, gsi);
- /* Check if GSI is actually mapped. */
- if ( !pirq_dpci(pirq) )
- return;
-
hvm_gsi_deassert(d, gsi);
- hvm_pirq_eoi(pirq);
+
+ /* Check if GSI is actually mapped. */
+ if ( pirq_dpci(pirq) )
+ hvm_pirq_eoi(pirq);
}
static int cf_check _hvm_dpci_isairq_eoi(
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86)
2025-07-31 19:22 ` [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86) dmkhn
@ 2025-08-01 0:46 ` Stefano Stabellini
2025-08-01 1:53 ` dmkhn
2025-08-04 11:06 ` Jan Beulich
1 sibling, 1 reply; 61+ messages in thread
From: Stefano Stabellini @ 2025-08-01 0:46 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Enable virtual NS16550 for PVH domains in xl.
>
> {map,unmap}_domain_emuirq_pirq() 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 v3:
> - new patch
> ---
> tools/libs/light/libxl_x86.c | 2 +-
> xen/arch/x86/domain.c | 2 ++
> xen/arch/x86/hvm/vioapic.c | 10 ++++++++++
> xen/arch/x86/include/asm/irq.h | 1 +
> xen/common/emul/vuart/vuart-ns16550.c | 27 +++++++++++++++++++++++++--
> xen/drivers/passthrough/x86/hvm.c | 9 ++++-----
> 6 files changed, 43 insertions(+), 8 deletions(-)
>
> diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
> index 0f039ca65a88..a40647c06cb9 100644
> --- a/tools/libs/light/libxl_x86.c
> +++ b/tools/libs/light/libxl_x86.c
> @@ -54,7 +54,7 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> break;
> case LIBXL_DOMAIN_TYPE_PVH:
> config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
> - libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> + libxl__arch_domain_vuart_enable(gc, d_config, config);
> break;
> case LIBXL_DOMAIN_TYPE_PV:
> config->arch.emulation_flags = 0;
> diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
> index 6a010a509a60..39b0c0b199b9 100644
> --- a/xen/arch/x86/domain.c
> +++ b/xen/arch/x86/domain.c
> @@ -769,12 +769,14 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
> {
> .caps = CAP_HVM | CAP_HWDOM,
> .min = X86_EMU_LAPIC | X86_EMU_IOAPIC | X86_EMU_VPCI,
> + .opt = X86_EMU_NS16550,
> },
>
> /* PVH domU */
> {
> .caps = CAP_HVM | CAP_DOMU,
> .min = X86_EMU_LAPIC,
> + .opt = X86_EMU_NS16550,
> },
>
> /* HVM domU */
> diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
> index 7c725f9e471f..86fe3aa4a201 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.
> + */
> + read_lock(&currd->event_lock);
> + ret = domain_pirq_to_emuirq(currd, gsi);
> + read_unlock(&currd->event_lock);
> + if ( ret != IRQ_UNBOUND )
> + 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 8c81f66434a8..731d2bbbb1b4 100644
> --- a/xen/arch/x86/include/asm/irq.h
> +++ b/xen/arch/x86/include/asm/irq.h
> @@ -221,6 +221,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/vuart-ns16550.c b/xen/common/emul/vuart/vuart-ns16550.c
> index 48bbf58264fe..9ec9aed2c594 100644
> --- a/xen/common/emul/vuart/vuart-ns16550.c
> +++ b/xen/common/emul/vuart/vuart-ns16550.c
> @@ -355,7 +355,9 @@ static void ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> struct domain *d = vdev->owner;
> int vector;
>
> - if ( has_vpic(d) ) /* HVM */
> + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> + vector = hvm_ioapic_assert(d, vdev->irq, false);
> + else if ( has_vpic(d) ) /* HVM */
> vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
> else
> ASSERT_UNREACHABLE();
> @@ -367,7 +369,9 @@ static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> {
> struct domain *d = vdev->owner;
>
> - if ( has_vpic(d) ) /* HVM */
> + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> + hvm_ioapic_deassert(d, vdev->irq);
> + else if ( has_vpic(d) ) /* HVM */
> hvm_isa_irq_deassert(d, vdev->irq);
> else
> ASSERT_UNREACHABLE();
> @@ -889,6 +893,17 @@ static int cf_check ns16550_init(struct domain *d,
> return rc;
> }
>
> + /* Claim virtual IRQ */
> + write_lock(&d->event_lock);
> + rc = map_domain_emuirq_pirq(d, r->addr, IRQ_EMU);
> + write_unlock(&d->event_lock);
> + if ( rc )
> + {
> + pr_err("%s: virtual IRQ#%"PRIu64": cannot claim: %d\n",
> + desc->name, r->addr, rc);
> + return rc;
> + }
> +
> vdev->irq = r->addr;
> }
> else
> @@ -919,12 +934,20 @@ static int cf_check ns16550_init(struct domain *d,
> static void cf_check ns16550_deinit(struct domain *d)
> {
> struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> + int rc;
>
> if ( !vdev )
> return;
>
> spin_lock(&vdev->lock);
>
> + rc = unmap_domain_pirq_emuirq(vdev->owner, vdev->irq);
> + if ( rc )
> + {
> + pr_err("%s: virtual IRQ#%d: cannot unclaim: %d\n",
> + vdev->name, vdev->irq, rc);
> + }
write_lock(&d->event_lock); ?
> ns16550_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..22905cd86f95 100644
> --- a/xen/drivers/passthrough/x86/hvm.c
> +++ b/xen/drivers/passthrough/x86/hvm.c
> @@ -924,12 +924,11 @@ static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
> {
> struct pirq *pirq = pirq_info(d, gsi);
>
> - /* Check if GSI is actually mapped. */
> - if ( !pirq_dpci(pirq) )
> - return;
> -
> hvm_gsi_deassert(d, gsi);
> - hvm_pirq_eoi(pirq);
> +
> + /* Check if GSI is actually mapped. */
> + if ( pirq_dpci(pirq) )
> + hvm_pirq_eoi(pirq);
> }
>
> static int cf_check _hvm_dpci_isairq_eoi(
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86)
2025-08-01 0:46 ` Stefano Stabellini
@ 2025-08-01 1:53 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-01 1:53 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Thu, Jul 31, 2025 at 05:46:37PM -0700, Stefano Stabellini wrote:
> On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Enable virtual NS16550 for PVH domains in xl.
> >
> > {map,unmap}_domain_emuirq_pirq() 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 v3:
> > - new patch
> > ---
> > tools/libs/light/libxl_x86.c | 2 +-
> > xen/arch/x86/domain.c | 2 ++
> > xen/arch/x86/hvm/vioapic.c | 10 ++++++++++
> > xen/arch/x86/include/asm/irq.h | 1 +
> > xen/common/emul/vuart/vuart-ns16550.c | 27 +++++++++++++++++++++++++--
> > xen/drivers/passthrough/x86/hvm.c | 9 ++++-----
> > 6 files changed, 43 insertions(+), 8 deletions(-)
> >
> > diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c
> > index 0f039ca65a88..a40647c06cb9 100644
> > --- a/tools/libs/light/libxl_x86.c
> > +++ b/tools/libs/light/libxl_x86.c
> > @@ -54,7 +54,7 @@ int libxl__arch_domain_prepare_config(libxl__gc *gc,
> > break;
> > case LIBXL_DOMAIN_TYPE_PVH:
> > config->arch.emulation_flags = XEN_X86_EMU_LAPIC;
> > - libxl__arch_domain_vuart_unsupported(gc, d_config, config);
> > + libxl__arch_domain_vuart_enable(gc, d_config, config);
> > break;
> > case LIBXL_DOMAIN_TYPE_PV:
> > config->arch.emulation_flags = 0;
> > diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
> > index 6a010a509a60..39b0c0b199b9 100644
> > --- a/xen/arch/x86/domain.c
> > +++ b/xen/arch/x86/domain.c
> > @@ -769,12 +769,14 @@ static bool emulation_flags_ok(const struct domain *d, uint32_t emflags)
> > {
> > .caps = CAP_HVM | CAP_HWDOM,
> > .min = X86_EMU_LAPIC | X86_EMU_IOAPIC | X86_EMU_VPCI,
> > + .opt = X86_EMU_NS16550,
> > },
> >
> > /* PVH domU */
> > {
> > .caps = CAP_HVM | CAP_DOMU,
> > .min = X86_EMU_LAPIC,
> > + .opt = X86_EMU_NS16550,
> > },
> >
> > /* HVM domU */
> > diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c
> > index 7c725f9e471f..86fe3aa4a201 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.
> > + */
> > + read_lock(&currd->event_lock);
> > + ret = domain_pirq_to_emuirq(currd, gsi);
> > + read_unlock(&currd->event_lock);
> > + if ( ret != IRQ_UNBOUND )
> > + 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 8c81f66434a8..731d2bbbb1b4 100644
> > --- a/xen/arch/x86/include/asm/irq.h
> > +++ b/xen/arch/x86/include/asm/irq.h
> > @@ -221,6 +221,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/vuart-ns16550.c b/xen/common/emul/vuart/vuart-ns16550.c
> > index 48bbf58264fe..9ec9aed2c594 100644
> > --- a/xen/common/emul/vuart/vuart-ns16550.c
> > +++ b/xen/common/emul/vuart/vuart-ns16550.c
> > @@ -355,7 +355,9 @@ static void ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> > struct domain *d = vdev->owner;
> > int vector;
> >
> > - if ( has_vpic(d) ) /* HVM */
> > + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> > + vector = hvm_ioapic_assert(d, vdev->irq, false);
> > + else if ( has_vpic(d) ) /* HVM */
> > vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
> > else
> > ASSERT_UNREACHABLE();
> > @@ -367,7 +369,9 @@ static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> > {
> > struct domain *d = vdev->owner;
> >
> > - if ( has_vpic(d) ) /* HVM */
> > + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> > + hvm_ioapic_deassert(d, vdev->irq);
> > + else if ( has_vpic(d) ) /* HVM */
> > hvm_isa_irq_deassert(d, vdev->irq);
> > else
> > ASSERT_UNREACHABLE();
> > @@ -889,6 +893,17 @@ static int cf_check ns16550_init(struct domain *d,
> > return rc;
> > }
> >
> > + /* Claim virtual IRQ */
> > + write_lock(&d->event_lock);
> > + rc = map_domain_emuirq_pirq(d, r->addr, IRQ_EMU);
> > + write_unlock(&d->event_lock);
> > + if ( rc )
> > + {
> > + pr_err("%s: virtual IRQ#%"PRIu64": cannot claim: %d\n",
> > + desc->name, r->addr, rc);
> > + return rc;
> > + }
> > +
> > vdev->irq = r->addr;
> > }
> > else
> > @@ -919,12 +934,20 @@ static int cf_check ns16550_init(struct domain *d,
> > static void cf_check ns16550_deinit(struct domain *d)
> > {
> > struct vuart_ns16550 *vdev = d->arch.hvm.vuart;
> > + int rc;
> >
> > if ( !vdev )
> > return;
> >
> > spin_lock(&vdev->lock);
> >
> > + rc = unmap_domain_pirq_emuirq(vdev->owner, vdev->irq);
> > + if ( rc )
> > + {
> > + pr_err("%s: virtual IRQ#%d: cannot unclaim: %d\n",
> > + vdev->name, vdev->irq, rc);
> > + }
>
> write_lock(&d->event_lock); ?
Thanks
>
>
> > ns16550_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..22905cd86f95 100644
> > --- a/xen/drivers/passthrough/x86/hvm.c
> > +++ b/xen/drivers/passthrough/x86/hvm.c
> > @@ -924,12 +924,11 @@ static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
> > {
> > struct pirq *pirq = pirq_info(d, gsi);
> >
> > - /* Check if GSI is actually mapped. */
> > - if ( !pirq_dpci(pirq) )
> > - return;
> > -
> > hvm_gsi_deassert(d, gsi);
> > - hvm_pirq_eoi(pirq);
> > +
> > + /* Check if GSI is actually mapped. */
> > + if ( pirq_dpci(pirq) )
> > + hvm_pirq_eoi(pirq);
> > }
> >
> > static int cf_check _hvm_dpci_isairq_eoi(
> > --
> > 2.34.1
> >
> >
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86)
2025-07-31 19:22 ` [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86) dmkhn
2025-08-01 0:46 ` Stefano Stabellini
@ 2025-08-04 11:06 ` Jan Beulich
2025-08-07 19:38 ` dmkhn
1 sibling, 1 reply; 61+ messages in thread
From: Jan Beulich @ 2025-08-04 11:06 UTC (permalink / raw)
To: dmkhn
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On 31.07.2025 21:22, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> Enable virtual NS16550 for PVH domains in xl.
>
> {map,unmap}_domain_emuirq_pirq() 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 v3:
> - new patch
> ---
> tools/libs/light/libxl_x86.c | 2 +-
> xen/arch/x86/domain.c | 2 ++
> xen/arch/x86/hvm/vioapic.c | 10 ++++++++++
> xen/arch/x86/include/asm/irq.h | 1 +
> xen/common/emul/vuart/vuart-ns16550.c | 27 +++++++++++++++++++++++++--
> xen/drivers/passthrough/x86/hvm.c | 9 ++++-----
> 6 files changed, 43 insertions(+), 8 deletions(-)
Given this diffstat, how come the patch prefix is "tools/xl:"? You don't even
touch xl ...
> --- a/xen/common/emul/vuart/vuart-ns16550.c
> +++ b/xen/common/emul/vuart/vuart-ns16550.c
> @@ -355,7 +355,9 @@ static void ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> struct domain *d = vdev->owner;
> int vector;
>
> - if ( has_vpic(d) ) /* HVM */
> + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> + vector = hvm_ioapic_assert(d, vdev->irq, false);
> + else if ( has_vpic(d) ) /* HVM */
> vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
Why not
if ( has_vpic(d) ) /* HVM */
vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
else if ( has_vioapic(d) ) /* PVH */
vector = hvm_ioapic_assert(d, vdev->irq, false);
Less code churn and maybe even less generated code.
> @@ -367,7 +369,9 @@ static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> {
> struct domain *d = vdev->owner;
>
> - if ( has_vpic(d) ) /* HVM */
> + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> + hvm_ioapic_deassert(d, vdev->irq);
> + else if ( has_vpic(d) ) /* HVM */
> hvm_isa_irq_deassert(d, vdev->irq);
Same here then.
> --- a/xen/drivers/passthrough/x86/hvm.c
> +++ b/xen/drivers/passthrough/x86/hvm.c
> @@ -924,12 +924,11 @@ static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
> {
> struct pirq *pirq = pirq_info(d, gsi);
>
> - /* Check if GSI is actually mapped. */
> - if ( !pirq_dpci(pirq) )
> - return;
> -
> hvm_gsi_deassert(d, gsi);
> - hvm_pirq_eoi(pirq);
> +
> + /* Check if GSI is actually mapped. */
> + if ( pirq_dpci(pirq) )
> + hvm_pirq_eoi(pirq);
> }
The correctness of this change (in particular hvm_gsi_deassert() now always
running) wants reasoning about in the description.
Jan
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86)
2025-08-04 11:06 ` Jan Beulich
@ 2025-08-07 19:38 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-07 19:38 UTC (permalink / raw)
To: Jan Beulich
Cc: andrew.cooper3, anthony.perard, julien, michal.orzel, roger.pau,
sstabellini, dmukhin, xen-devel
On Mon, Aug 04, 2025 at 01:06:32PM +0200, Jan Beulich wrote:
> On 31.07.2025 21:22, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > Enable virtual NS16550 for PVH domains in xl.
> >
> > {map,unmap}_domain_emuirq_pirq() 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 v3:
> > - new patch
> > ---
> > tools/libs/light/libxl_x86.c | 2 +-
> > xen/arch/x86/domain.c | 2 ++
> > xen/arch/x86/hvm/vioapic.c | 10 ++++++++++
> > xen/arch/x86/include/asm/irq.h | 1 +
> > xen/common/emul/vuart/vuart-ns16550.c | 27 +++++++++++++++++++++++++--
> > xen/drivers/passthrough/x86/hvm.c | 9 ++++-----
> > 6 files changed, 43 insertions(+), 8 deletions(-)
>
> Given this diffstat, how come the patch prefix is "tools/xl:"? You don't even
> touch xl ...
Yeah, my bad, that should have been saying something like
"enable IRQ emulation via vIOAPIC"
Will update.
>
> > --- a/xen/common/emul/vuart/vuart-ns16550.c
> > +++ b/xen/common/emul/vuart/vuart-ns16550.c
> > @@ -355,7 +355,9 @@ static void ns16550_irq_assert(const struct vuart_ns16550 *vdev)
> > struct domain *d = vdev->owner;
> > int vector;
> >
> > - if ( has_vpic(d) ) /* HVM */
> > + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> > + vector = hvm_ioapic_assert(d, vdev->irq, false);
> > + else if ( has_vpic(d) ) /* HVM */
> > vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
>
> Why not
>
> if ( has_vpic(d) ) /* HVM */
> vector = hvm_isa_irq_assert(d, vdev->irq, vioapic_get_vector);
> else if ( has_vioapic(d) ) /* PVH */
> vector = hvm_ioapic_assert(d, vdev->irq, false);
>
> Less code churn and maybe even less generated code.
>
> > @@ -367,7 +369,9 @@ static void ns16550_irq_deassert(const struct vuart_ns16550 *vdev)
> > {
> > struct domain *d = vdev->owner;
> >
> > - if ( has_vpic(d) ) /* HVM */
> > + if ( has_vioapic(d) && !has_vpic(d) ) /* PVH */
> > + hvm_ioapic_deassert(d, vdev->irq);
> > + else if ( has_vpic(d) ) /* HVM */
> > hvm_isa_irq_deassert(d, vdev->irq);
>
> Same here then.
>
> > --- a/xen/drivers/passthrough/x86/hvm.c
> > +++ b/xen/drivers/passthrough/x86/hvm.c
> > @@ -924,12 +924,11 @@ static void hvm_gsi_eoi(struct domain *d, unsigned int gsi)
> > {
> > struct pirq *pirq = pirq_info(d, gsi);
> >
> > - /* Check if GSI is actually mapped. */
> > - if ( !pirq_dpci(pirq) )
> > - return;
> > -
> > hvm_gsi_deassert(d, gsi);
> > - hvm_pirq_eoi(pirq);
> > +
> > + /* Check if GSI is actually mapped. */
> > + if ( pirq_dpci(pirq) )
> > + hvm_pirq_eoi(pirq);
> > }
>
> The correctness of this change (in particular hvm_gsi_deassert() now always
> running) wants reasoning about in the description.
>
> Jan
>
^ permalink raw reply [flat|nested] 61+ messages in thread
* [PATCH v4 8/8] emul/vuart: introduce console forwarding enforcement via vUART
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
` (6 preceding siblings ...)
2025-07-31 19:22 ` [PATCH v4 7/8] tools/xl: enable NS16550-compatible UART emulator for PVH (x86) dmkhn
@ 2025-07-31 19:22 ` dmkhn
2025-08-01 0:10 ` Stefano Stabellini
2025-08-06 13:48 ` [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator Roger Pau Monné
8 siblings, 1 reply; 61+ messages in thread
From: dmkhn @ 2025-07-31 19:22 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>
It may be useful to enforce console forwarding over the virtual UART. E.g.
hardware domain uses PV console by default, but it may be necessary to have
console forwarded to the hardware domain via emulated UART.
Add CONFIG_VUART_CONSOLE_FOCUS to enforce such behavior.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v3:
- new patch
---
xen/arch/x86/domain.c | 6 ++++++
xen/common/emul/vuart/Kconfig | 5 +++++
xen/drivers/char/console.c | 2 +-
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 39b0c0b199b9..40ff92ad6c61 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -848,6 +848,12 @@ int arch_domain_create(struct domain *d,
if ( is_hardware_domain(d) && is_pv_domain(d) )
emflags |= XEN_X86_EMU_PIT;
+ if ( IS_ENABLED(CONFIG_VUART_CONSOLE_FOCUS) &&
+ IS_ENABLED(CONFIG_VUART_NS16550) &&
+ is_hardware_domain(d) &&
+ is_hvm_domain(d) )
+ emflags |= XEN_X86_EMU_NS16550;
+
if ( emflags & ~XEN_X86_EMU_ALL )
{
printk(XENLOG_G_ERR
diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
index ebefd90d913e..1069ca95f2db 100644
--- a/xen/common/emul/vuart/Kconfig
+++ b/xen/common/emul/vuart/Kconfig
@@ -51,4 +51,9 @@ config VUART_NS16550_DEBUG
help
Enable development debugging.
+config VUART_CONSOLE_FOCUS
+ bool "Console input forwarding via UART emulator"
+ help
+ Enable physical console input forwarding to guest OS via emulated UART.
+
endmenu
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 93254979817b..d142f5511d61 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -597,7 +597,7 @@ static void __serial_rx(char c)
if ( !d )
return;
- if ( is_hardware_domain(d) )
+ if ( !IS_ENABLED(CONFIG_VUART_CONSOLE_FOCUS) && is_hardware_domain(d) )
{
/*
* Deliver input to the hardware domain buffer, unless it is
--
2.34.1
^ permalink raw reply related [flat|nested] 61+ messages in thread* Re: [PATCH v4 8/8] emul/vuart: introduce console forwarding enforcement via vUART
2025-07-31 19:22 ` [PATCH v4 8/8] emul/vuart: introduce console forwarding enforcement via vUART dmkhn
@ 2025-08-01 0:10 ` Stefano Stabellini
2025-08-01 1:51 ` dmkhn
0 siblings, 1 reply; 61+ messages in thread
From: Stefano Stabellini @ 2025-08-01 0:10 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, sstabellini, dmukhin
On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
> From: Denis Mukhin <dmukhin@ford.com>
>
> It may be useful to enforce console forwarding over the virtual UART. E.g.
> hardware domain uses PV console by default, but it may be necessary to have
> console forwarded to the hardware domain via emulated UART.
>
> Add CONFIG_VUART_CONSOLE_FOCUS to enforce such behavior.
>
> Signed-off-by: Denis Mukhin <dmukhin@ford.com>
I realize that hyperlaunch is not merged yet, but I think this one would
be best as a hyperlaunch configuration option rather than a kconfig. But
it is certainly useful for testing until Hyperlaunch is merged!
> ---
> Changes since v3:
> - new patch
> ---
> xen/arch/x86/domain.c | 6 ++++++
> xen/common/emul/vuart/Kconfig | 5 +++++
> xen/drivers/char/console.c | 2 +-
> 3 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
> index 39b0c0b199b9..40ff92ad6c61 100644
> --- a/xen/arch/x86/domain.c
> +++ b/xen/arch/x86/domain.c
> @@ -848,6 +848,12 @@ int arch_domain_create(struct domain *d,
> if ( is_hardware_domain(d) && is_pv_domain(d) )
> emflags |= XEN_X86_EMU_PIT;
>
> + if ( IS_ENABLED(CONFIG_VUART_CONSOLE_FOCUS) &&
> + IS_ENABLED(CONFIG_VUART_NS16550) &&
> + is_hardware_domain(d) &&
> + is_hvm_domain(d) )
> + emflags |= XEN_X86_EMU_NS16550;
> +
> if ( emflags & ~XEN_X86_EMU_ALL )
> {
> printk(XENLOG_G_ERR
> diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> index ebefd90d913e..1069ca95f2db 100644
> --- a/xen/common/emul/vuart/Kconfig
> +++ b/xen/common/emul/vuart/Kconfig
> @@ -51,4 +51,9 @@ config VUART_NS16550_DEBUG
> help
> Enable development debugging.
>
> +config VUART_CONSOLE_FOCUS
> + bool "Console input forwarding via UART emulator"
> + help
> + Enable physical console input forwarding to guest OS via emulated UART.
> +
> endmenu
> diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> index 93254979817b..d142f5511d61 100644
> --- a/xen/drivers/char/console.c
> +++ b/xen/drivers/char/console.c
> @@ -597,7 +597,7 @@ static void __serial_rx(char c)
> if ( !d )
> return;
>
> - if ( is_hardware_domain(d) )
> + if ( !IS_ENABLED(CONFIG_VUART_CONSOLE_FOCUS) && is_hardware_domain(d) )
> {
> /*
> * Deliver input to the hardware domain buffer, unless it is
> --
> 2.34.1
>
>
^ permalink raw reply [flat|nested] 61+ messages in thread* Re: [PATCH v4 8/8] emul/vuart: introduce console forwarding enforcement via vUART
2025-08-01 0:10 ` Stefano Stabellini
@ 2025-08-01 1:51 ` dmkhn
0 siblings, 0 replies; 61+ messages in thread
From: dmkhn @ 2025-08-01 1:51 UTC (permalink / raw)
To: Stefano Stabellini
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, roger.pau, dmukhin
On Thu, Jul 31, 2025 at 05:10:12PM -0700, Stefano Stabellini wrote:
> On Thu, 31 Jul 2025, dmkhn@proton.me wrote:
> > From: Denis Mukhin <dmukhin@ford.com>
> >
> > It may be useful to enforce console forwarding over the virtual UART. E.g.
> > hardware domain uses PV console by default, but it may be necessary to have
> > console forwarded to the hardware domain via emulated UART.
> >
> > Add CONFIG_VUART_CONSOLE_FOCUS to enforce such behavior.
> >
> > Signed-off-by: Denis Mukhin <dmukhin@ford.com>
>
> I realize that hyperlaunch is not merged yet, but I think this one would
> be best as a hyperlaunch configuration option rather than a kconfig. But
> it is certainly useful for testing until Hyperlaunch is merged!
Yep, I used that to test PVH dom0 w/ emulator.
re: configuration option: I agree and, initially I wanted to hook a flag to xl
/ dom0 command line opton which will say that emulated UART can accept
physical input.
But then decided to hook such capability via Kconfig for simplicity.
>
>
> > ---
> > Changes since v3:
> > - new patch
> > ---
> > xen/arch/x86/domain.c | 6 ++++++
> > xen/common/emul/vuart/Kconfig | 5 +++++
> > xen/drivers/char/console.c | 2 +-
> > 3 files changed, 12 insertions(+), 1 deletion(-)
> >
> > diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
> > index 39b0c0b199b9..40ff92ad6c61 100644
> > --- a/xen/arch/x86/domain.c
> > +++ b/xen/arch/x86/domain.c
> > @@ -848,6 +848,12 @@ int arch_domain_create(struct domain *d,
> > if ( is_hardware_domain(d) && is_pv_domain(d) )
> > emflags |= XEN_X86_EMU_PIT;
> >
> > + if ( IS_ENABLED(CONFIG_VUART_CONSOLE_FOCUS) &&
> > + IS_ENABLED(CONFIG_VUART_NS16550) &&
> > + is_hardware_domain(d) &&
> > + is_hvm_domain(d) )
> > + emflags |= XEN_X86_EMU_NS16550;
> > +
> > if ( emflags & ~XEN_X86_EMU_ALL )
> > {
> > printk(XENLOG_G_ERR
> > diff --git a/xen/common/emul/vuart/Kconfig b/xen/common/emul/vuart/Kconfig
> > index ebefd90d913e..1069ca95f2db 100644
> > --- a/xen/common/emul/vuart/Kconfig
> > +++ b/xen/common/emul/vuart/Kconfig
> > @@ -51,4 +51,9 @@ config VUART_NS16550_DEBUG
> > help
> > Enable development debugging.
> >
> > +config VUART_CONSOLE_FOCUS
> > + bool "Console input forwarding via UART emulator"
> > + help
> > + Enable physical console input forwarding to guest OS via emulated UART.
> > +
> > endmenu
> > diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
> > index 93254979817b..d142f5511d61 100644
> > --- a/xen/drivers/char/console.c
> > +++ b/xen/drivers/char/console.c
> > @@ -597,7 +597,7 @@ static void __serial_rx(char c)
> > if ( !d )
> > return;
> >
> > - if ( is_hardware_domain(d) )
> > + if ( !IS_ENABLED(CONFIG_VUART_CONSOLE_FOCUS) && is_hardware_domain(d) )
> > {
> > /*
> > * Deliver input to the hardware domain buffer, unless it is
> > --
> > 2.34.1
> >
> >
^ permalink raw reply [flat|nested] 61+ messages in thread
* Re: [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator
2025-07-31 19:21 [PATCH v4 0/8] x86: introduce NS16550-compatible UART emulator dmkhn
` (7 preceding siblings ...)
2025-07-31 19:22 ` [PATCH v4 8/8] emul/vuart: introduce console forwarding enforcement via vUART dmkhn
@ 2025-08-06 13:48 ` Roger Pau Monné
8 siblings, 0 replies; 61+ messages in thread
From: Roger Pau Monné @ 2025-08-06 13:48 UTC (permalink / raw)
To: dmkhn
Cc: xen-devel, andrew.cooper3, anthony.perard, jbeulich, julien,
michal.orzel, sstabellini, dmukhin
On Thu, Jul 31, 2025 at 07:21:37PM +0000, dmkhn@proton.me 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_NS16550.
>
> 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_NS16550 enables emulation of NS16550 at I/O port
> 0x3f8, IRQ#4 in guest OS (legacy COM1). But all legacy COM resources can be
> selected at built-time (no per-domain configuration yet).
>
> The NS16550 emulator is disabled in default x86 configuration and goes under
> CONFIG_EXPERT in menuconfig.
>
> Limitations
> ===========
> - Only x86;
> - Only HVM domains support (build-time), PVH domains are not supported yet;
I'm confused by the above statement, HVM domains always have an
emulated serial provided by QEMU if needed, so I think you got this
backwards, and it should instead be:
- Only PVH domains supported, PV and HVM domains are not supported yet.
?
Thanks, Roger.
^ permalink raw reply [flat|nested] 61+ messages in thread