Commit f1a9761f authored by Oliver Upton's avatar Oliver Upton Committed by Paolo Bonzini
Browse files

KVM: x86: Allow userspace to opt out of hypercall patching



KVM handles the VMCALL/VMMCALL instructions very strangely. Even though
both of these instructions really should #UD when executed on the wrong
vendor's hardware (i.e. VMCALL on SVM, VMMCALL on VMX), KVM replaces the
guest's instruction with the appropriate instruction for the vendor.
Nonetheless, older guest kernels without commit c1118b36 ("x86: kvm:
use alternatives for VMCALL vs. VMMCALL if kernel text is read-only")
do not patch in the appropriate instruction using alternatives, likely
motivating KVM's intervention.

Add a quirk allowing userspace to opt out of hypercall patching. If the
quirk is disabled, KVM synthesizes a #UD in the guest.

Signed-off-by: default avatarOliver Upton <oupton@google.com>
Message-Id: <20220316005538.2282772-2-oupton@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent c15e0ae4
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -7134,6 +7134,15 @@ The valid bits in cap.args[0] are:
                                    Additionally, when this quirk is disabled,
                                    KVM clears CPUID.01H:ECX[bit 3] if
                                    IA32_MISC_ENABLE[bit 18] is cleared.

 KVM_X86_QUIRK_FIX_HYPERCALL_INSN   By default, KVM rewrites guest
                                    VMMCALL/VMCALL instructions to match the
                                    vendor's hypercall instruction for the
                                    system. When this quirk is disabled, KVM
                                    will no longer rewrite invalid guest
                                    hypercall instructions. Executing the
                                    incorrect hypercall instruction will
                                    generate a #UD within the guest.
=================================== ============================================

8. Other capabilities.
+2 −1
Original line number Diff line number Diff line
@@ -1987,6 +1987,7 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages);
	 KVM_X86_QUIRK_CD_NW_CLEARED |		\
	 KVM_X86_QUIRK_LAPIC_MMIO_HOLE |	\
	 KVM_X86_QUIRK_OUT_7E_INC_RIP |		\
	 KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)
	 KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT |	\
	 KVM_X86_QUIRK_FIX_HYPERCALL_INSN)

#endif /* _ASM_X86_KVM_HOST_H */
+6 −5
Original line number Diff line number Diff line
@@ -433,6 +433,7 @@ struct kvm_sync_regs {
#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE		(1 << 2)
#define KVM_X86_QUIRK_OUT_7E_INC_RIP		(1 << 3)
#define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT	(1 << 4)
#define KVM_X86_QUIRK_FIX_HYPERCALL_INSN	(1 << 5)

#define KVM_STATE_NESTED_FORMAT_VMX	0
#define KVM_STATE_NESTED_FORMAT_SVM	1
+11 −0
Original line number Diff line number Diff line
@@ -9267,6 +9267,17 @@ static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt)
	char instruction[3];
	unsigned long rip = kvm_rip_read(vcpu);

	/*
	 * If the quirk is disabled, synthesize a #UD and let the guest pick up
	 * the pieces.
	 */
	if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_FIX_HYPERCALL_INSN)) {
		ctxt->exception.error_code_valid = false;
		ctxt->exception.vector = UD_VECTOR;
		ctxt->have_exception = true;
		return X86EMUL_PROPAGATE_FAULT;
	}

	static_call(kvm_x86_patch_hypercall)(vcpu, instruction);

	return emulator_write_emulated(ctxt, rip, instruction, 3,