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
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
30 #include <libpayload.h>
31 #include <arch/apic.h>
32 #include <arch/cpuid.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)
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
{
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
);
100 die_if(!apic_bar
, "APIC is not initialized");
103 (apic_read32(APIC_ID
) & APIC_ID_MASK
) >> APIC_ID_SHIFT
;
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 */
121 /* Disable interrupts so we don't get a race condition between
122 * starting the timer and the hlt instruction. */
123 disable_interrupts();
127 apic_write32(APIC_TIMER_INIT_COUNT
, ticks
);
129 /* Loop in case another interrupt has fired and resumed execution. */
134 /* Disable interrupts to prevent a race condition
135 * between checking timer_waiting and executing the hlt
136 * instruction again. */
138 } while (timer_waiting
);
140 /* Leave hardware interrupts enabled. */
144 static void timer_interrupt_handler(u8 vector
)
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.
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
;
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
;
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
;
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 */
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. */
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
)
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
);
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();
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");