#include <types.h>
#include <kernel/thread.h>
#include <kernel/vspace.h>
#include <machine/registerset.h>
#include <model/statedata.h>
#include <object/notification.h>
#include <object/cnode.h>
#include <object/endpoint.h>
#include <object/tcb.h>
static inline tcb_queue_t PURE
ep_ptr_get_queue(endpoint_t *epptr)
{
tcb_queue_t queue;
queue.head = (tcb_t*)endpoint_ptr_get_epQueue_head(epptr);
queue.end = (tcb_t*)endpoint_ptr_get_epQueue_tail(epptr);
return queue;
}
static inline void
ep_ptr_set_queue(endpoint_t *epptr, tcb_queue_t queue)
{
endpoint_ptr_set_epQueue_head(epptr, (word_t)queue.head);
endpoint_ptr_set_epQueue_tail(epptr, (word_t)queue.end);
}
void
sendIPC(bool_t blocking, bool_t do_call, word_t badge,
bool_t canGrant, tcb_t *thread, endpoint_t *epptr)
{
switch (endpoint_ptr_get_state(epptr)) {
case EPState_Idle:
case EPState_Send:
if (blocking) {
tcb_queue_t queue;
thread_state_ptr_set_tsType(&thread->tcbState,
ThreadState_BlockedOnSend);
thread_state_ptr_set_blockingObject(
&thread->tcbState, EP_REF(epptr));
thread_state_ptr_set_blockingIPCBadge(
&thread->tcbState, badge);
thread_state_ptr_set_blockingIPCCanGrant(
&thread->tcbState, canGrant);
thread_state_ptr_set_blockingIPCIsCall(
&thread->tcbState, do_call);
scheduleTCB(thread);
queue = ep_ptr_get_queue(epptr);
queue = tcbEPAppend(thread, queue);
endpoint_ptr_set_state(epptr, EPState_Send);
ep_ptr_set_queue(epptr, queue);
}
break;
case EPState_Recv: {
tcb_queue_t queue;
tcb_t *dest;
queue = ep_ptr_get_queue(epptr);
dest = queue.head;
assert(dest);
queue = tcbEPDequeue(dest, queue);
ep_ptr_set_queue(epptr, queue);
if (!queue.head) {
endpoint_ptr_set_state(epptr, EPState_Idle);
}
doIPCTransfer(thread, epptr, badge, canGrant, dest);
setThreadState(dest, ThreadState_Running);
attemptSwitchTo(dest);
if (do_call ||
seL4_Fault_ptr_get_seL4_FaultType(&thread->tcbFault) != seL4_Fault_NullFault) {
if (canGrant) {
setupCallerCap(thread, dest);
} else {
setThreadState(thread, ThreadState_Inactive);
}
}
break;
}
}
}
void
receiveIPC(tcb_t *thread, cap_t cap, bool_t isBlocking)
{
endpoint_t *epptr;
notification_t *ntfnPtr;
assert(cap_get_capType(cap) == cap_endpoint_cap);
epptr = EP_PTR(cap_endpoint_cap_get_capEPPtr(cap));
ntfnPtr = thread->tcbBoundNotification;
if (ntfnPtr && notification_ptr_get_state(ntfnPtr) == NtfnState_Active) {
completeSignal(ntfnPtr, thread);
} else {
switch (endpoint_ptr_get_state(epptr)) {
case EPState_Idle:
case EPState_Recv: {
tcb_queue_t queue;
if (isBlocking) {
thread_state_ptr_set_tsType(&thread->tcbState,
ThreadState_BlockedOnReceive);
thread_state_ptr_set_blockingObject(
&thread->tcbState, EP_REF(epptr));
scheduleTCB(thread);
queue = ep_ptr_get_queue(epptr);
queue = tcbEPAppend(thread, queue);
endpoint_ptr_set_state(epptr, EPState_Recv);
ep_ptr_set_queue(epptr, queue);
} else {
doNBRecvFailedTransfer(thread);
}
break;
}
case EPState_Send: {
tcb_queue_t queue;
tcb_t *sender;
word_t badge;
bool_t canGrant;
bool_t do_call;
queue = ep_ptr_get_queue(epptr);
sender = queue.head;
assert(sender);
queue = tcbEPDequeue(sender, queue);
ep_ptr_set_queue(epptr, queue);
if (!queue.head) {
endpoint_ptr_set_state(epptr, EPState_Idle);
}
badge = thread_state_ptr_get_blockingIPCBadge(&sender->tcbState);
canGrant =
thread_state_ptr_get_blockingIPCCanGrant(&sender->tcbState);
doIPCTransfer(sender, epptr, badge,
canGrant, thread);
do_call = thread_state_ptr_get_blockingIPCIsCall(&sender->tcbState);
if (do_call ||
seL4_Fault_get_seL4_FaultType(sender->tcbFault) != seL4_Fault_NullFault) {
if (canGrant) {
setupCallerCap(sender, thread);
} else {
setThreadState(sender, ThreadState_Inactive);
}
} else {
setThreadState(sender, ThreadState_Running);
switchIfRequiredTo(sender);
}
break;
}
}
}
}
void
replyFromKernel_error(tcb_t *thread)
{
word_t len;
word_t *ipcBuffer;
ipcBuffer = lookupIPCBuffer(true, thread);
setRegister(thread, badgeRegister, 0);
len = setMRs_syscall_error(thread, ipcBuffer);
setRegister(thread, msgInfoRegister, wordFromMessageInfo(
seL4_MessageInfo_new(current_syscall_error.type, 0, 0, len)));
}
void
replyFromKernel_success_empty(tcb_t *thread)
{
setRegister(thread, badgeRegister, 0);
setRegister(thread, msgInfoRegister, wordFromMessageInfo(
seL4_MessageInfo_new(0, 0, 0, 0)));
}
void
cancelIPC(tcb_t *tptr)
{
thread_state_t *state = &tptr->tcbState;
switch (thread_state_ptr_get_tsType(state)) {
case ThreadState_BlockedOnSend:
case ThreadState_BlockedOnReceive: {
endpoint_t *epptr;
tcb_queue_t queue;
epptr = EP_PTR(thread_state_ptr_get_blockingObject(state));
assert(endpoint_ptr_get_state(epptr) != EPState_Idle);
queue = ep_ptr_get_queue(epptr);
queue = tcbEPDequeue(tptr, queue);
ep_ptr_set_queue(epptr, queue);
if (!queue.head) {
endpoint_ptr_set_state(epptr, EPState_Idle);
}
setThreadState(tptr, ThreadState_Inactive);
break;
}
case ThreadState_BlockedOnNotification:
cancelSignal(tptr,
NTFN_PTR(thread_state_ptr_get_blockingObject(state)));
break;
case ThreadState_BlockedOnReply: {
cte_t *slot, *callerCap;
tptr->tcbFault = seL4_Fault_NullFault_new();
slot = TCB_PTR_CTE_PTR(tptr, tcbReply);
callerCap = CTE_PTR(mdb_node_get_mdbNext(slot->cteMDBNode));
if (callerCap) {
cteDeleteOne(callerCap);
}
break;
}
}
}
void
cancelAllIPC(endpoint_t *epptr)
{
switch (endpoint_ptr_get_state(epptr)) {
case EPState_Idle:
break;
default: {
tcb_t *thread = TCB_PTR(endpoint_ptr_get_epQueue_head(epptr));
endpoint_ptr_set_state(epptr, EPState_Idle);
endpoint_ptr_set_epQueue_head(epptr, 0);
endpoint_ptr_set_epQueue_tail(epptr, 0);
for (; thread; thread = thread->tcbEPNext) {
setThreadState (thread, ThreadState_Restart);
SCHED_ENQUEUE(thread);
}
rescheduleRequired();
break;
}
}
}
void
cancelBadgedSends(endpoint_t *epptr, word_t badge)
{
switch (endpoint_ptr_get_state(epptr)) {
case EPState_Idle:
case EPState_Recv:
break;
case EPState_Send: {
tcb_t *thread, *next;
tcb_queue_t queue = ep_ptr_get_queue(epptr);
endpoint_ptr_set_state(epptr, EPState_Idle);
endpoint_ptr_set_epQueue_head(epptr, 0);
endpoint_ptr_set_epQueue_tail(epptr, 0);
for (thread = queue.head; thread; thread = next) {
word_t b = thread_state_ptr_get_blockingIPCBadge(
&thread->tcbState);
next = thread->tcbEPNext;
if (b == badge) {
setThreadState(thread, ThreadState_Restart);
SCHED_ENQUEUE(thread);
queue = tcbEPDequeue(thread, queue);
}
}
ep_ptr_set_queue(epptr, queue);
if (queue.head) {
endpoint_ptr_set_state(epptr, EPState_Send);
}
rescheduleRequired();
break;
}
default:
fail("invalid EP state");
}
}