*x86: Support x2apic mode
Implement x2apic mode as existing code only supports apic mode. Use info from LAPIC_BASE_MSR (LAPIC_BASE_MSR_X2APIC_MODE) to check if apic mode or x2apic mode and implement x2apic mode according to x2apic specfication. Reference: https://software.intel.com/content/www/us/en/develop/download/intel-64-architecture-x2apic-specification.html BUG=None BRANCH=None TEST=boot to OS and check apic mode cat /proc/cpuinfo | grep "apicid" ex) can see apicid bigger than 255 apicid : 256 apicid : 260 Signed-off-by: Wonkyu Kim <wonkyu.kim@intel.com> Change-Id: I0bb729b0521fb9dc38b7981014755daeaf9ca817 Reviewed-on: https://review.coreboot.org/c/coreboot/+/51723 Reviewed-by: Ravishankar Sarawadi <ravishankar.sarawadi@intel.com> Reviewed-by: Jamie Ryu <jamie.m.ryu@intel.com> Reviewed-by: Patrick Georgi <pgeorgi@google.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
		
				
					committed by
					
						 Patrick Georgi
						Patrick Georgi
					
				
			
			
				
	
			
			
			
						parent
						
							5c9bacca32
						
					
				
				
					commit
					26ab9bfeb5
				
			| @@ -213,17 +213,6 @@ static void set_cpu_ops(struct device *cpu) | |||||||
| /* Keep track of default APIC ids for SMM. */ | /* Keep track of default APIC ids for SMM. */ | ||||||
| static int cpus_default_apic_id[CONFIG_MAX_CPUS]; | static int cpus_default_apic_id[CONFIG_MAX_CPUS]; | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * When CPUID executes with EAX set to 1, additional processor identification |  | ||||||
|  * information is returned to EBX register: |  | ||||||
|  * Default APIC ID: EBX[31-24] - this number is the 8 bit ID that is assigned |  | ||||||
|  * to the local APIC on the processor during power on. |  | ||||||
|  */ |  | ||||||
| static int initial_lapicid(void) |  | ||||||
| { |  | ||||||
| 	return cpuid_ebx(1) >> 24; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Function to keep track of cpu default apic_id */ | /* Function to keep track of cpu default apic_id */ | ||||||
| void cpu_add_map_entry(unsigned int index) | void cpu_add_map_entry(unsigned int index) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -47,6 +47,6 @@ void lapic_virtual_wire_mode_init(void) | |||||||
| 			LAPIC_DELIVERY_MODE_NMI) | 			LAPIC_DELIVERY_MODE_NMI) | ||||||
| 		); | 		); | ||||||
|  |  | ||||||
| 	printk(BIOS_DEBUG, " apic_id: 0x%02x ", lapicid()); | 	printk(BIOS_DEBUG, " apic_id: 0x%x ", lapicid()); | ||||||
| 	printk(BIOS_INFO, "done.\n"); | 	printk(BIOS_INFO, "done.\n"); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -435,6 +435,28 @@ static int start_aps(struct bus *cpu_bus, int ap_count, atomic_t *num_aps) | |||||||
|  |  | ||||||
| 	printk(BIOS_DEBUG, "Attempting to start %d APs\n", ap_count); | 	printk(BIOS_DEBUG, "Attempting to start %d APs\n", ap_count); | ||||||
|  |  | ||||||
|  | 	if (is_x2apic_mode()) { | ||||||
|  | 		x2apic_send_ipi(LAPIC_DM_INIT | LAPIC_INT_LEVELTRIG | | ||||||
|  | 			LAPIC_INT_ASSERT | LAPIC_DEST_ALLBUT, 0); | ||||||
|  | 		mdelay(10); | ||||||
|  | 		x2apic_send_ipi(LAPIC_DM_STARTUP | LAPIC_INT_LEVELTRIG | | ||||||
|  | 			LAPIC_DEST_ALLBUT | sipi_vector, 0); | ||||||
|  |  | ||||||
|  | 		/* Wait for CPUs to check in up to 200 us. */ | ||||||
|  | 		wait_for_aps(num_aps, ap_count, 200 /* us */, 15 /* us */); | ||||||
|  |  | ||||||
|  | 		x2apic_send_ipi(LAPIC_DM_STARTUP | LAPIC_INT_LEVELTRIG | | ||||||
|  | 			LAPIC_DEST_ALLBUT | sipi_vector, 0); | ||||||
|  |  | ||||||
|  | 		/* Wait for CPUs to check in. */ | ||||||
|  | 		if (wait_for_aps(num_aps, ap_count, 100000 /* 100 ms */, 50 /* us */)) { | ||||||
|  | 			printk(BIOS_ERR, "Not all APs checked in: %d/%d.\n", | ||||||
|  | 			       atomic_read(num_aps), ap_count); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { | 	if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { | ||||||
| 		printk(BIOS_DEBUG, "Waiting for ICR not to be busy..."); | 		printk(BIOS_DEBUG, "Waiting for ICR not to be busy..."); | ||||||
| 		if (apic_wait_timeout(1000 /* 1 ms */, 50)) { | 		if (apic_wait_timeout(1000 /* 1 ms */, 50)) { | ||||||
| @@ -653,6 +675,11 @@ static void mp_initialize_cpu(void) | |||||||
|  |  | ||||||
| void smm_initiate_relocation_parallel(void) | void smm_initiate_relocation_parallel(void) | ||||||
| { | { | ||||||
|  | 	if (is_x2apic_mode()) { | ||||||
|  | 		x2apic_send_ipi(LAPIC_DM_SMI | LAPIC_INT_LEVELTRIG, lapicid()); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { | 	if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) { | ||||||
| 		printk(BIOS_DEBUG, "Waiting for ICR not to be busy..."); | 		printk(BIOS_DEBUG, "Waiting for ICR not to be busy..."); | ||||||
| 		if (apic_wait_timeout(1000 /* 1 ms */, 50)) { | 		if (apic_wait_timeout(1000 /* 1 ms */, 50)) { | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ | |||||||
|  |  | ||||||
| #include <cpu/x86/cr.h> | #include <cpu/x86/cr.h> | ||||||
| #include <cpu/x86/msr.h> | #include <cpu/x86/msr.h> | ||||||
|  | #include <cpu/x86/lapic_def.h> | ||||||
|  |  | ||||||
| .code32 | .code32 | ||||||
| .section ".module_parameters", "aw", @progbits | .section ".module_parameters", "aw", @progbits | ||||||
| @@ -29,7 +30,7 @@ fxsave_area_size: | |||||||
|  * APIC id is found at the given index, the contiguous CPU number is index |  * APIC id is found at the given index, the contiguous CPU number is index | ||||||
|  * into the table. */ |  * into the table. */ | ||||||
| apic_to_cpu_num: | apic_to_cpu_num: | ||||||
| .fill CONFIG_MAX_CPUS,1,0xff | .fill CONFIG_MAX_CPUS,2,0xffff | ||||||
| /* allows the STM to bring up SMM in 32-bit mode */ | /* allows the STM to bring up SMM in 32-bit mode */ | ||||||
| start32_offset: | start32_offset: | ||||||
| .long smm_trampoline32 - _start | .long smm_trampoline32 - _start | ||||||
| @@ -97,16 +98,31 @@ smm_trampoline32: | |||||||
|  |  | ||||||
| 	/* The CPU number is calculated by reading the initial APIC id. Since | 	/* The CPU number is calculated by reading the initial APIC id. Since | ||||||
| 	 * the OS can maniuplate the APIC id use the non-changing cpuid result | 	 * the OS can maniuplate the APIC id use the non-changing cpuid result | ||||||
| 	 * for APIC id (ebx[31:24]). A table is used to handle a discontiguous | 	 * for APIC id (ax). A table is used to handle a discontiguous | ||||||
| 	 * APIC id space.  */ | 	 * APIC id space.  */ | ||||||
| 	mov	$1, %eax | apic_id: | ||||||
| 	cpuid |         mov $LAPIC_BASE_MSR, %ecx | ||||||
| 	bswap	%ebx	/* Default APIC id in bl. */ |         rdmsr | ||||||
| 	mov	$(apic_to_cpu_num), %eax |         andl $LAPIC_BASE_MSR_X2APIC_MODE, %eax | ||||||
| 	xor	%ecx, %ecx |         jz xapic | ||||||
|  |  | ||||||
|  | x2apic: | ||||||
|  |         mov $X2APIC_LAPIC_ID, %ecx | ||||||
|  |         rdmsr | ||||||
|  |         jmp apicid_end | ||||||
|  |  | ||||||
|  | xapic: | ||||||
|  |         movl    $(LOCAL_APIC_ADDR | LAPIC_ID), %esi | ||||||
|  |         movl    (%esi), %eax | ||||||
|  |         shr     $24, %eax | ||||||
|  |  | ||||||
|  | apicid_end: | ||||||
|  |  | ||||||
|  |         mov     $(apic_to_cpu_num), %ebx | ||||||
|  |         xor     %ecx, %ecx | ||||||
|  |  | ||||||
| 1: | 1: | ||||||
| 	cmp	(%eax, %ecx, 1), %bl | 	cmp	(%ebx, %ecx, 2), %ax | ||||||
| 	je	1f | 	je	1f | ||||||
| 	inc	%ecx | 	inc	%ecx | ||||||
| 	cmp	$CONFIG_MAX_CPUS, %ecx | 	cmp	$CONFIG_MAX_CPUS, %ecx | ||||||
|   | |||||||
| @@ -2,19 +2,54 @@ | |||||||
| #define CPU_X86_LAPIC_H | #define CPU_X86_LAPIC_H | ||||||
|  |  | ||||||
| #include <arch/mmio.h> | #include <arch/mmio.h> | ||||||
|  | #include <arch/cpu.h> | ||||||
| #include <cpu/x86/lapic_def.h> | #include <cpu/x86/lapic_def.h> | ||||||
| #include <cpu/x86/msr.h> | #include <cpu/x86/msr.h> | ||||||
| #include <halt.h> | #include <halt.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  |  | ||||||
|  | static inline bool is_x2apic_mode(void) | ||||||
|  | { | ||||||
|  | 	msr_t msr; | ||||||
|  | 	msr = rdmsr(LAPIC_BASE_MSR); | ||||||
|  | 	return (msr.lo & LAPIC_BASE_MSR_X2APIC_MODE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void x2apic_send_ipi(uint32_t icrlow, uint32_t apicid) | ||||||
|  | { | ||||||
|  | 	msr_t icr; | ||||||
|  | 	icr.hi = apicid; | ||||||
|  | 	icr.lo = icrlow; | ||||||
|  | 	wrmsr(X2APIC_MSR_ICR_ADDRESS, icr); | ||||||
|  | } | ||||||
|  |  | ||||||
| static __always_inline uint32_t lapic_read(unsigned int reg) | static __always_inline uint32_t lapic_read(unsigned int reg) | ||||||
| { | { | ||||||
| 	return read32((volatile void *)(uintptr_t)(LAPIC_DEFAULT_BASE + reg)); | 	uint32_t value, index; | ||||||
|  | 	msr_t msr; | ||||||
|  |  | ||||||
|  | 	if (is_x2apic_mode()) { | ||||||
|  | 		index = X2APIC_MSR_BASE_ADDRESS + (uint32_t)(reg >> 4); | ||||||
|  | 		msr = rdmsr(index); | ||||||
|  | 		value = msr.lo; | ||||||
|  | 	} else { | ||||||
|  | 		value = read32((volatile void *)(uintptr_t)(LAPIC_DEFAULT_BASE + reg)); | ||||||
|  | 	} | ||||||
|  | 	return value; | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline void lapic_write(unsigned int reg, uint32_t v) | static __always_inline void lapic_write(unsigned int reg, uint32_t v) | ||||||
| { | { | ||||||
| 	write32((volatile void *)(uintptr_t)(LAPIC_DEFAULT_BASE + reg), v); | 	msr_t msr; | ||||||
|  | 	uint32_t index; | ||||||
|  | 	if (is_x2apic_mode()) { | ||||||
|  | 		index = X2APIC_MSR_BASE_ADDRESS + (uint32_t)(reg >> 4); | ||||||
|  | 		msr.hi = 0x0; | ||||||
|  | 		msr.lo = v; | ||||||
|  | 		wrmsr(index, msr); | ||||||
|  | 	} else { | ||||||
|  | 		write32((volatile void *)(uintptr_t)(LAPIC_DEFAULT_BASE + reg), v); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| static __always_inline void lapic_wait_icr_idle(void) | static __always_inline void lapic_wait_icr_idle(void) | ||||||
| @@ -41,9 +76,24 @@ static inline void disable_lapic(void) | |||||||
| 	wrmsr(LAPIC_BASE_MSR, msr); | 	wrmsr(LAPIC_BASE_MSR, msr); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static __always_inline unsigned int initial_lapicid(void) | ||||||
|  | { | ||||||
|  | 	uint32_t lapicid; | ||||||
|  | 	if (is_x2apic_mode()) | ||||||
|  | 		lapicid = lapic_read(LAPIC_ID); | ||||||
|  | 	else | ||||||
|  | 		lapicid = cpuid_ebx(1) >> 24; | ||||||
|  | 	return lapicid; | ||||||
|  | } | ||||||
|  |  | ||||||
| static __always_inline unsigned int lapicid(void) | static __always_inline unsigned int lapicid(void) | ||||||
| { | { | ||||||
| 	return lapic_read(LAPIC_ID) >> 24; | 	uint32_t lapicid = lapic_read(LAPIC_ID); | ||||||
|  |  | ||||||
|  | 	/* check x2apic mode and return accordingly */ | ||||||
|  | 	if (!is_x2apic_mode()) | ||||||
|  | 		lapicid >>= 24; | ||||||
|  | 	return lapicid; | ||||||
| } | } | ||||||
|  |  | ||||||
| #if !CONFIG(AP_IN_SIPI_WAIT) | #if !CONFIG(AP_IN_SIPI_WAIT) | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
|  |  | ||||||
| #define LAPIC_BASE_MSR 0x1B | #define LAPIC_BASE_MSR 0x1B | ||||||
| #define LAPIC_BASE_MSR_BOOTSTRAP_PROCESSOR (1 << 8) | #define LAPIC_BASE_MSR_BOOTSTRAP_PROCESSOR (1 << 8) | ||||||
|  | #define LAPIC_BASE_MSR_X2APIC_MODE (1 << 10) | ||||||
| #define LAPIC_BASE_MSR_ENABLE (1 << 11) | #define LAPIC_BASE_MSR_ENABLE (1 << 11) | ||||||
| #define LAPIC_BASE_MSR_ADDR_MASK 0xFFFFF000 | #define LAPIC_BASE_MSR_ADDR_MASK 0xFFFFF000 | ||||||
|  |  | ||||||
| @@ -94,4 +95,7 @@ | |||||||
| #define		LAPIC_TDR_DIV_64		0x9 | #define		LAPIC_TDR_DIV_64		0x9 | ||||||
| #define		LAPIC_TDR_DIV_128	0xA | #define		LAPIC_TDR_DIV_128	0xA | ||||||
|  |  | ||||||
|  | #define X2APIC_MSR_BASE_ADDRESS	0x800 | ||||||
|  | #define X2APIC_LAPIC_ID		(X2APIC_MSR_BASE_ADDRESS | (LAPIC_ID >> 4)) | ||||||
|  | #define	X2APIC_MSR_ICR_ADDRESS	0x830 | ||||||
| #endif /* CPU_X86_LAPIC_DEF_H */ | #endif /* CPU_X86_LAPIC_DEF_H */ | ||||||
|   | |||||||
| @@ -85,7 +85,7 @@ struct smm_stub_params { | |||||||
| 	 * initializes this array with a 1:1 mapping. If the APIC ids are not | 	 * initializes this array with a 1:1 mapping. If the APIC ids are not | ||||||
| 	 * contiguous like the 1:1 mapping it is up to the caller of the stub | 	 * contiguous like the 1:1 mapping it is up to the caller of the stub | ||||||
| 	 * loader to adjust this mapping. */ | 	 * loader to adjust this mapping. */ | ||||||
| 	u8 apic_id_to_cpu[CONFIG_MAX_CPUS]; | 	u16 apic_id_to_cpu[CONFIG_MAX_CPUS]; | ||||||
| 	/* STM's 32bit entry into SMI handler */ | 	/* STM's 32bit entry into SMI handler */ | ||||||
| 	u32 start32_offset; | 	u32 start32_offset; | ||||||
| } __packed; | } __packed; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user