x86: correct CPUID output for out of bounds input Another place where we should try to behave like real hardware; see the code comments. Signed-off-by: Jan Beulich --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -3358,6 +3358,31 @@ void hvm_cpuid(unsigned int input, unsig if ( !edx ) edx = &dummy; + if ( input & 0xffff ) + { + /* + * Requests beyond the highest supported leaf within a group return + * zero on AMD and the highest basic leaf output on others. + */ + unsigned int lvl; + + hvm_cpuid(input & 0xffff0000, &lvl, NULL, NULL, NULL); + if ( ((lvl ^ input) >> 16) || input > lvl ) + { + if ( d->arch.x86_vendor == X86_VENDOR_AMD ) + { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + return; + } + if ( input >> 16 ) + hvm_cpuid(0, &lvl, NULL, NULL, NULL); + input = lvl; + } + } + if ( cpuid_viridian_leaves(input, eax, ebx, ecx, edx) ) return; --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -944,7 +944,40 @@ void pv_cpuid(struct cpu_user_regs *regs struct vcpu *curr = current; struct domain *currd = curr->domain; - leaf = a = regs->eax; + leaf = regs->eax; + + if ( leaf & 0xffff ) + { + /* + * Requests beyond the highest supported leaf within a group return + * zero on AMD and the highest basic leaf output on others. + */ + if ( !is_control_domain(currd) && !is_hardware_domain(currd) ) + domain_cpuid(currd, leaf & 0xffff0000, 0, &a, &b, &c, &d); + else + a = cpuid_eax(leaf & 0xffff0000); + if ( ((a ^ leaf) >> 16) || leaf > a ) + { + if ( currd->arch.x86_vendor == X86_VENDOR_AMD ) + { + regs->eax = 0; + regs->ebx = 0; + regs->ecx = 0; + regs->edx = 0; + return; + } + if ( leaf >> 16 ) + { + if ( !is_control_domain(currd) && !is_hardware_domain(currd) ) + domain_cpuid(currd, 0, 0, &a, &b, &c, &d); + else + a = cpuid_eax(0); + } + leaf = a; + } + } + + a = regs->eax; b = regs->ebx; subleaf = c = regs->ecx; d = regs->edx;