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 <kernel/thread.h>
17 #include <stdbool.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);
assert_have_no_spinlock(void)23 static inline void __nostackcheck assert_have_no_spinlock(void)
24 {
25 	assert(!have_spinlock());
26 }
27 #else
spinlock_count_incr(void)28 static inline void spinlock_count_incr(void) { }
spinlock_count_decr(void)29 static inline void spinlock_count_decr(void) { }
assert_have_no_spinlock(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 
cpu_spin_lock_no_dldetect(unsigned int * lock)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 
thread_spin_trylock(unsigned int * lock)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  */
thread_spin_lock(unsigned int * lock)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 
thread_spin_unlock(unsigned int * lock)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 
cpu_spin_lock_dldetect(const char * func,const int line,unsigned int * lock)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
cpu_spin_lock(unsigned int * lock)94 static inline void cpu_spin_lock(unsigned int *lock)
95 {
96 	cpu_spin_lock_no_dldetect(lock);
97 }
98 #endif
99 
cpu_spin_trylock(unsigned int * lock)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 
cpu_spin_unlock(unsigned int * lock)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
cpu_spin_lock_xsave_no_dldetect(unsigned int * lock)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
cpu_spin_lock_xsave_dldetect(const char * func,const int line,unsigned int * lock)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
cpu_spin_lock_xsave(unsigned int * lock)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 
cpu_spin_unlock_xrestore(unsigned int * lock,uint32_t exceptions)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