1 /*
2  * Copyright (c) 2009 Corey Tabaka
3  * Copyright (c) 2015 Intel Corporation
4  *
5  * Use of this source code is governed by a MIT-style
6  * license that can be found in the LICENSE file or at
7  * https://opensource.org/licenses/MIT
8  */
9 #include <sys/types.h>
10 #include <lk/debug.h>
11 #include <lk/err.h>
12 #include <lk/reg.h>
13 #include <lk/trace.h>
14 #include <assert.h>
15 #include <kernel/thread.h>
16 #include <platform/interrupts.h>
17 #include <arch/ops.h>
18 #include <arch/x86.h>
19 #include <arch/x86/lapic.h>
20 #include <kernel/spinlock.h>
21 #include "platform_p.h"
22 #include <platform/pc.h>
23 
24 #define LOCAL_TRACE 0
25 
26 static spin_lock_t lock;
27 
28 #define INTC_TYPE_INTERNAL  0
29 #define INTC_TYPE_PIC       1
30 #define INTC_TYPE_MSI       2
31 
32 struct int_vector {
33     int_handler handler;
34     void *arg;
35     struct {
36         uint allocated : 1;
37         uint type : 2; // INTC_TYPE
38         uint edge : 1; // edge vs level
39     } flags;
40 };
41 
42 static struct int_vector int_table[INT_VECTORS];
43 
platform_init_interrupts(void)44 void platform_init_interrupts(void) {
45     pic_init();
46     lapic_init();
47 
48     // initialize all of the vectors
49     for (int i = 0; i < INT_VECTORS; i++) {
50         if (i >= INT_PIC1_BASE && i <= INT_PIC2_BASE + 8) {
51             int_table[i].flags.type = INTC_TYPE_PIC;
52         }
53         if (i >= INT_DYNAMIC_START && i <= INT_DYNAMIC_END) {
54             int_table[i].flags.allocated = false;
55         } else {
56             int_table[i].flags.allocated = true;
57         }
58     }
59 }
60 
mask_interrupt(unsigned int vector)61 status_t mask_interrupt(unsigned int vector) {
62     if (vector >= INT_VECTORS)
63         return ERR_INVALID_ARGS;
64 
65     LTRACEF("vector %#x\n", vector);
66 
67     spin_lock_saved_state_t state;
68     spin_lock_irqsave(&lock, state);
69 
70     if (int_table[vector].flags.type == INTC_TYPE_PIC) {
71         pic_enable(vector, false);
72     }
73 
74     spin_unlock_irqrestore(&lock, state);
75 
76     return NO_ERROR;
77 }
78 
79 
unmask_interrupt(unsigned int vector)80 status_t unmask_interrupt(unsigned int vector) {
81     if (vector >= INT_VECTORS)
82         return ERR_INVALID_ARGS;
83 
84     LTRACEF("vector %#x\n", vector);
85 
86     spin_lock_saved_state_t state;
87     spin_lock_irqsave(&lock, state);
88 
89     if (int_table[vector].flags.type == INTC_TYPE_PIC) {
90         pic_enable(vector, true);
91     }
92 
93     spin_unlock_irqrestore(&lock, state);
94 
95     return NO_ERROR;
96 }
97 
98 enum handler_return platform_irq(x86_iframe_t *frame);
platform_irq(x86_iframe_t * frame)99 enum handler_return platform_irq(x86_iframe_t *frame) {
100     // get the current vector
101     unsigned int vector = frame->vector;
102 
103     DEBUG_ASSERT(vector >= 0x20);
104 
105     struct int_vector *handler = &int_table[vector];
106 
107     // edge triggered interrupts are acked beforehand
108     if (handler->flags.edge) {
109         if (handler->flags.type == INTC_TYPE_MSI) {
110             lapic_eoi(vector);
111         } else {
112             pic_eoi(vector);
113         }
114     }
115 
116     // call the registered interrupt handler
117     enum handler_return ret = INT_NO_RESCHEDULE;
118     if (handler->handler) {
119         ret = handler->handler(handler->arg);
120     }
121 
122     // level triggered ack
123     if (!handler->flags.edge) {
124         if (handler->flags.type == INTC_TYPE_MSI) {
125             lapic_eoi(vector);
126         } else {
127             pic_eoi(vector);
128         }
129     }
130 
131     return ret;
132 }
133 
register_int_handler_etc(unsigned int vector,int_handler handler,void * arg,bool edge,uint type)134 static void register_int_handler_etc(unsigned int vector, int_handler handler, void *arg, bool edge, uint type) {
135     ASSERT(vector < INT_VECTORS);
136 
137     spin_lock_saved_state_t state;
138     spin_lock_irqsave(&lock, state);
139 
140     int_table[vector].arg = arg;
141     int_table[vector].handler = handler;
142     int_table[vector].flags.allocated = true;
143     int_table[vector].flags.edge = edge;
144     int_table[vector].flags.type = type;
145 
146     spin_unlock_irqrestore(&lock, state);
147 }
148 
register_int_handler(unsigned int vector,int_handler handler,void * arg)149 void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
150     register_int_handler_etc(vector, handler, arg, false, INTC_TYPE_PIC);
151 }
152 
register_int_handler_msi(unsigned int vector,int_handler handler,void * arg,bool edge)153 void register_int_handler_msi(unsigned int vector, int_handler handler, void *arg, bool edge) {
154     register_int_handler_etc(vector, handler, arg, edge, INTC_TYPE_MSI);
155 }
156 
platform_mask_irqs(void)157 void platform_mask_irqs(void) {
158     pic_mask_interrupts();
159 }
160 
platform_pci_int_to_vector(unsigned int pci_int,unsigned int * vector)161 status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) {
162     LTRACEF("pci_int %u\n", pci_int);
163 
164     // pci interrupts are relative to PIC style irq #s so simply add INT_BASE to it
165     uint out_vector = pci_int + INT_BASE;
166     if (out_vector > INT_VECTORS) {
167         return ERR_INVALID_ARGS;
168     }
169 
170     *vector = out_vector;
171     return NO_ERROR;
172 }
173 
platform_allocate_interrupts(size_t count,uint align_log2,bool msi,unsigned int * vector)174 status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) {
175     LTRACEF("count %zu align %u msi %d\n", count, align_log2, msi);
176     if (align_log2 > 0) {
177         PANIC_UNIMPLEMENTED;
178     }
179 
180     spin_lock_saved_state_t state;
181     spin_lock_irqsave(&lock, state);
182 
183     // find a free interrupt
184     status_t err = ERR_NOT_FOUND;
185     for (unsigned int i = 0; i < INT_VECTORS; i++) {
186         if (!int_table[i].flags.allocated) {
187             int_table[i].flags.allocated = true;
188             *vector = i;
189             LTRACEF("found irq %#x\n", i);
190             err = NO_ERROR;
191             break;
192         }
193     }
194 
195     spin_unlock_irqrestore(&lock, state);
196 
197     return err;
198 }
199 
platform_compute_msi_values(unsigned int vector,unsigned int cpu,bool edge,uint64_t * msi_address_out,uint16_t * msi_data_out)200 status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge,
201         uint64_t *msi_address_out, uint16_t *msi_data_out) {
202 
203     // only handle edge triggered at the moment
204     DEBUG_ASSERT(edge);
205 
206     *msi_data_out = (vector & 0xff) | (0<<15); // edge triggered
207     *msi_address_out = 0xfee00000 | (cpu << 12);
208 
209     return NO_ERROR;
210 }
211 
212