1 /* SPDX-License-Identifier: BSD-2-Clause */ 2 /* 3 * Copyright (c) 2014, STMicroelectronics International N.V. 4 * Copyright (c) 2016, Linaro Limited 5 */ 6 7 #ifndef __KERNEL_SPINLOCK_H 8 #define __KERNEL_SPINLOCK_H 9 10 #define SPINLOCK_LOCK 1 11 #define SPINLOCK_UNLOCK 0 12 13 #ifndef __ASSEMBLER__ 14 #include <assert.h> 15 #include <compiler.h> 16 #include <stdbool.h> 17 #include <kernel/thread.h> 18 19 #ifdef CFG_TEE_CORE_DEBUG 20 void spinlock_count_incr(void); 21 void spinlock_count_decr(void); 22 bool have_spinlock(void); 23 static inline void __nostackcheck assert_have_no_spinlock(void) 24 { 25 assert(!have_spinlock()); 26 } 27 #else 28 static inline void spinlock_count_incr(void) { } 29 static inline void spinlock_count_decr(void) { } 30 static inline void __nostackcheck assert_have_no_spinlock(void) { } 31 #endif 32 33 void __cpu_spin_lock(unsigned int *lock); 34 void __cpu_spin_unlock(unsigned int *lock); 35 /* returns 0 on locking success, non zero on failure */ 36 unsigned int __cpu_spin_trylock(unsigned int *lock); 37 38 static inline void cpu_spin_lock_no_dldetect(unsigned int *lock) 39 { 40 assert(thread_foreign_intr_disabled()); 41 __cpu_spin_lock(lock); 42 spinlock_count_incr(); 43 } 44 45 static inline bool thread_spin_trylock(unsigned int *lock) 46 { 47 assert(thread_get_id_may_fail() != THREAD_ID_INVALID); 48 return !__cpu_spin_trylock(lock); 49 } 50 51 /* 52 * To be used with lot of care: it is not recommended to spin in a thread 53 * context without masking foreign interrupts 54 */ 55 static inline void thread_spin_lock(unsigned int *lock) 56 { 57 assert(thread_get_id_may_fail() != THREAD_ID_INVALID); 58 __cpu_spin_lock(lock); 59 } 60 61 static inline void thread_spin_unlock(unsigned int *lock) 62 { 63 assert(thread_get_id_may_fail() != THREAD_ID_INVALID); 64 __cpu_spin_unlock(lock); 65 } 66 67 #ifdef CFG_TEE_CORE_DEBUG 68 #define cpu_spin_lock(lock) \ 69 cpu_spin_lock_dldetect(__func__, __LINE__, lock) 70 71 static inline void cpu_spin_lock_dldetect(const char *func, const int line, 72 unsigned int *lock) 73 { 74 unsigned int retries = 0; 75 unsigned int reminder = 0; 76 77 assert(thread_foreign_intr_disabled()); 78 79 while (__cpu_spin_trylock(lock)) { 80 retries++; 81 if (!retries) { 82 /* wrapped, time to report */ 83 trace_printf(func, line, TRACE_ERROR, true, 84 "possible spinlock deadlock reminder %u", 85 reminder); 86 if (reminder < UINT_MAX) 87 reminder++; 88 } 89 } 90 91 spinlock_count_incr(); 92 } 93 #else 94 static inline void cpu_spin_lock(unsigned int *lock) 95 { 96 cpu_spin_lock_no_dldetect(lock); 97 } 98 #endif 99 100 static inline bool cpu_spin_trylock(unsigned int *lock) 101 { 102 unsigned int rc; 103 104 assert(thread_foreign_intr_disabled()); 105 rc = __cpu_spin_trylock(lock); 106 if (!rc) 107 spinlock_count_incr(); 108 return !rc; 109 } 110 111 static inline void cpu_spin_unlock(unsigned int *lock) 112 { 113 assert(thread_foreign_intr_disabled()); 114 __cpu_spin_unlock(lock); 115 spinlock_count_decr(); 116 } 117 118 static inline uint32_t __must_check 119 cpu_spin_lock_xsave_no_dldetect(unsigned int *lock) 120 { 121 uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); 122 123 cpu_spin_lock(lock); 124 return exceptions; 125 } 126 127 #ifdef CFG_TEE_CORE_DEBUG 128 #define cpu_spin_lock_xsave(lock) \ 129 cpu_spin_lock_xsave_dldetect(__func__, __LINE__, lock) 130 131 static inline uint32_t __must_check 132 cpu_spin_lock_xsave_dldetect(const char *func, const int line, 133 unsigned int *lock) 134 { 135 uint32_t exceptions = thread_mask_exceptions(THREAD_EXCP_ALL); 136 137 cpu_spin_lock_dldetect(func, line, lock); 138 return exceptions; 139 } 140 #else 141 static inline uint32_t __must_check cpu_spin_lock_xsave(unsigned int *lock) 142 { 143 return cpu_spin_lock_xsave_no_dldetect(lock); 144 } 145 #endif 146 147 static inline void cpu_spin_unlock_xrestore(unsigned int *lock, 148 uint32_t exceptions) 149 { 150 cpu_spin_unlock(lock); 151 thread_unmask_exceptions(exceptions); 152 } 153 #endif /* __ASSEMBLER__ */ 154 155 #endif /* __KERNEL_SPINLOCK_H */ 156