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