4 * @brief Abstraction for Advanced Programmable Interrupts Controller (APIC)
8 * @copyright Copyright (c) 2022
13 #include "asm/x86_cpu.h"
14 #include "asm/soc/apic.h"
18 #include <lunaix/mm/mmio.h>
19 #include <lunaix/spike.h>
20 #include <lunaix/syslog.h>
21 #include <lunaix/device.h>
24 #include <hal/acpi/acpi.h>
30 #define IOAPIC_IOREGSEL 0x00
31 #define IOAPIC_IOWIN 0x10
32 #define IOAPIC_IOREDTBL_BASE 0x10
34 #define IOAPIC_REG_ID 0x00
35 #define IOAPIC_REG_VER 0x01
36 #define IOAPIC_REG_ARB 0x02
38 #define IOAPIC_DELMOD_FIXED 0b000
39 #define IOAPIC_DELMOD_LPRIO 0b001
40 #define IOAPIC_DELMOD_NMI 0b100
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)
47 #define IOAPIC_BASE_VADDR 0x2000
49 #define IOAPIC_REG_SEL *((volatile u32_t*)(_ioapic_base + IOAPIC_IOREGSEL))
50 #define IOAPIC_REG_WIN *((volatile u32_t*)(_ioapic_base + IOAPIC_IOWIN))
53 #define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
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)
62 static volatile ptr_t _ioapic_base;
63 static volatile ptr_t _apic_base;
66 apic_write_reg(unsigned int reg, unsigned int val)
68 *(unsigned int*)(_apic_base + reg) = val;
72 apic_ack_interrupt(irq_t irq)
74 *(unsigned int*)(_apic_base + APIC_EOI) = 0;
78 apic_read_reg(unsigned int reg)
80 return *(unsigned int*)(_apic_base + (reg));
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));
95 // ensure that external interrupt is disabled
96 cpu_disable_interrupt();
98 // Make sure the APIC is there
99 // FUTURE: Use 8259 as fallback
101 // FIXME apic abstraction as local interrupt controller
102 // assert_msg(cpu_has_apic(), "No APIC detected!");
104 // As we are going to use APIC, disable the old 8259 PIC
107 _apic_base = ioremap(__APIC_BASE_PADDR, 4096);
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"
116 "wrmsr\n" ::"i"(IA32_MSR_APIC_BASE),
117 "i"(IA32_APIC_ENABLE)
118 : "eax", "ecx", "edx");
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);
124 kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u",
127 (apic_ver >> 16) & 0xff);
129 // initialize the local vector table (LVT)
132 // initialize priority registers
134 // set the task priority to the lowest possible, so all external interrupts
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));
142 u32_t spiv = apic_read_reg(APIC_SPIVR);
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);
152 acpi_context* acpi_ctx = acpi_get_context();
155 ioremap(acpi_ctx->madt.ioapic->ioapic_addr & ~0xfff, 4096);
159 ioapic_write(u8_t sel, u32_t val)
161 IOAPIC_REG_SEL = sel;
162 IOAPIC_REG_WIN = val;
166 ioapic_read(u8_t sel)
168 IOAPIC_REG_SEL = sel;
169 return IOAPIC_REG_WIN;
173 __ioapic_install_line(struct irq_domain *domain, irq_t irq)
175 struct irq_line_wire *line;
180 reg_sel = IOAPIC_IOREDTBL_BASE + line->domain_mapped * 2;
181 ioapic_fg = IOAPIC_DELMOD_FIXED;
184 ioapic_write(reg_sel, (irq->vector | ioapic_fg) & 0x1FFFF);
186 // Write high 32 bits
187 ioapic_write(reg_sel + 1, 0);
191 __ioapic_translate_irq(struct irq_domain *domain, irq_t irq, void *irq_extra)
193 struct irq_line_wire *line;
195 if (irq->type == IRQ_LINE) {
197 line->domain_mapped = acpi_gsimap(line->domain_local);
204 __ioapic_install_irq(struct irq_domain *domain, irq_t irq)
206 if (irq->vector == IRQ_VECTOR_UNSET) {
207 irq->vector = btrie_map(&domain->irq_map, IV_EX_BEGIN, IV_EX_END, irq);
210 irq_record(domain, irq);
213 if (irq->type == IRQ_MESSAGE) {
214 irq->msi->wr_addr = __APIC_BASE_PADDR;
217 __ioapic_install_line(domain, irq);
224 static struct irq_domain_ops apic_domain_ops = {
225 .map_irq = __ioapic_translate_irq,
226 .install_irq = __ioapic_install_irq
230 apic_device_create(struct device_def* def, morph_t* morph)
234 struct irq_domain* domain;
239 dev = device_allocsys(NULL, NULL);
240 domain = irq_create_domain(dev, &apic_domain_ops);
242 irq_set_default_domain(domain);
243 register_device(dev, &def->class, "apic");
244 irq_attach_domain(NULL, domain);
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)
254 EXPORT_DEVICE(x86_apic, &apic_devdef, load_sysconf);