All of lore.kernel.org
 help / color / mirror / Atom feed
From: Etienne Carriere <etienne.carriere@linaro.org>
To: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org,
	Etienne Carriere <etienne.carriere@linaro.org>,
	Jens Wiklander <jens.wiklander@linaro.org>,
	Marc Zyngier <maz@kernel.org>, Sumit Garg <sumit.garg@linaro.org>,
	Pascal Paillet <p.paillet@foss.st.com>,
	Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Subject: [PATCH v3 2/3] optee: multiplex tee interrupt events on optee async notif irq
Date: Wed, 15 Mar 2023 12:32:00 +0100	[thread overview]
Message-ID: <20230315113201.1343781-2-etienne.carriere@linaro.org> (raw)
In-Reply-To: <20230315113201.1343781-1-etienne.carriere@linaro.org>

Implements an irqchip in optee driver for the interrupt events notified
from OP-TEE world to the Linux OS. Optee registers up to 1 interrupt
controller and identifies each line with a line number from 0 to
UINT16_MAX. The identifiers and meaning of the interrupt line number
are specific to the platform and shall be found in the OP-TEE platform
documentation

There already exist an optee asynchronous notification mechanisl using
an irq for OP-TEE to signal its optee driver in Linux of a pending
interrupt event. The implementation binds each event to the awaking of
a thread waiting to call the TEE. The interrupt notification service
added by this change allows TEE to send interrupt signals to Linux kernel
on secure event occurrences which end consumer is a Linux sub-system.

When optee driver initializes, TEE tells whether it supports interrupt
notification services or not.

OP-TEE SMC ABI defines 2 SMC function IDs for non-secure world to
manage interrupt events.

SMC function OPTEE_SMC_GET_NOTIF_ITR allows non-secure world to
retrieve pending interrupts by grapes up to 5 lines. For efficiency,
the function also reports whether there are pending async values
targeting suspended threaded sequences execution and whether TEE
has background threaded work to do.

SMC function OPTEE_SMC_NOTIF_ITR_SET_MASK allow Linux irq consumer
to mask/unmask the irq line.

Cc: Jens Wiklander <jens.wiklander@linaro.org>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Sumit Garg <sumit.garg@linaro.org>

Co-developed-by: Pascal Paillet <p.paillet@foss.st.com>
Signed-off-by: Pascal Paillet <p.paillet@foss.st.com>
Co-developed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
---
Changes since v2:
- Renamed it_notif/IT_NOTIF to itr_notif/ITR_NOTIF.
- Updated retrieve_pending_irqs() loop from review comments.
- Changed OP-TEE ABI function CONFIGURE_IT to a only act on interrupt
  mask state. The function is renamed xxx_NOTIF_ITR_SET_MASK.
- Don't set OPTEE_SMC_SEC_CAP_IT_NOTIF among non-secure capabilities, the
  capability is related only to whether secure world supports interrupt
  notification to normal world.
- Fixed inline description from review comments.
- Rephrased commit message and added a sentence about optee irq line
  number values and meaning.

Changes since v1:
- Removed dependency on optee per-cpu irq notification.
- Change SMC function ID API to retrieves up to 5 optee irq events,
  the optee bottom half event and returns if other async notifications
  are pending, in a single invocation.
- Implement only mask/unmask irqchip handlers with a 2nd SMC function
  to mask/unmask a optee irq in OP-TEE world from an interrupt context.
- Added Cc: tags.
---
 drivers/tee/optee/optee_private.h |  10 ++
 drivers/tee/optee/optee_smc.h     |  89 ++++++++++++++++-
 drivers/tee/optee/smc_abi.c       | 152 +++++++++++++++++++++++++++++-
 3 files changed, 245 insertions(+), 6 deletions(-)

diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 04ae58892608..f467409e02e9 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -94,11 +94,21 @@ struct optee_supp {
 	struct completion reqs_c;
 };
 
+/*
+ * struct optee_smc - optee smc communication struct
+ * @invoke_fn		handler function to invoke secure monitor
+ * @memremaped_shm	virtual address of memory in shared memory pool
+ * @sec_caps:		secure world capabilities defined by
+ *			OPTEE_SMC_SEC_CAP_* in optee_smc.h
+ * @notif_irq		interrupt used as async notification by OP-TEE or 0
+ * @domain		interrupt domain registered by OP-TEE driver
+ */
 struct optee_smc {
 	optee_invoke_fn *invoke_fn;
 	void *memremaped_shm;
 	u32 sec_caps;
 	unsigned int notif_irq;
+	struct irq_domain *domain;
 };
 
 /**
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
index 73b5e7760d10..3723832771db 100644
--- a/drivers/tee/optee/optee_smc.h
+++ b/drivers/tee/optee/optee_smc.h
@@ -226,7 +226,8 @@ struct optee_smc_get_shm_config_result {
  * a3	Bit[7:0]: Number of parameters needed for RPC to be supplied
  *		  as the second MSG arg struct for
  *		  OPTEE_SMC_CALL_WITH_ARG
- *	Bit[31:8]: Reserved (MBZ)
+ *	Bit[23:8]: The maximum interrupt event notification number
+ *	Bit[31:24]: Reserved (MBZ)
  * a4-7	Preserved
  *
  * Error return register usage:
@@ -254,6 +255,11 @@ struct optee_smc_get_shm_config_result {
 #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF		BIT(5)
 /* Secure world supports pre-allocating RPC arg struct */
 #define OPTEE_SMC_SEC_CAP_RPC_ARG		BIT(6)
+/* Secure world supports interrupt events notification to normal world */
+#define OPTEE_SMC_SEC_CAP_ITR_NOTIF		BIT(7)
+
+#define OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_MASK	GENMASK(23, 8)
+#define OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_SHIFT	8
 
 #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES	9
 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \
@@ -426,6 +432,87 @@ struct optee_smc_disable_shm_cache_result {
 /* See OPTEE_SMC_CALL_WITH_REGD_ARG above */
 #define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG	19
 
+/*
+ * Retrieve up to 5 pending interrupt events notified by OP-TEE world,
+ * whether bottom half is to be scheduled and if there are pending
+ * async event for waiting threads, all this since the last call of
+ * this function.
+ *
+ * Interrupts notified by OP-TEE are identified by a number from 0 to
+ * the interrupt number max value for that platform. Values for each
+ * interrupt number are platform specific and shall be found in the
+ * OP-TEE platform documentation.
+ *
+ * OP-TEE keeps a record of all posted interrupt notification events.
+ * When the async notif interrupt is received by normal world,
+ * this function should be called until all pended interrupt events
+ * have been retrieved. When an interrupt event is retrieved, it is
+ * cleared from the record in OP-TEE world. When do bottom half event
+ * is retrieved (async value 0), it is also cleared from its related
+ * record in OP-TEE world.
+ *
+ * It is expected that this function is called from an interrupt handler
+ * in normal world.
+ *
+ * Call requests usage:
+ * a0	SMC Function ID, OPTEE_SMC_GET_NOTIF_ITR
+ * a1-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0	OPTEE_SMC_RETURN_OK
+ * a1	Bit[7:0]: Number of pending interrupt carried in a1..a5
+ *	Bit[8]: OPTEE_SMC_NOTIF_ITR_PENDING if other interrupt(s) are pending
+ *	Bit[9]: OPTEE_SMC_NOTIF_ASYNC_PENDING if a threaded event is pending
+ *		excluding bottom half notification that is retrieved in Bit[10].
+ *	Bit[10]: OPTEE_SMC_NOTIF_DO_BOTTOM_HALF if retrieved bottom half notif
+ *	Bit[15:11]: Reserved for future use, MBZ
+ *	Bit[31:16]: Pending interrupt line value if a1 & 0xFF >= 1
+ * a2	Bit[15:0]:  Pending interrupt line value if a1 & 0xFF >= 2
+ *	Bit[31:16]: Pending interrupt line value if a1 & 0xFF >= 3
+ * a3	Bit[15:0]:  Pending interrupt line value if a1 & 0xFF >= 4
+ *	Bit[31:16]: Pending interrupt line value if a1 & 0xFF == 5
+ * a4-7 Preserved
+ *
+ * Not supported return register usage:
+ * a0	OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-7	Preserved
+ */
+#define OPTEE_SMC_NOTIF_ITR_COUNT_MASK		GENMASK(7, 0)
+#define OPTEE_SMC_NOTIF_ITR_PENDING		BIT(8)
+#define OPTEE_SMC_NOTIF_VALUE_PENDING		BIT(9)
+#define OPTEE_SMC_NOTIF_DO_BOTTOM_HALF		BIT(10)
+
+#define OPTEE_SMC_FUNCID_GET_NOTIF_ITR		20
+#define OPTEE_SMC_GET_NOTIF_ITR \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_NOTIF_ITR)
+
+/*
+ * Mask/unmask an interrupt notification
+ *
+ * Call requests usage:
+ * a0	SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_MASK
+ * a1	Interrupt number identifier value
+ * a2	1 to mask, 0 to unmask the interrupt notification.
+ * a3-6	Reserved for future use, MBZ
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0	OPTEE_SMC_RETURN_OK
+ * a1-7	Preserved
+ *
+ * Invalid command with provided arguments return usage:
+ * a0	OPTEE_SMC_RETURN_EBADCMD
+ * a1-7	Preserved
+ *
+ * Not supported return register usage:
+ * a0	OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-7	Preserved
+ */
+#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_MASK	21
+#define OPTEE_SMC_NOTIF_ITR_SET_MASK \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_MASK)
+
 /*
  * Resume from RPC (for example after processing a foreign interrupt)
  *
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index a1c1fa1a9c28..6e6c6979abfd 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -977,6 +977,65 @@ static int optee_smc_stop_async_notif(struct tee_context *ctx)
  * 5. Asynchronous notification
  */
 
+static void optee_itr_notif_mask(struct irq_data *d)
+{
+	struct optee *optee = d->domain->host_data;
+	struct arm_smccc_res res = { };
+
+	optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, d->hwirq, 1,
+			     0, 0, 0, 0, 0, &res);
+}
+
+static void optee_itr_notif_unmask(struct irq_data *d)
+{
+	struct optee *optee = d->domain->host_data;
+	struct arm_smccc_res res = { };
+
+	optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, d->hwirq, 0,
+			     0, 0, 0, 0, 0, &res);
+}
+
+static struct irq_chip optee_irq_chip = {
+	.name = "optee-it",
+	.irq_mask = optee_itr_notif_mask,
+	.irq_unmask = optee_itr_notif_unmask,
+};
+
+static int optee_itr_alloc(struct irq_domain *d, unsigned int virq,
+			  unsigned int nr_irqs, void *data)
+{
+	struct irq_fwspec *fwspec = data;
+	irq_hw_number_t hwirq;
+
+	hwirq = fwspec->param[0];
+
+	irq_domain_set_hwirq_and_chip(d, virq, hwirq, &optee_irq_chip,
+				      d->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops optee_irq_domain_ops = {
+	.alloc = optee_itr_alloc,
+	.free = irq_domain_free_irqs_common,
+};
+
+static int optee_irq_domain_init(struct platform_device *pdev,
+				 struct optee *optee, u_int max_it)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+
+	optee->smc.domain = irq_domain_add_linear(np, max_it,
+						  &optee_irq_domain_ops, optee);
+	if (!optee->smc.domain) {
+		dev_err(dev, "Unable to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
 static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid,
 				 bool *value_pending)
 {
@@ -991,6 +1050,61 @@ static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid,
 	return res.a1;
 }
 
+static void forward_irq(struct optee *optee, unsigned int itr_num)
+{
+	if (generic_handle_domain_irq(optee->smc.domain, itr_num)) {
+		struct arm_smccc_res res = { };
+
+		pr_err("No consumer for optee irq %u, masked\n", itr_num);
+		optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, itr_num, 1,
+				     0, 0, 0, 0, 0, &res);
+	}
+}
+
+static void retrieve_pending_irqs(struct optee *optee, bool *async_pending,
+				  bool *do_bottom_half)
+
+{
+	struct arm_smccc_res res;
+	bool irq_pending;
+	ssize_t cnt;
+	const unsigned int lsb_mask = GENMASK(15, 0);
+	const unsigned int msb_shift = 16;
+
+	*do_bottom_half = false;
+
+	do {
+		optee->smc.invoke_fn(OPTEE_SMC_GET_NOTIF_ITR, 0, 0, 0, 0, 0, 0,
+				     0, &res);
+
+		if (res.a0)
+			return;
+
+		if (res.a1 & OPTEE_SMC_NOTIF_DO_BOTTOM_HALF)
+			*do_bottom_half = true;
+
+		irq_pending = res.a1 & OPTEE_SMC_NOTIF_ITR_PENDING;
+		cnt = res.a1 & OPTEE_SMC_NOTIF_ITR_COUNT_MASK;
+		if (cnt > 5 || (!cnt && irq_pending)) {
+			WARN_ONCE(0, "Unexpected irq notif count %zi\n", cnt);
+			break;
+		}
+
+		if (cnt > 0)
+			forward_irq(optee, res.a1 >> msb_shift);
+		if (cnt > 1)
+			forward_irq(optee, res.a2 & lsb_mask);
+		if (cnt > 2)
+			forward_irq(optee, res.a2 >> msb_shift);
+		if (cnt > 3)
+			forward_irq(optee, res.a3 & lsb_mask);
+		if (cnt == 5)
+			forward_irq(optee, res.a3 >> msb_shift);
+	} while (irq_pending);
+
+	*async_pending = res.a1 & OPTEE_SMC_NOTIF_VALUE_PENDING;
+}
+
 static irqreturn_t notif_irq_handler(int irq, void *dev_id)
 {
 	struct optee *optee = dev_id;
@@ -999,9 +1113,14 @@ static irqreturn_t notif_irq_handler(int irq, void *dev_id)
 	bool value_pending;
 	u32 value;
 
-	do {
-		value = get_async_notif_value(optee->smc.invoke_fn,
-					      &value_valid, &value_pending);
+	if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF)
+		retrieve_pending_irqs(optee, &value_pending, &do_bottom_half);
+	else
+		value_pending = true;
+
+	while (value_pending) {
+		value = get_async_notif_value(optee->smc.invoke_fn, &value_valid,
+					      &value_pending);
 		if (!value_valid)
 			break;
 
@@ -1009,10 +1128,11 @@ static irqreturn_t notif_irq_handler(int irq, void *dev_id)
 			do_bottom_half = true;
 		else
 			optee_notif_send(optee, value);
-	} while (value_pending);
+	};
 
 	if (do_bottom_half)
 		return IRQ_WAKE_THREAD;
+
 	return IRQ_HANDLED;
 }
 
@@ -1048,6 +1168,9 @@ static void optee_smc_notif_uninit_irq(struct optee *optee)
 			free_irq(optee->smc.notif_irq, optee);
 			irq_dispose_mapping(optee->smc.notif_irq);
 		}
+
+		if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF)
+			irq_domain_remove(optee->smc.domain);
 	}
 }
 
@@ -1187,6 +1310,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
 
 static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
 					    u32 *sec_caps, u32 *max_notif_value,
+					    u32 *max_notif_it,
 					    unsigned int *rpc_param_count)
 {
 	union {
@@ -1219,6 +1343,13 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
 	else
 		*rpc_param_count = 0;
 
+	if (*sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF)
+		*max_notif_it = (res.result.data &
+				 OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_MASK) >>
+				OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_SHIFT;
+	else
+		*max_notif_it = 0;
+
 	return true;
 }
 
@@ -1364,6 +1495,7 @@ static int optee_probe(struct platform_device *pdev)
 	struct tee_device *teedev;
 	struct tee_context *ctx;
 	u32 max_notif_value;
+	u32 max_notif_it;
 	u32 arg_cache_flags;
 	u32 sec_caps;
 	int rc;
@@ -1385,7 +1517,7 @@ static int optee_probe(struct platform_device *pdev)
 	}
 
 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps,
-					     &max_notif_value,
+					     &max_notif_value, &max_notif_it,
 					     &rpc_param_count)) {
 		pr_warn("capabilities mismatch\n");
 		return -EINVAL;
@@ -1506,6 +1638,16 @@ static int optee_probe(struct platform_device *pdev)
 			irq_dispose_mapping(irq);
 			goto err_notif_uninit;
 		}
+
+		if (sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF) {
+			rc = optee_irq_domain_init(pdev, optee, max_notif_it);
+			if (rc) {
+				free_irq(optee->smc.notif_irq, optee);
+				irq_dispose_mapping(irq);
+				goto err_notif_uninit;
+			}
+		}
+
 		enable_async_notif(optee->smc.invoke_fn);
 		pr_info("Asynchronous notifications enabled\n");
 	}
-- 
2.25.1


WARNING: multiple messages have this Message-ID (diff)
From: Etienne Carriere <etienne.carriere@linaro.org>
To: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org,
	Etienne Carriere <etienne.carriere@linaro.org>,
	Jens Wiklander <jens.wiklander@linaro.org>,
	Marc Zyngier <maz@kernel.org>, Sumit Garg <sumit.garg@linaro.org>,
	Pascal Paillet <p.paillet@foss.st.com>,
	Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Subject: [PATCH v3 2/3] optee: multiplex tee interrupt events on optee async notif irq
Date: Wed, 15 Mar 2023 12:32:00 +0100	[thread overview]
Message-ID: <20230315113201.1343781-2-etienne.carriere@linaro.org> (raw)
In-Reply-To: <20230315113201.1343781-1-etienne.carriere@linaro.org>

Implements an irqchip in optee driver for the interrupt events notified
from OP-TEE world to the Linux OS. Optee registers up to 1 interrupt
controller and identifies each line with a line number from 0 to
UINT16_MAX. The identifiers and meaning of the interrupt line number
are specific to the platform and shall be found in the OP-TEE platform
documentation

There already exist an optee asynchronous notification mechanisl using
an irq for OP-TEE to signal its optee driver in Linux of a pending
interrupt event. The implementation binds each event to the awaking of
a thread waiting to call the TEE. The interrupt notification service
added by this change allows TEE to send interrupt signals to Linux kernel
on secure event occurrences which end consumer is a Linux sub-system.

When optee driver initializes, TEE tells whether it supports interrupt
notification services or not.

OP-TEE SMC ABI defines 2 SMC function IDs for non-secure world to
manage interrupt events.

SMC function OPTEE_SMC_GET_NOTIF_ITR allows non-secure world to
retrieve pending interrupts by grapes up to 5 lines. For efficiency,
the function also reports whether there are pending async values
targeting suspended threaded sequences execution and whether TEE
has background threaded work to do.

SMC function OPTEE_SMC_NOTIF_ITR_SET_MASK allow Linux irq consumer
to mask/unmask the irq line.

Cc: Jens Wiklander <jens.wiklander@linaro.org>
Cc: Marc Zyngier <maz@kernel.org>
Cc: Sumit Garg <sumit.garg@linaro.org>

Co-developed-by: Pascal Paillet <p.paillet@foss.st.com>
Signed-off-by: Pascal Paillet <p.paillet@foss.st.com>
Co-developed-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org>
---
Changes since v2:
- Renamed it_notif/IT_NOTIF to itr_notif/ITR_NOTIF.
- Updated retrieve_pending_irqs() loop from review comments.
- Changed OP-TEE ABI function CONFIGURE_IT to a only act on interrupt
  mask state. The function is renamed xxx_NOTIF_ITR_SET_MASK.
- Don't set OPTEE_SMC_SEC_CAP_IT_NOTIF among non-secure capabilities, the
  capability is related only to whether secure world supports interrupt
  notification to normal world.
- Fixed inline description from review comments.
- Rephrased commit message and added a sentence about optee irq line
  number values and meaning.

Changes since v1:
- Removed dependency on optee per-cpu irq notification.
- Change SMC function ID API to retrieves up to 5 optee irq events,
  the optee bottom half event and returns if other async notifications
  are pending, in a single invocation.
- Implement only mask/unmask irqchip handlers with a 2nd SMC function
  to mask/unmask a optee irq in OP-TEE world from an interrupt context.
- Added Cc: tags.
---
 drivers/tee/optee/optee_private.h |  10 ++
 drivers/tee/optee/optee_smc.h     |  89 ++++++++++++++++-
 drivers/tee/optee/smc_abi.c       | 152 +++++++++++++++++++++++++++++-
 3 files changed, 245 insertions(+), 6 deletions(-)

diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 04ae58892608..f467409e02e9 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -94,11 +94,21 @@ struct optee_supp {
 	struct completion reqs_c;
 };
 
+/*
+ * struct optee_smc - optee smc communication struct
+ * @invoke_fn		handler function to invoke secure monitor
+ * @memremaped_shm	virtual address of memory in shared memory pool
+ * @sec_caps:		secure world capabilities defined by
+ *			OPTEE_SMC_SEC_CAP_* in optee_smc.h
+ * @notif_irq		interrupt used as async notification by OP-TEE or 0
+ * @domain		interrupt domain registered by OP-TEE driver
+ */
 struct optee_smc {
 	optee_invoke_fn *invoke_fn;
 	void *memremaped_shm;
 	u32 sec_caps;
 	unsigned int notif_irq;
+	struct irq_domain *domain;
 };
 
 /**
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h
index 73b5e7760d10..3723832771db 100644
--- a/drivers/tee/optee/optee_smc.h
+++ b/drivers/tee/optee/optee_smc.h
@@ -226,7 +226,8 @@ struct optee_smc_get_shm_config_result {
  * a3	Bit[7:0]: Number of parameters needed for RPC to be supplied
  *		  as the second MSG arg struct for
  *		  OPTEE_SMC_CALL_WITH_ARG
- *	Bit[31:8]: Reserved (MBZ)
+ *	Bit[23:8]: The maximum interrupt event notification number
+ *	Bit[31:24]: Reserved (MBZ)
  * a4-7	Preserved
  *
  * Error return register usage:
@@ -254,6 +255,11 @@ struct optee_smc_get_shm_config_result {
 #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF		BIT(5)
 /* Secure world supports pre-allocating RPC arg struct */
 #define OPTEE_SMC_SEC_CAP_RPC_ARG		BIT(6)
+/* Secure world supports interrupt events notification to normal world */
+#define OPTEE_SMC_SEC_CAP_ITR_NOTIF		BIT(7)
+
+#define OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_MASK	GENMASK(23, 8)
+#define OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_SHIFT	8
 
 #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES	9
 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \
@@ -426,6 +432,87 @@ struct optee_smc_disable_shm_cache_result {
 /* See OPTEE_SMC_CALL_WITH_REGD_ARG above */
 #define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG	19
 
+/*
+ * Retrieve up to 5 pending interrupt events notified by OP-TEE world,
+ * whether bottom half is to be scheduled and if there are pending
+ * async event for waiting threads, all this since the last call of
+ * this function.
+ *
+ * Interrupts notified by OP-TEE are identified by a number from 0 to
+ * the interrupt number max value for that platform. Values for each
+ * interrupt number are platform specific and shall be found in the
+ * OP-TEE platform documentation.
+ *
+ * OP-TEE keeps a record of all posted interrupt notification events.
+ * When the async notif interrupt is received by normal world,
+ * this function should be called until all pended interrupt events
+ * have been retrieved. When an interrupt event is retrieved, it is
+ * cleared from the record in OP-TEE world. When do bottom half event
+ * is retrieved (async value 0), it is also cleared from its related
+ * record in OP-TEE world.
+ *
+ * It is expected that this function is called from an interrupt handler
+ * in normal world.
+ *
+ * Call requests usage:
+ * a0	SMC Function ID, OPTEE_SMC_GET_NOTIF_ITR
+ * a1-6	Not used
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0	OPTEE_SMC_RETURN_OK
+ * a1	Bit[7:0]: Number of pending interrupt carried in a1..a5
+ *	Bit[8]: OPTEE_SMC_NOTIF_ITR_PENDING if other interrupt(s) are pending
+ *	Bit[9]: OPTEE_SMC_NOTIF_ASYNC_PENDING if a threaded event is pending
+ *		excluding bottom half notification that is retrieved in Bit[10].
+ *	Bit[10]: OPTEE_SMC_NOTIF_DO_BOTTOM_HALF if retrieved bottom half notif
+ *	Bit[15:11]: Reserved for future use, MBZ
+ *	Bit[31:16]: Pending interrupt line value if a1 & 0xFF >= 1
+ * a2	Bit[15:0]:  Pending interrupt line value if a1 & 0xFF >= 2
+ *	Bit[31:16]: Pending interrupt line value if a1 & 0xFF >= 3
+ * a3	Bit[15:0]:  Pending interrupt line value if a1 & 0xFF >= 4
+ *	Bit[31:16]: Pending interrupt line value if a1 & 0xFF == 5
+ * a4-7 Preserved
+ *
+ * Not supported return register usage:
+ * a0	OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-7	Preserved
+ */
+#define OPTEE_SMC_NOTIF_ITR_COUNT_MASK		GENMASK(7, 0)
+#define OPTEE_SMC_NOTIF_ITR_PENDING		BIT(8)
+#define OPTEE_SMC_NOTIF_VALUE_PENDING		BIT(9)
+#define OPTEE_SMC_NOTIF_DO_BOTTOM_HALF		BIT(10)
+
+#define OPTEE_SMC_FUNCID_GET_NOTIF_ITR		20
+#define OPTEE_SMC_GET_NOTIF_ITR \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_GET_NOTIF_ITR)
+
+/*
+ * Mask/unmask an interrupt notification
+ *
+ * Call requests usage:
+ * a0	SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_MASK
+ * a1	Interrupt number identifier value
+ * a2	1 to mask, 0 to unmask the interrupt notification.
+ * a3-6	Reserved for future use, MBZ
+ * a7	Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * a0	OPTEE_SMC_RETURN_OK
+ * a1-7	Preserved
+ *
+ * Invalid command with provided arguments return usage:
+ * a0	OPTEE_SMC_RETURN_EBADCMD
+ * a1-7	Preserved
+ *
+ * Not supported return register usage:
+ * a0	OPTEE_SMC_RETURN_ENOTAVAIL
+ * a1-7	Preserved
+ */
+#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_MASK	21
+#define OPTEE_SMC_NOTIF_ITR_SET_MASK \
+	OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_MASK)
+
 /*
  * Resume from RPC (for example after processing a foreign interrupt)
  *
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index a1c1fa1a9c28..6e6c6979abfd 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -977,6 +977,65 @@ static int optee_smc_stop_async_notif(struct tee_context *ctx)
  * 5. Asynchronous notification
  */
 
+static void optee_itr_notif_mask(struct irq_data *d)
+{
+	struct optee *optee = d->domain->host_data;
+	struct arm_smccc_res res = { };
+
+	optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, d->hwirq, 1,
+			     0, 0, 0, 0, 0, &res);
+}
+
+static void optee_itr_notif_unmask(struct irq_data *d)
+{
+	struct optee *optee = d->domain->host_data;
+	struct arm_smccc_res res = { };
+
+	optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, d->hwirq, 0,
+			     0, 0, 0, 0, 0, &res);
+}
+
+static struct irq_chip optee_irq_chip = {
+	.name = "optee-it",
+	.irq_mask = optee_itr_notif_mask,
+	.irq_unmask = optee_itr_notif_unmask,
+};
+
+static int optee_itr_alloc(struct irq_domain *d, unsigned int virq,
+			  unsigned int nr_irqs, void *data)
+{
+	struct irq_fwspec *fwspec = data;
+	irq_hw_number_t hwirq;
+
+	hwirq = fwspec->param[0];
+
+	irq_domain_set_hwirq_and_chip(d, virq, hwirq, &optee_irq_chip,
+				      d->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops optee_irq_domain_ops = {
+	.alloc = optee_itr_alloc,
+	.free = irq_domain_free_irqs_common,
+};
+
+static int optee_irq_domain_init(struct platform_device *pdev,
+				 struct optee *optee, u_int max_it)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+
+	optee->smc.domain = irq_domain_add_linear(np, max_it,
+						  &optee_irq_domain_ops, optee);
+	if (!optee->smc.domain) {
+		dev_err(dev, "Unable to add irq domain\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
 static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid,
 				 bool *value_pending)
 {
@@ -991,6 +1050,61 @@ static u32 get_async_notif_value(optee_invoke_fn *invoke_fn, bool *value_valid,
 	return res.a1;
 }
 
+static void forward_irq(struct optee *optee, unsigned int itr_num)
+{
+	if (generic_handle_domain_irq(optee->smc.domain, itr_num)) {
+		struct arm_smccc_res res = { };
+
+		pr_err("No consumer for optee irq %u, masked\n", itr_num);
+		optee->smc.invoke_fn(OPTEE_SMC_NOTIF_ITR_SET_MASK, itr_num, 1,
+				     0, 0, 0, 0, 0, &res);
+	}
+}
+
+static void retrieve_pending_irqs(struct optee *optee, bool *async_pending,
+				  bool *do_bottom_half)
+
+{
+	struct arm_smccc_res res;
+	bool irq_pending;
+	ssize_t cnt;
+	const unsigned int lsb_mask = GENMASK(15, 0);
+	const unsigned int msb_shift = 16;
+
+	*do_bottom_half = false;
+
+	do {
+		optee->smc.invoke_fn(OPTEE_SMC_GET_NOTIF_ITR, 0, 0, 0, 0, 0, 0,
+				     0, &res);
+
+		if (res.a0)
+			return;
+
+		if (res.a1 & OPTEE_SMC_NOTIF_DO_BOTTOM_HALF)
+			*do_bottom_half = true;
+
+		irq_pending = res.a1 & OPTEE_SMC_NOTIF_ITR_PENDING;
+		cnt = res.a1 & OPTEE_SMC_NOTIF_ITR_COUNT_MASK;
+		if (cnt > 5 || (!cnt && irq_pending)) {
+			WARN_ONCE(0, "Unexpected irq notif count %zi\n", cnt);
+			break;
+		}
+
+		if (cnt > 0)
+			forward_irq(optee, res.a1 >> msb_shift);
+		if (cnt > 1)
+			forward_irq(optee, res.a2 & lsb_mask);
+		if (cnt > 2)
+			forward_irq(optee, res.a2 >> msb_shift);
+		if (cnt > 3)
+			forward_irq(optee, res.a3 & lsb_mask);
+		if (cnt == 5)
+			forward_irq(optee, res.a3 >> msb_shift);
+	} while (irq_pending);
+
+	*async_pending = res.a1 & OPTEE_SMC_NOTIF_VALUE_PENDING;
+}
+
 static irqreturn_t notif_irq_handler(int irq, void *dev_id)
 {
 	struct optee *optee = dev_id;
@@ -999,9 +1113,14 @@ static irqreturn_t notif_irq_handler(int irq, void *dev_id)
 	bool value_pending;
 	u32 value;
 
-	do {
-		value = get_async_notif_value(optee->smc.invoke_fn,
-					      &value_valid, &value_pending);
+	if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF)
+		retrieve_pending_irqs(optee, &value_pending, &do_bottom_half);
+	else
+		value_pending = true;
+
+	while (value_pending) {
+		value = get_async_notif_value(optee->smc.invoke_fn, &value_valid,
+					      &value_pending);
 		if (!value_valid)
 			break;
 
@@ -1009,10 +1128,11 @@ static irqreturn_t notif_irq_handler(int irq, void *dev_id)
 			do_bottom_half = true;
 		else
 			optee_notif_send(optee, value);
-	} while (value_pending);
+	};
 
 	if (do_bottom_half)
 		return IRQ_WAKE_THREAD;
+
 	return IRQ_HANDLED;
 }
 
@@ -1048,6 +1168,9 @@ static void optee_smc_notif_uninit_irq(struct optee *optee)
 			free_irq(optee->smc.notif_irq, optee);
 			irq_dispose_mapping(optee->smc.notif_irq);
 		}
+
+		if (optee->smc.sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF)
+			irq_domain_remove(optee->smc.domain);
 	}
 }
 
@@ -1187,6 +1310,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn)
 
 static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
 					    u32 *sec_caps, u32 *max_notif_value,
+					    u32 *max_notif_it,
 					    unsigned int *rpc_param_count)
 {
 	union {
@@ -1219,6 +1343,13 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
 	else
 		*rpc_param_count = 0;
 
+	if (*sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF)
+		*max_notif_it = (res.result.data &
+				 OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_MASK) >>
+				OPTEE_SMC_SEC_CAP_MAX_NOTIF_ITR_SHIFT;
+	else
+		*max_notif_it = 0;
+
 	return true;
 }
 
@@ -1364,6 +1495,7 @@ static int optee_probe(struct platform_device *pdev)
 	struct tee_device *teedev;
 	struct tee_context *ctx;
 	u32 max_notif_value;
+	u32 max_notif_it;
 	u32 arg_cache_flags;
 	u32 sec_caps;
 	int rc;
@@ -1385,7 +1517,7 @@ static int optee_probe(struct platform_device *pdev)
 	}
 
 	if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps,
-					     &max_notif_value,
+					     &max_notif_value, &max_notif_it,
 					     &rpc_param_count)) {
 		pr_warn("capabilities mismatch\n");
 		return -EINVAL;
@@ -1506,6 +1638,16 @@ static int optee_probe(struct platform_device *pdev)
 			irq_dispose_mapping(irq);
 			goto err_notif_uninit;
 		}
+
+		if (sec_caps & OPTEE_SMC_SEC_CAP_ITR_NOTIF) {
+			rc = optee_irq_domain_init(pdev, optee, max_notif_it);
+			if (rc) {
+				free_irq(optee->smc.notif_irq, optee);
+				irq_dispose_mapping(irq);
+				goto err_notif_uninit;
+			}
+		}
+
 		enable_async_notif(optee->smc.invoke_fn);
 		pr_info("Asynchronous notifications enabled\n");
 	}
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2023-03-15 11:33 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-15 11:31 [PATCH v3 1/3] dt-bindings: arm: optee: add interrupt controller properties Etienne Carriere
2023-03-15 11:31 ` Etienne Carriere
2023-03-15 11:32 ` Etienne Carriere [this message]
2023-03-15 11:32   ` [PATCH v3 2/3] optee: multiplex tee interrupt events on optee async notif irq Etienne Carriere
2023-03-15 11:32 ` [PATCH v3 3/3] optee: add enable/disable/set_wake handlers to optee irqs Etienne Carriere
2023-03-15 11:32   ` Etienne Carriere
2023-03-16 13:45   ` Jens Wiklander
2023-03-16 13:45     ` Jens Wiklander
2023-03-17 17:07     ` Etienne Carriere
2023-03-17 17:07       ` Etienne Carriere

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230315113201.1343781-2-etienne.carriere@linaro.org \
    --to=etienne.carriere@linaro.org \
    --cc=fabrice.gasnier@foss.st.com \
    --cc=jens.wiklander@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=maz@kernel.org \
    --cc=p.paillet@foss.st.com \
    --cc=sumit.garg@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.