From f5417856d25f1cf4932ac77a687979af8427f313 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 13 Dec 2022 23:12:59 +0000 Subject: [PATCH] hw/xen: Implement EVTCHNOP_bind_ipi Signed-off-by: David Woodhouse Reviewed-by: Paul Durrant --- hw/i386/kvm/xen_evtchn.c | 69 +++++++++++++++++++++++++++++++++++++++ hw/i386/kvm/xen_evtchn.h | 2 ++ target/i386/kvm/xen-emu.c | 15 +++++++++ 3 files changed, 86 insertions(+) diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index a3202d39ab..eea80dc0f5 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -13,6 +13,7 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" @@ -231,6 +232,43 @@ static void inject_callback(XenEvtchnState *s, uint32_t vcpu) kvm_xen_inject_vcpu_callback_vector(vcpu, type); } +static void deassign_kernel_port(evtchn_port_t port) +{ + struct kvm_xen_hvm_attr ha; + int ret; + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.flags = KVM_XEN_EVTCHN_DEASSIGN; + + ret = kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "Failed to unbind kernel port %d: %s\n", + port, strerror(ret)); + } +} + +static int assign_kernel_port(uint16_t type, evtchn_port_t port, + uint32_t vcpu_id) +{ + CPUState *cpu = qemu_get_cpu(vcpu_id); + struct kvm_xen_hvm_attr ha; + + if (!cpu) { + return -ENOENT; + } + + ha.type = KVM_XEN_ATTR_TYPE_EVTCHN; + ha.u.evtchn.send_port = port; + ha.u.evtchn.type = type; + ha.u.evtchn.flags = 0; + ha.u.evtchn.deliver.port.port = port; + ha.u.evtchn.deliver.port.vcpu = kvm_arch_vcpu_id(cpu); + ha.u.evtchn.deliver.port.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; + + return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &ha); +} + static bool valid_port(evtchn_port_t port) { if (!port) { @@ -551,6 +589,12 @@ static int close_port(XenEvtchnState *s, evtchn_port_t port) p->type_val, 0); break; + case EVTCHNSTAT_ipi: + if (s->evtchn_in_kernel) { + deassign_kernel_port(port); + } + break; + default: break; } @@ -640,3 +684,28 @@ int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq) return ret; } + +int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) +{ + XenEvtchnState *s = xen_evtchn_singleton; + int ret; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_vcpu(ipi->vcpu)) { + return -ENOENT; + } + + qemu_mutex_lock(&s->port_lock); + + ret = allocate_port(s, ipi->vcpu, EVTCHNSTAT_ipi, 0, &ipi->port); + if (!ret && s->evtchn_in_kernel) { + assign_kernel_port(EVTCHNSTAT_ipi, ipi->port, ipi->vcpu); + } + + qemu_mutex_unlock(&s->port_lock); + + return ret; +} diff --git a/hw/i386/kvm/xen_evtchn.h b/hw/i386/kvm/xen_evtchn.h index 0ea13dda3a..107f420848 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -19,9 +19,11 @@ struct evtchn_status; struct evtchn_close; struct evtchn_unmask; struct evtchn_bind_virq; +struct evtchn_bind_ipi; 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_ipi_op(struct evtchn_bind_ipi *ipi); #endif /* QEMU_XEN_EVTCHN_H */ diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index e0417f3d13..b8b439dd31 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -894,6 +894,21 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_bind_ipi: { + struct evtchn_bind_ipi ipi; + + qemu_build_assert(sizeof(ipi) == 8); + if (kvm_copy_from_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_bind_ipi_op(&ipi); + if (!err && kvm_copy_to_gva(cs, arg, &ipi, sizeof(ipi))) { + err = -EFAULT; + } + break; + } default: return false; }