1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <machine/timer.h>
8#include <mode/api/ipc_buffer.h>
9#include <object/schedcontext.h>
10#include <object/schedcontrol.h>
11#include <kernel/sporadic.h>
12
13static exception_t invokeSchedControl_Configure(sched_context_t *target, word_t core, ticks_t budget,
14                                                ticks_t period, word_t max_refills, word_t badge)
15{
16
17    target->scBadge = badge;
18
19    /* don't modify parameters of tcb while it is in a sorted queue */
20    if (target->scTcb) {
21        /* possibly stall a remote core */
22        SMP_COND_STATEMENT(remoteTCBStall(target->scTcb));
23        /* remove from scheduler */
24        tcbReleaseRemove(target->scTcb);
25        tcbSchedDequeue(target->scTcb);
26        /* bill the current consumed amount before adjusting the params */
27        if (NODE_STATE_ON_CORE(ksCurSC, target->scCore) == target) {
28#ifdef ENABLE_SMP_SUPPORT
29            if (target->scCore == getCurrentCPUIndex()) {
30#endif /* ENABLE_SMP_SUPPORT */
31                /* This could potentially mutate state but if it returns
32                 * true no state was modified, thus removing it should
33                 * be the same. */
34                assert(checkBudget());
35                commitTime();
36#ifdef ENABLE_SMP_SUPPORT
37            } else {
38                chargeBudget(NODE_STATE_ON_CORE(ksConsumed, target->scCore), false, target->scCore, false);
39                doReschedule(target->scCore);
40            }
41#endif /* ENABLE_SMP_SUPPORT */
42        }
43    }
44
45    if (budget == period) {
46        /* this is a cool hack: for round robin, we set the
47         * period to 0, which means that the budget will always be ready to be refilled
48         * and avoids some special casing.
49         */
50        period = 0;
51        max_refills = MIN_REFILLS;
52    }
53
54    if (SMP_COND_STATEMENT(core == target->scCore &&) target->scRefillMax > 0 && target->scTcb
55        && isRunnable(target->scTcb)) {
56        /* the scheduling context is active - it can be used, so
57         * we need to preserve the bandwidth */
58        refill_update(target, period, budget, max_refills);
59    } else {
60        /* the scheduling context isn't active - it's budget is not being used, so
61         * we can just populate the parameters from now */
62        REFILL_NEW(target, max_refills, budget, period, core);
63    }
64
65#ifdef ENABLE_SMP_SUPPORT
66    target->scCore = core;
67    if (target->scTcb) {
68        migrateTCB(target->scTcb, target->scCore);
69    }
70#endif /* ENABLE_SMP_SUPPORT */
71
72    if (target->scTcb && target->scRefillMax > 0) {
73        schedContext_resume(target);
74        if (SMP_TERNARY(core == CURRENT_CPU_INDEX(), true)) {
75            if (isRunnable(target->scTcb) && target->scTcb != NODE_STATE(ksCurThread)) {
76                possibleSwitchTo(target->scTcb);
77            }
78        } else if (isRunnable(target->scTcb)) {
79            SCHED_ENQUEUE(target->scTcb);
80        }
81        if (target->scTcb == NODE_STATE(ksCurThread)) {
82            rescheduleRequired();
83        }
84    }
85
86    return EXCEPTION_NONE;
87}
88
89static exception_t decodeSchedControl_Configure(word_t length, cap_t cap, extra_caps_t extraCaps, word_t *buffer)
90{
91    if (extraCaps.excaprefs[0] == NULL) {
92        userError("SchedControl_Configure: Truncated message.");
93        current_syscall_error.type = seL4_TruncatedMessage;
94        return EXCEPTION_SYSCALL_ERROR;
95    }
96
97    if (length < (TIME_ARG_SIZE * 2) + 2) {
98        userError("SchedControl_configure: truncated message.");
99        current_syscall_error.type = seL4_TruncatedMessage;
100        return EXCEPTION_SYSCALL_ERROR;
101    }
102
103    time_t budget_us = mode_parseTimeArg(0, buffer);
104    time_t period_us = mode_parseTimeArg(TIME_ARG_SIZE, buffer);
105    word_t extra_refills = getSyscallArg(TIME_ARG_SIZE * 2, buffer);
106    word_t badge = getSyscallArg(TIME_ARG_SIZE * 2 + 1, buffer);
107
108    cap_t targetCap = extraCaps.excaprefs[0]->cap;
109    if (unlikely(cap_get_capType(targetCap) != cap_sched_context_cap)) {
110        userError("SchedControl_Configure: target cap not a scheduling context cap");
111        current_syscall_error.type = seL4_InvalidCapability;
112        current_syscall_error.invalidCapNumber = 1;
113        return EXCEPTION_SYSCALL_ERROR;
114    }
115
116    if (budget_us > MAX_BUDGET_US || budget_us < MIN_BUDGET_US) {
117        userError("SchedControl_Configure: budget out of range.");
118        current_syscall_error.type = seL4_RangeError;
119        current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
120        current_syscall_error.rangeErrorMax = MAX_BUDGET_US;
121        return EXCEPTION_SYSCALL_ERROR;
122    }
123
124    if (period_us > MAX_BUDGET_US || period_us < MIN_BUDGET_US) {
125        userError("SchedControl_Configure: period out of range.");
126        current_syscall_error.type = seL4_RangeError;
127        current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
128        current_syscall_error.rangeErrorMax = MAX_BUDGET_US;
129        return EXCEPTION_SYSCALL_ERROR;
130    }
131
132    if (budget_us > period_us) {
133        userError("SchedControl_Configure: budget must be <= period");
134        current_syscall_error.type = seL4_RangeError;
135        current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
136        current_syscall_error.rangeErrorMax = period_us;
137        return EXCEPTION_SYSCALL_ERROR;
138    }
139
140    if (extra_refills + MIN_REFILLS > refill_absolute_max(targetCap)) {
141        current_syscall_error.type = seL4_RangeError;
142        current_syscall_error.rangeErrorMin = 0;
143        current_syscall_error.rangeErrorMax = refill_absolute_max(targetCap) - MIN_REFILLS;
144        userError("Max refills invalid, got %lu, max %lu",
145                  extra_refills,
146                  current_syscall_error.rangeErrorMax);
147        return EXCEPTION_SYSCALL_ERROR;
148    }
149
150    setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
151    return invokeSchedControl_Configure(SC_PTR(cap_sched_context_cap_get_capSCPtr(targetCap)),
152                                        cap_sched_control_cap_get_core(cap),
153                                        usToTicks(budget_us),
154                                        usToTicks(period_us),
155                                        extra_refills + MIN_REFILLS,
156                                        badge);
157}
158
159exception_t decodeSchedControlInvocation(word_t label, cap_t cap, word_t length, extra_caps_t extraCaps,
160                                         word_t *buffer)
161{
162    switch (label) {
163    case SchedControlConfigure:
164        return  decodeSchedControl_Configure(length, cap, extraCaps, buffer);
165    default:
166        userError("SchedControl invocation: Illegal operation attempted.");
167        current_syscall_error.type = seL4_IllegalOperation;
168        return EXCEPTION_SYSCALL_ERROR;
169    }
170}
171