#include <sys/types.h>
#include <sys/hypervisor.h>
#include <sys/machsystm.h>
#include <sys/mutex.h>
#include <sys/evtchn_impl.h>
#include <sys/ddi_impldefs.h>
#include <sys/avintr.h>
#include <sys/cpuvar.h>
#include <sys/smp_impldefs.h>
#include <sys/archsystm.h>
#include <sys/sysmacros.h>
#include <sys/cmn_err.h>
#include <sys/promif.h>
#include <sys/debug.h>
#include <sys/psm.h>
#include <sys/privregs.h>
#include <sys/trap.h>
#include <sys/atomic.h>
#include <sys/cpu.h>
#include <sys/psw.h>
#include <sys/traptrace.h>
#include <sys/stack.h>
#include <sys/x_call.h>
#include <xen/public/physdev.h>
#define IRQ_IS_CPUPOKE(irq) (ipi_info[XC_CPUPOKE_PIL].mi_irq == (irq))
#define EVTCHN_MASKED(ev) \
(HYPERVISOR_shared_info->evtchn_mask[(ev) >> EVTCHN_SHIFT] & \
(1ul << ((ev) & ((1ul << EVTCHN_SHIFT) - 1))))
static short evtchn_to_irq[NR_EVENT_CHANNELS];
static cpuset_t evtchn_cpus[NR_EVENT_CHANNELS];
static int evtchn_owner[NR_EVENT_CHANNELS];
#ifdef DEBUG
static kthread_t *evtchn_owner_thread[NR_EVENT_CHANNELS];
#endif
static irq_info_t irq_info[NR_IRQS];
static mec_info_t ipi_info[MAXIPL];
static mec_info_t virq_info[NR_VIRQS];
kmutex_t ec_lock;
static unsigned long pirq_needs_eoi[NR_PIRQS / (sizeof (unsigned long) * NBBY)];
static int ec_debug_irq = INVALID_IRQ;
int ec_dev_irq = INVALID_IRQ;
int
xen_bind_virq(unsigned int virq, processorid_t cpu, int *port)
{
evtchn_bind_virq_t bind;
int err;
bind.virq = virq;
bind.vcpu = cpu;
if ((err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind)) == 0)
*port = bind.port;
else
err = xen_xlate_errcode(err);
return (err);
}
int
xen_bind_interdomain(int domid, int remote_port, int *port)
{
evtchn_bind_interdomain_t bind;
int err;
bind.remote_dom = domid;
bind.remote_port = remote_port;
if ((err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
&bind)) == 0)
*port = bind.local_port;
else
err = xen_xlate_errcode(err);
return (err);
}
int
xen_alloc_unbound_evtchn(int domid, int *evtchnp)
{
evtchn_alloc_unbound_t alloc;
int err;
alloc.dom = DOMID_SELF;
alloc.remote_dom = domid;
if ((err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
&alloc)) == 0) {
*evtchnp = alloc.port;
(void) ec_mask_evtchn(*evtchnp);
} else {
err = xen_xlate_errcode(err);
}
return (err);
}
static int
xen_close_evtchn(int evtchn)
{
evtchn_close_t close;
int err;
close.port = evtchn;
err = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close);
if (err)
err = xen_xlate_errcode(err);
return (err);
}
static int
xen_bind_ipi(processorid_t cpu)
{
evtchn_bind_ipi_t bind;
ASSERT(MUTEX_HELD(&ec_lock));
bind.vcpu = cpu;
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind) != 0)
panic("xen_bind_ipi() failed");
return (bind.port);
}
static void
xen_bind_vcpu(int evtchn, int cpu)
{
evtchn_bind_vcpu_t bind;
ASSERT(MUTEX_HELD(&ec_lock));
bind.port = evtchn;
bind.vcpu = cpu;
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind) != 0)
panic("xen_bind_vcpu() failed");
}
static int
xen_bind_pirq(int pirq)
{
evtchn_bind_pirq_t bind;
int ret;
bind.pirq = pirq;
bind.flags = BIND_PIRQ__WILL_SHARE;
if ((ret = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind)) != 0)
panic("xen_bind_pirq() failed (err %d)", ret);
return (bind.port);
}
static void
xen_evtchn_unmask(int evtchn)
{
evtchn_unmask_t unmask;
unmask.port = evtchn;
if (HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask) != 0)
panic("xen_evtchn_unmask() failed");
}
static void
update_evtchn_affinity(int evtchn)
{
cpu_t *cp;
struct xen_evt_data *cpe;
ASSERT(evtchn_to_irq[evtchn] != INVALID_IRQ);
ASSERT(MUTEX_HELD(&ec_lock));
kpreempt_disable();
cp = cpu_list;
do {
cpe = cp->cpu_m.mcpu_evt_pend;
if (CPU_IN_SET(evtchn_cpus[evtchn], cp->cpu_id))
SET_EVTCHN_BIT(evtchn, cpe->evt_affinity);
else
CLEAR_EVTCHN_BIT(evtchn, cpe->evt_affinity);
} while ((cp = cp->cpu_next) != cpu_list);
kpreempt_enable();
}
static void
bind_evtchn_to_cpuset(int evtchn, cpuset_t cpus)
{
ASSERT(evtchn_to_irq[evtchn] != INVALID_IRQ);
CPUSET_ZERO(evtchn_cpus[evtchn]);
CPUSET_OR(evtchn_cpus[evtchn], cpus);
update_evtchn_affinity(evtchn);
}
static void
clear_evtchn_affinity(int evtchn)
{
CPUSET_ZERO(evtchn_cpus[evtchn]);
update_evtchn_affinity(evtchn);
}
static void
alloc_irq_evtchn(int irq, int index, int evtchn, int cpu)
{
irq_info_t *irqp = &irq_info[irq];
switch (irqp->ii_type) {
case IRQT_IPI:
ipi_info[index].mi_evtchns[cpu] = evtchn;
irqp->ii_u.index = index;
break;
case IRQT_VIRQ:
virq_info[index].mi_evtchns[cpu] = evtchn;
irqp->ii_u.index = index;
break;
default:
irqp->ii_u.evtchn = evtchn;
break;
}
evtchn_to_irq[evtchn] = irq;
if (cpu != -1) {
cpuset_t tcpus;
CPUSET_ONLY(tcpus, cpu);
bind_evtchn_to_cpuset(evtchn, tcpus);
}
}
static int
alloc_irq(int type, int index, int evtchn, int cpu)
{
int irq;
irq_info_t *irqp;
ASSERT(MUTEX_HELD(&ec_lock));
ASSERT(type != IRQT_IPI || cpu != -1);
for (irq = 0; irq < NR_IRQS; irq++) {
if (irq_info[irq].ii_type == IRQT_UNBOUND)
break;
}
if (irq == NR_IRQS)
panic("No available IRQ to bind to: increase NR_IRQS!\n");
irqp = &irq_info[irq];
irqp->ii_type = type;
irqp->ii_u2.has_handler = 0;
alloc_irq_evtchn(irq, index, evtchn, cpu);
return (irq);
}
static int
irq_evtchn(irq_info_t *irqp)
{
int evtchn;
ASSERT(irqp->ii_type != IRQT_DEV_EVTCHN);
switch (irqp->ii_type) {
case IRQT_IPI:
ASSERT(irqp->ii_u.index != 0);
evtchn = ipi_info[irqp->ii_u.index].mi_evtchns[CPU->cpu_id];
break;
case IRQT_VIRQ:
evtchn = virq_info[irqp->ii_u.index].mi_evtchns[CPU->cpu_id];
break;
default:
evtchn = irqp->ii_u.evtchn;
break;
}
return (evtchn);
}
int
ec_is_edge_pirq(int irq)
{
return (irq_info[irq].ii_type == IRQT_PIRQ &&
!TEST_EVTCHN_BIT(irq, &pirq_needs_eoi[0]));
}
static void
unbind_evtchn(ushort_t *evtchnp)
{
int err;
ASSERT(MUTEX_HELD(&ec_lock));
ASSERT(*evtchnp != 0);
err = xen_close_evtchn(*evtchnp);
ASSERT(err == 0);
clear_evtchn_affinity(*evtchnp);
evtchn_to_irq[*evtchnp] = INVALID_IRQ;
*evtchnp = 0;
}
static void
pirq_unmask_notify(int pirq)
{
struct physdev_eoi eoi;
if (TEST_EVTCHN_BIT(pirq, &pirq_needs_eoi[0])) {
eoi.irq = pirq;
(void) HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi);
}
}
static void
pirq_query_unmask(int pirq)
{
struct physdev_irq_status_query irq_status;
irq_status.irq = pirq;
(void) HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status);
CLEAR_EVTCHN_BIT(pirq, &pirq_needs_eoi[0]);
if (irq_status.flags & XENIRQSTAT_needs_eoi)
SET_EVTCHN_BIT(pirq, &pirq_needs_eoi[0]);
}
static void
end_pirq(int irq)
{
int evtchn = irq_evtchn(&irq_info[irq]);
if (TEST_EVTCHN_BIT(irq, &pirq_needs_eoi[0])) {
ec_unmask_evtchn(evtchn);
pirq_unmask_notify(IRQ_TO_PIRQ(irq));
}
}
void
ec_bind_vcpu(int evtchn, int cpu)
{
mutex_enter(&ec_lock);
xen_bind_vcpu(evtchn, cpu);
mutex_exit(&ec_lock);
}
void
ec_setup_pirq(int irq, int ipl, cpuset_t *cpusp)
{
int evtchn;
irq_info_t *irqp = &irq_info[irq];
if (irqp->ii_u.evtchn == INVALID_EVTCHN) {
evtchn = xen_bind_pirq(irq);
pirq_query_unmask(IRQ_TO_PIRQ(irq));
irqp->ii_type = IRQT_PIRQ;
irqp->ii_u.evtchn = evtchn;
evtchn_to_irq[evtchn] = irq;
irqp->ii_u2.ipl = ipl;
ec_set_irq_affinity(irq, *cpusp);
ec_enable_irq(irq);
pirq_unmask_notify(IRQ_TO_PIRQ(irq));
} else {
ASSERT(irqp->ii_u2.ipl != 0);
cmn_err(CE_NOTE, "!IRQ%d is shared", irq);
if (ipl > irqp->ii_u2.ipl)
irqp->ii_u2.ipl = ipl;
*cpusp = evtchn_cpus[irqp->ii_u.evtchn];
}
}
void
ec_unbind_irq(int irq)
{
irq_info_t *irqp = &irq_info[irq];
mec_info_t *virqp;
int drop_lock = 0;
int type, i;
if (mutex_owner(&ec_lock) != curthread) {
mutex_enter(&ec_lock);
drop_lock = 1;
}
type = irqp->ii_type;
ASSERT((type == IRQT_EVTCHN) || (type == IRQT_PIRQ) ||
(type == IRQT_VIRQ));
if ((type == IRQT_EVTCHN) || (type == IRQT_PIRQ)) {
unbind_evtchn(&irqp->ii_u.evtchn);
} else if (type == IRQT_VIRQ) {
virqp = &virq_info[irqp->ii_u.index];
for (i = 0; i < NCPU; i++) {
if (virqp->mi_evtchns[i] != 0)
unbind_evtchn(&virqp->mi_evtchns[i]);
}
virqp->mi_irq = INVALID_IRQ;
}
bzero(irqp, sizeof (*irqp));
if (type == IRQT_PIRQ)
irqp->ii_type = IRQT_PIRQ;
if (drop_lock)
mutex_exit(&ec_lock);
}
void
ec_set_irq_affinity(int irq, cpuset_t dest)
{
int evtchn, tcpu;
irq_info_t *irqp = &irq_info[irq];
mutex_enter(&ec_lock);
ASSERT(irq < NR_IRQS);
ASSERT(irqp->ii_type != IRQT_UNBOUND);
if (irqp->ii_type == IRQT_IPI || irqp->ii_type == IRQT_VIRQ ||
irqp->ii_type == IRQT_DEV_EVTCHN) {
mutex_exit(&ec_lock);
return;
}
CPUSET_FIND(dest, tcpu);
ASSERT(tcpu != CPUSET_NOTINSET);
evtchn = irq_evtchn(irqp);
xen_bind_vcpu(evtchn, tcpu);
bind_evtchn_to_cpuset(evtchn, dest);
mutex_exit(&ec_lock);
if (ncpus > 1)
poke_cpu(tcpu);
}
int
ec_set_irq_priority(int irq, int pri)
{
irq_info_t *irqp;
if (irq >= NR_IRQS)
return (-1);
irqp = &irq_info[irq];
if (irqp->ii_type == IRQT_UNBOUND)
return (-1);
irqp->ii_u2.ipl = pri;
return (0);
}
void
ec_clear_irq_priority(int irq)
{
irq_info_t *irqp = &irq_info[irq];
ASSERT(irq < NR_IRQS);
ASSERT(irqp->ii_type != IRQT_UNBOUND);
irqp->ii_u2.ipl = 0;
}
int
ec_bind_evtchn_to_irq(int evtchn)
{
mutex_enter(&ec_lock);
ASSERT(evtchn_to_irq[evtchn] == INVALID_IRQ);
(void) alloc_irq(IRQT_EVTCHN, 0, evtchn, -1);
mutex_exit(&ec_lock);
return (evtchn_to_irq[evtchn]);
}
int
ec_bind_virq_to_irq(int virq, int cpu)
{
int err;
int evtchn;
mec_info_t *virqp;
virqp = &virq_info[virq];
mutex_enter(&ec_lock);
err = xen_bind_virq(virq, cpu, &evtchn);
ASSERT(err == 0);
ASSERT(evtchn_to_irq[evtchn] == INVALID_IRQ);
if (virqp->mi_irq == INVALID_IRQ) {
virqp->mi_irq = alloc_irq(IRQT_VIRQ, virq, evtchn, cpu);
} else {
alloc_irq_evtchn(virqp->mi_irq, virq, evtchn, cpu);
}
mutex_exit(&ec_lock);
return (virqp->mi_irq);
}
int
ec_bind_ipi_to_irq(int ipl, int cpu)
{
int evtchn;
ulong_t flags;
mec_info_t *ipip;
mutex_enter(&ec_lock);
ipip = &ipi_info[ipl];
evtchn = xen_bind_ipi(cpu);
ASSERT(evtchn_to_irq[evtchn] == INVALID_IRQ);
if (ipip->mi_irq == INVALID_IRQ) {
ipip->mi_irq = alloc_irq(IRQT_IPI, ipl, evtchn, cpu);
} else {
alloc_irq_evtchn(ipip->mi_irq, ipl, evtchn, cpu);
}
flags = intr_clear();
ec_unmask_evtchn(evtchn);
intr_restore(flags);
mutex_exit(&ec_lock);
return (ipip->mi_irq);
}
void
ec_bind_cpu_ipis(int cpu)
{
int i;
for (i = 0; i < MAXIPL; i++) {
mec_info_t *ipip = &ipi_info[i];
if (ipip->mi_irq == INVALID_IRQ)
continue;
(void) ec_bind_ipi_to_irq(i, cpu);
}
}
int
ec_irq_rebindable(int irq)
{
irq_info_t *irqp = &irq_info[irq];
if (irqp->ii_u.evtchn == 0)
return (0);
return (irqp->ii_type == IRQT_EVTCHN || irqp->ii_type == IRQT_PIRQ);
}
int
ec_irq_needs_rebind(int irq, int cpu)
{
irq_info_t *irqp = &irq_info[irq];
return (ec_irq_rebindable(irq) &&
CPU_IN_SET(evtchn_cpus[irqp->ii_u.evtchn], cpu));
}
void
ec_send_ipi(int ipl, int cpu)
{
mec_info_t *ipip = &ipi_info[ipl];
ASSERT(ipip->mi_irq != INVALID_IRQ);
ec_notify_via_evtchn(ipip->mi_evtchns[cpu]);
}
void
ec_try_ipi(int ipl, int cpu)
{
mec_info_t *ipip = &ipi_info[ipl];
if (ipip->mi_irq == INVALID_IRQ || ipip->mi_irq == 0)
return;
ec_notify_via_evtchn(ipip->mi_evtchns[cpu]);
}
void
ec_irq_add_evtchn(int irq, int evtchn)
{
mutex_enter(&ec_lock);
ASSERT(irq == ec_dev_irq);
alloc_irq_evtchn(irq, 0, evtchn, 0);
irq_info[irq].ii_u.evtchn = 0;
mutex_exit(&ec_lock);
}
void
ec_irq_rm_evtchn(int irq, int evtchn)
{
ushort_t ec = evtchn;
mutex_enter(&ec_lock);
ASSERT(irq == ec_dev_irq);
unbind_evtchn(&ec);
mutex_exit(&ec_lock);
}
int
ec_dev_alloc_irq(void)
{
int i;
irq_info_t *irqp;
for (i = 0; i < NR_IRQS; i++) {
if (irq_info[i].ii_type == IRQT_UNBOUND)
break;
}
ASSERT(i != NR_IRQS);
irqp = &irq_info[i];
irqp->ii_type = IRQT_DEV_EVTCHN;
irqp->ii_u2.ipl = IPL_EVTCHN;
irqp->ii_u.evtchn = 0;
return (i);
}
void
ec_enable_irq(unsigned int irq)
{
ulong_t flag;
irq_info_t *irqp = &irq_info[irq];
if (irqp->ii_type == IRQT_DEV_EVTCHN)
return;
flag = intr_clear();
ec_unmask_evtchn(irq_evtchn(irqp));
intr_restore(flag);
}
void
ec_disable_irq(unsigned int irq)
{
irq_info_t *irqp = &irq_info[irq];
if (irqp->ii_type == IRQT_DEV_EVTCHN)
return;
while (!ec_mask_evtchn(irq_evtchn(irqp)))
SMT_PAUSE();
}
static int
ec_evtchn_pending(uint_t ev)
{
uint_t evi;
shared_info_t *si = HYPERVISOR_shared_info;
evi = ev >> EVTCHN_SHIFT;
ev &= (1ul << EVTCHN_SHIFT) - 1;
return ((si->evtchn_pending[evi] & (1ul << ev)) != 0);
}
int
ec_pending_irq(unsigned int irq)
{
int evtchn = irq_evtchn(&irq_info[irq]);
return (ec_evtchn_pending(evtchn));
}
void
ec_clear_irq(int irq)
{
irq_info_t *irqp = &irq_info[irq];
int evtchn;
if (irqp->ii_type == IRQT_DEV_EVTCHN)
return;
ASSERT(irqp->ii_type != IRQT_UNBOUND);
evtchn = irq_evtchn(irqp);
ASSERT(EVTCHN_MASKED(evtchn));
ec_clear_evtchn(evtchn);
}
void
ec_unmask_irq(int irq)
{
ulong_t flags;
irq_info_t *irqp = &irq_info[irq];
flags = intr_clear();
switch (irqp->ii_type) {
case IRQT_PIRQ:
end_pirq(irq);
break;
case IRQT_DEV_EVTCHN:
break;
default:
ec_unmask_evtchn(irq_evtchn(irqp));
break;
}
intr_restore(flags);
}
void
ec_try_unmask_irq(int irq)
{
ulong_t flags;
irq_info_t *irqp = &irq_info[irq];
int evtchn;
flags = intr_clear();
switch (irqp->ii_type) {
case IRQT_PIRQ:
end_pirq(irq);
break;
case IRQT_DEV_EVTCHN:
break;
default:
if ((evtchn = irq_evtchn(irqp)) != 0)
ec_unmask_evtchn(evtchn);
break;
}
intr_restore(flags);
}
void
ec_wait_on_evtchn(int evtchn, int (*check_func)(void *), void *arg)
{
if (DOMAIN_IS_INITDOMAIN(xen_info)) {
while (!check_func(arg))
(void) HYPERVISOR_yield();
return;
}
ASSERT(CPU->cpu_m.mcpu_vcpu_info->evtchn_upcall_mask != 0);
for (;;) {
evtchn_port_t ports[1];
ports[0] = evtchn;
ec_clear_evtchn(evtchn);
if (check_func(arg))
return;
(void) HYPERVISOR_poll(ports, 1, 0);
}
}
void
ec_wait_on_ipi(int ipl, int (*check_func)(void *), void *arg)
{
mec_info_t *ipip = &ipi_info[ipl];
if (ipip->mi_irq == INVALID_IRQ || ipip->mi_irq == 0)
return;
ec_wait_on_evtchn(ipip->mi_evtchns[CPU->cpu_id], check_func, arg);
}
void
ec_suspend(void)
{
irq_info_t *irqp;
ushort_t *evtchnp;
int i;
int c;
ASSERT(MUTEX_HELD(&ec_lock));
for (i = 0; i < MAXIPL; i++) {
if (ipi_info[i].mi_irq == INVALID_IRQ)
continue;
for (c = 0; c < NCPU; c++) {
if (cpu[c] == NULL)
continue;
if (CPU_IN_SET(cpu_suspend_lost_set, c))
continue;
evtchnp = &ipi_info[i].mi_evtchns[c];
ASSERT(*evtchnp != 0);
unbind_evtchn(evtchnp);
}
}
for (i = 0; i < NR_VIRQS; i++) {
if (virq_info[i].mi_irq == INVALID_IRQ)
continue;
if (virq_info[i].mi_shared) {
evtchnp = &virq_info[i].mi_evtchns[0];
unbind_evtchn(evtchnp);
for (c = 1; c < NCPU; c++)
virq_info[i].mi_evtchns[c] = 0;
} else {
for (c = 0; c < NCPU; c++) {
if (cpu[c] == NULL)
continue;
evtchnp = &virq_info[i].mi_evtchns[c];
if (*evtchnp != 0)
unbind_evtchn(evtchnp);
}
}
}
for (i = 0; i < NR_IRQS; i++) {
irqp = &irq_info[i];
switch (irqp->ii_type) {
case IRQT_EVTCHN:
case IRQT_DEV_EVTCHN:
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
break;
case IRQT_PIRQ:
if (irqp->ii_u.evtchn != 0)
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
break;
default:
break;
}
}
}
static void
share_virq(mec_info_t *virqp)
{
int evtchn = virqp->mi_evtchns[0];
cpuset_t tset;
int i;
ASSERT(evtchn != 0);
virqp->mi_shared = 1;
for (i = 1; i < NCPU; i++)
virqp->mi_evtchns[i] = evtchn;
CPUSET_ALL(tset);
bind_evtchn_to_cpuset(evtchn, tset);
}
static void
virq_resume(int virq)
{
mec_info_t *virqp = &virq_info[virq];
int evtchn;
int i, err;
for (i = 0; i < NCPU; i++) {
cpuset_t tcpus;
if (cpu[i] == NULL || CPU_IN_SET(cpu_suspend_lost_set, i))
continue;
err = xen_bind_virq(virq, i, &evtchn);
ASSERT(err == 0);
virqp->mi_evtchns[i] = evtchn;
evtchn_to_irq[evtchn] = virqp->mi_irq;
CPUSET_ONLY(tcpus, i);
bind_evtchn_to_cpuset(evtchn, tcpus);
ec_unmask_evtchn(evtchn);
if (virq != VIRQ_TIMER)
break;
}
if (virqp->mi_shared)
share_virq(virqp);
}
static void
ipi_resume(int ipl)
{
mec_info_t *ipip = &ipi_info[ipl];
int i;
for (i = 0; i < NCPU; i++) {
cpuset_t tcpus;
int evtchn;
if (cpu[i] == NULL || CPU_IN_SET(cpu_suspend_lost_set, i))
continue;
evtchn = xen_bind_ipi(i);
ipip->mi_evtchns[i] = evtchn;
evtchn_to_irq[evtchn] = ipip->mi_irq;
CPUSET_ONLY(tcpus, i);
bind_evtchn_to_cpuset(evtchn, tcpus);
ec_unmask_evtchn(evtchn);
}
}
void
ec_resume(void)
{
int i;
for (i = 0; i < NR_EVENT_CHANNELS; i++)
(void) ec_mask_evtchn(i);
for (i = 0; i < MAXIPL; i++) {
if (ipi_info[i].mi_irq == INVALID_IRQ)
continue;
ipi_resume(i);
}
for (i = 0; i < NR_VIRQS; i++) {
if (virq_info[i].mi_irq == INVALID_IRQ)
continue;
virq_resume(i);
}
}
int
ec_init(void)
{
int i;
mutex_init(&ec_lock, NULL, MUTEX_SPIN, (void *)ipltospl(SPL7));
for (i = 0; i < NR_EVENT_CHANNELS; i++) {
CPUSET_ZERO(evtchn_cpus[i]);
evtchn_to_irq[i] = INVALID_IRQ;
(void) ec_mask_evtchn(i);
}
for (i = 0; i < MAXIPL; i++)
ipi_info[i].mi_irq = INVALID_IRQ;
for (i = 0; i < NR_VIRQS; i++)
virq_info[i].mi_irq = INVALID_IRQ;
for (i = PIRQ_BASE; i < NR_PIRQS; i++) {
irq_info[PIRQ_TO_IRQ(i)].ii_type = IRQT_PIRQ;
}
return (0);
}
void
ec_init_debug_irq()
{
int irq;
irq = ec_bind_virq_to_irq(VIRQ_DEBUG, 0);
(void) add_avintr(NULL, IPL_DEBUG, xen_debug_handler,
"debug", irq, NULL, NULL, NULL, NULL);
mutex_enter(&ec_lock);
share_virq(&virq_info[irq_info[irq].ii_u.index]);
mutex_exit(&ec_lock);
ec_debug_irq = irq;
}
#define UNBLOCKED_EVENTS(si, ix, cpe, cpu_id) \
((si)->evtchn_pending[ix] & ~(si)->evtchn_mask[ix] & \
(cpe)->evt_affinity[ix])
void
xen_callback_handler(struct regs *rp, trap_trace_rec_t *ttp)
{
ulong_t pending_sels, pe, selbit;
int i, j, port, pri, curpri, irq, sipri;
uint16_t pending_ints, sip;
struct cpu *cpu = CPU;
volatile shared_info_t *si = HYPERVISOR_shared_info;
volatile vcpu_info_t *vci = cpu->cpu_m.mcpu_vcpu_info;
volatile struct xen_evt_data *cpe = cpu->cpu_m.mcpu_evt_pend;
volatile uint16_t *cpu_ipp = &cpu->cpu_m.mcpu_intr_pending;
extern void dosoftint(struct regs *);
ASSERT(rp->r_trapno == T_AST && rp->r_err == 0);
ASSERT(&si->vcpu_info[cpu->cpu_id] == vci);
ASSERT_STACK_ALIGNED();
vci->evtchn_upcall_pending = 0;
membar_enter();
do {
pending_sels = vci->evtchn_pending_sel;
} while (atomic_cas_ulong((volatile ulong_t *)&vci->evtchn_pending_sel,
pending_sels, 0) != pending_sels);
pending_ints = *cpu_ipp;
while ((i = ffs(pending_sels)) != 0) {
i--;
selbit = 1ul << i;
pending_sels &= ~selbit;
membar_enter();
while ((pe = UNBLOCKED_EVENTS(si, i, cpe, cpu->cpu_id)) != 0) {
j = ffs(pe) - 1;
pe &= ~(1ul << j);
port = (i << EVTCHN_SHIFT) + j;
irq = evtchn_to_irq[port];
if (irq == INVALID_IRQ) {
ec_clear_evtchn(port);
continue;
}
if (!irq_info[irq].ii_u2.has_handler) {
#ifdef TRAPTRACE
ttp->ttr_ipl = 0xff;
if (IRQ_IS_CPUPOKE(irq)) {
ttp->ttr_ipl = XC_CPUPOKE_PIL;
ttp->ttr_marker = TT_INTERRUPT;
}
ttp->ttr_pri = cpu->cpu_pri;
ttp->ttr_spl = cpu->cpu_base_spl;
ttp->ttr_vector = 0xff;
#endif
if (ec_mask_evtchn(port)) {
ec_clear_evtchn(port);
ec_unmask_evtchn(port);
continue;
}
}
pri = irq_info[irq].ii_u2.ipl;
if (ec_mask_evtchn(port)) {
if (ec_evtchn_pending(port)) {
cpe->pending_sel[pri] |= selbit;
cpe->pending_evts[pri][i] |= (1ul << j);
pending_ints |= 1 << pri;
if (ec_is_edge_pirq(irq)) {
ec_clear_evtchn(port);
ec_unmask_evtchn(port);
}
} else {
ec_unmask_evtchn(port);
}
}
}
}
*cpu_ipp = pending_ints;
if (pending_ints == 0)
return;
restart:
curpri = cpu->cpu_pri;
pri = bsrw_insn(*cpu_ipp);
while (pri > curpri) {
while ((pending_sels = cpe->pending_sel[pri]) != 0) {
i = ffs(pending_sels) - 1;
while ((pe = cpe->pending_evts[pri][i]) != 0) {
j = ffs(pe) - 1;
port = (i << EVTCHN_SHIFT) + j;
pe &= ~(1ul << j);
cpe->pending_evts[pri][i] = pe;
if (pe == 0) {
pending_sels = cpe->pending_sel[pri];
pending_sels &= ~(1ul << i);
cpe->pending_sel[pri] = pending_sels;
if (pending_sels == 0)
*cpu_ipp &= ~(1 << pri);
}
irq = evtchn_to_irq[port];
if (irq == INVALID_IRQ) {
ec_clear_evtchn(port);
ec_unmask_evtchn(port);
continue;
}
if (irq == ec_dev_irq) {
ASSERT(cpu->cpu_m.mcpu_ec_mbox == 0);
cpu->cpu_m.mcpu_ec_mbox = port;
}
rp->r_trapno = irq;
do_interrupt(rp, ttp);
if (cpu->cpu_pri != curpri)
goto restart;
}
}
pri = bsrw_insn(*cpu_ipp);
sip = (uint16_t)cpu->cpu_softinfo.st_pending;
if (sip != 0) {
sipri = bsrw_insn(sip);
if (sipri > pri && sipri > cpu->cpu_pri) {
dosoftint(rp);
if (cpu->cpu_pri != curpri)
goto restart;
}
}
}
if (cpu->cpu_softinfo.st_pending)
dosoftint(rp);
}
void
ec_unmask_evtchn(unsigned int ev)
{
uint_t evi, evb;
volatile shared_info_t *si = HYPERVISOR_shared_info;
volatile vcpu_info_t *vci = CPU->cpu_m.mcpu_vcpu_info;
volatile ulong_t *ulp;
ASSERT(!interrupts_enabled());
if (!CPU_IN_SET(evtchn_cpus[ev], CPU->cpu_id)) {
xen_evtchn_unmask(ev);
return;
}
evi = ev >> EVTCHN_SHIFT;
evb = ev & ((1ul << EVTCHN_SHIFT) - 1);
ulp = (volatile ulong_t *)&si->evtchn_mask[evi];
atomic_and_ulong(ulp, ~(1ul << evb));
membar_enter();
if (si->evtchn_pending[evi] & (1ul << evb)) {
membar_consumer();
ulp = (volatile ulong_t *)&vci->evtchn_pending_sel;
if (!(*ulp & (1ul << evi))) {
atomic_or_ulong(ulp, (1ul << evi));
}
vci->evtchn_upcall_pending = 1;
}
}
int
ec_mask_evtchn(unsigned int ev)
{
uint_t evi, evb;
ulong_t new, old, bit;
volatile shared_info_t *si = HYPERVISOR_shared_info;
volatile ulong_t *maskp;
int masked;
kpreempt_disable();
evi = ev >> EVTCHN_SHIFT;
evb = ev & ((1ul << EVTCHN_SHIFT) - 1);
bit = 1ul << evb;
maskp = (volatile ulong_t *)&si->evtchn_mask[evi];
do {
old = si->evtchn_mask[evi];
new = old | bit;
} while (atomic_cas_ulong(maskp, old, new) != old);
masked = (old & bit) == 0;
if (masked) {
evtchn_owner[ev] = CPU->cpu_id;
#ifdef DEBUG
evtchn_owner_thread[ev] = curthread;
#endif
}
kpreempt_enable();
return (masked);
}
void
ec_clear_evtchn(unsigned int ev)
{
uint_t evi;
shared_info_t *si = HYPERVISOR_shared_info;
volatile ulong_t *pendp;
evi = ev >> EVTCHN_SHIFT;
ev &= (1ul << EVTCHN_SHIFT) - 1;
pendp = (volatile ulong_t *)&si->evtchn_pending[evi];
atomic_and_ulong(pendp, ~(1ul << ev));
}
void
ec_notify_via_evtchn(unsigned int port)
{
evtchn_send_t send;
ASSERT(port != INVALID_EVTCHN);
send.port = port;
(void) HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
}
int
ec_block_irq(int irq)
{
irq_info_t *irqp = &irq_info[irq];
int evtchn;
evtchn = irq_evtchn(irqp);
(void) ec_mask_evtchn(evtchn);
return (evtchn_owner[evtchn]);
}
void
ec_unpend_irq(int irq)
{
irq_info_t *irqp = &irq_info[irq];
int pri = irqp->ii_u2.ipl;
ulong_t flags;
uint_t evtchn, evi, bit;
unsigned long pe, pending_sels;
struct xen_evt_data *cpe;
evtchn = irq_evtchn(irqp);
ASSERT(EVTCHN_MASKED(evtchn));
evi = evtchn >> EVTCHN_SHIFT;
bit = evtchn & (1ul << EVTCHN_SHIFT) - 1;
flags = intr_clear();
cpe = CPU->cpu_m.mcpu_evt_pend;
pe = cpe->pending_evts[pri][evi] & ~(1ul << bit);
cpe->pending_evts[pri][evi] = pe;
if (pe == 0) {
pending_sels = cpe->pending_sel[pri];
pending_sels &= ~(1ul << evi);
cpe->pending_sel[pri] = pending_sels;
if (pending_sels == 0)
CPU->cpu_m.mcpu_intr_pending &= ~(1 << pri);
}
intr_restore(flags);
}