1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <types.h>
8#include <api/failures.h>
9#include <config.h>
10
11#include <arch/object/interrupt.h>
12
13static exception_t Arch_invokeIRQControl(irq_t irq, cte_t *handlerSlot, cte_t *controlSlot, bool_t trigger)
14{
15#ifdef HAVE_SET_TRIGGER
16    setIRQTrigger(irq, trigger);
17#endif
18    return invokeIRQControl(irq, handlerSlot, controlSlot);
19}
20
21exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length,
22                                            cte_t *srcSlot, extra_caps_t excaps,
23                                            word_t *buffer)
24{
25    if (invLabel == ARMIRQIssueIRQHandlerTrigger) {
26        if (length < 4 || excaps.excaprefs[0] == NULL) {
27            current_syscall_error.type = seL4_TruncatedMessage;
28            return EXCEPTION_SYSCALL_ERROR;
29        }
30
31        if (!config_set(HAVE_SET_TRIGGER)) {
32            userError("This platform does not support setting the IRQ trigger");
33            current_syscall_error.type = seL4_IllegalOperation;
34            return EXCEPTION_SYSCALL_ERROR;
35        }
36
37        word_t irq_w = getSyscallArg(0, buffer);
38        irq_t irq = (irq_t) CORE_IRQ_TO_IRQT(0, irq_w);
39        bool_t trigger = !!getSyscallArg(1, buffer);
40        word_t index = getSyscallArg(2, buffer);
41        word_t depth = getSyscallArg(3, buffer);
42
43        cap_t cnodeCap = excaps.excaprefs[0]->cap;
44
45        exception_t status = Arch_checkIRQ(irq_w);
46        if (status != EXCEPTION_NONE) {
47            return status;
48        }
49
50#if defined ENABLE_SMP_SUPPORT
51        if (IRQ_IS_PPI(irq)) {
52            userError("Trying to get a handler on a PPI: use GetTriggerCore.");
53            return EXCEPTION_SYSCALL_ERROR;
54        }
55#endif
56        if (isIRQActive(irq)) {
57            current_syscall_error.type = seL4_RevokeFirst;
58            userError("Rejecting request for IRQ %u. Already active.", (int)IRQT_TO_IRQ(irq));
59            return EXCEPTION_SYSCALL_ERROR;
60        }
61
62        lookupSlot_ret_t 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)IRQT_TO_IRQ(irq));
66            return lu_ret.status;
67        }
68
69        cte_t *destSlot = lu_ret.slot;
70
71        status = ensureEmptySlot(destSlot);
72        if (status != EXCEPTION_NONE) {
73            userError("Target slot for new IRQ Handler cap not empty: cap %lu, IRQ %u.",
74                      getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
75            return status;
76        }
77
78        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
79        return Arch_invokeIRQControl(irq, destSlot, srcSlot, trigger);
80#ifdef ENABLE_SMP_SUPPORT
81    } else if (invLabel == ARMIRQIssueIRQHandlerTriggerCore) {
82        word_t irq_w = getSyscallArg(0, buffer);
83        bool_t trigger = !!getSyscallArg(1, buffer);
84        word_t index = getSyscallArg(2, buffer);
85        word_t depth = getSyscallArg(3, buffer) & 0xfful;
86        seL4_Word target = getSyscallArg(4, buffer);
87        cap_t cnodeCap = excaps.excaprefs[0]->cap;
88        exception_t status = Arch_checkIRQ(irq_w);
89        irq_t irq = CORE_IRQ_TO_IRQT(target, irq_w);
90
91        if (status != EXCEPTION_NONE) {
92            return status;
93        }
94
95        if (target >= CONFIG_MAX_NUM_NODES) {
96            current_syscall_error.type = seL4_InvalidArgument;
97            userError("Target core %lu is invalid.", target);
98            return EXCEPTION_SYSCALL_ERROR;
99        }
100
101        if (isIRQActive(irq)) {
102            current_syscall_error.type = seL4_RevokeFirst;
103            userError("Rejecting request for IRQ %u. Already active.", (int)IRQT_TO_IRQ(irq));
104            return EXCEPTION_SYSCALL_ERROR;
105        }
106
107        lookupSlot_ret_t lu_ret = lookupTargetSlot(cnodeCap, index, depth);
108        if (lu_ret.status != EXCEPTION_NONE) {
109            userError("Target slot for new IRQ Handler cap invalid: cap %lu, IRQ %u.",
110                      getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
111            return lu_ret.status;
112        }
113
114        cte_t *destSlot = lu_ret.slot;
115
116        status = ensureEmptySlot(destSlot);
117        if (status != EXCEPTION_NONE) {
118            userError("Target slot for new IRQ Handler cap not empty: cap %lu, IRQ %u.",
119                      getExtraCPtr(buffer, 0), (int)IRQT_TO_IRQ(irq));
120            return status;
121        }
122
123        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
124
125        /* If the IRQ is not a private interrupt, then the role of the syscall is to set
126         * target core to which the shared interrupt will be physically delivered.
127         */
128        if (!IRQ_IS_PPI(irq)) {
129            setIRQTarget(irq, target);
130        }
131        return Arch_invokeIRQControl(irq, destSlot, srcSlot, trigger);
132#endif /* ENABLE_SMP_SUPPORT */
133    } else {
134        current_syscall_error.type = seL4_IllegalOperation;
135        return EXCEPTION_SYSCALL_ERROR;
136    }
137}
138