From: David Woodhouse Date: Wed, 14 Dec 2022 00:11:07 +0000 (+0000) Subject: hw/xen: Implement EVTCHNOP_send X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=a22f1af10360b505da2e75100e4ad59e65653f4e;p=users%2Fdwmw2%2Fqemu.git hw/xen: Implement EVTCHNOP_send Signed-off-by: David Woodhouse --- diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 2e35812b32..d90a92a25a 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -441,6 +441,117 @@ static int unmask_port(XenEvtchnState *s, evtchn_port_t port, bool do_unmask) } } +static int do_set_port_lm(XenEvtchnState *s, evtchn_port_t port, + struct shared_info *shinfo, + struct vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + /* Update the pending bit itself. If it was already set, we're done. */ + if (qatomic_fetch_or(&shinfo->evtchn_pending[idx], mask) & mask) { + return 0; + } + + /* Check if it's masked. */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + kvm_xen_inject_vcpu_callback_vector(s->port_table[port].vcpu); + + return 0; +} + +static int do_set_port_compat(XenEvtchnState *s, evtchn_port_t port, + struct compat_shared_info *shinfo, + struct compat_vcpu_info *vcpu_info) +{ + const int bits_per_word = BITS_PER_BYTE * sizeof(shinfo->evtchn_pending[0]); + typeof(shinfo->evtchn_pending[0]) mask; + int idx = port / bits_per_word; + int offset = port % bits_per_word; + + mask = 1UL << offset; + + if (idx >= bits_per_word) { + return -EINVAL; + } + + /* Update the pending bit itself. If it was already set, we're done. */ + if (qatomic_fetch_or(&shinfo->evtchn_pending[idx], mask) & mask) { + return 0; + } + + /* Check if it's masked. */ + if (qatomic_fetch_or(&shinfo->evtchn_mask[idx], 0) & mask) { + return 0; + } + + /* Now on to the vcpu_info evtchn_pending_sel index... */ + mask = 1UL << idx; + + /* If a port in this word was already pending for this vCPU, all done. */ + if (qatomic_fetch_or(&vcpu_info->evtchn_pending_sel, mask) & mask) { + return 0; + } + + /* Set evtchn_upcall_pending for this vCPU */ + if (qatomic_fetch_or(&vcpu_info->evtchn_upcall_pending, 1)) { + return 0; + } + + kvm_xen_inject_vcpu_callback_vector(s->port_table[port].vcpu); + + return 0; +} + +static int set_port_pending(XenEvtchnState *s, evtchn_port_t port) +{ + void *vcpu_info, *shinfo; + + if (s->port_table[port].type == EVTCHNSTAT_closed) { + return -EINVAL; + } + + shinfo = xen_overlay_page_ptr(XENMAPSPACE_shared_info, 0); + if (!shinfo) { + return -ENOTSUP; + } + + vcpu_info = kvm_xen_get_vcpu_info_hva(s->port_table[port].vcpu); + if (!vcpu_info) { + return -EINVAL; + } + + if (xen_is_long_mode()) { + return do_set_port_lm(s, port, shinfo, vcpu_info); + } else { + return do_set_port_compat(s, port, shinfo, vcpu_info); + } +} + static bool virq_is_global(uint32_t virq) { switch (virq) { @@ -620,3 +731,54 @@ int xen_evtchn_bind_ipi_op(struct evtchn_bind_ipi *ipi) return ret; } + +int xen_evtchn_send_op(struct evtchn_send *send) +{ + XenEvtchnState *s = xen_evtchn_singleton; + XenEvtchnPort *p; + int ret = 0; + + if (!s) { + return -ENOTSUP; + } + + if (!valid_port(send->port)) { + return -EINVAL; + } + + qemu_mutex_lock(&s->port_lock); + + p = &s->port_table[send->port]; + + switch(p->type) { + case EVTCHNSTAT_interdomain: + if (p->type_val & PORT_INFO_TYPEVAL_REMOTE_QEMU) { + /* This is an event from the guest to qemu itself, which is + * serving as the driver domain. Not yet implemented; it will + * be hooked up to the qemu implementation of xenstore, + * console, PV net/block drivers etc. */ + ret = -ENOSYS; + } else { + /* Loopback interdomain ports; just a complex IPI */ + set_port_pending(s, p->type_val); + } + break; + + case EVTCHNSTAT_ipi: + set_port_pending(s, send->port); + break; + + case EVTCHNSTAT_unbound: + /* Xen will silently drop these */ + break; + + default: + ret = -EINVAL; + break; + } + + 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 52ade5a64e..c27b9e8096 100644 --- a/hw/i386/kvm/xen_evtchn.h +++ b/hw/i386/kvm/xen_evtchn.h @@ -18,8 +18,10 @@ struct evtchn_close; struct evtchn_unmask; struct evtchn_bind_virq; struct evtchn_bind_ipi; +struct evtchn_send; 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); +int xen_evtchn_send_op(struct evtchn_send *send); diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index b5f8f30d62..300b0d75bc 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -800,6 +800,18 @@ static bool kvm_xen_hcall_evtchn_op(struct kvm_xen_exit *exit, X86CPU *cpu, } break; } + case EVTCHNOP_send: { + struct evtchn_send send; + + qemu_build_assert(sizeof(send) == 4); + if (kvm_copy_from_gva(cs, arg, &send, sizeof(send))) { + err = -EFAULT; + break; + } + + err = xen_evtchn_send_op(&send); + break; + } default: return false; }