From 26ab9bfeb53a5d73ff4fdb01c8a15417a2f76876 Mon Sep 17 00:00:00 2001 From: Wonkyu Kim Date: Mon, 22 Mar 2021 19:59:18 -0700 Subject: [PATCH] *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 Change-Id: I0bb729b0521fb9dc38b7981014755daeaf9ca817 Reviewed-on: https://review.coreboot.org/c/coreboot/+/51723 Reviewed-by: Ravishankar Sarawadi Reviewed-by: Jamie Ryu Reviewed-by: Patrick Georgi Tested-by: build bot (Jenkins) --- src/arch/x86/cpu.c | 11 ------- src/cpu/x86/lapic/lapic.c | 2 +- src/cpu/x86/mp_init.c | 27 ++++++++++++++++ src/cpu/x86/smm/smm_stub.S | 32 ++++++++++++++----- src/include/cpu/x86/lapic.h | 56 +++++++++++++++++++++++++++++++-- src/include/cpu/x86/lapic_def.h | 4 +++ src/include/cpu/x86/smm.h | 2 +- 7 files changed, 110 insertions(+), 24 deletions(-) diff --git a/src/arch/x86/cpu.c b/src/arch/x86/cpu.c index 3ef878f517..07bd7b5ff2 100644 --- a/src/arch/x86/cpu.c +++ b/src/arch/x86/cpu.c @@ -213,17 +213,6 @@ static void set_cpu_ops(struct device *cpu) /* Keep track of default APIC ids for SMM. */ 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 */ void cpu_add_map_entry(unsigned int index) { diff --git a/src/cpu/x86/lapic/lapic.c b/src/cpu/x86/lapic/lapic.c index 988d664c94..a5b4cd5047 100644 --- a/src/cpu/x86/lapic/lapic.c +++ b/src/cpu/x86/lapic/lapic.c @@ -47,6 +47,6 @@ void lapic_virtual_wire_mode_init(void) 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"); } diff --git a/src/cpu/x86/mp_init.c b/src/cpu/x86/mp_init.c index 94a8fd8d8b..76f2650330 100644 --- a/src/cpu/x86/mp_init.c +++ b/src/cpu/x86/mp_init.c @@ -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); + 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)) { printk(BIOS_DEBUG, "Waiting for ICR not to be busy..."); if (apic_wait_timeout(1000 /* 1 ms */, 50)) { @@ -653,6 +675,11 @@ static void mp_initialize_cpu(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)) { printk(BIOS_DEBUG, "Waiting for ICR not to be busy..."); if (apic_wait_timeout(1000 /* 1 ms */, 50)) { diff --git a/src/cpu/x86/smm/smm_stub.S b/src/cpu/x86/smm/smm_stub.S index 7c09e04108..f479d62536 100644 --- a/src/cpu/x86/smm/smm_stub.S +++ b/src/cpu/x86/smm/smm_stub.S @@ -11,6 +11,7 @@ #include #include +#include .code32 .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 * into the table. */ 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 */ start32_offset: .long smm_trampoline32 - _start @@ -97,16 +98,31 @@ smm_trampoline32: /* 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 - * 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. */ - mov $1, %eax - cpuid - bswap %ebx /* Default APIC id in bl. */ - mov $(apic_to_cpu_num), %eax - xor %ecx, %ecx +apic_id: + mov $LAPIC_BASE_MSR, %ecx + rdmsr + andl $LAPIC_BASE_MSR_X2APIC_MODE, %eax + 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: - cmp (%eax, %ecx, 1), %bl + cmp (%ebx, %ecx, 2), %ax je 1f inc %ecx cmp $CONFIG_MAX_CPUS, %ecx diff --git a/src/include/cpu/x86/lapic.h b/src/include/cpu/x86/lapic.h index f4291ab35b..e2ca297659 100644 --- a/src/include/cpu/x86/lapic.h +++ b/src/include/cpu/x86/lapic.h @@ -2,19 +2,54 @@ #define CPU_X86_LAPIC_H #include +#include #include #include #include #include +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) { - 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) { - 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) @@ -41,9 +76,24 @@ static inline void disable_lapic(void) 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) { - 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) diff --git a/src/include/cpu/x86/lapic_def.h b/src/include/cpu/x86/lapic_def.h index 9da89ee873..5b25e5a968 100644 --- a/src/include/cpu/x86/lapic_def.h +++ b/src/include/cpu/x86/lapic_def.h @@ -3,6 +3,7 @@ #define LAPIC_BASE_MSR 0x1B #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_ADDR_MASK 0xFFFFF000 @@ -94,4 +95,7 @@ #define LAPIC_TDR_DIV_64 0x9 #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 */ diff --git a/src/include/cpu/x86/smm.h b/src/include/cpu/x86/smm.h index b42f3faef4..c42eb8cf7f 100644 --- a/src/include/cpu/x86/smm.h +++ b/src/include/cpu/x86/smm.h @@ -85,7 +85,7 @@ struct smm_stub_params { * 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 * 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 */ u32 start32_offset; } __packed;