rework external irq system, introduce hierarchical irq
[lunaix-os.git] / lunaix-os / arch / x86 / hal / apic.c
1 /**
2  * @file apic.c
3  * @author Lunaixsky
4  * @brief Abstraction for Advanced Programmable Interrupts Controller (APIC)
5  * @version 0.1
6  * @date 2022-03-06
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11
12 #include "asm/x86.h"
13 #include "asm/x86_cpu.h"
14 #include "asm/soc/apic.h"
15
16 #include <asm/hart.h>
17 #include <asm/x86_isrm.h>
18
19 #include <lunaix/mm/mmio.h>
20 #include <lunaix/spike.h>
21 #include <lunaix/syslog.h>
22 #include <lunaix/device.h>
23
24 #include <hal/irq.h>
25 #include <hal/acpi/acpi.h>
26
27 #include "pic.h"
28
29 LOG_MODULE("APIC")
30
31 #define IOAPIC_IOREGSEL 0x00
32 #define IOAPIC_IOWIN 0x10
33 #define IOAPIC_IOREDTBL_BASE 0x10
34
35 #define IOAPIC_REG_ID 0x00
36 #define IOAPIC_REG_VER 0x01
37 #define IOAPIC_REG_ARB 0x02
38
39 #define IOAPIC_DELMOD_FIXED 0b000
40 #define IOAPIC_DELMOD_LPRIO 0b001
41 #define IOAPIC_DELMOD_NMI 0b100
42
43 #define IOAPIC_MASKED (1 << 16)
44 #define IOAPIC_TRIG_LEVEL (1 << 15)
45 #define IOAPIC_INTPOL_L (1 << 13)
46 #define IOAPIC_DESTMOD_LOGIC (1 << 11)
47
48 #define IOAPIC_BASE_VADDR 0x2000
49
50 #define IOAPIC_REG_SEL *((volatile u32_t*)(_ioapic_base + IOAPIC_IOREGSEL))
51 #define IOAPIC_REG_WIN *((volatile u32_t*)(_ioapic_base + IOAPIC_IOWIN))
52
53
54 #define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
55
56 // Pin LINT#1 is configured for relaying NMI, but we masked it here as I think
57 //  it is too early for that
58 // LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
59 #define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
60 #define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector)
61
62
63 static volatile ptr_t _ioapic_base;
64 static volatile ptr_t _apic_base;
65
66 void
67 apic_write_reg(unsigned int reg, unsigned int val)
68 {
69     *(unsigned int*)(_apic_base + reg) = val;
70 }
71
72 void
73 isrm_notify_eoi(cpu_t id, int iv)
74 {
75     // for all external interrupts except the spurious interrupt
76     //  this is required by Intel Manual Vol.3A, section 10.8.1 & 10.8.5
77     if (iv >= IV_EX_BEGIN && iv != APIC_SPIV_IV) {
78         *(unsigned int*)(_apic_base + APIC_EOI) = 0;
79     }
80 }
81
82 unsigned int
83 apic_read_reg(unsigned int reg)
84 {
85     return *(unsigned int*)(_apic_base + (reg));
86 }
87
88
89 static void
90 apic_setup_lvts()
91 {
92     apic_write_reg(APIC_LVT_LINT0, LVT_ENTRY_LINT0(APIC_LINT0_IV));
93     apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
94     apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
95 }
96
97 static void
98 apic_init()
99 {
100     // ensure that external interrupt is disabled
101     cpu_disable_interrupt();
102
103     // Make sure the APIC is there
104     //  FUTURE: Use 8259 as fallback
105
106     // FIXME apic abstraction as local interrupt controller
107     // assert_msg(cpu_has_apic(), "No APIC detected!");
108
109     // As we are going to use APIC, disable the old 8259 PIC
110     pic_disable();
111
112     _apic_base = ioremap(__APIC_BASE_PADDR, 4096);
113
114     // Hardware enable the APIC
115     // By setting bit 11 of IA32_APIC_BASE register
116     // Note: After this point, you can't disable then re-enable it until a
117     // reset (i.e., reboot)
118     asm volatile("movl %0, %%ecx\n"
119                  "rdmsr\n"
120                  "orl %1, %%eax\n"
121                  "wrmsr\n" ::"i"(IA32_MSR_APIC_BASE),
122                  "i"(IA32_APIC_ENABLE)
123                  : "eax", "ecx", "edx");
124
125     // Print the basic information of our current local APIC
126     u32_t apic_id = apic_read_reg(APIC_IDR) >> 24;
127     u32_t apic_ver = apic_read_reg(APIC_VER);
128
129     kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u",
130             apic_id,
131             apic_ver & 0xff,
132             (apic_ver >> 16) & 0xff);
133
134     // initialize the local vector table (LVT)
135     apic_setup_lvts();
136
137     // initialize priority registers
138
139     // set the task priority to the lowest possible, so all external interrupts
140     // are acceptable
141     //   Note, the lowest possible priority class is 2, not 0, 1, as they are
142     //   reserved for internal interrupts (vector 0-31, and each p-class
143     //   resposible for 16 vectors). See Intel Manual Vol. 3A, 10-29
144     apic_write_reg(APIC_TPR, APIC_PRIORITY(2, 0));
145
146     // enable APIC
147     u32_t spiv = apic_read_reg(APIC_SPIVR);
148
149     // install our handler for spurious interrupt.
150     spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV;
151     apic_write_reg(APIC_SPIVR, spiv);
152 }
153
154 static void
155 ioapic_init()
156 {
157     acpi_context* acpi_ctx = acpi_get_context();
158
159     _ioapic_base =
160         ioremap(acpi_ctx->madt.ioapic->ioapic_addr & ~0xfff, 4096);
161 }
162
163 static void
164 ioapic_write(u8_t sel, u32_t val)
165 {
166     IOAPIC_REG_SEL = sel;
167     IOAPIC_REG_WIN = val;
168 }
169
170 static u32_t
171 ioapic_read(u8_t sel)
172 {
173     IOAPIC_REG_SEL = sel;
174     return IOAPIC_REG_WIN;
175 }
176
177 static void
178 __ioapic_install_line(struct irq_domain *domain, irq_t irq)
179 {
180     struct irq_line_wire *line;
181     u8_t reg_sel;
182     u32_t ioapic_fg;
183
184     line = irq->line;
185     reg_sel = IOAPIC_IOREDTBL_BASE + line->domain_mapped * 2;
186     ioapic_fg = IOAPIC_DELMOD_FIXED;
187
188     // Write low 32 bits
189     ioapic_write(reg_sel, (irq->vector | ioapic_fg) & 0x1FFFF);
190
191     // Write high 32 bits
192     ioapic_write(reg_sel + 1, 0);
193 }
194
195 static int
196 __ioapic_translate_irq(struct irq_domain *domain, irq_t irq, void *irq_extra)
197 {
198     struct irq_line_wire *line;
199
200     if (irq->type == IRQ_LINE) {
201         line = irq->line;
202         line->domain_mapped = acpi_gsimap(line->domain_local);
203     }
204
205     return 0;
206 }
207
208 static void
209 __external_irq_dispatch(const struct hart_state* state)
210 {
211     irq_t irq;
212
213     irq = irq_find(irq_get_default_domain(), hart_vector_stamp(state));
214
215     assert(irq);
216     irq_serve(irq, state);
217 }
218
219 static int
220 __ioapic_install_irq(struct irq_domain *domain, irq_t irq)
221 {
222     irq->vector = isrm_ivexalloc(__external_irq_dispatch);
223
224     if (irq->type == IRQ_MESSAGE) {
225         irq->msi->wr_addr = __APIC_BASE_PADDR;
226     }
227     else {
228         __ioapic_install_line(domain, irq);
229     }
230
231     irq_record(domain, irq);
232     
233     return 0;
234 }
235
236
237 static struct irq_domain_ops apic_domain_ops = {
238     .map_irq = __ioapic_translate_irq,
239     .install_irq = __ioapic_install_irq
240 };
241
242 static int
243 apic_device_create(struct device_def* def, morph_t* morph)
244 {
245     int err;
246     struct device* dev;
247     struct irq_domain* domain;
248
249     apic_init();
250     ioapic_init();
251
252     dev = device_allocsys(NULL, NULL);
253     domain = irq_create_domain(dev, &apic_domain_ops);
254
255     irq_set_default_domain(domain);
256     register_device(dev, &def->class, "apic");
257     irq_attach_domain(NULL, domain);
258     
259     return 0;
260 }
261
262 static struct device_def apic_devdef = {
263     def_device_class(INTEL, CFG, INTC),
264     def_device_name("x86 APIC"),
265     def_on_create(apic_device_create)
266 };
267 EXPORT_DEVICE(x86_apic, &apic_devdef, load_sysconf);