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