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"
17 #include <asm/x86_isrm.h>
19 #include <lunaix/mm/mmio.h>
20 #include <lunaix/spike.h>
21 #include <lunaix/syslog.h>
22 #include <lunaix/device.h>
25 #include <hal/acpi/acpi.h>
31 #define IOAPIC_IOREGSEL 0x00
32 #define IOAPIC_IOWIN 0x10
33 #define IOAPIC_IOREDTBL_BASE 0x10
35 #define IOAPIC_REG_ID 0x00
36 #define IOAPIC_REG_VER 0x01
37 #define IOAPIC_REG_ARB 0x02
39 #define IOAPIC_DELMOD_FIXED 0b000
40 #define IOAPIC_DELMOD_LPRIO 0b001
41 #define IOAPIC_DELMOD_NMI 0b100
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)
48 #define IOAPIC_BASE_VADDR 0x2000
50 #define IOAPIC_REG_SEL *((volatile u32_t*)(_ioapic_base + IOAPIC_IOREGSEL))
51 #define IOAPIC_REG_WIN *((volatile u32_t*)(_ioapic_base + IOAPIC_IOWIN))
54 #define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
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)
63 static volatile ptr_t _ioapic_base;
64 static volatile ptr_t _apic_base;
67 apic_write_reg(unsigned int reg, unsigned int val)
69 *(unsigned int*)(_apic_base + reg) = val;
73 isrm_notify_eoi(cpu_t id, int iv)
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;
83 apic_read_reg(unsigned int reg)
85 return *(unsigned int*)(_apic_base + (reg));
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));
100 // ensure that external interrupt is disabled
101 cpu_disable_interrupt();
103 // Make sure the APIC is there
104 // FUTURE: Use 8259 as fallback
106 // FIXME apic abstraction as local interrupt controller
107 // assert_msg(cpu_has_apic(), "No APIC detected!");
109 // As we are going to use APIC, disable the old 8259 PIC
112 _apic_base = ioremap(__APIC_BASE_PADDR, 4096);
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"
121 "wrmsr\n" ::"i"(IA32_MSR_APIC_BASE),
122 "i"(IA32_APIC_ENABLE)
123 : "eax", "ecx", "edx");
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);
129 kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u",
132 (apic_ver >> 16) & 0xff);
134 // initialize the local vector table (LVT)
137 // initialize priority registers
139 // set the task priority to the lowest possible, so all external interrupts
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));
147 u32_t spiv = apic_read_reg(APIC_SPIVR);
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);
157 acpi_context* acpi_ctx = acpi_get_context();
160 ioremap(acpi_ctx->madt.ioapic->ioapic_addr & ~0xfff, 4096);
164 ioapic_write(u8_t sel, u32_t val)
166 IOAPIC_REG_SEL = sel;
167 IOAPIC_REG_WIN = val;
171 ioapic_read(u8_t sel)
173 IOAPIC_REG_SEL = sel;
174 return IOAPIC_REG_WIN;
178 __ioapic_install_line(struct irq_domain *domain, irq_t irq)
180 struct irq_line_wire *line;
185 reg_sel = IOAPIC_IOREDTBL_BASE + line->domain_mapped * 2;
186 ioapic_fg = IOAPIC_DELMOD_FIXED;
189 ioapic_write(reg_sel, (irq->vector | ioapic_fg) & 0x1FFFF);
191 // Write high 32 bits
192 ioapic_write(reg_sel + 1, 0);
196 __ioapic_translate_irq(struct irq_domain *domain, irq_t irq, void *irq_extra)
198 struct irq_line_wire *line;
200 if (irq->type == IRQ_LINE) {
202 line->domain_mapped = acpi_gsimap(line->domain_local);
209 __external_irq_dispatch(const struct hart_state* state)
213 irq = irq_find(irq_get_default_domain(), hart_vector_stamp(state));
216 irq_serve(irq, state);
220 __ioapic_install_irq(struct irq_domain *domain, irq_t irq)
222 irq->vector = isrm_ivexalloc(__external_irq_dispatch);
224 if (irq->type == IRQ_MESSAGE) {
225 irq->msi->wr_addr = __APIC_BASE_PADDR;
228 __ioapic_install_line(domain, irq);
231 irq_record(domain, irq);
237 static struct irq_domain_ops apic_domain_ops = {
238 .map_irq = __ioapic_translate_irq,
239 .install_irq = __ioapic_install_irq
243 apic_device_create(struct device_def* def, morph_t* morph)
247 struct irq_domain* domain;
252 dev = device_allocsys(NULL, NULL);
253 domain = irq_create_domain(dev, &apic_domain_ops);
255 irq_set_default_domain(domain);
256 register_device(dev, &def->class, "apic");
257 irq_attach_domain(NULL, domain);
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)
267 EXPORT_DEVICE(x86_apic, &apic_devdef, load_sysconf);