1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2021, Linaro Limited
4  */
5 
6 #include <bitstring.h>
7 #include <drivers/gic.h>
8 #include <kernel/interrupt.h>
9 #include <kernel/mutex.h>
10 #include <kernel/notif.h>
11 #include <kernel/spinlock.h>
12 #include <kernel/thread.h>
13 #include <optee_rpc_cmd.h>
14 #include <sm/optee_smc.h>
15 #include <types_ext.h>
16 
17 #if defined(CFG_CORE_ASYNC_NOTIF)
18 static struct mutex notif_mutex = MUTEX_INITIALIZER;
19 static unsigned int notif_lock = SPINLOCK_UNLOCK;
20 
21 SLIST_HEAD(notif_driver_head, notif_driver);
22 static struct notif_driver_head notif_driver_head =
23 	SLIST_HEAD_INITIALIZER(&notif_driver_head);
24 
25 static bitstr_t bit_decl(notif_values, NOTIF_ASYNC_VALUE_MAX + 1);
26 static bitstr_t bit_decl(notif_alloc_values, NOTIF_ASYNC_VALUE_MAX + 1);
27 static bool notif_started;
28 
notif_alloc_async_value(uint32_t * val)29 TEE_Result notif_alloc_async_value(uint32_t *val)
30 {
31 	static bool alloc_values_inited;
32 	uint32_t old_itr_status = 0;
33 	int bit = 0;
34 
35 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
36 
37 	if (!alloc_values_inited) {
38 		bit_set(notif_alloc_values, NOTIF_VALUE_DO_BOTTOM_HALF);
39 		alloc_values_inited = true;
40 	}
41 
42 	bit_ffc(notif_alloc_values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
43 	if (bit >= 0) {
44 		*val = bit;
45 		bit_set(notif_alloc_values, bit);
46 	}
47 
48 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
49 
50 	if (bit < 0)
51 		return TEE_ERROR_OUT_OF_MEMORY;
52 
53 	return TEE_SUCCESS;
54 }
55 
notif_free_async_value(uint32_t val)56 void notif_free_async_value(uint32_t val)
57 {
58 	uint32_t old_itr_status = 0;
59 
60 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
61 
62 	assert(val < NOTIF_ASYNC_VALUE_MAX);
63 	assert(bit_test(notif_alloc_values, val));
64 	bit_clear(notif_alloc_values, val);
65 
66 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
67 }
68 
notif_get_value(bool * value_valid,bool * value_pending)69 uint32_t notif_get_value(bool *value_valid, bool *value_pending)
70 {
71 	uint32_t old_itr_status = 0;
72 	uint32_t res = 0;
73 	int bit = 0;
74 
75 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
76 
77 	bit_ffs(notif_values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
78 	*value_valid = (bit >= 0);
79 	if (!*value_valid) {
80 		*value_pending = false;
81 		goto out;
82 	}
83 
84 	res = bit;
85 	bit_clear(notif_values, res);
86 	bit_ffs(notif_values, (int)NOTIF_ASYNC_VALUE_MAX + 1, &bit);
87 	*value_pending = (bit >= 0);
88 out:
89 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
90 
91 	return res;
92 }
93 
notif_send_async(uint32_t value)94 void notif_send_async(uint32_t value)
95 {
96 	uint32_t old_itr_status = 0;
97 
98 	COMPILE_TIME_ASSERT(NOTIF_VALUE_DO_BOTTOM_HALF ==
99 			    OPTEE_SMC_ASYNC_NOTIF_VALUE_DO_BOTTOM_HALF);
100 	COMPILE_TIME_ASSERT(CFG_CORE_ASYNC_NOTIF_GIC_INTID >= GIC_SPI_BASE);
101 
102 	assert(value <= NOTIF_ASYNC_VALUE_MAX);
103 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
104 
105 	DMSG("0x%"PRIx32, value);
106 	bit_set(notif_values, value);
107 	itr_raise_pi(CFG_CORE_ASYNC_NOTIF_GIC_INTID);
108 
109 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
110 }
111 
notif_async_is_started(void)112 bool notif_async_is_started(void)
113 {
114 	uint32_t old_itr_status = 0;
115 	bool ret = false;
116 
117 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
118 	ret = notif_started;
119 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
120 
121 	return ret;
122 }
123 
notif_register_driver(struct notif_driver * ndrv)124 void notif_register_driver(struct notif_driver *ndrv)
125 {
126 	uint32_t old_itr_status = 0;
127 
128 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
129 
130 	SLIST_INSERT_HEAD(&notif_driver_head, ndrv, link);
131 
132 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
133 }
134 
notif_unregister_driver(struct notif_driver * ndrv)135 void notif_unregister_driver(struct notif_driver *ndrv)
136 {
137 	uint32_t old_itr_status = 0;
138 
139 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
140 
141 	SLIST_REMOVE(&notif_driver_head, ndrv, notif_driver, link);
142 
143 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
144 }
145 
notif_deliver_atomic_event(enum notif_event ev)146 void notif_deliver_atomic_event(enum notif_event ev)
147 {
148 	uint32_t old_itr_status = 0;
149 	struct notif_driver *nd = NULL;
150 
151 	assert(ev == NOTIF_EVENT_STARTED);
152 
153 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
154 
155 	if (notif_started) {
156 		DMSG("Already started");
157 		goto out;
158 	}
159 	notif_started = true;
160 
161 	SLIST_FOREACH(nd, &notif_driver_head, link)
162 		if (nd->atomic_cb)
163 			nd->atomic_cb(nd, ev);
164 
165 out:
166 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
167 }
168 
notif_deliver_event(enum notif_event ev)169 void notif_deliver_event(enum notif_event ev)
170 {
171 	uint32_t old_itr_status = 0;
172 	struct notif_driver *nd = NULL;
173 	struct notif_driver *nd_tmp = NULL;
174 
175 	assert(ev == NOTIF_EVENT_DO_BOTTOM_HALF || ev == NOTIF_EVENT_STOPPED);
176 
177 	/* Serialize all yielding notifications */
178 	mutex_lock(&notif_mutex);
179 	old_itr_status = cpu_spin_lock_xsave(&notif_lock);
180 
181 	if (!notif_started) {
182 		DMSG("Not started ev %d", (int)ev);
183 		goto out;
184 	}
185 
186 	if (ev == NOTIF_EVENT_STOPPED)
187 		notif_started = false;
188 
189 	SLIST_FOREACH_SAFE(nd, &notif_driver_head, link, nd_tmp) {
190 		cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
191 
192 		if (nd->yielding_cb)
193 			nd->yielding_cb(nd, ev);
194 
195 		old_itr_status = cpu_spin_lock_xsave(&notif_lock);
196 
197 		if (ev == NOTIF_EVENT_STOPPED && notif_started) {
198 			DMSG("Started again while stopping");
199 			goto out;
200 		}
201 	}
202 
203 out:
204 	cpu_spin_unlock_xrestore(&notif_lock, old_itr_status);
205 	mutex_unlock(&notif_mutex);
206 }
207 #endif /*CFG_CORE_ASYNC_NOTIF*/
208 
notif_rpc(uint32_t func,uint32_t value)209 static TEE_Result notif_rpc(uint32_t func, uint32_t value)
210 {
211 	struct thread_param params = THREAD_PARAM_VALUE(IN, func, value, 0);
212 
213 	return thread_rpc_cmd(OPTEE_RPC_CMD_NOTIFICATION, 1, &params);
214 }
215 
notif_wait(uint32_t value)216 TEE_Result notif_wait(uint32_t value)
217 {
218 	return notif_rpc(OPTEE_RPC_NOTIFICATION_WAIT, value);
219 }
220 
notif_send_sync(uint32_t value)221 TEE_Result notif_send_sync(uint32_t value)
222 {
223 	return notif_rpc(OPTEE_RPC_NOTIFICATION_SEND, value);
224 }
225