| 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 | |
| 13 | uint32_t *lapic; |
| 14 | bool x2apic_enabled = false; |
| 15 | static LOG_HANDLE_DECLARE_DEFAULT(lapic); |
| 16 | |
| 17 | void 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 | |
| 22 | void 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 | |
| 47 | void 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 | |
| 53 | void 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 | |
| 59 | void 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 | |
| 65 | void lapic_eoi(struct irq_desc *unused) { |
| 66 | (void) unused; |
| 67 | lapic_write(LAPIC_REG_EOI, val: 0); |
| 68 | } |
| 69 | |
| 70 | bool lapic_timer_is_enabled() { |
| 71 | uint32_t lvt = lapic_read(LAPIC_REG_LVT_TIMER); |
| 72 | return !(lvt & (1 << 16)); |
| 73 | } |
| 74 | |
| 75 | static 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 | |
| 86 | void 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 | |
| 97 | void 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 | |
| 107 | void 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 | |
| 135 | static 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 | |
| 146 | void 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 | |
| 158 | uint32_t x2apic_get_id(void) { |
| 159 | return rdmsr(IA32_X2APIC_ID) & 0xFFFFFFFF; |
| 160 | } |
| 161 | |
| 162 | uint64_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 | |
| 168 | uint32_t cpu_get_this_id(void) { |
| 169 | return cpu_has_x2apic() ? x2apic_get_id() : lapic_get_id(); |
| 170 | } |
| 171 | |
| 172 | void 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 | |
| 179 | static 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 | |
| 188 | struct irq_chip *lapic_get_chip() { |
| 189 | return &lapic_irq_chip; |
| 190 | } |
| 191 | |