1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#pragma once
8
9#include <config.h>
10#include <types.h>
11#include <util.h>
12
13#include <arch/object/structures.h>
14#include <arch/model/statedata.h>
15#include <arch/kernel/apic.h>
16#include <machine/interrupt.h>
17#include <plat/machine/acpi.h>
18#include <plat/machine/ioapic.h>
19#include <plat/machine/pic.h>
20#include <plat/machine/intel-vtd.h>
21
22static inline void handleReservedIRQ(irq_t irq)
23{
24#ifdef CONFIG_IOMMU
25    if (irq == irq_iommu) {
26        vtd_handle_fault();
27        return;
28    }
29#endif
30
31#ifdef CONFIG_IRQ_REPORTING
32    printf("Received unhandled reserved IRQ: %d\n", (int)irq);
33#endif
34}
35
36static inline void receivePendingIRQ(void)
37{
38    assert(ARCH_NODE_STATE(x86KSPendingInterrupt) == int_invalid);
39    asm volatile("sti\n"
40                 "nop\n"
41                 "cli\n"
42                 : "=m"(ARCH_NODE_STATE(x86KSPendingInterrupt)));
43}
44
45static inline interrupt_t servicePendingIRQ(void)
46{
47    assert(ARCH_NODE_STATE(x86KScurInterrupt) == int_invalid);
48    assert(ARCH_NODE_STATE(x86KSPendingInterrupt) != int_invalid);
49    interrupt_t ret = ARCH_NODE_STATE(x86KSPendingInterrupt);
50    ARCH_NODE_STATE(x86KSPendingInterrupt) = int_invalid;
51    return ret;
52}
53
54/* Get the IRQ number currently working on. */
55static inline irq_t getActiveIRQ(void)
56{
57    if (ARCH_NODE_STATE(x86KScurInterrupt) == int_invalid) {
58        /* If we tried to get the active IRQ when we don't have one then
59         * we are polling for an interrupt for some reason, in which case
60         * we should try to get a pending interrupt if there isn't already
61         * one.
62         * This logic is here and not in the main call sites in handleSyscall
63         * because this is only relevant on some interrupt controllers (notably
64         * the x86 APIC) and is cleaner to have here */
65        if (ARCH_NODE_STATE(x86KSPendingInterrupt) == int_invalid) {
66            receivePendingIRQ();
67            /* Check if there was no pending IRQ */
68            if (ARCH_NODE_STATE(x86KSPendingInterrupt) == int_invalid) {
69                return irqInvalid;
70            }
71        }
72        /* Prepare to handle pending IRQ */
73        ARCH_NODE_STATE(x86KScurInterrupt) = servicePendingIRQ();
74    }
75    return ARCH_NODE_STATE(x86KScurInterrupt) - IRQ_INT_OFFSET;
76}
77
78/* Checks for pending IRQ */
79static inline bool_t isIRQPending(void)
80{
81    if (apic_is_interrupt_pending()) {
82        return true;
83    }
84
85    if (config_set(CONFIG_IRQ_PIC) && pic_is_irq_pending()) {
86        return true;
87    }
88
89    return false;
90}
91
92static inline void ackInterrupt(irq_t irq)
93{
94    if (config_set(CONFIG_IRQ_PIC) && irq <= irq_isa_max) {
95        pic_ack_active_irq();
96    } else {
97        apic_ack_active_interrupt();
98    }
99}
100
101static inline void handleSpuriousIRQ(void)
102{
103    /* do nothing */
104}
105
106static void inline updateIRQState(irq_t irq, x86_irq_state_t state)
107{
108    assert(irq <= maxIRQ);
109    x86KSIRQState[irq] = state;
110}
111
112static inline void maskInterrupt(bool_t disable, irq_t irq)
113{
114    if (irq >= irq_isa_min && irq <= irq_isa_max) {
115        if (config_set(CONFIG_IRQ_PIC)) {
116            pic_mask_irq(disable, irq);
117        } else {
118            /* We shouldn't receive interrupts on the PIC range
119             * if not using the PIC, but soldier on anyway */
120        }
121    } else if (irq >= irq_user_min && irq <= irq_user_max) {
122        x86_irq_state_t state = x86KSIRQState[irq];
123        switch (x86_irq_state_get_irqType(state)) {
124        case x86_irq_state_irq_ioapic: {
125            uint32_t ioapic = x86_irq_state_irq_ioapic_get_id(state);
126            uint32_t pin = x86_irq_state_irq_ioapic_get_pin(state);
127            ioapic_mask(disable, ioapic, pin);
128            state =  x86_irq_state_irq_ioapic_set_masked(state, disable);
129            updateIRQState(irq, state);
130        }
131        break;
132        case x86_irq_state_irq_msi:
133            /* currently MSI interrupts can not be disabled */
134            break;
135        case x86_irq_state_irq_free:
136            /* A spurious interrupt, and the resulting mask here,
137             * could be from a user ripping out a vector before
138             * the interrupt reached the kernel. Silently ignore */
139            break;
140        }
141    } else {
142        /* masking some other kind of interrupt source, this probably
143         * shouldn't happen, but soldier on */
144    }
145}
146
147