}
}
+static bool valid_vcpu(uint32_t vcpu)
+{
+ return !!qemu_get_cpu(vcpu);
+}
+
int xen_evtchn_status_op(struct evtchn_status *status)
{
XenEvtchnState *s = xen_evtchn_singleton;
clear_port_pending(s, port);
}
+static int allocate_port(XenEvtchnState *s, uint32_t vcpu, uint16_t type,
+ uint16_t val, evtchn_port_t *port)
+{
+ evtchn_port_t p = 1;
+
+ for (p = 1; valid_port(p); p++) {
+ if (s->port_table[p].type == EVTCHNSTAT_closed) {
+ s->port_table[p].vcpu = vcpu;
+ s->port_table[p].type = type;
+ s->port_table[p].type_val = val;
+
+ *port = p;
+
+ if (s->nr_ports < p + 1) {
+ s->nr_ports = p + 1;
+ }
+
+ return 0;
+ }
+ }
+ return -ENOSPC;
+}
+
+static bool virq_is_global(uint32_t virq)
+{
+ switch (virq) {
+ case VIRQ_TIMER:
+ case VIRQ_DEBUG:
+ case VIRQ_XENOPROF:
+ case VIRQ_XENPMU:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
static int close_port(XenEvtchnState *s, evtchn_port_t port)
{
XenEvtchnPort *p = &s->port_table[port];
case EVTCHNSTAT_closed:
return -ENOENT;
+ case EVTCHNSTAT_virq:
+ kvm_xen_set_vcpu_virq(virq_is_global(p->type_val) ? 0 : p->vcpu,
+ p->type_val, 0);
+ break;
+
default:
break;
}
return ret;
}
+
+int xen_evtchn_bind_virq_op(struct evtchn_bind_virq *virq)
+{
+ XenEvtchnState *s = xen_evtchn_singleton;
+ int ret;
+
+ if (!s) {
+ return -ENOTSUP;
+ }
+
+ if (virq->virq >= NR_VIRQS) {
+ return -EINVAL;
+ }
+
+ /* Global VIRQ must be allocated on vCPU0 first */
+ if (virq_is_global(virq->virq) && virq->vcpu != 0) {
+ return -EINVAL;
+ }
+
+ if (!valid_vcpu(virq->vcpu)) {
+ return -ENOENT;
+ }
+
+ qemu_mutex_lock(&s->port_lock);
+
+ ret = allocate_port(s, virq->vcpu, EVTCHNSTAT_virq, virq->virq,
+ &virq->port);
+ if (!ret) {
+ ret = kvm_xen_set_vcpu_virq(virq->vcpu, virq->virq, virq->port);
+ if (ret) {
+ free_port(s, virq->port);
+ }
+ }
+
+ qemu_mutex_unlock(&s->port_lock);
+
+ return ret;
+}
}
}
+static int kvm_xen_set_vcpu_timer(CPUState *cs)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+
+ struct kvm_xen_vcpu_attr va = {
+ .type = KVM_XEN_VCPU_ATTR_TYPE_TIMER,
+ .u.timer.port = env->xen_virq[VIRQ_TIMER],
+ .u.timer.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL,
+ .u.timer.expires_ns = env->xen_singleshot_timer_ns,
+ };
+
+ return kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_SET_ATTR, &va);
+}
+
+static void do_set_vcpu_timer_virq(CPUState *cs, run_on_cpu_data data)
+{
+ kvm_xen_set_vcpu_timer(cs);
+}
+
+int kvm_xen_set_vcpu_virq(uint32_t vcpu_id, uint16_t virq, uint16_t port)
+{
+ CPUState *cs = qemu_get_cpu(vcpu_id);
+
+ if (!cs) {
+ return -ENOENT;
+ }
+
+ /* cpu.h doesn't include the actual Xen header. */
+ qemu_build_assert(NR_VIRQS == XEN_NR_VIRQS);
+
+ if (virq >= NR_VIRQS) {
+ return -EINVAL;
+ }
+
+ if (port && X86_CPU(cs)->env.xen_virq[virq]) {
+ return -EEXIST;
+ }
+
+ X86_CPU(cs)->env.xen_virq[virq] = port;
+ if (virq == VIRQ_TIMER && kvm_xen_has_cap(EVTCHN_SEND)) {
+ async_run_on_cpu(cs, do_set_vcpu_timer_virq,
+ RUN_ON_CPU_HOST_INT(port));
+ }
+ return 0;
+}
+
static void do_set_vcpu_time_info_gpa(CPUState *cs, run_on_cpu_data data)
{
X86CPU *cpu = X86_CPU(cs);
env->xen_vcpu_time_info_gpa = INVALID_GPA;
env->xen_vcpu_runstate_gpa = INVALID_GPA;
env->xen_vcpu_callback_vector = 0;
+ env->xen_singleshot_timer_ns = 0;
+ memset(env->xen_virq, 0, sizeof(env->xen_virq));
set_vcpu_info(cs, INVALID_GPA);
kvm_xen_set_vcpu_attr(cs, KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO,
INVALID_GPA);
if (kvm_xen_has_cap(EVTCHN_SEND)) {
kvm_xen_set_vcpu_callback_vector(cs);
+ kvm_xen_set_vcpu_timer(cs);
}
}
err = xen_evtchn_unmask_op(&unmask);
break;
}
+ case EVTCHNOP_bind_virq: {
+ struct evtchn_bind_virq virq;
+
+ qemu_build_assert(sizeof(virq) == 12);
+ if (kvm_copy_from_gva(cs, arg, &virq, sizeof(virq))) {
+ err = -EFAULT;
+ break;
+ }
+
+ err = xen_evtchn_bind_virq_op(&virq);
+ if (!err && kvm_copy_to_gva(cs, arg, &virq, sizeof(virq))) {
+ err = -EFAULT;
+ }
+ break;
+ }
default:
return false;
}
}
}
+ if (env->xen_virq[VIRQ_TIMER]) {
+ ret = kvm_xen_set_vcpu_timer(cs);
+ if (ret < 0) {
+ return ret;
+ }
+ }
return 0;
}
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
uint64_t gpa;
+ int ret;
/*
* The kernel does not mark vcpu_info as dirty when it delivers interrupts
}
}
+ if (!kvm_xen_has_cap(EVTCHN_SEND)) {
+ return 0;
+ }
+
+ /*
+ * If the kernel is accelerating timers, read out the current value of the
+ * singleshot timer deadline.
+ */
+ if (env->xen_virq[VIRQ_TIMER]) {
+ struct kvm_xen_vcpu_attr va = {
+ .type = KVM_XEN_VCPU_ATTR_TYPE_TIMER,
+ };
+ ret = kvm_vcpu_ioctl(cs, KVM_XEN_VCPU_GET_ATTR, &va);
+ if (ret < 0) {
+ return ret;
+ }
+ env->xen_singleshot_timer_ns = va.u.timer.expires_ns;
+ }
+
return 0;
}