libpayload/apic: Register a spurious interrupt vector
[coreboot.git] / payloads / libpayload / arch / x86 / apic.c
blobdb81ea1163bc839867bbe7c548af8b4f8f2ddfca
1 /*
2 * This file is part of the libpayload project.
4 * Copyright 2018 Google LLC
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
30 #include <libpayload.h>
31 #include <arch/apic.h>
32 #include <arch/cpuid.h>
33 #include <arch/msr.h>
34 #include <exception.h>
36 #define APIC_BASE_MSR 0x0000001B
37 #define APIC_BASE_MASK (0xFFFFFFFULL << 12)
39 #define CPUID_XAPIC_ENABLED_BIT (1 << 9)
40 #define CPUID_XAPIC2_ENABLED_BIT (1 << 21)
42 #define XAPIC_ENABLED_BIT (1 << 11)
43 #define X2APIC_ENABLED_BIT (1 << 10)
44 #define APIC_MASKED_BIT (1 << 16)
46 #define APIC_ID 0x020
47 #define APIC_ID_SHIFT 24
48 #define APIC_ID_MASK (0xFFUL << APIC_ID_SHIFT)
49 #define APIC_VERSION 0x030
50 #define APIC_MAX_LVT_SHIFT 16
51 #define APIC_MAX_LVT_MASK (0xFFUL << APIC_MAX_LVT_SHIFT)
52 #define APIC_TASK_PRIORITY 0x080
53 #define APIC_TASK_PRIORITY_MASK 0xFFUL
54 #define APIC_EOI 0x0B0
55 #define APIC_SPURIOUS 0x0F0
56 #define APIC_SW_ENABLED_BIT (1 << 8)
57 #define APIC_SPURIOUS_VECTOR_MASK 0xFFUL
58 #define APIC_SPURIOUS 0x0F0
59 #define APIC_LVT_TIMER 0x320
60 #define APIC_TIMER_INIT_COUNT 0x380
61 #define APIC_TIMER_CUR_COUNT 0x390
62 #define APIC_TIMER_DIV_CFG 0x3E0
63 #define APIC_ISR_0 0x100
64 #define APIC_ISR_OFFSET 0x010
66 #define APIC_LVT_SIZE 0x010
68 #define APIC_TIMER_VECTOR 0x20UL
69 #define APIC_SPURIOUS_VECTOR 0xFFUL
71 static uint32_t apic_bar;
72 static int _apic_initialized;
73 // TODO: Build a lookup table to avoid calculating it.
74 static uint32_t ticks_per_ms;
75 static volatile uint8_t timer_waiting;
77 enum APIC_CAPABILITY {
78 DISABLED = 0,
79 XACPI = 1 << 0,
80 X2ACPI = 1 << 1
83 int apic_initialized(void)
85 return _apic_initialized;
88 static inline uint32_t apic_read32(uint32_t offset)
90 return read32((void *)(apic_bar + offset));
93 static inline void apic_write32(uint32_t offset, uint32_t value)
95 write32((void *)(apic_bar + offset), value);
98 uint8_t apic_id(void)
100 die_if(!apic_bar, "APIC is not initialized");
102 uint8_t id =
103 (apic_read32(APIC_ID) & APIC_ID_MASK) >> APIC_ID_SHIFT;
105 return id;
108 void apic_delay(unsigned int usec)
110 die_if(!ticks_per_ms, "apic_init_timer was not run.");
111 die_if(timer_waiting, "timer already started.");
112 die_if(!interrupts_enabled(), "Interrupts disabled.");
114 /* The order is important so we don't underflow */
115 uint64_t ticks = usec * ticks_per_ms / USECS_PER_MSEC;
117 /* Not enough resolution */
118 if (!ticks)
119 return;
121 /* Disable interrupts so we don't get a race condition between
122 * starting the timer and the hlt instruction. */
123 disable_interrupts();
125 timer_waiting = 1;
127 apic_write32(APIC_TIMER_INIT_COUNT, ticks);
129 /* Loop in case another interrupt has fired and resumed execution. */
130 do {
131 asm volatile(
132 "sti\n\t"
133 "hlt\n\t"
134 /* Disable interrupts to prevent a race condition
135 * between checking timer_waiting and executing the hlt
136 * instruction again. */
137 "cli\n\t");
138 } while (timer_waiting);
140 /* Leave hardware interrupts enabled. */
141 enable_interrupts();
144 static void timer_interrupt_handler(u8 vector)
146 timer_waiting = 0;
149 static void suprious_interrupt_handler(u8 vector) {}
151 void apic_eoi(uint8_t vector)
153 die_if(!apic_bar, "APIC is not initialized");
156 * Local and I/O APICs support 240 vectors (in the range of 16 to 255)
157 * as valid interrupts.
159 if (vector <= 15)
160 return;
162 /* Each bank handles 32 vectors */
163 uint8_t bank = vector / 32;
165 uint32_t offset = APIC_ISR_0 + bank * APIC_ISR_OFFSET;
167 uint32_t mask = apic_read32(offset);
169 uint8_t shift = vector % 32;
171 if (mask & (1 << shift))
172 apic_write32(APIC_EOI, 0);
175 static enum APIC_CAPABILITY apic_capabilities(void)
177 uint32_t eax, ebx, ecx, edx;
179 cpuid(1, eax, ebx, ecx, edx);
181 enum APIC_CAPABILITY capabilities = DISABLED;
183 if (edx & CPUID_XAPIC_ENABLED_BIT)
184 capabilities |= XACPI;
186 if (ecx & CPUID_XAPIC2_ENABLED_BIT)
187 capabilities |= X2ACPI;
189 return capabilities;
192 static uint8_t apic_max_lvt_entries(void)
194 die_if(!apic_bar, "APIC is not initialized");
196 uint32_t reg = apic_read32(APIC_VERSION);
197 reg &= APIC_MAX_LVT_MASK;
198 reg >>= APIC_MAX_LVT_SHIFT;
200 return (uint8_t)reg;
203 static void apic_reset_all_lvts(void)
205 uint8_t max = apic_max_lvt_entries();
206 for (int i = 0; i <= max; ++i) {
207 uint32_t offset = APIC_LVT_TIMER + APIC_LVT_SIZE * i;
208 apic_write32(offset, APIC_MASKED_BIT);
212 static void apic_set_task_priority(uint8_t priority)
214 die_if(!apic_bar, "APIC is not initialized");
216 uint32_t tpr = apic_read32(APIC_TASK_PRIORITY);
217 tpr &= ~APIC_TASK_PRIORITY_MASK;
218 tpr |= priority;
220 apic_write32(APIC_TASK_PRIORITY, priority);
223 static void apic_init_timer(void)
225 die_if(!apic_bar, "APIC is not initialized");
227 apic_write32(APIC_LVT_TIMER, APIC_MASKED_BIT);
229 /* Divide the clock by 1. */
230 apic_write32(APIC_TIMER_DIV_CFG, 0xB);
232 /* Calibrate the APIC timer */
233 if (!ticks_per_ms) {
234 /* Set APIC init counter to MAX and count for 1 ms */
235 apic_write32(APIC_TIMER_INIT_COUNT, UINT32_MAX);
237 /* This is safe because apic_initialized() returns false so
238 * arch_ndelay() falls back to a busy loop. */
239 mdelay(1);
241 ticks_per_ms =
242 UINT32_MAX - apic_read32(APIC_TIMER_CUR_COUNT);
245 /* Clear the count so we don't get any stale interrupts */
246 apic_write32(APIC_TIMER_INIT_COUNT, 0);
248 /* Unmask the timer and set the vector. */
249 apic_write32(APIC_LVT_TIMER, APIC_TIMER_VECTOR);
252 static void apic_sw_enable(void)
254 uint32_t reg = apic_read32(APIC_SPURIOUS);
255 if (reg & APIC_SW_ENABLED_BIT)
256 return;
258 reg |= APIC_SW_ENABLED_BIT;
260 apic_write32(APIC_SPURIOUS, reg);
263 static void apic_setup_spurious(void)
265 uint32_t reg = apic_read32(APIC_SPURIOUS);
267 reg &= ~APIC_SPURIOUS_VECTOR_MASK;
269 reg |= APIC_SPURIOUS_VECTOR;
271 apic_write32(APIC_SPURIOUS, reg);
274 void apic_init(void)
276 uint64_t apic_bar_reg;
278 printf("APIC Init Started\n");
280 die_if(apic_initialized(), "APIC already initialized");
281 die_if(!(apic_capabilities() & XACPI), "APIC is not supported");
283 apic_bar_reg = _rdmsr(APIC_BASE_MSR);
285 die_if(!(apic_bar_reg & XAPIC_ENABLED_BIT), "APIC is not enabled");
286 die_if(apic_bar_reg & X2APIC_ENABLED_BIT,
287 "APIC is configured in x2APIC mode which is not supported");
289 apic_bar = (uint32_t)(apic_bar_reg & APIC_BASE_MASK);
291 apic_reset_all_lvts();
292 apic_set_task_priority(0);
293 apic_setup_spurious();
295 apic_sw_enable();
297 apic_init_timer();
299 set_interrupt_handler(APIC_TIMER_VECTOR, &timer_interrupt_handler);
300 set_interrupt_handler(APIC_SPURIOUS_VECTOR,
301 &suprious_interrupt_handler);
303 _apic_initialized = 1;
305 printf("APIC Configured\n");