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