1#include <acpi/lapic.h>
2#include <asm.h>
3#include <drivers/mmio.h>
4#include <global.h>
5#include <irq/idt.h>
6#include <log.h>
7#include <mem/alloc.h>
8#include <mem/page.h>
9#include <mem/vmm.h>
10#include <smp/core.h>
11#include <time/spin_sleep.h>
12
13uint32_t *lapic;
14bool x2apic_enabled = false;
15static LOG_HANDLE_DECLARE_DEFAULT(lapic);
16
17void lapic_init(void) {
18 uintptr_t lapic_phys = rdmsr(IA32_APIC_BASE_MSR) & IA32_APIC_BASE_MASK;
19 lapic = mmio_map(phys: lapic_phys, PAGE_SIZE);
20}
21
22void lapic_timer_init(cpu_id_t core_id) {
23 uint32_t calibration_sleep_ms = 2;
24 uint32_t timeslice_ms = 15;
25
26 lapic_write(LAPIC_REG_SVR, LAPIC_ENABLE | 0xFF);
27 lapic_write(LAPIC_REG_TIMER_DIV, val: 0b0011);
28 lapic_write(LAPIC_REG_LVT_TIMER, TIMER_VECTOR | LAPIC_LVT_MASK);
29 lapic_write(LAPIC_REG_TIMER_INIT, val: 0xFFFFFFFF);
30
31 sleep_spin_ms(msec: calibration_sleep_ms);
32
33 uint32_t curr = lapic_read(reg: (LAPIC_REG_TIMER_CUR));
34 uint32_t elapsed = 0xFFFFFFFF - curr;
35
36 uint64_t lapic_calibrated_freq = elapsed * (1000 / calibration_sleep_ms);
37
38 uint32_t timeslice_ticks = (lapic_calibrated_freq * timeslice_ms) / 1000;
39
40 global.cores[core_id]->lapic_freq = lapic_calibrated_freq;
41 lapic_write(LAPIC_REG_LVT_TIMER, TIMER_VECTOR | TIMER_MODE_PERIODIC);
42
43 lapic_write(LAPIC_REG_TIMER_INIT, val: timeslice_ticks);
44 lapic_timer_disable();
45}
46
47void lapic_timer_set_ms(uint32_t ms) {
48 uint32_t ticks = (smp_core()->lapic_freq * ms) / 1000;
49
50 lapic_write(LAPIC_REG_TIMER_INIT, val: ticks);
51}
52
53void lapic_timer_disable() {
54 uint32_t lvt = lapic_read(LAPIC_REG_LVT_TIMER);
55 lvt |= LAPIC_LVT_MASK;
56 lapic_write(LAPIC_REG_LVT_TIMER, val: lvt);
57}
58
59void lapic_timer_enable() {
60 uint32_t lvt = lapic_read(LAPIC_REG_LVT_TIMER);
61 lvt &= ~LAPIC_LVT_MASK;
62 lapic_write(LAPIC_REG_LVT_TIMER, val: lvt);
63}
64
65void lapic_eoi(struct irq_desc *unused) {
66 (void) unused;
67 lapic_write(LAPIC_REG_EOI, val: 0);
68}
69
70bool lapic_timer_is_enabled() {
71 uint32_t lvt = lapic_read(LAPIC_REG_LVT_TIMER);
72 return !(lvt & (1 << 16));
73}
74
75static void lapic_send_ipi(uint8_t apic_id, uint8_t vector) {
76 enum irql irql = irql_raise(new_level: IRQL_HIGH_LEVEL);
77 while (lapic_read(LAPIC_ICR_LOW) & LAPIC_IPI_IN_FLIGHT)
78 cpu_relax();
79
80 lapic_write(LAPIC_ICR_HIGH, val: apic_id << LAPIC_DEST_SHIFT);
81 lapic_write(LAPIC_ICR_LOW, val: vector | LAPIC_DELIVERY_FIXED |
82 LAPIC_LEVEL_ASSERT | LAPIC_DEST_PHYSICAL);
83 irql_lower(old_level: irql);
84}
85
86void x2apic_send_ipi(uint32_t apic_id, uint8_t vector) {
87 uint64_t icr = 0;
88 icr |= vector;
89 icr |= LAPIC_DELIVERY_FIXED;
90 icr |= LAPIC_LEVEL_ASSERT;
91 icr |= LAPIC_DEST_PHYSICAL;
92 icr |= ((uint64_t) apic_id << 32);
93
94 wrmsr(IA32_X2APIC_ICR, value: icr);
95}
96
97void panic_broadcast(uint64_t exclude_core) {
98 size_t i;
99 for_each_cpu_id(i) {
100 if (i == exclude_core)
101 continue;
102
103 nmi_send(apic_id: i);
104 }
105}
106
107void nmi_send(uint32_t apic_id) {
108 if (x2apic_enabled) {
109 uint64_t icr = 0;
110
111 /* Delivery mode = NMI. Vector ignored. */
112 icr |= LAPIC_DELIVERY_NMI; /* bits 10:8 = 100b for NMI */
113 icr |= LAPIC_DEST_PHYSICAL; /* physical dest mode */
114 icr |= ((uint64_t) apic_id << 32);
115
116 wrmsr(IA32_X2APIC_ICR, value: icr);
117 return;
118 }
119
120 uint32_t hi = apic_id << LAPIC_DEST_SHIFT;
121 uint32_t lo = 0;
122
123 lo |= LAPIC_DELIVERY_NMI;
124 lo |= LAPIC_DEST_PHYSICAL;
125 lo |= LAPIC_LEVEL_ASSERT;
126
127 /* High must be written before low */
128 lapic_write(LAPIC_ICR_HIGH, val: hi);
129 lapic_write(LAPIC_ICR_LOW, val: lo);
130
131 while (lapic_read(LAPIC_ICR_LOW) & LAPIC_IPI_IN_FLIGHT)
132 cpu_relax();
133}
134
135static int cpu_has_x2apic(void) {
136 uint32_t eax, ebx, ecx, edx;
137
138 asm volatile("cpuid"
139 : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
140 : "a"(1)
141 :);
142
143 return (ecx >> 21) & 1;
144}
145
146void x2apic_init(void) {
147 if (!cpu_has_x2apic())
148 return;
149
150 x2apic_enabled = true;
151 uint64_t apic_base;
152 apic_base = rdmsr(IA32_APIC_BASE);
153 apic_base |= APIC_X2APIC_ENABLE;
154 wrmsr(IA32_APIC_BASE, value: apic_base);
155 log_info_global(LOG_HANDLE(lapic), "X2APIC enabled");
156}
157
158uint32_t x2apic_get_id(void) {
159 return rdmsr(IA32_X2APIC_ID) & 0xFFFFFFFF;
160}
161
162uint64_t lapic_get_id(void) {
163 uint32_t lapic_id_raw = lapic_read(LAPIC_REG_ID);
164 uint64_t cpu = (lapic_id_raw >> 24) & 0xFF;
165 return cpu;
166}
167
168uint32_t cpu_get_this_id(void) {
169 return cpu_has_x2apic() ? x2apic_get_id() : lapic_get_id();
170}
171
172void ipi_send(uint32_t apic_id, uint8_t vector) {
173 if (x2apic_enabled)
174 return x2apic_send_ipi(apic_id, vector);
175
176 lapic_send_ipi(apic_id, vector);
177}
178
179static struct irq_chip lapic_irq_chip = {
180 .eoi = lapic_eoi,
181 .mask = NULL,
182 .unmask = NULL,
183 .set_affinity = NULL,
184 .set_rate_limit = NULL,
185 .name = "lapic",
186};
187
188struct irq_chip *lapic_get_chip() {
189 return &lapic_irq_chip;
190}
191