1 /*
2 * Copyright (c) 2012-2018 LK Trusty Authors. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23 #include <arch/x86.h>
24 #include <arch/x86/mmu.h>
25 #include <arch/arch_ops.h>
26 #include <assert.h>
27 #include <debug.h>
28 #include <dev/interrupt/local_apic.h>
29 #include <lk/init.h>
30 #include <lk/macros.h>
31 #include <kernel/vm.h>
32 #include <reg.h>
33
34 typedef enum {
35 LAPIC_ID_REG = 0x2,
36 LAPIC_EOI = 0xB,
37 LAPIC_SIVR = 0xF,
38 LAPIC_INTR_CMD_REG = 0x30, /* 64-bits in x2APIC */
39 LAPIC_INTR_CMD_HI_REG = 0x31, /* not available in x2APIC */
40 LAPIC_SELF_IPI_REG = 0x3F /* not available in xAPIC */
41 } lapic_reg_id_t;
42
43 #define LAPIC_REG_SHIFT 4
44
45 #define PAGE_4K_MASK 0xfffULL
46
47 #define MSR_APIC_BASE 0x1B
48 #define LAPIC_ENABLED (1ULL << 11)
49 #define LAPIC_X2_ENABLED (1ULL << 10)
50 #define LAPIC_BASE_ADDR(base_msr) ((base_msr) & (~PAGE_4K_MASK))
51
52 /* deliver status bit 12. 0 idle, 1 send pending. */
53 #define APIC_DS_BIT (1ULL << 12)
54 #define MSR_X2APIC_BASE 0x800
55
56 #define APIC_DM_FIXED 0x000
57 #define APIC_LEVEL_ASSERT 0x4000
58 #define APIC_DEST_SELF 0x40000
59
60 static vaddr_t lapic_base_virtual_addr = 0;
61 static bool lapic_x2_enabled = false;
62
lapic_is_x2_enabled(uint64_t value)63 static bool lapic_is_x2_enabled(uint64_t value)
64 {
65 return !!(value & LAPIC_X2_ENABLED);
66 }
67
lapic_x1_read_reg(lapic_reg_id_t reg_id)68 static uint32_t lapic_x1_read_reg(lapic_reg_id_t reg_id)
69 {
70 DEBUG_ASSERT(lapic_base_virtual_addr);
71 vaddr_t addr = lapic_base_virtual_addr + (reg_id << LAPIC_REG_SHIFT);
72
73 return readl(addr);
74 }
75
lapic_x1_write_reg(lapic_reg_id_t reg_id,uint32_t value)76 static void lapic_x1_write_reg(lapic_reg_id_t reg_id, uint32_t value)
77 {
78 DEBUG_ASSERT(lapic_base_virtual_addr);
79 vaddr_t addr = lapic_base_virtual_addr + (reg_id << LAPIC_REG_SHIFT);
80
81 writel(value, addr);
82 }
83
84 /* Caller must make sure xAPIC mode. */
lapic_x1_wait_for_ipi(void)85 static void lapic_x1_wait_for_ipi(void)
86 {
87 uint32_t icr_low;
88
89 do {
90 icr_low = lapic_x1_read_reg(LAPIC_INTR_CMD_REG);
91 } while (icr_low & APIC_DS_BIT);
92 }
93
lapic_x2_read_reg(lapic_reg_id_t reg_id)94 static uint64_t lapic_x2_read_reg(lapic_reg_id_t reg_id)
95 {
96 return read_msr(MSR_X2APIC_BASE + reg_id);
97 }
98
lapic_x2_write_reg(lapic_reg_id_t reg_id,uint64_t value)99 static void lapic_x2_write_reg(lapic_reg_id_t reg_id, uint64_t value)
100 {
101 write_msr(MSR_X2APIC_BASE + reg_id, value);
102 }
103
104 /*
105 * Caller must make sure APIC is enabled.
106 * Do not use this API to write ICR register, since xAPIC and
107 * x2APIC have different definition on ICR.
108 */
lapic_write_reg(lapic_reg_id_t reg_id,uint32_t value)109 static void lapic_write_reg(lapic_reg_id_t reg_id, uint32_t value)
110 {
111 DEBUG_ASSERT(LAPIC_INTR_CMD_REG != reg_id);
112
113 if (lapic_x2_enabled) {
114 lapic_x2_write_reg(reg_id, value);
115 } else {
116 lapic_x1_write_reg(reg_id, value);
117 }
118 }
119
lapic_eoi(void)120 void lapic_eoi(void)
121 {
122 lapic_write_reg(LAPIC_EOI, 1);
123 }
124
lapic_software_disable(void)125 void lapic_software_disable(void)
126 {
127 lapic_write_reg(LAPIC_SIVR, 0xFF);
128 }
129
send_self_ipi(uint32_t vector)130 bool send_self_ipi(uint32_t vector)
131 {
132 uint32_t icr_low;
133 uint64_t value = read_msr(MSR_APIC_BASE);
134
135 if (!(value & LAPIC_ENABLED)) {
136 return false;
137 }
138
139 icr_low = APIC_DEST_SELF | APIC_LEVEL_ASSERT | APIC_DM_FIXED | vector;
140
141 if (lapic_x2_enabled) {
142 lapic_x2_write_reg(LAPIC_SELF_IPI_REG, vector);
143 } else {
144 lapic_x1_wait_for_ipi();
145 lapic_x1_write_reg(LAPIC_INTR_CMD_REG, icr_low);
146 }
147
148 return true;
149 }
150
151 /* Remap Local APIC instead of hard code mapping */
local_apic_init(uint level)152 void local_apic_init(uint level)
153 {
154 status_t ret;
155 paddr_t lapic_base_phy_addr;
156 uint64_t value = read_msr(MSR_APIC_BASE);
157
158 lapic_base_phy_addr = LAPIC_BASE_ADDR(value);
159 ret = vmm_alloc_physical(vmm_get_kernel_aspace(),
160 "lapic",
161 4096,
162 (void **)&lapic_base_virtual_addr,
163 PAGE_SIZE_SHIFT,
164 lapic_base_phy_addr,
165 0,
166 ARCH_MMU_FLAG_UNCACHED_DEVICE);
167
168 if (ret) {
169 dprintf(CRITICAL, "Failed to allocate memory for Local APIC!\n");
170 return;
171 }
172 }
173
174 LK_INIT_HOOK(lapic, &local_apic_init, LK_INIT_LEVEL_VM + 1);
175