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