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