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