SDM[*1] says that if there are CPUs with APIC ID greater than 254, BIOS is to pass control to OS in x2APIC mode. Use the fact that QEMU passes in "etc/max-cpus" max possible "APIC ID + 1" to detect need for x2APIC mode. Also instead of CMOS_BIOS_SMP_COUNT which is limited to 256 CPUs use a new rom file "etc/boot-cpus" that QEMU supporting more than 256 CPUs will provide.
*1) SDM: Volume 3: EXTENDED XAPIC (X2APIC): Initialization by System Software
Signed-off-by: Igor Mammedov imammedo@redhat.com --- v2: * merge handle_x2apic() into apic_id_init() --- src/x86.h | 1 + src/fw/smp.c | 26 +++++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/src/x86.h b/src/x86.h index 53378e9..a770e6f 100644 --- a/src/x86.h +++ b/src/x86.h @@ -68,6 +68,7 @@ static inline void wbinvd(void) #define CPUID_MSR (1 << 5) #define CPUID_APIC (1 << 9) #define CPUID_MTRR (1 << 12) +#define CPUID_X2APIC (1 << 21) static inline void __cpuid(u32 index, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx) { asm("cpuid" diff --git a/src/fw/smp.c b/src/fw/smp.c index 2c5670c..352f52e 100644 --- a/src/fw/smp.c +++ b/src/fw/smp.c @@ -19,6 +19,9 @@ #define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360)
#define APIC_ENABLED 0x0100 +#define MSR_IA32_APIC_BASE 0x01B +#define MSR_LOCAL_APIC_ID 0x802 +#define MSR_IA32_APICBASE_EXTD (1ULL << 10) /* Enable x2APIC mode */
static struct { u32 index; u64 val; } smp_msr[32]; static u32 smp_msr_count; @@ -46,6 +49,7 @@ smp_write_msrs(void) }
u32 MaxCountCPUs; +static u16 boot_cpus_count; static u32 CountCPUs; // 256 bits for the found APIC IDs static u32 FoundAPICIDs[256/32]; @@ -58,13 +62,24 @@ int apic_id_is_present(u8 apic_id) static int apic_id_init(void) { + u32 apic_id; CountCPUs++;
- // Track found apic id for use in legacy internal bios tables u32 eax, ebx, ecx, cpuid_features; cpuid(1, &eax, &ebx, &ecx, &cpuid_features); - u8 apic_id = ebx>>24; - FoundAPICIDs[apic_id/32] |= 1 << (apic_id % 32); + apic_id = ebx>>24; + if (MaxCountCPUs < 256) { // xAPIC mode + // Track found apic id for use in legacy internal bios tables + FoundAPICIDs[apic_id/32] |= 1 << (apic_id % 32); + } else if (ecx & CPUID_X2APIC) { + // switch to x2APIC mode + u64 apic_base = rdmsr(MSR_IA32_APIC_BASE); + wrmsr(MSR_IA32_APIC_BASE, apic_base | MSR_IA32_APICBASE_EXTD); + apic_id = rdmsr(MSR_LOCAL_APIC_ID); + } else { + // x2APIC is masked by CPUID + apic_id = -1; + }
return apic_id; } @@ -132,8 +147,7 @@ smp_scan(void) apic_id_init();
// Wait for other CPUs to process the SIPI. - u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1; - while (cmos_smp_count != CountCPUs) + while (boot_cpus_count != CountCPUs) asm volatile( // Release lock and allow other processors to use the stack. " movl %%esp, %1\n" @@ -164,6 +178,8 @@ smp_setup(void) if (MaxCountCPUs < cmos_smp_count) MaxCountCPUs = cmos_smp_count;
+ boot_cpus_count = romfile_loadint("etc/boot-cpus", cmos_smp_count); + smp_scan(); }