HVM guests with more than 128 vCPUs require APIC IDs above 255 (since APIC ID = vcpu_id * 2), which exceeds the 8-bit xAPIC limit. Currently, CPUID leaf 0xB returns EBX=0, making firmware (OVMF/EDK2) fall back to CPUID leaf 1 for APIC ID discovery, which is limited to 8 bits and cannot distinguish vCPUs with APIC IDs greater than 256. To address this, implement proper CPUID leaf 0xB x2APIC topology enumeration. Present all vCPUs as cores in a single package with correct shift values based on the maximum APIC ID, and non-zero EBX so that firmware uses the 32-bit x2APIC ID from EDX. Moreover, set CPUID leaf 1 EBX[23:16] (max addressable logical processor IDs) to the appropriate power-of-2 value, but capped at 255.
Signed-off-by: Julian Vetter <[email protected]> --- xen/arch/x86/cpuid.c | 54 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c index ceac37b3ae..fb17c71d74 100644 --- a/xen/arch/x86/cpuid.c +++ b/xen/arch/x86/cpuid.c @@ -284,10 +284,26 @@ void guest_cpuid(const struct vcpu *v, uint32_t leaf, const struct cpu_user_regs *regs; case 0x1: - /* TODO: Rework topology logic. */ res->b &= 0x00ffffffu; if ( is_hvm_domain(d) ) - res->b |= (v->vcpu_id * 2) << 24; + { + unsigned int max_apic_id, max_lp; + + /* + * EBX[23:16] = Maximum number of addressable IDs for logical + * processors in a physical package. Must be large enough to + * accommodate all vCPU APIC IDs. Round up to next power of 2. + * + * With APIC ID = vcpu_id * 2, max APIC ID = (max_vcpus - 1) * 2. + * We need max_lp to be greater than max_apic_id for proper + * enumeration. + */ + max_apic_id = (d->max_vcpus - 1) * 2; + max_lp = min(1u << fls(max_apic_id), 255u); + + res->b = (res->b & 0xff00ffffu) | (max_lp << 16); + res->b |= (uint8_t)(v->vcpu_id * 2) << 24; + } /* TODO: Rework vPMU control in terms of toolstack choices. */ if ( vpmu_available(v) && @@ -463,11 +479,41 @@ void guest_cpuid(const struct vcpu *v, uint32_t leaf, * coupled with x2apic, and we offer an x2apic-capable APIC emulation * to guests on AMD hardware as well. * - * TODO: Rework topology logic. + * Provide a simple topology where all vCPUs are cores in a single + * package (no SMT). This ensures EBX is non-zero so that software + * (like EDK2/OVMF) uses the 32-bit x2APIC ID from EDX. */ if ( p->basic.x2apic ) { - *(uint8_t *)&res->c = subleaf; + unsigned int max_vcpus = d->max_vcpus; + unsigned int max_apic_id = (max_vcpus - 1) * 2; + unsigned int shift; + + /* Calculate shift value for Core level topology. */ + shift = fls(max_apic_id); + + switch ( subleaf ) + { + /* SMT level - no hyperthreading, 1 thread per core */ + case 0x0: + res->a = 0; /* No shift (1 thread per core) */ + res->b = 1; /* 1 logical processor at this level */ + res->c = 0x100 | 0; /* Level type 1 (SMT), level number 0 */ + break; + + /* Core level - all vCPUs are cores in one package */ + case 0x1: + res->a = shift; /* Bits to shift to get package ID */ + res->b = max_vcpus; /* Number of logical processors */ + res->c = 0x200 | 1; /* Level type 2 (Core), level number 1 */ + break; + + default: /* Invalid level */ + res->a = 0; + res->b = 0; + res->c = subleaf; /* Level number only, type 0 (invalid) */ + break; + } /* Fix the x2APIC identifier. */ res->d = v->vcpu_id * 2; -- 2.51.0 -- Julian Vetter | Vates Hypervisor & Kernel Developer XCP-ng & Xen Orchestra - Vates solutions web: https://vates.tech
