1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * Copyright (c) 2021, Linaro Limited
4 */
5
6 #ifndef __KERNEL_NOTIF_H
7 #define __KERNEL_NOTIF_H
8
9 #include <compiler.h>
10 #include <sys/queue.h>
11 #include <tee_api_types.h>
12 #include <types_ext.h>
13 #include <config.h>
14
15 /*
16 * Notification values are divided into two kinds, asynchronous and
17 * synchronous, where the asynchronous has the lowest values.
18 * They are ordered as:
19 * 0 Do bottom half
20 * 1..NOTIF_ASYNC_MAX Free for signalling in PTAs and should be
21 * allocated with notif_alloc_async_value()
22 * NOTIF_SYNC_VALUE_BASE.. Used as NOTIF_SYNC_VALUE_BASE + thread_id
23 * NOTIF_VALUE_MAX for mutex and condvar wait/wakeup
24 *
25 * Any value can be signalled with notif_send_sync() while only the ones
26 * <= NOTIF_ASYNC_VALUE_MAX can be signalled with notif_send_async().
27 */
28
29 #if defined(CFG_CORE_ASYNC_NOTIF)
30 #define NOTIF_ASYNC_VALUE_MAX U(63)
31 #define NOTIF_SYNC_VALUE_BASE (NOTIF_ASYNC_VALUE_MAX + U(1))
32 #else
33 #define NOTIF_SYNC_VALUE_BASE 0
34 #endif
35
36 #define NOTIF_VALUE_MAX (NOTIF_SYNC_VALUE_BASE + \
37 CFG_NUM_THREADS)
38
39 #define NOTIF_VALUE_DO_BOTTOM_HALF 0
40
41 /*
42 * enum notif_event - Notification of an event
43 * @NOTIF_EVENT_STARTED: Delivered in an atomic context to inform
44 * drivers that normal world has enabled
45 * asynchronous notifications.
46 * @NOTIF_EVENT_DO_BOTTOM_HALF: Delivered in a yielding context to let a
47 * driver do bottom half processing.
48 * @NOTIF_EVENT_STOPPED: Delivered in a yielding contest to inform
49 * drivers that normal world is about to disable
50 * asynchronous notifications.
51 *
52 * Once a driver has received a @NOTIF_EVENT_STARTED asynchronous notifications
53 * driving the @NOTIF_EVENT_DO_BOTTOM_HALF deliveries is enabled.
54 *
55 * In case a @NOTIF_EVENT_STOPPED is received there will be no more
56 * @NOTIF_EVENT_DO_BOTTOM_HALF events delivered, until @NOTIF_EVENT_STARTED
57 * has been delivered again.
58 *
59 * Note that while a @NOTIF_EVENT_STOPPED is being delivered at the same
60 * time may a @NOTIF_EVENT_STARTED be delivered again so a driver is
61 * required to sychronize accesses to its internal state.
62 */
63 enum notif_event {
64 NOTIF_EVENT_STARTED,
65 NOTIF_EVENT_DO_BOTTOM_HALF,
66 NOTIF_EVENT_STOPPED,
67 NOTIF_EVENT_SHUTDOWN,
68 };
69
70 /*
71 * struct notif_driver - Registration of driver notification
72 * @atomic_cb: A callback called in an atomic context from
73 * notif_deliver_atomic_event(). Currently only used to
74 * signal @NOTIF_EVENT_STARTED.
75 * @yielding_cb: A callback called in a yielding context from
76 * notif_deliver_event(). Currently only used to signal
77 * @NOTIF_EVENT_DO_BOTTOM_HALF and @NOTIF_EVENT_STOPPED.
78 *
79 * A atomic context means that interrupts are masked and a common spinlock
80 * is held. Calls via @atomic_cb are only atomic with regards to each
81 * other, other CPUs may execute yielding calls or even receive interrupts.
82 * If CFG_NS_VIRTUALIZATION=y then code is executing in Nexus context
83 * without a partition activated, the @guest_id triggering the event is
84 * instead supplied as an argument to the callback. The @guest_id should be
85 * ignored if CFG_NS_VIRTUALIZATION isn't enabled.
86 *
87 * A yielding context means that the function is executing in a normal
88 * threaded context allowing RPC and synchronization with other thread
89 * using mutexes and condition variables. If CFG_NS_VIRTUALIZATION=y then
90 * is a partition matching the guest or VM that triggered the event.
91 */
92 struct notif_driver {
93 void (*atomic_cb)(struct notif_driver *ndrv, enum notif_event ev,
94 uint16_t guest_id);
95 void (*yielding_cb)(struct notif_driver *ndrv, enum notif_event ev);
96 SLIST_ENTRY(notif_driver) link;
97 };
98
99 #if defined(CFG_CORE_ASYNC_NOTIF)
100 bool notif_async_is_started(uint16_t guest_id);
101 #else
notif_async_is_started(uint16_t guest_id __unused)102 static inline bool notif_async_is_started(uint16_t guest_id __unused)
103 {
104 return false;
105 }
106 #endif
107
108 TEE_Result notif_alloc_async_value(uint32_t *value);
109 void notif_free_async_value(uint32_t value);
110
111 /*
112 * Wait in normal world for a value to be sent by notif_send()
113 */
114 TEE_Result notif_wait(uint32_t value);
115
116 /*
117 * Wait timeout in normal world for a value to be sent by notif_send()
118 */
119 TEE_Result notif_wait_timeout(uint32_t value, uint32_t timeout_ms);
120
121 /*
122 * Send an asynchronous value, note that it must be <= NOTIF_ASYNC_VALUE_MAX
123 */
124 #if defined(CFG_CORE_ASYNC_NOTIF)
125 void notif_send_async(uint32_t value, uint16_t guest_id);
126 #else
notif_send_async(uint32_t value __unused,uint16_t guest_id __unused)127 static inline void notif_send_async(uint32_t value __unused,
128 uint16_t guest_id __unused)
129 {
130 }
131 #endif
132
133 /*
134 * Send a sychronous value, note that it must be <= NOTIF_VALUE_MAX. The
135 * notification is synchronous even if the value happens to belong in the
136 * asynchronous range.
137 */
138 TEE_Result notif_send_sync(uint32_t value);
139
140 /*
141 * Called by device drivers.
142 */
143 #if defined(CFG_CORE_ASYNC_NOTIF)
144 void notif_register_driver(struct notif_driver *ndrv);
145 void notif_unregister_driver(struct notif_driver *ndrv);
146 #else
notif_register_driver(struct notif_driver * ndrv __unused)147 static inline void notif_register_driver(struct notif_driver *ndrv __unused)
148 {
149 }
150
notif_unregister_driver(struct notif_driver * ndrv __unused)151 static inline void notif_unregister_driver(struct notif_driver *ndrv __unused)
152 {
153 }
154 #endif
155
156 /* This is called from a fast call */
157 #if defined(CFG_CORE_ASYNC_NOTIF)
158 uint32_t notif_get_value(bool *value_valid, bool *value_pending);
159 #else
notif_get_value(bool * value_valid,bool * value_pending)160 static inline uint32_t notif_get_value(bool *value_valid, bool *value_pending)
161 {
162 *value_valid = false;
163 *value_pending = false;
164 return UINT32_MAX;
165 }
166 #endif
167
168 #if defined(CFG_CORE_ASYNC_NOTIF)
169 void notif_deliver_atomic_event(enum notif_event ev, uint16_t guest_id);
170 void notif_deliver_event(enum notif_event ev);
171 #else
notif_deliver_atomic_event(enum notif_event ev __unused,uint16_t guest_id __unused)172 static inline void notif_deliver_atomic_event(enum notif_event ev __unused,
173 uint16_t guest_id __unused)
174 {
175 }
notif_deliver_event(enum notif_event ev __unused)176 static inline void notif_deliver_event(enum notif_event ev __unused)
177 {
178 }
179 #endif
180
181 #endif /*__KERNEL_NOTIF_H*/
182