Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.

+77 -17
+27 -17
arch/x86/kvm/cpuid.c
··· 189 189 return 0; 190 190 } 191 191 192 - static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcpu, 193 - const char *sig) 192 + static struct kvm_hypervisor_cpuid __kvm_get_hypervisor_cpuid(struct kvm_cpuid_entry2 *entries, 193 + int nent, const char *sig) 194 194 { 195 195 struct kvm_hypervisor_cpuid cpuid = {}; 196 196 struct kvm_cpuid_entry2 *entry; 197 197 u32 base; 198 198 199 199 for_each_possible_hypervisor_cpuid_base(base) { 200 - entry = kvm_find_cpuid_entry(vcpu, base); 200 + entry = cpuid_entry2_find(entries, nent, base, KVM_CPUID_INDEX_NOT_SIGNIFICANT); 201 201 202 202 if (entry) { 203 203 u32 signature[3]; ··· 217 217 return cpuid; 218 218 } 219 219 220 - static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu, 221 - struct kvm_cpuid_entry2 *entries, int nent) 220 + static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcpu, 221 + const char *sig) 222 + { 223 + return __kvm_get_hypervisor_cpuid(vcpu->arch.cpuid_entries, 224 + vcpu->arch.cpuid_nent, sig); 225 + } 226 + 227 + static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_cpuid_entry2 *entries, 228 + int nent, u32 kvm_cpuid_base) 229 + { 230 + return cpuid_entry2_find(entries, nent, kvm_cpuid_base | KVM_CPUID_FEATURES, 231 + KVM_CPUID_INDEX_NOT_SIGNIFICANT); 232 + } 233 + 234 + static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu) 222 235 { 223 236 u32 base = vcpu->arch.kvm_cpuid.base; 224 237 225 238 if (!base) 226 239 return NULL; 227 240 228 - return cpuid_entry2_find(entries, nent, base | KVM_CPUID_FEATURES, 229 - KVM_CPUID_INDEX_NOT_SIGNIFICANT); 230 - } 231 - 232 - static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu) 233 - { 234 - return __kvm_find_kvm_cpuid_features(vcpu, vcpu->arch.cpuid_entries, 235 - vcpu->arch.cpuid_nent); 241 + return __kvm_find_kvm_cpuid_features(vcpu->arch.cpuid_entries, 242 + vcpu->arch.cpuid_nent, base); 236 243 } 237 244 238 245 void kvm_update_pv_runtime(struct kvm_vcpu *vcpu) ··· 273 266 int nent) 274 267 { 275 268 struct kvm_cpuid_entry2 *best; 269 + struct kvm_hypervisor_cpuid kvm_cpuid; 276 270 277 271 best = cpuid_entry2_find(entries, nent, 1, KVM_CPUID_INDEX_NOT_SIGNIFICANT); 278 272 if (best) { ··· 300 292 cpuid_entry_has(best, X86_FEATURE_XSAVEC))) 301 293 best->ebx = xstate_required_size(vcpu->arch.xcr0, true); 302 294 303 - best = __kvm_find_kvm_cpuid_features(vcpu, entries, nent); 304 - if (kvm_hlt_in_guest(vcpu->kvm) && best && 305 - (best->eax & (1 << KVM_FEATURE_PV_UNHALT))) 306 - best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT); 295 + kvm_cpuid = __kvm_get_hypervisor_cpuid(entries, nent, KVM_SIGNATURE); 296 + if (kvm_cpuid.base) { 297 + best = __kvm_find_kvm_cpuid_features(entries, nent, kvm_cpuid.base); 298 + if (kvm_hlt_in_guest(vcpu->kvm) && best) 299 + best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT); 300 + } 307 301 308 302 if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)) { 309 303 best = cpuid_entry2_find(entries, nent, 0x1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
+11
tools/testing/selftests/kvm/include/x86_64/processor.h
··· 1037 1037 void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu, 1038 1038 struct kvm_x86_cpu_property property, 1039 1039 uint32_t value); 1040 + void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr); 1040 1041 1041 1042 void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function); 1043 + 1044 + static inline bool vcpu_cpuid_has(struct kvm_vcpu *vcpu, 1045 + struct kvm_x86_cpu_feature feature) 1046 + { 1047 + struct kvm_cpuid_entry2 *entry; 1048 + 1049 + entry = __vcpu_get_cpuid_entry(vcpu, feature.function, feature.index); 1050 + return *((&entry->eax) + feature.reg) & BIT(feature.bit); 1051 + } 1052 + 1042 1053 void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu, 1043 1054 struct kvm_x86_cpu_feature feature, 1044 1055 bool set);
+39
tools/testing/selftests/kvm/x86_64/kvm_pv_test.c
··· 133 133 } 134 134 } 135 135 136 + static void test_pv_unhalt(void) 137 + { 138 + struct kvm_vcpu *vcpu; 139 + struct kvm_vm *vm; 140 + struct kvm_cpuid_entry2 *ent; 141 + u32 kvm_sig_old; 142 + 143 + pr_info("testing KVM_FEATURE_PV_UNHALT\n"); 144 + 145 + TEST_REQUIRE(KVM_CAP_X86_DISABLE_EXITS); 146 + 147 + /* KVM_PV_UNHALT test */ 148 + vm = vm_create_with_one_vcpu(&vcpu, guest_main); 149 + vcpu_set_cpuid_feature(vcpu, X86_FEATURE_KVM_PV_UNHALT); 150 + 151 + TEST_ASSERT(vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT), 152 + "Enabling X86_FEATURE_KVM_PV_UNHALT had no effect"); 153 + 154 + /* Make sure KVM clears vcpu->arch.kvm_cpuid */ 155 + ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE); 156 + kvm_sig_old = ent->ebx; 157 + ent->ebx = 0xdeadbeef; 158 + vcpu_set_cpuid(vcpu); 159 + 160 + vm_enable_cap(vm, KVM_CAP_X86_DISABLE_EXITS, KVM_X86_DISABLE_EXITS_HLT); 161 + ent = vcpu_get_cpuid_entry(vcpu, KVM_CPUID_SIGNATURE); 162 + ent->ebx = kvm_sig_old; 163 + vcpu_set_cpuid(vcpu); 164 + 165 + TEST_ASSERT(!vcpu_cpuid_has(vcpu, X86_FEATURE_KVM_PV_UNHALT), 166 + "KVM_FEATURE_PV_UNHALT is set with KVM_CAP_X86_DISABLE_EXITS"); 167 + 168 + /* FIXME: actually test KVM_FEATURE_PV_UNHALT feature */ 169 + 170 + kvm_vm_free(vm); 171 + } 172 + 136 173 int main(void) 137 174 { 138 175 struct kvm_vcpu *vcpu; ··· 188 151 189 152 enter_guest(vcpu); 190 153 kvm_vm_free(vm); 154 + 155 + test_pv_unhalt(); 191 156 }