#include <config.h>
#include <object.h>
#include <util.h>
#include <api/faults.h>
#include <api/types.h>
#include <kernel/cspace.h>
#include <kernel/thread.h>
#include <kernel/vspace.h>
#include <model/statedata.h>
#include <arch/machine.h>
#include <arch/kernel/thread.h>
#include <machine/registerset.h>
#include <arch/linker.h>
static seL4_MessageInfo_t
transferCaps(seL4_MessageInfo_t info, extra_caps_t caps,
endpoint_t *endpoint, tcb_t *receiver,
word_t *receiveBuffer);
static inline bool_t PURE
isBlocked(const tcb_t *thread)
{
switch (thread_state_get_tsType(thread->tcbState)) {
case ThreadState_Inactive:
case ThreadState_BlockedOnReceive:
case ThreadState_BlockedOnSend:
case ThreadState_BlockedOnNotification:
case ThreadState_BlockedOnReply:
return true;
default:
return false;
}
}
static inline bool_t PURE
isRunnable(const tcb_t *thread)
{
switch (thread_state_get_tsType(thread->tcbState)) {
case ThreadState_Running:
case ThreadState_Restart:
#ifdef CONFIG_VTX
case ThreadState_RunningVM:
#endif
return true;
default:
return false;
}
}
BOOT_CODE void
configureIdleThread(tcb_t *tcb)
{
Arch_configureIdleThread(tcb);
setThreadState(tcb, ThreadState_IdleThreadState);
}
void
activateThread(void)
{
switch (thread_state_get_tsType(NODE_STATE(ksCurThread)->tcbState)) {
case ThreadState_Running:
#ifdef CONFIG_VTX
case ThreadState_RunningVM:
#endif
break;
case ThreadState_Restart: {
word_t pc;
pc = getRestartPC(NODE_STATE(ksCurThread));
setNextPC(NODE_STATE(ksCurThread), pc);
setThreadState(NODE_STATE(ksCurThread), ThreadState_Running);
break;
}
case ThreadState_IdleThreadState:
Arch_activateIdleThread(NODE_STATE(ksCurThread));
break;
default:
fail("Current thread is blocked");
}
}
void
suspend(tcb_t *target)
{
cancelIPC(target);
setThreadState(target, ThreadState_Inactive);
tcbSchedDequeue(target);
}
void
restart(tcb_t *target)
{
if (isBlocked(target)) {
cancelIPC(target);
setupReplyMaster(target);
setThreadState(target, ThreadState_Restart);
SCHED_ENQUEUE(target);
switchIfRequiredTo(target);
}
}
void
doIPCTransfer(tcb_t *sender, endpoint_t *endpoint, word_t badge,
bool_t grant, tcb_t *receiver)
{
void *receiveBuffer, *sendBuffer;
receiveBuffer = lookupIPCBuffer(true, receiver);
if (likely(seL4_Fault_get_seL4_FaultType(sender->tcbFault) == seL4_Fault_NullFault)) {
sendBuffer = lookupIPCBuffer(false, sender);
doNormalTransfer(sender, sendBuffer, endpoint, badge, grant,
receiver, receiveBuffer);
} else {
doFaultTransfer(badge, sender, receiver, receiveBuffer);
}
}
void
doReplyTransfer(tcb_t *sender, tcb_t *receiver, cte_t *slot)
{
assert(thread_state_get_tsType(receiver->tcbState) ==
ThreadState_BlockedOnReply);
if (likely(seL4_Fault_get_seL4_FaultType(receiver->tcbFault) == seL4_Fault_NullFault)) {
doIPCTransfer(sender, NULL, 0, true, receiver);
cteDeleteOne(slot);
setThreadState(receiver, ThreadState_Running);
attemptSwitchTo(receiver);
} else {
bool_t restart;
cteDeleteOne(slot);
restart = handleFaultReply(receiver, sender);
receiver->tcbFault = seL4_Fault_NullFault_new();
if (restart) {
setThreadState(receiver, ThreadState_Restart);
attemptSwitchTo(receiver);
} else {
setThreadState(receiver, ThreadState_Inactive);
}
}
}
void
doNormalTransfer(tcb_t *sender, word_t *sendBuffer, endpoint_t *endpoint,
word_t badge, bool_t canGrant, tcb_t *receiver,
word_t *receiveBuffer)
{
word_t msgTransferred;
seL4_MessageInfo_t tag;
exception_t status;
extra_caps_t caps;
tag = messageInfoFromWord(getRegister(sender, msgInfoRegister));
if (canGrant) {
status = lookupExtraCaps(sender, sendBuffer, tag);
caps = current_extra_caps;
if (unlikely(status != EXCEPTION_NONE)) {
caps.excaprefs[0] = NULL;
}
} else {
caps = current_extra_caps;
caps.excaprefs[0] = NULL;
}
msgTransferred = copyMRs(sender, sendBuffer, receiver, receiveBuffer,
seL4_MessageInfo_get_length(tag));
tag = transferCaps(tag, caps, endpoint, receiver, receiveBuffer);
tag = seL4_MessageInfo_set_length(tag, msgTransferred);
setRegister(receiver, msgInfoRegister, wordFromMessageInfo(tag));
setRegister(receiver, badgeRegister, badge);
}
void
doFaultTransfer(word_t badge, tcb_t *sender, tcb_t *receiver,
word_t *receiverIPCBuffer)
{
word_t sent;
seL4_MessageInfo_t msgInfo;
sent = setMRs_fault(sender, receiver, receiverIPCBuffer);
msgInfo = seL4_MessageInfo_new(
seL4_Fault_get_seL4_FaultType(sender->tcbFault), 0, 0, sent);
setRegister(receiver, msgInfoRegister, wordFromMessageInfo(msgInfo));
setRegister(receiver, badgeRegister, badge);
}
static seL4_MessageInfo_t
transferCaps(seL4_MessageInfo_t info, extra_caps_t caps,
endpoint_t *endpoint, tcb_t *receiver,
word_t *receiveBuffer)
{
word_t i;
cte_t* destSlot;
info = seL4_MessageInfo_set_extraCaps(info, 0);
info = seL4_MessageInfo_set_capsUnwrapped(info, 0);
if (likely(!caps.excaprefs[0] || !receiveBuffer)) {
return info;
}
destSlot = getReceiveSlots(receiver, receiveBuffer);
for (i = 0; i < seL4_MsgMaxExtraCaps && caps.excaprefs[i] != NULL; i++) {
cte_t *slot = caps.excaprefs[i];
cap_t cap = slot->cap;
if (cap_get_capType(cap) == cap_endpoint_cap &&
EP_PTR(cap_endpoint_cap_get_capEPPtr(cap)) == endpoint) {
setExtraBadge(receiveBuffer,
cap_endpoint_cap_get_capEPBadge(cap), i);
info = seL4_MessageInfo_set_capsUnwrapped(info,
seL4_MessageInfo_get_capsUnwrapped(info) | (1 << i));
} else {
deriveCap_ret_t dc_ret;
if (!destSlot) {
break;
}
dc_ret = deriveCap(slot, cap);
if (dc_ret.status != EXCEPTION_NONE) {
break;
}
if (cap_get_capType(dc_ret.cap) == cap_null_cap) {
break;
}
cteInsert(dc_ret.cap, slot, destSlot);
destSlot = NULL;
}
}
return seL4_MessageInfo_set_extraCaps(info, i);
}
void doNBRecvFailedTransfer(tcb_t *thread)
{
setRegister(thread, badgeRegister, 0);
}
static void
nextDomain(void)
{
ksDomScheduleIdx++;
if (ksDomScheduleIdx >= ksDomScheduleLength) {
ksDomScheduleIdx = 0;
}
ksWorkUnitsCompleted = 0;
ksCurDomain = ksDomSchedule[ksDomScheduleIdx].domain;
ksDomainTime = ksDomSchedule[ksDomScheduleIdx].length;
}
void
schedule(void)
{
word_t action;
action = (word_t)NODE_STATE(ksSchedulerAction);
if (action == (word_t)SchedulerAction_ChooseNewThread) {
if (isRunnable(NODE_STATE(ksCurThread))) {
SCHED_ENQUEUE_CURRENT_TCB;
}
if (ksDomainTime == 0) {
nextDomain();
}
chooseThread();
NODE_STATE(ksSchedulerAction) = SchedulerAction_ResumeCurrentThread;
} else if (action != (word_t)SchedulerAction_ResumeCurrentThread) {
if (isRunnable(NODE_STATE(ksCurThread))) {
SCHED_ENQUEUE_CURRENT_TCB;
}
switchToThread(NODE_STATE(ksSchedulerAction));
NODE_STATE(ksSchedulerAction) = SchedulerAction_ResumeCurrentThread;
}
#if CONFIG_MAX_NUM_NODES > 1
doMaskReschedule(ARCH_NODE_STATE(ipiReschedulePending));
ARCH_NODE_STATE(ipiReschedulePending) = 0;
#endif
}
void
chooseThread(void)
{
word_t prio;
word_t dom;
tcb_t *thread;
if (CONFIG_NUM_DOMAINS > 1) {
dom = ksCurDomain;
} else {
dom = 0;
}
if (likely(NODE_STATE(ksReadyQueuesL1Bitmap[dom]))) {
word_t l1index = (wordBits - 1) - clzl(NODE_STATE(ksReadyQueuesL1Bitmap[dom]));
word_t l2index = (wordBits - 1) - clzl(NODE_STATE(ksReadyQueuesL2Bitmap[dom][l1index]));
prio = l1index_to_prio(l1index) | l2index;
thread = NODE_STATE(ksReadyQueues[ready_queues_index(dom, prio)]).head;
assert(thread);
assert(isRunnable(thread));
switchToThread(thread);
} else {
switchToIdleThread();
}
}
void
switchToThread(tcb_t *thread)
{
#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION
benchmark_utilisation_switch(NODE_STATE(ksCurThread), thread);
#endif
Arch_switchToThread(thread);
tcbSchedDequeue(thread);
NODE_STATE(ksCurThread) = thread;
}
void
switchToIdleThread(void)
{
#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION
benchmark_utilisation_switch(NODE_STATE(ksCurThread), NODE_STATE(ksIdleThread));
#endif
Arch_switchToIdleThread();
NODE_STATE(ksCurThread) = NODE_STATE(ksIdleThread);
}
void
setDomain(tcb_t *tptr, dom_t dom)
{
tcbSchedDequeue(tptr);
tptr->tcbDomain = dom;
if (isRunnable(tptr)) {
SCHED_ENQUEUE(tptr);
}
if (tptr == NODE_STATE(ksCurThread)) {
rescheduleRequired();
}
}
void
setMCPriority(tcb_t *tptr, prio_t mcp)
{
tptr->tcbMCP = mcp;
}
void
setPriority(tcb_t *tptr, prio_t prio)
{
tcbSchedDequeue(tptr);
tptr->tcbPriority = prio;
if (isRunnable(tptr)) {
SCHED_ENQUEUE(tptr);
}
if (tptr == NODE_STATE(ksCurThread)) {
rescheduleRequired();
}
}
static void
possibleSwitchTo(tcb_t* target, bool_t onSamePriority)
{
dom_t curDom, targetDom;
prio_t curPrio, targetPrio;
tcb_t *action;
curDom = ksCurDomain;
curPrio = NODE_STATE(ksCurThread)->tcbPriority;
targetDom = target->tcbDomain;
targetPrio = target->tcbPriority;
action = NODE_STATE(ksSchedulerAction);
if (targetDom != curDom) {
SCHED_ENQUEUE(target);
} else {
if ((targetPrio > curPrio || (targetPrio == curPrio && onSamePriority))
&& action == SchedulerAction_ResumeCurrentThread
SMP_COND_STATEMENT( && target->tcbAffinity == getCurrentCPUIndex())) {
NODE_STATE(ksSchedulerAction) = target;
} else {
SCHED_ENQUEUE(target);
}
if (action != SchedulerAction_ResumeCurrentThread
&& action != SchedulerAction_ChooseNewThread) {
rescheduleRequired();
}
}
}
void
attemptSwitchTo(tcb_t* target)
{
possibleSwitchTo(target, true);
}
void
switchIfRequiredTo(tcb_t* target)
{
possibleSwitchTo(target, false);
}
void
setThreadState(tcb_t *tptr, _thread_state_t ts)
{
thread_state_ptr_set_tsType(&tptr->tcbState, ts);
scheduleTCB(tptr);
}
void
scheduleTCB(tcb_t *tptr)
{
if (tptr == NODE_STATE(ksCurThread) &&
NODE_STATE(ksSchedulerAction) == SchedulerAction_ResumeCurrentThread &&
!isRunnable(tptr)) {
rescheduleRequired();
}
}
void
timerTick(void)
{
if (likely(thread_state_get_tsType(NODE_STATE(ksCurThread)->tcbState) ==
ThreadState_Running)) {
if (NODE_STATE(ksCurThread)->tcbTimeSlice > 1) {
NODE_STATE(ksCurThread)->tcbTimeSlice--;
} else {
NODE_STATE(ksCurThread)->tcbTimeSlice = CONFIG_TIME_SLICE;
SCHED_APPEND_CURRENT_TCB;
rescheduleRequired();
}
}
if (CONFIG_NUM_DOMAINS > 1) {
ksDomainTime--;
if (ksDomainTime == 0) {
rescheduleRequired();
}
}
}
void
rescheduleRequired(void)
{
if (NODE_STATE(ksSchedulerAction) != SchedulerAction_ResumeCurrentThread
&& NODE_STATE(ksSchedulerAction) != SchedulerAction_ChooseNewThread) {
SCHED_ENQUEUE(NODE_STATE(ksSchedulerAction));
}
NODE_STATE(ksSchedulerAction) = SchedulerAction_ChooseNewThread;
}