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
15void 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 */
37exception_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
54static 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
60static 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
69exception_t Arch_decodeIRQControlInvocation(word_t invLabel, word_t length, cte_t *srcSlot, extra_caps_t excaps,
70                                            word_t *buffer)
71{
72    word_t index, depth;
73    cte_t *destSlot;
74    cap_t cnodeCap;
75    lookupSlot_ret_t lu_ret;
76    exception_t status;
77    irq_t irq;
78    word_t vector;
79
80    if (!config_set(CONFIG_IRQ_IOAPIC)) {
81        userError("IRQControl: Illegal operation.");
82        current_syscall_error.type = seL4_IllegalOperation;
83        return EXCEPTION_SYSCALL_ERROR;
84    }
85
86    /* ensure we have a valid invocation before continuing any decoding */
87    if (invLabel != X86IRQIssueIRQHandlerIOAPIC && invLabel != X86IRQIssueIRQHandlerMSI) {
88        userError("IRQControl: Illegal operation");
89        current_syscall_error.type = seL4_IllegalOperation;
90        return EXCEPTION_SYSCALL_ERROR;
91    }
92
93    /* check the common parameters */
94
95    if (length < 7 || excaps.excaprefs[0] == NULL) {
96        userError("IRQControl: Truncated message");
97        current_syscall_error.type = seL4_TruncatedMessage;
98        return EXCEPTION_SYSCALL_ERROR;
99    }
100    index = getSyscallArg(0, buffer);
101    depth = getSyscallArg(1, buffer);
102    cnodeCap = excaps.excaprefs[0]->cap;
103    irq = getSyscallArg(6, buffer);
104    if (irq > irq_user_max - irq_user_min) {
105        userError("IRQControl: Invalid irq %ld should be between 0-%ld", (long)irq, (long)(irq_user_max - irq_user_min));
106        current_syscall_error.type = seL4_RangeError;
107        current_syscall_error.rangeErrorMin = 0;
108        current_syscall_error.rangeErrorMax = irq_user_max - irq_user_min;
109        return EXCEPTION_SYSCALL_ERROR;
110    }
111    irq += irq_user_min;
112
113    if (isIRQActive(irq)) {
114        userError("IRQControl: IRQ %d is already active.", (int)irq);
115        current_syscall_error.type = seL4_RevokeFirst;
116        return EXCEPTION_SYSCALL_ERROR;
117    }
118
119    vector = (word_t)irq + IRQ_INT_OFFSET;
120
121    lu_ret = lookupTargetSlot(cnodeCap, index, depth);
122    if (lu_ret.status != EXCEPTION_NONE) {
123        return lu_ret.status;
124    }
125
126    destSlot = lu_ret.slot;
127
128    status = ensureEmptySlot(destSlot);
129    if (status != EXCEPTION_NONE) {
130        return status;
131    }
132
133    switch (invLabel) {
134    case X86IRQIssueIRQHandlerIOAPIC: {
135        word_t ioapic = getSyscallArg(2, buffer);
136        word_t pin = getSyscallArg(3, buffer);
137        word_t level = getSyscallArg(4, buffer);
138        word_t polarity = getSyscallArg(5, buffer);
139
140        status = ioapic_decode_map_pin_to_vector(ioapic, pin, level, polarity, vector);
141        if (status != EXCEPTION_NONE) {
142            return status;
143        }
144
145        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
146        return invokeIssueIRQHandlerIOAPIC(irq, ioapic, pin, level, polarity, vector, destSlot, srcSlot);
147    }
148    break;
149    case X86IRQIssueIRQHandlerMSI: {
150        word_t pci_bus = getSyscallArg(2, buffer);
151        word_t pci_dev = getSyscallArg(3, buffer);
152        word_t pci_func = getSyscallArg(4, buffer);
153        word_t handle = getSyscallArg(5, buffer);
154        x86_irq_state_t irqState;
155        /* until we support msi interrupt remaping through vt-d we ignore the
156         * vector and trust the user */
157
158        if (pci_bus > PCI_BUS_MAX) {
159            current_syscall_error.type = seL4_RangeError;
160            current_syscall_error.rangeErrorMin = 0;
161            current_syscall_error.rangeErrorMax = PCI_BUS_MAX;
162            return EXCEPTION_SYSCALL_ERROR;
163        }
164
165        if (pci_dev > PCI_DEV_MAX) {
166            current_syscall_error.type = seL4_RangeError;
167            current_syscall_error.rangeErrorMin = 0;
168            current_syscall_error.rangeErrorMax = PCI_DEV_MAX;
169            return EXCEPTION_SYSCALL_ERROR;
170        }
171
172        if (pci_func > PCI_FUNC_MAX) {
173            current_syscall_error.type = seL4_RangeError;
174            current_syscall_error.rangeErrorMin = 0;
175            current_syscall_error.rangeErrorMax = PCI_FUNC_MAX;
176            return EXCEPTION_SYSCALL_ERROR;
177        }
178
179        irqState = x86_irq_state_irq_msi_new(pci_bus, pci_dev, pci_func, handle);
180
181        setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
182        return Arch_invokeIRQControl(irq, destSlot, srcSlot, irqState);
183    }
184    break;
185    default:
186        /* the check at the start of this function should guarantee we do not get here */
187        fail("IRQControl: Illegal operation");
188    }
189}
190