Skip to content

Commit 3eba032

Browse files
committed
Merge branch 'kvm-userspace-hypercall' into HEAD
Make the completion of hypercalls go through the complete_hypercall function pointer argument, no matter if the hypercall exits to userspace or not. Previously, the code assumed that KVM_HC_MAP_GPA_RANGE specifically went to userspace, and all the others did not; the new code need not special case KVM_HC_MAP_GPA_RANGE and in fact does not care at all whether there was an exit to userspace or not.
2 parents 43f640f + c50be1c commit 3eba032

File tree

4 files changed

+67
-41
lines changed

4 files changed

+67
-41
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,12 +2203,6 @@ static inline void kvm_clear_apicv_inhibit(struct kvm *kvm,
22032203
kvm_set_or_clear_apicv_inhibit(kvm, reason, false);
22042204
}
22052205

2206-
unsigned long __kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
2207-
unsigned long a0, unsigned long a1,
2208-
unsigned long a2, unsigned long a3,
2209-
int op_64_bit, int cpl);
2210-
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
2211-
22122206
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,
22132207
void *insn, int insn_len);
22142208
void kvm_mmu_print_sptes(struct kvm_vcpu *vcpu, gpa_t gpa, const char *msg);

arch/x86/kvm/svm/sev.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3627,13 +3627,20 @@ static int snp_begin_psc_msr(struct vcpu_svm *svm, u64 ghcb_msr)
36273627
return 1; /* resume guest */
36283628
}
36293629

3630-
if (!(vcpu->kvm->arch.hypercall_exit_enabled & (1 << KVM_HC_MAP_GPA_RANGE))) {
3630+
if (!user_exit_on_hypercall(vcpu->kvm, KVM_HC_MAP_GPA_RANGE)) {
36313631
set_ghcb_msr(svm, GHCB_MSR_PSC_RESP_ERROR);
36323632
return 1; /* resume guest */
36333633
}
36343634

36353635
vcpu->run->exit_reason = KVM_EXIT_HYPERCALL;
36363636
vcpu->run->hypercall.nr = KVM_HC_MAP_GPA_RANGE;
3637+
/*
3638+
* In principle this should have been -KVM_ENOSYS, but userspace (QEMU <=9.2)
3639+
* assumed that vcpu->run->hypercall.ret is never changed by KVM and thus that
3640+
* it was always zero on KVM_EXIT_HYPERCALL. Since KVM is now overwriting
3641+
* vcpu->run->hypercall.ret, ensuring that it is zero to not break QEMU.
3642+
*/
3643+
vcpu->run->hypercall.ret = 0;
36373644
vcpu->run->hypercall.args[0] = gpa;
36383645
vcpu->run->hypercall.args[1] = 1;
36393646
vcpu->run->hypercall.args[2] = (op == SNP_PAGE_STATE_PRIVATE)
@@ -3710,7 +3717,7 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc)
37103717
bool huge;
37113718
u64 gfn;
37123719

3713-
if (!(vcpu->kvm->arch.hypercall_exit_enabled & (1 << KVM_HC_MAP_GPA_RANGE))) {
3720+
if (!user_exit_on_hypercall(vcpu->kvm, KVM_HC_MAP_GPA_RANGE)) {
37143721
snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC);
37153722
return 1;
37163723
}
@@ -3797,6 +3804,13 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc)
37973804
case VMGEXIT_PSC_OP_SHARED:
37983805
vcpu->run->exit_reason = KVM_EXIT_HYPERCALL;
37993806
vcpu->run->hypercall.nr = KVM_HC_MAP_GPA_RANGE;
3807+
/*
3808+
* In principle this should have been -KVM_ENOSYS, but userspace (QEMU <=9.2)
3809+
* assumed that vcpu->run->hypercall.ret is never changed by KVM and thus that
3810+
* it was always zero on KVM_EXIT_HYPERCALL. Since KVM is now overwriting
3811+
* vcpu->run->hypercall.ret, ensuring that it is zero to not break QEMU.
3812+
*/
3813+
vcpu->run->hypercall.ret = 0;
38003814
vcpu->run->hypercall.args[0] = gfn_to_gpa(gfn);
38013815
vcpu->run->hypercall.args[1] = npages;
38023816
vcpu->run->hypercall.args[2] = entry_start.operation == VMGEXIT_PSC_OP_PRIVATE

arch/x86/kvm/x86.c

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9997,17 +9997,19 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu)
99979997
if (!is_64_bit_hypercall(vcpu))
99989998
ret = (u32)ret;
99999999
kvm_rax_write(vcpu, ret);
10000-
++vcpu->stat.hypercalls;
1000110000
return kvm_skip_emulated_instruction(vcpu);
1000210001
}
1000310002

10004-
unsigned long __kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
10005-
unsigned long a0, unsigned long a1,
10006-
unsigned long a2, unsigned long a3,
10007-
int op_64_bit, int cpl)
10003+
int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
10004+
unsigned long a0, unsigned long a1,
10005+
unsigned long a2, unsigned long a3,
10006+
int op_64_bit, int cpl,
10007+
int (*complete_hypercall)(struct kvm_vcpu *))
1000810008
{
1000910009
unsigned long ret;
1001010010

10011+
++vcpu->stat.hypercalls;
10012+
1001110013
trace_kvm_hypercall(nr, a0, a1, a2, a3);
1001210014

1001310015
if (!op_64_bit) {
@@ -10059,7 +10061,7 @@ unsigned long __kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
1005910061
u64 gpa = a0, npages = a1, attrs = a2;
1006010062

1006110063
ret = -KVM_ENOSYS;
10062-
if (!(vcpu->kvm->arch.hypercall_exit_enabled & (1 << KVM_HC_MAP_GPA_RANGE)))
10064+
if (!user_exit_on_hypercall(vcpu->kvm, KVM_HC_MAP_GPA_RANGE))
1006310065
break;
1006410066

1006510067
if (!PAGE_ALIGNED(gpa) || !npages ||
@@ -10070,6 +10072,13 @@ unsigned long __kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
1007010072

1007110073
vcpu->run->exit_reason = KVM_EXIT_HYPERCALL;
1007210074
vcpu->run->hypercall.nr = KVM_HC_MAP_GPA_RANGE;
10075+
/*
10076+
* In principle this should have been -KVM_ENOSYS, but userspace (QEMU <=9.2)
10077+
* assumed that vcpu->run->hypercall.ret is never changed by KVM and thus that
10078+
* it was always zero on KVM_EXIT_HYPERCALL. Since KVM is now overwriting
10079+
* vcpu->run->hypercall.ret, ensuring that it is zero to not break QEMU.
10080+
*/
10081+
vcpu->run->hypercall.ret = 0;
1007310082
vcpu->run->hypercall.args[0] = gpa;
1007410083
vcpu->run->hypercall.args[1] = npages;
1007510084
vcpu->run->hypercall.args[2] = attrs;
@@ -10078,8 +10087,7 @@ unsigned long __kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
1007810087
vcpu->run->hypercall.flags |= KVM_EXIT_HYPERCALL_LONG_MODE;
1007910088

1008010089
WARN_ON_ONCE(vcpu->run->hypercall.flags & KVM_EXIT_HYPERCALL_MBZ);
10081-
vcpu->arch.complete_userspace_io = complete_hypercall_exit;
10082-
/* stat is incremented on completion. */
10090+
vcpu->arch.complete_userspace_io = complete_hypercall;
1008310091
return 0;
1008410092
}
1008510093
default:
@@ -10088,41 +10096,23 @@ unsigned long __kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
1008810096
}
1008910097

1009010098
out:
10091-
++vcpu->stat.hypercalls;
10092-
return ret;
10099+
vcpu->run->hypercall.ret = ret;
10100+
return 1;
1009310101
}
10094-
EXPORT_SYMBOL_GPL(__kvm_emulate_hypercall);
10102+
EXPORT_SYMBOL_GPL(____kvm_emulate_hypercall);
1009510103

1009610104
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
1009710105
{
10098-
unsigned long nr, a0, a1, a2, a3, ret;
10099-
int op_64_bit;
10100-
int cpl;
10101-
1010210106
if (kvm_xen_hypercall_enabled(vcpu->kvm))
1010310107
return kvm_xen_hypercall(vcpu);
1010410108

1010510109
if (kvm_hv_hypercall_enabled(vcpu))
1010610110
return kvm_hv_hypercall(vcpu);
1010710111

10108-
nr = kvm_rax_read(vcpu);
10109-
a0 = kvm_rbx_read(vcpu);
10110-
a1 = kvm_rcx_read(vcpu);
10111-
a2 = kvm_rdx_read(vcpu);
10112-
a3 = kvm_rsi_read(vcpu);
10113-
op_64_bit = is_64_bit_hypercall(vcpu);
10114-
cpl = kvm_x86_call(get_cpl)(vcpu);
10115-
10116-
ret = __kvm_emulate_hypercall(vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl);
10117-
if (nr == KVM_HC_MAP_GPA_RANGE && !ret)
10118-
/* MAP_GPA tosses the request to the user space. */
10119-
return 0;
10120-
10121-
if (!op_64_bit)
10122-
ret = (u32)ret;
10123-
kvm_rax_write(vcpu, ret);
10124-
10125-
return kvm_skip_emulated_instruction(vcpu);
10112+
return __kvm_emulate_hypercall(vcpu, rax, rbx, rcx, rdx, rsi,
10113+
is_64_bit_hypercall(vcpu),
10114+
kvm_x86_call(get_cpl)(vcpu),
10115+
complete_hypercall_exit);
1012610116
}
1012710117
EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);
1012810118

arch/x86/kvm/x86.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,4 +616,32 @@ int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size,
616616
unsigned int port, void *data, unsigned int count,
617617
int in);
618618

619+
static inline bool user_exit_on_hypercall(struct kvm *kvm, unsigned long hc_nr)
620+
{
621+
return kvm->arch.hypercall_exit_enabled & BIT(hc_nr);
622+
}
623+
624+
int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, unsigned long nr,
625+
unsigned long a0, unsigned long a1,
626+
unsigned long a2, unsigned long a3,
627+
int op_64_bit, int cpl,
628+
int (*complete_hypercall)(struct kvm_vcpu *));
629+
630+
#define __kvm_emulate_hypercall(_vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl, complete_hypercall) \
631+
({ \
632+
int __ret; \
633+
\
634+
__ret = ____kvm_emulate_hypercall(_vcpu, \
635+
kvm_##nr##_read(_vcpu), kvm_##a0##_read(_vcpu), \
636+
kvm_##a1##_read(_vcpu), kvm_##a2##_read(_vcpu), \
637+
kvm_##a3##_read(_vcpu), op_64_bit, cpl, \
638+
complete_hypercall); \
639+
\
640+
if (__ret > 0) \
641+
__ret = complete_hypercall(_vcpu); \
642+
__ret; \
643+
})
644+
645+
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
646+
619647
#endif

0 commit comments

Comments
 (0)