1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <assert.h>
4 #include <console/console.h>
5 #include <cpu/cpu.h>
6 #include <cpu/x86/lapic.h>
7 #include <cpu/x86/lapic_def.h>
8 #include <cpu/x86/msr.h>
9 #include <smp/node.h>
10 #include <types.h>
11
12 static bool quirk_x2apic_allowed;
13
enable_lapic_mode(bool try_set_x2apic)14 void enable_lapic_mode(bool try_set_x2apic)
15 {
16 uintptr_t apic_base;
17 bool use_x2apic = false;
18 msr_t msr;
19
20 msr = rdmsr(LAPIC_BASE_MSR);
21 if (!(msr.lo & LAPIC_BASE_MSR_ENABLE)) {
22 msr.hi &= 0xffffff00;
23 msr.lo &= ~LAPIC_BASE_MSR_ADDR_MASK;
24 msr.lo |= LAPIC_DEFAULT_BASE;
25 msr.lo |= LAPIC_BASE_MSR_ENABLE;
26 wrmsr(LAPIC_BASE_MSR, msr);
27 msr = rdmsr(LAPIC_BASE_MSR);
28 }
29
30 ASSERT(msr.lo & LAPIC_BASE_MSR_ENABLE);
31
32 apic_base = msr.lo & LAPIC_BASE_MSR_ADDR_MASK;
33 ASSERT(apic_base == LAPIC_DEFAULT_BASE);
34
35 if (try_set_x2apic)
36 use_x2apic = !!(cpu_get_feature_flags_ecx() & CPUID_X2APIC);
37
38 if (use_x2apic == !!(msr.lo & LAPIC_BASE_MSR_X2APIC_MODE)) {
39 printk(BIOS_INFO, "LAPIC 0x%x in %s mode.\n", lapicid(),
40 use_x2apic ? "X2APIC" : "XAPIC");
41 } else if (use_x2apic) {
42 msr.lo |= LAPIC_BASE_MSR_X2APIC_MODE;
43 wrmsr(LAPIC_BASE_MSR, msr);
44 msr = rdmsr(LAPIC_BASE_MSR);
45 ASSERT(!!(msr.lo & LAPIC_BASE_MSR_X2APIC_MODE));
46 printk(BIOS_INFO, "LAPIC 0x%x switched to X2APIC mode.\n", lapicid());
47 } else {
48 die("Switching from X2APIC to XAPIC mode is not implemented.");
49 }
50
51 if (CONFIG(X2APIC_LATE_WORKAROUND) && use_x2apic)
52 quirk_x2apic_allowed = true;
53 }
54
enable_lapic(void)55 void enable_lapic(void)
56 {
57 bool try_set_x2apic = true;
58
59 if (CONFIG(XAPIC_ONLY))
60 try_set_x2apic = false;
61 else if (CONFIG(X2APIC_LATE_WORKAROUND))
62 try_set_x2apic = quirk_x2apic_allowed;
63
64 enable_lapic_mode(try_set_x2apic);
65 }
66
disable_lapic(void)67 void disable_lapic(void)
68 {
69 msr_t msr;
70 msr = rdmsr(LAPIC_BASE_MSR);
71 msr.lo &= ~(LAPIC_BASE_MSR_ENABLE | LAPIC_BASE_MSR_X2APIC_MODE);
72 wrmsr(LAPIC_BASE_MSR, msr);
73 }
74
cpu_get_lapic_addr(void)75 uintptr_t cpu_get_lapic_addr(void)
76 {
77 return LAPIC_DEFAULT_BASE;
78 }
79
setup_lapic_interrupts(void)80 void setup_lapic_interrupts(void)
81 {
82 /*
83 * Set Task Priority to 'accept all'.
84 */
85 lapic_update32(LAPIC_TASKPRI, ~LAPIC_TPRI_MASK, 0);
86
87 /* Set spurious interrupt vector to 0 and keep LAPIC enabled to
88 be able to clear LVT register mask bits. */
89 lapic_update32(LAPIC_SPIV, ~LAPIC_VECTOR_MASK, LAPIC_SPIV_ENABLE);
90
91 /* Put the local APIC in virtual wire mode */
92 uint32_t mask = LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER | LAPIC_INPUT_POLARITY |
93 LAPIC_DELIVERY_MODE_MASK;
94
95 if (boot_cpu())
96 lapic_update32(LAPIC_LVT0, ~mask, LAPIC_DELIVERY_MODE_EXTINT);
97 else
98 lapic_update32(LAPIC_LVT0, ~mask, LAPIC_LVT_MASKED |
99 LAPIC_DELIVERY_MODE_EXTINT);
100
101 lapic_update32(LAPIC_LVT1, ~mask, LAPIC_DELIVERY_MODE_NMI);
102 }
103