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