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 
Arch_invokeIRQControl(irq_t irq,cte_t * handlerSlot,cte_t * controlSlot,bool_t trigger)13 static 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 
Arch_decodeIRQControlInvocation(word_t invLabel,word_t length,cte_t * srcSlot,word_t * buffer)21 exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length,
22                                             cte_t *srcSlot, word_t *buffer)
23 {
24     if (invLabel == ARMIRQIssueIRQHandlerTrigger) {
25         if (length < 4 || current_extra_caps.excaprefs[0] == NULL) {
26             current_syscall_error.type = seL4_TruncatedMessage;
27             return EXCEPTION_SYSCALL_ERROR;
28         }
29 
30         if (!config_set(HAVE_SET_TRIGGER)) {
31             userError("This platform does not support setting the IRQ trigger");
32             current_syscall_error.type = seL4_IllegalOperation;
33             return EXCEPTION_SYSCALL_ERROR;
34         }
35 
36         word_t irq_w = getSyscallArg(0, buffer);
37         irq_t irq = (irq_t) CORE_IRQ_TO_IRQT(0, irq_w);
38         bool_t trigger = !!getSyscallArg(1, buffer);
39         word_t index = getSyscallArg(2, buffer);
40         word_t depth = getSyscallArg(3, buffer);
41 
42         cap_t cnodeCap = current_extra_caps.excaprefs[0]->cap;
43 
44         exception_t status = Arch_checkIRQ(irq_w);
45         if (status != EXCEPTION_NONE) {
46             return status;
47         }
48 
49 #if defined ENABLE_SMP_SUPPORT
50         if (IRQ_IS_PPI(irq)) {
51             userError("Trying to get a handler on a PPI: use GetTriggerCore.");
52             current_syscall_error.type = seL4_IllegalOperation;
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 = current_extra_caps.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