[PATCH RFC 04/13] xen: implement basic PIRQ support for Dom0
Roger Pau Monne
roger.pau at citrix.com
Tue Dec 24 11:22:43 UTC 2013
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
@@ -104,6 +104,8 @@ 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;
@@ -111,6 +113,9 @@ 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;
@@ -136,6 +141,9 @@ 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.
@@ -163,12 +171,12 @@ 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
};
@@ -282,11 +290,10 @@ 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"));
@@ -297,12 +304,19 @@ 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);
@@ -388,7 +402,7 @@ 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);
@@ -592,6 +606,10 @@ 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);
}
@@ -925,6 +943,9 @@ 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);
}
/*
@@ -966,8 +987,115 @@ 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 -------------------------------*/
@@ -1190,6 +1318,50 @@ 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
@@ -159,6 +159,17 @@ 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)
More information about the freebsd-current
mailing list