int set_global_virq_handler(struct domain *d, uint32_t virq) { struct domain *old; if (virq >= NR_VIRQS) return -EINVAL; if (!virq_is_global(virq)) return -EINVAL; if (global_virq_handlers[virq] == d) return 0; if (unlikely(!get_domain(d))) return -EINVAL; spin_lock(&global_virq_handlers_lock); old = global_virq_handlers[virq]; global_virq_handlers[virq] = d; spin_unlock(&global_virq_handlers_lock); if (old != NULL) put_domain(old); return 0; }
void send_global_virq(uint32_t virq) { ASSERT(virq < NR_VIRQS); ASSERT(virq_is_global(virq)); send_guest_global_virq(global_virq_handlers[virq] ?: dom0, virq); }
static void send_guest_global_virq(struct domain *d, uint32_t virq) { unsigned long flags; int port; struct vcpu *v; struct evtchn *chn; ASSERT(virq_is_global(virq)); if ( unlikely(d == NULL) || unlikely(d->vcpu == NULL) ) return; v = d->vcpu[0]; if ( unlikely(v == NULL) ) return; spin_lock_irqsave(&v->virq_lock, flags); port = v->virq_to_evtchn[virq]; if ( unlikely(port == 0) ) goto out; chn = evtchn_from_port(d, port); evtchn_set_pending(d->vcpu[chn->notify_vcpu_id], port); out: spin_unlock_irqrestore(&v->virq_lock, flags); }
long evtchn_bind_vcpu(unsigned int port, unsigned int vcpu_id) { struct domain *d = current->domain; struct evtchn *chn; long rc = 0; if ( (vcpu_id >= d->max_vcpus) || (d->vcpu[vcpu_id] == NULL) ) return -ENOENT; spin_lock(&d->event_lock); if ( !port_is_valid(d, port) ) { rc = -EINVAL; goto out; } chn = evtchn_from_port(d, port); /* Guest cannot re-bind a Xen-attached event channel. */ if ( unlikely(consumer_is_xen(chn)) ) { rc = -EINVAL; goto out; } switch ( chn->state ) { case ECS_VIRQ: if ( virq_is_global(chn->u.virq) ) chn->notify_vcpu_id = vcpu_id; else rc = -EINVAL; break; case ECS_UNBOUND: case ECS_INTERDOMAIN: chn->notify_vcpu_id = vcpu_id; break; case ECS_PIRQ: if ( chn->notify_vcpu_id == vcpu_id ) break; unlink_pirq_port(chn, d->vcpu[chn->notify_vcpu_id]); chn->notify_vcpu_id = vcpu_id; pirq_set_affinity(d, chn->u.pirq.irq, cpumask_of(d->vcpu[vcpu_id]->processor)); link_pirq_port(port, chn, d->vcpu[vcpu_id]); break; default: rc = -EINVAL; break; } out: spin_unlock(&d->event_lock); return rc; }
void send_guest_vcpu_virq(struct vcpu *v, uint32_t virq) { unsigned long flags; int port; ASSERT(!virq_is_global(virq)); spin_lock_irqsave(&v->virq_lock, flags); port = v->virq_to_evtchn[virq]; if ( unlikely(port == 0) ) goto out; evtchn_set_pending(v, port); out: spin_unlock_irqrestore(&v->virq_lock, flags); }
static long evtchn_bind_virq(evtchn_bind_virq_t *bind) { struct evtchn *chn; struct vcpu *v; struct domain *d = current->domain; int port, virq = bind->virq, vcpu = bind->vcpu; long rc = 0; if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) return -EINVAL; if ( virq_is_global(virq) && (vcpu != 0) ) return -EINVAL; if ( (vcpu < 0) || (vcpu >= ARRAY_SIZE(d->vcpu)) || ((v = d->vcpu[vcpu]) == NULL) ) return -ENOENT; spin_lock(&d->evtchn_lock); if ( v->virq_to_evtchn[virq] != 0 ) ERROR_EXIT(-EEXIST); if ( (port = get_free_port(d)) < 0 ) ERROR_EXIT(port); chn = evtchn_from_port(d, port); chn->state = ECS_VIRQ; chn->notify_vcpu_id = vcpu; chn->u.virq = virq; v->virq_to_evtchn[virq] = bind->port = port; out: spin_unlock(&d->evtchn_lock); return rc; }