Skip to content

Commit 0d17564

Browse files
committed
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.
2 parents c20722c + c258504 commit 0d17564

File tree

3 files changed

+76
-16
lines changed

3 files changed

+76
-16
lines changed

arch/x86/kvm/cpuid.c

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,15 @@ static int kvm_cpuid_check_equal(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2
189189
return 0;
190190
}
191191

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)
194194
{
195195
struct kvm_hypervisor_cpuid cpuid = {};
196196
struct kvm_cpuid_entry2 *entry;
197197
u32 base;
198198

199199
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);
201201

202202
if (entry) {
203203
u32 signature[3];
@@ -217,22 +217,29 @@ static struct kvm_hypervisor_cpuid kvm_get_hypervisor_cpuid(struct kvm_vcpu *vcp
217217
return cpuid;
218218
}
219219

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)
222222
{
223-
u32 base = vcpu->arch.kvm_cpuid.base;
224-
225-
if (!base)
226-
return NULL;
223+
return __kvm_get_hypervisor_cpuid(vcpu->arch.cpuid_entries,
224+
vcpu->arch.cpuid_nent, sig);
225+
}
227226

228-
return cpuid_entry2_find(entries, nent, base | KVM_CPUID_FEATURES,
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,
229231
KVM_CPUID_INDEX_NOT_SIGNIFICANT);
230232
}
231233

232234
static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu)
233235
{
234-
return __kvm_find_kvm_cpuid_features(vcpu, vcpu->arch.cpuid_entries,
235-
vcpu->arch.cpuid_nent);
236+
u32 base = vcpu->arch.kvm_cpuid.base;
237+
238+
if (!base)
239+
return NULL;
240+
241+
return __kvm_find_kvm_cpuid_features(vcpu->arch.cpuid_entries,
242+
vcpu->arch.cpuid_nent, base);
236243
}
237244

238245
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
266273
int nent)
267274
{
268275
struct kvm_cpuid_entry2 *best;
276+
struct kvm_hypervisor_cpuid kvm_cpuid;
269277

270278
best = cpuid_entry2_find(entries, nent, 1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);
271279
if (best) {
@@ -292,10 +300,12 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e
292300
cpuid_entry_has(best, X86_FEATURE_XSAVEC)))
293301
best->ebx = xstate_required_size(vcpu->arch.xcr0, true);
294302

295-
best = __kvm_find_kvm_cpuid_features(vcpu, entries, nent);
296-
if (kvm_hlt_in_guest(vcpu->kvm) && best &&
297-
(best->eax & (1 << KVM_FEATURE_PV_UNHALT)))
298-
best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT);
303+
kvm_cpuid = __kvm_get_hypervisor_cpuid(entries, nent, KVM_SIGNATURE);
304+
if (kvm_cpuid.base) {
305+
best = __kvm_find_kvm_cpuid_features(entries, nent, kvm_cpuid.base);
306+
if (kvm_hlt_in_guest(vcpu->kvm) && best)
307+
best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT);
308+
}
299309

300310
if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)) {
301311
best = cpuid_entry2_find(entries, nent, 0x1, KVM_CPUID_INDEX_NOT_SIGNIFICANT);

tools/testing/selftests/kvm/include/x86_64/processor.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,8 +1037,19 @@ static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu)
10371037
void vcpu_set_cpuid_property(struct kvm_vcpu *vcpu,
10381038
struct kvm_x86_cpu_property property,
10391039
uint32_t value);
1040+
void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr);
10401041

10411042
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+
10421053
void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu,
10431054
struct kvm_x86_cpu_feature feature,
10441055
bool set);

tools/testing/selftests/kvm/x86_64/kvm_pv_test.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,43 @@ static void enter_guest(struct kvm_vcpu *vcpu)
133133
}
134134
}
135135

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+
136173
int main(void)
137174
{
138175
struct kvm_vcpu *vcpu;
@@ -151,4 +188,6 @@ int main(void)
151188

152189
enter_guest(vcpu);
153190
kvm_vm_free(vm);
191+
192+
test_pv_unhalt();
154193
}

0 commit comments

Comments
 (0)