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
23exception_t decodeIRQControlInvocation(word_t invLabel, word_t length,
24                                       cte_t *srcSlot, extra_caps_t excaps,
25                                       word_t *buffer)
26{
27    if (invLabel == IRQIssueIRQHandler) {
28        word_t index, depth, irq_w;
29        irq_t irq;
30        cte_t *destSlot;
31        cap_t cnodeCap;
32        lookupSlot_ret_t lu_ret;
33        exception_t status;
34
35        if (length < 3 || excaps.excaprefs[0] == NULL) {
36            current_syscall_error.type = seL4_TruncatedMessage;
37            return EXCEPTION_SYSCALL_ERROR;
38        }
39        irq_w = getSyscallArg(0, buffer);
40        irq = CORE_IRQ_TO_IRQT(0, irq_w);
41        index = getSyscallArg(1, buffer);
42        depth = getSyscallArg(2, buffer);
43
44        cnodeCap = excaps.excaprefs[0]->cap;
45
46        status = Arch_checkIRQ(irq_w);
47        if (status != EXCEPTION_NONE) {
48            return status;
49        }
50
51        if (isIRQActive(irq)) {
52            current_syscall_error.type = seL4_RevokeFirst;
53            userError("Rejecting request for IRQ %u. Already active.", (int)IRQT_TO_IRQ(irq));
54            return EXCEPTION_SYSCALL_ERROR;
55        }
56
57        lu_ret = lookupTargetSlot(cnodeCap, index, depth);
58        if (lu_ret.status != EXCEPTION_NONE) {
59            userError("Target slot for new IRQ Handler cap invalid: cap %lu, IRQ %u.",
60                      getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
61            return lu_ret.status;
62        }
63        destSlot = lu_ret.slot;
64
65        status = ensureEmptySlot(destSlot);
66        if (status != EXCEPTION_NONE) {
67            userError("Target slot for new IRQ Handler cap not empty: cap %lu, IRQ %u.",
68                      getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
69            return status;
70        }
71
72        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
73        return invokeIRQControl(irq, destSlot, srcSlot);
74    } else {
75        return Arch_decodeIRQControlInvocation(invLabel, length, srcSlot, excaps, buffer);
76    }
77}
78
79exception_t invokeIRQControl(irq_t irq, cte_t *handlerSlot, cte_t *controlSlot)
80{
81    setIRQState(IRQSignal, irq);
82    cteInsert(cap_irq_handler_cap_new(IRQT_TO_IDX(irq)), controlSlot, handlerSlot);
83
84    return EXCEPTION_NONE;
85}
86
87exception_t decodeIRQHandlerInvocation(word_t invLabel, irq_t irq,
88                                       extra_caps_t excaps)
89{
90    switch (invLabel) {
91    case IRQAckIRQ:
92        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
93        invokeIRQHandler_AckIRQ(irq);
94        return EXCEPTION_NONE;
95
96    case IRQSetIRQHandler: {
97        cap_t ntfnCap;
98        cte_t *slot;
99
100        if (excaps.excaprefs[0] == NULL) {
101            current_syscall_error.type = seL4_TruncatedMessage;
102            return EXCEPTION_SYSCALL_ERROR;
103        }
104        ntfnCap = excaps.excaprefs[0]->cap;
105        slot = excaps.excaprefs[0];
106
107        if (cap_get_capType(ntfnCap) != cap_notification_cap ||
108            !cap_notification_cap_get_capNtfnCanSend(ntfnCap)) {
109            if (cap_get_capType(ntfnCap) != cap_notification_cap) {
110                userError("IRQSetHandler: provided cap is not an notification capability.");
111            } else {
112                userError("IRQSetHandler: caller does not have send rights on the endpoint.");
113            }
114            current_syscall_error.type = seL4_InvalidCapability;
115            current_syscall_error.invalidCapNumber = 0;
116            return EXCEPTION_SYSCALL_ERROR;
117        }
118
119        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
120        invokeIRQHandler_SetIRQHandler(irq, ntfnCap, slot);
121        return EXCEPTION_NONE;
122    }
123
124    case IRQClearIRQHandler:
125        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
126        invokeIRQHandler_ClearIRQHandler(irq);
127        return EXCEPTION_NONE;
128
129    default:
130        userError("IRQHandler: Illegal operation.");
131        current_syscall_error.type = seL4_IllegalOperation;
132        return EXCEPTION_SYSCALL_ERROR;
133    }
134}
135
136void invokeIRQHandler_AckIRQ(irq_t irq)
137{
138#ifdef CONFIG_ARCH_RISCV
139    plic_complete_claim(irq);
140#else
141#if defined ENABLE_SMP_SUPPORT && defined CONFIG_ARCH_ARM
142    if (IRQ_IS_PPI(irq) && IRQT_TO_CORE(irq) != getCurrentCPUIndex()) {
143        doRemoteMaskPrivateInterrupt(IRQT_TO_CORE(irq), false, IRQT_TO_IDX(irq));
144        return;
145    }
146#endif
147    maskInterrupt(false, irq);
148#endif
149}
150
151void invokeIRQHandler_SetIRQHandler(irq_t irq, cap_t cap, cte_t *slot)
152{
153    cte_t *irqSlot;
154
155    irqSlot = intStateIRQNode + IRQT_TO_IDX(irq);
156    /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (-1))" */
157    cteDeleteOne(irqSlot);
158    cteInsert(cap, slot, irqSlot);
159}
160
161void invokeIRQHandler_ClearIRQHandler(irq_t irq)
162{
163    cte_t *irqSlot;
164
165    irqSlot = intStateIRQNode + IRQT_TO_IDX(irq);
166    /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (-1))" */
167    cteDeleteOne(irqSlot);
168}
169
170void deletingIRQHandler(irq_t irq)
171{
172    cte_t *slot;
173
174    slot = intStateIRQNode + IRQT_TO_IDX(irq);
175    /** GHOSTUPD: "(True, gs_set_assn cteDeleteOne_'proc (ucast cap_notification_cap))" */
176    cteDeleteOne(slot);
177}
178
179void deletedIRQHandler(irq_t irq)
180{
181    setIRQState(IRQInactive, irq);
182}
183
184void handleInterrupt(irq_t irq)
185{
186    if (unlikely(IRQT_TO_IRQ(irq) > maxIRQ)) {
187        /* mask, ack and pretend it didn't happen. We assume that because
188         * the interrupt controller for the platform returned this IRQ that
189         * it is safe to use in mask and ack operations, even though it is
190         * above the claimed maxIRQ. i.e. we're assuming maxIRQ is wrong */
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    switch (intStateIRQTable[IRQT_TO_IDX(irq)]) {
197    case IRQSignal: {
198        cap_t cap;
199
200        cap = intStateIRQNode[IRQT_TO_IDX(irq)].cap;
201
202        if (cap_get_capType(cap) == cap_notification_cap &&
203            cap_notification_cap_get_capNtfnCanSend(cap)) {
204            sendSignal(NTFN_PTR(cap_notification_cap_get_capNtfnPtr(cap)),
205                       cap_notification_cap_get_capNtfnBadge(cap));
206        } else {
207#ifdef CONFIG_IRQ_REPORTING
208            printf("Undelivered IRQ: %d\n", (int)IRQT_TO_IRQ(irq));
209#endif
210        }
211#ifndef CONFIG_ARCH_RISCV
212        maskInterrupt(true, irq);
213#endif
214        break;
215    }
216
217    case IRQTimer:
218#ifdef CONFIG_KERNEL_MCS
219        ackDeadlineIRQ();
220        NODE_STATE(ksReprogram) = true;
221#else
222        timerTick();
223        resetTimer();
224#endif
225        break;
226
227#ifdef ENABLE_SMP_SUPPORT
228    case IRQIPI:
229        handleIPI(irq, true);
230        break;
231#endif /* ENABLE_SMP_SUPPORT */
232
233    case IRQReserved:
234        handleReservedIRQ(irq);
235        break;
236
237    case IRQInactive:
238        /*
239         * This case shouldn't happen anyway unless the hardware or
240         * platform code is broken. Hopefully masking it again should make
241         * the interrupt go away.
242         */
243        maskInterrupt(true, irq);
244#ifdef CONFIG_IRQ_REPORTING
245        printf("Received disabled IRQ: %d\n", (int)IRQT_TO_IRQ(irq));
246#endif
247        break;
248
249    default:
250        /* No corresponding haskell error */
251        fail("Invalid IRQ state");
252    }
253
254    ackInterrupt(irq);
255}
256
257bool_t isIRQActive(irq_t irq)
258{
259    return intStateIRQTable[IRQT_TO_IDX(irq)] != IRQInactive;
260}
261
262void setIRQState(irq_state_t irqState, irq_t irq)
263{
264    intStateIRQTable[IRQT_TO_IDX(irq)] = irqState;
265#if defined ENABLE_SMP_SUPPORT && defined CONFIG_ARCH_ARM
266    if (IRQ_IS_PPI(irq) && IRQT_TO_CORE(irq) != getCurrentCPUIndex()) {
267        doRemoteMaskPrivateInterrupt(IRQT_TO_CORE(irq), irqState == IRQInactive, IRQT_TO_IDX(irq));
268        return;
269    }
270#endif
271    maskInterrupt(irqState == IRQInactive, irq);
272}
273