Fix a bug in KVM_SET_CPUID{2,} where KVM looks at the wrong CPUID entries (old
vs. new) and ultimately neglects to clear PV_UNHALT from vCPUs with HLT-exiting disabled. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEKTobbabEP7vbhhN9OlYIJqCjN/0FAmX4yVUACgkQOlYIJqCj N/0BpQ/9Flr0fL9150AUb+yZofb0JTbVRgSNfvY12hr9vIp88KY/ryOw8OzlJy0v veXD3IqSxkClTp+i2ocRJi1zBVo3ww7s6VwWJwY9SkDEfIYyqRWu+Es/mHNZ/0HM BvMcwwyGDtHdZi2BHnztbfLzhh+AQvYm57RKBGyjTx76kdaYiiHwvHRIlJgYTC6q w4YBvInIys8Fj5dGKp1I72UvA0F+db9QOC4vxW/x/OAEcbMi6mMkEzdr3ftK5U/q 8K4h1OvE3PfMXR3S0HDoqnGCenGX/93REhduOO36SfP5gupN0TzkgQwqIAWpqvER zQFdJ3+/6H07q83tlhpThggD7qgqQeg2a/DhFnj6AK5ima44zg+MrW3v14D42hY1 GbBXz9CLWsnzm0ieZqaOhJW1Gx57a9AoXr5YZ7NGQxJ2fEaG7zSAzLMKP28+6PDT 1OXlozPVAMYNL8xZmkA5+QIoBMRUQVaRhXmoW1wr7NqUqHcm6ILQl6DOIM4sGGXL TPMGjBkZwLVv0J5rtcSIIPoXChcB5V1DqqMyIuu+arAzoR8ulcETdqb6kJvyP1HT GQHtinqq/nc0cpaNhkmB4WkLg7fvMlvz5YNPQAEs+2ZZGTiwAo05jMv1Gpky3yI6 XXQf+bhT7ghJdTJy0QKmUGw3YCDjrYXzfYfPEwwewVqAbIlrjFM= =o7dM -----END PGP SIGNATURE----- Merge tag 'kvm-x86-pvunhalt-6.9' of https://github.com/kvm-x86/linux into HEAD Fix a bug in KVM_SET_CPUID{2,} where KVM looks at the wrong CPUID entries (old vs. new) and ultimately neglects to clear PV_UNHALT from vCPUs with HLT-exiting disabled.
This commit is contained in:
commit
0d1756482e
@ -189,15 +189,15 @@ static int kvm_cpuid_check_equal(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcpu,
|
||||
const char *sig)
|
||||
static struct kvm_hypervisor_cpuid __kvm_get_hypervisor_cpuid(struct kvm_cpuid_entry2 *entries,
|
||||
int nent, const char *sig)
|
||||
{
|
||||
struct kvm_hypervisor_cpuid cpuid = {};
|
||||
struct kvm_cpuid_entry2 *entry;
|
||||
u32 base;
|
||||
|
||||
for_each_possible_hypervisor_cpuid_base(base) {
|
||||
entry = kvm_find_cpuid_entry(vcpu, base);
|
||||
entry = cpuid_entry2_find(entries, nent, base, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
|
||||
|
||||
if (entry) {
|
||||
u32 signature[3];
|
||||
@ -217,22 +217,29 @@ static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcp
|
||||
return cpuid;
|
||||
}
|
||||
|
||||
static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu,
|
||||
struct kvm_cpuid_entry2 *entries, int nent)
|
||||
static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcpu,
|
||||
const char *sig)
|
||||
{
|
||||
return __kvm_get_hypervisor_cpuid(vcpu->arch.cpuid_entries,
|
||||
vcpu->arch.cpuid_nent, sig);
|
||||
}
|
||||
|
||||
static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_cpuid_entry2 *entries,
|
||||
int nent, u32 kvm_cpuid_base)
|
||||
{
|
||||
return cpuid_entry2_find(entries, nent, kvm_cpuid_base | KVM_CPUID_FEATURES,
|
||||
KVM_CPUID_INDEX_NOT_SIGNIFICANT);
|
||||
}
|
||||
|
||||
static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 base = vcpu->arch.kvm_cpuid.base;
|
||||
|
||||
if (!base)
|
||||
return NULL;
|
||||
|
||||
return cpuid_entry2_find(entries, nent, base | KVM_CPUID_FEATURES,
|
||||
KVM_CPUID_INDEX_NOT_SIGNIFICANT);
|
||||
}
|
||||
|
||||
static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return __kvm_find_kvm_cpuid_features(vcpu, vcpu->arch.cpuid_entries,
|
||||
vcpu->arch.cpuid_nent);
|
||||
return __kvm_find_kvm_cpuid_features(vcpu->arch.cpuid_entries,
|
||||
vcpu->arch.cpuid_nent, base);
|
||||
}
|
||||
|
||||
void kvm_update_pv_runtime(struct kvm_vcpu *vcpu)
|
||||
@ -266,6 +273,7 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e
|
||||
int nent)
|
||||
{
|
||||
struct kvm_cpuid_entry2 *best;
|
||||
struct kvm_hypervisor_cpuid kvm_cpuid;
|
||||
|
||||
best = cpuid_entry2_find(entries, nent, 1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
|
||||
if (best) {
|
||||
@ -292,10 +300,12 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e
|
||||
cpuid_entry_has(best, X86_FEATURE_XSAVEC)))
|
||||
best->ebx = xstate_required_size(vcpu->arch.xcr0, true);
|
||||
|
||||
best = __kvm_find_kvm_cpuid_features(vcpu, entries, nent);
|
||||
if (kvm_hlt_in_guest(vcpu->kvm) && best &&
|
||||
(best->eax & (1 << KVM_FEATURE_PV_UNHALT)))
|
||||
best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT);
|
||||
kvm_cpuid = __kvm_get_hypervisor_cpuid(entries, nent, KVM_SIGNATURE);
|
||||
if (kvm_cpuid.base) {
|
||||
best = __kvm_find_kvm_cpuid_features(entries, nent, kvm_cpuid.base);
|
||||
if (kvm_hlt_in_guest(vcpu->kvm) && best)
|
||||
best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT);
|
||||
}
|
||||
|
||||
if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)) {
|
||||
best = cpuid_entry2_find(entries, nent, 0x1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
|
||||
|
@ -1037,8 +1037,19 @@ static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu)
|
||||
void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu,
|
||||
struct kvm_x86_cpu_property property,
|
||||
uint32_t value);
|
||||
void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr);
|
||||
|
||||
void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function);
|
||||
|
||||
static inline bool vcpu_cpuid_has(struct kvm_vcpu *vcpu,
|
||||
struct kvm_x86_cpu_feature feature)
|
||||
{
|
||||
struct kvm_cpuid_entry2 *entry;
|
||||
|
||||
entry = __vcpu_get_cpuid_entry(vcpu, feature.function, feature.index);
|
||||
return *((&entry->eax) + feature.reg) & BIT(feature.bit);
|
||||
}
|
||||
|
||||
void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu,
|
||||
struct kvm_x86_cpu_feature feature,
|
||||
bool set);
|
||||
|
@ -133,6 +133,43 @@ static void enter_guest(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_pv_unhalt(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_cpuid_entry2 *ent;
|
||||
u32 kvm_sig_old;
|
||||
|
||||
pr_info("testing KVM_FEATURE_PV_UNHALT\n");
|
||||
|
||||
TEST_REQUIRE(KVM_CAP_X86_DISABLE_EXITS);
|
||||
|
||||
/* KVM_PV_UNHALT test */
|
||||
vm = vm_create_with_one_vcpu(&vcpu, guest_main);
|
||||
vcpu_set_cpuid_feature(vcpu, X86_FEATURE_KVM_PV_UNHALT);
|
||||
|
||||
TEST_ASSERT(vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),
|
||||
"Enabling X86_FEATURE_KVM_PV_UNHALT had no effect");
|
||||
|
||||
/* Make sure KVM clears vcpu->arch.kvm_cpuid */
|
||||
ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE);
|
||||
kvm_sig_old = ent->ebx;
|
||||
ent->ebx = 0xdeadbeef;
|
||||
vcpu_set_cpuid(vcpu);
|
||||
|
||||
vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, KVM_X86_DISABLE_EXITS_HLT);
|
||||
ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE);
|
||||
ent->ebx = kvm_sig_old;
|
||||
vcpu_set_cpuid(vcpu);
|
||||
|
||||
TEST_ASSERT(!vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT),
|
||||
"KVM_FEATURE_PV_UNHALT is set with KVM_CAP_X86_DISABLE_EXITS");
|
||||
|
||||
/* FIXME: actually test KVM_FEATURE_PV_UNHALT feature */
|
||||
|
||||
kvm_vm_free(vm);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpu;
|
||||
@ -151,4 +188,6 @@ int main(void)
|
||||
|
||||
enter_guest(vcpu);
|
||||
kvm_vm_free(vm);
|
||||
|
||||
test_pv_unhalt();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user