1 /*
2  * Copyright 2014, General Dynamics C4 Systems
3  *
4  * SPDX-License-Identifier: GPL-2.0-only
5  */
6 
7 #include <kernel/boot.h>
8 #include <model/statedata.h>
9 #include <arch/object/interrupt.h>
10 #include <arch/api/invocation.h>
11 #include <linker.h>
12 #include <plat/machine/hardware.h>
13 #include <plat/machine/pci.h>
14 
Arch_irqStateInit(void)15 void Arch_irqStateInit(void)
16 {
17     int i = 0;
18     for (i = 0; i <= maxIRQ; i++) {
19         if (i == irq_timer
20 #ifdef CONFIG_IOMMU
21             || i == irq_iommu
22 #endif
23            ) {
24             x86KSIRQState[i] = x86_irq_state_irq_reserved_new();
25         } else {
26             x86KSIRQState[i] = x86_irq_state_irq_free_new();
27         }
28     }
29 }
30 
31 /* for x86, the IRQIssueIRQHandler is only allowed to
32  * issue a hander for IRQ 0-15, the isa IRQs.
33  * Use getIRQHandlerIOAPIC and getIRQHandlerMSI for
34  * the IRQs >= 16. Additionally these IRQs only exist
35  * if using the legacy PIC interrupt
36  */
Arch_checkIRQ(word_t irq_w)37 exception_t Arch_checkIRQ(word_t irq_w)
38 {
39     if (config_set(CONFIG_IRQ_PIC) && irq_w >= irq_isa_min && irq_w <= irq_isa_max) {
40         return EXCEPTION_NONE;
41     }
42     if (config_set(CONFIG_IRQ_IOAPIC)) {
43         userError("IRQControl: Illegal operation");
44         current_syscall_error.type = seL4_IllegalOperation;
45     } else {
46         userError("IRQControl: IRQ %ld should be in range %ld - %ld", irq_w, (long)irq_isa_min, (long)irq_isa_max);
47         current_syscall_error.type = seL4_RangeError;
48         current_syscall_error.rangeErrorMin = irq_isa_min;
49         current_syscall_error.rangeErrorMax = irq_isa_max;
50     }
51     return EXCEPTION_SYSCALL_ERROR;
52 }
53 
Arch_invokeIRQControl(irq_t irq,cte_t * handlerSlot,cte_t * controlSlot,x86_irq_state_t irqState)54 static exception_t Arch_invokeIRQControl(irq_t irq, cte_t *handlerSlot, cte_t *controlSlot, x86_irq_state_t irqState)
55 {
56     updateIRQState(irq, irqState);
57     return invokeIRQControl(irq, handlerSlot, controlSlot);
58 }
59 
invokeIssueIRQHandlerIOAPIC(irq_t irq,word_t ioapic,word_t pin,word_t level,word_t polarity,word_t vector,cte_t * handlerSlot,cte_t * controlSlot)60 static exception_t invokeIssueIRQHandlerIOAPIC(irq_t irq, word_t ioapic, word_t pin, word_t level, word_t polarity,
61                                                word_t vector,
62                                                cte_t *handlerSlot, cte_t *controlSlot)
63 {
64     x86_irq_state_t irqState = x86_irq_state_irq_ioapic_new(ioapic, pin, level, polarity, 1);
65     ioapic_map_pin_to_vector(ioapic, pin, level, polarity, vector);
66     return Arch_invokeIRQControl(irq, handlerSlot, controlSlot, irqState);
67 }
68 
Arch_decodeIRQControlInvocation(word_t invLabel,word_t length,cte_t * srcSlot,word_t * buffer)69 exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length, cte_t *srcSlot, word_t *buffer)
70 {
71     word_t index, depth;
72     cte_t *destSlot;
73     cap_t cnodeCap;
74     lookupSlot_ret_t lu_ret;
75     exception_t status;
76     irq_t irq;
77     word_t vector;
78 
79     if (!config_set(CONFIG_IRQ_IOAPIC)) {
80         userError("IRQControl: Illegal operation.");
81         current_syscall_error.type = seL4_IllegalOperation;
82         return EXCEPTION_SYSCALL_ERROR;
83     }
84 
85     /* ensure we have a valid invocation before continuing any decoding */
86     if (invLabel != X86IRQIssueIRQHandlerIOAPIC && invLabel != X86IRQIssueIRQHandlerMSI) {
87         userError("IRQControl: Illegal operation");
88         current_syscall_error.type = seL4_IllegalOperation;
89         return EXCEPTION_SYSCALL_ERROR;
90     }
91 
92     /* check the common parameters */
93 
94     if (length < 7 || current_extra_caps.excaprefs[0] == NULL) {
95         userError("IRQControl: Truncated message");
96         current_syscall_error.type = seL4_TruncatedMessage;
97         return EXCEPTION_SYSCALL_ERROR;
98     }
99     index = getSyscallArg(0, buffer);
100     depth = getSyscallArg(1, buffer);
101     cnodeCap = current_extra_caps.excaprefs[0]->cap;
102     irq = getSyscallArg(6, buffer);
103     if (irq > irq_user_max - irq_user_min) {
104         userError("IRQControl: Invalid irq %ld should be between 0-%ld", (long)irq, (long)(irq_user_max - irq_user_min));
105         current_syscall_error.type = seL4_RangeError;
106         current_syscall_error.rangeErrorMin = 0;
107         current_syscall_error.rangeErrorMax = irq_user_max - irq_user_min;
108         return EXCEPTION_SYSCALL_ERROR;
109     }
110     irq += irq_user_min;
111 
112     if (isIRQActive(irq)) {
113         userError("IRQControl: IRQ %d is already active.", (int)irq);
114         current_syscall_error.type = seL4_RevokeFirst;
115         return EXCEPTION_SYSCALL_ERROR;
116     }
117 
118     vector = (word_t)irq + IRQ_INT_OFFSET;
119 
120     lu_ret = lookupTargetSlot(cnodeCap, index, depth);
121     if (lu_ret.status != EXCEPTION_NONE) {
122         return lu_ret.status;
123     }
124 
125     destSlot = lu_ret.slot;
126 
127     status = ensureEmptySlot(destSlot);
128     if (status != EXCEPTION_NONE) {
129         return status;
130     }
131 
132     switch (invLabel) {
133     case X86IRQIssueIRQHandlerIOAPIC: {
134         word_t ioapic = getSyscallArg(2, buffer);
135         word_t pin = getSyscallArg(3, buffer);
136         word_t level = getSyscallArg(4, buffer);
137         word_t polarity = getSyscallArg(5, buffer);
138 
139         status = ioapic_decode_map_pin_to_vector(ioapic, pin, level, polarity, vector);
140         if (status != EXCEPTION_NONE) {
141             return status;
142         }
143 
144         setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
145         return invokeIssueIRQHandlerIOAPIC(irq, ioapic, pin, level, polarity, vector, destSlot, srcSlot);
146     }
147     break;
148     case X86IRQIssueIRQHandlerMSI: {
149         word_t pci_bus = getSyscallArg(2, buffer);
150         word_t pci_dev = getSyscallArg(3, buffer);
151         word_t pci_func = getSyscallArg(4, buffer);
152         word_t handle = getSyscallArg(5, buffer);
153         x86_irq_state_t irqState;
154         /* until we support msi interrupt remaping through vt-d we ignore the
155          * vector and trust the user */
156 
157         if (pci_bus > PCI_BUS_MAX) {
158             current_syscall_error.type = seL4_RangeError;
159             current_syscall_error.rangeErrorMin = 0;
160             current_syscall_error.rangeErrorMax = PCI_BUS_MAX;
161             return EXCEPTION_SYSCALL_ERROR;
162         }
163 
164         if (pci_dev > PCI_DEV_MAX) {
165             current_syscall_error.type = seL4_RangeError;
166             current_syscall_error.rangeErrorMin = 0;
167             current_syscall_error.rangeErrorMax = PCI_DEV_MAX;
168             return EXCEPTION_SYSCALL_ERROR;
169         }
170 
171         if (pci_func > PCI_FUNC_MAX) {
172             current_syscall_error.type = seL4_RangeError;
173             current_syscall_error.rangeErrorMin = 0;
174             current_syscall_error.rangeErrorMax = PCI_FUNC_MAX;
175             return EXCEPTION_SYSCALL_ERROR;
176         }
177 
178         irqState = x86_irq_state_irq_msi_new(pci_bus, pci_dev, pci_func, handle);
179 
180         setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
181         return Arch_invokeIRQControl(irq, destSlot, srcSlot, irqState);
182     }
183     break;
184     default:
185         /* the check at the start of this function should guarantee we do not get here */
186         fail("IRQControl: Illegal operation");
187     }
188 }
189