/******************************************************************************
 * evtchn.c
 * 
 * Communication via Xen event channels.
 * 
 * Copyright (c) 2002-2005, K A Fraser
 * Copyright (c) 2005-2006 Kip Macy
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>

#include <machine/cpufunc.h>
#include <machine/intr_machdep.h>
#include <machine/xen-os.h>
#include <machine/xen_intr.h>
#include <machine/synch_bitops.h>
#include <machine/evtchn.h>
#include <machine/hypervisor.h>
#include <machine/hypervisor-ifs.h>



/* linux helper functions that got sucked in 
 * rename and move XXX
 */


static inline int find_first_bit(const unsigned long *addr, unsigned size)
{
	int d0, d1;
	int res;

	/* This looks at memory. Mark it volatile to tell gcc not to move it around */
	__asm__ __volatile__(
		"xorl %%eax,%%eax\n\t"
		"repe; scasl\n\t"
		"jz 1f\n\t"
		"leal -4(%%edi),%%edi\n\t"
		"bsfl (%%edi),%%eax\n"
		"1:\tsubl %%ebx,%%edi\n\t"
		"shll $3,%%edi\n\t"
		"addl %%edi,%%eax"
		:"=a" (res), "=&c" (d0), "=&D" (d1)
		:"1" ((size + 31) >> 5), "2" (addr), "b" (addr) : "memory");
	return res;
}

#define min_t(type,x,y) \
	({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
#define first_cpu(src) __first_cpu(&(src), NR_CPUS)
static inline int __first_cpu(const xen_cpumask_t *srcp, int nbits)
{
	return min_t(int, nbits, find_first_bit(srcp->bits, nbits));
}

static inline unsigned long __ffs(unsigned long word)
{
        __asm__("bsfl %1,%0"
                :"=r" (word)
                :"rm" (word));
        return word;
}

static struct mtx irq_mapping_update_lock;
static struct xenpic *xp;
struct xenpic_intsrc {
	struct intsrc     xp_intsrc;
	uint8_t           xp_vector;
	boolean_t	  xp_masked;
};

struct xenpic { 
	struct pic           *xp_dynirq_pic; 
	struct pic           *xp_pirq_pic;   
	uint16_t             xp_numintr; 
	struct xenpic_intsrc xp_pins[0]; 
}; 





#define TODO            printf("%s: not implemented!\n", __func__) 

/* IRQ <-> event-channel mappings. */
static int evtchn_to_irq[NR_EVENT_CHANNELS];

/* Packed IRQ information: binding type, sub-type index, and event channel. */
static uint32_t irq_info[NR_IRQS];
/* Binding types. */
enum { IRQT_UNBOUND, IRQT_PIRQ, IRQT_VIRQ, IRQT_IPI, IRQT_EVTCHN };
/* Constructor for packed IRQ information. */
#define mk_irq_info(type, index, evtchn)				\
	(((uint32_t)(type) << 24) | ((uint32_t)(index) << 16) | (uint32_t)(evtchn))
/* Convenient shorthand for packed representation of an unbound IRQ. */
#define IRQ_UNBOUND	mk_irq_info(IRQT_UNBOUND, 0, 0)
/* Accessor macros for packed IRQ information. */
#define evtchn_from_irq(irq) ((uint16_t)(irq_info[irq]))
#define index_from_irq(irq)  ((uint8_t)(irq_info[irq] >> 16))
#define type_from_irq(irq)   ((uint8_t)(irq_info[irq] >> 24))

/* IRQ <-> VIRQ mapping. */
DEFINE_PER_CPU(int, virq_to_irq[NR_VIRQS]);

/* IRQ <-> IPI mapping. */
#ifndef NR_IPIS
#define NR_IPIS 1 
#endif
DEFINE_PER_CPU(int, ipi_to_irq[NR_IPIS]);

/* Bitmap indicating which PIRQs require Xen to be notified on unmask. */
static unsigned long pirq_needs_unmask_notify[NR_PIRQS/sizeof(unsigned long)];

/* Reference counts for bindings to IRQs. */
static int irq_bindcount[NR_IRQS];

#define VALID_EVTCHN(_chn) ((_chn) != 0)

#ifdef CONFIG_SMP

static u8 cpu_evtchn[NR_EVENT_CHANNELS];
static unsigned long cpu_evtchn_mask[NR_CPUS][NR_EVENT_CHANNELS/BITS_PER_LONG];

#define active_evtchns(cpu,sh,idx)		\
	((sh)->evtchn_pending[idx] &		\
	 cpu_evtchn_mask[cpu][idx] &		\
	 ~(sh)->evtchn_mask[idx])

static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu)
{
	clear_bit(chn, (unsigned long *)cpu_evtchn_mask[cpu_evtchn[chn]]);
	set_bit(chn, (unsigned long *)cpu_evtchn_mask[cpu]);
	cpu_evtchn[chn] = cpu;
}

static void init_evtchn_cpu_bindings(void)
{
	/* By default all event channels notify CPU#0. */
	memset(cpu_evtchn, 0, sizeof(cpu_evtchn));
	memset(cpu_evtchn_mask[0], ~0, sizeof(cpu_evtchn_mask[0]));
}

#define cpu_from_evtchn(evtchn)		(cpu_evtchn[evtchn])

#else

#define active_evtchns(cpu,sh,idx)		\
	((sh)->evtchn_pending[idx] &		\
	 ~(sh)->evtchn_mask[idx])
#define bind_evtchn_to_cpu(chn,cpu)	((void)0)
#define init_evtchn_cpu_bindings()	((void)0)
#define cpu_from_evtchn(evtchn)		(0)

#endif


/*
 * Force a proper event-channel callback from Xen after clearing the
 * callback mask. We do this in a very simple manner, by making a call
 * down into Xen. The pending flag will be checked by Xen on return.
 */
void force_evtchn_callback(void)
{
	(void)HYPERVISOR_xen_version(0, NULL);
}

void 
evtchn_do_upcall(struct trapframe *frame) 
{
	unsigned long  l1, l2;
	unsigned int   l1i, l2i, port;
	int            irq, cpu;
	shared_info_t *s;
	vcpu_info_t   *vcpu_info;
	
	cpu = smp_processor_id();
	s = HYPERVISOR_shared_info;
	vcpu_info = &s->vcpu_info[cpu];

	vcpu_info->evtchn_upcall_pending = 0;

	/* NB. No need for a barrier here -- XCHG is a barrier on x86. */
	l1 = xen_xchg(&vcpu_info->evtchn_pending_sel, 0);

	while (l1 != 0) {
		l1i = __ffs(l1);
		l1 &= ~(1 << l1i);
		
		while ((l2 = active_evtchns(cpu, s, l1i)) != 0) {
			l2i = __ffs(l2);

			port = (l1i * BITS_PER_LONG) + l2i;
			if ((irq = evtchn_to_irq[port]) != -1) {
				struct intsrc *isrc = intr_lookup_source(irq);
				/* 
				 * ack 
				 */
				mask_evtchn(port);
				clear_evtchn(port); 

				intr_execute_handlers(isrc, frame);
			} else {
				evtchn_device_upcall(port);
			}
		}
	}
}

static int 
find_unbound_irq(void)
{
	int irq;

	for (irq = 0; irq < NR_IRQS; irq++)
		if (irq_bindcount[irq] == 0)
			break;

	if (irq == NR_IRQS)
		panic("No available IRQ to bind to: increase NR_IRQS!\n");

	return irq;
}

static int 
bind_evtchn_to_irq(unsigned int evtchn)
{
	int irq;

	mtx_lock_spin(&irq_mapping_update_lock);
	
	if ((irq = evtchn_to_irq[evtchn]) == -1) {
		irq = find_unbound_irq();
		evtchn_to_irq[evtchn] = irq;
		irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn);
	}

	irq_bindcount[irq]++;
	intr_register_source(&xp->xp_pins[irq].xp_intsrc);

	mtx_unlock_spin(&irq_mapping_update_lock);
    
	return irq;
}

static int 
bind_virq_to_irq(unsigned int virq, unsigned int cpu)
{
	evtchn_op_t op;
	int evtchn, irq;

	mtx_lock_spin(&irq_mapping_update_lock);

	if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) {
		op.cmd              = EVTCHNOP_bind_virq;
		op.u.bind_virq.virq = virq;
		op.u.bind_virq.vcpu = cpu;
		PANIC_IF(HYPERVISOR_event_channel_op(&op) != 0);

		evtchn = op.u.bind_virq.port;

		irq = find_unbound_irq();
		evtchn_to_irq[evtchn] = irq;
		irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn);

		per_cpu(virq_to_irq, cpu)[virq] = irq;

		bind_evtchn_to_cpu(evtchn, cpu);
	}

	irq_bindcount[irq]++;

	mtx_unlock_spin(&irq_mapping_update_lock);

	return irq;
}

static int 
bind_ipi_to_irq(unsigned int ipi, unsigned int cpu)
{
	evtchn_op_t op;
	int evtchn, irq;
	
	mtx_lock_spin(&irq_mapping_update_lock);
	
	if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) {
		op.u.bind_ipi.vcpu = cpu;
		op.cmd = EVTCHNOP_bind_ipi;
		PANIC_IF(HYPERVISOR_event_channel_op(&op) != 0);
		evtchn = op.u.bind_ipi.port;

		irq = find_unbound_irq();
		evtchn_to_irq[evtchn] = irq;
		irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn);

		per_cpu(ipi_to_irq, cpu)[ipi] = irq;

		bind_evtchn_to_cpu(evtchn, cpu);
	}

	irq_bindcount[irq]++;

	mtx_unlock_spin(&irq_mapping_update_lock);

	return irq;
}


void 
unbind_from_irq(int irq)
{
	evtchn_op_t op = { .cmd = EVTCHNOP_close };
	int evtchn = evtchn_from_irq(irq);

	mtx_lock_spin(&irq_mapping_update_lock);

	if ((--irq_bindcount[irq] == 0) && VALID_EVTCHN(evtchn)) {
		op.u.close.port = evtchn;
		PANIC_IF(HYPERVISOR_event_channel_op(&op) != 0);

		switch (type_from_irq(irq)) {
		case IRQT_VIRQ:
			per_cpu(virq_to_irq, cpu_from_evtchn(evtchn))[index_from_irq(irq)] = -1;
			break;
		case IRQT_IPI:
			per_cpu(ipi_to_irq, cpu_from_evtchn(evtchn))[index_from_irq(irq)] = -1;
			break;
		default:
			break;
		}

		/* Closed ports are implicitly re-bound to VCPU0. */
		bind_evtchn_to_cpu(evtchn, 0);

		evtchn_to_irq[evtchn] = -1;
		irq_info[irq] = IRQ_UNBOUND;
	}

	mtx_unlock_spin(&irq_mapping_update_lock);
}

int 
bind_evtchn_to_irqhandler(unsigned int evtchn,
			  const char *devname,
			  driver_intr_t handler,
			  void *arg,
			  unsigned long irqflags)
{
	unsigned int irq;
	int retval;

	irq = bind_evtchn_to_irq(evtchn);
	intr_register_source(&xp->xp_pins[irq].xp_intsrc);
	retval = intr_add_handler(devname, irq, handler, arg, irqflags, NULL);
	if (retval != 0) {
		unbind_from_irq(irq);
		return retval;
	}

	return irq;
}

int 
bind_virq_to_irqhandler(unsigned int virq,
			unsigned int cpu,
			const char *devname,
			driver_intr_t handler,
			unsigned long irqflags)
{
	unsigned int irq;
	int retval;

	irq = bind_virq_to_irq(virq, cpu);
	intr_register_source(&xp->xp_pins[irq].xp_intsrc);
	retval = intr_add_handler(devname, irq, handler, NULL, irqflags, NULL);
	if (retval != 0) {
		unbind_from_irq(irq);
		return retval;
	}

	return irq;
}

int 
bind_ipi_to_irqhandler(unsigned int ipi,
		       unsigned int cpu,
		       const char *devname,
		       driver_intr_t handler,
		       unsigned long irqflags)
{
	unsigned int irq;
	int retval;

	irq = bind_ipi_to_irq(ipi, cpu);
	retval = intr_add_handler(devname, irq, handler, NULL, irqflags, NULL);
	if (retval != 0) {
		unbind_from_irq(irq);
		return retval;
	}

	return irq;
}

void unbind_from_irqhandler(unsigned int irq, void *dev_id)
{
#ifdef notyet
	intr_remove_handler(dev_id); /* XXX */
#endif 
	unbind_from_irq(irq);
}

#if 0
/* Rebind an evtchn so that it gets delivered to a specific cpu */
static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
{
	evtchn_op_t op = { .cmd = EVTCHNOP_bind_vcpu };
	int evtchn;

	mtx_lock_spin(&irq_mapping_update_lock);

	evtchn = evtchn_from_irq(irq);
	if (!VALID_EVTCHN(evtchn)) {
		mtx_unlock_spin(&irq_mapping_update_lock);
		return;
	}

	/* Send future instances of this interrupt to other vcpu. */
	op.u.bind_vcpu.port = evtchn;
	op.u.bind_vcpu.vcpu = tcpu;

	/*
	 * If this fails, it usually just indicates that we're dealing with a 
	 * virq or IPI channel, which don't actually need to be rebound. Ignore
	 * it, but don't do the xenlinux-level rebind in that case.
	 */
	if (HYPERVISOR_event_channel_op(&op) >= 0)
		bind_evtchn_to_cpu(evtchn, tcpu);

	mtx_unlock_spin(&irq_mapping_update_lock);

}

static void set_affinity_irq(unsigned irq, xen_cpumask_t dest)
{
	unsigned tcpu = first_cpu(dest);
	rebind_irq_to_cpu(irq, tcpu);
}
#endif

/*
 * Interface to generic handling in intr_machdep.c
 */


/*------------ interrupt handling --------------------------------------*/
#define TODO            printf("%s: not implemented!\n", __func__) 


static void     xenpic_dynirq_enable_source(struct intsrc *isrc); 
static void     xenpic_dynirq_disable_source(struct intsrc *isrc, int); 
static void     xenpic_dynirq_eoi_source(struct intsrc *isrc); 
static void     xenpic_dynirq_enable_intr(struct intsrc *isrc); 

static void     xenpic_pirq_enable_source(struct intsrc *isrc); 
static void     xenpic_pirq_disable_source(struct intsrc *isrc, int); 
static void     xenpic_pirq_eoi_source(struct intsrc *isrc); 
static void     xenpic_pirq_enable_intr(struct intsrc *isrc); 


static int      xenpic_vector(struct intsrc *isrc); 
static int      xenpic_source_pending(struct intsrc *isrc); 
static void     xenpic_suspend(struct intsrc *isrc); 
static void     xenpic_resume(struct intsrc *isrc); 


struct pic xenpic_dynirq_template  =  { 
	xenpic_dynirq_enable_source, 
	xenpic_dynirq_disable_source,
	xenpic_dynirq_eoi_source, 
	xenpic_dynirq_enable_intr, 
	xenpic_vector, 
	xenpic_source_pending,
	xenpic_suspend, 
	xenpic_resume 
};

struct pic xenpic_pirq_template  =  { 
	xenpic_pirq_enable_source, 
	xenpic_pirq_disable_source,
	xenpic_pirq_eoi_source, 
	xenpic_pirq_enable_intr, 
	xenpic_vector, 
	xenpic_source_pending,
	xenpic_suspend, 
	xenpic_resume 
};


void 
xenpic_dynirq_enable_source(struct intsrc *isrc)
{
	unsigned int irq;
	struct xenpic_intsrc *xp;

	xp = (struct xenpic_intsrc *)isrc;
	
	mtx_lock_spin(&irq_mapping_update_lock);
	if (xp->xp_masked) {
		irq = xenpic_vector(isrc);
		unmask_evtchn(evtchn_from_irq(irq));
		xp->xp_masked = FALSE;
	}
	mtx_unlock_spin(&irq_mapping_update_lock);
}

static void 
xenpic_dynirq_disable_source(struct intsrc *isrc, int foo)
{
	unsigned int irq;
	struct xenpic_intsrc *xp;
	
	xp = (struct xenpic_intsrc *)isrc;
	
	mtx_lock_spin(&irq_mapping_update_lock);
	if (!xp->xp_masked) {
		irq = xenpic_vector(isrc);
		mask_evtchn(evtchn_from_irq(irq));
		xp->xp_masked = TRUE;
	}	
	mtx_unlock_spin(&irq_mapping_update_lock);
}

static void 
xenpic_dynirq_enable_intr(struct intsrc *isrc)
{
	unsigned int irq;
	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	unmask_evtchn(evtchn_from_irq(irq));
	mtx_unlock_spin(&irq_mapping_update_lock);
}

static void 
xenpic_dynirq_eoi_source(struct intsrc *isrc)
{
	unsigned int irq;

	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	unmask_evtchn(evtchn_from_irq(irq));
	mtx_unlock_spin(&irq_mapping_update_lock);
}

static int
xenpic_vector(struct intsrc *isrc)
{
    struct xenpic_intsrc *pin;

    pin = (struct xenpic_intsrc *)isrc;

    return (pin->xp_vector);
}

static int
xenpic_source_pending(struct intsrc *isrc)
{
	TODO;
	return 0;
}

static void 
xenpic_suspend(struct intsrc *isrc) 
{ 
	TODO; 
} 
 
static void 
xenpic_resume(struct intsrc *isrc) 
{ 
	TODO; 
} 


void notify_remote_via_irq(int irq)
{
	int evtchn = evtchn_from_irq(irq);

	if (VALID_EVTCHN(evtchn))
		notify_remote_via_evtchn(evtchn);
}

/* required for support of physical devices */
static inline void 
pirq_unmask_notify(int pirq)
{
	physdev_op_t op;
	if (unlikely(test_bit(pirq, &pirq_needs_unmask_notify[0]))) {
		op.cmd = PHYSDEVOP_IRQ_UNMASK_NOTIFY;
		(void)HYPERVISOR_physdev_op(&op);
	}
}

static inline void 
pirq_query_unmask(int pirq)
{
	physdev_op_t op;
	op.cmd = PHYSDEVOP_IRQ_STATUS_QUERY;
	op.u.irq_status_query.irq = pirq;
	(void)HYPERVISOR_physdev_op(&op);
	clear_bit(pirq, &pirq_needs_unmask_notify[0]);
	if ( op.u.irq_status_query.flags & PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY )
		set_bit(pirq, &pirq_needs_unmask_notify[0]);
}

/*
 * On startup, if there is no action associated with the IRQ then we are
 * probing. In this case we should not share with others as it will confuse us.
 */
#define probing_irq(_irq) (intr_lookup_source(irq) == NULL)

static void 
xenpic_pirq_enable_intr(struct intsrc *isrc)
{
	evtchn_op_t op;
	int evtchn;
	unsigned int irq;
	
	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	evtchn = evtchn_from_irq(irq);

	if (VALID_EVTCHN(evtchn))
		goto out;

	op.cmd               = EVTCHNOP_bind_pirq;
	op.u.bind_pirq.pirq  = irq;
	/* NB. We are happy to share unless we are probing. */
	op.u.bind_pirq.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE;
	
	if (HYPERVISOR_event_channel_op(&op) != 0) {
		if (!probing_irq(irq)) /* Some failures are expected when probing. */
			printf("Failed to obtain physical IRQ %d\n", irq);
		mtx_unlock_spin(&irq_mapping_update_lock);
		return;
	}
	evtchn = op.u.bind_pirq.port;

	pirq_query_unmask(irq_to_pirq(irq));

	bind_evtchn_to_cpu(evtchn, 0);
	evtchn_to_irq[evtchn] = irq;
	irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn);

 out:
	unmask_evtchn(evtchn);
	pirq_unmask_notify(irq_to_pirq(irq));
	mtx_unlock_spin(&irq_mapping_update_lock);
}

#if 0
static void 
xenpic_pirq_disable_intr(struct intsrc *isrc)
{
	evtchn_op_t op;
	unsigned int irq;
	int evtchn;
	
	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	evtchn = evtchn_from_irq(irq);

	if (!VALID_EVTCHN(evtchn)) 
		goto done;
	
	mask_evtchn(evtchn);

	op.cmd          = EVTCHNOP_close;
	op.u.close.port = evtchn;
	PANIC_IF(HYPERVISOR_event_channel_op(&op) != 0);

	bind_evtchn_to_cpu(evtchn, 0);
	evtchn_to_irq[evtchn] = -1;
	irq_info[irq] = IRQ_UNBOUND;
 done:
	mtx_unlock_spin(&irq_mapping_update_lock);
}
#endif

static void 
xenpic_pirq_enable_source(struct intsrc *isrc)
{
	int evtchn;
	unsigned int irq;

	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	evtchn = evtchn_from_irq(irq);

	if (!VALID_EVTCHN(evtchn))
		goto done;

	unmask_evtchn(evtchn);
	pirq_unmask_notify(irq_to_pirq(irq));
 done:
	mtx_unlock_spin(&irq_mapping_update_lock);
}

static void 
xenpic_pirq_disable_source(struct intsrc *isrc, int eoi)
{
	int evtchn;
	unsigned int irq;

	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	evtchn = evtchn_from_irq(irq);

	if (!VALID_EVTCHN(evtchn))
		goto done;

	mask_evtchn(evtchn);
 done:
	mtx_unlock_spin(&irq_mapping_update_lock);
}


static void 
xenpic_pirq_eoi_source(struct intsrc *isrc)
{
	int evtchn;
	unsigned int irq;

	mtx_lock_spin(&irq_mapping_update_lock);
	irq = xenpic_vector(isrc);
	evtchn = evtchn_from_irq(irq);

	if (!VALID_EVTCHN(evtchn))
		goto done;

	unmask_evtchn(evtchn);
	pirq_unmask_notify(irq_to_pirq(irq));
 done:
	mtx_unlock_spin(&irq_mapping_update_lock);
}

void 
mask_evtchn(int port)
{
	shared_info_t *s = HYPERVISOR_shared_info;
	synch_set_bit(port, &s->evtchn_mask[0]);
}

void 
unmask_evtchn(int port)
{
	shared_info_t *s = HYPERVISOR_shared_info;
	unsigned int cpu = smp_processor_id();
	vcpu_info_t *vcpu_info = &s->vcpu_info[cpu];

	/* Slow path (hypercall) if this is a non-local port. */
	if (unlikely(cpu != cpu_from_evtchn(port))) {
		evtchn_op_t op = { .cmd = EVTCHNOP_unmask,
				   .u.unmask.port = port };
		(void)HYPERVISOR_event_channel_op(&op);
		return;
	}

	synch_clear_bit(port, &s->evtchn_mask[0]);

	/*
	 * The following is basically the equivalent of 'hw_resend_irq'. Just
	 * like a real IO-APIC we 'lose the interrupt edge' if the channel is
	 * masked.
	 */
	if (synch_test_bit(port, &s->evtchn_pending[0]) && 
	    !synch_test_and_set_bit(port / BITS_PER_LONG,
				    &vcpu_info->evtchn_pending_sel)) {
		vcpu_info->evtchn_upcall_pending = 1;
		if (!vcpu_info->evtchn_upcall_mask)
			force_evtchn_callback();
	}
}

void irq_resume(void)
{
	evtchn_op_t op;
	int         cpu, pirq, virq, ipi, irq, evtchn;

	init_evtchn_cpu_bindings();

	/* New event-channel space is not 'live' yet. */
	for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
		mask_evtchn(evtchn);

	/* Check that no PIRQs are still bound. */
	for (pirq = 0; pirq < NR_PIRQS; pirq++)
		PANIC_IF(irq_info[pirq_to_irq(pirq)] != IRQ_UNBOUND);

	/* Secondary CPUs must have no VIRQ or IPI bindings. */
	for (cpu = 1; cpu < NR_CPUS; cpu++) {
		for (virq = 0; virq < NR_VIRQS; virq++)
			PANIC_IF(per_cpu(virq_to_irq, cpu)[virq] != -1);
		for (ipi = 0; ipi < NR_IPIS; ipi++)
			PANIC_IF(per_cpu(ipi_to_irq, cpu)[ipi] != -1);
	}

	/* No IRQ <-> event-channel mappings. */
	for (irq = 0; irq < NR_IRQS; irq++)
		irq_info[irq] &= ~0xFFFF; /* zap event-channel binding */
	for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
		evtchn_to_irq[evtchn] = -1;

	/* Primary CPU: rebind VIRQs automatically. */
	for (virq = 0; virq < NR_VIRQS; virq++) {
		if ((irq = per_cpu(virq_to_irq, 0)[virq]) == -1)
			continue;

		PANIC_IF(irq_info[irq] != mk_irq_info(IRQT_VIRQ, virq, 0));

		/* Get a new binding from Xen. */
		memset(&op, 0, sizeof(op));
		op.cmd              = EVTCHNOP_bind_virq;
		op.u.bind_virq.virq = virq;
		op.u.bind_virq.vcpu = 0;
		PANIC_IF(HYPERVISOR_event_channel_op(&op) != 0);
		evtchn = op.u.bind_virq.port;
        
		/* Record the new mapping. */
		evtchn_to_irq[evtchn] = irq;
		irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn);

		/* Ready for use. */
		unmask_evtchn(evtchn);
	}

	/* Primary CPU: rebind IPIs automatically. */
	for (ipi = 0; ipi < NR_IPIS; ipi++) {
		if ((irq = per_cpu(ipi_to_irq, 0)[ipi]) == -1)
			continue;

		PANIC_IF(irq_info[irq] != mk_irq_info(IRQT_IPI, ipi, 0));

		/* Get a new binding from Xen. */
		memset(&op, 0, sizeof(op));
		op.cmd = EVTCHNOP_bind_ipi;
		op.u.bind_ipi.vcpu = 0;
		PANIC_IF(HYPERVISOR_event_channel_op(&op) != 0);
		evtchn = op.u.bind_ipi.port;
        
		/* Record the new mapping. */
		evtchn_to_irq[evtchn] = irq;
		irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn);

		/* Ready for use. */
		unmask_evtchn(evtchn);
	}
}

static void 
evtchn_init(void *dummy __unused)
{
	int i, cpu;
	struct xenpic_intsrc *pin, *tpin;
	
	/* No VIRQ or IPI bindings. */
	for (cpu = 0; cpu < NR_CPUS; cpu++) {
		for (i = 0; i < NR_VIRQS; i++)
			per_cpu(virq_to_irq, cpu)[i] = -1;
		for (i = 0; i < NR_IPIS; i++)
			per_cpu(ipi_to_irq, cpu)[i] = -1;
	}

	/* No event-channel -> IRQ mappings. */
	for (i = 0; i < NR_EVENT_CHANNELS; i++) {
		evtchn_to_irq[i] = -1;
		mask_evtchn(i); /* No event channels are 'live' right now. */
	}

	/* No IRQ -> event-channel mappings. */
	for (i = 0; i < NR_IRQS; i++)
		irq_info[i] = IRQ_UNBOUND;
	
	xp = malloc(sizeof(struct xenpic) + NR_IRQS*sizeof(struct xenpic_intsrc), 
		    M_DEVBUF, M_WAITOK);

	xp->xp_dynirq_pic = &xenpic_dynirq_template;
	xp->xp_pirq_pic = &xenpic_pirq_template;
	xp->xp_numintr = NR_IRQS;
	bzero(xp->xp_pins, sizeof(struct xenpic_intsrc) * NR_IRQS);

	for (i = 0,pin = xp->xp_pins; i < NR_DYNIRQS; i++) {
		/* Dynamic IRQ space is currently unbound. Zero the refcnts. */
		irq_bindcount[dynirq_to_irq(i)] = 0;

		tpin = &pin[dynirq_to_irq(i)];		
		tpin->xp_intsrc.is_pic = xp->xp_dynirq_pic;
		tpin->xp_vector = dynirq_to_irq(i);
	}


	for (i = 0, pin = xp->xp_pins; i < NR_PIRQS; i++) {
		/* Dynamic IRQ space is currently unbound. Zero the refcnts. */
		irq_bindcount[pirq_to_irq(i)] = 0;

#ifdef RTC_IRQ
		/* If not domain 0, force our RTC driver to fail its probe. */
		if ((i == RTC_IRQ) &&
		    !(xen_start_info->flags & SIF_INITDOMAIN))
			continue;
#endif
		tpin = &pin[pirq_to_irq(i)];		
		tpin->xp_intsrc.is_pic = xp->xp_pirq_pic;
		tpin->xp_vector = pirq_to_irq(i);
	}
}

SYSINIT(evtchn_init, SI_SUB_INTR, SI_ORDER_ANY, evtchn_init, NULL);
    /*
     * irq_mapping_update_lock: in order to allow an interrupt to occur in a critical
     * 	        section, to set pcpu->ipending (etc...) properly, we
     *	        must be able to get the icu lock, so it can't be
     *	        under witness.
     */

MTX_SYSINIT(irq_mapping_update_lock, &irq_mapping_update_lock, "xp", MTX_SPIN);
