1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * This software may be distributed and modified according to the terms of 5 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 6 * See "LICENSE_GPLv2.txt" for details. 7 * 8 * @TAG(GD_GPL) 9 */ 10 11#include <assert.h> 12 13#include <types.h> 14#include <kernel/thread.h> 15#include <object/structures.h> 16#include <object/tcb.h> 17#include <object/endpoint.h> 18#include <model/statedata.h> 19#include <machine/io.h> 20 21#include <object/notification.h> 22 23static inline tcb_queue_t PURE 24ntfn_ptr_get_queue(notification_t *ntfnPtr) 25{ 26 tcb_queue_t ntfn_queue; 27 28 ntfn_queue.head = (tcb_t*)notification_ptr_get_ntfnQueue_head(ntfnPtr); 29 ntfn_queue.end = (tcb_t*)notification_ptr_get_ntfnQueue_tail(ntfnPtr); 30 31 return ntfn_queue; 32} 33 34static inline void 35ntfn_ptr_set_queue(notification_t *ntfnPtr, tcb_queue_t ntfn_queue) 36{ 37 notification_ptr_set_ntfnQueue_head(ntfnPtr, (word_t)ntfn_queue.head); 38 notification_ptr_set_ntfnQueue_tail(ntfnPtr, (word_t)ntfn_queue.end); 39} 40 41static inline void 42ntfn_set_active(notification_t *ntfnPtr, word_t badge) 43{ 44 notification_ptr_set_state(ntfnPtr, NtfnState_Active); 45 notification_ptr_set_ntfnMsgIdentifier(ntfnPtr, badge); 46} 47 48static inline void 49maybeDonateSchedContext(tcb_t *tcb, notification_t *ntfnPtr) 50{ 51 if (tcb->tcbSchedContext == NULL) { 52 sched_context_t *sc = SC_PTR(notification_ptr_get_ntfnSchedContext(ntfnPtr)); 53 if (sc != NULL && sc->scTcb == NULL) { 54 schedContext_donate(sc, tcb); 55 } 56 } 57} 58 59static inline void 60maybeReturnSchedContext(notification_t *ntfnPtr, tcb_t *tcb) 61{ 62 63 sched_context_t *sc = SC_PTR(notification_ptr_get_ntfnSchedContext(ntfnPtr)); 64 if (sc == tcb->tcbSchedContext) { 65 tcb->tcbSchedContext = NULL; 66 sc->scTcb = NULL; 67 } 68} 69 70void 71sendSignal(notification_t *ntfnPtr, word_t badge) 72{ 73 switch (notification_ptr_get_state(ntfnPtr)) { 74 case NtfnState_Idle: { 75 tcb_t *tcb = (tcb_t*)notification_ptr_get_ntfnBoundTCB(ntfnPtr); 76 /* Check if we are bound and that thread is waiting for a message */ 77 if (tcb) { 78 if (thread_state_ptr_get_tsType(&tcb->tcbState) == ThreadState_BlockedOnReceive) { 79 /* Send and start thread running */ 80 cancelIPC(tcb); 81 maybeDonateSchedContext(tcb, ntfnPtr); 82 setThreadState(tcb, ThreadState_Running); 83 setRegister(tcb, badgeRegister, badge); 84 possibleSwitchTo(tcb); 85#ifdef CONFIG_VTX 86 } else if (thread_state_ptr_get_tsType(&tcb->tcbState) == ThreadState_RunningVM) { 87#ifdef ENABLE_SMP_SUPPORT 88 if (tcb->tcbAffinity != getCurrentCPUIndex()) { 89 ntfn_set_active(ntfnPtr, badge); 90 doRemoteVMCheckBoundNotification(tcb->tcbAffinity, tcb); 91 } else 92#endif /* ENABLE_SMP_SUPPORT */ 93 { 94 maybeDonateSchedContext(tcb, ntfnPtr); 95 setThreadState(tcb, ThreadState_Running); 96 setRegister(tcb, badgeRegister, badge); 97 Arch_leaveVMAsyncTransfer(tcb); 98 possibleSwitchTo(tcb); 99 } 100#endif /* CONFIG_VTX */ 101 } else { 102 /* In particular, this path is taken when a thread 103 * is waiting on a reply cap since BlockedOnReply 104 * would also trigger this path. I.e, a thread 105 * with a bound notification will not be awakened 106 * by signals on that bound notification if it is 107 * in the middle of an seL4_Call. 108 */ 109 ntfn_set_active(ntfnPtr, badge); 110 } 111 } else { 112 ntfn_set_active(ntfnPtr, badge); 113 } 114 break; 115 } 116 case NtfnState_Waiting: { 117 tcb_queue_t ntfn_queue; 118 tcb_t *dest; 119 120 ntfn_queue = ntfn_ptr_get_queue(ntfnPtr); 121 dest = ntfn_queue.head; 122 123 /* Haskell error "WaitingNtfn Notification must have non-empty queue" */ 124 assert(dest); 125 126 /* Dequeue TCB */ 127 ntfn_queue = tcbEPDequeue(dest, ntfn_queue); 128 ntfn_ptr_set_queue(ntfnPtr, ntfn_queue); 129 130 /* set the thread state to idle if the queue is empty */ 131 if (!ntfn_queue.head) { 132 notification_ptr_set_state(ntfnPtr, NtfnState_Idle); 133 } 134 135 maybeDonateSchedContext(dest, ntfnPtr); 136 setThreadState(dest, ThreadState_Running); 137 setRegister(dest, badgeRegister, badge); 138 possibleSwitchTo(dest); 139 break; 140 } 141 142 case NtfnState_Active: { 143 word_t badge2; 144 145 badge2 = notification_ptr_get_ntfnMsgIdentifier(ntfnPtr); 146 badge2 |= badge; 147 148 notification_ptr_set_ntfnMsgIdentifier(ntfnPtr, badge2); 149 break; 150 } 151 } 152} 153 154void 155receiveSignal(tcb_t *thread, cap_t cap, bool_t isBlocking) 156{ 157 notification_t *ntfnPtr; 158 159 ntfnPtr = NTFN_PTR(cap_notification_cap_get_capNtfnPtr(cap)); 160 161 switch (notification_ptr_get_state(ntfnPtr)) { 162 case NtfnState_Idle: 163 case NtfnState_Waiting: { 164 tcb_queue_t ntfn_queue; 165 166 if (isBlocking) { 167 /* Block thread on notification object */ 168 thread_state_ptr_set_tsType(&thread->tcbState, 169 ThreadState_BlockedOnNotification); 170 thread_state_ptr_set_blockingObject(&thread->tcbState, 171 NTFN_REF(ntfnPtr)); 172 maybeReturnSchedContext(ntfnPtr, thread); 173 scheduleTCB(thread); 174 175 /* Enqueue TCB */ 176 ntfn_queue = ntfn_ptr_get_queue(ntfnPtr); 177 ntfn_queue = tcbEPAppend(thread, ntfn_queue); 178 179 notification_ptr_set_state(ntfnPtr, NtfnState_Waiting); 180 ntfn_ptr_set_queue(ntfnPtr, ntfn_queue); 181 } else { 182 doNBRecvFailedTransfer(thread); 183 } 184 185 break; 186 } 187 188 case NtfnState_Active: 189 setRegister( 190 thread, badgeRegister, 191 notification_ptr_get_ntfnMsgIdentifier(ntfnPtr)); 192 notification_ptr_set_state(ntfnPtr, NtfnState_Idle); 193 maybeDonateSchedContext(thread, ntfnPtr); 194 break; 195 } 196} 197 198void 199cancelAllSignals(notification_t *ntfnPtr) 200{ 201 if (notification_ptr_get_state(ntfnPtr) == NtfnState_Waiting) { 202 tcb_t *thread = TCB_PTR(notification_ptr_get_ntfnQueue_head(ntfnPtr)); 203 204 notification_ptr_set_state(ntfnPtr, NtfnState_Idle); 205 notification_ptr_set_ntfnQueue_head(ntfnPtr, 0); 206 notification_ptr_set_ntfnQueue_tail(ntfnPtr, 0); 207 208 /* Set all waiting threads to Restart */ 209 for (; thread; thread = thread->tcbEPNext) { 210 setThreadState(thread, ThreadState_Restart); 211 possibleSwitchTo(thread); 212 } 213 rescheduleRequired(); 214 } 215} 216 217void 218cancelSignal(tcb_t *threadPtr, notification_t *ntfnPtr) 219{ 220 tcb_queue_t ntfn_queue; 221 222 /* Haskell error "cancelSignal: notification object must be in a waiting" state */ 223 assert(notification_ptr_get_state(ntfnPtr) == NtfnState_Waiting); 224 225 /* Dequeue TCB */ 226 ntfn_queue = ntfn_ptr_get_queue(ntfnPtr); 227 ntfn_queue = tcbEPDequeue(threadPtr, ntfn_queue); 228 ntfn_ptr_set_queue(ntfnPtr, ntfn_queue); 229 230 /* Make notification object idle */ 231 if (!ntfn_queue.head) { 232 notification_ptr_set_state(ntfnPtr, NtfnState_Idle); 233 } 234 235 /* Make thread inactive */ 236 setThreadState(threadPtr, ThreadState_Inactive); 237} 238 239void 240completeSignal(notification_t *ntfnPtr, tcb_t *tcb) 241{ 242 word_t badge; 243 244 if (likely(tcb && notification_ptr_get_state(ntfnPtr) == NtfnState_Active)) { 245 badge = notification_ptr_get_ntfnMsgIdentifier(ntfnPtr); 246 setRegister(tcb, badgeRegister, badge); 247 notification_ptr_set_state(ntfnPtr, NtfnState_Idle); 248 } else { 249 fail("tried to complete signal with inactive notification object"); 250 } 251} 252 253static inline void 254doUnbindNotification(notification_t *ntfnPtr, tcb_t *tcbptr) 255{ 256 notification_ptr_set_ntfnBoundTCB(ntfnPtr, (word_t) 0); 257 tcbptr->tcbBoundNotification = NULL; 258} 259 260void 261unbindMaybeNotification(notification_t *ntfnPtr) 262{ 263 tcb_t *boundTCB; 264 boundTCB = (tcb_t*)notification_ptr_get_ntfnBoundTCB(ntfnPtr); 265 266 if (boundTCB) { 267 doUnbindNotification(ntfnPtr, boundTCB); 268 } 269} 270 271void 272unbindNotification(tcb_t *tcb) 273{ 274 notification_t *ntfnPtr; 275 ntfnPtr = tcb->tcbBoundNotification; 276 277 if (ntfnPtr) { 278 doUnbindNotification(ntfnPtr, tcb); 279 } 280} 281 282void 283bindNotification(tcb_t *tcb, notification_t *ntfnPtr) 284{ 285 notification_ptr_set_ntfnBoundTCB(ntfnPtr, (word_t)tcb); 286 tcb->tcbBoundNotification = ntfnPtr; 287} 288 289void 290reorderNTFN(notification_t *ntfnPtr, tcb_t *thread) 291{ 292 tcb_queue_t queue = ntfn_ptr_get_queue(ntfnPtr); 293 queue = tcbEPDequeue(thread, queue); 294 queue = tcbEPAppend(thread, queue); 295 ntfn_ptr_set_queue(ntfnPtr, queue); 296} 297