[PATCH RFC 04/13] xen: implement basic PIRQ support for Dom0

From: Roger Pau Monne <roger.pau_at_citrix.com>
Date: Tue, 24 Dec 2013 12:20:53 +0100
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