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