[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC PATCH v7bis 17/19] hw/xen: Implement emulated PIRQ hypercall suppor
From: |
David Woodhouse |
Subject: |
[RFC PATCH v7bis 17/19] hw/xen: Implement emulated PIRQ hypercall support |
Date: |
Mon, 16 Jan 2023 22:19:17 +0000 |
From: David Woodhouse <dwmw@amazon.co.uk>
This wires up the basic infrastructure but the actual interrupts aren't
there yet, so don't advertise it to the guest.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
hw/i386/kvm/trace-events | 4 +
hw/i386/kvm/trace.h | 1 +
hw/i386/kvm/xen_evtchn.c | 252 +++++++++++++++++++++++++++++++++++++-
hw/i386/kvm/xen_evtchn.h | 2 +
meson.build | 1 +
target/i386/kvm/xen-emu.c | 15 +++
6 files changed, 270 insertions(+), 5 deletions(-)
create mode 100644 hw/i386/kvm/trace-events
create mode 100644 hw/i386/kvm/trace.h
diff --git a/hw/i386/kvm/trace-events b/hw/i386/kvm/trace-events
new file mode 100644
index 0000000000..04e60c5bb8
--- /dev/null
+++ b/hw/i386/kvm/trace-events
@@ -0,0 +1,4 @@
+kvm_xen_map_pirq(int pirq, int gsi) "pirq %d gsi %d"
+kvm_xen_unmap_pirq(int pirq, int gsi) "pirq %d gsi %d"
+kvm_xen_get_free_pirq(int pirq, int type) "pirq %d type %d"
+kvm_xen_bind_pirq(int pirq, int port) "pirq %d port %d"
diff --git a/hw/i386/kvm/trace.h b/hw/i386/kvm/trace.h
new file mode 100644
index 0000000000..e55d0812fd
--- /dev/null
+++ b/hw/i386/kvm/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_i386_kvm.h"
diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c
index c381a9f405..5672b083c3 100644
--- a/hw/i386/kvm/xen_evtchn.c
+++ b/hw/i386/kvm/xen_evtchn.c
@@ -23,6 +23,7 @@
#include "exec/target_page.h"
#include "exec/address-spaces.h"
#include "migration/vmstate.h"
+#include "trace.h"
#include "hw/sysbus.h"
#include "hw/xen/xen.h"
@@ -105,6 +106,23 @@ struct xenevtchn_handle {
#define PORT_INFO_TYPEVAL_REMOTE_QEMU 0x8000
#define PORT_INFO_TYPEVAL_REMOTE_PORT_MASK 0x7FFF
+#define MAX_XEN_PIRQ 0x1048 /* Empirically */
+
+/*
+ * These 'emuirq' values are used by Xen in the LM stream... and yes, I am
+ * insane enough to think about guest-transparent live migration from actual
+ * Xen to QEMU, and ensuring that we can convert/consume the stream.
+ */
+#define IRQ_UNBOUND -1
+#define IRQ_PT -2
+#define IRQ_MSI_EMU -3
+
+
+struct pirq_info {
+ int gsi;
+ uint16_t port;
+};
+
struct XenEvtchnState {
/*< private >*/
SysBusDevice busdev;
@@ -122,6 +140,14 @@ struct XenEvtchnState {
qemu_irq gsis[GSI_NUM_PINS];
struct xenevtchn_handle *be_handles[EVTCHN_2L_NR_CHANNELS];
+
+ /* GSI → PIRQ mapping (serialized) */
+ uint16_t gsi_pirq[GSI_NUM_PINS];
+ /* Bitmap of allocated PIRQs (serialized) */
+ uint64_t pirq_inuse[DIV_ROUND_UP(MAX_XEN_PIRQ, 64)];
+
+ /* Per-PIRQ information (rebuilt on migration) */
+ struct pirq_info pirq[MAX_XEN_PIRQ];
};
struct XenEvtchnState *xen_evtchn_singleton;
@@ -181,6 +207,9 @@ static const VMStateDescription xen_evtchn_vmstate = {
VMSTATE_UINT32(nr_ports, XenEvtchnState),
VMSTATE_STRUCT_VARRAY_UINT32(port_table, XenEvtchnState, nr_ports, 1,
xen_evtchn_port_vmstate, XenEvtchnPort),
+ VMSTATE_UINT16_ARRAY(gsi_pirq, XenEvtchnState, GSI_NUM_PINS),
+ VMSTATE_UINT64_ARRAY(pirq_inuse, XenEvtchnState,
+ DIV_ROUND_UP(MAX_XEN_PIRQ, 64)),
VMSTATE_END_OF_LIST()
}
};
@@ -935,6 +964,10 @@ static int close_port(XenEvtchnState *s, evtchn_port_t
port)
case EVTCHNSTAT_closed:
return -ENOENT;
+ case EVTCHNSTAT_pirq:
+ s->pirq[p->type_val].port = 0;
+ break;
+
case EVTCHNSTAT_virq:
kvm_xen_set_vcpu_virq(virq_is_global(p->type_val) ? 0 : p->vcpu,
p->type_val, 0);
@@ -1129,6 +1162,37 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq
*virq)
return ret;
}
+int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq)
+{
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int ret;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ if (pirq->pirq >= MAX_XEN_PIRQ) {
+ return -EINVAL;
+ }
+
+ QEMU_LOCK_GUARD(&s->port_lock);
+
+ if (s->pirq[pirq->pirq].port) {
+ return -EBUSY;
+ }
+
+ ret = allocate_port(s, 0, EVTCHNSTAT_pirq, pirq->pirq,
+ &pirq->port);
+ if (ret) {
+ return ret;
+ }
+
+ s->pirq[pirq->pirq].port = pirq->port;
+ trace_kvm_xen_bind_pirq(pirq->pirq, pirq->port);
+
+ return ret;
+}
+
int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi)
{
XenEvtchnState *s = xen_evtchn_singleton;
@@ -1355,29 +1419,207 @@ int xen_evtchn_set_port(uint16_t port)
return ret;
}
+#define pirq_inuse_word(s, pirq) (s->pirq_inuse[((pirq) / 64)])
+#define pirq_inuse_bit(pirq) (1ULL << ((pirq) & 63))
+
+#define pirq_inuse(s, pirq) (pirq_inuse_word(s, pirq) & pirq_inuse_bit(pirq))
+
+static int allocate_pirq(XenEvtchnState *s, int type, int gsi)
+{
+ uint16_t pirq;
+
+ /* Preserve the allocation strategy that Xen has. It looks like
+ * we *never* give out PIRQ 0-15, we give out 16-nr_irqs_gsi only
+ * to GSIs (counting up from 16), and then we count backwards from
+ * the top for MSIs or when the GSI space is exhausted. */
+ if (type == MAP_PIRQ_TYPE_GSI) {
+ for (pirq = 16 ; pirq < GSI_NUM_PINS; pirq++) {
+ if (pirq_inuse(s, pirq)) {
+ continue;
+ }
+
+ /* Found it */
+ goto found;
+ }
+ }
+ for (pirq = MAX_XEN_PIRQ - 1; pirq >= GSI_NUM_PINS; pirq--) {
+ /* Skip whole words at a time when they're full */
+ if (pirq_inuse_word(s, pirq) == UINT64_MAX) {
+ pirq &= ~63ULL;
+ continue;
+ }
+ if (pirq_inuse(s, pirq)) {
+ continue;
+ }
+
+ goto found;
+ }
+ return -ENOSPC;
+
+ found:
+ pirq_inuse_word(s, pirq) |= pirq_inuse_bit(pirq);
+ if (gsi >= 0) {
+ assert(gsi <= GSI_NUM_PINS);
+ s->gsi_pirq[gsi] = pirq;
+ }
+ s->pirq[pirq].gsi = gsi;
+ return pirq;
+}
+
int xen_physdev_map_pirq(struct physdev_map_pirq *map)
{
- return -ENOTSUP;
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int pirq = map->pirq;
+ int gsi = map->index;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ QEMU_LOCK_GUARD(&s->port_lock);
+
+ if (map->domid != DOMID_SELF && map->domid != xen_domid) {
+ return -EPERM;
+ }
+ if (map->type != MAP_PIRQ_TYPE_GSI) {
+ return -EINVAL;
+ }
+ if (gsi < 0 || gsi >= GSI_NUM_PINS) {
+ return -EINVAL;
+ }
+
+ if (pirq < 0) {
+ pirq = allocate_pirq(s, map->type, gsi);
+ if (pirq < 0) {
+ return pirq;
+ }
+ map->pirq = pirq;
+ } else if (pirq > MAX_XEN_PIRQ) {
+ return -EINVAL;
+ } else {
+ /* User specified a valid-looking PIRQ#. Allow it if it is
+ * allocated and not yet bound, or if it is unallocated */
+ if (pirq_inuse(s, pirq)) {
+ if (s->pirq[pirq].gsi != IRQ_UNBOUND) {
+ return -EBUSY;
+ }
+ } else {
+ /* If it was unused, mark it used now. */
+ pirq_inuse_word(s, pirq) |= pirq_inuse_bit(pirq);
+ }
+ /* Set the mapping in both directions. */
+ s->pirq[pirq].gsi = gsi;
+ s->gsi_pirq[gsi] = pirq;
+ }
+
+ trace_kvm_xen_map_pirq(pirq, gsi);
+ return 0;
}
int xen_physdev_unmap_pirq(struct physdev_unmap_pirq *unmap)
{
- return -ENOTSUP;
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int pirq = unmap->pirq;
+ int gsi;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ if (unmap->domid != DOMID_SELF && unmap->domid != xen_domid)
+ return -EPERM;
+ if (pirq < 0 || pirq >= MAX_XEN_PIRQ)
+ return -EINVAL;
+
+ QEMU_LOCK_GUARD(&s->port_lock);
+
+ if (!pirq_inuse(s, pirq)) {
+ return -ENOENT;
+ }
+
+ gsi = s->pirq[pirq].gsi;
+
+ /* We can only unmap GSI PIRQs */
+ if (gsi < 0) {
+ return -EINVAL;
+ }
+
+ s->gsi_pirq[gsi] = 0;
+ s->pirq[pirq].gsi = IRQ_UNBOUND; /* Doesn't actually matter because: */
+ pirq_inuse_word(s, pirq) &= ~pirq_inuse_bit(pirq);
+
+ trace_kvm_xen_unmap_pirq(pirq, gsi);
+ return 0;
}
int xen_physdev_eoi_pirq(struct physdev_eoi *eoi)
{
- return -ENOTSUP;
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int pirq = eoi->irq;
+ int gsi;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ QEMU_LOCK_GUARD(&s->port_lock);
+
+ if (!pirq_inuse(s, pirq)) {
+ return -ENOENT;
+ }
+
+ gsi = s->pirq[pirq].gsi;
+ if (gsi < 0) {
+ return -EINVAL;
+ }
+
+ // XX: Reassert a level IRQ if needed */
+ return 0;
}
int xen_physdev_query_pirq(struct physdev_irq_status_query *query)
{
- return -ENOTSUP;
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int pirq = query->irq;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ QEMU_LOCK_GUARD(&s->port_lock);
+
+ if (!pirq_inuse(s, pirq)) {
+ return -ENOENT;
+ }
+
+ if (s->pirq[pirq].gsi >= 0) {
+ query->flags = XENIRQSTAT_needs_eoi;
+ } else {
+ query->flags = 0;
+ }
+
+ return 0;
}
int xen_physdev_get_free_pirq(struct physdev_get_free_pirq *get)
{
- return -ENOTSUP;
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int pirq;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ QEMU_LOCK_GUARD(&s->port_lock);
+
+ pirq = allocate_pirq(s, get->type, IRQ_UNBOUND);
+ if (pirq < 0) {
+ return pirq;
+ }
+
+ get->pirq = pirq;
+ trace_kvm_xen_get_free_pirq(pirq, get->type);
+ return 0;
}
struct xenevtchn_handle *xen_be_evtchn_open(void)
diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h
index ccf58aa796..2c12506cc2 100644
--- a/hw/i386/kvm/xen_evtchn.h
+++ b/hw/i386/kvm/xen_evtchn.h
@@ -48,6 +48,7 @@ struct evtchn_status;
struct evtchn_close;
struct evtchn_unmask;
struct evtchn_bind_virq;
+struct evtchn_bind_pirq;
struct evtchn_bind_ipi;
struct evtchn_send;
struct evtchn_alloc_unbound;
@@ -58,6 +59,7 @@ int xen_evtchn_status_op(struct evtchn_status *status);
int xen_evtchn_close_op(struct evtchn_close *close);
int xen_evtchn_unmask_op(struct evtchn_unmask *unmask);
int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq);
+int xen_evtchn_bind_pirq_op(struct evtchn_bind_pirq *pirq);
int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi);
int xen_evtchn_send_op(struct evtchn_send *send);
int xen_evtchn_alloc_unbound_op(struct evtchn_alloc_unbound *alloc);
diff --git a/meson.build b/meson.build
index 6171183a30..743da8861c 100644
--- a/meson.build
+++ b/meson.build
@@ -2938,6 +2938,7 @@ if have_system
'hw/i2c',
'hw/i386',
'hw/i386/xen',
+ 'hw/i386/kvm',
'hw/ide',
'hw/input',
'hw/intc',
diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c
index 623331c40e..03b999d0b5 100644
--- a/target/i386/kvm/xen-emu.c
+++ b/target/i386/kvm/xen-emu.c
@@ -1227,6 +1227,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit
*exit, X86CPU *cpu,
}
break;
}
+ case EVTCHNOP_bind_pirq: {
+ struct evtchn_bind_pirq pirq;
+
+ qemu_build_assert(sizeof(pirq) == 12);
+ if (kvm_copy_from_gva(cs, arg, &pirq, sizeof(pirq))) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = xen_evtchn_bind_pirq_op(&pirq);
+ if (!err && kvm_copy_to_gva(cs, arg, &pirq, sizeof(pirq))) {
+ err = -EFAULT;
+ }
+ break;
+ }
case EVTCHNOP_bind_ipi: {
struct evtchn_bind_ipi ipi;
--
2.39.0
- [PATCH v7 01/51] include: import Xen public headers to include/standard-headers/, (continued)
- [PATCH v7 01/51] include: import Xen public headers to include/standard-headers/, David Woodhouse, 2023/01/16
- [PATCH v7 33/51] hw/xen: Implement EVTCHNOP_send, David Woodhouse, 2023/01/16
- [PATCH v7 49/51] hw/xen: Add backend implementation of interdomain event channel support, David Woodhouse, 2023/01/16
- [PATCH v7 22/51] i386/xen: handle VCPUOP_register_runstate_memory_area, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 00/19] Emulated Xen PV backend and PIRQ support, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 13/19] hw/xen: Implement soft reset for emulated gnttab, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 11/19] hw/xen: Map guest XENSTORE_PFN grant in emulated Xenstore, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 16/19] i386/xen: Implement HYPERVISOR_physdev_op, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 03/19] hw/xen: Add gnttab operations to allow redirection to internal emulation, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 05/19] hw/xen: Add foreignmem operations to allow redirection to internal emulation, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 17/19] hw/xen: Implement emulated PIRQ hypercall support,
David Woodhouse <=
- [RFC PATCH v7bis 08/19] hw/xen: Use XEN_PAGE_SIZE in PV backend drivers, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 07/19] hw/xen: Move xenstore_store_pv_console_info to xen_console.c, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 06/19] hw/xen: Add xenstore operations to allow redirection to internal emulation, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 15/19] i386/xen: Initialize XenBus and legacy backends from pc_init1(), David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 12/19] hw/xen: Add backend implementation of grant table operations, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 14/19] hw/xen: Remove old version of Xen headers, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 02/19] hw/xen: Add emulated evtchn ops, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 18/19] hw/xen: Support GSI mapping to PIRQ, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 19/19] hw/xen: Support MSI mapping to PIRQ, David Woodhouse, 2023/01/16
- [RFC PATCH v7bis 04/19] hw/xen: Pass grant ref to gnttab unmap, David Woodhouse, 2023/01/16