This allows Dom0 to manage physical hardware, redirecting the physical interrupts to event channels. --- sys/x86/xen/xen_intr.c | 190 +++++++++++++++++++++++++++++++++++++++++++++-- sys/xen/xen_intr.h | 11 +++ 2 files changed, 192 insertions(+), 9 deletions(-) diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c index bc0781e..340e5ed 100644 --- a/sys/x86/xen/xen_intr.c +++ b/sys/x86/xen/xen_intr.c _at__at_ -104,6 +104,8 _at__at_ DPCPU_DECLARE(struct vcpu_info *, vcpu_info); #define is_valid_evtchn(x) ((x) != 0) +#define EEXIST 17 /* Xen "already exists" error */ + struct xenisrc { struct intsrc xi_intsrc; enum evtchn_type xi_type; _at__at_ -111,6 +113,9 _at__at_ struct xenisrc { int xi_vector; /* Global isrc vector number. */ evtchn_port_t xi_port; int xi_pirq; + int xi_activehi:1; + int xi_edgetrigger:1; + int xi_configured:1; int xi_virq; u_int xi_close:1; /* close on unbind? */ u_int xi_needs_eoi:1; _at__at_ -136,6 +141,9 _at__at_ static void xen_intr_pirq_enable_source(struct intsrc *isrc); static void xen_intr_pirq_disable_source(struct intsrc *isrc, int eoi); static void xen_intr_pirq_eoi_source(struct intsrc *isrc); static void xen_intr_pirq_enable_intr(struct intsrc *isrc); +static void xen_intr_pirq_disable_intr(struct intsrc *isrc); +static int xen_intr_pirq_config_intr(struct intsrc *isrc, + enum intr_trigger trig, enum intr_polarity pol); /** * PIC interface for all event channel port types except physical IRQs. _at__at_ -163,12 +171,12 _at__at_ struct pic xen_intr_pirq_pic = { .pic_disable_source = xen_intr_pirq_disable_source, .pic_eoi_source = xen_intr_pirq_eoi_source, .pic_enable_intr = xen_intr_pirq_enable_intr, - .pic_disable_intr = xen_intr_disable_intr, + .pic_disable_intr = xen_intr_pirq_disable_intr, .pic_vector = xen_intr_vector, .pic_source_pending = xen_intr_source_pending, .pic_suspend = xen_intr_suspend, .pic_resume = xen_intr_resume, - .pic_config_intr = xen_intr_config_intr, + .pic_config_intr = xen_intr_pirq_config_intr, .pic_assign_cpu = xen_intr_assign_cpu }; _at__at_ -282,11 +290,10 _at__at_ xen_intr_find_unused_isrc(enum evtchn_type type) * object or NULL. */ static struct xenisrc * -xen_intr_alloc_isrc(enum evtchn_type type) +xen_intr_alloc_isrc(enum evtchn_type type, int vector) { static int warned; struct xenisrc *isrc; - int vector; KASSERT(mtx_owned(&xen_intr_isrc_lock), ("Evtchn alloc lock not held")); _at__at_ -297,12 +304,19 _at__at_ xen_intr_alloc_isrc(enum evtchn_type type) } return (NULL); } - vector = FIRST_EVTCHN_INT + xen_intr_isrc_count; - xen_intr_isrc_count++; + + if (type != EVTCHN_TYPE_PIRQ) { + vector = FIRST_EVTCHN_INT + xen_intr_isrc_count; + xen_intr_isrc_count++; + } + + KASSERT((intr_lookup_source(vector) == NULL), + ("Trying to use an already allocated vector")); mtx_unlock(&xen_intr_isrc_lock); isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO); - isrc->xi_intsrc.is_pic = &xen_intr_pic; + isrc->xi_intsrc.is_pic = (type == EVTCHN_TYPE_PIRQ) ? + &xen_intr_pirq_pic : &xen_intr_pic; isrc->xi_vector = vector; isrc->xi_type = type; intr_register_source(&isrc->xi_intsrc); _at__at_ -388,7 +402,7 _at__at_ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port, mtx_lock(&xen_intr_isrc_lock); isrc = xen_intr_find_unused_isrc(type); if (isrc == NULL) { - isrc = xen_intr_alloc_isrc(type); + isrc = xen_intr_alloc_isrc(type, 0); if (isrc == NULL) { mtx_unlock(&xen_intr_isrc_lock); return (ENOSPC); _at__at_ -592,6 +606,10 _at__at_ xen_intr_init(void *dummy __unused) } intr_register_pic(&xen_intr_pic); + intr_register_pic(&xen_intr_pirq_pic); + + if (bootverbose) + printf("Xen interrupt system initialized\n"); return (0); } _at__at_ -925,6 +943,9 _at__at_ xen_intr_pirq_disable_source(struct intsrc *base_isrc, int eoi) isrc = (struct xenisrc *)base_isrc; evtchn_mask_port(isrc->xi_port); + + if (eoi == PIC_EOI) + xen_intr_pirq_eoi_source(base_isrc); } /* _at__at_ -966,8 +987,115 _at__at_ xen_intr_pirq_eoi_source(struct intsrc *base_isrc) * \param isrc The interrupt source to enable. */ static void -xen_intr_pirq_enable_intr(struct intsrc *isrc) +xen_intr_pirq_enable_intr(struct intsrc *base_isrc) +{ + struct xenisrc *isrc; + struct evtchn_bind_pirq bind_pirq; + struct physdev_irq_status_query irq_status; + int error; + + isrc = (struct xenisrc *)base_isrc; + + if (!isrc->xi_configured) { + xen_intr_pirq_config_intr(base_isrc, + isrc->xi_edgetrigger ? INTR_TRIGGER_EDGE : + INTR_TRIGGER_LEVEL, + isrc->xi_activehi ? INTR_POLARITY_HIGH : + INTR_POLARITY_LOW); + } + + irq_status.irq = isrc->xi_pirq; + error = HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status); + if (error) + panic("unable to get status of IRQ#%d", isrc->xi_pirq); + + if (irq_status.flags & XENIRQSTAT_needs_eoi) + isrc->xi_needs_eoi = 1; + + bind_pirq.pirq = isrc->xi_pirq; + bind_pirq.flags = isrc->xi_edgetrigger ? 0 : BIND_PIRQ__WILL_SHARE; + error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq); + if (error) + panic("unable to bind IRQ#%d", isrc->xi_pirq); + + isrc->xi_port = bind_pirq.port; + + mtx_lock(&xen_intr_isrc_lock); + KASSERT((xen_intr_port_to_isrc[bind_pirq.port] == NULL), + ("trying to override an already setup event channel port")); + xen_intr_port_to_isrc[bind_pirq.port] = isrc; + mtx_unlock(&xen_intr_isrc_lock); + + evtchn_unmask_port(isrc->xi_port); +} + +/* + * Disable an interrupt source. + * + * \param isrc The interrupt source to disable. + */ +static void +xen_intr_pirq_disable_intr(struct intsrc *base_isrc) +{ + struct xenisrc *isrc; + struct evtchn_close close; + int error; + + isrc = (struct xenisrc *)base_isrc; + + close.port = isrc->xi_port; + error = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); + if (error) + panic("unable to close event channel %d IRQ#%d", + isrc->xi_port, isrc->xi_pirq); + + mtx_lock(&xen_intr_isrc_lock); + xen_intr_port_to_isrc[isrc->xi_port] = NULL; + mtx_unlock(&xen_intr_isrc_lock); + + isrc->xi_port = 0; +} + +/** + * Perform configuration of an interrupt source. + * + * \param isrc The interrupt source to configure. + * \param trig Edge or level. + * \param pol Active high or low. + * + * \returns 0 if no events are pending, otherwise non-zero. + */ +static int +xen_intr_pirq_config_intr(struct intsrc *base_isrc, enum intr_trigger trig, + enum intr_polarity pol) { + struct xenisrc *isrc = (struct xenisrc *)base_isrc; + struct physdev_setup_gsi setup_gsi; + int error; + + KASSERT(!(trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM), + ("%s: Conforming trigger or polarity\n", __func__)); + + setup_gsi.gsi = isrc->xi_pirq; + setup_gsi.triggering = trig == INTR_TRIGGER_EDGE ? 0 : 1; + setup_gsi.polarity = pol == INTR_POLARITY_HIGH ? 0 : 1; + + error = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi); + if (error == -EEXIST) { + if ((isrc->xi_edgetrigger && (trig != INTR_TRIGGER_EDGE)) || + (isrc->xi_activehi && (pol != INTR_POLARITY_HIGH))) + panic("unable to reconfigure interrupt IRQ#%d", + isrc->xi_pirq); + error = 0; + } + if (error) + panic("unable to configure IRQ#%d\n", isrc->xi_pirq); + + isrc->xi_configured = 1; + isrc->xi_activehi = pol == INTR_POLARITY_HIGH ? 1 : 0; + isrc->xi_edgetrigger = trig == INTR_POLARITY_HIGH ? 1 : 0; + + return (0); } /*--------------------------- Public Functions -------------------------------*/ _at__at_ -1190,6 +1318,50 _at__at_ xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu, } int +xen_register_pirq(int vector, int activehi, int edgetrigger) +{ + struct physdev_map_pirq map_pirq; + struct physdev_irq alloc_pirq; + struct xenisrc *isrc; + int error; + + if (vector == 0) + return (EINVAL); + + if (bootverbose) + printf("xen: register IRQ#%d\n", vector); + + map_pirq.domid = DOMID_SELF; + map_pirq.type = MAP_PIRQ_TYPE_GSI; + map_pirq.index = vector; + map_pirq.pirq = vector; + + error = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_pirq); + if (error) { + printf("xen: unable to map IRQ#%d\n", vector); + return (error); + } + + alloc_pirq.irq = vector; + alloc_pirq.vector = 0; + error = HYPERVISOR_physdev_op(PHYSDEVOP_alloc_irq_vector, &alloc_pirq); + if (error) { + printf("xen: unable to alloc PIRQ for IRQ#%d\n", vector); + return (error); + } + + mtx_lock(&xen_intr_isrc_lock); + isrc = xen_intr_alloc_isrc(EVTCHN_TYPE_PIRQ, vector); + mtx_unlock(&xen_intr_isrc_lock); + KASSERT((isrc != NULL), ("xen: unable to allocate isrc for interrupt")); + isrc->xi_pirq = vector; + isrc->xi_activehi = activehi; + isrc->xi_edgetrigger = edgetrigger; + + return (0); +} + +int xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...) { char descr[MAXCOMLEN + 1]; diff --git a/sys/xen/xen_intr.h b/sys/xen/xen_intr.h index 3b339a5..eda5fdf 100644 --- a/sys/xen/xen_intr.h +++ b/sys/xen/xen_intr.h _at__at_ -159,6 +159,17 _at__at_ int xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu, xen_intr_handle_t *handlep); /** + * Register a physical interrupt vector and setup the interrupt source. + * + * \param vector The global vector to use. + * \param activehi Default polarity of the interrupt. + * \param edgetrigger Default trigger method. + * + * \returns 0 on success, otherwise an errno. + */ +int xen_register_pirq(int vector, int activehi, int edgetrigger); + +/** * Unbind an interrupt handler from its interrupt source. * * \param handlep A pointer to the opaque handle that was initialized -- 1.7.7.5 (Apple Git-26)Received on Tue Dec 24 2013 - 10:22:43 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:45 UTC