1 /*
2  * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3  *
4  * SPDX-License-Identifier: GPL-2.0-only
5  */
6 
7 #include <object/reply.h>
8 
reply_push(tcb_t * tcb_caller,tcb_t * tcb_callee,reply_t * reply,bool_t canDonate)9 void reply_push(tcb_t *tcb_caller, tcb_t *tcb_callee, reply_t *reply, bool_t canDonate)
10 {
11     sched_context_t *sc_donated = tcb_caller->tcbSchedContext;
12 
13     assert(tcb_caller != NULL);
14     assert(reply != NULL);
15     assert(reply->replyTCB == NULL);
16 
17     assert(call_stack_get_callStackPtr(reply->replyPrev) == 0);
18     assert(call_stack_get_callStackPtr(reply->replyNext) == 0);
19 
20     /* tcb caller should not be in a existing call stack */
21     assert(thread_state_get_replyObject(tcb_caller->tcbState) == 0);
22 
23     /* unlink callee and reply - they may not have been linked already,
24      * if this rendesvous is occuring when seL4_Recv is called,
25      * however, no harm in overring 0 with 0 */
26     thread_state_ptr_set_replyObject(&tcb_callee->tcbState, 0);
27 
28     /* link caller and reply */
29     reply->replyTCB = tcb_caller;
30     thread_state_ptr_set_replyObject(&tcb_caller->tcbState, REPLY_REF(reply));
31     setThreadState(tcb_caller, ThreadState_BlockedOnReply);
32 
33     if (sc_donated != NULL && tcb_callee->tcbSchedContext == NULL && canDonate) {
34         reply_t *old_caller = sc_donated->scReply;
35 
36         /* check stack integrity */
37         assert(old_caller == NULL ||
38                SC_PTR(call_stack_get_callStackPtr(old_caller->replyNext)) == sc_donated);
39 
40         /* push on to stack */
41         reply->replyPrev = call_stack_new(REPLY_REF(old_caller), false);
42         if (old_caller) {
43             old_caller->replyNext = call_stack_new(REPLY_REF(reply), false);
44         }
45         reply->replyNext = call_stack_new(SC_REF(sc_donated), true);
46         sc_donated->scReply = reply;
47 
48         /* now do the actual donation */
49         schedContext_donate(sc_donated, tcb_callee);
50     }
51 }
52 
53 /* Pop the head reply from the call stack */
reply_pop(reply_t * reply,tcb_t * tcb)54 void reply_pop(reply_t *reply, tcb_t *tcb)
55 {
56     assert(reply != NULL);
57     assert(reply->replyTCB == tcb);
58     assert(thread_state_get_tsType(tcb->tcbState) == ThreadState_BlockedOnReply);
59     assert(thread_state_get_replyObject(tcb->tcbState) == REPLY_REF(reply));
60 
61     word_t next_ptr = call_stack_get_callStackPtr(reply->replyNext);
62     word_t prev_ptr = call_stack_get_callStackPtr(reply->replyPrev);
63 
64     if (likely(next_ptr != 0)) {
65         assert(call_stack_get_isHead(reply->replyNext));
66 
67         SC_PTR(next_ptr)->scReply = REPLY_PTR(prev_ptr);
68         if (prev_ptr != 0) {
69             REPLY_PTR(prev_ptr)->replyNext = reply->replyNext;
70             assert(call_stack_get_isHead(REPLY_PTR(prev_ptr)->replyNext));
71         }
72 
73         /* give it back */
74         if (tcb->tcbSchedContext == NULL) {
75             /* only give the SC back if our SC is NULL. This prevents
76              * strange behaviour when a thread is bound to an sc while it is
77              * in the BlockedOnReply state. The semantics in this case are that the
78              * SC cannot go back to the caller if the caller has received another one */
79             schedContext_donate(SC_PTR(next_ptr), tcb);
80         }
81     }
82 
83     reply->replyPrev = call_stack_new(0, false);
84     reply->replyNext = call_stack_new(0, false);
85     reply_unlink(reply, tcb);
86 }
87 
88 /* Remove a reply from the middle of the call stack */
reply_remove(reply_t * reply,tcb_t * tcb)89 void reply_remove(reply_t *reply, tcb_t *tcb)
90 {
91     assert(reply->replyTCB == tcb);
92     assert(thread_state_get_tsType(tcb->tcbState) == ThreadState_BlockedOnReply);
93     assert(thread_state_get_replyObject(tcb->tcbState) == REPLY_REF(reply));
94 
95     word_t next_ptr = call_stack_get_callStackPtr(reply->replyNext);
96     word_t prev_ptr = call_stack_get_callStackPtr(reply->replyPrev);
97 
98     if (likely(next_ptr && call_stack_get_isHead(reply->replyNext))) {
99         /* head of the call stack -> just pop */
100         reply_pop(reply, tcb);
101     } else {
102         if (next_ptr) {
103             /* not the head, remove from middle - break the chain */
104             REPLY_PTR(next_ptr)->replyPrev = call_stack_new(0, false);
105         }
106         if (prev_ptr) {
107             REPLY_PTR(prev_ptr)->replyNext = call_stack_new(0, false);
108         }
109         reply->replyPrev = call_stack_new(0, false);
110         reply->replyNext = call_stack_new(0, false);
111         reply_unlink(reply, tcb);
112     }
113 }
114 
reply_remove_tcb(tcb_t * tcb)115 void reply_remove_tcb(tcb_t *tcb)
116 {
117     assert(thread_state_get_tsType(tcb->tcbState) == ThreadState_BlockedOnReply);
118     reply_t *reply = REPLY_PTR(thread_state_get_replyObject(tcb->tcbState));
119     word_t next_ptr = call_stack_get_callStackPtr(reply->replyNext);
120     word_t prev_ptr = call_stack_get_callStackPtr(reply->replyPrev);
121 
122     if (next_ptr) {
123         if (call_stack_get_isHead(reply->replyNext)) {
124             SC_PTR(next_ptr)->scReply = NULL;
125         } else {
126             REPLY_PTR(next_ptr)->replyPrev = call_stack_new(0, false);
127         }
128     }
129 
130     if (prev_ptr) {
131         REPLY_PTR(prev_ptr)->replyNext = call_stack_new(0, false);
132     }
133 
134     reply->replyPrev = call_stack_new(0, false);
135     reply->replyNext = call_stack_new(0, false);
136     reply_unlink(reply, tcb);
137 }
138