From 5534a462b7243860b8b670d793384b3663cf2551 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 23 Jan 2023 15:03:21 +0100 Subject: [PATCH 1/9] libutils: add bit_ffs_from() Adds bitstring function bit_ffs_from() that mimics bit_ffs() but looks from a start bit position given as argument, and defines bit_ffs() based on bit_ffs_from(). Signed-off-by: Etienne Carriere --- lib/libutils/ext/include/bitstring.h | 32 +++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/libutils/ext/include/bitstring.h b/lib/libutils/ext/include/bitstring.h index e160ae76f3e..bebf092a370 100644 --- a/lib/libutils/ext/include/bitstring.h +++ b/lib/libutils/ext/include/bitstring.h @@ -126,22 +126,34 @@ typedef unsigned char bitstr_t; } while (0) /* find first bit set in name */ -#define bit_ffs(name, nbits, value) do { \ - register bitstr_t *_name = (name); \ - register int _byte, _nbits = (nbits); \ - register int _stopbyte = _bit_byte(_nbits - 1), _value = -1; \ - if (_nbits > 0) \ - for (_byte = 0; _byte <= _stopbyte; ++_byte) \ - if (_name[_byte]) { \ - bitstr_t _lb; \ +#define bit_ffs_from(name, nbits, startbit, value) do { \ + bitstr_t *_name = (name); \ + int _byte = 0; \ + int _nbits = (nbits); \ + int _startbyte = _bit_byte(startbit); \ + int _stopbyte = _bit_byte(_nbits - 1), _value = -1; \ + bitstr_t _test_bit_mask = 0xff << ((startbit) % 8); \ + \ + if (_nbits > 0) { \ + for (_byte = _startbyte; _byte <= _stopbyte; ++_byte) { \ + if (_name[_byte] & _test_bit_mask) { \ + bitstr_t _lb = 0; \ + \ _value = _byte << 3; \ - for (_lb = _name[_byte]; !(_lb&0x1); \ - ++_value, _lb >>= 1); \ + for (_lb = _name[_byte] & _test_bit_mask; \ + !(_lb&0x1); ++_value, _lb >>= 1) \ + ; \ break; \ } \ + _test_bit_mask = 0xff; \ + } \ + } \ if (_value >= nbits) \ _value = -1; \ *(value) = _value; \ } while (0) +#define bit_ffs(name, nbits, value) \ + bit_ffs_from((name), (nbits), 0, (value)) + #endif /* !_SYS_BITSTRING_H_ */ From f1c66b141a4f0dd12627e88d4fc99dd382783faa Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 10 Jan 2023 15:03:13 +0100 Subject: [PATCH 2/9] core: notif: fix input comment typo Fixes inline comment typo in OP-TEE standard SMCs description and CFG_CORE_ASYNC_NOTIF switch description. Reviewed-by: Jens Wiklander Signed-off-by: Etienne Carriere --- core/arch/arm/include/sm/optee_smc.h | 2 +- mk/config.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/arch/arm/include/sm/optee_smc.h b/core/arch/arm/include/sm/optee_smc.h index cd45917d776..04a3839a2c1 100644 --- a/core/arch/arm/include/sm/optee_smc.h +++ b/core/arch/arm/include/sm/optee_smc.h @@ -530,7 +530,7 @@ * a0 OPTEE_SMC_RETURN_OK * a1 value * a2 Bit[0]: OPTEE_SMC_ASYNC_NOTIF_VALUE_VALID if the value in a1 is - * valid, else 0 if no values where pending + * valid, else 0 if no values were pending * a2 Bit[1]: OPTEE_SMC_ASYNC_NOTIF_VALUE_PENDING if another value is * pending, else 0. * Bit[31:2]: MBZ diff --git a/mk/config.mk b/mk/config.mk index 1838862bd4e..db550a06f3a 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -933,7 +933,7 @@ ifeq (y-y,$(CFG_WITH_PAGER)-$(CFG_MEMTAG)) $(error CFG_WITH_PAGER and CFG_MEMTAG are not compatible) endif -# CFG_CORE_ASYNC_NOTIF is defined by the platform to enable enables support +# CFG_CORE_ASYNC_NOTIF is defined by the platform to enable support # for sending asynchronous notifications to normal world. Note that an # interrupt ID must be configurged by the platform too. Currently is only # CFG_CORE_ASYNC_NOTIF_GIC_INTID defined. From 5d3133d20e62179297345e4319a3a858f0e80bbd Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 23 Jan 2023 15:50:26 +0100 Subject: [PATCH 3/9] core: notif: interrupt notification Implements interrupt notification support for events OP-TEE notifies to normal world as interrupt event multiplex on async notif physical interrupt. Such events can be logical events generated by OP-TEE as when the SCMI server emits a message to its non-secure agent, or physical events as a interrupt event dedicated to non-secure world but caught from a secure interrupt controller driver because. Interrupt identifier numbers (often referred to as itr_num) are platform specific. The feature is embedded upon new boolean config switch CFG_CORE_ITR_NOTIF=y. The feature is quite related to async notif as they share resources (the physical interrupt used to notify non-secure world) and use a similar mechanism to retrieve pending interrupt events. Platform driver willing to notify a interrupt event to non-secure must register the interrupt number with core functions notif_itr_register(). Function notif_itr_raise_event() allows core to notify non-secure world that an interrupt event is pending: storing pending event in bitstring and raising normal world async notif interrupt. Function notif_get_pending() retrieves pending interrupt events from the interrupt notif record, if any, informs whether other interrupt events are pending, retrieves do bottom half event and informs whether there are other pending async values than do bottom half. SMC fastcall function ID OPTEE_SMC_FUNCID_GET_NOTIF_ITR allows normal world to collect pending interrupt events from the async notif interrupt context. Core functions notif_itr_set_mask() masks/unmasks an interrupt notification. SMC fastcall function ID OPTEE_SMC_NOTIF_ITR_SET_MASK allows normal world to call this function. Core functions notif_itr_set_state() and notif_itr_set_wakeup() are services to enable and disable the interruption notification and to set or clear interrupt wakeup capability possibly used for the interrupt to wakeup the platform during low power states. SMC standard function IDs OPTEE_SMC_NOTIF_ITR_SET_STATE and OPTEE_SMC_NOTIF_ITR_SET_WAKEUP allow normal world to call the core API functions. Co-developed-by: Pascal Paillet Signed-off-by: Pascal Paillet Signed-off-by: Etienne Carriere --- core/arch/arm/include/sm/optee_smc.h | 133 ++++++++++++- core/arch/arm/kernel/thread_optee_smc.c | 26 +++ core/arch/arm/tee/entry_fast.c | 85 ++++++++- core/include/kernel/notif.h | 118 +++++++++++- core/kernel/notif.c | 241 +++++++++++++++++++++++- mk/config.mk | 22 +++ 6 files changed, 620 insertions(+), 5 deletions(-) diff --git a/core/arch/arm/include/sm/optee_smc.h b/core/arch/arm/include/sm/optee_smc.h index 04a3839a2c1..95fdfa87bf0 100644 --- a/core/arch/arm/include/sm/optee_smc.h +++ b/core/arch/arm/include/sm/optee_smc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* - * Copyright (c) 2015-2021, Linaro Limited + * Copyright (c) 2015-2023, Linaro Limited */ #ifndef OPTEE_SMC_H #define OPTEE_SMC_H @@ -289,7 +289,11 @@ * 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 number being notified. Interrupts + * notified by OP-TEE are identified by a number from 0 to + * that max value. Values for each interrupt number are + * platform specific bindings. + * Bit[31:24]: Reserved (MBZ) * a3-7 Preserved * * Error return register usage: @@ -316,6 +320,11 @@ #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 notification to normal world */ +#define OPTEE_SMC_SEC_CAP_ITR_NOTIF BIT(7) + +#define OPTEE_SMC_SEC_CAP_ITR_NOTIF_MAX_MASK GENMASK_32(23, 8) +#define OPTEE_SMC_SEC_CAP_ITR_NOTIF_MAX_SHIFT 8 #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES U(9) #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ @@ -559,6 +568,126 @@ /* See OPTEE_SMC_CALL_WITH_REGD_ARG above */ #define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG U(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 bindings. + * + * 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 number if a1 & 0xFF >= 1 + * a2 Bit[15:0]: Pending interrupt number if a1 & 0xFF >= 2 + * Bit[31:16]: Pending interrupt number if a1 & 0xFF >= 3 + * a3 Bit[15:0]: Pending interrupt number if a1 & 0xFF >= 4 + * Bit[31:16]: Pending interrupt number 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_32(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 + */ +#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) + +/* + * Enable/disable an interrupt notification + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_STATE + * a1 Interrupt number identifier value + * a2 1 to enable, 0 to disable 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 + */ +#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_STATE 22 +#define OPTEE_SMC_NOTIF_ITR_SET_STATE \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_STATE) + +/* + * Enable/disable the wake up from low power feature of an interrupt event + * + * Call requests usage: + * a0 SMC Function ID, OPTEE_SMC_NOTIF_ITR_SET_WAKEUP + * a1 Interrupt number identifier value + * a2 1 to enable, 0 to disable the interrupt wake up capability + * 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 + */ +#define OPTEE_SMC_FUNCID_NOTIF_ITR_SET_WAKEUP 23 +#define OPTEE_SMC_NOTIF_ITR_SET_WAKEUP \ + OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_NOTIF_ITR_SET_WAKEUP) + /* * Resume from RPC (for example after processing a foreign interrupt) * diff --git a/core/arch/arm/kernel/thread_optee_smc.c b/core/arch/arm/kernel/thread_optee_smc.c index bc69d8beac2..17dddcfff81 100644 --- a/core/arch/arm/kernel/thread_optee_smc.c +++ b/core/arch/arm/kernel/thread_optee_smc.c @@ -266,6 +266,28 @@ static uint32_t std_entry_with_regd_arg(uint64_t cookie, size_t offset) return rv; } +static uint32_t std_entry_notif_itr_set_state(uint32_t a1, uint32_t a2) +{ + uint32_t itr_num = a1; + bool do_enable = a2; + + if (a2 > 1 || notif_itr_set_state(itr_num, do_enable)) + return OPTEE_SMC_RETURN_EBADCMD; + + return OPTEE_SMC_RETURN_OK; +} + +static uint32_t std_entry_notif_itr_set_wakeup(uint32_t a1, uint32_t a2) +{ + uint32_t itr_num = a1; + bool do_enable = a2; + + if (a2 > 1 || notif_itr_set_wakeup(itr_num, do_enable)) + return OPTEE_SMC_RETURN_EBADCMD; + + return OPTEE_SMC_RETURN_OK; +} + static uint32_t std_smc_entry(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3 __unused) { @@ -280,6 +302,10 @@ static uint32_t std_smc_entry(uint32_t a0, uint32_t a1, uint32_t a2, with_rpc_arg); case OPTEE_SMC_CALL_WITH_REGD_ARG: return std_entry_with_regd_arg(reg_pair_to_64(a1, a2), a3); + case OPTEE_SMC_NOTIF_ITR_SET_STATE: + return std_entry_notif_itr_set_state(a1, a2); + case OPTEE_SMC_NOTIF_ITR_SET_WAKEUP: + return std_entry_notif_itr_set_wakeup(a1, a2); default: EMSG("Unknown SMC 0x%"PRIx32, a0); return OPTEE_SMC_RETURN_EBADCMD; diff --git a/core/arch/arm/tee/entry_fast.c b/core/arch/arm/tee/entry_fast.c index f62da8cf9fa..315e8943439 100644 --- a/core/arch/arm/tee/entry_fast.c +++ b/core/arch/arm/tee/entry_fast.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BSD-2-Clause /* - * Copyright (c) 2015-2021, Linaro Limited + * Copyright (c) 2015-2023, Linaro Limited * Copyright (c) 2014, STMicroelectronics International N.V. */ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include #include #ifdef CFG_CORE_RESERVED_SHM @@ -66,6 +68,14 @@ static void tee_entry_exchange_capabilities(struct thread_smc_args *args) { bool res_shm_en = IS_ENABLED(CFG_CORE_RESERVED_SHM); bool dyn_shm_en __maybe_unused = false; + unsigned int notif_itr_max_number = 0; + + static_assert(THREAD_RPC_MAX_NUM_PARAMS <= UINT8_MAX); +#ifdef CFG_CORE_ITR_NOTIF + static_assert(CFG_CORE_ITR_NOTIF_MAX <= UINT16_MAX); + + notif_itr_max_number = CFG_CORE_ITR_NOTIF_MAX; +#endif /* * Currently we ignore OPTEE_SMC_NSEC_CAP_UNIPROCESSOR. @@ -116,6 +126,14 @@ static void tee_entry_exchange_capabilities(struct thread_smc_args *args) args->a1 |= OPTEE_SMC_SEC_CAP_RPC_ARG; args->a3 = THREAD_RPC_MAX_NUM_PARAMS; + + if (IS_ENABLED(CFG_CORE_ITR_NOTIF)) { + args->a1 |= OPTEE_SMC_SEC_CAP_ITR_NOTIF; + args->a3 |= notif_itr_max_number << + OPTEE_SMC_SEC_CAP_ITR_NOTIF_MAX_SHIFT; + } + IMSG("Interrupt notifications are %sabled", + args->a1 & OPTEE_SMC_SEC_CAP_ITR_NOTIF ? "en" : "dis"); } static void tee_entry_disable_shm_cache(struct thread_smc_args *args) @@ -217,6 +235,57 @@ static void get_async_notif_value(struct thread_smc_args *args) args->a2 |= OPTEE_SMC_ASYNC_NOTIF_PENDING; } +static void get_pending_notif(struct thread_smc_args *args) +{ + bool do_bottom_half = false; + bool value_pending = false; + bool itr_pending = false; + uint16_t itr[5] = { 0 }; + size_t count = ARRAY_SIZE(itr); + + notif_get_pending(&do_bottom_half, &value_pending, itr, &count); + + assert(count <= ARRAY_SIZE(itr) + 1); + if (count == ARRAY_SIZE(itr) + 1) { + count = ARRAY_SIZE(itr); + itr_pending = true; + } + + args->a0 = OPTEE_SMC_RETURN_OK; + args->a1 = count | SHIFT_U32((uint32_t)itr[0], 16); + args->a2 = itr[1] | SHIFT_U32((uint32_t)itr[2], 16); + args->a3 = itr[3] | SHIFT_U32((uint32_t)itr[4], 16); + + if (itr_pending) + args->a1 |= OPTEE_SMC_NOTIF_ITR_PENDING; + if (value_pending) + args->a1 |= OPTEE_SMC_NOTIF_VALUE_PENDING; + if (do_bottom_half) + args->a1 |= OPTEE_SMC_NOTIF_DO_BOTTOM_HALF; + + count = args->a1 & OPTEE_SMC_NOTIF_ITR_COUNT_MASK; + FMSG("Pending notif: do bottom half %u, async events %u, %zu it: %" + PRId16" %"PRId16" %"PRId16" %"PRId16" %"PRId16", pending %u", + do_bottom_half, value_pending, count, itr[0], itr[1], itr[2], + itr[3], itr[4], itr_pending); +} + +static void set_itr_notif_mask(struct thread_smc_args *args __maybe_unused) +{ +#ifdef CFG_CORE_ITR_NOTIF + uint32_t itr_num = args->a1; + bool masked = args->a2; + + if (args->a2 > 1 || itr_num > CFG_CORE_ITR_NOTIF_MAX) { + args->a0 = OPTEE_SMC_RETURN_EBADCMD; + return; + } + + notif_itr_set_mask(itr_num, masked); + args->a0 = OPTEE_SMC_RETURN_OK; +#endif +} + /* * If tee_entry_fast() is overridden, it's still supposed to call this * function. @@ -291,6 +360,20 @@ void __tee_entry_fast(struct thread_smc_args *args) args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; break; + case OPTEE_SMC_GET_NOTIF_ITR: + if (IS_ENABLED(CFG_CORE_ITR_NOTIF)) + get_pending_notif(args); + else + args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; + break; + + case OPTEE_SMC_NOTIF_ITR_SET_MASK: + if (IS_ENABLED(CFG_CORE_ITR_NOTIF)) + set_itr_notif_mask(args); + else + args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; + break; + default: args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; break; diff --git a/core/include/kernel/notif.h b/core/include/kernel/notif.h index 71b53d8eacc..747e6ab3fe3 100644 --- a/core/include/kernel/notif.h +++ b/core/include/kernel/notif.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-2-Clause */ /* - * Copyright (c) 2021, Linaro Limited + * Copyright (c) 2021-2023, Linaro Limited */ #ifndef __KERNEL_NOTIF_H @@ -89,6 +89,30 @@ struct notif_driver { SLIST_ENTRY(notif_driver) link; }; +/* + * struct notif_itr - Interrupt notifier: notifying normal world of an event + * + * @itr_num Interrupt number used as identifier for the interrupt event + * @ops Notifier unpaged callback operations or NULL if none + */ +struct notif_itr { + unsigned int itr_num; + const struct notif_itr_ops *ops; +}; + +/* + * struct notif_itr_ops - Interrupt notifier operations + * + * @set_mask Fasctcall callback for (un)masking the event or NULL if not used + * @set_state Callback for enabling/disabling the event or NULL if not used + * @set_wakeup Callback for enabling/disabling standby wakeup source or NULL + */ +struct notif_itr_ops { + void (*set_mask)(struct notif_itr *notif, bool do_mask); + TEE_Result (*set_state)(struct notif_itr *notif, bool do_enable); + TEE_Result (*set_wakeup)(struct notif_itr *notif, bool do_enable); +}; + #if defined(CFG_CORE_ASYNC_NOTIF) bool notif_async_is_started(void); #else @@ -152,6 +176,98 @@ static inline uint32_t notif_get_value(bool *value_valid, bool *value_pending) } #endif +#if defined(CFG_CORE_ITR_NOTIF) +/* + * Notify an interrupt event to normal world + * + * @notif Reference to registered notifier + */ +void notif_itr_raise_event(struct notif_itr *notif); + +/* + * Mask/unmask an interrupt notification + * + * @itr_num Interrupt identifier provided by normal world + * @do_mask True to mask the event, false to unmask the event + * + * This function is called from a fastcall context + */ +void notif_itr_set_mask(unsigned int itr_num, bool do_mask); + +/* + * Activate (enable) or deactivate (disable) an interrupt notification + * + * @itr_num Interrupt identifier provided by normal world + * @do_enable True to enable the event detection, false disable it + */ +TEE_Result notif_itr_set_state(unsigned int itr_num, bool do_enable); + +/* + * Enable or disable the low power wakeup capability of the interrupt event + * + * @itr_num Interrupt identifier provided by normal world + * @do_enable True to enable the event detection, false disable it + */ +TEE_Result notif_itr_set_wakeup(unsigned int itr_num, bool do_enable); + +/* + * Report pending interrupts and asynchronous notification values. + * @do_bottom_half: Set to true if NOTIF_VALUE_DO_BOTTOM_HALF was pending and + * has been cleared in the record as delivered. + * @async_value: Set to true if any other asynchronous notification value + * is pending. + * @itr_nums: Array of size *@it_count used to report pending interrupts + * @itr_count: (Input) array size of @itr_num[] + * (Output) number of cells retrieving an interrupt number, or + * input @itr_count + 1 if all cells are used and another + * interrupt notif is pending. + * + * This function is called from a fastcall context. + */ +void notif_get_pending(bool *do_bottom_half, bool *async_value, + uint16_t *itr_nums, size_t *itr_count); + +/* + * Register an interrupt notifier + * + * @notif Preloaded notifier structure to register + */ +TEE_Result notif_itr_register(struct notif_itr *notif); + +/* + * Unregister an interrupt notifier + * + * @notif Registered notifier + */ +TEE_Result notif_itr_unregister(struct notif_itr *notif); +#else +static inline void notif_get_pending(bool *do_bottom_half, bool *value_pending, + uint16_t *itr_nums __unused, + size_t *itr_count) +{ + *do_bottom_half = false; + *value_pending = false; + *itr_count = 0; +} + +static inline void notif_itr_set_mask(unsigned int itr_num __unused, + bool do_enable __unused) +{ +} + +static inline TEE_Result notif_itr_set_state(unsigned int itr_num __unused, + bool do_enable __unused) +{ + return TEE_ERROR_NOT_SUPPORTED; +} + +static inline TEE_Result notif_itr_set_wakeup(unsigned int itr_num __unused, + bool do_enable __unused) +{ + return TEE_ERROR_NOT_SUPPORTED; +} +#endif + /* * These are called from yielding calls */ diff --git a/core/kernel/notif.c b/core/kernel/notif.c index 7ecc246fdf5..8cb4df4e34f 100644 --- a/core/kernel/notif.c +++ b/core/kernel/notif.c @@ -1,18 +1,30 @@ // SPDX-License-Identifier: BSD-2-Clause /* - * Copyright (c) 2021, Linaro Limited + * Copyright (c) 2021-2023, Linaro Limited */ #include +#include #include #include +#include #include #include #include #include +#include #include +#include +#include +#include #include +/* + * Notification of non-secure interrupt events identified by an IT number + * from 0 to CFG_CORE_ITR_NOTIF_MAX. + */ +#define NOTIF_ITR_VALUE_MAX CFG_CORE_ITR_NOTIF_MAX + #if defined(CFG_CORE_ASYNC_NOTIF) static struct mutex notif_mutex = MUTEX_INITIALIZER; static unsigned int notif_lock = SPINLOCK_UNLOCK; @@ -25,6 +37,14 @@ static bitstr_t bit_decl(notif_values, NOTIF_ASYNC_VALUE_MAX + 1); static bitstr_t bit_decl(notif_alloc_values, NOTIF_ASYNC_VALUE_MAX + 1); static bool notif_started; +#if defined(CFG_CORE_ITR_NOTIF) +static bitstr_t bit_decl(notif_itr_pending, NOTIF_ITR_VALUE_MAX + 1); +static bitstr_t bit_decl(notif_itr_masked, NOTIF_ITR_VALUE_MAX + 1); + +static unsigned int notif_itr_lock = SPINLOCK_UNLOCK; +static struct notif_itr *notif_itr_handler[NOTIF_ITR_VALUE_MAX + 1]; +#endif + TEE_Result notif_alloc_async_value(uint32_t *val) { static bool alloc_values_inited; @@ -106,6 +126,217 @@ void notif_send_async(uint32_t value) cpu_spin_unlock_xrestore(¬if_lock, old_itr_status); } +#if defined(CFG_CORE_ITR_NOTIF) +static int get_pending_itr_num(int start_bit) +{ + const int max_bit = NOTIF_ITR_VALUE_MAX + 1; + int bit = start_bit - 1; + + do { + bit_ffs_from(notif_itr_pending, max_bit, bit + 1, &bit); + } while (bit >= 0 && bit_test(notif_itr_masked, bit)); + + return bit; +} + +void notif_get_pending(bool *do_bottom_half, bool *value_pending, + uint16_t *itr_nums, size_t *itr_count) +{ + uint32_t exceptions = 0; + bool do_bh = false; + size_t count = 0; + int bit = 0; + + exceptions = cpu_spin_lock_xsave(¬if_itr_lock); + + /* Retrieve at most *itr_num's pending event */ + for (count = 0; count < *itr_count; count++) { + bit = get_pending_itr_num(bit); + if (bit < 0) + break; + + bit_clear(notif_itr_pending, bit); + itr_nums[count] = bit; + } + + /* Report if there are other pending interrupts */ + if (count == *itr_count && get_pending_itr_num(0) >= 0) + count++; + + /* Retrieve bottom half event if pending */ + if (bit_test(notif_values, NOTIF_VALUE_DO_BOTTOM_HALF)) { + bit_clear(notif_values, NOTIF_VALUE_DO_BOTTOM_HALF); + do_bh = true; + } + + /* Report if there are pending async notif other than do bottom half */ + bit_ffs_from(notif_values, NOTIF_VALUE_DO_BOTTOM_HALF + 1, + (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit); + + cpu_spin_unlock_xrestore(¬if_itr_lock, exceptions); + + *itr_count = count; + *value_pending = (bit >= 0); + *do_bottom_half = do_bh; +} + +/* This function can execute in a fastcall interrupt masked context */ +static void set_itr_mask(unsigned int itr_num, bool do_mask) +{ + uint32_t exceptions = 0; + + if (itr_num > NOTIF_ITR_VALUE_MAX) + return; + + FMSG("Itr notif %u %smasked", itr_num, do_mask ? "" : "un"); + + exceptions = cpu_spin_lock_xsave(¬if_itr_lock); + + if (do_mask) { + bit_set(notif_itr_masked, itr_num); + } else { + bit_clear(notif_itr_masked, itr_num); + + if (bit_test(notif_itr_pending, itr_num)) + itr_raise_pi(CFG_CORE_ASYNC_NOTIF_GIC_INTID); + } + + cpu_spin_unlock_xrestore(¬if_itr_lock, exceptions); +} + +static struct notif_itr __maybe_unused *find_notif_itr(unsigned int itr_num) +{ + assert(itr_num <= NOTIF_ITR_VALUE_MAX); + return notif_itr_handler[itr_num]; +} + +static struct notif_itr *find_notif_itr_safe(unsigned int itr_num) +{ + /* Sanitize interrupt identifier provided by normal world */ + if (itr_num > NOTIF_ITR_VALUE_MAX) + return NULL; + itr_num = confine_array_index(itr_num, NOTIF_ITR_VALUE_MAX + 1); + + return notif_itr_handler[itr_num]; +} + +static void remove_notif_itr_handler(struct notif_itr *notif) +{ + assert(notif && notif->itr_num <= NOTIF_ITR_VALUE_MAX && + notif_itr_handler[notif->itr_num] == notif); + + notif_itr_handler[notif->itr_num] = NULL; +} + +static void add_notif_itr_handler(struct notif_itr *notif) +{ + assert(notif && notif->itr_num <= NOTIF_ITR_VALUE_MAX && + !notif_itr_handler[notif->itr_num]); + + notif_itr_handler[notif->itr_num] = notif; +} + +void notif_itr_set_mask(unsigned int itr_num, bool do_mask) +{ + struct notif_itr *notif = find_notif_itr_safe(itr_num); + + if (notif) { + set_itr_mask(itr_num, do_mask); + if (notif->ops && notif->ops->set_mask) + notif->ops->set_mask(notif, do_mask); + } +} + +TEE_Result notif_itr_set_state(unsigned int itr_num, bool do_enable) +{ + struct notif_itr *notif = find_notif_itr_safe(itr_num); + + if (!notif) + return TEE_ERROR_BAD_PARAMETERS; + + set_itr_mask(itr_num, !do_enable); + + if (notif->ops && notif->ops->set_state) + return notif->ops->set_state(notif, do_enable); + + /* Notifier may not implement set_state */ + return TEE_SUCCESS; +} + +TEE_Result notif_itr_set_wakeup(unsigned int itr_num, bool do_enable) +{ + struct notif_itr *notif = find_notif_itr_safe(itr_num); + + if (!notif) + return TEE_ERROR_BAD_PARAMETERS; + + if (notif->ops && notif->ops->set_wakeup) + return notif->ops->set_wakeup(notif, do_enable); + + /* Notifier must implement set_wakeup for a wakeup source interrupt */ + return TEE_ERROR_NOT_SUPPORTED; +} + +void notif_itr_raise_event(struct notif_itr *notif) +{ + uint32_t exceptions = 0; + + assert(find_notif_itr(notif->itr_num) == notif); + + exceptions = cpu_spin_lock_xsave(¬if_itr_lock); + + bit_set(notif_itr_pending, notif->itr_num); + + if (!bit_test(notif_itr_masked, notif->itr_num)) + itr_raise_pi(CFG_CORE_ASYNC_NOTIF_GIC_INTID); + + cpu_spin_unlock_xrestore(¬if_itr_lock, exceptions); +} + +TEE_Result notif_itr_register(struct notif_itr *notif) +{ + unsigned int itr_num = 0; + uint32_t exceptions = 0; + const struct notif_itr_ops __maybe_unused *ops = NULL; + + assert(notif && is_unpaged(notif)); + itr_num = notif->itr_num; + assert(itr_num <= NOTIF_ITR_VALUE_MAX && !find_notif_itr(itr_num)); + ops = notif->ops; + assert(!ops || (is_unpaged((void *)ops) && + (!ops->set_mask || is_unpaged(ops->set_mask)))); + + exceptions = cpu_spin_lock_xsave(¬if_itr_lock); + bit_clear(notif_itr_pending, itr_num); + cpu_spin_unlock_xrestore(¬if_itr_lock, exceptions); + + set_itr_mask(itr_num, 1); + + add_notif_itr_handler(notif); + + return TEE_SUCCESS; +} + +TEE_Result notif_itr_unregister(struct notif_itr *notif) +{ + uint32_t exceptions = 0; + + if (!notif) + return TEE_SUCCESS; + + assert(find_notif_itr(notif->itr_num) == notif); + + exceptions = cpu_spin_lock_xsave(¬if_lock); + bit_clear(notif_itr_pending, notif->itr_num); + bit_set(notif_itr_masked, notif->itr_num); + cpu_spin_unlock_xrestore(¬if_lock, exceptions); + + remove_notif_itr_handler(notif); + + return TEE_SUCCESS; +} +#endif /*CFG_CORE_ITR_NOTIF*/ + bool notif_async_is_started(void) { uint32_t old_itr_status = 0; @@ -127,6 +358,14 @@ void notif_register_driver(struct notif_driver *ndrv) SLIST_INSERT_HEAD(¬if_driver_head, ndrv, link); cpu_spin_unlock_xrestore(¬if_lock, old_itr_status); + +#if defined(CFG_CORE_ITR_NOTIF) + old_itr_status = cpu_spin_lock_xsave(¬if_itr_lock); + + bit_nset(notif_itr_masked, 0, (int)NOTIF_ITR_VALUE_MAX); + + cpu_spin_unlock_xrestore(¬if_itr_lock, old_itr_status); +#endif } void notif_unregister_driver(struct notif_driver *ndrv) diff --git a/mk/config.mk b/mk/config.mk index db550a06f3a..2764195da29 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -1045,3 +1045,25 @@ CFG_TA_OPTEE_CORE_API_COMPAT_1_1 ?= n # - PerInstance/AttestationTest# # Note that this violates GP requirements of HMAC size range. CFG_HMAC_64_1024_RANGE ?= n + +# CFG_CORE_ITR_NOTIF is defined by the platform to enable support +# for sending asynchronous notifications to normal world for dedicated +# interrupt events to be handle in an interrupt context in normal world. +# CFG_CORE_ITR_NOTIF depends on CFG_CORE_ASYNC_NOTIF. +# When enabled, OP-TEE can notify interrupt events identified by an +# interrupt number from 0 to CFG_CORE_ITR_NOTIF_MAX. Assignment of the +# interrupt numbers is platform specific and shall be found in the +# platform OP-TEE firmware documentation. + +# CFG_CORE_ITR_NOTIF_MAX selects CFG_CORE_ITR_NOTIF +ifneq ($(CFG_CORE_ITR_NOTIF_MAX),) +CFG_CORE_ITR_NOTIF ?= y +endif +CFG_CORE_ITR_NOTIF ?= n + +ifeq ($(CFG_CORE_ITR_NOTIF),y) +ifeq ($(CFG_CORE_ITR_NOTIF_MAX),) +$(error CFG_CORE_ITR_NOTIF requires a value for CFG_CORE_ITR_NOTIF_MAX) +endif +endif +$(eval $(call cfg-depends-all,CFG_CORE_ITR_NOTIF,CFG_CORE_ASYNC_NOTIF)) From 664a13f362877f4a8ad280d2b1b8ddf673bf8533 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 20 Mar 2023 10:50:31 +0100 Subject: [PATCH 4/9] core: notif: export NOTIF_ITR_VALUE_MAX to core Defines NOTIF_ITR_VALUE_MAX from notif.h header file visible from core source files to ease addition of test interrupt lines for interrupt notification embedded tests. Signed-off-by: Etienne Carriere --- core/arch/arm/tee/entry_fast.c | 6 +++--- core/include/kernel/notif.h | 9 +++++++++ core/kernel/notif.c | 6 ------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/core/arch/arm/tee/entry_fast.c b/core/arch/arm/tee/entry_fast.c index 315e8943439..1e6cbf64cc8 100644 --- a/core/arch/arm/tee/entry_fast.c +++ b/core/arch/arm/tee/entry_fast.c @@ -72,9 +72,9 @@ static void tee_entry_exchange_capabilities(struct thread_smc_args *args) static_assert(THREAD_RPC_MAX_NUM_PARAMS <= UINT8_MAX); #ifdef CFG_CORE_ITR_NOTIF - static_assert(CFG_CORE_ITR_NOTIF_MAX <= UINT16_MAX); + static_assert(NOTIF_ITR_VALUE_MAX <= UINT16_MAX); - notif_itr_max_number = CFG_CORE_ITR_NOTIF_MAX; + notif_itr_max_number = NOTIF_ITR_VALUE_MAX; #endif /* @@ -276,7 +276,7 @@ static void set_itr_notif_mask(struct thread_smc_args *args __maybe_unused) uint32_t itr_num = args->a1; bool masked = args->a2; - if (args->a2 > 1 || itr_num > CFG_CORE_ITR_NOTIF_MAX) { + if (args->a2 > 1 || itr_num > NOTIF_ITR_VALUE_MAX) { args->a0 = OPTEE_SMC_RETURN_EBADCMD; return; } diff --git a/core/include/kernel/notif.h b/core/include/kernel/notif.h index 747e6ab3fe3..4187c9e0f90 100644 --- a/core/include/kernel/notif.h +++ b/core/include/kernel/notif.h @@ -38,6 +38,15 @@ #define NOTIF_VALUE_DO_BOTTOM_HALF 0 +/* Number of extra interrupt notifiers for embedded test purpose */ +#define TEST_ITR_NOTIF_COUNT 0 + +/* + * Notification of non-secure interrupt identified by an number from 0 + * to CFG_CORE_ITR_NOTIF_MAX plus possibly test purpose notifiers. + */ +#define NOTIF_ITR_VALUE_MAX (CFG_CORE_ITR_NOTIF_MAX + TEST_ITR_NOTIF_COUNT) + /* * enum notif_event - Notification of an event * @NOTIF_EVENT_STARTED: Delivered in an atomic context to inform diff --git a/core/kernel/notif.c b/core/kernel/notif.c index 8cb4df4e34f..eb3b411f160 100644 --- a/core/kernel/notif.c +++ b/core/kernel/notif.c @@ -19,12 +19,6 @@ #include #include -/* - * Notification of non-secure interrupt events identified by an IT number - * from 0 to CFG_CORE_ITR_NOTIF_MAX. - */ -#define NOTIF_ITR_VALUE_MAX CFG_CORE_ITR_NOTIF_MAX - #if defined(CFG_CORE_ASYNC_NOTIF) static struct mutex notif_mutex = MUTEX_INITIALIZER; static unsigned int notif_lock = SPINLOCK_UNLOCK; From da212a8f6e68ea262671ace9b2ea82607e843324 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Mon, 23 Jan 2023 15:04:26 +0100 Subject: [PATCH 5/9] core: notif: add status helper functions for embedded tests Adds notif_it_is_pending() and notif_it_is_masked() helper functions for use in interrupt notification embedded tests. Signed-off-by: Etienne Carriere --- core/include/kernel/notif.h | 20 ++++++++++++++++++++ core/kernel/notif.c | 18 +++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/core/include/kernel/notif.h b/core/include/kernel/notif.h index 4187c9e0f90..c08feec7f43 100644 --- a/core/include/kernel/notif.h +++ b/core/include/kernel/notif.h @@ -249,6 +249,26 @@ TEE_Result notif_itr_register(struct notif_itr *notif); * @notif Registered notifier */ TEE_Result notif_itr_unregister(struct notif_itr *notif); + +/* + * Return whether interrupt notification is masked or unmasked + * + * @itr_num Interrupt identifier + * Return true if notifier is masked, false otherwise + * + * Panics if @itr_num is not a valid value + */ +bool notif_itr_is_masked(unsigned int itr_num); + +/* + * Return whether interrupt notif event is pending or not + * + * @itr_num Interrupt identifier + * Return true if notifier event is pending, false otherwise + * + * Panics if @itr_num is not a valid value + */ +bool notif_itr_is_pending(unsigned int itr_num); #else static inline void notif_get_pending(bool *do_bottom_half, bool *value_pending, uint16_t *itr_nums __unused, diff --git a/core/kernel/notif.c b/core/kernel/notif.c index eb3b411f160..e3016523f85 100644 --- a/core/kernel/notif.c +++ b/core/kernel/notif.c @@ -198,7 +198,7 @@ static void set_itr_mask(unsigned int itr_num, bool do_mask) cpu_spin_unlock_xrestore(¬if_itr_lock, exceptions); } -static struct notif_itr __maybe_unused *find_notif_itr(unsigned int itr_num) +static struct notif_itr *find_notif_itr(unsigned int itr_num) { assert(itr_num <= NOTIF_ITR_VALUE_MAX); return notif_itr_handler[itr_num]; @@ -241,6 +241,22 @@ void notif_itr_set_mask(unsigned int itr_num, bool do_mask) } } +bool notif_itr_is_masked(unsigned int itr_num) +{ + if (!find_notif_itr(itr_num)) + panic("Invalid interrupt number"); + + return bit_test(notif_itr_masked, itr_num); +} + +bool notif_itr_is_pending(unsigned int itr_num) +{ + if (!find_notif_itr(itr_num)) + panic("Invalid interrupt number"); + + return bit_test(notif_itr_pending, itr_num); +} + TEE_Result notif_itr_set_state(unsigned int itr_num, bool do_enable) { struct notif_itr *notif = find_notif_itr_safe(itr_num); From 319ffe103a528d8b7973fb06856431f1382c2f7e Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 24 Jan 2023 22:30:56 +0100 Subject: [PATCH 6/9] core: pta: test: invoke pta command to test interrupt notif Adds a command to the invoke test PTA to play with interrupt notifiers. The test uses interrupt lines not consumed by the Linux driver hence Linux will mask them since unused. A straight forward test is to unmask some interrupt events, raise them and check that they've been masked back by the normal world OS. Test is embedded upon boolean config switch CFG_ITR_NOTIF_TEST that depends on CFG_TEE_CORE_EMBED_INTERNAL_TESTS and CFG_CORE_ITR_NOTIF. Signed-off-by: Etienne Carriere --- core/include/kernel/notif.h | 4 + core/pta/tests/invoke.c | 2 + core/pta/tests/misc.c | 343 +++++++++++++++++++++++++ core/pta/tests/misc.h | 2 + lib/libutee/include/pta_invoke_tests.h | 5 + mk/config.mk | 5 + 6 files changed, 361 insertions(+) diff --git a/core/include/kernel/notif.h b/core/include/kernel/notif.h index c08feec7f43..267d08a55b9 100644 --- a/core/include/kernel/notif.h +++ b/core/include/kernel/notif.h @@ -39,7 +39,11 @@ #define NOTIF_VALUE_DO_BOTTOM_HALF 0 /* Number of extra interrupt notifiers for embedded test purpose */ +#if defined(CFG_ITR_NOTIF_TEST) +#define TEST_ITR_NOTIF_COUNT 9 +#else #define TEST_ITR_NOTIF_COUNT 0 +#endif /* * Notification of non-secure interrupt identified by an number from 0 diff --git a/core/pta/tests/invoke.c b/core/pta/tests/invoke.c index bbc761fa671..16eef5e46c3 100644 --- a/core/pta/tests/invoke.c +++ b/core/pta/tests/invoke.c @@ -433,6 +433,8 @@ static TEE_Result invoke_command(void *pSessionContext __unused, return core_aes_perf_tests(nParamTypes, pParams); case PTA_INVOKE_TESTS_CMD_DT_DRIVER_TESTS: return core_dt_driver_tests(nParamTypes, pParams); + case PTA_INVOKE_TESTS_CMD_ITR_NOTIF_TESTS: + return core_itr_notif_tests(nParamTypes, pParams); default: break; } diff --git a/core/pta/tests/misc.c b/core/pta/tests/misc.c index 63dbea48b70..c806d690225 100644 --- a/core/pta/tests/misc.c +++ b/core/pta/tests/misc.c @@ -4,7 +4,11 @@ */ #include #include +#include +#include #include +#include +#include #include #include #include @@ -21,6 +25,338 @@ */ #define LOG(...) +#ifdef CFG_ITR_NOTIF_TEST +/* + * Register TEST_ITR_NOTIF_COUNT interrupt notifiers with interrupt number IDs + * starting from CFG_CORE_ITR_NOTIF_MAX + 1. + */ +static struct notif_itr test_itr_notif[TEST_ITR_NOTIF_COUNT]; +/* Helper to release only registered resources in case of error */ +static bool test_itr_notif_registered[TEST_ITR_NOTIF_COUNT]; + +struct mutex itr_notif_test_lock = MUTEX_INITIALIZER; + +static TEE_Result register_test_itr_notif(unsigned int test_itr_index, + const struct notif_itr_ops *ops) +{ + struct notif_itr *notif = test_itr_notif + test_itr_index; + TEE_Result res = TEE_SUCCESS; + unsigned int itr_num = 0; + + assert(test_itr_index < TEST_ITR_NOTIF_COUNT); + itr_num = CFG_CORE_ITR_NOTIF_MAX + 1 + test_itr_index; + + if (test_itr_notif_registered[test_itr_index]) + return TEE_ERROR_GENERIC; + + notif->itr_num = itr_num; + notif->ops = ops; + + res = notif_itr_register(notif); + if (res) { + EMSG("Registering itr notif %u failed %#"PRIx32, itr_num, res); + return res; + } + + if (notif_itr_is_pending(itr_num)) { + EMSG("Bad itr notifier #%u state: event pending", itr_num); + res = TEE_ERROR_GENERIC; + } + if (!notif_itr_is_masked(itr_num)) { + EMSG("Bad itr notifier #%u state: not masked", itr_num); + res = TEE_ERROR_GENERIC; + } + + if (res) + notif_itr_unregister(notif); + else + test_itr_notif_registered[test_itr_index] = true; + + return res; +} + +static void unregister_test_all_notif(void) +{ + TEE_Result res = TEE_SUCCESS; + size_t test_index = 0; + + for (test_index = 0; test_index < TEST_ITR_NOTIF_COUNT; test_index++) { + struct notif_itr *notif = test_itr_notif + test_index; + + if (test_itr_notif_registered[test_index]) { + res = notif_itr_unregister(notif); + if (res) { + EMSG("Can't unregister itr notif %u: %#"PRIx32, + notif->itr_num, res); + panic(); + } + + test_itr_notif_registered[test_index] = false; + } + } +} + +/* + * Test1: simple test on maksing and raising interrupts + */ +static TEE_Result register_test1_itr_notif(void) +{ + TEE_Result res = TEE_SUCCESS; + size_t test_index = 0; + + for (test_index = 0; test_index < TEST_ITR_NOTIF_COUNT; test_index++) { + res = register_test_itr_notif(test_index, NULL); + if (res) + return res; + } + + return TEE_SUCCESS; +} + +static TEE_Result test1_itr_notif_do(void) +{ + TEE_Result res = TEE_SUCCESS; + uint32_t excep = 0; + size_t n = 0; + + static_assert(TEST_ITR_NOTIF_COUNT >= 8); + + mutex_lock(&itr_notif_test_lock); + + res = register_test1_itr_notif(); + if (res) + goto out; + + IMSG("Itr-notif test1: check all test interrupt notifs are masked"); + for (n = 0; n < TEST_ITR_NOTIF_COUNT; n++) { + if (!notif_itr_is_masked(test_itr_notif[n].itr_num)) { + DMSG("ITR notifier %zu is not default masked", n); + res = TEE_ERROR_GENERIC; + } + } + if (res) + goto out; + + /* Unmask test itr number 2, raise itr and check it's been retrieved */ + IMSG("Itr-notif test1: test single interrupt on itr notif %u", + test_itr_notif[2].itr_num); + + notif_itr_set_mask(test_itr_notif[2].itr_num, 0); + if (notif_itr_is_masked(test_itr_notif[2].itr_num)) { + DMSG("Unmasking notification has no effect"); + res = TEE_ERROR_GENERIC; + goto out; + } + + notif_itr_raise_event(test_itr_notif + 2); + mdelay(10); + + if (notif_itr_is_pending(test_itr_notif[2].itr_num)) { + EMSG("Itr-notif test1: notif %u still pending", + test_itr_notif[2].itr_num); + res = TEE_ERROR_GENERIC; + goto out; + } + notif_itr_set_mask(test_itr_notif[2].itr_num, 1); + + /* + * Unmask test interrupt 1 to 6, raise test interrupts 0 to 6 in 1 shot + * and check state of test interrupts 0 to 7. + */ + IMSG("Itr-notif test1: test notification of 7 interrupt events"); + + /* Unmask test interrupts 1 to 6 */ + for (n = 1; n <= 6; n++) + notif_itr_set_mask(test_itr_notif[n].itr_num, 0); + + /* Test itr 0 and 7 should be masked, and 1 to 6 unmasked */ + if (notif_itr_is_pending(test_itr_notif[0].itr_num) || + !notif_itr_is_masked(test_itr_notif[0].itr_num)) { + EMSG("Itr-notif test1: notif %u in bad state", + test_itr_notif[0].itr_num); + res = TEE_ERROR_GENERIC; + } + for (n = 1; n <= 6; n++) { + if (notif_itr_is_pending(test_itr_notif[n].itr_num) || + notif_itr_is_masked(test_itr_notif[n].itr_num)) { + EMSG("Itr-notif test1: notif %u in bad state", + test_itr_notif[n].itr_num); + res = TEE_ERROR_GENERIC; + } + } + if (notif_itr_is_pending(test_itr_notif[7].itr_num) || + !notif_itr_is_masked(test_itr_notif[7].itr_num)) { + EMSG("Itr-notif test1: notif %u in bad state", + test_itr_notif[7].itr_num); + res = TEE_ERROR_GENERIC; + } + if (res) + goto out; + + /* Throw a round of notif (in 1 burst if there only 1 core) */ + excep = thread_mask_exceptions(THREAD_EXCP_ALL); + for (n = 0; n <= 6; n++) + notif_itr_raise_event(test_itr_notif + n); + thread_unmask_exceptions(excep); + mdelay(10); + + /* Check 0 is pending/masked, 1 to 7 are not pending, 7 is masked */ + if (!notif_itr_is_pending(test_itr_notif[0].itr_num) || + !notif_itr_is_masked(test_itr_notif[0].itr_num)) { + EMSG("Itr-notif test1: notif %u in bad state", + test_itr_notif[0].itr_num); + res = TEE_ERROR_GENERIC; + goto out; + } + for (n = 1; n <= 6; n++) { + if (notif_itr_is_pending(test_itr_notif[n].itr_num)) { + EMSG("Itr-notif test1: notif %u bad state", + test_itr_notif[n].itr_num); + res = TEE_ERROR_GENERIC; + } + } + if (notif_itr_is_pending(test_itr_notif[7].itr_num) || + !notif_itr_is_masked(test_itr_notif[7].itr_num)) { + EMSG("Itr-notif test1: notif %u bad state", + test_itr_notif[7].itr_num); + res = TEE_ERROR_GENERIC; + goto out; + } + if (res) + goto out; + + IMSG("Itr-notif test1: test unmasking pending event delivers it"); + + /* Unmake test interrupt 0 to get it delivered */ + notif_itr_set_mask(test_itr_notif[0].itr_num, 0); + mdelay(10); + + /* Check 0 is no more pending */ + if (notif_itr_is_pending(test_itr_notif[0].itr_num)) { + EMSG("Itr-notif test1: notif %u in bad state", + test_itr_notif[0].itr_num); + res = TEE_ERROR_GENERIC; + } + +out: + unregister_test_all_notif(); + mutex_unlock(&itr_notif_test_lock); + + if (res) + EMSG("Itr-notif test1: failed with %#"PRIx32, res); + else + IMSG("Itr-notif test1: success"); + + return res; +} + +/* + * Test2: test interrupt event during REE interrupt context + * + * Use REE mask operation on triggered unused interrupts, which is + * executed from REE async notif interrupt context, to check notification + * of interrupt events are not lost when happening during REE interrupt + * retrieve sequence that is executed from normal world interrupt context. + */ + +static void test2_notif_set_mask(struct notif_itr *notif, bool do_mask) +{ + if (!do_mask) + return; + + if (notif == test_itr_notif) + notif_itr_raise_event(test_itr_notif + 1); + else if (notif == test_itr_notif + 1) + notif_itr_raise_event(test_itr_notif + 2); + else if (notif == test_itr_notif + 2) + notif_itr_raise_event(test_itr_notif + 3); +} +DECLARE_KEEP_PAGER(test2_notif_set_mask); + +const struct notif_itr_ops test2_notif_ops = { + .set_mask = test2_notif_set_mask, +}; + +static TEE_Result test2_itr_notif_do(void) +{ + TEE_Result res = TEE_SUCCESS; + size_t n = 0; + + static_assert(TEST_ITR_NOTIF_COUNT >= 4); + + mutex_lock(&itr_notif_test_lock); + + for (n = 0; n <= 3; n++) { + res = register_test_itr_notif(n, &test2_notif_ops); + if (res) + goto out; + } + + IMSG("Itr-notif test2: test interrupt during interrupt "); + + for (n = 0; n <= 3; n++) + notif_itr_set_mask(test_itr_notif[n].itr_num, 0); + + /* + * This test simulates cases where an interrupt is notified and + * under processing in REE, in an interrupt context, while another + * OP-TEE event raises a notification. The goal is to check the 2nd + * notification is well signaled to REE and not pending. + * + * Raise test interrupt #0. In return, REE will mask it because + * it has no consumer in Linux kernel. During mask operation, + * test interrupt #0 raises #1, #1 raises #2, #2 raises #3. + * So raising #n makes #n to #3 be raised and masked back by REE. + * + * Check that all interrupts are consumed, with 2, 3 or 4 linked + * interrupts. + */ + notif_itr_raise_event(test_itr_notif + 0); + mdelay(10); + + for (n = 0; n <= 3; n++) + if (notif_itr_is_pending(test_itr_notif[n].itr_num)) + break; + + if (n < 4) { + EMSG("Itr-notif test2: events are still pending"); + res = TEE_ERROR_GENERIC; + goto out; + } + + for (n = 0; n <= 3; n++) + notif_itr_set_mask(test_itr_notif[n].itr_num, 1); + +out: + unregister_test_all_notif(); + mutex_unlock(&itr_notif_test_lock); + + if (res) + EMSG("Itr-notif test2: failed with %#"PRIx32, res); + else + IMSG("Itr-notif test2: success"); + + return res; +} + +static TEE_Result test_itr_notif_do(void) +{ + TEE_Result res = TEE_ERROR_GENERIC; + + res = test1_itr_notif_do(); + if (res) + return res; + + return test2_itr_notif_do(); +} +#else +static TEE_Result test_itr_notif_do(void) +{ + /* Interrupt notifucation not embedded so nothing to test */ + return TEE_SUCCESS; +} +#endif /* CFG_ITR_NOTIF_TEST */ + static int self_test_add_overflow(void) { uint32_t r_u32; @@ -578,3 +914,10 @@ TEE_Result core_dt_driver_tests(uint32_t nParamTypes __unused, return TEE_SUCCESS; } + +/* Exported entrypoint for ITR_NOTIF tests */ +TEE_Result core_itr_notif_tests(uint32_t nParamTypes __unused, + TEE_Param pParams[TEE_NUM_PARAMS] __unused) +{ + return test_itr_notif_do(); +} diff --git a/core/pta/tests/misc.h b/core/pta/tests/misc.h index 58aced40af1..50bf0971b0b 100644 --- a/core/pta/tests/misc.h +++ b/core/pta/tests/misc.h @@ -37,4 +37,6 @@ TEE_Result core_aes_perf_tests(uint32_t param_types, TEE_Result core_dt_driver_tests(uint32_t param_types, TEE_Param params[TEE_NUM_PARAMS]); +TEE_Result core_itr_notif_tests(uint32_t nParamTypes __unused, + TEE_Param pParams[TEE_NUM_PARAMS] __unused); #endif /*CORE_PTA_TESTS_MISC_H*/ diff --git a/lib/libutee/include/pta_invoke_tests.h b/lib/libutee/include/pta_invoke_tests.h index 79cd9893364..9bb82e66cf7 100644 --- a/lib/libutee/include/pta_invoke_tests.h +++ b/lib/libutee/include/pta_invoke_tests.h @@ -108,5 +108,10 @@ */ #define PTA_INVOKE_TESTS_CMD_DT_DRIVER_TESTS 11 +/* + * Run interrupt notification tests + */ +#define PTA_INVOKE_TESTS_CMD_ITR_NOTIF_TESTS 12 + #endif /*__PTA_INVOKE_TESTS_H*/ diff --git a/mk/config.mk b/mk/config.mk index 2764195da29..39531b1750b 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -1054,6 +1054,9 @@ CFG_HMAC_64_1024_RANGE ?= n # interrupt number from 0 to CFG_CORE_ITR_NOTIF_MAX. Assignment of the # interrupt numbers is platform specific and shall be found in the # platform OP-TEE firmware documentation. +# +# CFG_ITR_NOTIF_TEST enables interrupt notification tests using invoke test PTA. +# that expects not being consumed by normal world. # CFG_CORE_ITR_NOTIF_MAX selects CFG_CORE_ITR_NOTIF ifneq ($(CFG_CORE_ITR_NOTIF_MAX),) @@ -1065,5 +1068,7 @@ ifeq ($(CFG_CORE_ITR_NOTIF),y) ifeq ($(CFG_CORE_ITR_NOTIF_MAX),) $(error CFG_CORE_ITR_NOTIF requires a value for CFG_CORE_ITR_NOTIF_MAX) endif +CFG_ITR_NOTIF_TEST ?= $(CFG_TEE_CORE_EMBED_INTERNAL_TESTS) endif $(eval $(call cfg-depends-all,CFG_CORE_ITR_NOTIF,CFG_CORE_ASYNC_NOTIF)) +$(eval $(call cfg-depends-all,CFG_ITR_NOTIF_TEST,CFG_TEE_CORE_EMBED_INTERNAL_TESTS CFG_CORE_ITR_NOTIF)) From d5ce3cb6b4d57ea45ef7aac74487442d29b2995e Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 24 Jan 2023 22:33:51 +0100 Subject: [PATCH 7/9] plat-vexpress: qemu_virt: enable interrupt notif Enables interrupt notifiers in platform vexpress-qemu_virt when embedded test are enabled. Signed-off-by: Etienne Carriere --- core/arch/arm/plat-vexpress/conf.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/arch/arm/plat-vexpress/conf.mk b/core/arch/arm/plat-vexpress/conf.mk index e5ef10a15a6..b3b43cc6d90 100644 --- a/core/arch/arm/plat-vexpress/conf.mk +++ b/core/arch/arm/plat-vexpress/conf.mk @@ -146,6 +146,9 @@ ifneq (,$(filter $(PLATFORM_FLAVOR),qemu_virt qemu_armv8a)) CFG_DT_DRIVER_EMBEDDED_TEST ?= y ifeq ($(CFG_DT_DRIVER_EMBEDDED_TEST),y) $(call force,CFG_EMBED_DTB_SOURCE_FILE,embedded_dtb_test.dts,Mandated for DT tests) +# Default enable CFG_CORE_ITR_NOTIF for test purpose as denoted by max value 0 +CFG_CORE_ITR_NOTIF ?= $(CFG_CORE_ASYNC_NOTIF) +CFG_CORE_ITR_NOTIF_MAX ?= 0 endif endif From 285f11924f970a914ac1ba09e5fdae6c161058ba Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Sat, 6 May 2023 08:10:05 +0200 Subject: [PATCH 8/9] plat-vexpress: qemuv8a: enable async notif and interrupt notif tests Default enable async notif on vexpress-qemuv8a platform flavor to exercise async and interrupt notif tests in OP-TEE OS CI tests. Signed-off-by: Etienne Carriere --- core/arch/arm/plat-vexpress/conf.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/arch/arm/plat-vexpress/conf.mk b/core/arch/arm/plat-vexpress/conf.mk index b3b43cc6d90..8cd87f40a1b 100644 --- a/core/arch/arm/plat-vexpress/conf.mk +++ b/core/arch/arm/plat-vexpress/conf.mk @@ -140,6 +140,8 @@ CFG_DTB_MAX_SIZE ?= 0x100000 ifeq ($(CFG_SCMI_SCPFW),y) $(call force,CFG_SCMI_SCPFW_PRODUCT,optee-fvp) endif +CFG_CORE_ASYNC_NOTIF ?= y +CFG_CORE_ASYNC_NOTIF_GIC_INTID ?= 219 endif ifneq (,$(filter $(PLATFORM_FLAVOR),qemu_virt qemu_armv8a)) From 22facda26aa781ebd2570a6cf72bcb8c600f74f6 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Tue, 10 Jan 2023 22:38:28 +0100 Subject: [PATCH 9/9] plat-stm32mp1: enable async notif and interrupt notif on stm32mp13 Enables async notif with interrupt notification using GIC PPI 15 as non-secure interrupt notifier for STM32MP13 variants. Enables interrupt notification for STM32MP13 variants that can generate up to 8 interrupt events in non-secure world. Signed-off-by: Etienne Carriere --- core/arch/arm/plat-stm32mp1/conf.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/arch/arm/plat-stm32mp1/conf.mk b/core/arch/arm/plat-stm32mp1/conf.mk index a7134e11c3d..a041d0d9459 100644 --- a/core/arch/arm/plat-stm32mp1/conf.mk +++ b/core/arch/arm/plat-stm32mp1/conf.mk @@ -101,6 +101,10 @@ $(call force,CFG_STM32_SHARED_IO,y) ifeq ($(CFG_STM32MP13),y) $(call force,CFG_BOOT_SECONDARY_REQUEST,n) +$(call force,CFG_CORE_ASYNC_NOTIF,y) +$(call force,CFG_CORE_ASYNC_NOTIF_GIC_INTID,31) +$(call force,CFG_CORE_ITR_NOTIF,y) +$(call force,CFG_CORE_ITR_NOTIF_MAX,7) $(call force,CFG_CORE_RESERVED_SHM,n) $(call force,CFG_DRIVERS_CLK_FIXED,y) $(call force,CFG_SECONDARY_INIT_CNTFRQ,n)