Embed ISRs at known address
[viridis.git] / kernel / apic.c
1 /* vim: set ts=4 sw=4 sts=4 et : */
2
3 /* Support for the built-in xAPIC, or x2APIC. We don't bother with the old 386
4  * PIC except to disable it because xAPIC is a foundation technology of the
5  * AMD64 arch.
6  *
7  * While I'm reticent to implement too much "old" hardware, the interfaces here
8  * are very similar, and since xAPIC was present on all early x86_64 hardware
9  * we might as well support it as a baseline.
10  *
11  * Extant differences:
12  *
13  * - x2APIC used via MSRs, not MMIO (although offsets are related)
14  * - x2APIC has a full 32 bit ID, instead of 8 bits
15  * - x2APIC ICR is written as a single 64 bit MSR, instead of 2x 32 bit MMIOs
16  * - x2APIC ICR with expanded ID destination
17  * - x2APIC ICR has no busy bit, will accept writes in order
18  */
19
20 #include <kernel.h>
21 #include <vsalloc.h>
22 #include <map.h>
23 #include <apic.h>
24
25 #include <asm/cpuid.h>
26 #include <asm/realmode.h>
27 #include <asm/barrier.h>
28 #include <asm/mmio.h>
29 #include <asm/msr.h>
30
31 static volatile u64 *mmio_mem = NULL;
32
33 static void apic_init_common(void)
34 {
35     apic->write(APIC_SVR, SVR_APIC_EN);
36     apic->write(APIC_LVT3, LVT_VECTOR(34));
37 }
38
39 static void apic_start_timer(void)
40 {
41     u32 lvt;
42
43     /* Divide by 1 */
44     apic->write(APIC_TIMER_DIV, 0x11);
45
46     /* Setup a periodic timer interrupt */
47     lvt = apic->read(APIC_LVT_TIMER);
48
49     lvt &= ~LVT_MASKED;
50     lvt |= LVT_VECTOR(32) | LVT_TIMER_PERIODIC;
51
52     apic->write(APIC_LVT_TIMER, lvt);
53
54     /* Set the initial count, which will start the timer */
55     apic->write(APIC_TIMER_INIT_CT, 0x1000);
56 }
57
58 static void apic_eoi(void)
59 {
60     /* Allow another interrupt to come through when we re-enable
61      * them (exiting through exception)
62      */
63     apic->write(APIC_EOI, 0);
64 }
65
66 static void apic_wake_secondary(u8 apic_id)
67 {
68     u32 lo;
69
70     lo = ICRL_MT(MT_INIT) | ICRL_ASSERT | ICRL_TRIGGER_LEVEL;
71
72     apic->write_icr(apic_id, lo);
73
74     apic->wait();
75
76     lo = ICRL_MT(MT_INIT) | ICRL_TRIGGER_LEVEL;
77
78     apic->write_icr(apic_id, lo);
79
80     apic->wait();
81
82     lo = ICRL_MT(MT_STARTUP) | ICRL_ASSERT | 2;
83
84     apic->write_icr(apic_id, lo);
85
86     apic->wait();
87
88     lo = ICRL_MT(MT_STARTUP) | ICRL_ASSERT | 2;
89
90     apic->write_icr(apic_id, lo);
91
92     apic->wait();
93 }
94
95 static void xapic_init(void)
96 {
97     u64 msr;
98
99     /* Make sure the APIC is actually enabled */
100     msr = rdmsr(MSR_IA32_APIC_BASE);
101     wrmsr(MSR_IA32_APIC_BASE, msr | XAPIC_ENABLE);
102
103     /* Get a nocache virtual memory of the base memory */
104     mmio_mem = vsalloc("APIC", 0, 1);
105
106     map_page_nocache((u64) mmio_mem, msr & ~(0xFFFUL));
107
108     apic_init_common();
109 }
110
111 static void xapic_mmio_write(u32 offset, u32 val)
112 {
113     mmio_write(mmio_mem, MMIO_OFFSET(offset), val);
114 }
115
116 static u32 xapic_mmio_read(u32 offset)
117 {
118     return mmio_read(mmio_mem, MMIO_OFFSET(offset));
119 }
120
121 static void xapic_ipi_wait(void)
122 {
123     while (apic->read(APIC_ICRL) & ICRL_BUSY) {
124     }
125 }
126
127 static void xapic_write_icr(u32 dest, u32 val)
128 {
129     apic->write(APIC_ICRH, ICRH_DEST(dest));
130     apic->write(APIC_ICRL, val);
131 }
132
133 static u32 xapic_get_id(void)
134 {
135     return ID(apic->read(APIC_ID));
136 }
137
138 struct apic xapic = {
139     .init = xapic_init,
140     .eoi = apic_eoi,
141     .wake_secondary = apic_wake_secondary,
142     .start_timer = apic_start_timer,
143     .get_id = xapic_get_id,
144     .write = xapic_mmio_write,
145     .write_icr = xapic_write_icr,
146     .read = xapic_mmio_read,
147     .wait = xapic_ipi_wait,
148 };
149
150 static void x2apic_init(void)
151 {
152     u64 msr;
153
154     /* Make sure the APIC is actually enabled */
155     msr = rdmsr(MSR_IA32_APIC_BASE);
156
157     /* Need EXTD flag to use MSR interface */
158     wrmsr(MSR_IA32_APIC_BASE, msr | XAPIC_ENABLE | XAPIC_EXTD);
159
160     apic_init_common();
161 }
162
163 static void x2apic_msr_write(u32 offset, u32 val)
164 {
165     wrmsr(MSR_OFFSET(offset), val);
166 }
167
168 static u32 x2apic_msr_read(u32 offset)
169 {
170     return rdmsr(MSR_OFFSET(offset));
171 }
172
173 static void x2apic_ipi_wait(void)
174 {
175     return;
176 }
177
178 static void x2apic_write_icr(u32 dest, u32 val)
179 {
180     wrmsr(MSR_OFFSET(APIC_ICRL), ((u64) dest << 32) | val);
181 }
182
183 static u32 x2apic_get_id(void)
184 {
185     return apic->read(APIC_ID);
186 }
187
188 struct apic x2apic = {
189     .init = x2apic_init,
190     .eoi = apic_eoi,
191     .wake_secondary = apic_wake_secondary,
192     .start_timer = apic_start_timer,
193     .get_id = x2apic_get_id,
194     .read = x2apic_msr_read,
195     .write = x2apic_msr_write,
196     .write_icr = x2apic_write_icr,
197     .wait = x2apic_ipi_wait,
198 };
199
200 int apic_init(void)
201 {
202     u32 regs[4];
203
204     cpuid(0x1, regs);
205
206     if (regs[2] & CPUID_X2APIC) {
207         printk("x2apic detected\n");
208         apic = &x2apic;
209     } else if (regs[3] & CPUID_LAPIC) {
210         printk("xapic detected\n");
211         apic = &xapic;
212     } else {
213         printk("no APIC detected\n");
214         return -1;
215     }
216
217     apic->init();
218
219     return 0;
220 }