*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:
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
|
||||||
|
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
|
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,20 +2,55 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
|
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);
|
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;
|
||||||
|
|
Loading…
Reference in New Issue