Allow kvm-based VMMs to request KVM to pass a custom vmcall from the guest to the VMM in the host. Quite often, operating systems research projects and/or specialized paravirtualized workloads would benefit from a extra-low-overhead, extra-low-latency guest-host communication channel. With cloud-hypervisor modified to handle the new hypercall (simply return the sum of the received arguments), the following function in guest _userspace_ completes, on average, in 2.5 microseconds (walltime) on a relatively modern Intel Xeon processor: uint64_t hypercall_custom_vmm(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) { uint64_t ret; asm( "movq $13, %%rax \n\t" // hypercall nr. "movq %[a0], %%rbx \n\t" // a0 "movq %[a1], %%rcx \n\t" // a1 "movq %[a2], %%rdx \n\t" // a2 "movq %[a3], %%rsi \n\t" // a3 "vmcall \n\t" "movq %%rax, %[ret] \n\t" // ret : [ret] "=r"(ret) : [a0] "r"(a0), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3) : "rax", "rbx", "rcx", "rdx", "rsi" ); return ret; } Signed-off-by: Peter Oskolkov <posk@xxxxxxxxxx> --- arch/x86/kvm/x86.c | 28 ++++++++++++++++++++++++++-- include/uapi/linux/kvm_para.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ab336f7c82e4..343971128da7 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -108,7 +108,8 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); static u64 __read_mostly cr4_reserved_bits = CR4_RESERVED_BITS; -#define KVM_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE) +#define KVM_EXIT_HYPERCALL_VALID_MASK ((1 << KVM_HC_MAP_GPA_RANGE) | \ + (1 << KVM_HC_VMM_CUSTOM)) #define KVM_CAP_PMU_VALID_MASK KVM_PMU_CAP_DISABLE @@ -9207,10 +9208,16 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu) return kvm_skip_emulated_instruction(vcpu); } +static int kvm_allow_hypercall_from_userspace(int nr) +{ + return nr == KVM_HC_VMM_CUSTOM; +} + int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) { unsigned long nr, a0, a1, a2, a3, ret; int op_64_bit; + int cpl; if (kvm_xen_hypercall_enabled(vcpu->kvm)) return kvm_xen_hypercall(vcpu); @@ -9235,7 +9242,8 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) a3 &= 0xFFFFFFFF; } - if (static_call(kvm_x86_get_cpl)(vcpu) != 0) { + cpl = static_call(kvm_x86_get_cpl)(vcpu); + if (cpl != 0 && !kvm_allow_hypercall_from_userspace(nr)) { ret = -KVM_EPERM; goto out; } @@ -9294,6 +9302,22 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) vcpu->arch.complete_userspace_io = complete_hypercall_exit; return 0; } + case KVM_HC_VMM_CUSTOM: + ret = -KVM_ENOSYS; + if (!(vcpu->kvm->arch.hypercall_exit_enabled & (1 << KVM_HC_VMM_CUSTOM))) + break; + + vcpu->run->exit_reason = KVM_EXIT_HYPERCALL; + vcpu->run->hypercall.nr = KVM_HC_VMM_CUSTOM; + vcpu->run->hypercall.args[0] = a0; + vcpu->run->hypercall.args[1] = a1; + vcpu->run->hypercall.args[2] = a2; + vcpu->run->hypercall.args[3] = a3; + vcpu->run->hypercall.args[4] = 0; + vcpu->run->hypercall.args[5] = cpl; + vcpu->run->hypercall.longmode = op_64_bit; + vcpu->arch.complete_userspace_io = complete_hypercall_exit; + return 0; default: ret = -KVM_ENOSYS; break; diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index 960c7e93d1a9..8caab28c9025 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -30,6 +30,7 @@ #define KVM_HC_SEND_IPI 10 #define KVM_HC_SCHED_YIELD 11 #define KVM_HC_MAP_GPA_RANGE 12 +#define KVM_HC_VMM_CUSTOM 13 /* * hypercalls use architecture specific base-commit: 150866cd0ec871c765181d145aa0912628289c8a -- 2.36.0.rc2.479.g8af0fa9b8e-goog