2024-11-12 15:35:28 +08:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#ifndef __KVM_X86_VMX_COMMON_H
|
|
|
|
#define __KVM_X86_VMX_COMMON_H
|
|
|
|
|
|
|
|
#include <linux/kvm_host.h>
|
2025-03-14 14:06:48 -04:00
|
|
|
#include <asm/posted_intr.h>
|
2024-11-12 15:35:28 +08:00
|
|
|
|
|
|
|
#include "mmu.h"
|
|
|
|
|
2025-03-14 14:06:48 -04:00
|
|
|
union vmx_exit_reason {
|
|
|
|
struct {
|
|
|
|
u32 basic : 16;
|
|
|
|
u32 reserved16 : 1;
|
|
|
|
u32 reserved17 : 1;
|
|
|
|
u32 reserved18 : 1;
|
|
|
|
u32 reserved19 : 1;
|
|
|
|
u32 reserved20 : 1;
|
|
|
|
u32 reserved21 : 1;
|
|
|
|
u32 reserved22 : 1;
|
|
|
|
u32 reserved23 : 1;
|
|
|
|
u32 reserved24 : 1;
|
|
|
|
u32 reserved25 : 1;
|
|
|
|
u32 bus_lock_detected : 1;
|
|
|
|
u32 enclave_mode : 1;
|
|
|
|
u32 smi_pending_mtf : 1;
|
|
|
|
u32 smi_from_vmx_root : 1;
|
|
|
|
u32 reserved30 : 1;
|
|
|
|
u32 failed_vmentry : 1;
|
|
|
|
};
|
|
|
|
u32 full;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct vcpu_vt {
|
|
|
|
/* Posted interrupt descriptor */
|
|
|
|
struct pi_desc pi_desc;
|
|
|
|
|
|
|
|
/* Used if this vCPU is waiting for PI notification wakeup. */
|
|
|
|
struct list_head pi_wakeup_list;
|
|
|
|
|
|
|
|
union vmx_exit_reason exit_reason;
|
|
|
|
|
|
|
|
unsigned long exit_qualification;
|
|
|
|
u32 exit_intr_info;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If true, guest state has been loaded into hardware, and host state
|
|
|
|
* saved into vcpu_{vt,vmx,tdx}. If false, host state is loaded into
|
|
|
|
* hardware.
|
|
|
|
*/
|
|
|
|
bool guest_state_loaded;
|
2025-02-22 09:47:54 +08:00
|
|
|
bool emulation_required;
|
2025-03-14 14:06:48 -04:00
|
|
|
|
|
|
|
#ifdef CONFIG_X86_64
|
|
|
|
u64 msr_host_kernel_gs_base;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_KVM_INTEL_TDX
|
|
|
|
|
|
|
|
static __always_inline bool is_td(struct kvm *kvm)
|
|
|
|
{
|
|
|
|
return kvm->arch.vm_type == KVM_X86_TDX_VM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __always_inline bool is_td_vcpu(struct kvm_vcpu *vcpu)
|
|
|
|
{
|
|
|
|
return is_td(vcpu->kvm);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2025-05-27 16:44:37 +08:00
|
|
|
static __always_inline bool is_td(struct kvm *kvm) { return false; }
|
|
|
|
static __always_inline bool is_td_vcpu(struct kvm_vcpu *vcpu) { return false; }
|
2025-03-14 14:06:48 -04:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2024-11-12 15:35:39 +08:00
|
|
|
static inline bool vt_is_tdx_private_gpa(struct kvm *kvm, gpa_t gpa)
|
|
|
|
{
|
|
|
|
/* For TDX the direct mask is the shared mask. */
|
|
|
|
return !kvm_is_addr_direct(kvm, gpa);
|
|
|
|
}
|
|
|
|
|
2024-11-12 15:35:28 +08:00
|
|
|
static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa,
|
|
|
|
unsigned long exit_qualification)
|
|
|
|
{
|
|
|
|
u64 error_code;
|
|
|
|
|
|
|
|
/* Is it a read fault? */
|
|
|
|
error_code = (exit_qualification & EPT_VIOLATION_ACC_READ)
|
|
|
|
? PFERR_USER_MASK : 0;
|
|
|
|
/* Is it a write fault? */
|
|
|
|
error_code |= (exit_qualification & EPT_VIOLATION_ACC_WRITE)
|
|
|
|
? PFERR_WRITE_MASK : 0;
|
|
|
|
/* Is it a fetch fault? */
|
|
|
|
error_code |= (exit_qualification & EPT_VIOLATION_ACC_INSTR)
|
|
|
|
? PFERR_FETCH_MASK : 0;
|
|
|
|
/* ept page table entry is present? */
|
Merge branch 'kvm-tdx-initial' into HEAD
This large commit contains the initial support for TDX in KVM. All x86
parts enable the host-side hypercalls that KVM uses to talk to the TDX
module, a software component that runs in a special CPU mode called SEAM
(Secure Arbitration Mode).
The series is in turn split into multiple sub-series, each with a separate
merge commit:
- Initialization: basic setup for using the TDX module from KVM, plus
ioctls to create TDX VMs and vCPUs.
- MMU: in TDX, private and shared halves of the address space are mapped by
different EPT roots, and the private half is managed by the TDX module.
Using the support that was added to the generic MMU code in 6.14,
add support for TDX's secure page tables to the Intel side of KVM.
Generic KVM code takes care of maintaining a mirror of the secure page
tables so that they can be queried efficiently, and ensuring that changes
are applied to both the mirror and the secure EPT.
- vCPU enter/exit: implement the callbacks that handle the entry of a TDX
vCPU (via the SEAMCALL TDH.VP.ENTER) and the corresponding save/restore
of host state.
- Userspace exits: introduce support for guest TDVMCALLs that KVM forwards to
userspace. These correspond to the usual KVM_EXIT_* "heavyweight vmexits"
but are triggered through a different mechanism, similar to VMGEXIT for
SEV-ES and SEV-SNP.
- Interrupt handling: support for virtual interrupt injection as well as
handling VM-Exits that are caused by vectored events. Exclusive to
TDX are machine-check SMIs, which the kernel already knows how to
handle through the kernel machine check handler (commit 7911f145de5f,
"x86/mce: Implement recovery for errors in TDX/SEAM non-root mode")
- Loose ends: handling of the remaining exits from the TDX module, including
EPT violation/misconfig and several TDVMCALL leaves that are handled in
the kernel (CPUID, HLT, RDMSR/WRMSR, GetTdVmCallInfo); plus returning
an error or ignoring operations that are not supported by TDX guests
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2025-03-19 09:46:59 -04:00
|
|
|
error_code |= (exit_qualification & EPT_VIOLATION_PROT_MASK)
|
2024-11-12 15:35:28 +08:00
|
|
|
? PFERR_PRESENT_MASK : 0;
|
|
|
|
|
|
|
|
if (error_code & EPT_VIOLATION_GVA_IS_VALID)
|
|
|
|
error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) ?
|
|
|
|
PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK;
|
|
|
|
|
2024-11-12 15:35:39 +08:00
|
|
|
if (vt_is_tdx_private_gpa(vcpu->kvm, gpa))
|
|
|
|
error_code |= PFERR_PRIVATE_ACCESS;
|
|
|
|
|
2024-11-12 15:35:28 +08:00
|
|
|
return kvm_mmu_page_fault(vcpu, gpa, error_code, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2025-02-22 09:47:44 +08:00
|
|
|
static inline void kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu,
|
|
|
|
int pi_vec)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
if (vcpu->mode == IN_GUEST_MODE) {
|
|
|
|
/*
|
|
|
|
* The vector of the virtual has already been set in the PIR.
|
|
|
|
* Send a notification event to deliver the virtual interrupt
|
|
|
|
* unless the vCPU is the currently running vCPU, i.e. the
|
|
|
|
* event is being sent from a fastpath VM-Exit handler, in
|
|
|
|
* which case the PIR will be synced to the vIRR before
|
|
|
|
* re-entering the guest.
|
|
|
|
*
|
|
|
|
* When the target is not the running vCPU, the following
|
|
|
|
* possibilities emerge:
|
|
|
|
*
|
|
|
|
* Case 1: vCPU stays in non-root mode. Sending a notification
|
|
|
|
* event posts the interrupt to the vCPU.
|
|
|
|
*
|
|
|
|
* Case 2: vCPU exits to root mode and is still runnable. The
|
|
|
|
* PIR will be synced to the vIRR before re-entering the guest.
|
|
|
|
* Sending a notification event is ok as the host IRQ handler
|
|
|
|
* will ignore the spurious event.
|
|
|
|
*
|
|
|
|
* Case 3: vCPU exits to root mode and is blocked. vcpu_block()
|
|
|
|
* has already synced PIR to vIRR and never blocks the vCPU if
|
|
|
|
* the vIRR is not empty. Therefore, a blocked vCPU here does
|
|
|
|
* not wait for any requested interrupts in PIR, and sending a
|
|
|
|
* notification event also results in a benign, spurious event.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (vcpu != kvm_get_running_vcpu())
|
|
|
|
__apic_send_IPI_mask(get_cpu_mask(vcpu->cpu), pi_vec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* The vCPU isn't in the guest; wake the vCPU in case it is blocking,
|
|
|
|
* otherwise do nothing as KVM will grab the highest priority pending
|
|
|
|
* IRQ via ->sync_pir_to_irr() in vcpu_enter_guest().
|
|
|
|
*/
|
|
|
|
kvm_vcpu_wake_up(vcpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Post an interrupt to a vCPU's PIR and trigger the vCPU to process the
|
|
|
|
* interrupt if necessary.
|
|
|
|
*/
|
|
|
|
static inline void __vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu,
|
|
|
|
struct pi_desc *pi_desc, int vector)
|
|
|
|
{
|
|
|
|
if (pi_test_and_set_pir(vector, pi_desc))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* If a previous notification has sent the IPI, nothing to do. */
|
|
|
|
if (pi_test_and_set_on(pi_desc))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The implied barrier in pi_test_and_set_on() pairs with the smp_mb_*()
|
|
|
|
* after setting vcpu->mode in vcpu_enter_guest(), thus the vCPU is
|
|
|
|
* guaranteed to see PID.ON=1 and sync the PIR to IRR if triggering a
|
|
|
|
* posted interrupt "fails" because vcpu->mode != IN_GUEST_MODE.
|
|
|
|
*/
|
|
|
|
kvm_vcpu_trigger_posted_interrupt(vcpu, POSTED_INTR_VECTOR);
|
|
|
|
}
|
|
|
|
|
2025-02-22 09:47:55 +08:00
|
|
|
noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu);
|
|
|
|
|
2024-11-12 15:35:28 +08:00
|
|
|
#endif /* __KVM_X86_VMX_COMMON_H */
|