Decoupling Architectural-specific Code (#35)
[lunaix-os.git] / lunaix-os / arch / i386 / 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 "sys/x86_isa.h"
13 #include <sys/cpu.h>
14
15 #include "sys/apic.h"
16 #include <sys/hart.h>
17
18 #include <lunaix/mm/mmio.h>
19 #include <lunaix/spike.h>
20 #include <lunaix/syslog.h>
21
22 #include "pic.h"
23
24 LOG_MODULE("APIC")
25
26 static volatile ptr_t _apic_base;
27
28 void
29 apic_setup_lvts();
30
31 void
32 apic_init()
33 {
34     // ensure that external interrupt is disabled
35     cpu_disable_interrupt();
36
37     // Make sure the APIC is there
38     //  FUTURE: Use 8259 as fallback
39
40     // FIXME apic abstraction as local interrupt controller
41     // assert_msg(cpu_has_apic(), "No APIC detected!");
42
43     // As we are going to use APIC, disable the old 8259 PIC
44     pic_disable();
45
46     _apic_base = (ptr_t)ioremap(__APIC_BASE_PADDR, 4096);
47
48     // Hardware enable the APIC
49     // By setting bit 11 of IA32_APIC_BASE register
50     // Note: After this point, you can't disable then re-enable it until a
51     // reset (i.e., reboot)
52     asm volatile("movl %0, %%ecx\n"
53                  "rdmsr\n"
54                  "orl %1, %%eax\n"
55                  "wrmsr\n" ::"i"(IA32_MSR_APIC_BASE),
56                  "i"(IA32_APIC_ENABLE)
57                  : "eax", "ecx", "edx");
58
59     // Print the basic information of our current local APIC
60     u32_t apic_id = apic_read_reg(APIC_IDR) >> 24;
61     u32_t apic_ver = apic_read_reg(APIC_VER);
62
63     kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u",
64             apic_id,
65             apic_ver & 0xff,
66             (apic_ver >> 16) & 0xff);
67
68     // initialize the local vector table (LVT)
69     apic_setup_lvts();
70
71     // initialize priority registers
72
73     // set the task priority to the lowest possible, so all external interrupts
74     // are acceptable
75     //   Note, the lowest possible priority class is 2, not 0, 1, as they are
76     //   reserved for internal interrupts (vector 0-31, and each p-class
77     //   resposible for 16 vectors). See Intel Manual Vol. 3A, 10-29
78     apic_write_reg(APIC_TPR, APIC_PRIORITY(2, 0));
79
80     // enable APIC
81     u32_t spiv = apic_read_reg(APIC_SPIVR);
82
83     // install our handler for spurious interrupt.
84     spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV;
85     apic_write_reg(APIC_SPIVR, spiv);
86 }
87
88 #define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector)
89
90 // Pin LINT#1 is configured for relaying NMI, but we masked it here as I think
91 //  it is too early for that
92 // LINT#1 *must* be edge trigged (Intel manual vol3. 10-14)
93 #define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE)
94 #define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector)
95
96 void
97 apic_setup_lvts()
98 {
99     apic_write_reg(APIC_LVT_LINT0, LVT_ENTRY_LINT0(APIC_LINT0_IV));
100     apic_write_reg(APIC_LVT_LINT1, LVT_ENTRY_LINT1);
101     apic_write_reg(APIC_LVT_ERROR, LVT_ENTRY_ERROR(APIC_ERROR_IV));
102 }
103
104 void
105 apic_on_eoi(struct x86_intc* intc_ctx, cpu_t cpu, int iv)
106 {
107     // for all external interrupts except the spurious interrupt
108     //  this is required by Intel Manual Vol.3A, section 10.8.1 & 10.8.5
109     if (iv >= IV_EX_BEGIN && iv != APIC_SPIV_IV) {
110         *(unsigned int*)(_apic_base + APIC_EOI) = 0;
111     }
112 }
113
114 unsigned int
115 apic_read_reg(unsigned int reg)
116 {
117     return *(unsigned int*)(_apic_base + (reg));
118 }
119
120 void
121 apic_write_reg(unsigned int reg, unsigned int val)
122 {
123     *(unsigned int*)(_apic_base + reg) = val;
124 }