1/* 2 * Copyright 2016, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 8 * See "LICENSE_GPLv2.txt" for details. 9 * 10 * @TAG(D61_GPL) 11 */ 12#include <object/reply.h> 13 14void 15reply_push(tcb_t *tcb_caller, tcb_t *tcb_callee, reply_t *reply, bool_t canDonate) 16{ 17 sched_context_t *sc_donated = tcb_caller->tcbSchedContext; 18 19 assert(tcb_caller != NULL); 20 assert(reply != NULL); 21 assert(reply->replyTCB == NULL); 22 23 if (tcb_callee->tcbSchedContext) { 24 /* receiver already has sc */ 25 canDonate = false; 26 } 27 28 assert(call_stack_get_callStackPtr(reply->replyPrev) == 0); 29 assert(call_stack_get_callStackPtr(reply->replyNext) == 0); 30 31 /* tcb caller should not be in a existing call stack */ 32 assert(thread_state_get_replyObject(tcb_caller->tcbState) == 0); 33 34 /* unlink callee and reply - they may not have been linked already, 35 * if this rendesvous is occuring when seL4_Recv is called, 36 * however, no harm in overring 0 with 0 */ 37 thread_state_ptr_set_replyObject(&tcb_callee->tcbState, 0); 38 39 /* link caller and reply */ 40 reply->replyTCB = tcb_caller; 41 thread_state_ptr_set_replyObject(&tcb_caller->tcbState, REPLY_REF(reply)); 42 setThreadState(tcb_caller, ThreadState_BlockedOnReply); 43 44 if (sc_donated != NULL && canDonate) { 45 assert(tcb_callee->tcbSchedContext == NULL); 46 47 reply_t *old_caller = sc_donated->scReply; 48 49 /* check stack integrity */ 50 assert(old_caller == NULL || 51 SC_PTR(call_stack_get_callStackPtr(old_caller->replyNext)) == sc_donated); 52 53 /* push on to stack */ 54 reply->replyPrev = call_stack_new(REPLY_REF(old_caller), false); 55 if (old_caller) { 56 old_caller->replyNext = call_stack_new(REPLY_REF(reply), false); 57 } 58 reply->replyNext = call_stack_new(SC_REF(sc_donated), true); 59 sc_donated->scReply = reply; 60 61 /* now do the actual donation */ 62 schedContext_donate(sc_donated, tcb_callee); 63 } 64} 65 66/* Pop the head reply from the call stack */ 67void 68reply_pop(reply_t *reply) 69{ 70 assert(reply != NULL); 71 assert(reply->replyTCB != NULL); 72 assert(thread_state_get_tsType(reply->replyTCB->tcbState) == ThreadState_BlockedOnReply); 73 /* unlink tcb and reply */ 74 tcb_t *tcb = reply->replyTCB; 75 reply_unlink(reply); 76 77 word_t next_ptr = call_stack_get_callStackPtr(reply->replyNext); 78 word_t prev_ptr = call_stack_get_callStackPtr(reply->replyPrev); 79 80 if (likely(next_ptr != 0)) { 81 assert(call_stack_get_isHead(reply->replyNext)); 82 83 /* give it back */ 84 if (tcb->tcbSchedContext == NULL) { 85 /* only give the SC back if our SC is NULL. This prevents 86 * strange behaviour when a thread is bound to an sc while it is 87 * in the BlockedOnReply state. The semantics in this case are that the 88 * SC cannot go back to the caller if the caller has received another one */ 89 schedContext_donate(SC_PTR(next_ptr), tcb); 90 } 91 92 SC_PTR(next_ptr)->scReply = REPLY_PTR(prev_ptr); 93 if (prev_ptr != 0) { 94 REPLY_PTR(prev_ptr)->replyNext = reply->replyNext; 95 assert(call_stack_get_isHead(REPLY_PTR(prev_ptr)->replyNext)); 96 } 97 98 reply->replyPrev = call_stack_new(0, false); 99 reply->replyNext = call_stack_new(0, false); 100 } 101} 102 103/* Remove a reply from the middle of the call stack */ 104void 105reply_remove(reply_t *reply) 106{ 107 assert(thread_state_get_tsType(reply->replyTCB->tcbState) == ThreadState_BlockedOnReply); 108 109 word_t next_ptr = call_stack_get_callStackPtr(reply->replyNext); 110 word_t prev_ptr = call_stack_get_callStackPtr(reply->replyPrev); 111 112 if (likely(next_ptr)) { 113 if (likely(call_stack_get_isHead(reply->replyNext))) { 114 /* head of the call stack -> just pop */ 115 reply_pop(reply); 116 return; 117 } 118 /* not the head, remove from middle */ 119 REPLY_PTR(next_ptr)->replyPrev = reply->replyPrev; 120 if (REPLY_PTR(next_ptr)->replyTCB) { 121 reply_unlink(REPLY_PTR(next_ptr)); 122 } 123 tcb_t *tcb = reply->replyTCB; 124 assert(tcb); 125 /* to maintain the call chain, we remove this caller and 126 * replaced them with the next - so the TCB that the reply object 127 * we are passing in is linked to is linked to the next reply object, and the 128 * tcb of the next_ptr is dropped */ 129 REPLY_PTR(next_ptr)->replyTCB = tcb; 130 reply->replyTCB = NULL; 131 thread_state_ptr_set_replyObject(&tcb->tcbState, REPLY_REF(next_ptr)); 132 } else if (reply->replyTCB) { 133 /* removing start of call chain */ 134 reply_unlink(reply); 135 } 136 137 if (prev_ptr) { 138 REPLY_PTR(prev_ptr)->replyNext = reply->replyNext; 139 } 140 141 reply->replyPrev = call_stack_new(0, false); 142 reply->replyNext = call_stack_new(0, false); 143} 144 145void reply_remove_tcb(tcb_t *tcb) 146{ 147 assert(thread_state_get_tsType(tcb->tcbState) == ThreadState_BlockedOnReply); 148 reply_t *reply = REPLY_PTR(thread_state_get_replyObject(tcb->tcbState)); 149 word_t next_ptr = call_stack_get_callStackPtr(reply->replyNext); 150 word_t prev_ptr = call_stack_get_callStackPtr(reply->replyPrev); 151 152 153 if (call_stack_get_isHead(reply->replyNext)) { 154 reply_pop(reply); 155 return; 156 } 157 158 if (next_ptr) { 159 REPLY_PTR(next_ptr)->replyPrev = reply->replyPrev; 160 } 161 162 if (prev_ptr) { 163 REPLY_PTR(prev_ptr)->replyNext = reply->replyNext; 164 } 165 166 reply->replyPrev = call_stack_new(0, false); 167 reply->replyNext = call_stack_new(0, false); 168 reply_unlink(reply); 169} 170