// SPDX-License-Identifier: GPL-2.0-only
/*
* Kernel-based Virtual Machine driver for Linux
* cpuid support routines
*
* derived from arch/x86/kvm/x86.c
*
* Copyright 2011 Red Hat, Inc. and/or its affiliates.
* Copyright IBM Corporation, 2008
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kvm_host.h>
#include "linux/lockdep.h"
#include <linux/export.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/sched/stat.h>
#include <asm/processor.h>
#include <asm/user.h>
#include <asm/fpu/xstate.h>
#include <asm/sgx.h>
#include <asm/cpuid.h>
#include "cpuid.h"
#include "lapic.h"
#include "mmu.h"
#include "trace.h"
#include "pmu.h"
#include "xen.h"
/*
* Unlike "struct cpuinfo_x86.x86_capability", kvm_cpu_caps doesn't need to be
* aligned to sizeof(unsigned long) because it's not accessed via bitops.
*/
u32 kvm_cpu_caps[NR_KVM_CPU_CAPS] __read_mostly;
EXPORT_SYMBOL_GPL(kvm_cpu_caps);
u32 xstate_required_size(u64 xstate_bv, bool compacted)
{
int feature_bit = 0;
u32 ret = XSAVE_HDR_SIZE + XSAVE_HDR_OFFSET;
xstate_bv &= XFEATURE_MASK_EXTEND;
while (xstate_bv) {
if (xstate_bv & 0x1) {
u32 eax, ebx, ecx, edx, offset;
cpuid_count(0xD, feature_bit, &eax, &ebx, &ecx, &edx);
/* ECX[1]: 64B alignment in compacted form */
if (compacted)
offset = (ecx & 0x2) ? ALIGN(ret, 64) : ret;
else
offset = ebx;
ret = max(ret, offset + eax);
}
xstate_bv >>= 1;
feature_bit++;
}
return ret;
}
#define F feature_bit
/* Scattered Flag - For features that are scattered by cpufeatures.h. */
#define SF(name) \
({ \
BUILD_BUG_ON(X86_FEATURE_##name >= MAX_CPU_FEATURES); \
(boot_cpu_has(X86_FEATURE_##name) ? F(name) : 0); \
})
/*
* Magic value used by KVM when querying userspace-provided CPUID entries and
* doesn't care about the CPIUD index because the index of the function in
* question is not significant. Note, this magic value must have at least one
* bit set in bits[63:32] and must be consumed as a u64 by cpuid_entry2_find()
* to avoid false positives when processing guest CPUID input.
*/
#define KVM_CPUID_INDEX_NOT_SIGNIFICANT -1ull
static inline struct kvm_cpuid_entry2 *cpuid_entry2_find(
struct kvm_cpuid_entry2 *entries, int nent, u32 function, u64 index)
{
struct kvm_cpuid_entry2 *e;
int i;
/*
* KVM has a semi-arbitrary rule that querying the guest's CPUID model
* with IRQs disabled is disallowed. The CPUID model can legitimately
* have over one hundred entries, i.e. the lookup is slow, and IRQs are
* typically disabled in KVM only when KVM is in a performance critical
* path, e.g. the core VM-Enter/VM-Exit run loop. Nothing will break
* if this rule is violated, this assertion is purely to flag potential
* performance issues. If this fires, consider moving the lookup out
* of the hotpath, e.g. by caching information during CPUID updates.
*/
lockdep_assert_irqs_enabled();
for (i = 0; i < nent; i++) {
e = &entries[i];
if (e->function != function)
continue;
/*
* If the index isn't significant, use the first entry with a
* matching function. It's userspace's responsibility to not
* provide "duplicate" entries in all cases.
*/
if (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) || e->index == index)
return e;
|