From 5b27c2845c822d6cf78464927d3a7f889c27a78d Mon Sep 17 00:00:00 2001
From: Roger Pau Monne <roger.pau@citrix.com>
Date: Thu, 19 Jun 2014 13:05:53 +0200
Subject: [PATCH v5 01/29] xen: implement event channel PIRQ support

This allows Dom0 to manage physical hardware, redirecting the
physical interrupts to event channels.

Approved by: xxx
Sponsored by: Citrix Systems R&D

x86/xen/xen_intr.c:
 - Expand struct xenisrc to hold the level and triggering of PIRQ
   event channels.
 - Implement missing methods in xen_intr_pirq_pic.
 - Allow xen_intr_alloc_isrc to take a vector parameter that globally
   identifies the interrupt. This is only used for PIRQs that are
   bound to a specific hardware IRQ.
 - Introduce xen_register_pirq used to register IO APIC legacy PIRQ
   interrupts.
 - Add support for the dynamic PIRQ EOI map, this shared memory is
   modified by Xen (if it suppoorts that feature), and notifies the
   guest if an EOI is needed or not. If it's not available fall back
   to the old implementation using PHYSDEVOP_irq_status_query.

xen/xen_intr.h:
 - Add prototype for xen_register_pirq.
---
 sys/x86/xen/xen_intr.c |  208 +++++++++++++++++++++++++++++++++++++++++++++---
 sys/xen/xen_intr.h     |   12 +++
 2 files changed, 208 insertions(+), 12 deletions(-)

diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c
index 18f88c2..bf94168 100644
--- a/sys/x86/xen/xen_intr.c
+++ b/sys/x86/xen/xen_intr.c
@@ -104,6 +104,9 @@ DPCPU_DECLARE(struct vcpu_info *, vcpu_info);
 
 #define is_valid_evtchn(x)	((x) != 0)
 
+#define	EEXIST	17	/* Xen "already exists" error */
+#define	XEN_ALLOCATE_VECTOR	0 /* Allocate a vector for this event channel */
+
 struct xenisrc {
 	struct intsrc	xi_intsrc;
 	enum evtchn_type xi_type;
@@ -113,8 +116,9 @@ struct xenisrc {
 	int		xi_pirq;
 	int		xi_virq;
 	u_int		xi_close:1;	/* close on unbind? */
-	u_int		xi_needs_eoi:1;
 	u_int		xi_shared:1;	/* Shared with other domains. */
+	u_int 		xi_activehi:1;
+	u_int 		xi_edgetrigger:1;
 };
 
 #define ARRAY_SIZE(a)	(sizeof(a) / sizeof(a[0]))
@@ -136,6 +140,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,18 +170,20 @@ 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
 };
 
 static struct mtx	xen_intr_isrc_lock;
 static int		xen_intr_isrc_count;
 static struct xenisrc  *xen_intr_port_to_isrc[NR_EVENT_CHANNELS];
+static unsigned long	*xen_intr_pirq_eoi_map;
+boolean_t		xen_intr_pirq_eoi_map_enabled;
 
 /*------------------------- Private Functions --------------------------------*/
 /**
@@ -282,11 +291,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 +305,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 +403,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, XEN_ALLOCATE_VECTOR);
 		if (isrc == NULL) {
 			mtx_unlock(&xen_intr_isrc_lock);
 			return (ENOSPC);
@@ -570,7 +585,8 @@ static int
 xen_intr_init(void *dummy __unused)
 {
 	struct xen_intr_pcpu_data *pcpu;
-	int i;
+	struct physdev_pirq_eoi_gmfn eoi_gmfn;
+	int i, rc;
 
 	if (!xen_domain())
 		return (0);
@@ -591,7 +607,20 @@ xen_intr_init(void *dummy __unused)
 		xen_intr_intrcnt_add(i);
 	}
 
+	/* Try to register PIRQ EOI map */
+	xen_intr_pirq_eoi_map = malloc(PAGE_SIZE, M_XENINTR, M_WAITOK | M_ZERO);
+	eoi_gmfn.gmfn = atop(vtophys(xen_intr_pirq_eoi_map));
+	rc = HYPERVISOR_physdev_op(PHYSDEVOP_pirq_eoi_gmfn_v2, &eoi_gmfn);
+	if (rc != 0 && bootverbose)
+		printf("Xen interrupts: unable to register PIRQ EOI map\n");
+	else
+		xen_intr_pirq_eoi_map_enabled = true;
+
 	intr_register_pic(&xen_intr_pic);
+	intr_register_pic(&xen_intr_pirq_pic);
+
+	if (bootverbose)
+		printf("Xen interrupt system initialized\n");
 
 	return (0);
 }
@@ -916,6 +945,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);
 }
 
 /*
@@ -944,7 +976,7 @@ xen_intr_pirq_eoi_source(struct intsrc *base_isrc)
 
 	/* XXX Use shared page of flags for this. */
 	isrc = (struct xenisrc *)base_isrc;
-	if (isrc->xi_needs_eoi != 0) {
+	if (test_bit(isrc->xi_pirq, xen_intr_pirq_eoi_map)) {
 		struct physdev_eoi eoi = { .irq = isrc->xi_pirq };
 
 		(void)HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi);
@@ -957,8 +989,116 @@ 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 (!xen_intr_pirq_eoi_map_enabled) {
+		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) {
+			/*
+			 * Since the dynamic PIRQ EOI map is not available
+			 * mark the PIRQ as needing EOI unconditionally.
+			 */
+			set_bit(isrc->xi_pirq, xen_intr_pirq_eoi_map);
+		}
+	}
+
+	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;
+
+	evtchn_mask_port(isrc->xi_port);
+
+	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_activehi = pol == INTR_POLARITY_HIGH ? 1 : 0;
+	isrc->xi_edgetrigger = trig == INTR_TRIGGER_EDGE ? 1 : 0;
+
+	return (0);
 }
 
 /*--------------------------- Public Functions -------------------------------*/
@@ -1181,6 +1321,50 @@ xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu,
 }
 
 int
+xen_register_pirq(int vector, enum intr_trigger trig, enum intr_polarity pol)
+{
+	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 = pol == INTR_POLARITY_HIGH ? 1 : 0;
+	isrc->xi_edgetrigger = trig == INTR_TRIGGER_EDGE ? 1 : 0;
+
+	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..a1ff666 100644
--- a/sys/xen/xen_intr.h
+++ b/sys/xen/xen_intr.h
@@ -159,6 +159,18 @@ 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 trig          Default trigger method.
+ * \param pol           Default polarity of the interrupt.
+ *
+ * \returns  0 on success, otherwise an errno.
+ */
+int xen_register_pirq(int vector, enum intr_trigger trig,
+	enum intr_polarity pol);
+
+/**
  * 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)

