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