mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge branch 'kvm-tdx-finish-initial' into HEAD
This patch ties the remaining loose ends and finally enables TDX guests to
run inside KVM. It implements handling of EPT violation/misconfig and of
several TDVMCALL leaves that are handled in the kernel (CPUID, HLT, RDMSR/WRMSR,
GetTdVmCallInfo); it also adds a bunch of wrappers in vmx/main.c to
ignore operations not supported by TDX guests(*)
Finally, it introduces documentation for the new APIs that have been
added along the way.
(*) access to CPU state, VMX preemption timer, accesses to TSC offset or
multiplier, LMCE enable/disable, hypercall patching.
This commit is contained in:
@@ -1407,6 +1407,9 @@ the memory region are automatically reflected into the guest. For example, an
|
||||
mmap() that affects the region will be made visible immediately. Another
|
||||
example is madvise(MADV_DROP).
|
||||
|
||||
For TDX guest, deleting/moving memory region loses guest memory contents.
|
||||
Read only region isn't supported. Only as-id 0 is supported.
|
||||
|
||||
Note: On arm64, a write generated by the page-table walker (to update
|
||||
the Access and Dirty flags, for example) never results in a
|
||||
KVM_EXIT_MMIO exit when the slot has the KVM_MEM_READONLY flag. This
|
||||
@@ -4764,7 +4767,7 @@ H_GET_CPU_CHARACTERISTICS hypercall.
|
||||
|
||||
:Capability: basic
|
||||
:Architectures: x86
|
||||
:Type: vm
|
||||
:Type: vm ioctl, vcpu ioctl
|
||||
:Parameters: an opaque platform specific structure (in/out)
|
||||
:Returns: 0 on success; -1 on error
|
||||
|
||||
@@ -4772,9 +4775,11 @@ If the platform supports creating encrypted VMs then this ioctl can be used
|
||||
for issuing platform-specific memory encryption commands to manage those
|
||||
encrypted VMs.
|
||||
|
||||
Currently, this ioctl is used for issuing Secure Encrypted Virtualization
|
||||
(SEV) commands on AMD Processors. The SEV commands are defined in
|
||||
Documentation/virt/kvm/x86/amd-memory-encryption.rst.
|
||||
Currently, this ioctl is used for issuing both Secure Encrypted Virtualization
|
||||
(SEV) commands on AMD Processors and Trusted Domain Extensions (TDX) commands
|
||||
on Intel Processors. The detailed commands are defined in
|
||||
Documentation/virt/kvm/x86/amd-memory-encryption.rst and
|
||||
Documentation/virt/kvm/x86/intel-tdx.rst.
|
||||
|
||||
4.111 KVM_MEMORY_ENCRYPT_REG_REGION
|
||||
-----------------------------------
|
||||
@@ -8160,6 +8165,28 @@ KVM_X86_QUIRK_STUFF_FEATURE_MSRS By default, at vCPU creation, KVM sets the
|
||||
and 0x489), as KVM does now allow them to
|
||||
be set by userspace (KVM sets them based on
|
||||
guest CPUID, for safety purposes).
|
||||
|
||||
KVM_X86_QUIRK_IGNORE_GUEST_PAT By default, on Intel platforms, KVM ignores
|
||||
guest PAT and forces the effective memory
|
||||
type to WB in EPT. The quirk is not available
|
||||
on Intel platforms which are incapable of
|
||||
safely honoring guest PAT (i.e., without CPU
|
||||
self-snoop, KVM always ignores guest PAT and
|
||||
forces effective memory type to WB). It is
|
||||
also ignored on AMD platforms or, on Intel,
|
||||
when a VM has non-coherent DMA devices
|
||||
assigned; KVM always honors guest PAT in
|
||||
such case. The quirk is needed to avoid
|
||||
slowdowns on certain Intel Xeon platforms
|
||||
(e.g. ICX, SPR) where self-snoop feature is
|
||||
supported but UC is slow enough to cause
|
||||
issues with some older guests that use
|
||||
UC instead of WC to map the video RAM.
|
||||
Userspace can disable the quirk to honor
|
||||
guest PAT if it knows that there is no such
|
||||
guest software, for example if it does not
|
||||
expose a bochs graphics device (which is
|
||||
known to have had a buggy driver).
|
||||
=================================== ============================================
|
||||
|
||||
7.32 KVM_CAP_MAX_VCPU_ID
|
||||
|
||||
@@ -11,6 +11,7 @@ KVM for x86 systems
|
||||
cpuid
|
||||
errata
|
||||
hypercalls
|
||||
intel-tdx
|
||||
mmu
|
||||
msr
|
||||
nested-vmx
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===================================
|
||||
Intel Trust Domain Extensions (TDX)
|
||||
===================================
|
||||
|
||||
Overview
|
||||
========
|
||||
Intel's Trust Domain Extensions (TDX) protect confidential guest VMs from the
|
||||
host and physical attacks. A CPU-attested software module called 'the TDX
|
||||
module' runs inside a new CPU isolated range to provide the functionalities to
|
||||
manage and run protected VMs, a.k.a, TDX guests or TDs.
|
||||
|
||||
Please refer to [1] for the whitepaper, specifications and other resources.
|
||||
|
||||
This documentation describes TDX-specific KVM ABIs. The TDX module needs to be
|
||||
initialized before it can be used by KVM to run any TDX guests. The host
|
||||
core-kernel provides the support of initializing the TDX module, which is
|
||||
described in the Documentation/arch/x86/tdx.rst.
|
||||
|
||||
API description
|
||||
===============
|
||||
|
||||
KVM_MEMORY_ENCRYPT_OP
|
||||
---------------------
|
||||
:Type: vm ioctl, vcpu ioctl
|
||||
|
||||
For TDX operations, KVM_MEMORY_ENCRYPT_OP is re-purposed to be generic
|
||||
ioctl with TDX specific sub-ioctl() commands.
|
||||
|
||||
::
|
||||
|
||||
/* Trust Domain Extensions sub-ioctl() commands. */
|
||||
enum kvm_tdx_cmd_id {
|
||||
KVM_TDX_CAPABILITIES = 0,
|
||||
KVM_TDX_INIT_VM,
|
||||
KVM_TDX_INIT_VCPU,
|
||||
KVM_TDX_INIT_MEM_REGION,
|
||||
KVM_TDX_FINALIZE_VM,
|
||||
KVM_TDX_GET_CPUID,
|
||||
|
||||
KVM_TDX_CMD_NR_MAX,
|
||||
};
|
||||
|
||||
struct kvm_tdx_cmd {
|
||||
/* enum kvm_tdx_cmd_id */
|
||||
__u32 id;
|
||||
/* flags for sub-command. If sub-command doesn't use this, set zero. */
|
||||
__u32 flags;
|
||||
/*
|
||||
* data for each sub-command. An immediate or a pointer to the actual
|
||||
* data in process virtual address. If sub-command doesn't use it,
|
||||
* set zero.
|
||||
*/
|
||||
__u64 data;
|
||||
/*
|
||||
* Auxiliary error code. The sub-command may return TDX SEAMCALL
|
||||
* status code in addition to -Exxx.
|
||||
*/
|
||||
__u64 hw_error;
|
||||
};
|
||||
|
||||
KVM_TDX_CAPABILITIES
|
||||
--------------------
|
||||
:Type: vm ioctl
|
||||
:Returns: 0 on success, <0 on error
|
||||
|
||||
Return the TDX capabilities that current KVM supports with the specific TDX
|
||||
module loaded in the system. It reports what features/capabilities are allowed
|
||||
to be configured to the TDX guest.
|
||||
|
||||
- id: KVM_TDX_CAPABILITIES
|
||||
- flags: must be 0
|
||||
- data: pointer to struct kvm_tdx_capabilities
|
||||
- hw_error: must be 0
|
||||
|
||||
::
|
||||
|
||||
struct kvm_tdx_capabilities {
|
||||
__u64 supported_attrs;
|
||||
__u64 supported_xfam;
|
||||
__u64 reserved[254];
|
||||
|
||||
/* Configurable CPUID bits for userspace */
|
||||
struct kvm_cpuid2 cpuid;
|
||||
};
|
||||
|
||||
|
||||
KVM_TDX_INIT_VM
|
||||
---------------
|
||||
:Type: vm ioctl
|
||||
:Returns: 0 on success, <0 on error
|
||||
|
||||
Perform TDX specific VM initialization. This needs to be called after
|
||||
KVM_CREATE_VM and before creating any VCPUs.
|
||||
|
||||
- id: KVM_TDX_INIT_VM
|
||||
- flags: must be 0
|
||||
- data: pointer to struct kvm_tdx_init_vm
|
||||
- hw_error: must be 0
|
||||
|
||||
::
|
||||
|
||||
struct kvm_tdx_init_vm {
|
||||
__u64 attributes;
|
||||
__u64 xfam;
|
||||
__u64 mrconfigid[6]; /* sha384 digest */
|
||||
__u64 mrowner[6]; /* sha384 digest */
|
||||
__u64 mrownerconfig[6]; /* sha384 digest */
|
||||
|
||||
/* The total space for TD_PARAMS before the CPUIDs is 256 bytes */
|
||||
__u64 reserved[12];
|
||||
|
||||
/*
|
||||
* Call KVM_TDX_INIT_VM before vcpu creation, thus before
|
||||
* KVM_SET_CPUID2.
|
||||
* This configuration supersedes KVM_SET_CPUID2s for VCPUs because the
|
||||
* TDX module directly virtualizes those CPUIDs without VMM. The user
|
||||
* space VMM, e.g. qemu, should make KVM_SET_CPUID2 consistent with
|
||||
* those values. If it doesn't, KVM may have wrong idea of vCPUIDs of
|
||||
* the guest, and KVM may wrongly emulate CPUIDs or MSRs that the TDX
|
||||
* module doesn't virtualize.
|
||||
*/
|
||||
struct kvm_cpuid2 cpuid;
|
||||
};
|
||||
|
||||
|
||||
KVM_TDX_INIT_VCPU
|
||||
-----------------
|
||||
:Type: vcpu ioctl
|
||||
:Returns: 0 on success, <0 on error
|
||||
|
||||
Perform TDX specific VCPU initialization.
|
||||
|
||||
- id: KVM_TDX_INIT_VCPU
|
||||
- flags: must be 0
|
||||
- data: initial value of the guest TD VCPU RCX
|
||||
- hw_error: must be 0
|
||||
|
||||
KVM_TDX_INIT_MEM_REGION
|
||||
-----------------------
|
||||
:Type: vcpu ioctl
|
||||
:Returns: 0 on success, <0 on error
|
||||
|
||||
Initialize @nr_pages TDX guest private memory starting from @gpa with userspace
|
||||
provided data from @source_addr.
|
||||
|
||||
Note, before calling this sub command, memory attribute of the range
|
||||
[gpa, gpa + nr_pages] needs to be private. Userspace can use
|
||||
KVM_SET_MEMORY_ATTRIBUTES to set the attribute.
|
||||
|
||||
If KVM_TDX_MEASURE_MEMORY_REGION flag is specified, it also extends measurement.
|
||||
|
||||
- id: KVM_TDX_INIT_MEM_REGION
|
||||
- flags: currently only KVM_TDX_MEASURE_MEMORY_REGION is defined
|
||||
- data: pointer to struct kvm_tdx_init_mem_region
|
||||
- hw_error: must be 0
|
||||
|
||||
::
|
||||
|
||||
#define KVM_TDX_MEASURE_MEMORY_REGION (1UL << 0)
|
||||
|
||||
struct kvm_tdx_init_mem_region {
|
||||
__u64 source_addr;
|
||||
__u64 gpa;
|
||||
__u64 nr_pages;
|
||||
};
|
||||
|
||||
|
||||
KVM_TDX_FINALIZE_VM
|
||||
-------------------
|
||||
:Type: vm ioctl
|
||||
:Returns: 0 on success, <0 on error
|
||||
|
||||
Complete measurement of the initial TD contents and mark it ready to run.
|
||||
|
||||
- id: KVM_TDX_FINALIZE_VM
|
||||
- flags: must be 0
|
||||
- data: must be 0
|
||||
- hw_error: must be 0
|
||||
|
||||
|
||||
KVM_TDX_GET_CPUID
|
||||
-----------------
|
||||
:Type: vcpu ioctl
|
||||
:Returns: 0 on success, <0 on error
|
||||
|
||||
Get the CPUID values that the TDX module virtualizes for the TD guest.
|
||||
When it returns -E2BIG, the user space should allocate a larger buffer and
|
||||
retry. The minimum buffer size is updated in the nent field of the
|
||||
struct kvm_cpuid2.
|
||||
|
||||
- id: KVM_TDX_GET_CPUID
|
||||
- flags: must be 0
|
||||
- data: pointer to struct kvm_cpuid2 (in/out)
|
||||
- hw_error: must be 0 (out)
|
||||
|
||||
::
|
||||
|
||||
struct kvm_cpuid2 {
|
||||
__u32 nent;
|
||||
__u32 padding;
|
||||
struct kvm_cpuid_entry2 entries[0];
|
||||
};
|
||||
|
||||
struct kvm_cpuid_entry2 {
|
||||
__u32 function;
|
||||
__u32 index;
|
||||
__u32 flags;
|
||||
__u32 eax;
|
||||
__u32 ebx;
|
||||
__u32 ecx;
|
||||
__u32 edx;
|
||||
__u32 padding[3];
|
||||
};
|
||||
|
||||
KVM TDX creation flow
|
||||
=====================
|
||||
In addition to the standard KVM flow, new TDX ioctls need to be called. The
|
||||
control flow is as follows:
|
||||
|
||||
#. Check system wide capability
|
||||
|
||||
* KVM_CAP_VM_TYPES: Check if VM type is supported and if KVM_X86_TDX_VM
|
||||
is supported.
|
||||
|
||||
#. Create VM
|
||||
|
||||
* KVM_CREATE_VM
|
||||
* KVM_TDX_CAPABILITIES: Query TDX capabilities for creating TDX guests.
|
||||
* KVM_CHECK_EXTENSION(KVM_CAP_MAX_VCPUS): Query maximum VCPUs the TD can
|
||||
support at VM level (TDX has its own limitation on this).
|
||||
* KVM_SET_TSC_KHZ: Configure TD's TSC frequency if a different TSC frequency
|
||||
than host is desired. This is Optional.
|
||||
* KVM_TDX_INIT_VM: Pass TDX specific VM parameters.
|
||||
|
||||
#. Create VCPU
|
||||
|
||||
* KVM_CREATE_VCPU
|
||||
* KVM_TDX_INIT_VCPU: Pass TDX specific VCPU parameters.
|
||||
* KVM_SET_CPUID2: Configure TD's CPUIDs.
|
||||
* KVM_SET_MSRS: Configure TD's MSRs.
|
||||
|
||||
#. Initialize initial guest memory
|
||||
|
||||
* Prepare content of initial guest memory.
|
||||
* KVM_TDX_INIT_MEM_REGION: Add initial guest memory.
|
||||
* KVM_TDX_FINALIZE_VM: Finalize the measurement of the TDX guest.
|
||||
|
||||
#. Run VCPU
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [1] https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/documentation.html
|
||||
@@ -2420,7 +2420,12 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages);
|
||||
KVM_X86_QUIRK_FIX_HYPERCALL_INSN | \
|
||||
KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS | \
|
||||
KVM_X86_QUIRK_SLOT_ZAP_ALL | \
|
||||
KVM_X86_QUIRK_STUFF_FEATURE_MSRS)
|
||||
KVM_X86_QUIRK_STUFF_FEATURE_MSRS | \
|
||||
KVM_X86_QUIRK_IGNORE_GUEST_PAT)
|
||||
|
||||
#define KVM_X86_CONDITIONAL_QUIRKS \
|
||||
(KVM_X86_QUIRK_CD_NW_CLEARED | \
|
||||
KVM_X86_QUIRK_IGNORE_GUEST_PAT)
|
||||
|
||||
/*
|
||||
* KVM previously used a u32 field in kvm_run to indicate the hypercall was
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
#define TD_CTLS_LOCK BIT_ULL(TD_CTLS_LOCK_BIT)
|
||||
|
||||
/* TDX hypercall Leaf IDs */
|
||||
#define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000
|
||||
#define TDVMCALL_MAP_GPA 0x10001
|
||||
#define TDVMCALL_GET_QUOTE 0x10002
|
||||
#define TDVMCALL_REPORT_FATAL_ERROR 0x10003
|
||||
|
||||
@@ -585,12 +585,14 @@ enum vm_entry_failure_code {
|
||||
#define EPT_VIOLATION_ACC_WRITE_BIT 1
|
||||
#define EPT_VIOLATION_ACC_INSTR_BIT 2
|
||||
#define EPT_VIOLATION_RWX_SHIFT 3
|
||||
#define EPT_VIOLATION_EXEC_R3_LIN_BIT 6
|
||||
#define EPT_VIOLATION_GVA_IS_VALID_BIT 7
|
||||
#define EPT_VIOLATION_GVA_TRANSLATED_BIT 8
|
||||
#define EPT_VIOLATION_ACC_READ (1 << EPT_VIOLATION_ACC_READ_BIT)
|
||||
#define EPT_VIOLATION_ACC_WRITE (1 << EPT_VIOLATION_ACC_WRITE_BIT)
|
||||
#define EPT_VIOLATION_ACC_INSTR (1 << EPT_VIOLATION_ACC_INSTR_BIT)
|
||||
#define EPT_VIOLATION_RWX_MASK (VMX_EPT_RWX_MASK << EPT_VIOLATION_RWX_SHIFT)
|
||||
#define EPT_VIOLATION_EXEC_FOR_RING3_LIN (1 << EPT_VIOLATION_EXEC_R3_LIN_BIT)
|
||||
#define EPT_VIOLATION_GVA_IS_VALID (1 << EPT_VIOLATION_GVA_IS_VALID_BIT)
|
||||
#define EPT_VIOLATION_GVA_TRANSLATED (1 << EPT_VIOLATION_GVA_TRANSLATED_BIT)
|
||||
|
||||
|
||||
@@ -441,6 +441,7 @@ struct kvm_sync_regs {
|
||||
#define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6)
|
||||
#define KVM_X86_QUIRK_SLOT_ZAP_ALL (1 << 7)
|
||||
#define KVM_X86_QUIRK_STUFF_FEATURE_MSRS (1 << 8)
|
||||
#define KVM_X86_QUIRK_IGNORE_GUEST_PAT (1 << 9)
|
||||
|
||||
#define KVM_STATE_NESTED_FORMAT_VMX 0
|
||||
#define KVM_STATE_NESTED_FORMAT_SVM 1
|
||||
|
||||
+1
-1
@@ -232,7 +232,7 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
|
||||
return -(u32)fault & errcode;
|
||||
}
|
||||
|
||||
bool kvm_mmu_may_ignore_guest_pat(void);
|
||||
bool kvm_mmu_may_ignore_guest_pat(struct kvm *kvm);
|
||||
|
||||
int kvm_mmu_post_init_vm(struct kvm *kvm);
|
||||
void kvm_mmu_pre_destroy_vm(struct kvm *kvm);
|
||||
|
||||
@@ -4663,19 +4663,6 @@ out_unlock:
|
||||
}
|
||||
#endif
|
||||
|
||||
bool kvm_mmu_may_ignore_guest_pat(void)
|
||||
{
|
||||
/*
|
||||
* When EPT is enabled (shadow_memtype_mask is non-zero), and the VM
|
||||
* has non-coherent DMA (DMA doesn't snoop CPU caches), KVM's ABI is to
|
||||
* honor the memtype from the guest's PAT so that guest accesses to
|
||||
* memory that is DMA'd aren't cached against the guest's wishes. As a
|
||||
* result, KVM _may_ ignore guest PAT, whereas without non-coherent DMA,
|
||||
* KVM _always_ ignores guest PAT (when EPT is enabled).
|
||||
*/
|
||||
return shadow_memtype_mask;
|
||||
}
|
||||
|
||||
int kvm_tdp_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
|
||||
{
|
||||
#ifdef CONFIG_X86_64
|
||||
|
||||
+2
-17
@@ -37,7 +37,6 @@ u64 __read_mostly shadow_mmio_value;
|
||||
u64 __read_mostly shadow_mmio_mask;
|
||||
u64 __read_mostly shadow_mmio_access_mask;
|
||||
u64 __read_mostly shadow_present_mask;
|
||||
u64 __read_mostly shadow_memtype_mask;
|
||||
u64 __read_mostly shadow_me_value;
|
||||
u64 __read_mostly shadow_me_mask;
|
||||
u64 __read_mostly shadow_acc_track_mask;
|
||||
@@ -203,9 +202,7 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
|
||||
if (level > PG_LEVEL_4K)
|
||||
spte |= PT_PAGE_SIZE_MASK;
|
||||
|
||||
if (shadow_memtype_mask)
|
||||
spte |= kvm_x86_call(get_mt_mask)(vcpu, gfn,
|
||||
kvm_is_mmio_pfn(pfn));
|
||||
spte |= kvm_x86_call(get_mt_mask)(vcpu, gfn, kvm_is_mmio_pfn(pfn));
|
||||
if (host_writable)
|
||||
spte |= shadow_host_writable_mask;
|
||||
else
|
||||
@@ -460,13 +457,7 @@ void kvm_mmu_set_ept_masks(bool has_ad_bits, bool has_exec_only)
|
||||
/* VMX_EPT_SUPPRESS_VE_BIT is needed for W or X violation. */
|
||||
shadow_present_mask =
|
||||
(has_exec_only ? 0ull : VMX_EPT_READABLE_MASK) | VMX_EPT_SUPPRESS_VE_BIT;
|
||||
/*
|
||||
* EPT overrides the host MTRRs, and so KVM must program the desired
|
||||
* memtype directly into the SPTEs. Note, this mask is just the mask
|
||||
* of all bits that factor into the memtype, the actual memtype must be
|
||||
* dynamically calculated, e.g. to ensure host MMIO is mapped UC.
|
||||
*/
|
||||
shadow_memtype_mask = VMX_EPT_MT_MASK | VMX_EPT_IPAT_BIT;
|
||||
|
||||
shadow_acc_track_mask = VMX_EPT_RWX_MASK;
|
||||
shadow_host_writable_mask = EPT_SPTE_HOST_WRITABLE;
|
||||
shadow_mmu_writable_mask = EPT_SPTE_MMU_WRITABLE;
|
||||
@@ -518,12 +509,6 @@ void kvm_mmu_reset_all_pte_masks(void)
|
||||
shadow_x_mask = 0;
|
||||
shadow_present_mask = PT_PRESENT_MASK;
|
||||
|
||||
/*
|
||||
* For shadow paging and NPT, KVM uses PAT entry '0' to encode WB
|
||||
* memtype in the SPTEs, i.e. relies on host MTRRs to provide the
|
||||
* correct memtype (WB is the "weakest" memtype).
|
||||
*/
|
||||
shadow_memtype_mask = 0;
|
||||
shadow_acc_track_mask = 0;
|
||||
shadow_me_mask = 0;
|
||||
shadow_me_value = 0;
|
||||
|
||||
@@ -187,7 +187,6 @@ extern u64 __read_mostly shadow_mmio_value;
|
||||
extern u64 __read_mostly shadow_mmio_mask;
|
||||
extern u64 __read_mostly shadow_mmio_access_mask;
|
||||
extern u64 __read_mostly shadow_present_mask;
|
||||
extern u64 __read_mostly shadow_memtype_mask;
|
||||
extern u64 __read_mostly shadow_me_value;
|
||||
extern u64 __read_mostly shadow_me_mask;
|
||||
|
||||
|
||||
@@ -5472,6 +5472,7 @@ static __init int svm_hardware_setup(void)
|
||||
*/
|
||||
allow_smaller_maxphyaddr = !npt_enabled;
|
||||
|
||||
kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_CD_NW_CLEARED;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
||||
+438
-44
File diff suppressed because it is too large
Load Diff
@@ -203,7 +203,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)
|
||||
return;
|
||||
|
||||
if (kvm_vcpu_is_blocking(vcpu) &&
|
||||
(is_td_vcpu(vcpu) || !vmx_interrupt_blocked(vcpu)))
|
||||
((is_td_vcpu(vcpu) && tdx_interrupt_allowed(vcpu)) ||
|
||||
(!is_td_vcpu(vcpu) && !vmx_interrupt_blocked(vcpu))))
|
||||
pi_enable_wakeup_handler(vcpu);
|
||||
|
||||
/*
|
||||
|
||||
+374
-13
File diff suppressed because it is too large
Load Diff
@@ -37,6 +37,13 @@ struct kvm_tdx {
|
||||
|
||||
/* For KVM_TDX_INIT_MEM_REGION. */
|
||||
atomic64_t nr_premapped;
|
||||
|
||||
/*
|
||||
* Prevent vCPUs from TD entry to ensure SEPT zap related SEAMCALLs do
|
||||
* not contend with tdh_vp_enter() and TDCALLs.
|
||||
* Set/unset is protected with kvm->mmu_lock.
|
||||
*/
|
||||
bool wait_for_sept_zap;
|
||||
};
|
||||
|
||||
/* TDX module vCPU states */
|
||||
@@ -116,6 +123,7 @@ static __always_inline void tdvps_vmcs_check(u32 field, u8 bits)
|
||||
}
|
||||
|
||||
static __always_inline void tdvps_management_check(u64 field, u8 bits) {}
|
||||
static __always_inline void tdvps_state_non_arch_check(u64 field, u8 bits) {}
|
||||
|
||||
#define TDX_BUILD_TDVPS_ACCESSORS(bits, uclass, lclass) \
|
||||
static __always_inline u##bits td_##lclass##_read##bits(struct vcpu_tdx *tdx, \
|
||||
@@ -163,11 +171,16 @@ static __always_inline void td_##lclass##_clearbit##bits(struct vcpu_tdx *tdx, \
|
||||
tdh_vp_wr_failed(tdx, #uclass, " &= ~", field, bit, err);\
|
||||
}
|
||||
|
||||
|
||||
bool tdx_interrupt_allowed(struct kvm_vcpu *vcpu);
|
||||
int tdx_complete_emulated_msr(struct kvm_vcpu *vcpu, int err);
|
||||
|
||||
TDX_BUILD_TDVPS_ACCESSORS(16, VMCS, vmcs);
|
||||
TDX_BUILD_TDVPS_ACCESSORS(32, VMCS, vmcs);
|
||||
TDX_BUILD_TDVPS_ACCESSORS(64, VMCS, vmcs);
|
||||
|
||||
TDX_BUILD_TDVPS_ACCESSORS(8, MANAGEMENT, management);
|
||||
TDX_BUILD_TDVPS_ACCESSORS(64, STATE_NON_ARCH, state_non_arch);
|
||||
|
||||
#else
|
||||
static inline int tdx_bringup(void) { return 0; }
|
||||
@@ -183,6 +196,9 @@ struct vcpu_tdx {
|
||||
struct kvm_vcpu vcpu;
|
||||
};
|
||||
|
||||
static inline bool tdx_interrupt_allowed(struct kvm_vcpu *vcpu) { return false; }
|
||||
static inline int tdx_complete_emulated_msr(struct kvm_vcpu *vcpu, int err) { return 0; }
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,17 @@ enum tdx_tdcs_execution_control {
|
||||
TD_TDCS_EXEC_TSC_MULTIPLIER = 11,
|
||||
};
|
||||
|
||||
enum tdx_vcpu_guest_other_state {
|
||||
TD_VCPU_STATE_DETAILS_NON_ARCH = 0x100,
|
||||
};
|
||||
|
||||
#define TDX_VCPU_STATE_DETAILS_INTR_PENDING BIT_ULL(0)
|
||||
|
||||
static inline bool tdx_vcpu_state_details_intr_pending(u64 vcpu_state_details)
|
||||
{
|
||||
return !!(vcpu_state_details & TDX_VCPU_STATE_DETAILS_INTR_PENDING);
|
||||
}
|
||||
|
||||
/* @field is any of enum tdx_tdcs_execution_control */
|
||||
#define TDCS_EXEC(field) BUILD_TDX_FIELD(TD_CLASS_EXECUTION_CONTROLS, (field))
|
||||
|
||||
@@ -70,6 +81,8 @@ struct tdx_cpuid_value {
|
||||
#define TDX_TD_ATTR_KL BIT_ULL(31)
|
||||
#define TDX_TD_ATTR_PERFMON BIT_ULL(63)
|
||||
|
||||
#define TDX_EXT_EXIT_QUAL_TYPE_MASK GENMASK(3, 0)
|
||||
#define TDX_EXT_EXIT_QUAL_TYPE_PENDING_EPT_VIOLATION 6
|
||||
/*
|
||||
* TD_PARAMS is provided as an input to TDH_MNG_INIT, the size of which is 1024B.
|
||||
*/
|
||||
|
||||
+36
-7
@@ -7595,6 +7595,17 @@ int vmx_vm_init(struct kvm *kvm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool vmx_ignore_guest_pat(struct kvm *kvm)
|
||||
{
|
||||
/*
|
||||
* Non-coherent DMA devices need the guest to flush CPU properly.
|
||||
* In that case it is not possible to map all guest RAM as WB, so
|
||||
* always trust guest PAT.
|
||||
*/
|
||||
return !kvm_arch_has_noncoherent_dma(kvm) &&
|
||||
kvm_check_has_quirk(kvm, KVM_X86_QUIRK_IGNORE_GUEST_PAT);
|
||||
}
|
||||
|
||||
u8 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
|
||||
{
|
||||
/*
|
||||
@@ -7604,13 +7615,8 @@ u8 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
|
||||
if (is_mmio)
|
||||
return MTRR_TYPE_UNCACHABLE << VMX_EPT_MT_EPTE_SHIFT;
|
||||
|
||||
/*
|
||||
* Force WB and ignore guest PAT if the VM does NOT have a non-coherent
|
||||
* device attached. Letting the guest control memory types on Intel
|
||||
* CPUs may result in unexpected behavior, and so KVM's ABI is to trust
|
||||
* the guest to behave only as a last resort.
|
||||
*/
|
||||
if (!kvm_arch_has_noncoherent_dma(vcpu->kvm))
|
||||
/* Force WB if ignoring guest PAT */
|
||||
if (vmx_ignore_guest_pat(vcpu->kvm))
|
||||
return (MTRR_TYPE_WRBACK << VMX_EPT_MT_EPTE_SHIFT) | VMX_EPT_IPAT_BIT;
|
||||
|
||||
return (MTRR_TYPE_WRBACK << VMX_EPT_MT_EPTE_SHIFT);
|
||||
@@ -8428,6 +8434,8 @@ __init int vmx_hardware_setup(void)
|
||||
if (enable_ept)
|
||||
kvm_mmu_set_ept_masks(enable_ept_ad_bits,
|
||||
cpu_has_vmx_ept_execute_only());
|
||||
else
|
||||
vt_x86_ops.get_mt_mask = NULL;
|
||||
|
||||
/*
|
||||
* Setup shadow_me_value/shadow_me_mask to include MKTME KeyID
|
||||
@@ -8502,6 +8510,27 @@ __init int vmx_hardware_setup(void)
|
||||
|
||||
kvm_set_posted_intr_wakeup_handler(pi_wakeup_handler);
|
||||
|
||||
/*
|
||||
* On Intel CPUs that lack self-snoop feature, letting the guest control
|
||||
* memory types may result in unexpected behavior. So always ignore guest
|
||||
* PAT on those CPUs and map VM as writeback, not allowing userspace to
|
||||
* disable the quirk.
|
||||
*
|
||||
* On certain Intel CPUs (e.g. SPR, ICX), though self-snoop feature is
|
||||
* supported, UC is slow enough to cause issues with some older guests (e.g.
|
||||
* an old version of bochs driver uses ioremap() instead of ioremap_wc() to
|
||||
* map the video RAM, causing wayland desktop to fail to get started
|
||||
* correctly). To avoid breaking those older guests that rely on KVM to force
|
||||
* memory type to WB, provide KVM_X86_QUIRK_IGNORE_GUEST_PAT to preserve the
|
||||
* safer (for performance) default behavior.
|
||||
*
|
||||
* On top of this, non-coherent DMA devices need the guest to flush CPU
|
||||
* caches properly. This also requires honoring guest PAT, and is forced
|
||||
* independent of the quirk in vmx_ignore_guest_pat().
|
||||
*/
|
||||
if (!static_cpu_has(X86_FEATURE_SELFSNOOP))
|
||||
kvm_caps.supported_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT;
|
||||
kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,9 @@ void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
|
||||
void tdx_inject_nmi(struct kvm_vcpu *vcpu);
|
||||
void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
|
||||
u64 *info1, u64 *info2, u32 *intr_info, u32 *error_code);
|
||||
bool tdx_has_emulated_msr(u32 index);
|
||||
int tdx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr);
|
||||
int tdx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr);
|
||||
|
||||
int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp);
|
||||
|
||||
@@ -187,6 +190,9 @@ static inline void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mo
|
||||
static inline void tdx_inject_nmi(struct kvm_vcpu *vcpu) {}
|
||||
static inline void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, u64 *info1,
|
||||
u64 *info2, u32 *intr_info, u32 *error_code) {}
|
||||
static inline bool tdx_has_emulated_msr(u32 index) { return false; }
|
||||
static inline int tdx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) { return 1; }
|
||||
static inline int tdx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) { return 1; }
|
||||
|
||||
static inline int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp) { return -EOPNOTSUPP; }
|
||||
|
||||
|
||||
+13
-5
@@ -90,7 +90,6 @@
|
||||
#include "trace.h"
|
||||
|
||||
#define MAX_IO_MSRS 256
|
||||
#define KVM_MAX_MCE_BANKS 32
|
||||
|
||||
/*
|
||||
* Note, kvm_caps fields should *never* have default values, all fields must be
|
||||
@@ -4791,7 +4790,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
r = enable_pmu ? KVM_CAP_PMU_VALID_MASK : 0;
|
||||
break;
|
||||
case KVM_CAP_DISABLE_QUIRKS2:
|
||||
r = KVM_X86_VALID_QUIRKS;
|
||||
r = kvm_caps.supported_quirks;
|
||||
break;
|
||||
case KVM_CAP_X86_NOTIFY_VMEXIT:
|
||||
r = kvm_caps.has_notify_vmexit;
|
||||
@@ -6530,11 +6529,11 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
|
||||
switch (cap->cap) {
|
||||
case KVM_CAP_DISABLE_QUIRKS2:
|
||||
r = -EINVAL;
|
||||
if (cap->args[0] & ~KVM_X86_VALID_QUIRKS)
|
||||
if (cap->args[0] & ~kvm_caps.supported_quirks)
|
||||
break;
|
||||
fallthrough;
|
||||
case KVM_CAP_DISABLE_QUIRKS:
|
||||
kvm->arch.disabled_quirks = cap->args[0];
|
||||
kvm->arch.disabled_quirks |= cap->args[0] & kvm_caps.supported_quirks;
|
||||
r = 0;
|
||||
break;
|
||||
case KVM_CAP_SPLIT_IRQCHIP: {
|
||||
@@ -9784,6 +9783,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
|
||||
kvm_host.xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK);
|
||||
kvm_caps.supported_xcr0 = kvm_host.xcr0 & KVM_SUPPORTED_XCR0;
|
||||
}
|
||||
kvm_caps.supported_quirks = KVM_X86_VALID_QUIRKS;
|
||||
kvm_caps.inapplicable_quirks = KVM_X86_CONDITIONAL_QUIRKS;
|
||||
|
||||
rdmsrl_safe(MSR_EFER, &kvm_host.efer);
|
||||
|
||||
@@ -9828,6 +9829,10 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops)
|
||||
if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && tdp_mmu_enabled)
|
||||
kvm_caps.supported_vm_types |= BIT(KVM_X86_SW_PROTECTED_VM);
|
||||
|
||||
/* KVM always ignores guest PAT for shadow paging. */
|
||||
if (!tdp_enabled)
|
||||
kvm_caps.supported_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT;
|
||||
|
||||
if (!kvm_cpu_cap_has(X86_FEATURE_XSAVES))
|
||||
kvm_caps.supported_xss = 0;
|
||||
|
||||
@@ -12734,6 +12739,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||
/* Decided by the vendor code for other VM types. */
|
||||
kvm->arch.pre_fault_allowed =
|
||||
type == KVM_X86_DEFAULT_VM || type == KVM_X86_SW_PROTECTED_VM;
|
||||
kvm->arch.disabled_quirks = kvm_caps.inapplicable_quirks & kvm_caps.supported_quirks;
|
||||
|
||||
ret = kvm_page_track_init(kvm);
|
||||
if (ret)
|
||||
@@ -13561,8 +13567,10 @@ static void kvm_noncoherent_dma_assignment_start_or_stop(struct kvm *kvm)
|
||||
* due to toggling the "ignore PAT" bit. Zap all SPTEs when the first
|
||||
* (or last) non-coherent device is (un)registered to so that new SPTEs
|
||||
* with the correct "ignore guest PAT" setting are created.
|
||||
*
|
||||
* If KVM always honors guest PAT, however, there is nothing to do.
|
||||
*/
|
||||
if (kvm_mmu_may_ignore_guest_pat())
|
||||
if (kvm_check_has_quirk(kvm, KVM_X86_QUIRK_IGNORE_GUEST_PAT))
|
||||
kvm_zap_gfn_range(kvm, gpa_to_gfn(0), gpa_to_gfn(~0ULL));
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user