1 /*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include <assert.h>
8 #include <types.h>
9 #include <api/failures.h>
10 #include <api/invocation.h>
11 #include <api/syscall.h>
12 #include <machine/io.h>
13 #include <object/structures.h>
14 #include <object/interrupt.h>
15 #include <object/cnode.h>
16 #include <object/notification.h>
17 #include <kernel/cspace.h>
18 #include <kernel/thread.h>
19 #include <model/statedata.h>
20 #include <machine/timer.h>
21 #include <smp/ipi.h>
22
decodeIRQControlInvocation(word_t invLabel,word_t length,cte_t * srcSlot,word_t * buffer)23 exception_t decodeIRQControlInvocation(word_t invLabel, word_t length,
24 cte_t *srcSlot, word_t *buffer)
25 {
26 if (invLabel == IRQIssueIRQHandler) {
27 word_t index, depth, irq_w;
28 irq_t irq;
29 cte_t *destSlot;
30 cap_t cnodeCap;
31 lookupSlot_ret_t lu_ret;
32 exception_t status;
33
34 if (length < 3 || current_extra_caps.excaprefs[0] == NULL) {
35 current_syscall_error.type = seL4_TruncatedMessage;
36 return EXCEPTION_SYSCALL_ERROR;
37 }
38 irq_w = getSyscallArg(0, buffer);
39 irq = CORE_IRQ_TO_IRQT(0, irq_w);
40 index = getSyscallArg(1, buffer);
41 depth = getSyscallArg(2, buffer);
42
43 cnodeCap = current_extra_caps.excaprefs[0]->cap;
44
45 status = Arch_checkIRQ(irq_w);
46 if (status != EXCEPTION_NONE) {
47 return status;
48 }
49
50 if (isIRQActive(irq)) {
51 current_syscall_error.type = seL4_RevokeFirst;
52 userError("Rejecting request for IRQ %u. Already active.", (int)IRQT_TO_IRQ(irq));
53 return EXCEPTION_SYSCALL_ERROR;
54 }
55
56 lu_ret = lookupTargetSlot(cnodeCap, index, depth);
57 if (lu_ret.status != EXCEPTION_NONE) {
58 userError("Target slot for new IRQ Handler cap invalid: cap %lu, IRQ %u.",
59 getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
60 return lu_ret.status;
61 }
62 destSlot = lu_ret.slot;
63
64 status = ensureEmptySlot(destSlot);
65 if (status != EXCEPTION_NONE) {
66 userError("Target slot for new IRQ Handler cap not empty: cap %lu, IRQ %u.",
67 getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
68 return status;
69 }
70
71 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
72 return invokeIRQControl(irq, destSlot, srcSlot);
73 } else {
74 return Arch_decodeIRQControlInvocation(invLabel, length, srcSlot, buffer);
75 }
76 }
77
invokeIRQControl(irq_t irq,cte_t * handlerSlot,cte_t * controlSlot)78 exception_t invokeIRQControl(irq_t irq, cte_t *handlerSlot, cte_t *controlSlot)
79 {
80 setIRQState(IRQSignal, irq);
81 cteInsert(cap_irq_handler_cap_new(IRQT_TO_IDX(irq)), controlSlot, handlerSlot);
82
83 return EXCEPTION_NONE;
84 }
85
decodeIRQHandlerInvocation(word_t invLabel,irq_t irq)86 exception_t decodeIRQHandlerInvocation(word_t invLabel, irq_t irq)
87 {
88 switch (invLabel) {
89 case IRQAckIRQ:
90 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
91 invokeIRQHandler_AckIRQ(irq);
92 return EXCEPTION_NONE;
93
94 case IRQSetIRQHandler: {
95 cap_t ntfnCap;
96 cte_t *slot;
97
98 if (current_extra_caps.excaprefs[0] == NULL) {
99 current_syscall_error.type = seL4_TruncatedMessage;
100 return EXCEPTION_SYSCALL_ERROR;
101 }
102 ntfnCap = current_extra_caps.excaprefs[0]->cap;
103 slot = current_extra_caps.excaprefs[0];
104
105 if (cap_get_capType(ntfnCap) != cap_notification_cap ||
106 !cap_notification_cap_get_capNtfnCanSend(ntfnCap)) {
107 if (cap_get_capType(ntfnCap) != cap_notification_cap) {
108 userError("IRQSetHandler: provided cap is not an notification capability.");
109 } else {
110 userError("IRQSetHandler: caller does not have send rights on the endpoint.");
111 }
112 current_syscall_error.type = seL4_InvalidCapability;
113 current_syscall_error.invalidCapNumber = 0;
114 return EXCEPTION_SYSCALL_ERROR;
115 }
116
117 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
118 invokeIRQHandler_SetIRQHandler(irq, ntfnCap, slot);
119 return EXCEPTION_NONE;
120 }
121
122 case IRQClearIRQHandler:
123 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
124 invokeIRQHandler_ClearIRQHandler(irq);
125 return EXCEPTION_NONE;
126
127 default:
128 userError("IRQHandler: Illegal operation.");
129 current_syscall_error.type = seL4_IllegalOperation;
130 return EXCEPTION_SYSCALL_ERROR;
131 }
132 }
133
invokeIRQHandler_AckIRQ(irq_t irq)134 void invokeIRQHandler_AckIRQ(irq_t irq)
135 {
136 #ifdef CONFIG_ARCH_RISCV
137 plic_complete_claim(irq);
138 #else
139 #if defined ENABLE_SMP_SUPPORT && defined CONFIG_ARCH_ARM
140 if (IRQ_IS_PPI(irq) && IRQT_TO_CORE(irq) != getCurrentCPUIndex()) {
141 doRemoteMaskPrivateInterrupt(IRQT_TO_CORE(irq), false, IRQT_TO_IDX(irq));
142 return;
143 }
144 #endif
145 maskInterrupt(false, irq);
146 #endif
147 }
148
invokeIRQHandler_SetIRQHandler(irq_t irq,cap_t cap,cte_t * slot)149 void invokeIRQHandler_SetIRQHandler(irq_t irq, cap_t cap, cte_t *slot)
150 {
151 cte_t *irqSlot;
152
153 irqSlot = intStateIRQNode + IRQT_TO_IDX(irq);
154 /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (-1))" */
155 cteDeleteOne(irqSlot);
156 cteInsert(cap, slot, irqSlot);
157 }
158
invokeIRQHandler_ClearIRQHandler(irq_t irq)159 void invokeIRQHandler_ClearIRQHandler(irq_t irq)
160 {
161 cte_t *irqSlot;
162
163 irqSlot = intStateIRQNode + IRQT_TO_IDX(irq);
164 /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (-1))" */
165 cteDeleteOne(irqSlot);
166 }
167
deletingIRQHandler(irq_t irq)168 void deletingIRQHandler(irq_t irq)
169 {
170 cte_t *slot;
171
172 slot = intStateIRQNode + IRQT_TO_IDX(irq);
173 /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (ucast cap_notification_cap))" */
174 cteDeleteOne(slot);
175 }
176
deletedIRQHandler(irq_t irq)177 void deletedIRQHandler(irq_t irq)
178 {
179 setIRQState(IRQInactive, irq);
180 }
181
handleInterrupt(irq_t irq)182 void handleInterrupt(irq_t irq)
183 {
184 if (unlikely(IRQT_TO_IRQ(irq) > maxIRQ)) {
185 /* The interrupt number is out of range. Pretend it did not happen by
186 * handling it like an inactive interrupt (mask and ack). We assume this
187 * is acceptable, because the platform specific interrupt controller
188 * driver reported this interrupt. Maybe the value maxIRQ is just wrong
189 * or set to a lower value because the interrupts are unused.
190 */
191 printf("Received IRQ %d, which is above the platforms maxIRQ of %d\n", (int)IRQT_TO_IRQ(irq), (int)maxIRQ);
192 maskInterrupt(true, irq);
193 ackInterrupt(irq);
194 return;
195 }
196
197 switch (intStateIRQTable[IRQT_TO_IDX(irq)]) {
198 case IRQSignal: {
199 /* Merging the variable declaration and initialization into one line
200 * requires an update in the proofs first. Might be a c89 legacy.
201 */
202 cap_t cap;
203 cap = intStateIRQNode[IRQT_TO_IDX(irq)].cap;
204 if (cap_get_capType(cap) == cap_notification_cap &&
205 cap_notification_cap_get_capNtfnCanSend(cap)) {
206 sendSignal(NTFN_PTR(cap_notification_cap_get_capNtfnPtr(cap)),
207 cap_notification_cap_get_capNtfnBadge(cap));
208 } else {
209 #ifdef CONFIG_IRQ_REPORTING
210 printf("Undelivered IRQ: %d\n", (int)IRQT_TO_IRQ(irq));
211 #endif
212 }
213 #ifndef CONFIG_ARCH_RISCV
214 maskInterrupt(true, irq);
215 #endif
216 break;
217 }
218
219 case IRQTimer:
220 #ifdef CONFIG_KERNEL_MCS
221 ackDeadlineIRQ();
222 NODE_STATE(ksReprogram) = true;
223 #else
224 timerTick();
225 resetTimer();
226 #endif
227 break;
228
229 #ifdef ENABLE_SMP_SUPPORT
230 case IRQIPI:
231 handleIPI(irq, true);
232 break;
233 #endif /* ENABLE_SMP_SUPPORT */
234
235 case IRQReserved:
236 handleReservedIRQ(irq);
237 break;
238
239 case IRQInactive:
240 /* This case shouldn't happen anyway unless the hardware or platform
241 * code is broken. Hopefully masking it again should make the interrupt
242 * go away.
243 */
244 maskInterrupt(true, irq);
245 #ifdef CONFIG_IRQ_REPORTING
246 printf("Received disabled IRQ: %d\n", (int)IRQT_TO_IRQ(irq));
247 #endif
248 break;
249
250 default:
251 /* No corresponding haskell error */
252 fail("Invalid IRQ state");
253 }
254
255 /* Every interrupt is ack'd, even if it is an inactive one. Rationale is,
256 * that for any interrupt reported by the platform specific code the generic
257 * kernel code does report here that it is done with handling it. */
258 ackInterrupt(irq);
259 }
260
isIRQActive(irq_t irq)261 bool_t isIRQActive(irq_t irq)
262 {
263 return intStateIRQTable[IRQT_TO_IDX(irq)] != IRQInactive;
264 }
265
setIRQState(irq_state_t irqState,irq_t irq)266 void setIRQState(irq_state_t irqState, irq_t irq)
267 {
268 intStateIRQTable[IRQT_TO_IDX(irq)] = irqState;
269 #if defined ENABLE_SMP_SUPPORT && defined CONFIG_ARCH_ARM
270 if (IRQ_IS_PPI(irq) && IRQT_TO_CORE(irq) != getCurrentCPUIndex()) {
271 doRemoteMaskPrivateInterrupt(IRQT_TO_CORE(irq), irqState == IRQInactive, IRQT_TO_IDX(irq));
272 return;
273 }
274 #endif
275 maskInterrupt(irqState == IRQInactive, irq);
276 }
277