Change-Id: I6faeb7c783052edc4217d2d301dbb905e1fc6a19 Signed-off-by: Elyes HAOUAS <ehaouas@noos.fr> Reviewed-on: https://review.coreboot.org/c/coreboot/+/44605 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Michael Niewöhner <foss@mniewoehner.de>
		
			
				
	
	
		
			333 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *
 | |
|  * Copyright 2018 Google LLC
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions
 | |
|  * are met:
 | |
|  * 1. Redistributions of source code must retain the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer.
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright
 | |
|  *    notice, this list of conditions and the following disclaimer in the
 | |
|  *    documentation and/or other materials provided with the distribution.
 | |
|  * 3. The name of the author may not be used to endorse or promote products
 | |
|  *    derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | |
|  * SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <libpayload.h>
 | |
| #include <arch/apic.h>
 | |
| #include <arch/cpuid.h>
 | |
| #include <arch/msr.h>
 | |
| #include <exception.h>
 | |
| 
 | |
| #define APIC_BASE_MSR			0x0000001B
 | |
| #define APIC_BASE_MASK			(0xFFFFFFFULL << 12)
 | |
| 
 | |
| #define CPUID_XAPIC_ENABLED_BIT		(1 << 9)
 | |
| #define CPUID_XAPIC2_ENABLED_BIT	(1 << 21)
 | |
| 
 | |
| #define XAPIC_ENABLED_BIT		(1 << 11)
 | |
| #define X2APIC_ENABLED_BIT		(1 << 10)
 | |
| #define APIC_MASKED_BIT			(1 << 16)
 | |
| 
 | |
| #define APIC_ID				0x020
 | |
| #define   APIC_ID_SHIFT			24
 | |
| #define   APIC_ID_MASK			(0xFFUL << APIC_ID_SHIFT)
 | |
| #define APIC_VERSION			0x030
 | |
| #define   APIC_MAX_LVT_SHIFT		16
 | |
| #define   APIC_MAX_LVT_MASK		(0xFFUL << APIC_MAX_LVT_SHIFT)
 | |
| #define APIC_TASK_PRIORITY		0x080
 | |
| #define   APIC_TASK_PRIORITY_MASK	0xFFUL
 | |
| #define APIC_EOI			0x0B0
 | |
| #define APIC_SPURIOUS			0x0F0
 | |
| #define   APIC_SW_ENABLED_BIT		(1 << 8)
 | |
| #define   APIC_SPURIOUS_VECTOR_MASK	0xFFUL
 | |
| #define APIC_SPURIOUS			0x0F0
 | |
| #define APIC_LVT_TIMER			0x320
 | |
| #define APIC_TIMER_INIT_COUNT		0x380
 | |
| #define APIC_TIMER_CUR_COUNT		0x390
 | |
| #define APIC_TIMER_DIV_CFG		0x3E0
 | |
| #define APIC_ISR_0			0x100
 | |
| #define APIC_ISR_OFFSET			0x010
 | |
| 
 | |
| #define APIC_LVT_SIZE			0x010
 | |
| 
 | |
| #define APIC_TIMER_VECTOR		0x20UL
 | |
| #define APIC_SPURIOUS_VECTOR		0xFFUL
 | |
| 
 | |
| static uint32_t apic_bar;
 | |
| static int _apic_initialized;
 | |
| // TODO: Build a lookup table to avoid calculating it.
 | |
| static uint32_t ticks_per_ms;
 | |
| static volatile uint8_t timer_waiting;
 | |
| 
 | |
| enum APIC_CAPABILITY {
 | |
| 	DISABLED = 0,
 | |
| 	XACPI = 1 << 0,
 | |
| 	X2ACPI = 1 << 1
 | |
| };
 | |
| 
 | |
| int apic_initialized(void)
 | |
| {
 | |
| 	return _apic_initialized;
 | |
| }
 | |
| 
 | |
| static inline uint32_t apic_read32(uint32_t offset)
 | |
| {
 | |
| 	return read32((void *)(apic_bar + offset));
 | |
| }
 | |
| 
 | |
| static inline void apic_write32(uint32_t offset, uint32_t value)
 | |
| {
 | |
| 	write32((void *)(apic_bar + offset), value);
 | |
| }
 | |
| 
 | |
| uint8_t apic_id(void)
 | |
| {
 | |
| 	die_if(!apic_bar, "APIC is not initialized");
 | |
| 
 | |
| 	uint8_t id =
 | |
| 		(apic_read32(APIC_ID) & APIC_ID_MASK) >> APIC_ID_SHIFT;
 | |
| 
 | |
| 	return id;
 | |
| }
 | |
| 
 | |
| void apic_start_delay(unsigned int usec)
 | |
| {
 | |
| 	die_if(!ticks_per_ms, "apic_init_timer was not run.");
 | |
| 	die_if(timer_waiting, "timer already started.");
 | |
| 	die_if(!interrupts_enabled(), "Interrupts disabled.");
 | |
| 
 | |
| 	/* The order is important so we don't underflow */
 | |
| 	uint64_t ticks = usec * ticks_per_ms / USECS_PER_MSEC;
 | |
| 
 | |
| 	/* Not enough resolution */
 | |
| 	if (!ticks)
 | |
| 		return;
 | |
| 
 | |
| 	/* Disable interrupts so we don't get a race condition between
 | |
| 	 * starting the timer and the hlt instruction. */
 | |
| 	disable_interrupts();
 | |
| 
 | |
| 	timer_waiting = 1;
 | |
| 
 | |
| 	apic_write32(APIC_TIMER_INIT_COUNT, ticks);
 | |
| 	enable_interrupts();
 | |
| }
 | |
| 
 | |
| void apic_wait_delay(void)
 | |
| {
 | |
| 	/* Loop in case another interrupt has fired and resumed execution. */
 | |
| 	disable_interrupts();
 | |
| 	/* Note: when we test timer_waiting, interrupts are disabled by the line
 | |
| 	 * above and the cli below. */
 | |
| 	while (timer_waiting) {
 | |
| 		asm volatile(
 | |
| 			"sti\n\t"
 | |
| 			"hlt\n\t"
 | |
| 			/* Disable interrupts to prevent a race condition
 | |
| 			 * between checking timer_waiting and executing the hlt
 | |
| 			 * instruction again. */
 | |
| 			"cli\n\t");
 | |
| 	}
 | |
| 
 | |
| 	/* Leave hardware interrupts enabled. */
 | |
| 	enable_interrupts();
 | |
| }
 | |
| 
 | |
| void apic_delay(unsigned int usec)
 | |
| {
 | |
| 	apic_start_delay(usec);
 | |
| 	apic_wait_delay();
 | |
| }
 | |
| 
 | |
| static void timer_interrupt_handler(u8 vector)
 | |
| {
 | |
| 	timer_waiting = 0;
 | |
| 	apic_eoi(APIC_TIMER_VECTOR);
 | |
| }
 | |
| 
 | |
| static void suprious_interrupt_handler(u8 vector) {}
 | |
| 
 | |
| void apic_eoi(uint8_t vector)
 | |
| {
 | |
| 	die_if(!apic_bar, "APIC is not initialized");
 | |
| 
 | |
| 	/*
 | |
| 	 * Local and I/O APICs support 240 vectors (in the range of 16 to 255)
 | |
| 	 * as valid interrupts.
 | |
| 	 */
 | |
| 	if (vector <= 15)
 | |
| 		return;
 | |
| 
 | |
| 	/* Each bank handles 32 vectors */
 | |
| 	uint8_t bank = vector / 32;
 | |
| 
 | |
| 	uint32_t offset = APIC_ISR_0 + bank * APIC_ISR_OFFSET;
 | |
| 
 | |
| 	uint32_t mask = apic_read32(offset);
 | |
| 
 | |
| 	uint8_t shift = vector % 32;
 | |
| 
 | |
| 	if (mask & (1 << shift))
 | |
| 		apic_write32(APIC_EOI, 0);
 | |
| }
 | |
| 
 | |
| static enum APIC_CAPABILITY apic_capabilities(void)
 | |
| {
 | |
| 	uint32_t eax, ebx, ecx, edx;
 | |
| 
 | |
| 	cpuid(1, eax, ebx, ecx, edx);
 | |
| 
 | |
| 	enum APIC_CAPABILITY capabilities = DISABLED;
 | |
| 
 | |
| 	if (edx & CPUID_XAPIC_ENABLED_BIT)
 | |
| 		capabilities |= XACPI;
 | |
| 
 | |
| 	if (ecx & CPUID_XAPIC2_ENABLED_BIT)
 | |
| 		capabilities |= X2ACPI;
 | |
| 
 | |
| 	return capabilities;
 | |
| }
 | |
| 
 | |
| static uint8_t apic_max_lvt_entries(void)
 | |
| {
 | |
| 	die_if(!apic_bar, "APIC is not initialized");
 | |
| 
 | |
| 	uint32_t reg = apic_read32(APIC_VERSION);
 | |
| 	reg &= APIC_MAX_LVT_MASK;
 | |
| 	reg >>= APIC_MAX_LVT_SHIFT;
 | |
| 
 | |
| 	return (uint8_t)reg;
 | |
| }
 | |
| 
 | |
| static void apic_reset_all_lvts(void)
 | |
| {
 | |
| 	uint8_t max = apic_max_lvt_entries();
 | |
| 	for (int i = 0; i <= max; ++i) {
 | |
| 		uint32_t offset = APIC_LVT_TIMER + APIC_LVT_SIZE * i;
 | |
| 		apic_eoi(i);
 | |
| 		apic_write32(offset, APIC_MASKED_BIT);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void apic_set_task_priority(uint8_t priority)
 | |
| {
 | |
| 	die_if(!apic_bar, "APIC is not initialized");
 | |
| 
 | |
| 	uint32_t tpr = apic_read32(APIC_TASK_PRIORITY);
 | |
| 	tpr &= ~APIC_TASK_PRIORITY_MASK;
 | |
| 	tpr |= priority;
 | |
| 
 | |
| 	apic_write32(APIC_TASK_PRIORITY, priority);
 | |
| }
 | |
| 
 | |
| static void apic_init_timer(void)
 | |
| {
 | |
| 	die_if(!apic_bar, "APIC is not initialized");
 | |
| 
 | |
| 	apic_write32(APIC_LVT_TIMER, APIC_MASKED_BIT);
 | |
| 
 | |
| 	/* Divide the clock by 1. */
 | |
| 	apic_write32(APIC_TIMER_DIV_CFG, 0xB);
 | |
| 
 | |
| 	/* Calibrate the APIC timer */
 | |
| 	if (!ticks_per_ms) {
 | |
| 		/* Set APIC init counter to MAX and count for 1 ms */
 | |
| 		apic_write32(APIC_TIMER_INIT_COUNT, UINT32_MAX);
 | |
| 
 | |
| 		/* This is safe because apic_initialized() returns false so
 | |
| 		 * arch_ndelay() falls back to a busy loop. */
 | |
| 		mdelay(1);
 | |
| 
 | |
| 		ticks_per_ms =
 | |
| 			UINT32_MAX - apic_read32(APIC_TIMER_CUR_COUNT);
 | |
| 	}
 | |
| 
 | |
| 	/* Clear the count so we don't get any stale interrupts */
 | |
| 	apic_write32(APIC_TIMER_INIT_COUNT, 0);
 | |
| 
 | |
| 	/* Unmask the timer and set the vector. */
 | |
| 	apic_write32(APIC_LVT_TIMER, APIC_TIMER_VECTOR);
 | |
| }
 | |
| 
 | |
| static void apic_sw_disable(void)
 | |
| {
 | |
| 	uint32_t reg = apic_read32(APIC_SPURIOUS);
 | |
| 
 | |
| 	reg &= ~APIC_SW_ENABLED_BIT;
 | |
| 	printf("%s: writing %#x to %#x\n", __func__, reg, APIC_SPURIOUS);
 | |
| 
 | |
| 	apic_write32(APIC_SPURIOUS, reg);
 | |
| }
 | |
| 
 | |
| static void apic_sw_enable(void)
 | |
| {
 | |
| 	uint32_t reg = apic_read32(APIC_SPURIOUS);
 | |
| 	if (reg & APIC_SW_ENABLED_BIT)
 | |
| 		return;
 | |
| 
 | |
| 	reg |= APIC_SW_ENABLED_BIT;
 | |
| 
 | |
| 	apic_write32(APIC_SPURIOUS, reg);
 | |
| }
 | |
| 
 | |
| static void apic_setup_spurious(void)
 | |
| {
 | |
| 	uint32_t reg = apic_read32(APIC_SPURIOUS);
 | |
| 
 | |
| 	reg &= ~APIC_SPURIOUS_VECTOR_MASK;
 | |
| 
 | |
| 	reg |= APIC_SPURIOUS_VECTOR;
 | |
| 
 | |
| 	apic_write32(APIC_SPURIOUS, reg);
 | |
| }
 | |
| 
 | |
| void apic_init(void)
 | |
| {
 | |
| 	uint64_t apic_bar_reg;
 | |
| 
 | |
| 	printf("APIC Init Started\n");
 | |
| 
 | |
| 	die_if(apic_initialized(), "APIC already initialized");
 | |
| 	die_if(!(apic_capabilities() & XACPI), "APIC is not supported");
 | |
| 
 | |
| 	apic_bar_reg = _rdmsr(APIC_BASE_MSR);
 | |
| 	printf("apic_bar_reg is 0x%llx\n", apic_bar_reg);
 | |
| 
 | |
| 	die_if(!(apic_bar_reg & XAPIC_ENABLED_BIT), "APIC is not enabled");
 | |
| 	die_if(apic_bar_reg & X2APIC_ENABLED_BIT,
 | |
| 	       "APIC is configured in x2APIC mode which is not supported");
 | |
| 
 | |
| 	apic_bar = (uint32_t)(apic_bar_reg & APIC_BASE_MASK);
 | |
| 
 | |
| 	apic_sw_disable();
 | |
| 	apic_reset_all_lvts();
 | |
| 	apic_set_task_priority(0);
 | |
| 	apic_setup_spurious();
 | |
| 
 | |
| 	apic_sw_enable();
 | |
| 
 | |
| 	apic_init_timer();
 | |
| 
 | |
| 	set_interrupt_handler(APIC_TIMER_VECTOR, &timer_interrupt_handler);
 | |
| 	set_interrupt_handler(APIC_SPURIOUS_VECTOR,
 | |
| 			      &suprious_interrupt_handler);
 | |
| 
 | |
| 	_apic_initialized = 1;
 | |
| 
 | |
| 	printf("APIC Configured\n");
 | |
| }
 |