diff --git a/core/arch/arm/include/sm/optee_smc.h b/core/arch/arm/include/sm/optee_smc.h index cd45917d776..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 \ @@ -530,7 +539,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 @@ -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/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) diff --git a/core/arch/arm/plat-vexpress/conf.mk b/core/arch/arm/plat-vexpress/conf.mk index e5ef10a15a6..8cd87f40a1b 100644 --- a/core/arch/arm/plat-vexpress/conf.mk +++ b/core/arch/arm/plat-vexpress/conf.mk @@ -140,12 +140,17 @@ 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)) 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 diff --git a/core/arch/arm/tee/entry_fast.c b/core/arch/arm/tee/entry_fast.c index f62da8cf9fa..1e6cbf64cc8 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(NOTIF_ITR_VALUE_MAX <= UINT16_MAX); + + notif_itr_max_number = NOTIF_ITR_VALUE_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 > NOTIF_ITR_VALUE_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..267d08a55b9 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 @@ -38,6 +38,19 @@ #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 + * 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 @@ -89,6 +102,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 +189,118 @@ 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); + +/* + * 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, + 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..e3016523f85 100644 --- a/core/kernel/notif.c +++ b/core/kernel/notif.c @@ -1,16 +1,22 @@ // 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 #if defined(CFG_CORE_ASYNC_NOTIF) @@ -25,6 +31,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 +120,233 @@ 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 *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); + } +} + +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); + + 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 +368,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/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/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_ */ diff --git a/mk/config.mk b/mk/config.mk index 1838862bd4e..39531b1750b 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. @@ -1045,3 +1045,30 @@ 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_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),) +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 +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))