#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "../port/error.h" #include "xen.h" #define INTRLOG(a) #define FASTLOG(a) #define INFO(a) a /* * real time clock and non-volatile ram */ #define NR_IRQS 32 #define NR_PIRQS 32 unsigned long upcallcount = 0; enum{ Qdir = 0, Qevtchn, }; Dirtab evtchndir[]={ ".", {Qdir, 0, QTDIR}, 0, 0555, "evtchn", {Qevtchn, 0}, 0, 0664, }; static Chan* evtchnattach(char* spec) { return devattach('Z', spec); } static Walkqid* evtchnwalk(Chan* c, Chan *nc, char** name, int nname) { return devwalk(c, nc, name, nname, evtchndir, nelem(evtchndir), devgen); } static int evtchnstat(Chan* c, uchar* dp, int n) { return devstat(c, dp, n, evtchndir, nelem(evtchndir), devgen); } static Chan* evtchnopen(Chan* c, int omode) { omode = openmode(omode); switch((ulong)c->qid.path){ case Qevtchn: error(Eperm); } return devopen(c, omode, evtchndir, nelem(evtchndir), devgen); } static void evtchnclose(Chan*) { } static long evtchnread(Chan* c, void *buf, long n, vlong ) { if(c->qid.type & QTDIR) return devdirread(c, buf, n, evtchndir, nelem(evtchndir), devgen); switch((ulong)c->qid.path){ case Qevtchn: if(n == 0) return 0; return 0; } error(Ebadarg); return 0; } static long evtchnwrite(Chan* c, void* /*buf*/, long n, vlong off) { ulong offset = off; if(offset!=0) error(Ebadarg); switch((ulong)c->qid.path){ case Qevtchn: if(n == 0) return 0; return 0; } error(Ebadarg); return 0; } /****************************************************************************** * evtchn.c * * Communication via Xen event channels. * * Copyright (c) 2002-2004, K A Fraser */ /* * This lock protects updates to the following mapping and reference-count * arrays. The lock does not need to be acquired to read the mapping tables. */ static Lock irq_mapping_update_lock; /* IRQ <-> event-channel mappings. */ static int evtchn_to_irq[NR_EVENT_CHANNELS]; static int irq_to_evtchn[NR_IRQS]; /* IRQ <-> VIRQ mapping. */ static int virq_to_irq[NR_VIRQS]; /* Reference counts for bindings to IRQs. */ static int irq_bindcount[NR_IRQS] = { /* this one is special. There may be other really * special ones too, we'll see */ [VectorBPT] = 1, }; /* get rid of bitmap nonsense SOON and turn it into array of ints. */ /* Bitmap indicating which PIRQs require Xen to be notified on unmask. */ static unsigned long pirq_needs_unmask_notify[NR_PIRQS/sizeof(unsigned long)]; /* if only such a thing existed. /* Upcall to generic IRQ layer. */ //extern asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs); #define VALID_EVTCHN(_chn) ((_chn) != -1) /****************************************************************************** * evtchn.h * * Communication via Xen event channels. * Also definitions for the device that demuxes notifications to userspace. * * Copyright (c) 2004, K A Fraser */ /* * LOW-LEVEL DEFINITIONS */ /* Entry point for notifications into Linux subsystems. */ void evtchn_do_upcall(Ureg *regs); /* Entry point for notifications into the userland character device. */ void evtchn_device_upcall(int port); /**/ void do_IRQ(int irq, Ureg *regs) { void trap(Ureg *regs); static void ack_dynirq(int); static void end_dynirq(int); ack_dynirq(irq); regs->trap = irq; trap(regs); end_dynirq(irq); //dp("dI:%x m 0x%ulx\n", irq, HYPERVISOR_shared_info->evtchn_mask[0]); } /**/ void evtchn_do_upcall(Ureg *) { unsigned long l1, l2; unsigned int port; int l1i, l2i; int irq; shared_info_t *s = HYPERVISOR_shared_info; int x; // What's the p9 equivalent to this one? // local_irq_save(flags); x = splhi(); FASTLOG(dp("E%d\n", upcallcount)); upcallcount++; INTRLOG(dp("evtchn_do_upcall x is %d\n", x)); while ( s->vcpu_data[0].evtchn_upcall_pending ) { INTRLOG(dp("upcall pending is 0x%x\n", s->vcpu_data[0].evtchn_upcall_pending)); s->vcpu_data[0].evtchn_upcall_pending = 0; /* NB. No need for a barrier here -- XCHG is a barrier on x86. */ l1 = xchg((unsigned long *) &s->evtchn_pending_sel, 0); INTRLOG(dp("l1 is 0x%x\n", l1)); INTRLOG(dp("s->evtchn_pending_sel is 0x%lx\n", s->evtchn_pending_sel)); INTRLOG(dp("try ffs\n")); INTRLOG(dp("ffs(0x%ulx is %d\n", l1, ffs(l1))); while ( (l1i = ffs(l1)) != 0 ) { INTRLOG(dp("l1i 0x%x\n", l1i)); l1i--; l1 &= ~(1 << l1i); INTRLOG(dp("pending for %d is 0x%ulx, mask for that is 0x%ulx\n", l1i, s->evtchn_pending[l1i], s->evtchn_mask[l1i])); l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i]; INTRLOG(dp("l2 is 0x%ulx, ffs of that is %d\n", l2, ffs(l2))); while ( (l2i = ffs(l2)) != 0 ) { l2i--; l2 &= ~(1 << l2i); port = (l1i << 5) + l2i; if ( (irq = evtchn_to_irq[port]) != -1 ) { Ureg someregs; INTRLOG(dp("WHAT do_IRQ? for 0x%x\n", irq)); INTRLOG(dp("evtchn %d, irq %d\n", port, irq)); do_IRQ(irq, &someregs); FASTLOG(dp("i\n")); } else { INTRLOG(dp("Device upcall port 0x%ulx\n", port)); evtchn_device_upcall(port); } clear_evtchn(port); } } } FASTLOG(dp("x\n")); splx(x); FASTLOG(dp("e: m 0x%ulx\n", s->evtchn_mask[0])); INTRLOG(dp("FINISH upcall\n")); // local_irq_restore(flags); } static int find_unbound_irq(int want) { int irq; /* this code really blows. It has no locks and the first thing * I do is a damned race. Fix Me. */ if (want) { if (irq_bindcount[want] == 0) return want; panic("find_unbound_irq: Wanted %d, not avail\n", want); } /* never use irq 0 */ for ( irq = 1; 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; } int bind_virq_to_irq(int virq, int want) { evtchn_op_t op; int evtchn, irq; ilock(&irq_mapping_update_lock); if ( (irq = virq_to_irq[virq]) == -1 ) { op.cmd = EVTCHNOP_bind_virq; op.u.bind_virq.virq = virq; if ( HYPERVISOR_event_channel_op(&op) != 0 ) panic("Failed to bind virtual IRQ %d\n", virq); evtchn = op.u.bind_virq.port; INTRLOG(dp("evtchn for virq %d is %d\n", virq, evtchn)); irq = find_unbound_irq(want); evtchn_to_irq[evtchn] = irq; irq_to_evtchn[irq] = evtchn; INTRLOG(dp("for VIRQ %d irq is %d\n", virq, irq)); virq_to_irq[virq] = irq; } irq_bindcount[irq]++; iunlock(&irq_mapping_update_lock); return irq; } void unbind_virq_from_irq(int virq) { evtchn_op_t op; int irq = virq_to_irq[virq]; int evtchn = irq_to_evtchn[irq]; ilock(&irq_mapping_update_lock); if ( --irq_bindcount[irq] == 0 ) { op.cmd = EVTCHNOP_close; op.u.close.dom = DOMID_SELF; op.u.close.port = evtchn; if ( HYPERVISOR_event_channel_op(&op) != 0 ) panic("Failed to unbind virtual IRQ %d\n", virq); evtchn_to_irq[evtchn] = -1; irq_to_evtchn[irq] = -1; virq_to_irq[virq] = -1; } iunlock(&irq_mapping_update_lock); } int bind_evtchn_to_irq(int evtchn, int want) { int irq; ilock(&irq_mapping_update_lock); if ( (irq = evtchn_to_irq[evtchn]) == -1 ) { irq = find_unbound_irq(want); evtchn_to_irq[evtchn] = irq; irq_to_evtchn[irq] = evtchn; } irq_bindcount[irq]++; iunlock(&irq_mapping_update_lock); return irq; } void unbind_evtchn_from_irq(int evtchn) { int irq = evtchn_to_irq[evtchn]; ilock(&irq_mapping_update_lock); if ( --irq_bindcount[irq] == 0 ) { evtchn_to_irq[evtchn] = -1; irq_to_evtchn[irq] = -1; } iunlock(&irq_mapping_update_lock); } /* * Interface to generic handling */ static unsigned int startup_dynirq(unsigned int irq) { unmask_evtchn(irq_to_evtchn[irq]); return 0; } static void shutdown_dynirq(unsigned int irq) { mask_evtchn(irq_to_evtchn[irq]); } static void enable_dynirq(unsigned int irq) { unmask_evtchn(irq_to_evtchn[irq]); } static void disable_dynirq(unsigned int irq) { mask_evtchn(irq_to_evtchn[irq]); } static void ack_dynirq(unsigned int irq) { mask_evtchn(irq_to_evtchn[irq]); clear_evtchn(irq_to_evtchn[irq]); } static void end_dynirq(unsigned int irq) { /* if ( !(irq_desc[irq].status & IRQ_DISABLED) )*/ unmask_evtchn(irq_to_evtchn[irq]); } /* equivalent of i8259isr ... */ int xenisr( int irq) { end_dynirq(irq); return irq; } int xenintrdisable(int irq) { dp("xenintrdisable\n"); disable_dynirq(irq); return 0; } int xenintrvecno(int irq) { dp("xenintrvecno returns %d\n", irq); return irq; } /* maybe not */ static void misdirect_interrupt(void *arg){ extern void xentimerclock(Ureg *, void *); /* oh boy. Need to reconcile the xen and plan 9 regs ... */ Ureg reg; INTRLOG(dp("misdirect: arg %p, &arg %p\n", arg, &arg)); USED(arg); xentimerclock(®, 0); /* nothing */ } void irq_suspend(void) { int virq, irq, evtchn; /* Unbind VIRQs from event channels. */ for ( virq = 0; virq < NR_VIRQS; virq++ ) { if ( (irq = virq_to_irq[virq]) == -1 ) continue; evtchn = irq_to_evtchn[irq]; /* Mark the event channel as unused in our table. */ evtchn_to_irq[evtchn] = -1; irq_to_evtchn[irq] = -1; } /* * We should now be unbound from all event channels. Stale bindings to * PIRQs and/or inter-domain event channels will cause us to barf here. */ for ( evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++ ) if ( evtchn_to_irq[evtchn] != -1 ) panic("Suspend attempted while bound to evtchn %d.\n", evtchn); } void irq_resume(void) { evtchn_op_t op; int virq, irq, evtchn; for ( evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++ ) mask_evtchn(evtchn); /* New event-channel space is not 'live' yet. */ for ( virq = 0; virq < NR_VIRQS; virq++ ) { if ( (irq = virq_to_irq[virq]) == -1 ) continue; /* Get a new binding from Xen. */ op.cmd = EVTCHNOP_bind_virq; op.u.bind_virq.virq = virq; if ( HYPERVISOR_event_channel_op(&op) != 0 ) panic("Failed to bind virtual IRQ %d\n", virq); evtchn = op.u.bind_virq.port; /* Record the new mapping. */ evtchn_to_irq[evtchn] = irq; irq_to_evtchn[irq] = evtchn; /* Ready for use. */ unmask_evtchn(evtchn); } } /* given a Vctl with a virq in it, allocate an irq. Return the irq * for the vector. Trap will map irq to virq in the code */ int xenintrenable(Vctl *v) { int irq = v->irq; int virq = VIRQ_MISDIRECT; int evtchn; int want = 0; if (irq == IrqTIMER) want = VIRQ_TIMER; /* is it already set up? */ evtchn = irq_to_evtchn[irq]; if (evtchn> -1) { INFO(dp("xenintrenable: IRQ %d Already set up evtchn %d. \n", irq, evtchn);) } else { irq = bind_virq_to_irq(virq, want); evtchn = irq_to_evtchn[irq]; } INFO(dp("xenintrenable virq %d, irq %d, evtchn %d\n", virq, irq ,evtchn)); /* intrenable now calls this, not the other way around.*/ unmask_evtchn(evtchn); INFO(dp(" enabled, virq %d, irq %d\n", virq, irq)); v->eoi = xenisr; return irq; } void evtchninit(void) { int i; extern void xenmisdirect(Ureg*,void*); INTRLOG(dp("EVTCHNINIT\n")); ilock(&irq_mapping_update_lock); /* No VIRQ -> IRQ mappings. */ for ( i = 0; i < NR_VIRQS; i++ ) virq_to_irq[i] = -1; /* just VIRQs for now */ INTRLOG(dp("NO VIRQ->IRQ done\n")); /* 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. */ } INTRLOG(dp("no evt chan -> irq done\n")); /* No IRQ -> event-channel mappings. */ for ( i = 0; i < NR_IRQS; i++ ) irq_to_evtchn[i] = -1; INTRLOG(dp("clear irq to evt\n")); INTRLOG(dp("do intr enable\n")); iunlock(&irq_mapping_update_lock); } void evtchnresume(void) { extern void xenmisdirect(Ureg *, void *); intrenable(bind_virq_to_irq(VIRQ_MISDIRECT, 0), xenmisdirect, nil, 0, "Xen misdirect"); unmask_evtchn(irq_to_evtchn[virq_to_irq[VIRQ_MISDIRECT]]); INTRLOG(dp("done intr enable\n")); #ifdef NOT /* This needs to be done early, but after the IRQ subsystem is alive. */ INTRLOG(dp("call crtl_if_init\n")); ctrl_if_init(); INTRLOG(dp("DONE EVTCHNINIT\n")); #endif } /****************************************************************************** * evtchn.c * * Xenolinux driver for receiving and demuxing event-channel signals. * * Copyright (c) 2004, K A Fraser */ /* Only one process may open /dev/xen/evtchn at any time. */ static unsigned long evtchn_dev_inuse; /* Notification ring, accessed via /dev/xen/evtchn. */ #define RING_SIZE 2048 /* 2048 16-bit entries */ #define RING_MASK(_i) ((_i)&(RING_SIZE-1)) static u16 *ring; static unsigned int ring_cons, ring_prod, ring_overflow; /* Which ports is user-space bound to? */ static u32 bound_ports[32]; static Lock edu_lock; void evtchn_device_upcall(int port) { ilock(&edu_lock); mask_evtchn(port); clear_evtchn(port); if ( ring != nil ) { if ( (ring_prod - ring_cons) < RING_SIZE ) { ring[RING_MASK(ring_prod)] = (u16)port; if ( ring_cons == ring_prod++ ) { //wake_up_interruptible(&evtchn_wait); // kill_fasync(&evtchn_async_queue, SIGIO, POLL_IN); } } else { ring_overflow = 1; } } INTRLOG(dp("something to do in evtchn_device_upcall\n")); /* THI S MAY BE BOGUS!\n");*/ unmask_evtchn(port); panic("evtchn_device_upcall"); iunlock(&edu_lock); } #ifdef NOTYET static void __evtchn_reset_buffer_ring(void) { /* Initialise the ring to empty. Clear errors. */ ring_cons = ring_prod = ring_overflow = 0; } static ssize_t evtchn_read(struct file *file, char *buf, size_t count, loff_t *ppos) { int rc; unsigned int c, p, bytes1 = 0, bytes2 = 0; DECLARE_WAITQUEUE(wait, current); add_wait_queue(&evtchn_wait, &wait); count &= ~1; /* even number of bytes */ if ( count == 0 ) { rc = 0; goto out; } if ( count > PAGE_SIZE ) count = PAGE_SIZE; for ( ; ; ) { set_current_state(TASK_INTERRUPTIBLE); if ( (c = ring_cons) != (p = ring_prod) ) break; if ( ring_overflow ) { rc = -EFBIG; goto out; } if ( file->f_flags & O_NONBLOCK ) { rc = -EAGAIN; goto out; } if ( signal_pending(current) ) { rc = -ERESTARTSYS; goto out; } schedule(); } /* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */ if ( ((c ^ p) & RING_SIZE) != 0 ) { bytes1 = (RING_SIZE - RING_MASK(c)) * sizeof(u16); bytes2 = RING_MASK(p) * sizeof(u16); } else { bytes1 = (p - c) * sizeof(u16); bytes2 = 0; } /* Truncate chunks according to caller's maximum byte count. */ if ( bytes1 > count ) { bytes1 = count; bytes2 = 0; } else if ( (bytes1 + bytes2) > count ) { bytes2 = count - bytes1; } if ( copy_to_user(buf, &ring[RING_MASK(c)], bytes1) || ((bytes2 != 0) && copy_to_user(&buf[bytes1], &ring[0], bytes2)) ) { rc = -EFAULT; goto out; } ring_cons += (bytes1 + bytes2) / sizeof(u16); rc = bytes1 + bytes2; out: __set_current_state(TASK_RUNNING); remove_wait_queue(&evtchn_wait, &wait); return rc; } static ssize_t evtchn_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { int rc, i; u16 *kbuf = (u16 *)get_free_page(GFP_KERNEL); if ( kbuf == NULL ) return -ENOMEM; count &= ~1; /* even number of bytes */ if ( count == 0 ) { rc = 0; goto out; } if ( count > PAGE_SIZE ) count = PAGE_SIZE; if ( copy_from_user(kbuf, buf, count) != 0 ) { rc = -EFAULT; goto out; } spin_lock_irq(&lock); for ( i = 0; i < (count/2); i++ ) if ( test_bit(kbuf[i], &bound_ports[0]) ) unmask_evtchn(kbuf[i]); spin_unlock_irq(&lock); rc = count; out: free_page((unsigned long)kbuf); return rc; } static int evtchn_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int rc = 0; spin_lock_irq(&lock); switch ( cmd ) { case EVTCHN_RESET: __evtchn_reset_buffer_ring(); break; case EVTCHN_BIND: if ( !test_and_set_bit(arg, &bound_ports[0]) ) unmask_evtchn(arg); else rc = -EINVAL; break; case EVTCHN_UNBIND: if ( test_and_clear_bit(arg, &bound_ports[0]) ) mask_evtchn(arg); else rc = -EINVAL; break; default: rc = -ENOSYS; break; } spin_unlock_irq(&lock); return rc; } static int evtchn_open(struct inode *inode, struct file *filp) { u16 *_ring; if ( test_and_set_bit(0, &evtchn_dev_inuse) ) return -EBUSY; /* Allocate outside locked region so that we can use GFP_KERNEL. */ if ( (_ring = (u16 *)get_free_page(GFP_KERNEL)) == NULL ) return -ENOMEM; spin_lock_irq(&lock); ring = _ring; __evtchn_reset_buffer_ring(); spin_unlock_irq(&lock); MOD_INC_USE_COUNT; return 0; } static int evtchn_release(struct inode *inode, struct file *filp) { int i; spin_lock_irq(&lock); if ( ring != NULL ) { free_page((unsigned long)ring); ring = NULL; } for ( i = 0; i < NR_EVENT_CHANNELS; i++ ) if ( test_and_clear_bit(i, &bound_ports[0]) ) mask_evtchn(i); spin_unlock_irq(&lock); evtchn_dev_inuse = 0; MOD_DEC_USE_COUNT; return 0; } #endif Dev evtchndevtab = { 'Z', "evtchn", devreset, devinit, /* done by hand in main */ devshutdown, evtchnattach, evtchnwalk, evtchnstat, evtchnopen, devcreate, evtchnclose, evtchnread, devbread, evtchnwrite, devbwrite, devremove, devwstat, };