KVM TDX basic feature support
Subsystems (8)
PATCH v19 90/130 KVM: x86: Assume timer IRQ was injected if APIC state is proteced
In reply to [email protected]From: Sean Christopherson <[email protected]>
If APIC state is protected, i.e. the vCPU is a TDX guest, assume a timer IRQ was injected when deciding whether or not to busy wait in the "timer advanced" path. The "real" vIRR is not readable/writable, so trying to query for a pending timer IRQ will return garbage.
Note, TDX can scour the PIR if it wants to be more precise and skip the "wait" call entirely.
--- arch/x86/kvm/lapic.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
arch/x86/kvm/lapic.c +10 -1
@@ -1774,8 +1774,17 @@ static void apic_update_lvtt(struct kvm_lapic *apic)static bool lapic_timer_int_injected (struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic; -u32 reg =kvm_lapic_get_reg (apic,APIC_LVTT ); +u32 reg; +/* +* Assume a timer IRQ was "injected" if the APIC is protected. KVM's +* copy of the vIRR is bogus, it's the responsibility of the caller to +* precisely check whether or not a timer IRQ is pending. +*/ +if (apic->guest_apic_protected) +return true; + + reg =kvm_lapic_get_reg (apic,APIC_LVTT );if (kvm_apic_hw_enabled (apic)) {int vec = reg &APIC_VECTOR_MASK ;void *bitmap = apic->regs +APIC_ISR ;
Signature · 1 line
2.25.1
PATCH v19 91/130 KVM: TDX: remove use of struct vcpu_vmx from posted_interrupt.c
In reply to [email protected]From: Isaku Yamahata <[email protected]>
As TDX will use posted_interrupt.c, the use of struct vcpu_vmx is a blocker. Because the members of struct pi_desc pi_desc and struct list_head pi_wakeup_list are only used in posted_interrupt.c, introduce common structure, struct vcpu_pi, make vcpu_vmx and vcpu_tdx has same layout in the top of structure.
To minimize the diff size, avoid code conversion like, vmx->pi_desc => vmx->common->pi_desc. Instead add compile time check if the layout is expected.
--- arch/x86/kvm/vmx/posted_intr.c | 41 ++++++++++++++++++++++++++-------- arch/x86/kvm/vmx/posted_intr.h | 11 +++++++++ arch/x86/kvm/vmx/tdx.c | 1 + arch/x86/kvm/vmx/tdx.h | 8 +++++++ arch/x86/kvm/vmx/vmx.h | 14 +++++++----- 5 files changed, 60 insertions(+), 15 deletions(-)
arch/x86/kvm/vmx/posted_intr.c +32 -9
@@ -11,6 +11,7 @@#include "posted_intr.h" #include "trace.h" #include "vmx.h" +#include "tdx.h" /* * Maintain a per-CPU list of vCPUs that need to be awakened by wakeup_handler()
@@ -31,9 +32,29 @@ static DEFINE_PER_CPU(struct list_head, wakeup_vcpus_on_cpu);*/ static DEFINE_PER_CPU(raw_spinlock_t, wakeup_vcpus_on_cpu_lock); +/* +* The layout of the head of struct vcpu_vmx and struct vcpu_tdx must match with +* struct vcpu_pi. +*/ +static_assert (offsetof(struct vcpu_pi , pi_desc) == + offsetof(struct vcpu_vmx , pi_desc)); +static_assert (offsetof(struct vcpu_pi , pi_wakeup_list) == + offsetof(struct vcpu_vmx , pi_wakeup_list)); +#ifdef CONFIG_INTEL_TDX_HOST +static_assert (offsetof(struct vcpu_pi , pi_desc) == + offsetof(struct vcpu_tdx , pi_desc)); +static_assert (offsetof(struct vcpu_pi , pi_wakeup_list) == + offsetof(struct vcpu_tdx , pi_wakeup_list)); +#endif + +static inline struct vcpu_pi *vcpu_to_pi (struct kvm_vcpu *vcpu) +{ +return (struct vcpu_pi *)vcpu; +} +static inline struct pi_desc *vcpu_to_pi_desc (struct kvm_vcpu *vcpu) { -return &(to_vmx (vcpu)->pi_desc); +return &vcpu_to_pi (vcpu)->pi_desc; }static int pi_try_set_control (struct pi_desc *pi_desc,u64 *pold,u64 new)
@@ -52,8 +73,8 @@ static int pi_try_set_control(struct pi_desc *pi_desc, u64 *pold, u64 new)void vmx_vcpu_pi_load (struct kvm_vcpu *vcpu,int cpu) { -struct pi_desc *pi_desc =vcpu_to_pi_desc (vcpu); -struct vcpu_vmx *vmx =to_vmx (vcpu); +struct vcpu_pi *vcpu_pi =vcpu_to_pi (vcpu); +struct pi_desc *pi_desc = &vcpu_pi->pi_desc;struct pi_desc old, new;unsigned long flags;unsigned int dest;
@@ -90,7 +111,7 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)*/ if (pi_desc->nv ==POSTED_INTR_WAKEUP_VECTOR ) {raw_spin_lock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); -list_del (&vmx->pi_wakeup_list); +list_del (&vcpu_pi->pi_wakeup_list);raw_spin_unlock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); }
@@ -145,15 +166,15 @@ static bool vmx_can_use_vtd_pi(struct kvm *kvm)*/ static void pi_enable_wakeup_handler (struct kvm_vcpu *vcpu) { -struct pi_desc *pi_desc =vcpu_to_pi_desc (vcpu); -struct vcpu_vmx *vmx =to_vmx (vcpu); +struct vcpu_pi *vcpu_pi =vcpu_to_pi (vcpu); +struct pi_desc *pi_desc = &vcpu_pi->pi_desc;struct pi_desc old, new;unsigned long flags;local_irq_save (flags);raw_spin_lock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); -list_add_tail (&vmx->pi_wakeup_list, +list_add_tail (&vcpu_pi->pi_wakeup_list, &per_cpu (wakeup_vcpus_on_cpu, vcpu->cpu));raw_spin_unlock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu));
@@ -190,7 +211,8 @@ static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu)* notification vector is switched to the one that calls * back to the pi_wakeup_handler() function. */ -return vmx_can_use_ipiv (vcpu) ||vmx_can_use_vtd_pi (vcpu->kvm); +return (vmx_can_use_ipiv (vcpu) && !is_td_vcpu (vcpu)) || +vmx_can_use_vtd_pi (vcpu->kvm); }void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu)
@@ -200,7 +222,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)if (!vmx_needs_pi_wakeup (vcpu))return ; -if (kvm_vcpu_is_blocking (vcpu) && !vmx_interrupt_blocked (vcpu)) +if (kvm_vcpu_is_blocking (vcpu) && + (is_td_vcpu (vcpu) || !vmx_interrupt_blocked (vcpu)))pi_enable_wakeup_handler (vcpu);/*
arch/x86/kvm/vmx/posted_intr.h +11 -0
@@ -94,6 +94,17 @@ static inline bool pi_test_sn(struct pi_desc *pi_desc) (unsigned long *)&pi_desc->control); } +struct vcpu_pi { +struct kvm_vcpu vcpu; + +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here common layout betwwn vcpu_vmx and vcpu_tdx. */ +}; +void vmx_vcpu_pi_load (struct kvm_vcpu *vcpu,int cpu);void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu);void pi_wakeup_handler (void );
arch/x86/kvm/vmx/tdx.c +1 -0
@@ -584,6 +584,7 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu)fpstate_set_confidential (&vcpu->arch.guest_fpu); vcpu->arch.apic->guest_apic_protected = true; +INIT_LIST_HEAD (&tdx->pi_wakeup_list); vcpu->arch.efer =EFER_SCE |EFER_LME |EFER_LMA |EFER_NX ;
arch/x86/kvm/vmx/tdx.h +8 -0
@@ -4,6 +4,7 @@#ifdef CONFIG_INTEL_TDX_HOST +#include "posted_intr.h" #include "pmu_intel.h" #include "tdx_ops.h"
@@ -69,6 +70,13 @@ union tdx_exit_reason {struct vcpu_tdx {struct kvm_vcpu vcpu; +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here same layout to struct vcpu_pi. */ +unsigned long tdvpr_pa;unsigned long *tdvpx_pa;bool td_vcpu_created;
arch/x86/kvm/vmx/vmx.h +8 -6
@@ -235,6 +235,14 @@ struct nested_vmx {struct vcpu_vmx {struct kvm_vcpu vcpu; + +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here same layout to struct vcpu_pi. */ +u8 fail;u8 x2apic_msr_bitmap_mode;
@@ -304,12 +312,6 @@ struct vcpu_vmx {union vmx_exit_reason exit_reason; -/* Posted interrupt descriptor */ -struct pi_desc pi_desc; - -/* Used if this vCPU is waiting for PI notification wakeup. */ -struct list_head pi_wakeup_list; -/* Support for a guest hypervisor (nested VMX) */ struct nested_vmx nested;
Signature · 1 line
2.25.1
Re: [PATCH v19 091/130] KVM: TDX: remove use of struct vcpu_vmx from posted_interrupt.c
Quoted 4 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
As TDX will use
posted_interrupt.c, the use of structvcpu_vmxis a blocker. Because the members of
Extra "of"
Quoted 127 lines
struct
pi_descpi_descand structlist_headpi_wakeup_listare only used inposted_interrupt.c, introduce common structure, structvcpu_pi, makevcpu_vmxandvcpu_tdxhas same layout in the top of structure.To minimize the diff size, avoid code conversion like,
vmx->pi_desc=>vmx->common->pi_desc. Instead add compile time check if the layout is expected.Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/vmx/posted_intr.c | 41 ++++++++++++++++++++++++++-------- arch/x86/kvm/vmx/posted_intr.h | 11 +++++++++ arch/x86/kvm/vmx/tdx.c | 1 + arch/x86/kvm/vmx/tdx.h | 8 +++++++ arch/x86/kvm/vmx/vmx.h | 14 +++++++----- 5 files changed, 60 insertions(+), 15 deletions(-)arch/x86/kvm/vmx/posted_intr.c +32
-9@@ -11,6 +11,7 @@#include "posted_intr.h" #include "trace.h" #include "vmx.h" +#include "tdx.h" /* * Maintain a per-CPU list of vCPUs that need to be awakened by wakeup_handler() @@ -31,9 +32,29 @@ static DEFINE_PER_CPU(struct list_head, wakeup_vcpus_on_cpu);*/ static DEFINE_PER_CPU(raw_spinlock_t, wakeup_vcpus_on_cpu_lock); +/* +* The layout of the head of struct vcpu_vmx and struct vcpu_tdx must match with +* struct vcpu_pi. +*/ +static_assert (offsetof(struct vcpu_pi , pi_desc) == + offsetof(struct vcpu_vmx , pi_desc)); +static_assert (offsetof(struct vcpu_pi , pi_wakeup_list) == + offsetof(struct vcpu_vmx , pi_wakeup_list)); +#ifdef CONFIG_INTEL_TDX_HOST +static_assert (offsetof(struct vcpu_pi , pi_desc) == + offsetof(struct vcpu_tdx , pi_desc)); +static_assert (offsetof(struct vcpu_pi , pi_wakeup_list) == + offsetof(struct vcpu_tdx , pi_wakeup_list)); +#endif + +static inline struct vcpu_pi *vcpu_to_pi (struct kvm_vcpu *vcpu) +{ +return (struct vcpu_pi *)vcpu; +} +static inline struct pi_desc *vcpu_to_pi_desc (struct kvm_vcpu *vcpu) { -return &(to_vmx (vcpu)->pi_desc); +return &vcpu_to_pi (vcpu)->pi_desc; }static int pi_try_set_control (struct pi_desc *pi_desc,u64 *pold,u64 new)@@ -52,8 +73,8 @@ static int pi_try_set_control(struct pi_desc *pi_desc, u64 *pold, u64 new)void vmx_vcpu_pi_load (struct kvm_vcpu *vcpu,int cpu) { -struct pi_desc *pi_desc =vcpu_to_pi_desc (vcpu); -struct vcpu_vmx *vmx =to_vmx (vcpu); +struct vcpu_pi *vcpu_pi =vcpu_to_pi (vcpu); +struct pi_desc *pi_desc = &vcpu_pi->pi_desc;struct pi_desc old, new;unsigned long flags;unsigned int dest;@@ -90,7 +111,7 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)*/ if (pi_desc->nv ==POSTED_INTR_WAKEUP_VECTOR ) {raw_spin_lock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); -list_del (&vmx->pi_wakeup_list); +list_del (&vcpu_pi->pi_wakeup_list);raw_spin_unlock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); }@@ -145,15 +166,15 @@ static bool vmx_can_use_vtd_pi(struct kvm *kvm)*/ static void pi_enable_wakeup_handler (struct kvm_vcpu *vcpu) { -struct pi_desc *pi_desc =vcpu_to_pi_desc (vcpu); -struct vcpu_vmx *vmx =to_vmx (vcpu); +struct vcpu_pi *vcpu_pi =vcpu_to_pi (vcpu); +struct pi_desc *pi_desc = &vcpu_pi->pi_desc;struct pi_desc old, new;unsigned long flags;local_irq_save (flags);raw_spin_lock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); -list_add_tail (&vmx->pi_wakeup_list, +list_add_tail (&vcpu_pi->pi_wakeup_list, &per_cpu (wakeup_vcpus_on_cpu, vcpu->cpu));raw_spin_unlock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu));@@ -190,7 +211,8 @@ static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu)* notification vector is switched to the one that calls * back to the pi_wakeup_handler() function. */ -return vmx_can_use_ipiv (vcpu) ||vmx_can_use_vtd_pi (vcpu->kvm); +return (vmx_can_use_ipiv (vcpu) && !is_td_vcpu (vcpu)) || +vmx_can_use_vtd_pi (vcpu->kvm); }void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu)@@ -200,7 +222,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)if (!vmx_needs_pi_wakeup (vcpu))return ; -if (kvm_vcpu_is_blocking (vcpu) && !vmx_interrupt_blocked (vcpu)) +if (kvm_vcpu_is_blocking (vcpu) && + (is_td_vcpu (vcpu) || !vmx_interrupt_blocked (vcpu)))pi_enable_wakeup_handler (vcpu);/* arch/x86/kvm/vmx/posted_intr.h +0
-0@@ -94,6 +94,17 @@ static inline bool pi_test_sn(struct pi_desc *pi_desc) (unsigned long *)&pi_desc->control); }
arch/x86/kvm/vmx/posted_intr.h static inline bool pi_test_sn(struct pi_desc *pi_desc) · line 105
Quoted 10 lines
arch/x86/kvm/vmx/posted_intr.h +9
-0+struct vcpu_pi { +struct kvm_vcpu vcpu; + +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here common layout betwwn vcpu_vmx and vcpu_tdx. */
s/betwwn/between
Also, in pi_wakeup_handler(), it is still using struct vcpu_vmx, but it could be vcpu_tdx.
Functionally it is OK, however, since you have added vcpu_pi, should it use vcpu_pi instead of vcpu_vmx in pi_wakeup_handler()?
Quoted 75 lines
arch/x86/kvm/vmx/posted_intr.h +2
-0+}; +void vmx_vcpu_pi_load (struct kvm_vcpu *vcpu,int cpu);void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu);void pi_wakeup_handler (void );arch/x86/kvm/vmx/tdx.c +1
-0@@ -584,6 +584,7 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu)fpstate_set_confidential (&vcpu->arch.guest_fpu); vcpu->arch.apic->guest_apic_protected = true; +INIT_LIST_HEAD (&tdx->pi_wakeup_list); vcpu->arch.efer =EFER_SCE |EFER_LME |EFER_LMA |EFER_NX ;arch/x86/kvm/vmx/tdx.h +8
-0@@ -4,6 +4,7 @@#ifdef CONFIG_INTEL_TDX_HOST +#include "posted_intr.h" #include "pmu_intel.h" #include "tdx_ops.h" @@ -69,6 +70,13 @@ union tdx_exit_reason {struct vcpu_tdx {struct kvm_vcpu vcpu; +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here same layout to struct vcpu_pi. */ +unsigned long tdvpr_pa;unsigned long *tdvpx_pa;bool td_vcpu_created;arch/x86/kvm/vmx/vmx.h +8
-6@@ -235,6 +235,14 @@ struct nested_vmx {struct vcpu_vmx {struct kvm_vcpu vcpu; + +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here same layout to struct vcpu_pi. */ +u8 fail;u8 x2apic_msr_bitmap_mode;@@ -304,12 +312,6 @@ struct vcpu_vmx {union vmx_exit_reason exit_reason; -/* Posted interrupt descriptor */ -struct pi_desc pi_desc; - -/* Used if this vCPU is waiting for PI notification wakeup. */ -struct list_head pi_wakeup_list; -/* Support for a guest hypervisor (nested VMX) */ struct nested_vmx nested;
Re: [PATCH v19 091/130] KVM: TDX: remove use of struct vcpu_vmx from posted_interrupt.c
Quoted 138 lines · Binbin Wu
Quoted 4 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
As TDX will use
posted_interrupt.c, the use of structvcpu_vmxis a blocker. Because the members ofExtra "of"
Quoted 115 lines
struct
pi_descpi_descand structlist_headpi_wakeup_listare only used inposted_interrupt.c, introduce common structure, structvcpu_pi, makevcpu_vmxandvcpu_tdxhas same layout in the top of structure.To minimize the diff size, avoid code conversion like,
vmx->pi_desc=>vmx->common->pi_desc. Instead add compile time check if the layout is expected.Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/vmx/posted_intr.c | 41 ++++++++++++++++++++++++++-------- arch/x86/kvm/vmx/posted_intr.h | 11 +++++++++ arch/x86/kvm/vmx/tdx.c | 1 + arch/x86/kvm/vmx/tdx.h | 8 +++++++ arch/x86/kvm/vmx/vmx.h | 14 +++++++----- 5 files changed, 60 insertions(+), 15 deletions(-)arch/x86/kvm/vmx/posted_intr.c +32
-9@@ -11,6 +11,7 @@#include "posted_intr.h" #include "trace.h" #include "vmx.h" +#include "tdx.h" /* * Maintain a per-CPU list of vCPUs that need to be awakened by wakeup_handler() @@ -31,9 +32,29 @@ static DEFINE_PER_CPU(struct list_head, wakeup_vcpus_on_cpu);*/ static DEFINE_PER_CPU(raw_spinlock_t, wakeup_vcpus_on_cpu_lock); +/* +* The layout of the head of struct vcpu_vmx and struct vcpu_tdx must match with +* struct vcpu_pi. +*/ +static_assert (offsetof(struct vcpu_pi , pi_desc) == + offsetof(struct vcpu_vmx , pi_desc)); +static_assert (offsetof(struct vcpu_pi , pi_wakeup_list) == + offsetof(struct vcpu_vmx , pi_wakeup_list)); +#ifdef CONFIG_INTEL_TDX_HOST +static_assert (offsetof(struct vcpu_pi , pi_desc) == + offsetof(struct vcpu_tdx , pi_desc)); +static_assert (offsetof(struct vcpu_pi , pi_wakeup_list) == + offsetof(struct vcpu_tdx , pi_wakeup_list)); +#endif + +static inline struct vcpu_pi *vcpu_to_pi (struct kvm_vcpu *vcpu) +{ +return (struct vcpu_pi *)vcpu; +} +static inline struct pi_desc *vcpu_to_pi_desc (struct kvm_vcpu *vcpu) { -return &(to_vmx (vcpu)->pi_desc); +return &vcpu_to_pi (vcpu)->pi_desc; }static int pi_try_set_control (struct pi_desc *pi_desc,u64 *pold,u64 new)@@ -52,8 +73,8 @@ static int pi_try_set_control(struct pi_desc *pi_desc, u64 *pold, u64 new)void vmx_vcpu_pi_load (struct kvm_vcpu *vcpu,int cpu) { -struct pi_desc *pi_desc =vcpu_to_pi_desc (vcpu); -struct vcpu_vmx *vmx =to_vmx (vcpu); +struct vcpu_pi *vcpu_pi =vcpu_to_pi (vcpu); +struct pi_desc *pi_desc = &vcpu_pi->pi_desc;struct pi_desc old, new;unsigned long flags;unsigned int dest;@@ -90,7 +111,7 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)*/ if (pi_desc->nv ==POSTED_INTR_WAKEUP_VECTOR ) {raw_spin_lock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); -list_del (&vmx->pi_wakeup_list); +list_del (&vcpu_pi->pi_wakeup_list);raw_spin_unlock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); }@@ -145,15 +166,15 @@ static bool vmx_can_use_vtd_pi(struct kvm *kvm)*/ static void pi_enable_wakeup_handler (struct kvm_vcpu *vcpu) { -struct pi_desc *pi_desc =vcpu_to_pi_desc (vcpu); -struct vcpu_vmx *vmx =to_vmx (vcpu); +struct vcpu_pi *vcpu_pi =vcpu_to_pi (vcpu); +struct pi_desc *pi_desc = &vcpu_pi->pi_desc;struct pi_desc old, new;unsigned long flags;local_irq_save (flags);raw_spin_lock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu)); -list_add_tail (&vmx->pi_wakeup_list, +list_add_tail (&vcpu_pi->pi_wakeup_list, &per_cpu (wakeup_vcpus_on_cpu, vcpu->cpu));raw_spin_unlock (&per_cpu (wakeup_vcpus_on_cpu_lock, vcpu->cpu));@@ -190,7 +211,8 @@ static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu)* notification vector is switched to the one that calls * back to the pi_wakeup_handler() function. */ -return vmx_can_use_ipiv (vcpu) ||vmx_can_use_vtd_pi (vcpu->kvm); +return (vmx_can_use_ipiv (vcpu) && !is_td_vcpu (vcpu)) || +vmx_can_use_vtd_pi (vcpu->kvm); }void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu)@@ -200,7 +222,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)if (!vmx_needs_pi_wakeup (vcpu))return ; -if (kvm_vcpu_is_blocking (vcpu) && !vmx_interrupt_blocked (vcpu)) +if (kvm_vcpu_is_blocking (vcpu) && + (is_td_vcpu (vcpu) || !vmx_interrupt_blocked (vcpu)))pi_enable_wakeup_handler (vcpu);/* arch/x86/kvm/vmx/posted_intr.h +0
-0@@ -94,6 +94,17 @@ static inline bool pi_test_sn(struct pi_desc *pi_desc) (unsigned long *)&pi_desc->control);arch/x86/kvm/vmx/posted_intr.h static inline bool pi_test_sn(struct pi_desc *pi_desc) · line 104
Quoted 10 lines
arch/x86/kvm/vmx/posted_intr.h +9
-0} +struct vcpu_pi { +struct kvm_vcpu vcpu; + +/* Posted interrupt descriptor */ +struct pi_desc pi_desc; + +/* Used if this vCPU is waiting for PI notification wakeup. */ +struct list_head pi_wakeup_list; +/* Until here common layout betwwn vcpu_vmx and vcpu_tdx. */ s/betwwn/between
Quoted 5 lines
Also, in
pi_wakeup_handler(), it is still using structvcpu_vmx, but it could
bevcpu_tdx.
Functionally it is OK, however, since you have addedvcpu_pi, should it usevcpu_piinstead ofvcpu_vmxinpi_wakeup_handler()?
Makes sense.
arch/x86/kvm/vmx/posted_intr.c +4 -5
@@ -243,13 +243,13 @@ void pi_wakeup_handler(void)int cpu =smp_processor_id ();struct list_head *wakeup_list = &per_cpu (wakeup_vcpus_on_cpu, cpu);raw_spinlock_t *spinlock = &per_cpu (wakeup_vcpus_on_cpu_lock, cpu); -struct vcpu_vmx *vmx; +struct vcpu_pi *pi;raw_spin_lock (spinlock); -list_for_each_entry (vmx, wakeup_list, pi_wakeup_list) { +list_for_each_entry (pi, wakeup_list, pi_wakeup_list) { -if (pi_test_on (&vmx->pi_desc)) -kvm_vcpu_wake_up (&vmx->vcpu); +if (pi_test_on (&pi->pi_desc)) +kvm_vcpu_wake_up (&pi->vcpu); } --
Isaku Yamahata <[email protected]>
Re: [PATCH v19 091/130] KVM: TDX: remove use of struct vcpu_vmx from posted_interrupt.c
In reply to [email protected]arch/x86/kvm/vmx/posted_intr.c static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu) · line 215
Quoted 7 lines · On Mon, Feb 26, 2024 at 12:26:33AM -0800, [email protected] wrote:
arch/x86/kvm/vmx/posted_intr.c +2
-1@@ -190,7 +211,8 @@ static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu)* notification vector is switched to the one that calls * back to the pi_wakeup_handler() function. */ -return vmx_can_use_ipiv (vcpu) ||vmx_can_use_vtd_pi (vcpu->kvm); +return (vmx_can_use_ipiv (vcpu) && !is_td_vcpu (vcpu)) || +vmx_can_use_vtd_pi (vcpu->kvm);
It is better to separate this functional change from the code refactoring.
arch/x86/kvm/vmx/posted_intr.c void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu) · line 226
Quoted 10 lines
arch/x86/kvm/vmx/posted_intr.c +2
-1}void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu)@@ -200,7 +222,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)if (!vmx_needs_pi_wakeup (vcpu))return ; -if (kvm_vcpu_is_blocking (vcpu) && !vmx_interrupt_blocked (vcpu)) +if (kvm_vcpu_is_blocking (vcpu) && + (is_td_vcpu (vcpu) || !vmx_interrupt_blocked (vcpu)))
Ditto.
This looks incorrect to me. here we assume interrupt is always enabled for TD. But on TDVMCALL(HLT), the guest tells KVM if hlt is called with interrupt disabled. KVM can just check that interrupt status passed from the guest.
Quoted 2 lines
arch/x86/kvm/vmx/posted_intr.c +0
-0pi_enable_wakeup_handler (vcpu);
Re: [PATCH v19 091/130] KVM: TDX: remove use of struct vcpu_vmx from posted_interrupt.c
Quoted 10 lines · Chao Gao
arch/x86/kvm/vmx/posted_intr.c static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu) · line 215
Quoted 7 lines · On Mon, Feb 26, 2024 at 12:26:33AM -0800, [email protected] wrote:
arch/x86/kvm/vmx/posted_intr.c +2
-1@@ -190,7 +211,8 @@ static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu)* notification vector is switched to the one that calls * back to the pi_wakeup_handler() function. */ -return vmx_can_use_ipiv (vcpu) ||vmx_can_use_vtd_pi (vcpu->kvm); +return (vmx_can_use_ipiv (vcpu) && !is_td_vcpu (vcpu)) || +vmx_can_use_vtd_pi (vcpu->kvm);It is better to separate this functional change from the code refactoring.
Agreed. Let's split this patch.
Quoted 13 lines
arch/x86/kvm/vmx/posted_intr.c void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu) · line 226
Quoted 10 lines
arch/x86/kvm/vmx/posted_intr.c +2
-1}void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu)@@ -200,7 +222,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)if (!vmx_needs_pi_wakeup (vcpu))return ; -if (kvm_vcpu_is_blocking (vcpu) && !vmx_interrupt_blocked (vcpu)) +if (kvm_vcpu_is_blocking (vcpu) && + (is_td_vcpu (vcpu) || !vmx_interrupt_blocked (vcpu)))Ditto.
Quoted 3 lines
This looks incorrect to me. here we assume interrupt is always enabled for TD. But on TDVMCALL(HLT), the guest tells KVM if hlt is called with interrupt disabled. KVM can just check that interrupt status passed from the guest.
That's true. We can complicate this function and HLT emulation. But I don't think it's worthwhile because HLT with interrupt masked is rare. Only for CPU online.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 091/130] KVM: TDX: remove use of struct vcpu_vmx from posted_interrupt.c
Quoted 15 lines · Isaku Yamahata
Quoted 12 lines · Chao Gao
arch/x86/kvm/vmx/posted_intr.c void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu) · line 226
Quoted 10 lines
}
void
vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)arch/x86/kvm/vmx/posted_intr.c +2
-1@@ -200,7 +222,8 @@ void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)if (!vmx_needs_pi_wakeup (vcpu))return ; -if (kvm_vcpu_is_blocking (vcpu) && !vmx_interrupt_blocked (vcpu)) +if (kvm_vcpu_is_blocking (vcpu) && + (is_td_vcpu (vcpu) || !vmx_interrupt_blocked (vcpu)))Ditto.
Quoted 6 lines
Quoted 3 lines
This looks incorrect to me. here we assume interrupt is always enabled for TD. But on TDVMCALL(HLT), the guest tells KVM if hlt is called with interrupt disabled. KVM can just check that interrupt status passed from the guest.
That's true. We can complicate this function and HLT emulation. But I don't think it's worthwhile because HLT with interrupt masked is rare. Only for CPU online.
Then, it's better to add some comments?
PATCH v19 92/130 KVM: TDX: Implement interrupt injection
In reply to [email protected]From: Isaku Yamahata <[email protected]>
TDX supports interrupt inject into vcpu with posted interrupt. Wire up the corresponding kvm x86 operations to posted interrupt. Move kvm_vcpu_trigger_posted_interrupt() from vmx.c to common.h to share the code.
VMX can inject interrupt by setting interrupt information field, VM_ENTRY_INTR_INFO_FIELD, of VMCS. TDX supports interrupt injection only by posted interrupt. Ignore the execution path to access VM_ENTRY_INTR_INFO_FIELD.
As cpu state is protected and apicv is enabled for the TDX guest, VMM can inject interrupt by updating posted interrupt descriptor. Treat interrupt can be injected always.
--- arch/x86/kvm/vmx/common.h | 71 ++++++++++++++++++++++++++ arch/x86/kvm/vmx/main.c | 93 ++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/posted_intr.c | 2 +- arch/x86/kvm/vmx/posted_intr.h | 2 + arch/x86/kvm/vmx/tdx.c | 25 +++++++++ arch/x86/kvm/vmx/vmx.c | 67 +----------------------- arch/x86/kvm/vmx/x86_ops.h | 7 ++- 7 files changed, 190 insertions(+), 77 deletions(-)
arch/x86/kvm/vmx/common.h +71 -0
@@ -4,6 +4,7 @@#include <linux/kvm_host.h> +#include "posted_intr.h" #include "mmu.h" static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa,
@@ -30,4 +31,74 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, return kvm_mmu_page_fault(vcpu, gpa, error_code, NULL, 0); } +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); +} + +/* +* Send interrupt to vcpu via posted interrupt way. +* 1. If target vcpu is running(non-root mode), send posted interrupt +* notification to vcpu and hardware will sync PIR to vIRR atomically. +* 2. If target vcpu isn't running(root mode), kick it to pick up the +* interrupt from PIR in next vmentry. +*/ +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 ); +} +#endif /* __KVM_X86_VMX_COMMON_H */
arch/x86/kvm/vmx/main.c +84 -9
127 lines not on this page · raw message
arch/x86/kvm/vmx/posted_intr.c +1 -1
@@ -52,7 +52,7 @@ static inline struct vcpu_pi *vcpu_to_pi(struct kvm_vcpu *vcpu)return (struct vcpu_pi *)vcpu; } -static inline struct pi_desc *vcpu_to_pi_desc (struct kvm_vcpu *vcpu) +struct pi_desc *vcpu_to_pi_desc (struct kvm_vcpu *vcpu) {return &vcpu_to_pi (vcpu)->pi_desc; }
arch/x86/kvm/vmx/posted_intr.h +2 -0
@@ -105,6 +105,8 @@ struct vcpu_pi {/* Until here common layout betwwn vcpu_vmx and vcpu_tdx. */ }; +struct pi_desc *vcpu_to_pi_desc (struct kvm_vcpu *vcpu); +void vmx_vcpu_pi_load (struct kvm_vcpu *vcpu,int cpu);void vmx_vcpu_pi_put (struct kvm_vcpu *vcpu);void pi_wakeup_handler (void );
arch/x86/kvm/vmx/tdx.c +25 -0
@@ -7,6 +7,7 @@#include "capabilities.h" #include "x86_ops.h" +#include "common.h" #include "mmu.h" #include "tdx_arch.h" #include "tdx.h"
@@ -603,6 +604,9 @@ int tdx_vcpu_create(struct kvm_vcpu *vcpu) tdx->host_state_need_save = true; tdx->host_state_need_restore = false; + tdx->pi_desc.nv =POSTED_INTR_VECTOR ; + tdx->pi_desc.sn =1 ; +return 0 ; }
@@ -610,6 +614,7 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) {struct vcpu_tdx *tdx =to_tdx (vcpu); +vmx_vcpu_pi_load (vcpu, cpu);if (vcpu->cpu == cpu)return ;
@@ -848,6 +853,12 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)trace_kvm_entry (vcpu); +if (pi_test_on (&tdx->pi_desc)) { + apic->send_IPI_self (POSTED_INTR_VECTOR ); + +kvm_wait_lapic_expire (vcpu); + } +tdx_vcpu_enter_exit (tdx);tdx_user_return_update_cache (vcpu);
@@ -1213,6 +1224,16 @@ static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn,return tdx_sept_drop_private_spte (kvm, gfn, level, pfn); } +void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode, +int trig_mode,int vector) +{ +struct kvm_vcpu *vcpu = apic->vcpu; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +/* TDX supports only posted interrupt. No lapic emulation. */ +__vmx_deliver_posted_interrupt (vcpu, &tdx->pi_desc, vector); +} +static int tdx_get_capabilities (struct kvm_tdx_cmd *cmd) {struct kvm_tdx_capabilities __user *user_caps;
@@ -1972,6 +1993,10 @@ int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp)if (ret)return ret; +td_vmcs_write16 (tdx,POSTED_INTR_NV ,POSTED_INTR_VECTOR ); +td_vmcs_write64 (tdx,POSTED_INTR_DESC_ADDR ,__pa (&tdx->pi_desc)); +td_vmcs_setbit32 (tdx,PIN_BASED_VM_EXEC_CONTROL ,PIN_BASED_POSTED_INTR ); + tdx->initialized = true;return 0 ; }
arch/x86/kvm/vmx/vmx.c +1 -66
85 lines not on this page · raw message
arch/x86/kvm/vmx/x86_ops.h +6 -1
@@ -58,7 +58,6 @@ int vmx_check_intercept(struct kvm_vcpu *vcpu,bool vmx_apic_init_signal_blocked (struct kvm_vcpu *vcpu);void vmx_migrate_timers (struct kvm_vcpu *vcpu);void vmx_set_virtual_apic_mode (struct kvm_vcpu *vcpu); -void vmx_apicv_pre_state_restore (struct kvm_vcpu *vcpu);bool vmx_check_apicv_inhibit_reasons (enum kvm_apicv_inhibit reason);void vmx_hwapic_irr_update (struct kvm_vcpu *vcpu,int max_irr);void vmx_hwapic_isr_update (int max_isr);
@@ -158,6 +157,9 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu);bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu);u8 tdx_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio); +void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode, +int trig_mode,int vector); +int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp);void tdx_flush_tlb (struct kvm_vcpu *vcpu);
@@ -198,6 +200,9 @@ static inline void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) {}static inline bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu) {return false; }static inline u8 tdx_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {return 0 ; } +static inline void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode, +int trig_mode,int vector) {} +static inline int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp) {return -EOPNOTSUPP ; }static inline void tdx_flush_tlb (struct kvm_vcpu *vcpu) {}
Signature · 1 line
2.25.1
Re: [PATCH v19 092/130] KVM: TDX: Implement interrupt injection
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 859
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c +4
-0@@ -848,6 +853,12 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)trace_kvm_entry (vcpu); +if (pi_test_on (&tdx->pi_desc)) { + apic->send_IPI_self (POSTED_INTR_VECTOR ); + +kvm_wait_lapic_expire (vcpu);
it seems the APIC timer change was inadvertently included.
Re: [PATCH v19 092/130] KVM: TDX: Implement interrupt injection
Quoted 10 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 859
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c +4
-0@@ -848,6 +853,12 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)trace_kvm_entry (vcpu); +if (pi_test_on (&tdx->pi_desc)) { + apic->send_IPI_self (POSTED_INTR_VECTOR ); + +kvm_wait_lapic_expire (vcpu);it seems the APIC timer change was inadvertently included.
Oops. Thanks for catching it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 092/130] KVM: TDX: Implement interrupt injection
In reply to [email protected]Quoted 28 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata<[email protected]>
TDX supports interrupt inject into vcpu with posted interrupt. Wire up the corresponding kvm x86 operations to posted interrupt. Move
kvm_vcpu_trigger_posted_interrupt()fromvmx.ctocommon.hto share the code.VMX can inject interrupt by setting interrupt information field,
VM_ENTRY_INTR_INFO_FIELD, of VMCS. TDX supports interrupt injection only by posted interrupt. Ignore the execution path to accessVM_ENTRY_INTR_INFO_FIELD.As cpu state is protected and apicv is enabled for the TDX guest, VMM can inject interrupt by updating posted interrupt descriptor. Treat interrupt can be injected always.
Signed-off-by Isaku Yamahata<[email protected]>Reviewed-by Paolo Bonzini<[email protected]>--- arch/x86/kvm/vmx/common.h | 71 ++++++++++++++++++++++++++ arch/x86/kvm/vmx/main.c | 93 ++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/posted_intr.c | 2 +- arch/x86/kvm/vmx/posted_intr.h | 2 + arch/x86/kvm/vmx/tdx.c | 25 +++++++++ arch/x86/kvm/vmx/vmx.c | 67 +----------------------- arch/x86/kvm/vmx/x86_ops.h | 7 ++- 7 files changed, 190 insertions(+), 77 deletions(-)
[...]
arch/x86/kvm/vmx/main.c static void vt_sched_in(struct kvm_vcpu *vcpu, int cpu) · line 331
Quoted 5 lines
arch/x86/kvm/vmx/main.c +4
-0+static void vt_set_interrupt_shadow (struct kvm_vcpu *vcpu,int mask) +{ +if (is_td_vcpu (vcpu)) +return ;
Please add a blank line.
Quoted 3 lines
arch/x86/kvm/vmx/main.c +3
-0+vmx_set_interrupt_shadow (vcpu, mask); +} +
[...]
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 859
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +4
-0@@ -848,6 +853,12 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)trace_kvm_entry (vcpu); +if (pi_test_on (&tdx->pi_desc)) { + apic->send_IPI_self (POSTED_INTR_VECTOR ); + +kvm_wait_lapic_expire (vcpu);
As Chao pointed out, APIC timer change shouldn't be included in this patch.
Maybe better to put the splitted patch closer to patch "KVM: x86: Assume timer IRQ was injected if APIC state is proteced" becasue they are related.
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ } +tdx_vcpu_enter_exit (tdx);tdx_user_return_update_cache (vcpu);@@ -1213,6 +1224,16 @@ static int tdx_sept_remove_private_spte(struct kvm *kvm, gfn_t gfn,return tdx_sept_drop_private_spte (kvm, gfn, level, pfn); }
[...]
PATCH v19 93/130 KVM: TDX: Implements vcpu request_immediate_exit
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Now we are able to inject interrupts into TDX vcpu, it's ready to block TDX vcpu. Wire up kvm x86 methods for blocking/unblocking vcpu for TDX. To unblock on pending events, request immediate exit methods is also needed.
--- arch/x86/kvm/vmx/main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
arch/x86/kvm/vmx/main.c +11 -1
@@ -372,6 +372,16 @@ static void vt_enable_irq_window(struct kvm_vcpu *vcpu)vmx_enable_irq_window (vcpu); } +static void vt_request_immediate_exit (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +__kvm_request_immediate_exit (vcpu); +return ; + } + +vmx_request_immediate_exit (vcpu); +} +static u8 vt_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {if (is_td_vcpu (vcpu))
@@ -549,7 +559,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .check_intercept = vmx_check_intercept, .handle_exit_irqoff = vmx_handle_exit_irqoff, - .request_immediate_exit = vmx_request_immediate_exit, + .request_immediate_exit = vt_request_immediate_exit, .sched_in = vt_sched_in,
Signature · 1 line
2.25.1
Re: [PATCH v19 093/130] KVM: TDX: Implements vcpu request_immediate_exit
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:35AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Now we are able to inject interrupts into TDX vcpu, it's ready to block TDX vcpu. Wire up kvm x86 methods for blocking/unblocking vcpu for TDX. To unblock on pending events, request immediate exit methods is also needed.
TDX doesn't support this immediate exit. It is considered as a potential attack to TDs. TDX module deploys 0/1-step mitigations to prevent this. Even KVM issues a self-IPI before TD-entry, TD-exit will happen after the guest runs a random number of instructions.
KVM shouldn't request immediate exits in the first place. Just emit a warning if KVM tries to do this.
Quoted 41 lines
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
arch/x86/kvm/vmx/main.c| 12 +++++++++++-1 file changed, 11 insertions(+), 1 deletion(-)arch/x86/kvm/vmx/main.c +11
-1@@ -372,6 +372,16 @@ static void vt_enable_irq_window(struct kvm_vcpu *vcpu)vmx_enable_irq_window (vcpu); } +static void vt_request_immediate_exit (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +__kvm_request_immediate_exit (vcpu); +return ; + } + +vmx_request_immediate_exit (vcpu); +} +static u8 vt_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {if (is_td_vcpu (vcpu))@@ -549,7 +559,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .check_intercept = vmx_check_intercept, .handle_exit_irqoff = vmx_handle_exit_irqoff, - .request_immediate_exit = vmx_request_immediate_exit, + .request_immediate_exit = vt_request_immediate_exit, .sched_in = vt_sched_in,Signature · 1 line
2.25.1
Re: [PATCH v19 093/130] KVM: TDX: Implements vcpu request_immediate_exit
Quoted 7 lines · Chao Gao
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:35AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Now we are able to inject interrupts into TDX vcpu, it's ready to block TDX vcpu. Wire up kvm x86 methods for blocking/unblocking vcpu for TDX. To unblock on pending events, request immediate exit methods is also needed.
Quoted 7 lines
TDX doesn't support this immediate exit. It is considered as a potential attack to TDs. TDX module deploys 0/1-step mitigations to prevent this. Even KVM issues a self-IPI before TD-entry, TD-exit will happen after the guest runs a random number of instructions.
KVM shouldn't request immediate exits in the first place. Just emit a warning if KVM tries to do this.
0ec3d6d1f169
("KVM: x86: Fully defer to vendor code to decide how to force immediate exit") removed the hook. This patch will be dropped and tdx_vcpu_run() will ignore force_immediate_exit.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 94/130 KVM: TDX: Implement methods to inject NMI
In reply to [email protected]From: Isaku Yamahata <[email protected]>
TDX vcpu control structure defines one bit for pending NMI for VMM to inject NMI by setting the bit without knowing TDX vcpu NMI states. Because the vcpu state is protected, VMM can't know about NMI states of TDX vcpu. The TDX module handles actual injection and NMI states transition.
Add methods for NMI and treat NMI can be injected always.
--- arch/x86/kvm/vmx/main.c | 64 +++++++++++++++++++++++++++++++++++--- arch/x86/kvm/vmx/tdx.c | 6 ++++ arch/x86/kvm/vmx/x86_ops.h | 2 ++ 3 files changed, 67 insertions(+), 5 deletions(-)
arch/x86/kvm/vmx/main.c +59 -5
@@ -306,6 +306,60 @@ static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu)vmx_flush_tlb_guest (vcpu); } +static void vt_inject_nmi (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +tdx_inject_nmi (vcpu); +return ; + } + +vmx_inject_nmi (vcpu); +} + +static int vt_nmi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +/* +* The TDX module manages NMI windows and NMI reinjection, and hides NMI +* blocking, all KVM can do is throw an NMI over the wall. +*/ +if (is_td_vcpu (vcpu)) +return true; + +return vmx_nmi_allowed (vcpu, for_injection); +} + +static bool vt_get_nmi_mask (struct kvm_vcpu *vcpu) +{ +/* +* Assume NMIs are always unmasked. KVM could query PEND_NMI and treat +* NMIs as masked if a previous NMI is still pending, but SEAMCALLs are +* expensive and the end result is unchanged as the only relevant usage +* of get_nmi_mask() is to limit the number of pending NMIs, i.e. it +* only changes whether KVM or the TDX module drops an NMI. +*/ +if (is_td_vcpu (vcpu)) +return false; + +return vmx_get_nmi_mask (vcpu); +} + +static void vt_set_nmi_mask (struct kvm_vcpu *vcpu,bool masked) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_nmi_mask (vcpu, masked); +} + +static void vt_enable_nmi_window (struct kvm_vcpu *vcpu) +{ +/* Refer the comment in vt_get_nmi_mask(). */ +if (is_td_vcpu (vcpu)) +return ; + +vmx_enable_nmi_window (vcpu); +} +static void vt_load_mmu_pgd (struct kvm_vcpu *vcpu,hpa_t root_hpa,int pgd_level) {
@@ -515,14 +569,14 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .get_interrupt_shadow = vt_get_interrupt_shadow, .patch_hypercall = vmx_patch_hypercall, .inject_irq = vt_inject_irq, - .inject_nmi = vmx_inject_nmi, + .inject_nmi = vt_inject_nmi, .inject_exception = vmx_inject_exception, .cancel_injection = vt_cancel_injection, .interrupt_allowed = vt_interrupt_allowed, - .nmi_allowed = vmx_nmi_allowed, - .get_nmi_mask = vmx_get_nmi_mask, - .set_nmi_mask = vmx_set_nmi_mask, - .enable_nmi_window = vmx_enable_nmi_window, + .nmi_allowed = vt_nmi_allowed, + .get_nmi_mask = vt_get_nmi_mask, + .set_nmi_mask = vt_set_nmi_mask, + .enable_nmi_window = vt_enable_nmi_window, .enable_irq_window = vt_enable_irq_window, .update_cr8_intercept = vmx_update_cr8_intercept, .set_virtual_apic_mode = vmx_set_virtual_apic_mode,
arch/x86/kvm/vmx/tdx.c +6 -0
@@ -874,6 +874,12 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)return EXIT_FASTPATH_NONE ; } +void tdx_inject_nmi (struct kvm_vcpu *vcpu) +{ + ++vcpu->stat.nmi_injections; +td_management_write8 (to_tdx (vcpu),TD_VCPU_PEND_NMI ,1 ); +} +void tdx_load_mmu_pgd (struct kvm_vcpu *vcpu,hpa_t root_hpa,int pgd_level) {WARN_ON_ONCE (root_hpa & ~PAGE_MASK );
arch/x86/kvm/vmx/x86_ops.h +2 -0
@@ -159,6 +159,7 @@ u8 tdx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio);void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode,int trig_mode,int vector); +void tdx_inject_nmi (struct kvm_vcpu *vcpu);int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp);
@@ -202,6 +203,7 @@ static inline u8 tdx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)static inline void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode,int trig_mode,int vector) {} +static inline void tdx_inject_nmi (struct kvm_vcpu *vcpu) {}static inline int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp) {return -EOPNOTSUPP ; }
Signature · 1 line
2.25.1
Re: [PATCH v19 094/130] KVM: TDX: Implement methods to inject NMI
Quoted 6 lines
arch/x86/kvm/vmx/main.c +6
-0+static void vt_set_nmi_mask (struct kvm_vcpu *vcpu,bool masked) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_nmi_mask (vcpu, masked);
arch/x86/kvm/vmx/main.c static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu) · line 361
Quoted 10 lines
arch/x86/kvm/vmx/main.c +10
-0+} + +static void vt_enable_nmi_window (struct kvm_vcpu *vcpu) +{ +/* Refer the comment in vt_get_nmi_mask(). */ +if (is_td_vcpu (vcpu)) +return ; + +vmx_enable_nmi_window (vcpu); +}
The two actually request something to do done for the TD. But we make them nop as TDX module doesn't support VMM to configure nmi mask and nmi window. Do you think they are worth a WARN_ON_ONCE()? or adding WARN_ON_ONCE() requires a lot of code factoring in KVM's NMI injection logics?
Re: [PATCH v19 094/130] KVM: TDX: Implement methods to inject NMI
Quoted 13 lines · Chao Gao
Quoted 13 lines
arch/x86/kvm/vmx/main.c +13
-0+static void vt_set_nmi_mask (struct kvm_vcpu *vcpu,bool masked) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_nmi_mask (vcpu, masked); +} + +static void vt_enable_nmi_window (struct kvm_vcpu *vcpu) +{ +/* Refer the comment in vt_get_nmi_mask(). */ +if (is_td_vcpu (vcpu)) +return ;
Quoted 8 lines
arch/x86/kvm/vmx/main.c static void vt_flush_tlb_guest(struct kvm_vcpu *vcpu) · line 361
Quoted 3 lines
arch/x86/kvm/vmx/main.c +3
-0+ +vmx_enable_nmi_window (vcpu); +}The two actually request something to do done for the TD. But we make them nop as TDX module doesn't support VMM to configure nmi mask and nmi window. Do you think they are worth a
WARN_ON_ONCE()? or addingWARN_ON_ONCE()requires a lot of code factoring in KVM's NMI injection logics?
Because user space can reach those hooks with KVM_SET_VCPU_EVENTS, we shouldn't add WARN_ON_ONCE(). There are two choices. Ignore the request (the current choice) or return error for unsupported request.
It's troublesome to allow error for them because we have to fix up the caller up to the user space. The user space may abort on such error without fix.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 95/130 KVM: VMX: Modify NMI and INTR handlers to take intr_info as function argument
In reply to [email protected]From: Sean Christopherson <[email protected]>
TDX uses different ABI to get information about VM exit. Pass intr_info to the NMI and INTR handlers instead of pulling it from vcpu_vmx in preparation for sharing the bulk of the handlers with TDX.
When the guest TD exits to VMM, RAX holds status and exit reason, RCX holds exit qualification etc rather than the VMCS fields because VMM doesn't have access to the VMCS. The eventual code will be
VMX:
- get exit reason, intr_info, exit_qualification, and etc from VMCS
- call NMI/INTR handlers (common code)
TDX:
- get exit reason, intr_info, exit_qualification, and etc from guest registers
- call NMI/INTR handlers (common code)
--- arch/x86/kvm/vmx/vmx.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-)
arch/x86/kvm/vmx/vmx.c +7 -9
@@ -6912,24 +6912,22 @@ static void handle_nm_fault_irqoff(struct kvm_vcpu *vcpu)rdmsrl (MSR_IA32_XFD_ERR , vcpu->arch.guest_fpu.xfd_err); } -static void handle_exception_irqoff (struct vcpu_vmx *vmx) +static void handle_exception_irqoff (struct kvm_vcpu *vcpu,u32 intr_info) { -u32 intr_info =vmx_get_intr_info (&vmx->vcpu); -/* if exit due to PF check for async PF */ if (is_page_fault (intr_info)) - vmx->vcpu.arch.apf.host_apf_flags =kvm_read_and_reset_apf_flags (); + vcpu->arch.apf.host_apf_flags =kvm_read_and_reset_apf_flags ();/* if exit due to NM, handle before interrupts are enabled */ else if (is_nm_fault (intr_info)) -handle_nm_fault_irqoff (&vmx->vcpu); +handle_nm_fault_irqoff (vcpu);/* Handle machine checks before interrupts are enabled */ else if (is_machine_check (intr_info))kvm_machine_check (); } -static void handle_external_interrupt_irqoff (struct kvm_vcpu *vcpu) +static void handle_external_interrupt_irqoff (struct kvm_vcpu *vcpu, +u32 intr_info) { -u32 intr_info =vmx_get_intr_info (vcpu);unsigned int vector = intr_info &INTR_INFO_VECTOR_MASK ;gate_desc *desc = (gate_desc *)host_idt_base + vector;
@@ -6952,9 +6950,9 @@ void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu)return ;if (vmx->exit_reason.basic ==EXIT_REASON_EXTERNAL_INTERRUPT ) -handle_external_interrupt_irqoff (vcpu); +handle_external_interrupt_irqoff (vcpu,vmx_get_intr_info (vcpu));else if (vmx->exit_reason.basic ==EXIT_REASON_EXCEPTION_NMI ) -handle_exception_irqoff (vmx); +handle_exception_irqoff (vcpu,vmx_get_intr_info (vcpu)); }/*
Signature · 1 line
2.25.1
Re: [PATCH v19 095/130] KVM: VMX: Modify NMI and INTR handlers to take intr_info as function argument
Quoted 14 lines · On Mon, Feb 26, 2024 at 12:26:37AM -0800, [email protected] wrote:
From: Sean Christopherson <[email protected]>
TDX uses different ABI to get information about VM exit. Pass
intr_infoto the NMI and INTR handlers instead of pulling it fromvcpu_vmxin preparation for sharing the bulk of the handlers with TDX.When the guest TD exits to VMM, RAX holds status and exit reason, RCX holds exit qualification etc rather than the VMCS fields because VMM doesn't have access to the VMCS. The eventual code will be
VMX:
- get exit reason,intr_info,exit_qualification, and etc from VMCS
- call NMI/INTR handlers (common code)
Quoted 8 lines
TDX:
- get exit reason,intr_info,exit_qualification, and etc from guest registers
- call NMI/INTR handlers (common code)Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>
PATCH v19 96/130 KVM: VMX: Move NMI/exception handler to common helper
In reply to [email protected]From: Sean Christopherson <[email protected]>
TDX mostly handles NMI/exception exit mostly the same to VMX case. The difference is how to retrieve exit qualification. To share the code with TDX, move NMI/exception to a common header, common.h.
--- arch/x86/kvm/vmx/common.h | 59 +++++++++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 68 +++++---------------------------------- 2 files changed, 67 insertions(+), 60 deletions(-)
arch/x86/kvm/vmx/common.h +59 -0
@@ -4,8 +4,67 @@#include <linux/kvm_host.h> +#include <asm/traps.h> +#include "posted_intr.h" #include "mmu.h" +#include "vmcs.h" +#include "x86.h" + +extern unsigned long vmx_host_idt_base; +void vmx_do_interrupt_irqoff (unsigned long entry); +void vmx_do_nmi_irqoff (void ); + +static inline void vmx_handle_nm_fault_irqoff (struct kvm_vcpu *vcpu) +{ +/* +* Save xfd_err to guest_fpu before interrupt is enabled, so the +* MSR value is not clobbered by the host activity before the guest +* has chance to consume it. +* +* Do not blindly read xfd_err here, since this exception might +* be caused by L1 interception on a platform which doesn't +* support xfd at all. +* +* Do it conditionally upon guest_fpu::xfd. xfd_err matters +* only when xfd contains a non-zero value. +* +* Queuing exception is done in vmx_handle_exit. See comment there. +*/ +if (vcpu->arch.guest_fpu.fpstate->xfd) +rdmsrl (MSR_IA32_XFD_ERR , vcpu->arch.guest_fpu.xfd_err); +} + +static inline void vmx_handle_exception_irqoff (struct kvm_vcpu *vcpu, +u32 intr_info) +{ +/* if exit due to PF check for async PF */ +if (is_page_fault (intr_info)) + vcpu->arch.apf.host_apf_flags =kvm_read_and_reset_apf_flags (); +/* if exit due to NM, handle before interrupts are enabled */ +else if (is_nm_fault (intr_info)) +vmx_handle_nm_fault_irqoff (vcpu); +/* Handle machine checks before interrupts are enabled */ +else if (is_machine_check (intr_info)) +kvm_machine_check (); +} + +static inline void vmx_handle_external_interrupt_irqoff (struct kvm_vcpu *vcpu, +u32 intr_info) +{ +unsigned int vector = intr_info &INTR_INFO_VECTOR_MASK ; +gate_desc *desc = (gate_desc *)vmx_host_idt_base + vector; + +if (KVM_BUG (!is_external_intr (intr_info), vcpu->kvm, +"unexpected VM-Exit interrupt info: 0x%x" , intr_info)) +return ; + +kvm_before_interrupt (vcpu,KVM_HANDLING_IRQ ); +vmx_do_interrupt_irqoff (gate_offset (desc)); +kvm_after_interrupt (vcpu); + + vcpu->arch.at_instruction_boundary = true; +}static inline int __vmx_handle_ept_violation (struct kvm_vcpu *vcpu,gpa_t gpa,unsigned long exit_qualification)
arch/x86/kvm/vmx/vmx.c +8 -60
111 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH v19 096/130] KVM: VMX: Move NMI/exception handler to common helper
Quoted 5 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
TDX mostly handles NMI/exception exit mostly the same to VMX case. The difference is how to retrieve exit qualification. To share the code with TDX, move NMI/exception to a common header,
common.h.
Suggest to add "No functional change intended." in the changelog.
Quoted 202 lines
Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/vmx/common.h | 59 +++++++++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 68 +++++---------------------------------- 2 files changed, 67 insertions(+), 60 deletions(-)arch/x86/kvm/vmx/common.h +59
-0@@ -4,8 +4,67 @@#include <linux/kvm_host.h> +#include <asm/traps.h> +#include "posted_intr.h" #include "mmu.h" +#include "vmcs.h" +#include "x86.h" + +extern unsigned long vmx_host_idt_base; +void vmx_do_interrupt_irqoff (unsigned long entry); +void vmx_do_nmi_irqoff (void ); + +static inline void vmx_handle_nm_fault_irqoff (struct kvm_vcpu *vcpu) +{ +/* +* Save xfd_err to guest_fpu before interrupt is enabled, so the +* MSR value is not clobbered by the host activity before the guest +* has chance to consume it. +* +* Do not blindly read xfd_err here, since this exception might +* be caused by L1 interception on a platform which doesn't +* support xfd at all. +* +* Do it conditionally upon guest_fpu::xfd. xfd_err matters +* only when xfd contains a non-zero value. +* +* Queuing exception is done in vmx_handle_exit. See comment there. +*/ +if (vcpu->arch.guest_fpu.fpstate->xfd) +rdmsrl (MSR_IA32_XFD_ERR , vcpu->arch.guest_fpu.xfd_err); +} + +static inline void vmx_handle_exception_irqoff (struct kvm_vcpu *vcpu, +u32 intr_info) +{ +/* if exit due to PF check for async PF */ +if (is_page_fault (intr_info)) + vcpu->arch.apf.host_apf_flags =kvm_read_and_reset_apf_flags (); +/* if exit due to NM, handle before interrupts are enabled */ +else if (is_nm_fault (intr_info)) +vmx_handle_nm_fault_irqoff (vcpu); +/* Handle machine checks before interrupts are enabled */ +else if (is_machine_check (intr_info)) +kvm_machine_check (); +} + +static inline void vmx_handle_external_interrupt_irqoff (struct kvm_vcpu *vcpu, +u32 intr_info) +{ +unsigned int vector = intr_info &INTR_INFO_VECTOR_MASK ; +gate_desc *desc = (gate_desc *)vmx_host_idt_base + vector; + +if (KVM_BUG (!is_external_intr (intr_info), vcpu->kvm, +"unexpected VM-Exit interrupt info: 0x%x" , intr_info)) +return ; + +kvm_before_interrupt (vcpu,KVM_HANDLING_IRQ ); +vmx_do_interrupt_irqoff (gate_offset (desc)); +kvm_after_interrupt (vcpu); + + vcpu->arch.at_instruction_boundary = true; +}static inline int __vmx_handle_ept_violation (struct kvm_vcpu *vcpu,gpa_t gpa,unsigned long exit_qualification)arch/x86/kvm/vmx/vmx.c +8
-60@@ -518,7 +518,7 @@ static inline void vmx_segment_cache_clear(struct vcpu_vmx *vmx) vmx->segment_cache.bitmask =0 ; } -static unsigned long host_idt_base; +unsigned long vmx_host_idt_base;#if IS_ENABLED (CONFIG_HYPERV )static bool __read_mostly enlightened_vmcs = true;@@ -4273,7 +4273,7 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx)vmcs_write16 (HOST_SS_SELECTOR , __KERNEL_DS);/* 22.2.4 */ vmcs_write16 (HOST_TR_SELECTOR ,GDT_ENTRY_TSS *8 );/* 22.2.4 */ -vmcs_writel (HOST_IDTR_BASE , host_idt_base);/* 22.2.4 */ +vmcs_writel (HOST_IDTR_BASE , vmx_host_idt_base);/* 22.2.4 */ vmcs_writel (HOST_RIP , (unsigned long )vmx_vmexit);/* 22.2.5 */ @@ -5166,7 +5166,7 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) intr_info =vmx_get_intr_info (vcpu);/* -* Machine checks are handled by handle_exception_irqoff(), or by +* Machine checks are handled by vmx_handle_exception_irqoff(), or by * vmx_vcpu_run() if a #MC occurs on VM-Entry. NMIs are handled by * vmx_vcpu_enter_exit(). */ @@ -5174,7 +5174,7 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)return 1 ;/* -* Queue the exception here instead of in handle_nm_fault_irqoff(). +* Queue the exception here instead of in vmx_handle_nm_fault_irqoff(). * This ensures the nested_vmx check is not skipped so vmexit can * be reflected to L1 (when it intercepts #NM) before reaching this * point. @@ -6889,59 +6889,6 @@ void vmx_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)vmcs_write64 (EOI_EXIT_BITMAP3 , eoi_exit_bitmap[3 ]); } -void vmx_do_interrupt_irqoff (unsigned long entry); -void vmx_do_nmi_irqoff (void ); - -static void handle_nm_fault_irqoff (struct kvm_vcpu *vcpu) -{ -/* -* Save xfd_err to guest_fpu before interrupt is enabled, so the -* MSR value is not clobbered by the host activity before the guest -* has chance to consume it. -* -* Do not blindly read xfd_err here, since this exception might -* be caused by L1 interception on a platform which doesn't -* support xfd at all. -* -* Do it conditionally upon guest_fpu::xfd. xfd_err matters -* only when xfd contains a non-zero value. -* -* Queuing exception is done in vmx_handle_exit. See comment there. -*/ -if (vcpu->arch.guest_fpu.fpstate->xfd) -rdmsrl (MSR_IA32_XFD_ERR , vcpu->arch.guest_fpu.xfd_err); -} - -static void handle_exception_irqoff (struct kvm_vcpu *vcpu,u32 intr_info) -{ -/* if exit due to PF check for async PF */ -if (is_page_fault (intr_info)) - vcpu->arch.apf.host_apf_flags =kvm_read_and_reset_apf_flags (); -/* if exit due to NM, handle before interrupts are enabled */ -else if (is_nm_fault (intr_info)) -handle_nm_fault_irqoff (vcpu); -/* Handle machine checks before interrupts are enabled */ -else if (is_machine_check (intr_info)) -kvm_machine_check (); -} - -static void handle_external_interrupt_irqoff (struct kvm_vcpu *vcpu, -u32 intr_info) -{ -unsigned int vector = intr_info &INTR_INFO_VECTOR_MASK ; -gate_desc *desc = (gate_desc *)host_idt_base + vector; - -if (KVM_BUG (!is_external_intr (intr_info), vcpu->kvm, -"unexpected VM-Exit interrupt info: 0x%x" , intr_info)) -return ; - -kvm_before_interrupt (vcpu,KVM_HANDLING_IRQ ); -vmx_do_interrupt_irqoff (gate_offset (desc)); -kvm_after_interrupt (vcpu); - - vcpu->arch.at_instruction_boundary = true; -} -void vmx_handle_exit_irqoff (struct kvm_vcpu *vcpu) {struct vcpu_vmx *vmx =to_vmx (vcpu);@@ -6950,9 +6897,10 @@ void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu)return ;if (vmx->exit_reason.basic ==EXIT_REASON_EXTERNAL_INTERRUPT ) -handle_external_interrupt_irqoff (vcpu,vmx_get_intr_info (vcpu)); +vmx_handle_external_interrupt_irqoff (vcpu, +vmx_get_intr_info (vcpu));else if (vmx->exit_reason.basic ==EXIT_REASON_EXCEPTION_NMI ) -handle_exception_irqoff (vcpu,vmx_get_intr_info (vcpu)); +vmx_handle_exception_irqoff (vcpu,vmx_get_intr_info (vcpu)); }/* @@ -8284,7 +8232,7 @@ __init int vmx_hardware_setup(void)int r;store_idt (&dt); - host_idt_base = dt.address; + vmx_host_idt_base = dt.address;vmx_setup_user_return_msrs ();
PATCH v19 97/130 KVM: x86: Split core of hypercall emulation to helper function
In reply to [email protected]From: Sean Christopherson <[email protected]>
By necessity, TDX will use a different register ABI for hypercalls. Break out the core functionality so that it may be reused for TDX.
--- arch/x86/include/asm/kvm_host.h | 4 +++ arch/x86/kvm/x86.c | 56 ++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 18 deletions(-)
arch/x86/include/asm/kvm_host.h +4 -0
@@ -2177,6 +2177,10 @@ static inline void kvm_clear_apicv_inhibit(struct kvm *kvm,kvm_set_or_clear_apicv_inhibit (kvm, reason, false); } +unsigned long __kvm_emulate_hypercall (struct kvm_vcpu *vcpu,unsigned long nr, +unsigned long a0,unsigned long a1, +unsigned long a2,unsigned long a3, +int op_64_bit,int cpl);int kvm_emulate_hypercall (struct kvm_vcpu *vcpu); int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,
arch/x86/kvm/x86.c +38 -18
89 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH v19 097/130] KVM: x86: Split core of hypercall emulation to helper function
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10154
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:39AM -0800, [email protected] wrote:
arch/x86/kvm/x86.c +1
-0@@ -10162,18 +10151,49 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)WARN_ON_ONCE (vcpu->run->hypercall.flags &KVM_EXIT_HYPERCALL_MBZ ); vcpu->arch.complete_userspace_io = complete_hypercall_exit; +/* stat is incremented on completion. */
Perhaps we could use a distinct return value to signal that the request is redirected to userspace. This way, more cases can be supported, e.g., accesses to MTRR MSRs, requests to service TDs, etc. And then ...
Quoted 26 lines
arch/x86/kvm/x86.c +1
-0return 0 ; }default : ret = -KVM_ENOSYS ;break ; } +out:
arch/x86/kvm/x86.c +18
-0+ ++vcpu->stat.hypercalls; +return ret; +} +EXPORT_SYMBOL_GPL (__kvm_emulate_hypercall); + +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); + +if (kvm_hv_hypercall_enabled (vcpu)) +return kvm_hv_hypercall (vcpu); + + nr =kvm_rax_read (vcpu);
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10190
Quoted 10 lines
arch/x86/kvm/x86.c +10
-0+ a0 =kvm_rbx_read (vcpu); + a1 =kvm_rcx_read (vcpu); + a2 =kvm_rdx_read (vcpu); + a3 =kvm_rsi_read (vcpu); + op_64_bit =is_64_bit_hypercall (vcpu); + cpl =static_call (kvm_x86_get_cpl)(vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl); +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +/* MAP_GPA tosses the request to the user space. */
no need to check what the request is. Just checking the return value will suffice.
Quoted 14 lines
arch/x86/kvm/x86.c +2
-0+return 0 ; +if (!op_64_bit) ret = (u32)ret; kvm_rax_write(vcpu, ret);arch/x86/kvm/x86.c +0
-1- ++vcpu->stat.hypercalls;return kvm_skip_emulated_instruction(vcpu); }
EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);Signature · 1 line
2.25.1
Re: [PATCH v19 097/130] KVM: x86: Split core of hypercall emulation to helper function
Quoted 10 lines · Chao Gao
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10154
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:39AM -0800, [email protected] wrote:
arch/x86/kvm/x86.c +1
-0@@ -10162,18 +10151,49 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)WARN_ON_ONCE (vcpu->run->hypercall.flags &KVM_EXIT_HYPERCALL_MBZ ); vcpu->arch.complete_userspace_io = complete_hypercall_exit; +/* stat is incremented on completion. */ Perhaps we could use a distinct return value to signal that the request is redirected to userspace. This way, more cases can be supported, e.g., accesses to MTRR MSRs, requests to service TDs, etc. And then ...
The convention here is the one for exit_handler vcpu_enter_guest() already uses. If we introduce something like KVM_VCPU_CONTINUE=1, KVM_VCPU_EXIT_TO_USER=0, it will touch many places. So if we will (I'm not sure it's worthwhile), the cleanup should be done as independently.
Quoted 30 lines
Quoted 30 lines
arch/x86/kvm/x86.c +1
-0return 0 ; }default : ret = -KVM_ENOSYS ;break ; } +out:
arch/x86/kvm/x86.c +22
-0+ ++vcpu->stat.hypercalls; +return ret; +} +EXPORT_SYMBOL_GPL (__kvm_emulate_hypercall); + +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); + +if (kvm_hv_hypercall_enabled (vcpu)) +return kvm_hv_hypercall (vcpu); + + nr =kvm_rax_read (vcpu); + a0 =kvm_rbx_read (vcpu); + a1 =kvm_rcx_read (vcpu); + a2 =kvm_rdx_read (vcpu); + a3 =kvm_rsi_read (vcpu);
Quoted 8 lines
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10190
Quoted 6 lines
arch/x86/kvm/x86.c +6
-0+ op_64_bit =is_64_bit_hypercall (vcpu); + cpl =static_call (kvm_x86_get_cpl)(vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl); +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +/* MAP_GPA tosses the request to the user space. */ no need to check what the request is. Just checking the return value will suffice.
This is needed to avoid updating rax etc. KVM_HC_MAP_GPA_RANGE is only an exception to go to the user space. This check is a bit weird, but I couldn't find a good way.
Quoted 16 lines
Quoted 15 lines
arch/x86/kvm/x86.c +2
-0+return 0 ; +if (!op_64_bit) ret = (u32)ret; kvm_rax_write(vcpu, ret);arch/x86/kvm/x86.c +0
-1- ++vcpu->stat.hypercalls;return kvm_skip_emulated_instruction(vcpu); }
EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);Signature · 1 line
2.25.1
Signature · 1 line
Isaku Yamahata <[email protected]>
Sean Christopherson
maintainerRe: [PATCH v19 097/130] KVM: x86: Split core of hypercall emulation to helper function
Quoted 14 lines · Isaku Yamahata
Quoted 10 lines · Chao Gao
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10154
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:39AM -0800, [email protected] wrote:
arch/x86/kvm/x86.c +1
-0@@ -10162,18 +10151,49 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)WARN_ON_ONCE (vcpu->run->hypercall.flags &KVM_EXIT_HYPERCALL_MBZ ); vcpu->arch.complete_userspace_io = complete_hypercall_exit; +/* stat is incremented on completion. */ Perhaps we could use a distinct return value to signal that the request is redirected to userspace. This way, more cases can be supported, e.g., accesses to MTRR MSRs, requests to service TDs, etc. And then ...
Quoted 4 lines
The convention here is the one for
exit_handlervcpu_enter_guest()already uses. If we introduce something like KVM_VCPU_CONTINUE=1, KVM_VCPU_EXIT_TO_USER=0, it will touch many places. So if we will (I'm not sure it's worthwhile), the cleanup should be done as independently.
Yeah, this is far from the first time that someone has complained about KVM's awful 1/0 return magic. And every time we've looked at it, we've come to the conclusion that it's not worth the churn/risk.
And if we really need to further overload the return value, we can, e.g. KVM already does this for MSR accesses:
/*
* Internal error codes that are used to indicate that MSR emulation encountered
* an error that should result in #GP in the guest, unless userspace
* handles it.
*/
#define KVM_MSR_RET_INVALID 2 /* in-kernel MSR emulation #GP condition */
#define KVM_MSR_RET_FILTERED 3 /* #GP due to userspace MSR filter */
Re: [PATCH v19 097/130] KVM: x86: Split core of hypercall emulation to helper function
In reply to Isaku YamahataQuoted 29 lines · Isaku Yamahata
Quoted 18 lines · Chao Gao
Quoted 17 lines · On Mon, Feb 26, 2024 at 12:26:39AM -0800, [email protected] wrote:
arch/x86/kvm/x86.c +17
-0+ +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); + +if (kvm_hv_hypercall_enabled (vcpu)) +return kvm_hv_hypercall (vcpu); + + nr =kvm_rax_read (vcpu); + a0 =kvm_rbx_read (vcpu); + a1 =kvm_rcx_read (vcpu); + a2 =kvm_rdx_read (vcpu);Quoted 8 lines
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10190
Quoted 7 lines
arch/x86/kvm/x86.c +7
-0+ a3 =kvm_rsi_read (vcpu); + op_64_bit =is_64_bit_hypercall (vcpu); + cpl =static_call (kvm_x86_get_cpl)(vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl); +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +/* MAP_GPA tosses the request to the user space. */ no need to check what the request is. Just checking the return value will suffice.
Quoted 3 lines
This is needed to avoid updating rax etc.
KVM_HC_MAP_GPA_RANGEis only an exception to go to the user space. This check is a bit weird, but I couldn't find a good way.
To be generic, I think we can use
"vcpu->kvm->arch.hypercall_exit_enabled & (1 << nr)" to check if it needs to exit to userspace.
i.e.,
+ ...
+ ret = __kvm_emulate_hypercall(vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl); + if (!ret && (vcpu->kvm->arch.hypercall_exit_enabled & (1 << nr))) + /* The hypercall is requested to exit to userspace. */ + return 0;
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
arch/x86/kvm/x86.c +2
-0+return 0 ; +if (!op_64_bit) ret = (u32)ret; kvm_rax_write(vcpu, ret);arch/x86/kvm/x86.c +0
-1- ++vcpu->stat.hypercalls;return kvm_skip_emulated_instruction(vcpu); }
EXPORT_SYMBOL_GPL(kvm_emulate_hypercall);Signature · 1 line
2.25.1
Re: [PATCH v19 097/130] KVM: x86: Split core of hypercall emulation to helper function
In reply to [email protected]Quoted 84 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
By necessity, TDX will use a different register ABI for hypercalls. Break out the core functionality so that it may be reused for TDX.
Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/include/asm/kvm_host.h | 4 +++ arch/x86/kvm/x86.c | 56 ++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 18 deletions(-)arch/x86/include/asm/kvm_host.h +4
-0@@ -2177,6 +2177,10 @@ static inline void kvm_clear_apicv_inhibit(struct kvm *kvm,kvm_set_or_clear_apicv_inhibit (kvm, reason, false); } +unsigned long __kvm_emulate_hypercall (struct kvm_vcpu *vcpu,unsigned long nr, +unsigned long a0,unsigned long a1, +unsigned long a2,unsigned long a3, +int op_64_bit,int cpl);int kvm_emulate_hypercall (struct kvm_vcpu *vcpu); int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,arch/x86/kvm/x86.c +8
-17@@ -10073,26 +10073,15 @@ static int complete_hypercall_exit(struct kvm_vcpu *vcpu)return kvm_skip_emulated_instruction (vcpu); } -int kvm_emulate_hypercall (struct kvm_vcpu *vcpu) +unsigned long __kvm_emulate_hypercall (struct kvm_vcpu *vcpu,unsigned long nr, +unsigned long a0,unsigned long a1, +unsigned long a2,unsigned long a3, +int op_64_bit,int cpl) { -unsigned long nr, a0, a1, a2, a3, ret; -int op_64_bit; - -if (kvm_xen_hypercall_enabled (vcpu->kvm)) -return kvm_xen_hypercall (vcpu); - -if (kvm_hv_hypercall_enabled (vcpu)) -return kvm_hv_hypercall (vcpu); - - nr =kvm_rax_read (vcpu); - a0 =kvm_rbx_read (vcpu); - a1 =kvm_rcx_read (vcpu); - a2 =kvm_rdx_read (vcpu); - a3 =kvm_rsi_read (vcpu); +unsigned long ret;trace_kvm_hypercall (nr, a0, a1, a2, a3); - op_64_bit =is_64_bit_hypercall (vcpu);if (!op_64_bit) { nr &=0xFFFFFFFF ; a0 &=0xFFFFFFFF ;@@ -10101,7 +10090,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) a3 &=0xFFFFFFFF ; } -if (static_call (kvm_x86_get_cpl)(vcpu) !=0 ) { +if (cpl) { ret = -KVM_EPERM ; goto out; }@@ -10162,18 +10151,49 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)WARN_ON_ONCE (vcpu->run->hypercall.flags &KVM_EXIT_HYPERCALL_MBZ ); vcpu->arch.complete_userspace_io = complete_hypercall_exit; +/* stat is incremented on completion. */ return 0 ; }default : ret = -KVM_ENOSYS ;break ; } +
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10171
Quoted 10 lines
arch/x86/kvm/x86.c +9
-0out: + ++vcpu->stat.hypercalls; +return ret; +} +EXPORT_SYMBOL_GPL (__kvm_emulate_hypercall); + +int kvm_emulate_hypercall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +int op_64_bit;
Can it be opportunistically changed to bool type, as well as the argument type of "op_64_bit" in __kvm_emulate_hypercall()?
Quoted 29 lines
arch/x86/kvm/x86.c +21
-1+int cpl; + +if (kvm_xen_hypercall_enabled (vcpu->kvm)) +return kvm_xen_hypercall (vcpu); + +if (kvm_hv_hypercall_enabled (vcpu)) +return kvm_hv_hypercall (vcpu); + + nr =kvm_rax_read (vcpu); + a0 =kvm_rbx_read (vcpu); + a1 =kvm_rcx_read (vcpu); + a2 =kvm_rdx_read (vcpu); + a3 =kvm_rsi_read (vcpu); + op_64_bit =is_64_bit_hypercall (vcpu); + cpl =static_call (kvm_x86_get_cpl)(vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, op_64_bit, cpl); +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +/* MAP_GPA tosses the request to the user space. */ +return 0 ; +if (!op_64_bit) ret = (u32 )ret;kvm_rax_write (vcpu, ret); - ++vcpu->stat.hypercalls;return kvm_skip_emulated_instruction (vcpu); }EXPORT_SYMBOL_GPL (kvm_emulate_hypercall);
Re: [PATCH v19 097/130] KVM: x86: Split core of hypercall emulation to helper function
Quoted 7 lines · Binbin Wu
arch/x86/kvm/x86.c int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) · line 10171
Quoted 4 lines
arch/x86/kvm/x86.c +4
-0+int kvm_emulate_hypercall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +int op_64_bit;Can it be opportunistically changed to bool type, as well as the argument type of "
op_64_bit" in__kvm_emulate_hypercall()?
Yes. We can also fix kvm_pv_send_ipi(op_64_bit).
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 98/130 KVM: TDX: Add a place holder to handle TDX VM exit
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Wire up handle_exit and handle_exit_irqoff methods and add a place holder to handle VM exit. Add helper functions to get exit info, exit qualification, etc.
--- arch/x86/kvm/vmx/main.c | 37 ++++++++++++- arch/x86/kvm/vmx/tdx.c | 110 +++++++++++++++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 10 ++++ 3 files changed, 154 insertions(+), 3 deletions(-)
arch/x86/kvm/vmx/main.c +34 -3
@@ -228,6 +228,25 @@ static bool vt_protected_apic_has_interrupt(struct kvm_vcpu *vcpu)return tdx_protected_apic_has_interrupt (vcpu); } +static int vt_handle_exit (struct kvm_vcpu *vcpu, +enum exit_fastpath_completion fastpath) +{ +if (is_td_vcpu (vcpu)) +return tdx_handle_exit (vcpu, fastpath); + +return vmx_handle_exit (vcpu, fastpath); +} + +static void vt_handle_exit_irqoff (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +tdx_handle_exit_irqoff (vcpu); +return ; + } + +vmx_handle_exit_irqoff (vcpu); +} +static void vt_apicv_pre_state_restore (struct kvm_vcpu *vcpu) {struct pi_desc *pi =vcpu_to_pi_desc (vcpu);
@@ -436,6 +455,18 @@ static void vt_request_immediate_exit(struct kvm_vcpu *vcpu)vmx_request_immediate_exit (vcpu); } +static void vt_get_exit_info (struct kvm_vcpu *vcpu,u32 *reason, +u64 *info1,u64 *info2,u32 *intr_info,u32 *error_code) +{ +if (is_td_vcpu (vcpu)) { +tdx_get_exit_info (vcpu, reason, info1, info2, intr_info, + error_code); +return ; + } + +vmx_get_exit_info (vcpu, reason, info1, info2, intr_info, error_code); +} +static u8 vt_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {if (is_td_vcpu (vcpu))
@@ -562,7 +593,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .vcpu_pre_run = vt_vcpu_pre_run, .vcpu_run = vt_vcpu_run, - .handle_exit = vmx_handle_exit, + .handle_exit = vt_handle_exit, .skip_emulated_instruction = vmx_skip_emulated_instruction, .update_emulated_instruction = vmx_update_emulated_instruction, .set_interrupt_shadow = vt_set_interrupt_shadow,
@@ -597,7 +628,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .set_identity_map_addr = vmx_set_identity_map_addr, .get_mt_mask = vt_get_mt_mask, - .get_exit_info = vmx_get_exit_info, + .get_exit_info = vt_get_exit_info, .vcpu_after_set_cpuid = vmx_vcpu_after_set_cpuid,
@@ -611,7 +642,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .load_mmu_pgd = vt_load_mmu_pgd, .check_intercept = vmx_check_intercept, - .handle_exit_irqoff = vmx_handle_exit_irqoff, + .handle_exit_irqoff = vt_handle_exit_irqoff, .request_immediate_exit = vt_request_immediate_exit,
arch/x86/kvm/vmx/tdx.c +110 -0
134 lines not on this page · raw message
arch/x86/kvm/vmx/x86_ops.h +10 -0
@@ -155,11 +155,16 @@ void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu);void tdx_vcpu_put (struct kvm_vcpu *vcpu);void tdx_vcpu_load (struct kvm_vcpu *vcpu,int cpu);bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu); +void tdx_handle_exit_irqoff (struct kvm_vcpu *vcpu); +int tdx_handle_exit (struct kvm_vcpu *vcpu, +enum exit_fastpath_completion fastpath);u8 tdx_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio);void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode,int trig_mode,int vector);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);int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp);
@@ -199,11 +204,16 @@ static inline void tdx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) {}static inline void tdx_vcpu_put (struct kvm_vcpu *vcpu) {}static inline void tdx_vcpu_load (struct kvm_vcpu *vcpu,int cpu) {}static inline bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu) {return false; } +static inline void tdx_handle_exit_irqoff (struct kvm_vcpu *vcpu) {} +static inline int tdx_handle_exit (struct kvm_vcpu *vcpu, +enum exit_fastpath_completion fastpath) {return 0 ; }static inline u8 tdx_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {return 0 ; }static inline void tdx_deliver_interrupt (struct kvm_lapic *apic,int delivery_mode,int trig_mode,int vector) {}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 int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp) {return -EOPNOTSUPP ; }
Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v19 098/130] KVM: TDX: Add a place holder to handle TDX VM exit
Quoted 12 lines · On Mon, Feb 26, 2024, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +12
-0+int tdx_handle_exit (struct kvm_vcpu *vcpu,fastpath_t fastpath) +{ +union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason; + +/* See the comment of tdh_sept_seamcall(). */ +if (unlikely (exit_reason.full == (TDX_OPERAND_BUSY |TDX_OPERAND_ID_SEPT ))) +return 1 ; + +/* +* TDH.VP.ENTRY checks TD EPOCH which contend with TDH.MEM.TRACK and +* vcpu TDH.VP.ENTER. +*/
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1309
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+if (unlikely (exit_reason.full == (TDX_OPERAND_BUSY |TDX_OPERAND_ID_TD_EPOCH ))) +return 1 ; + +if (unlikely (exit_reason.full ==TDX_SEAMCALL_UD )) { +kvm_spurious_fault (); +/* +* In the case of reboot or kexec, loop with TDH.VP.ENTER and +* TDX_SEAMCALL_UD to avoid unnecessarily activity. +*/ +return 1 ;
No. This is unnecessarily risky. KVM_BUG_ON() and exit to userspace. The response to "SEAMCALL faulted" should never be, "well, let's try again!".
Also, what about #GP on SEAMCALL? In general, the error handling here seems lacking.
Re: [PATCH v19 098/130] KVM: TDX: Add a place holder to handle TDX VM exit
Quoted 27 lines · Sean Christopherson
Quoted 12 lines · On Mon, Feb 26, 2024, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +12
-0+int tdx_handle_exit (struct kvm_vcpu *vcpu,fastpath_t fastpath) +{ +union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason; + +/* See the comment of tdh_sept_seamcall(). */ +if (unlikely (exit_reason.full == (TDX_OPERAND_BUSY |TDX_OPERAND_ID_SEPT ))) +return 1 ; + +/* +* TDH.VP.ENTRY checks TD EPOCH which contend with TDH.MEM.TRACK and +* vcpu TDH.VP.ENTER. +*/ arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1309
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+if (unlikely (exit_reason.full == (TDX_OPERAND_BUSY |TDX_OPERAND_ID_TD_EPOCH ))) +return 1 ; + +if (unlikely (exit_reason.full ==TDX_SEAMCALL_UD )) { +kvm_spurious_fault (); +/* +* In the case of reboot or kexec, loop with TDH.VP.ENTER and +* TDX_SEAMCALL_UD to avoid unnecessarily activity. +*/ +return 1 ;No. This is unnecessarily risky.
KVM_BUG_ON()and exit to userspace. The response to "SEAMCALL faulted" should never be, "well, let's try again!".
Quoted 2 lines
Also, what about #GP on SEAMCALL? In general, the error handling here seems lacking.
As I replied at [1], let me revise error handling in general TDX KVM code.
[1] https://lore.kernel.org/kvm/[email protected]/T/#macc431c87676995d65ddcd8de632261a2dedc525
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 098/130] KVM: TDX: Add a place holder to handle TDX VM exit
In reply to [email protected]Quoted 3 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up
handle_exitandhandle_exit_irqoffmethods
This patch also wires up get_exit_info.
Quoted 12 lines
and add a place holder
to handle VM exit. Add helper functions to get exit info, exit qualification, etc.Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>--- arch/x86/kvm/vmx/main.c | 37 ++++++++++++- arch/x86/kvm/vmx/tdx.c | 110 +++++++++++++++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 10 ++++ 3 files changed, 154 insertions(+), 3 deletions(-)
[...]
Quoted 27 lines
arch/x86/kvm/vmx/main.c +3
-3@@ -562,7 +593,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .vcpu_pre_run = vt_vcpu_pre_run, .vcpu_run = vt_vcpu_run, - .handle_exit = vmx_handle_exit, + .handle_exit = vt_handle_exit, .skip_emulated_instruction = vmx_skip_emulated_instruction, .update_emulated_instruction = vmx_update_emulated_instruction, .set_interrupt_shadow = vt_set_interrupt_shadow,@@ -597,7 +628,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .set_identity_map_addr = vmx_set_identity_map_addr, .get_mt_mask = vt_get_mt_mask, - .get_exit_info = vmx_get_exit_info, + .get_exit_info = vt_get_exit_info, .vcpu_after_set_cpuid = vmx_vcpu_after_set_cpuid,@@ -611,7 +642,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .load_mmu_pgd = vt_load_mmu_pgd, .check_intercept = vmx_check_intercept, - .handle_exit_irqoff = vmx_handle_exit_irqoff, + .handle_exit_irqoff = vt_handle_exit_irqoff, .request_immediate_exit = vt_request_immediate_exit,
[...]
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1292
Quoted 6 lines
arch/x86/kvm/vmx/tdx.c +5
-0+int tdx_handle_exit (struct kvm_vcpu *vcpu,fastpath_t fastpath) +{ +union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason; + +/* See the comment of tdh_sept_seamcall(). */
Should be tdx_seamcall_sept().
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1293
Quoted 1 line
arch/x86/kvm/vmx/tdx.c +1
-0+if (unlikely (exit_reason.full == (TDX_OPERAND_BUSY |TDX_OPERAND_ID_SEPT )))
Can use "TDX_ERROR_SEPT_BUSY" instead.
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+return 1 ; + +/* +* TDH.VP.ENTRY
"TDH.VP.ENTRY" -> "TDH.VP.ENTER"
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1298
Quoted 2 lines
checks TD EPOCH which contend with TDH.MEM.TRACK and
arch/x86/kvm/vmx/tdx.c +1
-0+* vcpu TDH.VP.ENTER.
Do you mean TDH.VP.ENTER on one vcpu can contend with TDH.MEM.TRACK and TDH.VP.ENTER on another vcpu?
Quoted 2 lines
(quoted diff) +1
-0+ */
Re: [PATCH v19 098/130] KVM: TDX: Add a place holder to handle TDX VM exit
Quoted 11 lines · Binbin Wu
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+return 1 ; + +/* +* TDH.VP.ENTRY "TDH.VP.ENTRY" -> "TDH.VP.ENTER"
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1298
Quoted 2 lines
checks TD EPOCH which contend with TDH.MEM.TRACK and
arch/x86/kvm/vmx/tdx.c +1
-0+* vcpu TDH.VP.ENTER. Do you mean TDH.VP.ENTER on one vcpu can contend with TDH.MEM.TRACK and TDH.VP.ENTER on another vcpu?
Yes. The caller of TDH.MEM.TRACK() must ensure that other vCPUS go through inactive (not running vCPU) after TDH.MEM.TRACK().
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 99/130 KVM: TDX: Handle vmentry failure for INTEL TD guest
In reply to [email protected]From: Yao Yuan <[email protected]>
TDX module passes control back to VMM if it failed to vmentry for a TD, use same exit reason to notify user space, align with VMX.
If VMM corrupted TD VMCS, machine check during entry can happens. vm exit reason will be EXIT_REASON_MCE_DURING_VMENTRY. If VMM corrupted TD VMCS with debug TD by TDH.VP.WR, the exit reason would be EXIT_REASON_INVALID_STATE or EXIT_REASON_MSR_LOAD_FAIL.
--- arch/x86/kvm/vmx/tdx.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
arch/x86/kvm/vmx/tdx.c +22 -0
@@ -1320,6 +1320,28 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) goto unhandled_exit; } +/* +* When TDX module saw VMEXIT_REASON_FAILED_VMENTER_MC etc, TDH.VP.ENTER +* returns with TDX_SUCCESS | exit_reason with failed_vmentry = 1. +* Because TDX module maintains TD VMCS correctness, usually vmentry +* failure shouldn't happen. In some corner cases it can happen. For +* example +* - machine check during entry: EXIT_REASON_MCE_DURING_VMENTRY +* - TDH.VP.WR with debug TD. VMM can corrupt TD VMCS +* - EXIT_REASON_INVALID_STATE +* - EXIT_REASON_MSR_LOAD_FAIL +*/ +if (unlikely (exit_reason.failed_vmentry)) { +pr_err ("TDExit: exit_reason 0x%016llx qualification=%016lx ext_qualification=%016lx\n" , + exit_reason.full,tdexit_exit_qual (vcpu),tdexit_ext_exit_qual (vcpu)); + vcpu->run->exit_reason =KVM_EXIT_FAIL_ENTRY ; + vcpu->run->fail_entry.hardware_entry_failure_reason + = exit_reason.full; + vcpu->run->fail_entry.cpu = vcpu->arch.last_vmentry_cpu; + +return 0 ; + } +WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) {
Signature · 1 line
2.25.1
PATCH v19 100/130 KVM: TDX: handle EXIT_REASON_OTHER_SMI
In reply to [email protected]From: Isaku Yamahata <[email protected]>
If the control reaches EXIT_REASON_OTHER_SMI, #SMI is delivered and handled right after returning from the TDX module to KVM nothing needs to be done in KVM. Continue TDX vcpu execution.
--- arch/x86/include/uapi/asm/vmx.h | 1 + arch/x86/kvm/vmx/tdx.c | 7 +++++++ 2 files changed, 8 insertions(+)
arch/x86/include/uapi/asm/vmx.h +1 -0
@@ -34,6 +34,7 @@#define EXIT_REASON_TRIPLE_FAULT 2#define EXIT_REASON_INIT_SIGNAL 3#define EXIT_REASON_SIPI_SIGNAL 4 +#define EXIT_REASON_OTHER_SMI 6#define EXIT_REASON_INTERRUPT_WINDOW 7#define EXIT_REASON_NMI_WINDOW 8
arch/x86/kvm/vmx/tdx.c +7 -0
@@ -1345,6 +1345,13 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) { +case EXIT_REASON_OTHER_SMI : +/* +* If reach here, it's not a Machine Check System Management +* Interrupt(MSMI). #SMI is delivered and handled right after +* SEAMRET, nothing needs to be done in KVM. +*/ +return 1 ;default :break ; }
Signature · 1 line
2.25.1
Re: [PATCH v19 100/130] KVM: TDX: handle EXIT_REASON_OTHER_SMI
Quoted 4 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
If the control reaches
EXIT_REASON_OTHER_SMI, #SMI is delivered and handled right after returning from the TDX module to KVM
need a "," here
Quoted 15 lines
nothing needs to
be done in KVM. Continue TDX vcpu execution.Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>--- arch/x86/include/uapi/asm/vmx.h | 1 + arch/x86/kvm/vmx/tdx.c | 7 +++++++ 2 files changed, 8 insertions(+)arch/x86/include/uapi/asm/vmx.h +0
-0
arch/x86/include/uapi/asm/vmx.h line 37
Quoted 4 lines
arch/x86/include/uapi/asm/vmx.h +1
-0@@ -34,6 +34,7 @@#define EXIT_REASON_TRIPLE_FAULT 2#define EXIT_REASON_INIT_SIGNAL 3#define EXIT_REASON_SIPI_SIGNAL 4 +#define EXIT_REASON_OTHER_SMI 6
What does "OTHER" mean in this macro?
Quoted 8 lines
arch/x86/include/uapi/asm/vmx.h +0
-0#define EXIT_REASON_INTERRUPT_WINDOW 7#define EXIT_REASON_NMI_WINDOW 8arch/x86/kvm/vmx/tdx.c +0
-0
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1351
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +4
-0@@ -1345,6 +1345,13 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) { +case EXIT_REASON_OTHER_SMI : +/* +* If reach here, it's not a Machine Check System Management +* Interrupt(MSMI).
Since it's the first patch that mentions MSMI, maybe some description about it in the changelog can make it easier to understand.
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +3
-0#SMI is delivered and handled right after +* SEAMRET, nothing needs to be done in KVM. +*/ +return 1 ;default :break ;}
PATCH v19 101/130 KVM: TDX: handle ept violation/misconfig exit
In reply to [email protected]From: Isaku Yamahata <[email protected]>
On EPT violation, call a common function, __vmx_handle_ept_violation() to trigger x86 MMU code. On EPT misconfiguration, exit to ring 3 with KVM_EXIT_UNKNOWN. because EPT misconfiguration can't happen as MMIO is trigged by TDG.VP.VMCALL. No point to set a misconfiguration value for the fast path.
---
v14 -> v15:
- use PFERR_GUEST_ENC_MASK to tell the fault is private
--- arch/x86/kvm/vmx/common.h | 3 +++ arch/x86/kvm/vmx/tdx.c | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+)
arch/x86/kvm/vmx/common.h +3 -0
@@ -87,6 +87,9 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, error_code |= (exit_qualification &EPT_VIOLATION_GVA_TRANSLATED ) !=0 ?PFERR_GUEST_FINAL_MASK :PFERR_GUEST_PAGE_MASK ; +if (kvm_is_private_gpa (vcpu->kvm, gpa)) + error_code |=PFERR_GUEST_ENC_MASK ; +return kvm_mmu_page_fault (vcpu, gpa, error_code,NULL ,0 ); }
arch/x86/kvm/vmx/tdx.c +49 -0
61 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Quoted 6 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
On EPT violation, call a common function,
__vmx_handle_ept_violation()to trigger x86 MMU code. On EPT misconfiguration, exit to ring 3 withKVM_EXIT_UNKNOWN. because EPT misconfiguration can't happen as MMIO is trigged by TDG.VP.VMCALL. No point to set a misconfiguration value for the
s/trigged/triggered
Quoted 9 lines
fast path.
Signed-off-by Isaku Yamahata <[email protected]>---
v14 -> v15:
- usePFERR_GUEST_ENC_MASKto tell the fault is privateSigned-off-by Isaku Yamahata <[email protected]>
duplicated SOB
Quoted 86 lines
--- arch/x86/kvm/vmx/common.h | 3 +++ arch/x86/kvm/vmx/tdx.c | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+)arch/x86/kvm/vmx/common.h +3
-0@@ -87,6 +87,9 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa, error_code |= (exit_qualification &EPT_VIOLATION_GVA_TRANSLATED ) !=0 ?PFERR_GUEST_FINAL_MASK :PFERR_GUEST_PAGE_MASK ; +if (kvm_is_private_gpa (vcpu->kvm, gpa)) + error_code |=PFERR_GUEST_ENC_MASK ; +return kvm_mmu_page_fault (vcpu, gpa, error_code,NULL ,0 ); }arch/x86/kvm/vmx/tdx.c +49
-0@@ -1285,6 +1285,51 @@ void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,__vmx_deliver_posted_interrupt (vcpu, &tdx->pi_desc, vector); } +static int tdx_handle_ept_violation (struct kvm_vcpu *vcpu) +{ +unsigned long exit_qual; + +if (kvm_is_private_gpa (vcpu->kvm,tdexit_gpa (vcpu))) { +/* +* Always treat SEPT violations as write faults. Ignore the +* EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. +* TD private pages are always RWX in the SEPT tables, +* i.e. they're always mapped writable. Just as importantly, +* treating SEPT violations as write faults is necessary to +* avoid COW allocations, which will cause TDAUGPAGE failures +* due to aliasing a single HPA to multiple GPAs. +*/ +#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual =TDX_SEPT_VIOLATION_EXIT_QUAL ; + }else { + exit_qual =tdexit_exit_qual (vcpu); +if (exit_qual &EPT_VIOLATION_ACC_INSTR ) { +pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" , +tdexit_gpa (vcpu),kvm_rip_read (vcpu)); + vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; + vcpu->run->ex.exception =PF_VECTOR ; + vcpu->run->ex.error_code = exit_qual; +return 0 ; + } + } + +trace_kvm_page_fault (vcpu,tdexit_gpa (vcpu), exit_qual); +return __vmx_handle_ept_violation (vcpu,tdexit_gpa (vcpu), exit_qual); +} + +static int tdx_handle_ept_misconfig (struct kvm_vcpu *vcpu) +{ +WARN_ON_ONCE (1 ); + + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror =KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] =EXIT_REASON_EPT_MISCONFIG ; + vcpu->run->internal.data[1 ] = vcpu->arch.last_vmentry_cpu; + +return 0 ; +} +int tdx_handle_exit (struct kvm_vcpu *vcpu,fastpath_t fastpath) {union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason;@@ -1345,6 +1390,10 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) { +case EXIT_REASON_EPT_VIOLATION : +return tdx_handle_ept_violation (vcpu); +case EXIT_REASON_EPT_MISCONFIG : +return tdx_handle_ept_misconfig (vcpu);case EXIT_REASON_OTHER_SMI :/* * If reach here, it's not a Machine Check System Management
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
In reply to [email protected]Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +9
-0+static int tdx_handle_ept_violation (struct kvm_vcpu *vcpu) +{ +unsigned long exit_qual; + +if (kvm_is_private_gpa (vcpu->kvm,tdexit_gpa (vcpu))) { +/* +* Always treat SEPT violations as write faults. Ignore the +* EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. +* TD private pages are always RWX in the SEPT tables,
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1306
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+* i.e. they're always mapped writable. Just as importantly, +* treating SEPT violations as write faults is necessary to +* avoid COW allocations, which will cause TDAUGPAGE failures +* due to aliasing a single HPA to multiple GPAs. +*/ +#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual =TDX_SEPT_VIOLATION_EXIT_QUAL ; + }else { + exit_qual =tdexit_exit_qual (vcpu); +if (exit_qual &EPT_VIOLATION_ACC_INSTR ) {
Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Maybe KVM_BUG_ON() is more appropriate as it signifies a potential bug.
Quoted 30 lines
arch/x86/kvm/vmx/tdx.c +26
-0+pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" , +tdexit_gpa (vcpu),kvm_rip_read (vcpu)); + vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; + vcpu->run->ex.exception =PF_VECTOR ; + vcpu->run->ex.error_code = exit_qual; +return 0 ; + } + } + +trace_kvm_page_fault (vcpu,tdexit_gpa (vcpu), exit_qual); +return __vmx_handle_ept_violation (vcpu,tdexit_gpa (vcpu), exit_qual); +} + +static int tdx_handle_ept_misconfig (struct kvm_vcpu *vcpu) +{ +WARN_ON_ONCE (1 ); + + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror =KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] =EXIT_REASON_EPT_MISCONFIG ; + vcpu->run->internal.data[1 ] = vcpu->arch.last_vmentry_cpu; + +return 0 ; +} +int
tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath){ union tdx_exit_reason exit_reason = to_tdx(vcpu)->exit_reason;
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1396
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +4
-0@@ -1345,6 +1390,10 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) { +case EXIT_REASON_EPT_VIOLATION : +return tdx_handle_ept_violation (vcpu); +case EXIT_REASON_EPT_MISCONFIG : +return tdx_handle_ept_misconfig (vcpu);
Handling EPT misconfiguration can be dropped because the "default" case handles all unexpected exits in the same way
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +0
-0case EXIT_REASON_OTHER_SMI :/* * If reach here, it's not a Machine Check System Management Signature · 1 line
2.25.1
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Quoted 14 lines · Chao Gao
Quoted 14 lines
arch/x86/kvm/vmx/tdx.c +14
-0+static int tdx_handle_ept_violation (struct kvm_vcpu *vcpu) +{ +unsigned long exit_qual; + +if (kvm_is_private_gpa (vcpu->kvm,tdexit_gpa (vcpu))) { +/* +* Always treat SEPT violations as write faults. Ignore the +* EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. +* TD private pages are always RWX in the SEPT tables, +* i.e. they're always mapped writable. Just as importantly, +* treating SEPT violations as write faults is necessary to +* avoid COW allocations, which will cause TDAUGPAGE failures +* due to aliasing a single HPA to multiple GPAs. +*/
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1306
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual =TDX_SEPT_VIOLATION_EXIT_QUAL ; + }else { + exit_qual =tdexit_exit_qual (vcpu); +if (exit_qual &EPT_VIOLATION_ACC_INSTR ) {Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Yes.
Quoted 1 line
Maybe
KVM_BUG_ON()is more appropriate as it signifies a potential bug.
Bug of what component? CPU. If so, I think KVM_EXIT_INTERNAL_ERROR + KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON is more appropriate.
Quoted 32 lines
Quoted 32 lines
arch/x86/kvm/vmx/tdx.c +26
-0+pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" , +tdexit_gpa (vcpu),kvm_rip_read (vcpu)); + vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; + vcpu->run->ex.exception =PF_VECTOR ; + vcpu->run->ex.error_code = exit_qual; +return 0 ; + } + } + +trace_kvm_page_fault (vcpu,tdexit_gpa (vcpu), exit_qual); +return __vmx_handle_ept_violation (vcpu,tdexit_gpa (vcpu), exit_qual); +} + +static int tdx_handle_ept_misconfig (struct kvm_vcpu *vcpu) +{ +WARN_ON_ONCE (1 ); + + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror =KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] =EXIT_REASON_EPT_MISCONFIG ; + vcpu->run->internal.data[1 ] = vcpu->arch.last_vmentry_cpu; + +return 0 ; +} +int
tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath){ union tdx_exit_reason exit_reason = to_tdx(vcpu)->exit_reason;arch/x86/kvm/vmx/tdx.c +0
-0@@ -1345,6 +1390,10 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1396
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +4
-0switch (exit_reason.basic) { +case EXIT_REASON_EPT_VIOLATION : +return tdx_handle_ept_violation (vcpu); +case EXIT_REASON_EPT_MISCONFIG : +return tdx_handle_ept_misconfig (vcpu);Handling EPT misconfiguration can be dropped because the "default" case handles all unexpected exits in the same way
Ah, right. Will update it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Hi Isaku,
Quoted 26 lines · Isaku Yamahata
Quoted 14 lines · Chao Gao
Quoted 14 lines
(quoted diff) +14
-0+static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) +{ + unsigned long exit_qual; + + if (kvm_is_private_gpa(vcpu->kvm, tdexit_gpa(vcpu))) { + /* + * Always treat SEPT violations as write faults. Ignore the + * EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. + * TD private pages are always RWX in the SEPT tables, + * i.e. they're always mapped writable. Just as importantly, + * treating SEPT violations as write faults is necessary to + * avoid COW allocations, which will cause TDAUGPAGE failures + * due to aliasing a single HPA to multiple GPAs. + */Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual = TDX_SEPT_VIOLATION_EXIT_QUAL; + } else { + exit_qual = tdexit_exit_qual(vcpu); + if (exit_qual & EPT_VIOLATION_ACC_INSTR) {Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Quoted 8 lines
Yes.
Quoted 1 line
Maybe
KVM_BUG_ON()is more appropriate as it signifies a potential bug.Bug of what component? CPU. If so, I think
KVM_EXIT_INTERNAL_ERROR+KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASONis more appropriate.
Is below what you have in mind?
arch/x86/kvm/vmx/tdx.c +10 -3
@@ -1305,11 +1305,18 @@ static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) }else { exit_qual =tdexit_exit_qual (vcpu);if (exit_qual &EPT_VIOLATION_ACC_INSTR ) { +/* +* Instruction fetch in TD from shared memory +* causes a #PF. +*/ pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" ,tdexit_gpa (vcpu),kvm_rip_read (vcpu)); - vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; - vcpu->run->ex.exception =PF_VECTOR ; - vcpu->run->ex.error_code = exit_qual; + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror = +KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] =EXIT_REASON_EPT_VIOLATION ; + vcpu->run->internal.data[1 ] = vcpu->arch.last_vmentry_cpu;return 0 ; } }
Thank you
Reinette
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Quoted 32 lines · Reinette Chatre
Hi Isaku,
Quoted 29 lines · Isaku Yamahata
Quoted 14 lines · Chao Gao
Quoted 14 lines
(quoted diff) +14
-0+static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) +{ + unsigned long exit_qual; + + if (kvm_is_private_gpa(vcpu->kvm, tdexit_gpa(vcpu))) { + /* + * Always treat SEPT violations as write faults. Ignore the + * EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. + * TD private pages are always RWX in the SEPT tables, + * i.e. they're always mapped writable. Just as importantly, + * treating SEPT violations as write faults is necessary to + * avoid COW allocations, which will cause TDAUGPAGE failures + * due to aliasing a single HPA to multiple GPAs. + */Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual = TDX_SEPT_VIOLATION_EXIT_QUAL; + } else { + exit_qual = tdexit_exit_qual(vcpu); + if (exit_qual & EPT_VIOLATION_ACC_INSTR) {Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Yes.
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
Maybe
KVM_BUG_ON()is more appropriate as it signifies a potential bug.Bug of what component? CPU. If so, I think
KVM_EXIT_INTERNAL_ERROR+KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASONis more appropriate.Is below what you have in mind?
Yes. data[0] should be the raw value of exit reason if possible. data[2] should be exit_qual. Hmm, I don't find document on data[] for KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON.
Qemu doesn't assumt ndata = 2. Just report all data within ndata.
Quoted 33 lines
arch/x86/kvm/vmx/tdx.c +10
-3@@ -1305,11 +1305,18 @@ static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) }else { exit_qual =tdexit_exit_qual (vcpu);if (exit_qual &EPT_VIOLATION_ACC_INSTR ) { +/* +* Instruction fetch in TD from shared memory +* causes a #PF. +*/ pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" ,tdexit_gpa (vcpu),kvm_rip_read (vcpu)); - vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; - vcpu->run->ex.exception =PF_VECTOR ; - vcpu->run->ex.error_code = exit_qual; + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror = +KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] =EXIT_REASON_EPT_VIOLATION ; + vcpu->run->internal.data[1 ] = vcpu->arch.last_vmentry_cpu;return 0 ; } }Thank you
Reinette
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Hi Isaku,
Quoted 34 lines · Isaku Yamahata
Quoted 32 lines · Reinette Chatre
Quoted 31 lines · Isaku Yamahata
Quoted 14 lines · Chao Gao
Quoted 14 lines
(quoted diff) +14
-0+static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) +{ + unsigned long exit_qual; + + if (kvm_is_private_gpa(vcpu->kvm, tdexit_gpa(vcpu))) { + /* + * Always treat SEPT violations as write faults. Ignore the + * EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. + * TD private pages are always RWX in the SEPT tables, + * i.e. they're always mapped writable. Just as importantly, + * treating SEPT violations as write faults is necessary to + * avoid COW allocations, which will cause TDAUGPAGE failures + * due to aliasing a single HPA to multiple GPAs. + */Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual = TDX_SEPT_VIOLATION_EXIT_QUAL; + } else { + exit_qual = tdexit_exit_qual(vcpu); + if (exit_qual & EPT_VIOLATION_ACC_INSTR) {Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Yes.
Quoted 1 line
Maybe
KVM_BUG_ON()is more appropriate as it signifies a potential bug.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Bug of what component? CPU. If so, I think
KVM_EXIT_INTERNAL_ERROR+KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASONis more appropriate.Is below what you have in mind?
Yes. data[0] should be the raw value of exit reason if possible. data[2] should be
exit_qual. Hmm, I don't find document on data[] for
Did you perhaps intend to write "data[1] should be exit_qual" or would you like to see ndata = 3? I followed existing usages, for example [1] and [2], that have ndata = 2 with "data[1] = vcpu->arch.last_vmentry_cpu".
Quoted 2 lines
KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON.
Qemu doesn't assumt ndata = 2. Just report all data within ndata.
I am not sure I interpreted your response correctly so I share one possible snippet below as I interpret it. Could you please check where I misinterpreted you? I could also make ndata = 3 to break the existing custom and add "data[2] = vcpu->arch.last_vmentry_cpu" to match existing pattern. What do you think?
arch/x86/kvm/vmx/tdx.c +12 -3
@@ -1305,11 +1305,20 @@ static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) }else { exit_qual =tdexit_exit_qual (vcpu);if (exit_qual &EPT_VIOLATION_ACC_INSTR ) { +union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason; + +/* +* Instruction fetch in TD from shared memory +* causes a #PF. +*/ pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" ,tdexit_gpa (vcpu),kvm_rip_read (vcpu)); - vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; - vcpu->run->ex.exception =PF_VECTOR ; - vcpu->run->ex.error_code = exit_qual; + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror = +KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] = exit_reason.full; + vcpu->run->internal.data[1 ] = exit_qual;return 0 ; } }
Reinette
[1] https://github.com/kvm-x86/linux/blob/next/arch/x86/kvm/vmx/vmx.c#L6587
[2] https://github.com/kvm-x86/linux/blob/next/arch/x86/kvm/svm/svm.c#L3436
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Quoted 50 lines · Reinette Chatre
Hi Isaku,
Quoted 34 lines · Isaku Yamahata
Quoted 32 lines · Reinette Chatre
Quoted 31 lines · Isaku Yamahata
Quoted 14 lines · Chao Gao
Quoted 14 lines
(quoted diff) +14
-0+static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) +{ + unsigned long exit_qual; + + if (kvm_is_private_gpa(vcpu->kvm, tdexit_gpa(vcpu))) { + /* + * Always treat SEPT violations as write faults. Ignore the + * EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. + * TD private pages are always RWX in the SEPT tables, + * i.e. they're always mapped writable. Just as importantly, + * treating SEPT violations as write faults is necessary to + * avoid COW allocations, which will cause TDAUGPAGE failures + * due to aliasing a single HPA to multiple GPAs. + */Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual = TDX_SEPT_VIOLATION_EXIT_QUAL; + } else { + exit_qual = tdexit_exit_qual(vcpu); + if (exit_qual & EPT_VIOLATION_ACC_INSTR) {Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Yes.
Quoted 1 line
Maybe
KVM_BUG_ON()is more appropriate as it signifies a potential bug.Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Bug of what component? CPU. If so, I think
KVM_EXIT_INTERNAL_ERROR+KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASONis more appropriate.Is below what you have in mind?
Yes. data[0] should be the raw value of exit reason if possible. data[2] should be
exit_qual. Hmm, I don't find document on data[] forDid you perhaps intend to write "data[1] should be
exit_qual" or would you like to see ndata = 3? I followed existing usages, for example [1] and [2], that have ndata = 2 with "data[1] =vcpu->arch.last_vmentry_cpu".
Quoted 9 lines
Quoted 2 lines
KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON.
Qemu doesn't assumt ndata = 2. Just report all data within ndata.I am not sure I interpreted your response correctly so I share one possible snippet below as I interpret it. Could you please check where I misinterpreted you? I could also make ndata = 3 to break the existing custom and add "data[2] =
vcpu->arch.last_vmentry_cpu" to match existing pattern. What do you think?
Sorry, I wasn't clear enough. I meant
ndata = 3;
data[0] = exit_reason.full;
data[1] = vcpu->arch.last_vmentry_cpu;
data[2] = exit_qual;
Because I hesitate to change the meaning of data[1] from other usage, I appended exit_qual as data[2].
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Hi Isaku,
Quoted 62 lines · Isaku Yamahata
Quoted 50 lines · Reinette Chatre
Hi Isaku,
Quoted 34 lines · Isaku Yamahata
Quoted 32 lines · Reinette Chatre
Quoted 31 lines · Isaku Yamahata
Quoted 14 lines · Chao Gao
Quoted 14 lines
(quoted diff) +14
-0+static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) +{ + unsigned long exit_qual; + + if (kvm_is_private_gpa(vcpu->kvm, tdexit_gpa(vcpu))) { + /* + * Always treat SEPT violations as write faults. Ignore the + * EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. + * TD private pages are always RWX in the SEPT tables, + * i.e. they're always mapped writable. Just as importantly, + * treating SEPT violations as write faults is necessary to + * avoid COW allocations, which will cause TDAUGPAGE failures + * due to aliasing a single HPA to multiple GPAs. + */Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE + exit_qual = TDX_SEPT_VIOLATION_EXIT_QUAL; + } else { + exit_qual = tdexit_exit_qual(vcpu); + if (exit_qual & EPT_VIOLATION_ACC_INSTR) {Unless the CPU has a bug, instruction fetch in TD from shared memory causes a #PF. I think you can add a comment for this.
Yes.
Quoted 1 line
Maybe
KVM_BUG_ON()is more appropriate as it signifies a potential bug.Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
Bug of what component? CPU. If so, I think
KVM_EXIT_INTERNAL_ERROR+KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASONis more appropriate.Is below what you have in mind?
Yes. data[0] should be the raw value of exit reason if possible. data[2] should be
exit_qual. Hmm, I don't find document on data[] forDid you perhaps intend to write "data[1] should be
exit_qual" or would you like to see ndata = 3? I followed existing usages, for example [1] and [2], that have ndata = 2 with "data[1] =vcpu->arch.last_vmentry_cpu".Quoted 9 lines
Quoted 2 lines
KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON.
Qemu doesn't assumt ndata = 2. Just report all data within ndata.I am not sure I interpreted your response correctly so I share one possible snippet below as I interpret it. Could you please check where I misinterpreted you? I could also make ndata = 3 to break the existing custom and add "data[2] =
vcpu->arch.last_vmentry_cpu" to match existing pattern. What do you think?
Quoted 8 lines
Sorry, I wasn't clear enough. I meant
ndata = 3;
data[0] = exit_reason.full;
data[1] =vcpu->arch.last_vmentry_cpu;
data[2] =exit_qual;Because I hesitate to change the meaning of data[1] from other usage, I appended
exit_qualas data[2].
I understand it now. Thank you very much Isaku.
Reinette
Re: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
In reply to Reinette ChatreQuoted 5 lines · Reinette Chatre
arch/x86/kvm/vmx/tdx.c +0
-0
arch/x86/kvm/vmx/tdx.c static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) · line 1313
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +6
-0@@ -1305,11 +1305,20 @@ static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) }else { exit_qual =tdexit_exit_qual (vcpu);if (exit_qual &EPT_VIOLATION_ACC_INSTR ) { +union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason; + +/* +* Instruction fetch in TD from shared memory +* causes a #PF. +*/
It is better to hoist this comment above the if-statement.
/*
* Instruction fetch in TD from shared memory never causes EPT
* violation. Warn if such an EPT violation occurs as the CPU
* probably is buggy.
*/
if (exit_qual & EPT_VIOLATION_ACC_INSTR) {
...
}but, I am wondering why KVM needs to perform this check. I prefer to drop this check if KVM doesn't rely on it to handle EPT violation correctly.
arch/x86/kvm/vmx/tdx.c static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) · line 1315
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +0
-0pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" ,tdexit_gpa (vcpu),kvm_rip_read (vcpu));
how about using vcpu_unimpl()? it is a wrapper for printing such a message.
Quoted 17 lines
arch/x86/kvm/vmx/tdx.c +6
-3- vcpu->run->exit_reason =KVM_EXIT_EXCEPTION ; - vcpu->run->ex.exception =PF_VECTOR ; - vcpu->run->ex.error_code = exit_qual; + vcpu->run->exit_reason =KVM_EXIT_INTERNAL_ERROR ; + vcpu->run->internal.suberror = +KVM_INTERNAL_ERROR_UNEXPECTED_EXIT_REASON ; + vcpu->run->internal.ndata =2 ; + vcpu->run->internal.data[0 ] = exit_reason.full; + vcpu->run->internal.data[1 ] = exit_qual;return 0 ; } }Reinette
[1] https://github.com/kvm-x86/linux/blob/next/arch/x86/kvm/vmx/vmx.c#L6587
[2] https://github.com/kvm-x86/linux/blob/next/arch/x86/kvm/svm/svm.c#L3436
Sean Christopherson
maintainerRe: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
Quoted 28 lines · Chao Gao
Quoted 5 lines · Reinette Chatre
arch/x86/kvm/vmx/tdx.c +0
-0arch/x86/kvm/vmx/tdx.c static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) · line 1313
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +6
-0@@ -1305,11 +1305,20 @@ static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) }else { exit_qual =tdexit_exit_qual (vcpu);if (exit_qual &EPT_VIOLATION_ACC_INSTR ) { +union tdx_exit_reason exit_reason =to_tdx (vcpu)->exit_reason; + +/* +* Instruction fetch in TD from shared memory +* causes a #PF. +*/ It is better to hoist this comment above the if-statement.
/*
* Instruction fetch in TD from shared memory never causes EPT
* violation. Warn if such an EPT violation occurs as the CPU
* probably is buggy.
*/if (exit_qual & EPT_VIOLATION_ACC_INSTR) { ... }
Quoted 7 lines
but, I am wondering why KVM needs to perform this check. I prefer to drop this check if KVM doesn't rely on it to handle EPT violation correctly.
arch/x86/kvm/vmx/tdx.c static int tdx_handle_ept_violation(struct kvm_vcpu *vcpu) · line 1315
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +0
-0pr_warn ("kvm: TDX instr fetch to shared GPA = 0x%lx @ RIP = 0x%lx\n" ,tdexit_gpa (vcpu),kvm_rip_read (vcpu));how about using
vcpu_unimpl()? it is a wrapper for printing such a message.
This isn't an "unimplemented" scenario in KVM, this is a "hardware is broken" scenario. Just WARN_ON_ONCE() and move on. Or I suppose since EPT misconfig needs to do something as well:
if (KVM_BUG_ON(exit_qual & EPT_VIOLATION_ACC_INSTR, vcpu->kvm)) return -EIO;
Sean Christopherson
maintainerRe: [PATCH v19 101/130] KVM: TDX: handle ept violation/misconfig exit
In reply to [email protected]Quoted 5 lines · On Mon, Feb 26, 2024, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +5
-0+static int tdx_handle_ept_violation (struct kvm_vcpu *vcpu) +{ +unsigned long exit_qual; + +if (kvm_is_private_gpa (vcpu->kvm,tdexit_gpa (vcpu))) {
arch/x86/kvm/vmx/tdx.c void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 1302
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+/* +* Always treat SEPT violations as write faults. Ignore the +* EXIT_QUALIFICATION reported by TDX-SEAM for SEPT violations. +* TD private pages are always RWX in the SEPT tables, +* i.e. they're always mapped writable. Just as importantly, +* treating SEPT violations as write faults is necessary to +* avoid COW allocations, which will cause TDAUGPAGE failures +* due to aliasing a single HPA to multiple GPAs. +*/ +#define TDX_SEPT_VIOLATION_EXIT_QUAL EPT_VIOLATION_ACC_WRITE
This does not needd a #define. It's use in literally one place, one line below.
PATCH v19 102/130 KVM: TDX: handle EXCEPTION_NMI and EXTERNAL_INTERRUPT
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Because guest TD state is protected, exceptions in guest TDs can't be intercepted. TDX VMM doesn't need to handle exceptions. tdx_handle_exit_irqoff() handles NMI and machine check. Ignore NMI and machine check and continue guest TD execution.
For external interrupt, increment stats same to the VMX case.
--- arch/x86/kvm/vmx/tdx.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
arch/x86/kvm/vmx/tdx.c +23 -0
@@ -918,6 +918,25 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu)vmx_handle_exception_irqoff (vcpu,tdexit_intr_info (vcpu)); } +static int tdx_handle_exception (struct kvm_vcpu *vcpu) +{ +u32 intr_info =tdexit_intr_info (vcpu); + +if (is_nmi (intr_info) ||is_machine_check (intr_info)) +return 1 ; + +kvm_pr_unimpl ("unexpected exception 0x%x(exit_reason 0x%llx qual 0x%lx)\n" , + intr_info, +to_tdx (vcpu)->exit_reason.full,tdexit_exit_qual (vcpu)); +return -EFAULT ; +} + +static int tdx_handle_external_interrupt (struct kvm_vcpu *vcpu) +{ + ++vcpu->stat.irq_exits; +return 1 ; +} +static int tdx_handle_triple_fault (struct kvm_vcpu *vcpu) { vcpu->run->exit_reason =KVM_EXIT_SHUTDOWN ;
@@ -1390,6 +1409,10 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) { +case EXIT_REASON_EXCEPTION_NMI : +return tdx_handle_exception (vcpu); +case EXIT_REASON_EXTERNAL_INTERRUPT : +return tdx_handle_external_interrupt (vcpu);case EXIT_REASON_EPT_VIOLATION :return tdx_handle_ept_violation (vcpu);case EXIT_REASON_EPT_MISCONFIG :
Signature · 1 line
2.25.1
Re: [PATCH v19 102/130] KVM: TDX: handle EXCEPTION_NMI and EXTERNAL_INTERRUPT
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:44AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Because guest TD state is protected, exceptions in guest TDs can't be intercepted. TDX VMM doesn't need to handle exceptions.
tdx_handle_exit_irqoff()handles NMI and machine check. Ignore NMI and
tdx_handle_exit_irqoff() doesn't handle NMIs.
Quoted 15 lines
machine check and continue guest TD execution.
For external interrupt, increment stats same to the VMX case.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 23 +++++++++++++++++++++++1 file changed, 23 insertions(+)arch/x86/kvm/vmx/tdx.c +0
-0
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 926
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +6
-0@@ -918,6 +918,25 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu)vmx_handle_exception_irqoff (vcpu,tdexit_intr_info (vcpu)); } +static int tdx_handle_exception (struct kvm_vcpu *vcpu) +{ +u32 intr_info =tdexit_intr_info (vcpu); + +if (is_nmi (intr_info) ||is_machine_check (intr_info)) +return 1 ;
Add a comment in code as well.
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 931
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +kvm_pr_unimpl ("unexpected exception 0x%x(exit_reason 0x%llx qual 0x%lx)\n" , + intr_info, +to_tdx (vcpu)->exit_reason.full,tdexit_exit_qual (vcpu)); +return -EFAULT ;
-EFAULT looks incorrect.
Quoted 26 lines
arch/x86/kvm/vmx/tdx.c +12
-0+} + +static int tdx_handle_external_interrupt (struct kvm_vcpu *vcpu) +{ + ++vcpu->stat.irq_exits; +return 1 ; +} +static int tdx_handle_triple_fault (struct kvm_vcpu *vcpu) { vcpu->run->exit_reason =KVM_EXIT_SHUTDOWN ;@@ -1390,6 +1409,10 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)WARN_ON_ONCE (fastpath !=EXIT_FASTPATH_NONE );switch (exit_reason.basic) { +case EXIT_REASON_EXCEPTION_NMI : +return tdx_handle_exception (vcpu); +case EXIT_REASON_EXTERNAL_INTERRUPT : +return tdx_handle_external_interrupt (vcpu);case EXIT_REASON_EPT_VIOLATION :return tdx_handle_ept_violation (vcpu);case EXIT_REASON_EPT_MISCONFIG :Signature · 1 line
2.25.1
Re: [PATCH v19 102/130] KVM: TDX: handle EXCEPTION_NMI and EXTERNAL_INTERRUPT
Quoted 8 lines · Chao Gao
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:44AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Because guest TD state is protected, exceptions in guest TDs can't be intercepted. TDX VMM doesn't need to handle exceptions.
tdx_handle_exit_irqoff()handles NMI and machine check. Ignore NMI and
tdx_handle_exit_irqoff()doesn't handle NMIs.
Will it to tdx_handle_exception().
Quoted 18 lines
Quoted 18 lines
machine check and continue guest TD execution.
For external interrupt, increment stats same to the VMX case.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 23 +++++++++++++++++++++++1 file changed, 23 insertions(+)arch/x86/kvm/vmx/tdx.c +0
-0@@ -918,6 +918,25 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu)vmx_handle_exception_irqoff (vcpu,tdexit_intr_info (vcpu)); }
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 926
Quoted 6 lines
arch/x86/kvm/vmx/tdx.c +6
-0+static int tdx_handle_exception (struct kvm_vcpu *vcpu) +{ +u32 intr_info =tdexit_intr_info (vcpu); + +if (is_nmi (intr_info) ||is_machine_check (intr_info)) +return 1 ;Add a comment in code as well.
Sure.
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 931
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +kvm_pr_unimpl ("unexpected exception 0x%x(exit_reason 0x%llx qual 0x%lx)\n" , + intr_info, +to_tdx (vcpu)->exit_reason.full,tdexit_exit_qual (vcpu)); +return -EFAULT ;
-EFAULTlooks incorrect.
As this is unexpected exception, we should exit to to the user-space with KVM_EXIT_EXCEPTION. Then QEMU will abort with message.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 102/130] KVM: TDX: handle EXCEPTION_NMI and EXTERNAL_INTERRUPT
Quoted 11 lines · Isaku Yamahata
Quoted 7 lines · Chao Gao
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:44AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Because guest TD state is protected, exceptions in guest TDs can't be intercepted. TDX VMM doesn't need to handle exceptions.
tdx_handle_exit_irqoff()handles NMI and machine check. Ignore NMI and
tdx_handle_exit_irqoff()doesn't handle NMIs.Will it to
tdx_handle_exception().
I don't get why tdx_handle_exception()?
NMI is handled in tdx_vcpu_enter_exit() prior to leaving the safety of noinstr, according to patch 098.
https://lore.kernel.org/kvm/88920c598dcb55c15219642f27d0781af6d0c044.1708933498.git.isaku.yamahata@intel.com/
@@ -837,6 +857,12 @@ static noinstr void tdx_vcpu_enter_exit(struct vcpu_tdx *tdx) WARN_ON_ONCE(!kvm_rebooting &&
(tdx->exit_reason.full & TDX_SW_ERROR) == TDX_SW_ERROR);
+ if ((u16)tdx->exit_reason.basic == EXIT_REASON_EXCEPTION_NMI && + is_nmi(tdexit_intr_info(vcpu))) {
+ kvm_before_interrupt(vcpu, KVM_HANDLING_NMI);
+ vmx_do_nmi_irqoff();
+ kvm_after_interrupt(vcpu);
+ }
guest_state_exit_irqoff();
}
Quoted 20 lines
Quoted 20 lines
Quoted 20 lines
machine check and continue guest TD execution.
For external interrupt, increment stats same to the VMX case.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 23 +++++++++++++++++++++++1 file changed, 23 insertions(+)arch/x86/kvm/vmx/tdx.c +0
-0@@ -918,6 +918,25 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu)vmx_handle_exception_irqoff (vcpu,tdexit_intr_info (vcpu)); }
Quoted 1 line
Quoted 1 line
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 921
Quoted 1 line
arch/x86/kvm/vmx/tdx.c +1
-0+static int tdx_handle_exception (struct kvm_vcpu *vcpu)
Should this function be named as tdx_handle_exception_nmi() since it's checking nmi as well?
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+{ +u32 intr_info =tdexit_intr_info (vcpu); + +if (is_nmi (intr_info) ||is_machine_check (intr_info)) +return 1 ;
Re: [PATCH v19 102/130] KVM: TDX: handle EXCEPTION_NMI and EXTERNAL_INTERRUPT
Quoted 26 lines · Binbin Wu
Quoted 11 lines · Isaku Yamahata
Quoted 7 lines · Chao Gao
Quoted 5 lines · On Mon, Feb 26, 2024 at 12:26:44AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Because guest TD state is protected, exceptions in guest TDs can't be intercepted. TDX VMM doesn't need to handle exceptions.
tdx_handle_exit_irqoff()handles NMI and machine check. Ignore NMI and
tdx_handle_exit_irqoff()doesn't handle NMIs.Will it to
tdx_handle_exception().I don't get why
tdx_handle_exception()?NMI is handled in
tdx_vcpu_enter_exit()prior to leaving the safety of noinstr, according to patch 098.
https://lore.kernel.org/kvm/88920c598dcb55c15219642f27d0781af6d0c044.1708933498.git.isaku.yamahata@intel.com/(quoted diff) +0
-0@@ -837,6 +857,12 @@ static noinstr void tdx_vcpu_enter_exit(struct vcpu_tdx *tdx)WARN_ON_ONCE(!kvm_rebooting &&
(tdx->exit_reason.full&TDX_SW_ERROR) ==TDX_SW_ERROR);
Quoted 8 lines
+ if ((u16)tdx->exit_reason.basic ==
EXIT_REASON_EXCEPTION_NMI&& + is_nmi(tdexit_intr_info(vcpu))) {
+kvm_before_interrupt(vcpu, KVM_HANDLING_NMI);
+vmx_do_nmi_irqoff();
+kvm_after_interrupt(vcpu);
+ }
guest_state_exit_irqoff();
}
You're correct. tdx_vcpu_enter_exit() handles EXIT_REASON_EXCEPTION_NMI for NMI, and tdx_handle_exeption() ignores NMI case.
The commit message should be updated with tdx_vcpu_enter_exit().
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
machine check and continue guest TD execution.
For external interrupt, increment stats same to the VMX case.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 23 +++++++++++++++++++++++1 file changed, 23 insertions(+)arch/x86/kvm/vmx/tdx.c +0
-0@@ -918,6 +918,25 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu)vmx_handle_exception_irqoff (vcpu,tdexit_intr_info (vcpu)); }
Quoted 4 lines
Quoted 1 line
Quoted 1 line
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 921
Quoted 1 line
arch/x86/kvm/vmx/tdx.c +1
-0+static int tdx_handle_exception (struct kvm_vcpu *vcpu)Should this function be named as
tdx_handle_exception_nmi()since it's checking nmi as well?
Ok, tdx_handle_exception_nmi() is more consistent.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 103/130 KVM: TDX: Handle EXIT_REASON_OTHER_SMI with MSMI
In reply to [email protected]From: Isaku Yamahata <[email protected]>
When BIOS eMCA MCE-SMI morphing is enabled, the #MC is morphed to MSMI (Machine Check System Management Interrupt). Then the SMI causes TD exit with the read reason of EXIT_REASON_OTHER_SMI with MSMI bit set in the exit qualification to KVM instead of EXIT_REASON_EXCEPTION_NMI with MC exception.
Handle EXIT_REASON_OTHER_SMI with MSMI bit set in the exit qualification as MCE(Machine Check Exception) happened during TD guest running.
--- arch/x86/kvm/vmx/tdx.c | 40 ++++++++++++++++++++++++++++++++++--- arch/x86/kvm/vmx/tdx_arch.h | 2 ++ 2 files changed, 39 insertions(+), 3 deletions(-)
arch/x86/kvm/vmx/tdx.c +37 -3
@@ -916,6 +916,30 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) tdexit_intr_info(vcpu)); else if (exit_reason == EXIT_REASON_EXCEPTION_NMI) vmx_handle_exception_irqoff(vcpu, tdexit_intr_info(vcpu)); + else if (unlikely(tdx->exit_reason.non_recoverable || + tdx->exit_reason.error)) { +/* +* The only reason it gets EXIT_REASON_OTHER_SMI is there is an +* #MSMI(Machine Check System Management Interrupt) with +* exit_qualification bit 0 set in TD guest. +* The #MSMI is delivered right after SEAMCALL returns, +* and an #MC is delivered to host kernel after SMI handler +* returns. +* +* The #MC right after SEAMCALL is fixed up and skipped in #MC +* handler because it's an #MC happens in TD guest we cannot +* handle it with host's context. +* +* Call KVM's machine check handler explicitly here. +*/ +if (tdx->exit_reason.basic ==EXIT_REASON_OTHER_SMI ) { +unsigned long exit_qual; + + exit_qual =tdexit_exit_qual (vcpu); +if (exit_qual &TD_EXIT_OTHER_SMI_IS_MSMI ) +kvm_machine_check (); + } + } }static int tdx_handle_exception (struct kvm_vcpu *vcpu)
@@ -1381,6 +1405,11 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) exit_reason.full, exit_reason.basic,to_kvm_tdx (vcpu->kvm)->hkid, set_hkid_to_hpa(0, to_kvm_tdx(vcpu->kvm)->hkid)); + +/* +* tdx_handle_exit_irqoff() handled EXIT_REASON_OTHER_SMI. It +* must be handled before enabling preemption because it's #MC. +*/ goto unhandled_exit; }
@@ -1419,9 +1448,14 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)return tdx_handle_ept_misconfig (vcpu);case EXIT_REASON_OTHER_SMI :/* -* If reach here, it's not a Machine Check System Management -* Interrupt(MSMI). #SMI is delivered and handled right after -* SEAMRET, nothing needs to be done in KVM. +* Unlike VMX, all the SMI in SEAM non-root mode (i.e. when +* TD guest vcpu is running) will cause TD exit to TDX module, +* then SEAMRET to KVM. Once it exits to KVM, SMI is delivered +* and handled right away. +* +* - If it's an Machine Check System Management Interrupt +* (MSMI), it's handled above due to non_recoverable bit set. +* - If it's not an MSMI, don't need to do anything here. */ return 1 ;default :
arch/x86/kvm/vmx/tdx_arch.h +2 -0
@@ -42,6 +42,8 @@#define TDH_VP_WR 43#define TDH_SYS_LP_SHUTDOWN 44 +#define TD_EXIT_OTHER_SMI_IS_MSMI BIT(1) +/* TDX control structure (TDR/TDCS/TDVPS) field access codes */ #define TDX_NON_ARCH BIT_ULL(63)#define TDX_CLASS_SHIFT 56
Signature · 1 line
2.25.1
Re: [PATCH v19 103/130] KVM: TDX: Handle EXIT_REASON_OTHER_SMI with MSMI
Quoted 22 lines · On Mon, Feb 26, 2024 at 12:26:45AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
When BIOS eMCA MCE-SMI morphing is enabled, the #MC is morphed to MSMI (Machine Check System Management Interrupt). Then the SMI causes TD exit with the read reason of
EXIT_REASON_OTHER_SMIwith MSMI bit set in the exit qualification to KVM instead ofEXIT_REASON_EXCEPTION_NMIwith MC exception.Handle
EXIT_REASON_OTHER_SMIwith MSMI bit set in the exit qualification as MCE(Machine Check Exception) happened during TD guest running.Signed-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 40 ++++++++++++++++++++++++++++++++++---arch/x86/kvm/vmx/tdx_arch.h| 2 ++2 files changed, 39 insertions(+), 3 deletions(-)arch/x86/kvm/vmx/tdx.c +0
-0
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 920
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +2
-0@@ -916,6 +916,30 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) tdexit_intr_info(vcpu)); else if (exit_reason == EXIT_REASON_EXCEPTION_NMI) vmx_handle_exception_irqoff(vcpu, tdexit_intr_info(vcpu)); + else if (unlikely(tdx->exit_reason.non_recoverable || + tdx->exit_reason.error)) {
why not just:
else if (tdx->exit_reason.basic == EXIT_REASON_OTHER_SMI) {i.e., does EXIT_REASON_OTHER_SMI imply exit_reason.non_recoverable or exit_reason.error?
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 929
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +9
-0+/* +* The only reason it gets EXIT_REASON_OTHER_SMI is there is an +* #MSMI(Machine Check System Management Interrupt) with +* exit_qualification bit 0 set in TD guest. +* The #MSMI is delivered right after SEAMCALL returns, +* and an #MC is delivered to host kernel after SMI handler +* returns. +* +* The #MC right after SEAMCALL is fixed up and skipped in #MC
Looks fixing up and skipping #MC on the first instruction after TD-exit is missing in v19?
Quoted 18 lines
arch/x86/kvm/vmx/tdx.c +13
-0+* handler because it's an #MC happens in TD guest we cannot +* handle it with host's context. +* +* Call KVM's machine check handler explicitly here. +*/ +if (tdx->exit_reason.basic ==EXIT_REASON_OTHER_SMI ) { +unsigned long exit_qual; + + exit_qual =tdexit_exit_qual (vcpu); +if (exit_qual &TD_EXIT_OTHER_SMI_IS_MSMI ) +kvm_machine_check (); + } + } }static int
tdx_handle_exception(struct kvm_vcpu *vcpu)
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1412
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c +5
-0@@ -1381,6 +1405,11 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) exit_reason.full, exit_reason.basic,to_kvm_tdx (vcpu->kvm)->hkid, set_hkid_to_hpa(0, to_kvm_tdx(vcpu->kvm)->hkid)); + +/* +* tdx_handle_exit_irqoff() handled EXIT_REASON_OTHER_SMI. It +* must be handled before enabling preemption because it's #MC. +*/
Then EXIT_REASON_OTHER_SMI is handled, why still go to unhandled_exit?
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c +0
-1goto unhandled_exit; }@@ -1419,9 +1448,14 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)return tdx_handle_ept_misconfig (vcpu);case EXIT_REASON_OTHER_SMI :/* -* If reach here, it's not a Machine Check System Management
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1458
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +8
-2-* Interrupt(MSMI). #SMI is delivered and handled right after -* SEAMRET, nothing needs to be done in KVM. +* Unlike VMX, all the SMI in SEAM non-root mode (i.e. when +* TD guest vcpu is running) will cause TD exit to TDX module, +* then SEAMRET to KVM. Once it exits to KVM, SMI is delivered +* and handled right away. +* +* - If it's an Machine Check System Management Interrupt +* (MSMI), it's handled above due to non_recoverable bit set. +* - If it's not an MSMI, don't need to do anything here.
This corrects a comment added in patch 100. Maybe we can just merge patch 100 into this one?
Quoted 20 lines
arch/x86/kvm/vmx/tdx.c +0
-0*/ return 1 ;default :arch/x86/kvm/vmx/tdx_arch.h +2
-0@@ -42,6 +42,8 @@#define TDH_VP_WR 43#define TDH_SYS_LP_SHUTDOWN 44 +#define TD_EXIT_OTHER_SMI_IS_MSMI BIT(1) +/* TDX control structure (TDR/TDCS/TDVPS) field access codes */ #define TDX_NON_ARCH BIT_ULL(63)#define TDX_CLASS_SHIFT 56Signature · 1 line
2.25.1
Re: [PATCH v19 103/130] KVM: TDX: Handle EXIT_REASON_OTHER_SMI with MSMI
Quoted 30 lines · Chao Gao
Quoted 22 lines · On Mon, Feb 26, 2024 at 12:26:45AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
When BIOS eMCA MCE-SMI morphing is enabled, the #MC is morphed to MSMI (Machine Check System Management Interrupt). Then the SMI causes TD exit with the read reason of
EXIT_REASON_OTHER_SMIwith MSMI bit set in the exit qualification to KVM instead ofEXIT_REASON_EXCEPTION_NMIwith MC exception.Handle
EXIT_REASON_OTHER_SMIwith MSMI bit set in the exit qualification as MCE(Machine Check Exception) happened during TD guest running.Signed-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 40 ++++++++++++++++++++++++++++++++++---arch/x86/kvm/vmx/tdx_arch.h| 2 ++2 files changed, 39 insertions(+), 3 deletions(-)arch/x86/kvm/vmx/tdx.c +0
-0arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 920
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +2
-0@@ -916,6 +916,30 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) tdexit_intr_info(vcpu)); else if (exit_reason == EXIT_REASON_EXCEPTION_NMI) vmx_handle_exception_irqoff(vcpu, tdexit_intr_info(vcpu)); + else if (unlikely(tdx->exit_reason.non_recoverable || + tdx->exit_reason.error)) {why not just:
Quoted 5 lines
else if (tdx->exit_reason.basic == EXIT_REASON_OTHER_SMI) {i.e., does
EXIT_REASON_OTHER_SMIimply exit_reason.non_recoverable or exit_reason.error?
Yes, this should be refined.
Quoted 4 lines
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+/* +* The only reason it gets EXIT_REASON_OTHER_SMI is there is an +* #MSMI(Machine Check System Management Interrupt) with +* exit_qualification bit 0 set in TD guest.
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 929
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+* The #MSMI is delivered right after SEAMCALL returns, +* and an #MC is delivered to host kernel after SMI handler +* returns. +* +* The #MC right after SEAMCALL is fixed up and skipped in #MC Looks fixing up and skipping #MC on the first instruction after TD-exit is missing in v19?
Right. We removed it as MSMI will provides if #MC happened in SEAM or not.
Quoted 17 lines
Quoted 17 lines
arch/x86/kvm/vmx/tdx.c +13
-0+* handler because it's an #MC happens in TD guest we cannot +* handle it with host's context. +* +* Call KVM's machine check handler explicitly here. +*/ +if (tdx->exit_reason.basic ==EXIT_REASON_OTHER_SMI ) { +unsigned long exit_qual; + + exit_qual =tdexit_exit_qual (vcpu); +if (exit_qual &TD_EXIT_OTHER_SMI_IS_MSMI ) +kvm_machine_check (); + } + } }
Quoted 12 lines
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1412
Quoted 10 lines
static int
tdx_handle_exception(struct kvm_vcpu *vcpu)arch/x86/kvm/vmx/tdx.c +5
-0@@ -1381,6 +1405,11 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) exit_reason.full, exit_reason.basic,to_kvm_tdx (vcpu->kvm)->hkid, set_hkid_to_hpa(0, to_kvm_tdx(vcpu->kvm)->hkid)); + +/* +* tdx_handle_exit_irqoff() handled EXIT_REASON_OTHER_SMI. It +* must be handled before enabling preemption because it's #MC. +*/ Then
EXIT_REASON_OTHER_SMIis handled, why still go tounhandled_exit?
Let me update the comment.exit_irqoff() doesn't return value to tell vcpu_run loop to continue or exit to user-space. As the guest is dead, we'd like to exit to the user-space.
Quoted 13 lines
Quoted 13 lines
arch/x86/kvm/vmx/tdx.c +3
-3goto unhandled_exit; }@@ -1419,9 +1448,14 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath)return tdx_handle_ept_misconfig (vcpu);case EXIT_REASON_OTHER_SMI :/* -* If reach here, it's not a Machine Check System Management -* Interrupt(MSMI). #SMI is delivered and handled right after -* SEAMRET, nothing needs to be done in KVM. +* Unlike VMX, all the SMI in SEAM non-root mode (i.e. when +* TD guest vcpu is running) will cause TD exit to TDX module, +* then SEAMRET to KVM. Once it exits to KVM, SMI is delivered
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) · line 1458
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+* and handled right away. +* +* - If it's an Machine Check System Management Interrupt +* (MSMI), it's handled above due to non_recoverable bit set. +* - If it's not an MSMI, don't need to do anything here. This corrects a comment added in patch 100. Maybe we can just merge patch 100 into this one?
Yes. Will do.
Quoted 21 lines
Quoted 20 lines
arch/x86/kvm/vmx/tdx.c +0
-0*/ return 1 ;default :arch/x86/kvm/vmx/tdx_arch.h +2
-0@@ -42,6 +42,8 @@#define TDH_VP_WR 43#define TDH_SYS_LP_SHUTDOWN 44 +#define TD_EXIT_OTHER_SMI_IS_MSMI BIT(1) +/* TDX control structure (TDR/TDCS/TDVPS) field access codes */ #define TDX_NON_ARCH BIT_ULL(63)#define TDX_CLASS_SHIFT 56Signature · 1 line
2.25.1
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 103/130] KVM: TDX: Handle EXIT_REASON_OTHER_SMI with MSMI
Quoted 51 lines · Isaku Yamahata
Quoted 29 lines · Chao Gao
Quoted 22 lines · On Mon, Feb 26, 2024 at 12:26:45AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
When BIOS eMCA MCE-SMI morphing is enabled, the #MC is morphed to MSMI (Machine Check System Management Interrupt). Then the SMI causes TD exit with the read reason of
EXIT_REASON_OTHER_SMIwith MSMI bit set in the exit qualification to KVM instead ofEXIT_REASON_EXCEPTION_NMIwith MC exception.Handle
EXIT_REASON_OTHER_SMIwith MSMI bit set in the exit qualification as MCE(Machine Check Exception) happened during TD guest running.Signed-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 40 ++++++++++++++++++++++++++++++++++---arch/x86/kvm/vmx/tdx_arch.h| 2 ++2 files changed, 39 insertions(+), 3 deletions(-)arch/x86/kvm/vmx/tdx.c +0
-0arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 920
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +2
-0@@ -916,6 +916,30 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) tdexit_intr_info(vcpu)); else if (exit_reason == EXIT_REASON_EXCEPTION_NMI) vmx_handle_exception_irqoff(vcpu, tdexit_intr_info(vcpu)); + else if (unlikely(tdx->exit_reason.non_recoverable || + tdx->exit_reason.error)) {why not just:
Quoted 5 lines
else if (tdx->exit_reason.basic == EXIT_REASON_OTHER_SMI) {i.e., does
EXIT_REASON_OTHER_SMIimply exit_reason.non_recoverable or exit_reason.error?Yes, this should be refined.
Quoted 11 lines
arch/x86/kvm/vmx/tdx.c void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) · line 929
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +9
-0+/* +* The only reason it gets EXIT_REASON_OTHER_SMI is there is an +* #MSMI(Machine Check System Management Interrupt) with +* exit_qualification bit 0 set in TD guest. +* The #MSMI is delivered right after SEAMCALL returns, +* and an #MC is delivered to host kernel after SMI handler +* returns. +* +* The #MC right after SEAMCALL is fixed up and skipped in #MC Looks fixing up and skipping #MC on the first instruction after TD-exit is missing in v19?
Quoted 1 line
Right. We removed it as MSMI will provides if #MC happened in SEAM or not.
According to the patch of host #MC handler
https://lore.kernel.org/lkml/171265126376.10875.16864387954272613660.tip-bot2@tip-bot2/,
the #MC triggered by MSMI can be handled by kernel #MC handler. There is no need to call kvm_machine_check().
Does the following fixup make sense to you?
--------
KVM: TDX: Handle EXIT_REASON_OTHER_SMI
Handle "Other SMI" VM exit for TDX.
Unlike VMX, an SMI occurs in SEAM non-root mode cause VM exit to TDX module, then SEAMRET to KVM. Once it exits to KVM, SMI is delivered and handled by kernel handler right away.
Specifically, when BIOS eMCA MCE-SMI morphing is enabled, the #MC occurs in TDX guest is delivered as an Machine Check System Management Interrupt (MSMI) with the exit reason of EXIT_REASON_OTHER_SMI with MSMI (bit 0) set in the exit qualification. On VM exit, TDX module checks whether the "Other SMI" is caused by a MSMI or not. If so, TDX module makes TD as fatal, preventing further TD entries, and then completes the TD exit flow to KVM with the TDH.VP.ENTER outputs indicating TDX_NON_RECOVERABLE_TD. After TD exit, the MSMI is delivered and eventually handled by the kernel #MC handler[1].
So, to handle "Other SMI" VM exit:
- For non-MSMI case, KVM doesn't need to do anything, just continue TDX vCPU execution.
- For MSMI case, since the TDX guest is dead, follow other non-recoverable cases, exit to userspace.
[1] The patch supports handling MSMI signaled during SEAM operation. It's already in tip tree.
https://lore.kernel.org/lkml/171265126376.10875.16864387954272613660.tip-bot2@tip-bot2/
arch/x86/kvm/vmx/tdx.c +0 -0
@@ -975,30 +975,6 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu)
tdexit_intr_info(vcpu));
else if (exit_reason == EXIT_REASON_EXCEPTION_NMI) vmx_handle_exception_irqoff(vcpu, tdexit_intr_info(vcpu)); - else if (unlikely(tdx->exit_reason.non_recoverable ||
- tdx->exit_reason.error)) {
- /*
- * The only reason it gets EXIT_REASON_OTHER_SMI is there is an - * #MSMI(Machine Check System Management Interrupt) with - * exit_qualification bit 0 set in TD guest.
- * The #MSMI is delivered right after SEAMCALL returns, - * and an #MC is delivered to host kernel after SMI handler - * returns.
- *
- * The #MC right after SEAMCALL is fixed up and skipped in #MC - * handler because it's an #MC happens in TD guest we cannot - * handle it with host's context.
- *
- * Call KVM's machine check handler explicitly here. - */
- if (tdx->exit_reason.basic == EXIT_REASON_OTHER_SMI) { - unsigned long exit_qual;
-
- exit_qual = tdexit_exit_qual(vcpu);
- if (exit_qual & TD_EXIT_OTHER_SMI_IS_MSMI) - kvm_machine_check();
- }
- }
}
static int tdx_handle_exception(struct kvm_vcpu *vcpu)
@@ -1923,10 +1899,6 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) to_kvm_tdx(vcpu->kvm)->hkid, set_hkid_to_hpa(0, to_kvm_tdx(vcpu->kvm)->hkid));
- /*
- * tdx_handle_exit_irqoff() handled EXIT_REASON_OTHER_SMI. It - * must be handled before enabling preemption because it's #MC. - */
goto unhandled_exit;
}
@@ -1970,14 +1942,14 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) return tdx_handle_ept_misconfig(vcpu);
case EXIT_REASON_OTHER_SMI:
/*
- * Unlike VMX, all the SMI in SEAM non-root mode (i.e. when - * TD guest vcpu is running) will cause TD exit to TDX module, - * then SEAMRET to KVM. Once it exits to KVM, SMI is delivered - * and handled right away.
+ * Unlike VMX, SMI occurs in SEAM non-root mode (i.e. when + * TD guest vCPU is running) will cause VM exit to TDX module, + * then SEAMRET to KVM. Once it exits to KVM, SMI is delivered + * and handled by kernel handler right away.
*
- * - If it's an Machine Check System Management Interrupt - * (MSMI), it's handled above due to non_recoverable bit set. - * - If it's not an MSMI, don't need to do anything here. + * - A MSMI will not reach here, it's handled as non_recoverable + * case above.
+ * - If it's not a MSMI, no need to do anything here.
*/
return 1;
default:
arch/x86/kvm/vmx/tdx_arch.h +0 -0
@@ -41,8 +41,6 @@
#define TDH_PHYMEM_PAGE_WBINVD 41
#define TDH_VP_WR 43
-#define TD_EXIT_OTHER_SMI_IS_MSMI BIT_ULL(1)
-
/* TDX control structure (TDR/TDCS/TDVPS) field access codes */ #define TDX_NON_ARCH BIT_ULL(63) #define TDX_CLASS_SHIFT 56
Re: [PATCH v19 103/130] KVM: TDX: Handle EXIT_REASON_OTHER_SMI with MSMI
Quoted 11 lines · Binbin Wu
Quoted 11 lines
Quoted 11 lines
Review
Quoted 9 lines
(quoted diff) +9
-0+ /* + * The only reason it gets EXIT_REASON_OTHER_SMI is there is an + * #MSMI(Machine Check System Management Interrupt) with + * exit_qualification bit 0 set in TD guest. + * The #MSMI is delivered right after SEAMCALL returns, + * and an #MC is delivered to host kernel after SMI handler + * returns. + * + * The #MC right after SEAMCALL is fixed up and skipped in #MCLooks fixing up and skipping #MC on the first instruction after TD-exit is missing in v19?
Quoted 8 lines
Quoted 1 line
Right. We removed it as MSMI will provides if #MC happened in SEAM or not.
According to the patch of host #MC handler
https://lore.kernel.org/lkml/171265126376.10875.16864387954272613660.tip-bot2@tip-bot2/,
the #MC triggered by MSMI can be handled by kernel #MC handler. There is no need to callkvm_machine_check().Does the following fixup make sense to you?
Yes. Now this patch becomes mostly none. So it makes more sense to squash this patch into [PATCH v19 100/130] KVM: TDX: handle EXIT_REASON_OTHER_SMI.
Quoted 127 lines
--------
KVM: TDX: Handle
EXIT_REASON_OTHER_SMIHandle "Other SMI" VM exit for TDX.
Unlike VMX, an SMI occurs in SEAM non-root mode cause VM exit to TDX module, then SEAMRET to KVM. Once it exits to KVM, SMI is delivered and handled by kernel handler right away.
Specifically, when BIOS eMCA MCE-SMI morphing is enabled, the #MC occurs in TDX guest is delivered as an Machine Check System Management Interrupt (MSMI) with the exit reason of
EXIT_REASON_OTHER_SMIwith MSMI (bit 0) set in the exit qualification. On VM exit, TDX module checks whether the "Other SMI" is caused by a MSMI or not. If so, TDX module makes TD as fatal, preventing further TD entries, and then completes the TD exit flow to KVM with the TDH.VP.ENTER outputs indicatingTDX_NON_RECOVERABLE_TD. After TD exit, the MSMI is delivered and eventually handled by the kernel #MC handler[1].So, to handle "Other SMI" VM exit:
- For non-MSMI case, KVM doesn't need to do anything, just continue TDX vCPU execution.
- For MSMI case, since the TDX guest is dead, follow other non-recoverable cases, exit to userspace.[1] The patch supports handling MSMI signaled during SEAM operation. It's already in tip tree.
https://lore.kernel.org/lkml/171265126376.10875.16864387954272613660.tip-bot2@tip-bot2/arch/x86/kvm/vmx/tdx.c +0
-4@@ -975,30 +975,6 @@ void tdx_handle_exit_irqoff(struct kvm_vcpu *vcpu) tdexit_intr_info(vcpu)); else if (exit_reason == EXIT_REASON_EXCEPTION_NMI) vmx_handle_exception_irqoff(vcpu, tdexit_intr_info(vcpu)); - else if (unlikely(tdx->exit_reason.non_recoverable || - tdx->exit_reason.error)) { - /* -* The only reason it gets EXIT_REASON_OTHER_SMI is there is an
- * #MSMI(Machine Check System Management Interrupt) with - *exit_qualificationbit 0 set in TD guest.
- * The #MSMI is delivered right after SEAMCALL returns, - * and an #MC is delivered to host kernel after SMI handler - * returns.
- *
- * The #MC right after SEAMCALL is fixed up and skipped in #MC
- * handler because it's an #MC happens in TD guest we cannot - * handle it with host's context.
- *
- * Call KVM's machine check handler explicitly here. - */
- if (tdx->exit_reason.basic==EXIT_REASON_OTHER_SMI) { - unsigned longexit_qual;
-
-exit_qual=tdexit_exit_qual(vcpu);
- if (exit_qual&TD_EXIT_OTHER_SMI_IS_MSMI) -kvm_machine_check();
- }
- }
}static int
tdx_handle_exception(struct kvm_vcpu *vcpu)arch/x86/kvm/vmx/tdx.c +0
-2@@ -1923,10 +1899,6 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) to_kvm_tdx(vcpu->kvm)->hkid, set_hkid_to_hpa(0, to_kvm_tdx(vcpu->kvm)->hkid)); - /* -* tdx_handle_exit_irqoff() handled EXIT_REASON_OTHER_SMI. It
- * must be handled before enabling preemption because it's #MC.
- */
gotounhandled_exit;
}arch/x86/kvm/vmx/tdx.c +0
-0@@ -1970,14 +1942,14 @@ int tdx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t fastpath) return tdx_handle_ept_misconfig(vcpu);case
EXIT_REASON_OTHER_SMI:
/*
- * Unlike VMX, all the SMI in SEAM non-root mode (i.e. when - * TD guest vcpu is running) will cause TD exit to TDX module,
- * then SEAMRET to KVM. Once it exits to KVM, SMI is delivered
- * and handled right away.
+ * Unlike VMX, SMI occurs in SEAM non-root mode (i.e. when + * TD guest vCPU is running) will cause VM exit to TDX module,
+ * then SEAMRET to KVM. Once it exits to KVM, SMI is delivered
+ * and handled by kernel handler right away.
*
- * - If it's an Machine Check System Management Interrupt - * (MSMI), it's handled above due tonon_recoverablebit set.
- * - If it's not an MSMI, don't need to do anything here. + * - A MSMI will not reach here, it's handled asnon_recoverable
+ * case above.
+ * - If it's not a MSMI, no need to do anything here. */
return 1;
default:arch/x86/kvm/vmx/tdx_arch.h +0
-2@@ -41,8 +41,6 @@ #define TDH_PHYMEM_PAGE_WBINVD 41 #define TDH_VP_WR 43 -#define TD_EXIT_OTHER_SMI_IS_MSMI BIT_ULL(1) - /* TDX control structure (TDR/TDCS/TDVPS) field access codes */ #define TDX_NON_ARCH BIT_ULL(63) #define TDX_CLASS_SHIFT 56
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 104/130 KVM: TDX: Add a place holder for handler of TDX hypercalls (TDG.VP.VMCALL)
In reply to [email protected]From: Isaku Yamahata <[email protected]>
The TDX module specification defines TDG.VP.VMCALL API (TDVMCALL for short) for the guest TD to call hypercall to VMM. When the guest TD issues TDG.VP.VMCALL, the guest TD exits to VMM with a new exit reason of TDVMCALL. The arguments from the guest TD and returned values from the VMM are passed in the guest registers. The guest RCX registers indicates which registers are used. Define helper functions to access those registers as ABI.
Define the TDVMCALL exit reason, which is carved out from the VMX exit reason namespace as the TDVMCALL exit from TDX guest to TDX-SEAM is really just a VM-Exit. Add a place holder to handle TDVMCALL exit.
--- arch/x86/include/uapi/asm/vmx.h | 4 ++- arch/x86/kvm/vmx/tdx.c | 53 +++++++++++++++++++++++++++++++++ arch/x86/kvm/vmx/tdx.h | 13 ++++++++ 3 files changed, 69 insertions(+), 1 deletion(-)
arch/x86/include/uapi/asm/vmx.h +3 -1
@@ -93,6 +93,7 @@#define EXIT_REASON_TPAUSE 68#define EXIT_REASON_BUS_LOCK 74#define EXIT_REASON_NOTIFY 75 +#define EXIT_REASON_TDCALL 77#define VMX_EXIT_REASONS \ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \
@@ -156,7 +157,8 @@ { EXIT_REASON_UMWAIT, "UMWAIT" }, \ { EXIT_REASON_TPAUSE, "TPAUSE" }, \ { EXIT_REASON_BUS_LOCK, "BUS_LOCK" }, \ - { EXIT_REASON_NOTIFY, "NOTIFY" } + { EXIT_REASON_NOTIFY, "NOTIFY" }, \ + { EXIT_REASON_TDCALL, "TDCALL" } #define VMX_EXIT_REASON_FLAGS \ { VMX_EXIT_REASONS_FAILED_VMENTRY, "FAILED_VMENTRY" }
arch/x86/kvm/vmx/tdx.c +53 -0
77 lines not on this page · raw message
arch/x86/kvm/vmx/tdx.h +13 -0
@@ -83,6 +83,19 @@ struct vcpu_tdx {struct list_head cpu_list; +union { +struct { +union { +struct { +u16 gpr_mask; +u16 xmm_mask; + }; +u32 regs_mask; + }; +u32 reserved; + }; +u64 rcx; + } tdvmcall;union tdx_exit_reason exit_reason;bool initialized;
Signature · 1 line
2.25.1
Re: [PATCH v19 104/130] KVM: TDX: Add a place holder for handler of TDX hypercalls (TDG.VP.VMCALL)
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 936
Quoted 9 lines
static inline bool
is_td_vcpu_created(struct vcpu_tdx *tdx){ return tdx->td_vcpu_created;arch/x86/kvm/vmx/tdx.c +2
-0@@ -897,6 +932,11 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)tdx_complete_interrupts (vcpu); +if (tdx->exit_reason.basic ==EXIT_REASON_TDCALL ) + tdx->tdvmcall.rcx = vcpu->arch.regs[VCPU_REGS_RCX ];
kvm_rcx_read()?
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 938
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+else + tdx->tdvmcall.rcx =0 ;
RCX on TDVMCALL exit is supposed to be consumed by TDX module. I don't get why caching it is necessary. Can tdx->tdvmcall be simply dropped?
Re: [PATCH v19 104/130] KVM: TDX: Add a place holder for handler of TDX hypercalls (TDG.VP.VMCALL)
Quoted 13 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 936
Quoted 9 lines
static inline bool
is_td_vcpu_created(struct vcpu_tdx *tdx){ return tdx->td_vcpu_created;arch/x86/kvm/vmx/tdx.c +2
-0@@ -897,6 +932,11 @@ fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu)tdx_complete_interrupts (vcpu); +if (tdx->exit_reason.basic ==EXIT_REASON_TDCALL ) + tdx->tdvmcall.rcx = vcpu->arch.regs[VCPU_REGS_RCX ];
kvm_rcx_read()?
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c fastpath_t tdx_vcpu_run(struct kvm_vcpu *vcpu) · line 938
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+else + tdx->tdvmcall.rcx =0 ;RCX on TDVMCALL exit is supposed to be consumed by TDX module. I don't get why caching it is necessary. Can
tdx->tdvmcallbe simply dropped?
Now it's not used. Will drop tdvmcall.
It was originally used to remember a original register mask of TDVMCALL, and tdx_complete_vp_vmcall() used it as a valid value to copy back the output values. The current tdx_complete_vp_vmcall() uses kvm_rcx_read() because even if the user space changes rcx, it doesn't harm to KVM. KVM does what the user space tells.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 105/130 KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
In reply to [email protected]From: Isaku Yamahata <[email protected]>
The TDX Guest-Host communication interface (GHCI) specification defines
the ABI for the guest TD to issue hypercall. It reserves vendor specific
arguments for VMM specific use. Use it as KVM hypercall and handle it.
--- arch/x86/kvm/vmx/tdx.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+)
arch/x86/kvm/vmx/tdx.c +33 -0
@@ -1008,8 +1008,41 @@ static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu)return 0 ; } +static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; + +/* +* ABI for KVM tdvmcall argument: +* In Guest-Hypervisor Communication Interface(GHCI) specification, +* Non-zero leaf number (R10 != 0) is defined to indicate +* vendor-specific. KVM uses this for KVM hypercall. NOTE: KVM +* hypercall number starts from one. Zero isn't used for KVM hypercall +* number. +* +* R10: KVM hypercall number +* arguments: R11, R12, R13, R14. +*/ + nr =kvm_r10_read (vcpu); + a0 =kvm_r11_read (vcpu); + a1 =kvm_r12_read (vcpu); + a2 =kvm_r13_read (vcpu); + a3 =kvm_r14_read (vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, true,0 ); + +tdvmcall_set_return_code (vcpu, ret); + +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +return 0 ; +return 1 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) { +if (tdvmcall_exit_type (vcpu)) +return tdx_emulate_vmcall (vcpu); +switch (tdvmcall_leaf (vcpu)) {default :break ;
Signature · 1 line
2.25.1
Re: [PATCH v19 105/130] KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1014
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +
do you need to emulate xen/hyper-v hypercalls here?
Nothing tells userspace that xen/hyper-v hypercalls are not supported and so userspace may expose related CPUID leafs to TD guests.
Quoted 13 lines
arch/x86/kvm/vmx/tdx.c +13
-0+/* +* ABI for KVM tdvmcall argument: +* In Guest-Hypervisor Communication Interface(GHCI) specification, +* Non-zero leaf number (R10 != 0) is defined to indicate +* vendor-specific. KVM uses this for KVM hypercall. NOTE: KVM +* hypercall number starts from one. Zero isn't used for KVM hypercall +* number. +* +* R10: KVM hypercall number +* arguments: R11, R12, R13, R14. +*/ + nr =kvm_r10_read (vcpu); + a0 =kvm_r11_read (vcpu);
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1037
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ a1 =kvm_r12_read (vcpu); + a2 =kvm_r13_read (vcpu); + a3 =kvm_r14_read (vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, true,0 ); + +tdvmcall_set_return_code (vcpu, ret); + +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +return 0 ;
Can you add a comment to call out that KVM_HC_MAP_GPA_RANGE is redirected to the userspace?
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+return 1 ; +}
Re: [PATCH v19 105/130] KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
Quoted 6 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1014
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +do you need to emulate xen/hyper-v hypercalls here?
No. kvm_emulate_hypercall() handles xen/hyper-v hypercalls, __kvm_emulate_hypercall() doesn't.
Quoted 21 lines
Nothing tells userspace that xen/hyper-v hypercalls are not supported and so userspace may expose related CPUID leafs to TD guests.
Quoted 18 lines
arch/x86/kvm/vmx/tdx.c +18
-0+/* +* ABI for KVM tdvmcall argument: +* In Guest-Hypervisor Communication Interface(GHCI) specification, +* Non-zero leaf number (R10 != 0) is defined to indicate +* vendor-specific. KVM uses this for KVM hypercall. NOTE: KVM +* hypercall number starts from one. Zero isn't used for KVM hypercall +* number. +* +* R10: KVM hypercall number +* arguments: R11, R12, R13, R14. +*/ + nr =kvm_r10_read (vcpu); + a0 =kvm_r11_read (vcpu); + a1 =kvm_r12_read (vcpu); + a2 =kvm_r13_read (vcpu); + a3 =kvm_r14_read (vcpu); + + ret =__kvm_emulate_hypercall (vcpu, nr, a0, a1, a2, a3, true,0 );
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1037
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +tdvmcall_set_return_code (vcpu, ret); + +if (nr ==KVM_HC_MAP_GPA_RANGE && !ret) +return 0 ;Can you add a comment to call out that
KVM_HC_MAP_GPA_RANGEis redirected to the userspace?
Yes, this is confusing. We should refactor kvm_emulate_hypercall() more so that the caller shouldn't care about the return value like this. Will refactor it and update this patch.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 105/130] KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
Quoted 11 lines · Isaku Yamahata
Quoted 5 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1014
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +do you need to emulate xen/hyper-v hypercalls here?
No.
kvm_emulate_hypercall()handles xen/hyper-v hypercalls,__kvm_emulate_hypercall()doesn't.
So for TDX, kvm doesn't support xen/hyper-v, right?
Then, should KVM_CAP_XEN_HVM and KVM_CAP_HYPERV be filtered out for TDX?
Quoted 4 lines
Quoted 4 lines
Nothing tells userspace that xen/hyper-v hypercalls are not supported and so userspace may expose related CPUID leafs to TD guests.
Re: [PATCH v19 105/130] KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
Quoted 12 lines · Binbin Wu
Quoted 9 lines · Isaku Yamahata
Quoted 5 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1014
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +do you need to emulate xen/hyper-v hypercalls here?
Quoted 5 lines
Quoted 2 lines
No.
kvm_emulate_hypercall()handles xen/hyper-v hypercalls,__kvm_emulate_hypercall()doesn't.So for TDX, kvm doesn't support xen/hyper-v, right?
Then, should
KVM_CAP_XEN_HVMandKVM_CAP_HYPERVbe filtered out for TDX?
That's right. We should update kvm_vm_ioctl_check_extension() and kvm_vcpu_ioctl_enable_cap(). I didn't pay attention to them.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 105/130] KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
Quoted 13 lines · Isaku Yamahata
Quoted 10 lines · Binbin Wu
Quoted 8 lines · Isaku Yamahata
Quoted 5 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1014
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +do you need to emulate xen/hyper-v hypercalls here?
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
No.
kvm_emulate_hypercall()handles xen/hyper-v hypercalls,__kvm_emulate_hypercall()doesn't.So for TDX, kvm doesn't support xen/hyper-v, right?
Then, should
KVM_CAP_XEN_HVMandKVM_CAP_HYPERVbe filtered out for TDX?That's right. We should update
kvm_vm_ioctl_check_extension()andkvm_vcpu_ioctl_enable_cap(). I didn't pay attention to them.
Currently, QEMU checks the capabilities for Hyper-v/Xen via kvm_check_extension(), which is the global version.
Only modifications in KVM can't hide these capabilities. It needs userspace to use VM or vCPU version to check the capabilities for Hyper-v and Xen. Is it a change of ABI when the old global version is still workable, but userspace switches to use VM/vCPU version to check capabilities for Hyper-v and Xen? Are there objections if both QEMU and KVM are modified in order to hide Hyper-v/Xen capabilities for TDX?
Re: [PATCH v19 105/130] KVM: TDX: handle KVM hypercall with TDG.VP.VMCALL
Quoted 23 lines · Binbin Wu
Quoted 13 lines · Isaku Yamahata
Quoted 10 lines · Binbin Wu
Quoted 8 lines · Isaku Yamahata
Quoted 5 lines · Chao Gao
arch/x86/kvm/vmx/tdx.c static int tdx_handle_triple_fault(struct kvm_vcpu *vcpu) · line 1014
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+static int tdx_emulate_vmcall (struct kvm_vcpu *vcpu) +{ +unsigned long nr, a0, a1, a2, a3, ret; +do you need to emulate xen/hyper-v hypercalls here?
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
No.
kvm_emulate_hypercall()handles xen/hyper-v hypercalls,__kvm_emulate_hypercall()doesn't.So for TDX, kvm doesn't support xen/hyper-v, right?
Then, should
KVM_CAP_XEN_HVMandKVM_CAP_HYPERVbe filtered out for TDX?That's right. We should update
kvm_vm_ioctl_check_extension()andkvm_vcpu_ioctl_enable_cap(). I didn't pay attention to them.
Quoted 9 lines
Currently, QEMU checks the capabilities for Hyper-v/Xen via
kvm_check_extension(), which is the global version.
Only modifications in KVM can't hide these capabilities. It needs userspace to use VM or vCPU version to check the capabilities for Hyper-v and Xen. Is it a change of ABI when the old global version is still workable, but userspace switches to use VM/vCPU version to check capabilities for Hyper-v and Xen?
Are there objections if both QEMU and KVM are modified in order to hide Hyper-v/Xen capabilities for TDX?
I think it's okay for KVM_X86_TDX_VM as long as we don't change the value for KVM_X86_DEFAULT_VM. Because vm_type KVM_X86_TDX_VM is different from the default and the document (Documentation/virt/kvm/api.rst), 4.4 KVM_CHECK_EXTENSION explicitly encourages VM version.
Based on their initialization different VMs may have different capabilities. It is thus encouraged to use the vm ioctl to query for capabilities (available with KVM_CAP_CHECK_EXTENSION_VM on the vm fd)
The change to qemu will be mostly trivial with the quick check.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 106/130 KVM: TDX: Add KVM Exit for TDX TDG.VP.VMCALL
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Some of TDG.VP.VMCALL require device model, for example, qemu, to handle them on behalf of kvm kernel module. TDVMCALL_REPORT_FATAL_ERROR, TDVMCALL_MAP_GPA, TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT, and TDVMCALL_GET_QUOTE requires user space VMM handling.
Introduce new kvm exit, KVM_EXIT_TDX, and functions to setup it. Device model should update R10 if necessary as return value.
---
v14 -> v15:
- updated struct kvm_tdx_exit with union
- export constants for reg bitmask
--- arch/x86/kvm/vmx/tdx.c | 83 ++++++++++++++++++++++++++++++++++++- include/uapi/linux/kvm.h | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 2 deletions(-)
arch/x86/kvm/vmx/tdx.c +81 -2
@@ -1038,6 +1038,78 @@ static int tdx_emulate_vmcall(struct kvm_vcpu *vcpu)return 1 ; } +static int tdx_complete_vp_vmcall (struct kvm_vcpu *vcpu) +{ +struct kvm_tdx_vmcall *tdx_vmcall = &vcpu->run->tdx.u.vmcall; +__u64 reg_mask =kvm_rcx_read (vcpu); + +#define COPY_REG (MASK ,REG ) \ + do { \ + if (reg_mask & TDX_VMCALL_REG_MASK_ ## MASK) \ + kvm_## REG ## _write(vcpu, tdx_vmcall->out_ ## REG); \ + } while (0) + + +COPY_REG (R10 , r10); +COPY_REG (R11 , r11); +COPY_REG (R12 , r12); +COPY_REG (R13 , r13); +COPY_REG (R14 , r14); +COPY_REG (R15 , r15); +COPY_REG (RBX , rbx); +COPY_REG (RDI , rdi); +COPY_REG (RSI , rsi); +COPY_REG (R8 , r8); +COPY_REG (R9 , r9); +COPY_REG (RDX , rdx); + +#undef COPY_REG + +return 1 ; +} + +static int tdx_vp_vmcall_to_user (struct kvm_vcpu *vcpu) +{ +struct kvm_tdx_vmcall *tdx_vmcall = &vcpu->run->tdx.u.vmcall; +__u64 reg_mask; + + vcpu->arch.complete_userspace_io = tdx_complete_vp_vmcall; +memset (tdx_vmcall,0 ,sizeof (*tdx_vmcall)); + + vcpu->run->exit_reason =KVM_EXIT_TDX ; + vcpu->run->tdx.type =KVM_EXIT_TDX_VMCALL ; + + reg_mask =kvm_rcx_read (vcpu); + tdx_vmcall->reg_mask = reg_mask; + +#define COPY_REG (MASK ,REG ) \ + do { \ + if (reg_mask & TDX_VMCALL_REG_MASK_ ## MASK) { \ + tdx_vmcall->in_ ## REG = kvm_ ## REG ## _read(vcpu); \ + tdx_vmcall->out_ ## REG = tdx_vmcall->in_ ## REG; \ + } \ + } while (0) + + +COPY_REG (R10 , r10); +COPY_REG (R11 , r11); +COPY_REG (R12 , r12); +COPY_REG (R13 , r13); +COPY_REG (R14 , r14); +COPY_REG (R15 , r15); +COPY_REG (RBX , rbx); +COPY_REG (RDI , rdi); +COPY_REG (RSI , rsi); +COPY_REG (R8 , r8); +COPY_REG (R9 , r9); +COPY_REG (RDX , rdx); + +#undef COPY_REG + +/* notify userspace to handle the request */ +return 0 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))
@@ -1048,8 +1120,15 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)break ; } -tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); -return 1 ; +/* +* Unknown VMCALL. Toss the request to the user space VMM, e.g. qemu, +* as it may know how to handle. +* +* Those VMCALLs require user space VMM: +* TDVMCALL_REPORT_FATAL_ERROR, TDVMCALL_MAP_GPA, +* TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT, and TDVMCALL_GET_QUOTE. +*/ +return tdx_vp_vmcall_to_user (vcpu); }void tdx_load_mmu_pgd (struct kvm_vcpu *vcpu,hpa_t root_hpa,int pgd_level)
include/uapi/linux/kvm.h +89 -0
107 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH v19 106/130] KVM: TDX: Add KVM Exit for TDX TDG.VP.VMCALL
Quoted 30 lines · On Mon, Feb 26, 2024 at 12:26:48AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Some of TDG.VP.VMCALL require device model, for example, qemu, to handle them on behalf of kvm kernel module.
TDVMCALL_REPORT_FATAL_ERROR,TDVMCALL_MAP_GPA,TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT, andTDVMCALL_GET_QUOTErequires user space VMM handling.Introduce new kvm exit,
KVM_EXIT_TDX, and functions to setup it. Device model should update R10 if necessary as return value.Signed-off-by Isaku Yamahata <[email protected]>---
v14 -> v15:
- updated structkvm_tdx_exitwith union
- export constants for reg bitmaskSigned-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 83 ++++++++++++++++++++++++++++++++++++-include/uapi/linux/kvm.h| 89 ++++++++++++++++++++++++++++++++++++++++2 files changed, 170 insertions(+), 2 deletions(-)arch/x86/kvm/vmx/tdx.c +0
-0@@ -1038,6 +1038,78 @@ static int tdx_emulate_vmcall(struct kvm_vcpu *vcpu)return 1 ; }
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_vmcall(struct kvm_vcpu *vcpu) · line 1050
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+static int tdx_complete_vp_vmcall (struct kvm_vcpu *vcpu) +{ +struct kvm_tdx_vmcall *tdx_vmcall = &vcpu->run->tdx.u.vmcall; +__u64 reg_mask =kvm_rcx_read (vcpu); + +#define COPY_REG (MASK ,REG ) \ + do { \ + if (reg_mask & TDX_VMCALL_REG_MASK_ ## MASK) \ + kvm_## REG ## _write(vcpu, tdx_vmcall->out_ ## REG); \ + } while (0)
Why XMMs are not copied?
Looks you assume the guest won't use XMMs for TDVMCALL. But I think the ABI (KVM_EXIT_TDX) should be general, i.e., can support all kinds of (future) TDVMCALLs.
Quoted 19 lines
arch/x86/kvm/vmx/tdx.c +19
-0+ + +COPY_REG (R10 , r10); +COPY_REG (R11 , r11); +COPY_REG (R12 , r12); +COPY_REG (R13 , r13); +COPY_REG (R14 , r14); +COPY_REG (R15 , r15); +COPY_REG (RBX , rbx); +COPY_REG (RDI , rdi); +COPY_REG (RSI , rsi); +COPY_REG (R8 , r8); +COPY_REG (R9 , r9); +COPY_REG (RDX , rdx); + +#undef COPY_REG + +return 1 ; +}
Sean Christopherson
maintainerRe: [PATCH v19 106/130] KVM: TDX: Add KVM Exit for TDX TDG.VP.VMCALL
In reply to [email protected]Quoted 9 lines · On Mon, Feb 26, 2024, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Some of TDG.VP.VMCALL require device model, for example, qemu, to handle them on behalf of kvm kernel module.
TDVMCALL_REPORT_FATAL_ERROR,TDVMCALL_MAP_GPA,TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT, andTDVMCALL_GET_QUOTErequires user space VMM handling.Introduce new kvm exit,
KVM_EXIT_TDX, and functions to setup it. Device model should update R10 if necessary as return value.
Hard NAK.
KVM needs its own ABI, under no circumstance should KVM inherit ABI directly from the GHCI. Even worse, this doesn't even sanity check the "unknown" VMCALLs, KVM just blindly punts *everything* to userspace. And even worse than that, KVM already has at least one user exit that overlaps, TDVMCALL_MAP_GPA => KVM_HC_MAP_GPA_RANGE.
If the userspace VMM wants to run an end-around on KVM and directly communicate with the guest, e.g. via a synthetic device (a la virtio), that's totally fine, because *KVM* is not definining any unique ABI, KVM is purely providing the transport, e.g. emulated MMIO or PIO (and maybe not even that). IIRC, this option even came up in the context of GET_QUOTE.
But explicit exiting to userspace with KVM_EXIT_TDX is very different. KVM is creating a contract with userspace that says "for TDX VMCALLs [a-z], KVM will exit to userspace with values [a-z]". *Every* new VMCALL that's added to the GHCI will become KVM ABI, e.g. if Intel ships a TDX module that adds a new VMALL, then KVM will forward the exit to userspace, and userspace can then start relying on that behavior.
And punting all register state, decoding, etc. to userspace creates a crap ABI. KVM effectively did this for SEV and SEV-ES by copying the PSP ABI verbatim into KVM ioctls(), and it's a gross, ugly mess.
Each VMCALL that KVM wants to forward needs a dedicated KVM_EXIT_<reason> and associated struct in the exit union. Yes, it's slightly more work now, but it's one time pain. Whereas copying all registers is endless misery for everyone involved, e.g. *every* userspace VMM needs to decipher the registers, do sanity checking, etc. And *every* end user needs to do the same when a debugging inevitable failures.
This also solves Chao's comment about XMM registers. Except for emualting Hyper-V hypercalls, which have very explicit handling, KVM does NOT support using XMM registers in hypercalls.
Quoted 230 lines
Signed-off-by Isaku Yamahata <[email protected]>---
v14 -> v15:
- updated structkvm_tdx_exitwith union
- export constants for reg bitmaskSigned-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/vmx/tdx.c | 83 ++++++++++++++++++++++++++++++++++++- include/uapi/linux/kvm.h | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 2 deletions(-)arch/x86/kvm/vmx/tdx.c +81
-2@@ -1038,6 +1038,78 @@ static int tdx_emulate_vmcall(struct kvm_vcpu *vcpu)return 1 ; } +static int tdx_complete_vp_vmcall (struct kvm_vcpu *vcpu) +{ +struct kvm_tdx_vmcall *tdx_vmcall = &vcpu->run->tdx.u.vmcall; +__u64 reg_mask =kvm_rcx_read (vcpu); + +#define COPY_REG (MASK ,REG ) \ + do { \ + if (reg_mask & TDX_VMCALL_REG_MASK_ ## MASK) \ + kvm_## REG ## _write(vcpu, tdx_vmcall->out_ ## REG); \ + } while (0) + + +COPY_REG (R10 , r10); +COPY_REG (R11 , r11); +COPY_REG (R12 , r12); +COPY_REG (R13 , r13); +COPY_REG (R14 , r14); +COPY_REG (R15 , r15); +COPY_REG (RBX , rbx); +COPY_REG (RDI , rdi); +COPY_REG (RSI , rsi); +COPY_REG (R8 , r8); +COPY_REG (R9 , r9); +COPY_REG (RDX , rdx); + +#undef COPY_REG + +return 1 ; +} + +static int tdx_vp_vmcall_to_user (struct kvm_vcpu *vcpu) +{ +struct kvm_tdx_vmcall *tdx_vmcall = &vcpu->run->tdx.u.vmcall; +__u64 reg_mask; + + vcpu->arch.complete_userspace_io = tdx_complete_vp_vmcall; +memset (tdx_vmcall,0 ,sizeof (*tdx_vmcall)); + + vcpu->run->exit_reason =KVM_EXIT_TDX ; + vcpu->run->tdx.type =KVM_EXIT_TDX_VMCALL ; + + reg_mask =kvm_rcx_read (vcpu); + tdx_vmcall->reg_mask = reg_mask; + +#define COPY_REG (MASK ,REG ) \ + do { \ + if (reg_mask & TDX_VMCALL_REG_MASK_ ## MASK) { \ + tdx_vmcall->in_ ## REG = kvm_ ## REG ## _read(vcpu); \ + tdx_vmcall->out_ ## REG = tdx_vmcall->in_ ## REG; \ + } \ + } while (0) + + +COPY_REG (R10 , r10); +COPY_REG (R11 , r11); +COPY_REG (R12 , r12); +COPY_REG (R13 , r13); +COPY_REG (R14 , r14); +COPY_REG (R15 , r15); +COPY_REG (RBX , rbx); +COPY_REG (RDI , rdi); +COPY_REG (RSI , rsi); +COPY_REG (R8 , r8); +COPY_REG (R9 , r9); +COPY_REG (RDX , rdx); + +#undef COPY_REG + +/* notify userspace to handle the request */ +return 0 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))@@ -1048,8 +1120,15 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)break ; } -tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); -return 1 ; +/* +* Unknown VMCALL. Toss the request to the user space VMM, e.g. qemu, +* as it may know how to handle. +* +* Those VMCALLs require user space VMM: +* TDVMCALL_REPORT_FATAL_ERROR, TDVMCALL_MAP_GPA, +* TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT, and TDVMCALL_GET_QUOTE. +*/ +return tdx_vp_vmcall_to_user (vcpu); }void tdx_load_mmu_pgd (struct kvm_vcpu *vcpu,hpa_t root_hpa,int pgd_level)include/uapi/linux/kvm.h +89
-0@@ -167,6 +167,92 @@ struct kvm_xen_exit { } u; }; +/* masks for reg_mask to indicate which registers are passed. */ +#define TDX_VMCALL_REG_MASK_RBX BIT_ULL(2) +#define TDX_VMCALL_REG_MASK_RDX BIT_ULL(3) +#define TDX_VMCALL_REG_MASK_RSI BIT_ULL(6) +#define TDX_VMCALL_REG_MASK_RDI BIT_ULL(7) +#define TDX_VMCALL_REG_MASK_R8 BIT_ULL(8) +#define TDX_VMCALL_REG_MASK_R9 BIT_ULL(9) +#define TDX_VMCALL_REG_MASK_R10 BIT_ULL(10) +#define TDX_VMCALL_REG_MASK_R11 BIT_ULL(11) +#define TDX_VMCALL_REG_MASK_R12 BIT_ULL(12) +#define TDX_VMCALL_REG_MASK_R13 BIT_ULL(13) +#define TDX_VMCALL_REG_MASK_R14 BIT_ULL(14) +#define TDX_VMCALL_REG_MASK_R15 BIT_ULL(15) + +struct kvm_tdx_exit { +#define KVM_EXIT_TDX_VMCALL 1 +__u32 type; +__u32 pad; + +union { +struct kvm_tdx_vmcall { +/* +* RAX(bit 0), RCX(bit 1) and RSP(bit 4) are reserved. +* RAX(bit 0): TDG.VP.VMCALL status code. +* RCX(bit 1): bitmap for used registers. +* RSP(bit 4): the caller stack. +*/ +union { +__u64 in_rcx; +__u64 reg_mask; + }; + +/* +* Guest-Host-Communication Interface for TDX spec +* defines the ABI for TDG.VP.VMCALL. +*/ +/* Input parameters: guest -> VMM */ +union { +__u64 in_r10; +__u64 type; + }; +union { +__u64 in_r11; +__u64 subfunction; + }; +/* +* Subfunction specific. +* Registers are used in this order to pass input +* arguments. r12=arg0, r13=arg1, etc. +*/ +__u64 in_r12; +__u64 in_r13; +__u64 in_r14; +__u64 in_r15; +__u64 in_rbx; +__u64 in_rdi; +__u64 in_rsi; +__u64 in_r8; +__u64 in_r9; +__u64 in_rdx; + +/* Output parameters: VMM -> guest */ +union { +__u64 out_r10; +__u64 status_code; + }; +/* +* Subfunction specific. +* Registers are used in this order to output return +* values. r11=ret0, r12=ret1, etc. +*/ +__u64 out_r11; +__u64 out_r12; +__u64 out_r13; +__u64 out_r14; +__u64 out_r15; +__u64 out_rbx; +__u64 out_rdi; +__u64 out_rsi; +__u64 out_r8; +__u64 out_r9; +__u64 out_rdx; + } vmcall; + } u; +}; +#define KVM_S390_GET_SKEYS_NONE 1#define KVM_S390_SKEYS_MAX 1048576@@ -210,6 +296,7 @@ struct kvm_xen_exit {#define KVM_EXIT_NOTIFY 37#define KVM_EXIT_LOONGARCH_IOCSR 38#define KVM_EXIT_MEMORY_FAULT 39 +#define KVM_EXIT_TDX 40/* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -470,6 +557,8 @@ struct kvm_run {__u64 gpa;__u64 size; } memory_fault; +/* KVM_EXIT_TDX_VMCALL */ +struct kvm_tdx_exit tdx;/* Fix the size of the union. */ char padding[256 ]; };Signature · 1 line
2.25.1
Re: [PATCH v19 106/130] KVM: TDX: Add KVM Exit for TDX TDG.VP.VMCALL
Quoted 42 lines · Sean Christopherson
Quoted 9 lines · On Mon, Feb 26, 2024, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Some of TDG.VP.VMCALL require device model, for example, qemu, to handle them on behalf of kvm kernel module.
TDVMCALL_REPORT_FATAL_ERROR,TDVMCALL_MAP_GPA,TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT, andTDVMCALL_GET_QUOTErequires user space VMM handling.Introduce new kvm exit,
KVM_EXIT_TDX, and functions to setup it. Device model should update R10 if necessary as return value.Hard NAK.
KVM needs its own ABI, under no circumstance should KVM inherit ABI directly from the GHCI. Even worse, this doesn't even sanity check the "unknown" VMCALLs, KVM just blindly punts *everything* to userspace. And even worse than that, KVM already has at least one user exit that overlaps,
TDVMCALL_MAP_GPA=>KVM_HC_MAP_GPA_RANGE.If the userspace VMM wants to run an end-around on KVM and directly communicate with the guest, e.g. via a synthetic device (a la virtio), that's totally fine, because *KVM* is not definining any unique ABI, KVM is purely providing the transport, e.g. emulated MMIO or PIO (and maybe not even that). IIRC, this option even came up in the context of
GET_QUOTE.But explicit exiting to userspace with
KVM_EXIT_TDXis very different. KVM is creating a contract with userspace that says "for TDX VMCALLs [a-z], KVM will exit to userspace with values [a-z]". *Every* new VMCALL that's added to the GHCI will become KVM ABI, e.g. if Intel ships a TDX module that adds a new VMALL, then KVM will forward the exit to userspace, and userspace can then start relying on that behavior.And punting all register state, decoding, etc. to userspace creates a crap ABI. KVM effectively did this for SEV and SEV-ES by copying the PSP ABI verbatim into KVM
ioctls(), and it's a gross, ugly mess.Each VMCALL that KVM wants to forward needs a dedicated KVM_EXIT_<reason> and associated struct in the exit union. Yes, it's slightly more work now, but it's one time pain. Whereas copying all registers is endless misery for everyone involved, e.g. *every* userspace VMM needs to decipher the registers, do sanity checking, etc. And *every* end user needs to do the same when a debugging inevitable failures.
Quoted 3 lines
This also solves Chao's comment about XMM registers. Except for emualting Hyper-V hypercalls, which have very explicit handling, KVM does NOT support using XMM registers in hypercalls.
Sure. I will introduce the followings.
KVM_EXIT_TDX_GET_QUOTE
Request a quote.
KVM_EXIT_TDX_SETUP_EVENT_NOTIFY_INTERRUPT
Guest tells which interrupt vector the VMM uses to notify the guest. The use case if GetQuote. It is async request. The user-space VMM uses this interrupts to notify the guest on the completion. Or guest polls it.
KVM_EXIT_TDX_REPORT_FATAL_ERROR
Guest panicked. This conveys extra 64 bytes in registers. Probably this should be converted to KVM_EXIT_SYSTEM_EVENT and KVM_SYSTEM_EVENT_CRASH.
MapGPA is converted to KVM_HC_MAP_GPA_RANGE.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 107/130 KVM: TDX: Handle TDX PV CPUID hypercall
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Wire up TDX PV CPUID hypercall to the KVM backend function.
--- arch/x86/kvm/vmx/tdx.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
arch/x86/kvm/vmx/tdx.c +22 -0
@@ -1110,12 +1110,34 @@ static int tdx_vp_vmcall_to_user(struct kvm_vcpu *vcpu)return 0 ; } +static int tdx_emulate_cpuid (struct kvm_vcpu *vcpu) +{ +u32 eax, ebx, ecx, edx; + +/* EAX and ECX for cpuid is stored in R12 and R13. */ + eax =tdvmcall_a0_read (vcpu); + ecx =tdvmcall_a1_read (vcpu); + +kvm_cpuid (vcpu, &eax, &ebx, &ecx, &edx, false); + +tdvmcall_a0_write (vcpu, eax); +tdvmcall_a1_write (vcpu, ebx); +tdvmcall_a2_write (vcpu, ecx); +tdvmcall_a3_write (vcpu, edx); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); + +return 1 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))return tdx_emulate_vmcall (vcpu);switch (tdvmcall_leaf (vcpu)) { +case EXIT_REASON_CPUID : +return tdx_emulate_cpuid (vcpu);default :break ; }
Signature · 1 line
2.25.1
PATCH v19 108/130 KVM: TDX: Handle TDX PV HLT hypercall
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Wire up TDX PV HLT hypercall to the KVM backend function.
---
v19:
- move tdvps_state_non_arch_check() to this patch
v18:
- drop buggy_hlt_workaround and use TDH.VP.RD(TD_VCPU_STATE_DETAILS)
--- arch/x86/kvm/vmx/tdx.c | 26 +++++++++++++++++++++++++- arch/x86/kvm/vmx/tdx.h | 4 ++++ 2 files changed, 29 insertions(+), 1 deletion(-)
arch/x86/kvm/vmx/tdx.c +25 -1
@@ -688,7 +688,18 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu) { -return pi_has_pending_interrupt (vcpu); +bool ret =pi_has_pending_interrupt (vcpu); +union tdx_vcpu_state_details details; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +if (ret || vcpu->arch.mp_state !=KVM_MP_STATE_HALTED ) +return true; + +if (tdx->interrupt_disabled_hlt) +return false; + + details.full =td_state_non_arch_read64 (tdx,TD_VCPU_STATE_DETAILS_NON_ARCH ); +return !!details.vmxip; }void tdx_prepare_switch_to_guest (struct kvm_vcpu *vcpu)
@@ -1130,6 +1141,17 @@ static int tdx_emulate_cpuid(struct kvm_vcpu *vcpu)return 1 ; } +static int tdx_emulate_hlt (struct kvm_vcpu *vcpu) +{ +struct vcpu_tdx *tdx =to_tdx (vcpu); + +/* See tdx_protected_apic_has_interrupt() to avoid heavy seamcall */ + tdx->interrupt_disabled_hlt =tdvmcall_a0_read (vcpu); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +return kvm_emulate_halt_noskip (vcpu); +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))
@@ -1138,6 +1160,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)switch (tdvmcall_leaf (vcpu)) {case EXIT_REASON_CPUID :return tdx_emulate_cpuid (vcpu); +case EXIT_REASON_HLT : +return tdx_emulate_hlt (vcpu);default :break ; }
arch/x86/kvm/vmx/tdx.h +4 -0
@@ -104,6 +104,8 @@ struct vcpu_tdx {bool host_state_need_restore;u64 msr_host_kernel_gs_base; +bool interrupt_disabled_hlt; +/* * Dummy to make pmu_intel not corrupt memory. * TODO: Support PMU for TDX. Future work.
@@ -166,6 +168,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, \
@@ -226,6 +229,7 @@ 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); static __always_inline u64 td_tdcs_exec_read64(struct kvm_tdx *kvm_tdx, u32 field) {
Signature · 1 line
2.25.1
Re: [PATCH v19 108/130] KVM: TDX: Handle TDX PV HLT hypercall
Quoted 23 lines · On Mon, Feb 26, 2024 at 12:26:50AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV HLT hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>---
v19:
- movetdvps_state_non_arch_check()to this patchv18:
- dropbuggy_hlt_workaroundand useTDH.VP.RD(TD_VCPU_STATE_DETAILS)Signed-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 26 +++++++++++++++++++++++++-arch/x86/kvm/vmx/tdx.h| 4 ++++2 files changed, 29 insertions(+), 1 deletion(-)arch/x86/kvm/vmx/tdx.c +0
-0
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 691
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +1
-1@@ -688,7 +688,18 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu) { -return pi_has_pending_interrupt (vcpu); +bool ret =pi_has_pending_interrupt (vcpu);
Maybe
bool has_pending_interrupt = pi_has_pending_interrupt(vcpu);
"ret" isn't a good name. or even call pi_has_pending_interrupt() directly in the if statement below.
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 696
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+union tdx_vcpu_state_details details; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +if (ret || vcpu->arch.mp_state !=KVM_MP_STATE_HALTED ) +return true;
Question: why mp_state matters here?
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 699
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +3
-0+ +if (tdx->interrupt_disabled_hlt) +return false;
Shouldn't we move this into vt_interrupt_allowed()? VMX calls the function to check if interrupt is disabled. KVM can clear tdx->interrupt_disabled_hlt on every TD-enter and set it only on TD-exit due to the guest making a TDVMCALL(hlt) w/ interrupt disabled.
Quoted 66 lines
arch/x86/kvm/vmx/tdx.c +16
-0+ + details.full =td_state_non_arch_read64 (tdx,TD_VCPU_STATE_DETAILS_NON_ARCH ); +return !!details.vmxip; }void tdx_prepare_switch_to_guest (struct kvm_vcpu *vcpu)@@ -1130,6 +1141,17 @@ static int tdx_emulate_cpuid(struct kvm_vcpu *vcpu)return 1 ; } +static int tdx_emulate_hlt (struct kvm_vcpu *vcpu) +{ +struct vcpu_tdx *tdx =to_tdx (vcpu); + +/* See tdx_protected_apic_has_interrupt() to avoid heavy seamcall */ + tdx->interrupt_disabled_hlt =tdvmcall_a0_read (vcpu); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +return kvm_emulate_halt_noskip (vcpu); +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))@@ -1138,6 +1160,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)switch (tdvmcall_leaf (vcpu)) {case EXIT_REASON_CPUID :return tdx_emulate_cpuid (vcpu); +case EXIT_REASON_HLT : +return tdx_emulate_hlt (vcpu);default :break ; }arch/x86/kvm/vmx/tdx.h +4
-0@@ -104,6 +104,8 @@ struct vcpu_tdx {bool host_state_need_restore;u64 msr_host_kernel_gs_base; +bool interrupt_disabled_hlt; +/* * Dummy to make pmu_intel not corrupt memory. * TODO: Support PMU for TDX. Future work. @@ -166,6 +168,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, \@@ -226,6 +229,7 @@ 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); static __always_inline u64 td_tdcs_exec_read64(struct kvm_tdx *kvm_tdx, u32 field) {Signature · 1 line
2.25.1
Sean Christopherson
maintainerRe: [PATCH v19 108/130] KVM: TDX: Handle TDX PV HLT hypercall
Quoted 31 lines · Chao Gao
Quoted 23 lines · On Mon, Feb 26, 2024 at 12:26:50AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV HLT hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>---
v19:
- movetdvps_state_non_arch_check()to this patchv18:
- dropbuggy_hlt_workaroundand useTDH.VP.RD(TD_VCPU_STATE_DETAILS)Signed-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 26 +++++++++++++++++++++++++-arch/x86/kvm/vmx/tdx.h| 4 ++++2 files changed, 29 insertions(+), 1 deletion(-)arch/x86/kvm/vmx/tdx.c +0
-0arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 691
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +1
-1@@ -688,7 +688,18 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu) { -return pi_has_pending_interrupt (vcpu); +bool ret =pi_has_pending_interrupt (vcpu);Maybe
Quoted 4 lines
bool has_pending_interrupt = pi_has_pending_interrupt(vcpu);"ret" isn't a good name. or even call
pi_has_pending_interrupt()directly in the if statement below.
Ya, or split the if-statement into multiple chucks, with comments explaining what each non-intuitive chunk is doing. The pi_has_pending_interrupt(vcpu) check is self-explanatory, the halted thing, not so much. They are terminal statements, there's zero reason to pre-check the PID.
E.g.
/* * Comment explaining why KVM needs to assume a non-halted vCPU has a * pending interrupt (KVM can't see RFLAGS.IF). */ if (vcpu->arch.mp_state != KVM_MP_STATE_HALTED) return true; if (pi_has_pending_interrupt(vcpu)) return;
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 696
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+union tdx_vcpu_state_details details; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +if (ret || vcpu->arch.mp_state !=KVM_MP_STATE_HALTED ) +return true;Question: why
mp_statematters here?
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 699
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +3
-0+ +if (tdx->interrupt_disabled_hlt) +return false;Shouldn't we move this into
vt_interrupt_allowed()? VMX calls the function to check if interrupt is disabled. KVM can cleartdx->interrupt_disabled_hlton every TD-enter and set it only on TD-exit due to the guest making a TDVMCALL(hlt) w/ interrupt disabled.
I'm pretty sure interrupt_disabled_hlt shouldn't exist, should "a0", a.k.a. r12, be preserved at this point?
/* Another comment explaning magic code. */ if (to_vmx(vcpu)->exit_reason.basic == EXIT_REASON_HLT && tdvmcall_a0_read(vcpu)) return false;
Actually, can't this all be:
if (to_vmx(vcpu)->exit_reason.basic != EXIT_REASON_HLT) return true; if (!tdvmcall_a0_read(vcpu)) return false; if (pi_has_pending_interrupt(vcpu)) return true; return tdx_has_pending_virtual_interrupt(vcpu);
Re: [PATCH v19 108/130] KVM: TDX: Handle TDX PV HLT hypercall
Quoted 44 lines · Sean Christopherson
Quoted 31 lines · Chao Gao
Quoted 23 lines · On Mon, Feb 26, 2024 at 12:26:50AM -0800, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV HLT hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>---
v19:
- movetdvps_state_non_arch_check()to this patchv18:
- dropbuggy_hlt_workaroundand useTDH.VP.RD(TD_VCPU_STATE_DETAILS)Signed-off-by Isaku Yamahata <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 26 +++++++++++++++++++++++++-arch/x86/kvm/vmx/tdx.h| 4 ++++2 files changed, 29 insertions(+), 1 deletion(-)arch/x86/kvm/vmx/tdx.c +0
-0arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 691
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +1
-1@@ -688,7 +688,18 @@ void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)bool tdx_protected_apic_has_interrupt (struct kvm_vcpu *vcpu) { -return pi_has_pending_interrupt (vcpu); +bool ret =pi_has_pending_interrupt (vcpu);Maybe
Quoted 4 lines
bool has_pending_interrupt = pi_has_pending_interrupt(vcpu);"ret" isn't a good name. or even call
pi_has_pending_interrupt()directly in the if statement below.Ya, or split the if-statement into multiple chucks, with comments explaining what each non-intuitive chunk is doing. The
pi_has_pending_interrupt(vcpu)check is self-explanatory, the halted thing, not so much. They are terminal statements, there's zero reason to pre-check the PID.E.g.
Quoted 23 lines
/* * Comment explaining why KVM needs to assume a non-halted vCPU has a * pending interrupt (KVM can't see RFLAGS.IF). */ if (vcpu->arch.mp_state != KVM_MP_STATE_HALTED) return true; if (pi_has_pending_interrupt(vcpu)) return;Quoted 13 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 696
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+union tdx_vcpu_state_details details; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +if (ret || vcpu->arch.mp_state !=KVM_MP_STATE_HALTED ) +return true;Question: why
mp_statematters here?arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 699
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +3
-0+ +if (tdx->interrupt_disabled_hlt) +return false;Shouldn't we move this into
vt_interrupt_allowed()? VMX calls the function to check if interrupt is disabled.
Chao, are you suggesting to implement tdx_interrupt_allowed() as "EXIT_REASON_HLT && a0" instead of "return true"?
I don't think it makes sense because it's rare case and we can't avoid spurious wakeup for TDX case.
Quoted 15 lines
Quoted 3 lines
KVM can clear
tdx->interrupt_disabled_hlton
every TD-enter and set it only on TD-exit due to the guest making a TDVMCALL(hlt) w/ interrupt disabled.I'm pretty sure
interrupt_disabled_hltshouldn't exist, should "a0", a.k.a. r12, be preserved at this point?/* Another comment explaning magic code. */ if (to_vmx(vcpu)->exit_reason.basic == EXIT_REASON_HLT && tdvmcall_a0_read(vcpu)) return false;Actually, can't this all be:
Quoted 11 lines
if (to_vmx(vcpu)->exit_reason.basic != EXIT_REASON_HLT) return true; if (!tdvmcall_a0_read(vcpu)) return false; if (pi_has_pending_interrupt(vcpu)) return true; return tdx_has_pending_virtual_interrupt(vcpu);
Thanks for the suggestion. This is much cleaner. Will update the function.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 108/130] KVM: TDX: Handle TDX PV HLT hypercall
Quoted 14 lines
Quoted 7 lines
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 696
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+union tdx_vcpu_state_details details; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +if (ret || vcpu->arch.mp_state !=KVM_MP_STATE_HALTED ) +return true;Question: why
mp_statematters here?Quoted 6 lines
Quoted 6 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 699
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +3
-0+ +if (tdx->interrupt_disabled_hlt) +return false;Shouldn't we move this into
vt_interrupt_allowed()? VMX calls the function to check if interrupt is disabled.
Quoted 4 lines
Chao, are you suggesting to implement
tdx_interrupt_allowed()as "EXIT_REASON_HLT&& a0" instead of "return true"?
I don't think it makes sense because it's rare case and we can't avoid spurious wakeup for TDX case.
Yes. KVM differeniates "interrupt allowed" from "has interrupt", e.g.,
static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
...
if (kvm_arch_interrupt_allowed(vcpu) && (kvm_cpu_has_interrupt(vcpu) || kvm_guest_apic_has_interrupt(vcpu))) return true;
I think tdx_protected_apic_has_interrupt() mixes them together, which isn't good.
Probably it is a minor thing; if no one else thinks it is better to move the "interrupt allowed" check to tdx_interrupt_allowed(), I am also fine with not doing that.
Re: [PATCH v19 108/130] KVM: TDX: Handle TDX PV HLT hypercall
Quoted 24 lines · Chao Gao
Quoted 14 lines
Quoted 7 lines
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 696
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+union tdx_vcpu_state_details details; +struct vcpu_tdx *tdx =to_tdx (vcpu); + +if (ret || vcpu->arch.mp_state !=KVM_MP_STATE_HALTED ) +return true;Question: why
mp_statematters here?Quoted 6 lines
Quoted 6 lines
arch/x86/kvm/vmx/tdx.c void tdx_vcpu_load(struct kvm_vcpu *vcpu, int cpu) · line 699
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +3
-0+ +if (tdx->interrupt_disabled_hlt) +return false;Shouldn't we move this into
vt_interrupt_allowed()? VMX calls the function to check if interrupt is disabled.Quoted 4 lines
Chao, are you suggesting to implement
tdx_interrupt_allowed()as "EXIT_REASON_HLT&& a0" instead of "return true"?
I don't think it makes sense because it's rare case and we can't avoid spurious wakeup for TDX case.Yes. KVM differeniates "interrupt allowed" from "has interrupt", e.g.,
static inline bool
kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
...
Quoted 8 lines
if (kvm_arch_interrupt_allowed(vcpu) && (kvm_cpu_has_interrupt(vcpu) || kvm_guest_apic_has_interrupt(vcpu))) return true;I think
tdx_protected_apic_has_interrupt()mixes them together, which isn't good.
Your point is code clarity. Ok, we can code in that way. I don't expect any performance difference.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 109/130 KVM: TDX: Handle TDX PV port io hypercall
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Wire up TDX PV port IO hypercall to the KVM backend function.
---
v18:
- Fix out case to set R10 and R11 correctly when user space handled port out.
--- arch/x86/kvm/vmx/tdx.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)
arch/x86/kvm/vmx/tdx.c +67 -0
@@ -1152,6 +1152,71 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)return kvm_emulate_halt_noskip (vcpu); } +static int tdx_complete_pio_out (struct kvm_vcpu *vcpu) +{ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu,0 ); +return 1 ; +} + +static int tdx_complete_pio_in (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +int ret; + +WARN_ON_ONCE (vcpu->arch.pio.count !=1 ); + + ret = ctxt->ops->pio_in_emulated (ctxt, vcpu->arch.pio.size, + vcpu->arch.pio.port, &val,1 ); +WARN_ON_ONCE (!ret); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu, val); + +return 1 ; +} + +static int tdx_emulate_io (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +unsigned int port; +int size, ret; +bool write; + + ++vcpu->stat.io_exits; + + size =tdvmcall_a0_read (vcpu); + write =tdvmcall_a1_read (vcpu); + port =tdvmcall_a2_read (vcpu); + +if (size !=1 && size !=2 && size !=4 ) { +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } + +if (write) { + val =tdvmcall_a3_read (vcpu); + ret = ctxt->ops->pio_out_emulated (ctxt, size, port, &val,1 ); + +/* No need for a complete_userspace_io callback. */ + vcpu->arch.pio.count =0 ; + }else + ret = ctxt->ops->pio_in_emulated (ctxt, size, port, &val,1 ); + +if (ret) +tdvmcall_set_return_val (vcpu, val); +else { +if (write) + vcpu->arch.complete_userspace_io = tdx_complete_pio_out; +else + vcpu->arch.complete_userspace_io = tdx_complete_pio_in; + } + +return ret; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))
@@ -1162,6 +1227,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)return tdx_emulate_cpuid (vcpu);case EXIT_REASON_HLT :return tdx_emulate_hlt (vcpu); +case EXIT_REASON_IO_INSTRUCTION : +return tdx_emulate_io (vcpu);default :break ; }
Signature · 1 line
2.25.1
Re: [PATCH v19 109/130] KVM: TDX: Handle TDX PV port io hypercall
Quoted 61 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV port IO hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
v18:
- Fix out case to set R10 and R11 correctly when user space handled port out.--- arch/x86/kvm/vmx/tdx.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)arch/x86/kvm/vmx/tdx.c +39
-0@@ -1152,6 +1152,71 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)return kvm_emulate_halt_noskip (vcpu); } +static int tdx_complete_pio_out (struct kvm_vcpu *vcpu) +{ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu,0 ); +return 1 ; +} + +static int tdx_complete_pio_in (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +int ret; + +WARN_ON_ONCE (vcpu->arch.pio.count !=1 ); + + ret = ctxt->ops->pio_in_emulated (ctxt, vcpu->arch.pio.size, + vcpu->arch.pio.port, &val,1 ); +WARN_ON_ONCE (!ret); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu, val); + +return 1 ; +} + +static int tdx_emulate_io (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +unsigned int port; +int size, ret; +bool write; + + ++vcpu->stat.io_exits; + + size =tdvmcall_a0_read (vcpu); + write =tdvmcall_a1_read (vcpu); + port =tdvmcall_a2_read (vcpu); +
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_hlt(struct kvm_vcpu *vcpu) · line 1203
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+if (size !=1 && size !=2 && size !=4 ) { +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } + +if (write) { + val =tdvmcall_a3_read (vcpu); + ret = ctxt->ops->pio_out_emulated (ctxt, size, port, &val,1 ); + +/* No need for a complete_userspace_io callback. */
I am confused about the comment.
The code below sets the complete_userspace_io callback for write case, i.e. tdx_complete_pio_out().
Quoted 28 lines
arch/x86/kvm/vmx/tdx.c +18
-0+ vcpu->arch.pio.count =0 ; + }else + ret = ctxt->ops->pio_in_emulated (ctxt, size, port, &val,1 ); + +if (ret) +tdvmcall_set_return_val (vcpu, val); +else { +if (write) + vcpu->arch.complete_userspace_io = tdx_complete_pio_out; +else + vcpu->arch.complete_userspace_io = tdx_complete_pio_in; + } + +return ret; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))@@ -1162,6 +1227,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)return tdx_emulate_cpuid (vcpu);case EXIT_REASON_HLT :return tdx_emulate_hlt (vcpu); +case EXIT_REASON_IO_INSTRUCTION : +return tdx_emulate_io (vcpu);default :break ; }
Re: [PATCH v19 109/130] KVM: TDX: Handle TDX PV port io hypercall
Quoted 75 lines · Binbin Wu
Quoted 60 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV port IO hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
v18:
- Fix out case to set R10 and R11 correctly when user space handled port out.--- arch/x86/kvm/vmx/tdx.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)arch/x86/kvm/vmx/tdx.c +39
-0@@ -1152,6 +1152,71 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)return kvm_emulate_halt_noskip (vcpu); } +static int tdx_complete_pio_out (struct kvm_vcpu *vcpu) +{ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu,0 ); +return 1 ; +} + +static int tdx_complete_pio_in (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +int ret; + +WARN_ON_ONCE (vcpu->arch.pio.count !=1 ); + + ret = ctxt->ops->pio_in_emulated (ctxt, vcpu->arch.pio.size, + vcpu->arch.pio.port, &val,1 ); +WARN_ON_ONCE (!ret); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu, val); + +return 1 ; +} + +static int tdx_emulate_io (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +unsigned int port; +int size, ret; +bool write; + + ++vcpu->stat.io_exits; + + size =tdvmcall_a0_read (vcpu); + write =tdvmcall_a1_read (vcpu); + port =tdvmcall_a2_read (vcpu); +arch/x86/kvm/vmx/tdx.c static int tdx_emulate_hlt(struct kvm_vcpu *vcpu) · line 1202
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+if (size !=1 && size !=2 && size !=4 ) { +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } + +if (write) { + val =tdvmcall_a3_read (vcpu); + ret = ctxt->ops->pio_out_emulated (ctxt, size, port, &val,1 ); + +/* No need for a complete_userspace_io callback. */ I am confused about the comment.
Quoted 2 lines
The code below sets the
complete_userspace_iocallback for write case, i.e.tdx_complete_pio_out().
You're correct. This comment is stale and should be removed it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 109/130] KVM: TDX: Handle TDX PV port io hypercall
Quoted 77 lines · Isaku Yamahata
Quoted 74 lines · Binbin Wu
Quoted 60 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV port IO hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
v18:
- Fix out case to set R10 and R11 correctly when user space handled port out.--- arch/x86/kvm/vmx/tdx.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)arch/x86/kvm/vmx/tdx.c +39
-0@@ -1152,6 +1152,71 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)return kvm_emulate_halt_noskip (vcpu); } +static int tdx_complete_pio_out (struct kvm_vcpu *vcpu) +{ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu,0 ); +return 1 ; +} + +static int tdx_complete_pio_in (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +int ret; + +WARN_ON_ONCE (vcpu->arch.pio.count !=1 ); + + ret = ctxt->ops->pio_in_emulated (ctxt, vcpu->arch.pio.size, + vcpu->arch.pio.port, &val,1 ); +WARN_ON_ONCE (!ret); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu, val); + +return 1 ; +} + +static int tdx_emulate_io (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +unsigned int port; +int size, ret; +bool write; + + ++vcpu->stat.io_exits; + + size =tdvmcall_a0_read (vcpu); + write =tdvmcall_a1_read (vcpu); + port =tdvmcall_a2_read (vcpu); +arch/x86/kvm/vmx/tdx.c static int tdx_emulate_hlt(struct kvm_vcpu *vcpu) · line 1202
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+if (size !=1 && size !=2 && size !=4 ) { +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } + +if (write) { + val =tdvmcall_a3_read (vcpu); + ret = ctxt->ops->pio_out_emulated (ctxt, size, port, &val,1 ); + +/* No need for a complete_userspace_io callback. */ I am confused about the comment.
Quoted 3 lines
Quoted 2 lines
The code below sets the
complete_userspace_iocallback for write case, i.e.tdx_complete_pio_out().You're correct. This comment is stale and should be removed it.
Also, since the tdx_complete_pio_out() is installed as complete_userspace_io callback for write, it's more reasonable to move the reset of pio.count into tdx_complete_pio_out(). How about the following fixup:
arch/x86/kvm/vmx/tdx.c +0 -0
@@ -1115,6 +1115,7 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)
static int tdx_complete_pio_out(struct kvm_vcpu *vcpu)
{
+ vcpu->arch.pio.count = 0;
tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); tdvmcall_set_return_val(vcpu, 0);
return 1;
@@ -1159,15 +1160,13 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) if (write) {
val = tdvmcall_a3_read(vcpu);
ret = ctxt->ops->pio_out_emulated(ctxt, size, port, &val, 1); -
- /* No need for a complete_userspace_io callback. */ - vcpu->arch.pio.count = 0;
- } else
+ } else {
ret = ctxt->ops->pio_in_emulated(ctxt, size, port, &val, 1); + }
- if (ret)
+ if (ret) {
tdvmcall_set_return_val(vcpu, val);
- else {
+ } else {
if (write)
vcpu->arch.complete_userspace_io = tdx_complete_pio_out; else
Re: [PATCH v19 109/130] KVM: TDX: Handle TDX PV port io hypercall
Quoted 80 lines · Binbin Wu
Quoted 77 lines · Isaku Yamahata
Quoted 74 lines · Binbin Wu
Quoted 60 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Wire up TDX PV port IO hypercall to the KVM backend function.
Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
v18:
- Fix out case to set R10 and R11 correctly when user space handled port out.--- arch/x86/kvm/vmx/tdx.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+)arch/x86/kvm/vmx/tdx.c +39
-0@@ -1152,6 +1152,71 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu)return kvm_emulate_halt_noskip (vcpu); } +static int tdx_complete_pio_out (struct kvm_vcpu *vcpu) +{ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu,0 ); +return 1 ; +} + +static int tdx_complete_pio_in (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +int ret; + +WARN_ON_ONCE (vcpu->arch.pio.count !=1 ); + + ret = ctxt->ops->pio_in_emulated (ctxt, vcpu->arch.pio.size, + vcpu->arch.pio.port, &val,1 ); +WARN_ON_ONCE (!ret); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu, val); + +return 1 ; +} + +static int tdx_emulate_io (struct kvm_vcpu *vcpu) +{ +struct x86_emulate_ctxt *ctxt = vcpu->arch.emulate_ctxt; +unsigned long val =0 ; +unsigned int port; +int size, ret; +bool write; + + ++vcpu->stat.io_exits; + + size =tdvmcall_a0_read (vcpu); + write =tdvmcall_a1_read (vcpu); + port =tdvmcall_a2_read (vcpu); +arch/x86/kvm/vmx/tdx.c static int tdx_emulate_hlt(struct kvm_vcpu *vcpu) · line 1202
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+if (size !=1 && size !=2 && size !=4 ) { +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } + +if (write) { + val =tdvmcall_a3_read (vcpu); + ret = ctxt->ops->pio_out_emulated (ctxt, size, port, &val,1 ); + +/* No need for a complete_userspace_io callback. */ I am confused about the comment.
Quoted 7 lines
Quoted 3 lines
Quoted 2 lines
The code below sets the
complete_userspace_iocallback for write case, i.e.tdx_complete_pio_out().You're correct. This comment is stale and should be removed it.
Also, since the
tdx_complete_pio_out()is installed ascomplete_userspace_iocallback for write, it's more reasonable to move the reset of pio.count intotdx_complete_pio_out().
How about the following fixup:
It makes sense. It matches better with other complete callbacks for tdx_complete_pio_out() to clear pio.count to 0.
Quoted 36 lines
arch/x86/kvm/vmx/tdx.c +5
-6@@ -1115,6 +1115,7 @@ static int tdx_emulate_hlt(struct kvm_vcpu *vcpu) static int tdx_complete_pio_out(struct kvm_vcpu *vcpu) { + vcpu->arch.pio.count = 0; tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); tdvmcall_set_return_val(vcpu, 0); return 1;@@ -1159,15 +1160,13 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) if (write) { val = tdvmcall_a3_read(vcpu); ret = ctxt->ops->pio_out_emulated(ctxt, size, port, &val,1 ); - - /* No need for a complete_userspace_io callback. */ - vcpu->arch.pio.count = 0; - } else + } else { ret = ctxt->ops->pio_in_emulated(ctxt, size, port, &val, 1); + } - if (ret) + if (ret) { tdvmcall_set_return_val(vcpu, val); - else { + } else { if (write)
vcpu->arch.complete_userspace_io=tdx_complete_pio_out;
else
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 110/130 KVM: TDX: Handle TDX PV MMIO hypercall
In reply to [email protected]From: Sean Christopherson <[email protected]>
Export kvm_io_bus_read and kvm_mmio tracepoint and wire up TDX PV MMIO hypercall to the KVM backend functions.
kvm_io_bus_read/write() searches KVM device emulated in kernel of the given MMIO address and emulates the MMIO. As TDX PV MMIO also needs it, export kvm_io_bus_read(). kvm_io_bus_write() is already exported. TDX PV MMIO emulates some of MMIO itself. To add trace point consistently with x86 kvm, export kvm_mmio tracepoint.
--- arch/x86/kvm/vmx/tdx.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 1 + virt/kvm/kvm_main.c | 2 + 3 files changed, 117 insertions(+)
arch/x86/kvm/vmx/tdx.c +114 -0
@@ -1217,6 +1217,118 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu)return ret; } +static int tdx_complete_mmio (struct kvm_vcpu *vcpu) +{ +unsigned long val =0 ; +gpa_t gpa; +int size; + +KVM_BUG_ON (vcpu->mmio_needed !=1 , vcpu->kvm); + vcpu->mmio_needed =0 ; + +if (!vcpu->mmio_is_write) { + gpa = vcpu->mmio_fragments[0 ].gpa; + size = vcpu->mmio_fragments[0 ].len; + +memcpy (&val, vcpu->run->mmio.data, size); +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); + } +return 1 ; +} + +static inline int tdx_mmio_write (struct kvm_vcpu *vcpu,gpa_t gpa,int size, +unsigned long val) +{ +if (kvm_iodevice_write (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_write (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , size, gpa, &val); +return 0 ; +} + +static inline int tdx_mmio_read (struct kvm_vcpu *vcpu,gpa_t gpa,int size) +{ +unsigned long val; + +if (kvm_iodevice_read (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_read (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); +return 0 ; +} + +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ +struct kvm_memory_slot *slot; +int size, write, r; +unsigned long val; +gpa_t gpa; + +KVM_BUG_ON (vcpu->mmio_needed, vcpu->kvm); + + size =tdvmcall_a0_read (vcpu); + write =tdvmcall_a1_read (vcpu); + gpa =tdvmcall_a2_read (vcpu); + val = write ?tdvmcall_a3_read (vcpu) :0 ; + +if (size !=1 && size !=2 && size !=4 && size !=8 ) + goto error; +if (write !=0 && write !=1 ) + goto error; + +/* Strip the shared bit, allow MMIO with and without it set. */ + gpa = gpa & ~gfn_to_gpa (kvm_gfn_shared_mask (vcpu->kvm)); + +if (size >8u || ((gpa + size -1 ) ^ gpa) &PAGE_MASK ) + goto error; + + slot =kvm_vcpu_gfn_to_memslot (vcpu,gpa_to_gfn (gpa)); +if (slot && !(slot->flags &KVM_MEMSLOT_INVALID )) + goto error; + +if (!kvm_io_bus_write (vcpu,KVM_FAST_MMIO_BUS , gpa,0 ,NULL )) { +trace_kvm_fast_mmio (gpa); +return 1 ; + } + +if (write) + r =tdx_mmio_write (vcpu, gpa, size, val); +else + r =tdx_mmio_read (vcpu, gpa, size); +if (!r) { +/* Kernel completed device emulation. */ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +return 1 ; + } + +/* Request the device emulation to userspace device model. */ + vcpu->mmio_needed =1 ; + vcpu->mmio_is_write = write; + vcpu->arch.complete_userspace_io = tdx_complete_mmio; + + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write; + vcpu->run->exit_reason =KVM_EXIT_MMIO ; + +if (write) { +memcpy (vcpu->run->mmio.data, &val, size); + }else { + vcpu->mmio_fragments[0 ].gpa = gpa; + vcpu->mmio_fragments[0 ].len = size; +trace_kvm_mmio (KVM_TRACE_MMIO_READ_UNSATISFIED , size, gpa,NULL ); + } +return 0 ; + +error: +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))
@@ -1229,6 +1341,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)return tdx_emulate_hlt (vcpu);case EXIT_REASON_IO_INSTRUCTION :return tdx_emulate_io (vcpu); +case EXIT_REASON_EPT_VIOLATION : +return tdx_emulate_mmio (vcpu);default :break ; }
arch/x86/kvm/x86.c +1 -0
@@ -13975,6 +13975,7 @@ EXPORT_SYMBOL_GPL(kvm_sev_es_string_io);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_entry);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_exit); +EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_mmio);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_fast_mmio);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_inj_virq);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_page_fault);
virt/kvm/kvm_main.c +2 -0
@@ -2689,6 +2689,7 @@ struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfnreturn NULL ; } +EXPORT_SYMBOL_GPL (kvm_vcpu_gfn_to_memslot);bool kvm_is_visible_gfn (struct kvm *kvm,gfn_t gfn) {
@@ -5992,6 +5993,7 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr, r =__kvm_io_bus_read (vcpu, bus, &range, val);return r <0 ? r :0 ; } +EXPORT_SYMBOL_GPL (kvm_io_bus_read);int kvm_io_bus_register_dev (struct kvm *kvm,enum kvm_bus bus_idx,gpa_t addr,int len,struct kvm_io_device *dev)
Signature · 1 line
2.25.1
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 82 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
Export
kvm_io_bus_readandkvm_mmiotracepoint and wire up TDX PV MMIO hypercall to the KVM backend functions.kvm_io_bus_read/write() searches KVM device emulated in kernel of the given MMIO address and emulates the MMIO. As TDX PV MMIO also needs it, export
kvm_io_bus_read().kvm_io_bus_write()is already exported. TDX PV MMIO emulates some of MMIO itself. To add trace point consistently with x86 kvm, exportkvm_mmiotracepoint.Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>--- arch/x86/kvm/vmx/tdx.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 1 + virt/kvm/kvm_main.c | 2 + 3 files changed, 117 insertions(+)arch/x86/kvm/vmx/tdx.c +54
-0@@ -1217,6 +1217,118 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu)return ret; } +static int tdx_complete_mmio (struct kvm_vcpu *vcpu) +{ +unsigned long val =0 ; +gpa_t gpa; +int size; + +KVM_BUG_ON (vcpu->mmio_needed !=1 , vcpu->kvm); + vcpu->mmio_needed =0 ; + +if (!vcpu->mmio_is_write) { + gpa = vcpu->mmio_fragments[0 ].gpa; + size = vcpu->mmio_fragments[0 ].len; + +memcpy (&val, vcpu->run->mmio.data, size); +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); + } +return 1 ; +} + +static inline int tdx_mmio_write (struct kvm_vcpu *vcpu,gpa_t gpa,int size, +unsigned long val) +{ +if (kvm_iodevice_write (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_write (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , size, gpa, &val); +return 0 ; +} + +static inline int tdx_mmio_read (struct kvm_vcpu *vcpu,gpa_t gpa,int size) +{ +unsigned long val; + +if (kvm_iodevice_read (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_read (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); +return 0 ; +} + +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ +struct kvm_memory_slot *slot; +int size, write, r; +unsigned long val; +gpa_t gpa; + +KVM_BUG_ON (vcpu->mmio_needed, vcpu->kvm); + + size =tdvmcall_a0_read (vcpu);
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1283
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ write =tdvmcall_a1_read (vcpu); + gpa =tdvmcall_a2_read (vcpu); + val = write ?tdvmcall_a3_read (vcpu) :0 ; + +if (size !=1 && size !=2 && size !=4 && size !=8 ) + goto error; +if (write !=0 && write !=1 ) + goto error; + +/* Strip the shared bit, allow MMIO with and without it set. */
Based on the discussion https://lore.kernel.org/all/[email protected]/
Do we still allow the MMIO without shared bit?
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1286
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +3
-0+ gpa = gpa & ~gfn_to_gpa (kvm_gfn_shared_mask (vcpu->kvm)); + +if (size >8u || ((gpa + size -1 ) ^ gpa) &PAGE_MASK )
"size > 8u" can be removed, since based on the check of size above, it can't be greater than 8.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1293
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +7
-0+ goto error; + + slot =kvm_vcpu_gfn_to_memslot (vcpu,gpa_to_gfn (gpa)); +if (slot && !(slot->flags &KVM_MEMSLOT_INVALID )) + goto error; + +if (!kvm_io_bus_write (vcpu,KVM_FAST_MMIO_BUS , gpa,0 ,NULL )) {
Should this be checked for write first?
I check the handle_ept_misconfig() in VMX, it doesn't check write first neither.
Functionally, it should be OK since guest will not read the address range of fast mmio. So the read case will be filtered out by ioeventfd_write().
But it has take a long way to get to ioeventfd_write().
Isn't it more efficient to check write first?
Quoted 82 lines
arch/x86/kvm/vmx/tdx.c +40
-0+trace_kvm_fast_mmio (gpa); +return 1 ; + } + +if (write) + r =tdx_mmio_write (vcpu, gpa, size, val); +else + r =tdx_mmio_read (vcpu, gpa, size); +if (!r) { +/* Kernel completed device emulation. */ +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +return 1 ; + } + +/* Request the device emulation to userspace device model. */ + vcpu->mmio_needed =1 ; + vcpu->mmio_is_write = write; + vcpu->arch.complete_userspace_io = tdx_complete_mmio; + + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write; + vcpu->run->exit_reason =KVM_EXIT_MMIO ; + +if (write) { +memcpy (vcpu->run->mmio.data, &val, size); + }else { + vcpu->mmio_fragments[0 ].gpa = gpa; + vcpu->mmio_fragments[0 ].len = size; +trace_kvm_mmio (KVM_TRACE_MMIO_READ_UNSATISFIED , size, gpa,NULL ); + } +return 0 ; + +error: +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))@@ -1229,6 +1341,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)return tdx_emulate_hlt (vcpu);case EXIT_REASON_IO_INSTRUCTION :return tdx_emulate_io (vcpu); +case EXIT_REASON_EPT_VIOLATION : +return tdx_emulate_mmio (vcpu);default :break ; }arch/x86/kvm/x86.c +1
-0@@ -13975,6 +13975,7 @@ EXPORT_SYMBOL_GPL(kvm_sev_es_string_io);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_entry);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_exit); +EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_mmio);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_fast_mmio);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_inj_virq);EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_page_fault);virt/kvm/kvm_main.c +2
-0@@ -2689,6 +2689,7 @@ struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfnreturn NULL ; } +EXPORT_SYMBOL_GPL (kvm_vcpu_gfn_to_memslot);bool kvm_is_visible_gfn (struct kvm *kvm,gfn_t gfn) {@@ -5992,6 +5993,7 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr, r =__kvm_io_bus_read (vcpu, bus, &range, val);return r <0 ? r :0 ; } +EXPORT_SYMBOL_GPL (kvm_io_bus_read);int kvm_io_bus_register_dev (struct kvm *kvm,enum kvm_bus bus_idx,gpa_t addr,int len,struct kvm_io_device *dev)
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 40 lines · Binbin Wu
Quoted 12 lines
arch/x86/kvm/vmx/tdx.c +11
-0+ +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ + struct kvm_memory_slot *slot; + int size, write, r; + unsigned long val; + gpa_t gpa; + + KVM_BUG_ON(vcpu->mmio_needed, vcpu->kvm); + + size = tdvmcall_a0_read(vcpu);arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1283
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ write = tdvmcall_a1_read(vcpu); + gpa = tdvmcall_a2_read(vcpu); + val = write ? tdvmcall_a3_read(vcpu) : 0; + + if (size != 1 && size != 2 && size != 4 && size != 8) + goto error; + if (write != 0 && write != 1) + goto error; + + /* Strip the shared bit, allow MMIO with and without it set. */Based on the discussion https://lore.kernel.org/all/[email protected]/
Do we still allow the MMIO without shared bit?Quoted 3 lines
+ gpa = gpa & ~gfn_to_gpa(kvm_gfn_shared_mask(vcpu->kvm)); +
+ if (size > 8u || ((gpa + size - 1) ^ gpa) &PAGE_MASK)"size > 8u" can be removed, since based on the check of size above, it can't be greater than 8.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1293
Quoted 7 lines
+ goto error;
arch/x86/kvm/vmx/tdx.c +6
-0+ + slot = kvm_vcpu_gfn_to_memslot(vcpu, gpa_to_gfn(gpa)); + if (slot && !(slot->flags & KVM_MEMSLOT_INVALID)) + goto error; + + if (!kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {Should this be checked for write first?
Quoted 6 lines
I check the
handle_ept_misconfig()in VMX, it doesn't check write first neither.Functionally, it should be OK since guest will not read the address range of fast mmio. So the read case will be filtered out by
ioeventfd_write().
But it has take a long way to get toioeventfd_write().
Isn't it more efficient to check write first?
I got the reason why in handle_ept_misconfig(), it tries to do fast mmio write without checking. It was intended to make fast mmio faster.
And for ept misconfig case, it's not easy to get the info of read/write.
But in this patch, we have already have read/write info, so maybe we can add the check for write before fast mmio?
Quoted 85 lines
Quoted 83 lines
+
trace_kvm_fast_mmio(gpa);
+ return 1;
+ }arch/x86/kvm/vmx/tdx.c +27
-0+ + if (write) + r = tdx_mmio_write(vcpu, gpa, size, val); + else + r = tdx_mmio_read(vcpu, gpa, size); + if (!r) { + /* Kernel completed device emulation. */ + tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); + return 1; + } + + /* Request the device emulation to userspace device model. */ + vcpu->mmio_needed = 1; + vcpu->mmio_is_write = write; + vcpu->arch.complete_userspace_io = tdx_complete_mmio; + + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write; + vcpu->run->exit_reason = KVM_EXIT_MMIO; + + if (write) { + memcpy(vcpu->run->mmio.data, &val, size); + } else { + vcpu->mmio_fragments[0].gpa = gpa; + vcpu->mmio_fragments[0].len = size; + trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, size, gpa,NULL);
+ }
+ return 0;arch/x86/kvm/vmx/tdx.c +6
-0+ +error: + tdvmcall_set_return_code(vcpu, TDVMCALL_INVALID_OPERAND); + return 1; +} +static int
handle_tdvmcall(struct kvm_vcpu *vcpu){
if (tdvmcall_exit_type(vcpu))arch/x86/kvm/vmx/tdx.c +0
-0@@ -1229,6 +1341,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu) return tdx_emulate_hlt(vcpu);case
EXIT_REASON_IO_INSTRUCTION:
returntdx_emulate_io(vcpu);
+ caseEXIT_REASON_EPT_VIOLATION:
+ returntdx_emulate_mmio(vcpu);
default:
break;
}arch/x86/kvm/x86.c +1
-0@@ -13975,6 +13975,7 @@ EXPORT_SYMBOL_GPL(kvm_sev_es_string_io); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_entry); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit); +EXPORT_TRACEPOINT_SYMBOL_GPL (kvm_mmio); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_fast_mmio); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_page_fault);virt/kvm/kvm_main.c +2
-0@@ -2689,6 +2689,7 @@ struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn return NULL; } +EXPORT_SYMBOL_GPL(kvm_vcpu_gfn_to_memslot); bool kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn) {@@ -5992,6 +5993,7 @@ int kvm_io_bus_read(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr, r = __kvm_io_bus_read(vcpu, bus, &range, val); return r < 0 ? r : 0; } +EXPORT_SYMBOL_GPL (kvm_io_bus_read); int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, struct kvm_io_device *dev)
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 20 lines · Binbin Wu
Quoted 17 lines · Binbin Wu
Quoted 17 lines
arch/x86/kvm/vmx/tdx.c +16
-0+ +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ + struct kvm_memory_slot *slot; + int size, write, r; + unsigned long val; + gpa_t gpa; + + KVM_BUG_ON(vcpu->mmio_needed, vcpu->kvm); + + size = tdvmcall_a0_read(vcpu); + write = tdvmcall_a1_read(vcpu); + gpa = tdvmcall_a2_read(vcpu); + val = write ? tdvmcall_a3_read(vcpu) : 0; + + if (size != 1 && size != 2 && size != 4 && size != 8)
Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ goto error;
+ if (write != 0 && write != 1)
+ goto error;
+
+ /* Strip the shared bit, allow MMIO with and without it set. */Based on the discussion
https://lore.kernel.org/all/[email protected]/
Do we still allow the MMIO without shared bit?
That's independent. The part is how to work around guest accesses the MMIO region with private GPA. This part is, the guest issues TDG.VP.VMCALL<MMMIO> and KVM masks out the shared bit to make it friendly to the user space VMM.
Quoted 5 lines
Quoted 5 lines
Quoted 3 lines
+ gpa = gpa & ~gfn_to_gpa(kvm_gfn_shared_mask(vcpu->kvm)); +
+ if (size > 8u || ((gpa + size - 1) ^ gpa) &PAGE_MASK)"size > 8u" can be removed, since based on the check of size above, it can't be greater than 8.
Yes, will remove the check.
Quoted 18 lines
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1293
Quoted 7 lines
+ goto error;
arch/x86/kvm/vmx/tdx.c +6
-0+ + slot = kvm_vcpu_gfn_to_memslot(vcpu, gpa_to_gfn(gpa)); + if (slot && !(slot->flags & KVM_MEMSLOT_INVALID)) + goto error; + + if (!kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {Should this be checked for write first?
Quoted 8 lines
I check the
handle_ept_misconfig()in VMX, it doesn't check write first neither.Functionally, it should be OK since guest will not read the address range of fast mmio.
So the read case will be filtered out byioeventfd_write(). But it has take a long way to get toioeventfd_write(). Isn't it more efficient to check write first?
Quoted 7 lines
I got the reason why in
handle_ept_misconfig(), it tries to do fast mmio write without checking.
It was intended to make fast mmio faster.
And for ept misconfig case, it's not easy to get the info of read/write.But in this patch, we have already have read/write info, so maybe we can add the check for write before fast mmio?
Yes, let's add it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 29 lines · Isaku Yamahata
Quoted 18 lines · Binbin Wu
Quoted 16 lines · Binbin Wu
Quoted 16 lines
arch/x86/kvm/vmx/tdx.c +16
-0+ +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ + struct kvm_memory_slot *slot; + int size, write, r; + unsigned long val; + gpa_t gpa; + + KVM_BUG_ON(vcpu->mmio_needed, vcpu->kvm); + + size = tdvmcall_a0_read(vcpu); + write = tdvmcall_a1_read(vcpu); + gpa = tdvmcall_a2_read(vcpu); + val = write ? tdvmcall_a3_read(vcpu) : 0; + + if (size != 1 && size != 2 && size != 4 && size != 8)Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ goto error;
+ if (write != 0 && write != 1)
+ goto error;
+
+ /* Strip the shared bit, allow MMIO with and without it set. */Based on the discussion
https://lore.kernel.org/all/[email protected]/
Do we still allow the MMIO without shared bit?
Quoted 4 lines
That's independent. The part is how to work around guest accesses the MMIO region with private GPA. This part is, the guest issues TDG.VP.VMCALL<MMMIO> and KVM masks out the shared bit to make it friendly to the user space VMM.
It's similar.
The tdvmcall from the guest for mmio can also be private GPA, which is not reasonable, right? According to the comment, kvm doens't care about if the TD guest issue the tdvmcall with private GPA or shared GPA.
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 36 lines · Binbin Wu
Quoted 29 lines · Isaku Yamahata
Quoted 18 lines · Binbin Wu
Quoted 16 lines · Binbin Wu
Quoted 16 lines
(quoted diff) +16
-0+ +static int tdx_emulate_mmio(struct kvm_vcpu *vcpu) +{ + struct kvm_memory_slot *slot; + int size, write, r; + unsigned long val; + gpa_t gpa; + + KVM_BUG_ON(vcpu->mmio_needed, vcpu->kvm); + + size = tdvmcall_a0_read(vcpu); + write = tdvmcall_a1_read(vcpu); + gpa = tdvmcall_a2_read(vcpu); + val = write ? tdvmcall_a3_read(vcpu) : 0; + + if (size != 1 && size != 2 && size != 4 && size != 8)Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
+ goto error;
+ if (write != 0 && write != 1)
+ goto error;
+
+ /* Strip the shared bit, allow MMIO with and without it set. */Based on the discussion
https://lore.kernel.org/all/[email protected]/
Do we still allow the MMIO without shared bit?Quoted 4 lines
That's independent. The part is how to work around guest accesses the MMIO region with private GPA. This part is, the guest issues TDG.VP.VMCALL<MMMIO> and KVM masks out the shared bit to make it friendly to the user space VMM.
Quoted 5 lines
It's similar.
The tdvmcall from the guest for mmio can also be private GPA, which is not reasonable, right?
According to the comment, kvm doens't care about if the TD guest issue the tdvmcall with private GPA or shared GPA.
I checked the GHCI spec. It clearly states this hypercall is for shared GPA. We should return error for private GPA.
This TDG.VP.VMCALL is used to help request the VMM perform emulated-MMIO-access operation. The VMM may emulate MMIO space in shared-GPA space. The VMM can induce a #VE on these shared-GPA accesses by mapping shared GPAs with the suppress-VE bit cleared in the EPT Entries corresponding to these mappings
So we'll have something as follows. Compile only tested.
arch/x86/kvm/vmx/tdx.c +19 -9
@@ -1281,24 +1281,34 @@ static int tdx_emulate_mmio(struct kvm_vcpu *vcpu)if (write !=0 && write !=1 ) goto error; -/* Strip the shared bit, allow MMIO with and without it set. */ +/* +* MMIO with TDG.VP.VMCALL<MMIO> allows only shared GPA because +* private GPA is for device assignment. +*/ +if (kvm_is_private_gpa (gpa)) + goto error; + +/* +* Strip the shared bit because device emulator is assigned to GPA +* without shared bit. We'd like the existing code untouched. +*/ gpa = gpa & ~gfn_to_gpa (kvm_gfn_shared_mask (vcpu->kvm)); -if (size >8u || ((gpa + size -1 ) ^ gpa) &PAGE_MASK ) +/* Disallow MMIO crossing page boundary for simplicity. */ +if (((gpa + size -1 ) ^ gpa) &PAGE_MASK ) goto error; slot =kvm_vcpu_gfn_to_memslot (vcpu,gpa_to_gfn (gpa));if (slot && !(slot->flags &KVM_MEMSLOT_INVALID )) goto error; -if (!kvm_io_bus_write (vcpu,KVM_FAST_MMIO_BUS , gpa,0 ,NULL )) { -trace_kvm_fast_mmio (gpa); -return 1 ; - } - -if (write) +if (write) { +if (!kvm_io_bus_write (vcpu,KVM_FAST_MMIO_BUS , gpa,0 ,NULL )) { +trace_kvm_fast_mmio (gpa); +return 1 ; + } r =tdx_mmio_write (vcpu, gpa, size, val); -else + }else r =tdx_mmio_read (vcpu, gpa, size);if (!r) {/* Kernel completed device emulation. */
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
In reply to [email protected]Quoted 26 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
Export
kvm_io_bus_readandkvm_mmiotracepoint and wire up TDX PV MMIO hypercall to the KVM backend functions.kvm_io_bus_read/write() searches KVM device emulated in kernel of the given MMIO address and emulates the MMIO. As TDX PV MMIO also needs it, export
kvm_io_bus_read().kvm_io_bus_write()is already exported. TDX PV MMIO emulates some of MMIO itself. To add trace point consistently with x86 kvm, exportkvm_mmiotracepoint.Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>--- arch/x86/kvm/vmx/tdx.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 1 + virt/kvm/kvm_main.c | 2 + 3 files changed, 117 insertions(+)arch/x86/kvm/vmx/tdx.c +0
-0@@ -1217,6 +1217,118 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu)return ret;
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1227
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +8
-0} +static int tdx_complete_mmio (struct kvm_vcpu *vcpu) +{ +unsigned long val =0 ; +gpa_t gpa; +int size; + +KVM_BUG_ON (vcpu->mmio_needed !=1 , vcpu->kvm); + vcpu->mmio_needed =0 ;
mmio_needed is used by instruction emulator to setup the complete callback. Since TDX handle MMIO in a PV way, mmio_needed is not needed here.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1229
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ +if (!vcpu->mmio_is_write) {
It's also needed by instruction emulator, we can use vcpu->run->mmio.is_write instead.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1231
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ gpa = vcpu->mmio_fragments[0 ].gpa; + size = vcpu->mmio_fragments[0 ].len;
Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed. Just use vcpu->run->mmio.phys_addr and vcpu->run->mmio.len?
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1236
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +memcpy (&val, vcpu->run->mmio.data, size); +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); + }
Tracepoint for KVM_TRACE_MMIO_WRITE is missing when it is handled in userspace.
Also, the return code is only set when the emulation is done in kernel, but not set when it's handled in userspace.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1238
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+return 1 ; +}
How about the fixup as following:
@@ -1173,19 +1173,18 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) static int tdx_complete_mmio(struct kvm_vcpu *vcpu) { unsigned long val = 0; - gpa_t gpa; - int size; - - vcpu->mmio_needed = 0; - - if (!vcpu->mmio_is_write) { - gpa = vcpu->mmio_fragments[0].gpa; - size = vcpu->mmio_fragments[0].len; + gpa_t gpa = vcpu->run->mmio.phys_addr; + int size = vcpu->run->mmio.len; + if (vcpu->run->mmio.is_write) { + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, &val); + } else { memcpy(&val, vcpu->run->mmio.data, size); tdvmcall_set_return_val(vcpu, val); trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); } + + tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); return 1; }
Quoted 34 lines
arch/x86/kvm/vmx/tdx.c +34
-0+ +static inline int tdx_mmio_write (struct kvm_vcpu *vcpu,gpa_t gpa,int size, +unsigned long val) +{ +if (kvm_iodevice_write (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_write (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , size, gpa, &val); +return 0 ; +} + +static inline int tdx_mmio_read (struct kvm_vcpu *vcpu,gpa_t gpa,int size) +{ +unsigned long val; + +if (kvm_iodevice_read (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_read (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); +return 0 ; +} + +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ +struct kvm_memory_slot *slot; +int size, write, r; +unsigned long val; +gpa_t gpa; + +KVM_BUG_ON (vcpu->mmio_needed, vcpu->kvm); +
[...]
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1310
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+ +/* Request the device emulation to userspace device model. */ + vcpu->mmio_needed =1 ; + vcpu->mmio_is_write = write;
Then they can be dropped.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1322
Quoted 12 lines
arch/x86/kvm/vmx/tdx.c +12
-0+ vcpu->arch.complete_userspace_io = tdx_complete_mmio; + + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write; + vcpu->run->exit_reason =KVM_EXIT_MMIO ; + +if (write) { +memcpy (vcpu->run->mmio.data, &val, size); + }else { + vcpu->mmio_fragments[0 ].gpa = gpa; + vcpu->mmio_fragments[0 ].len = size;
These two lines can be dropped as well.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1331
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c +9
-0+trace_kvm_mmio (KVM_TRACE_MMIO_READ_UNSATISFIED , size, gpa,NULL ); + } +return 0 ; + +error: +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; +} +
- /* Request the device emulation to userspace device model. */ - vcpu->mmio_needed = 1; - vcpu->mmio_is_write = write; vcpu->arch.complete_userspace_io = tdx_complete_mmio; vcpu->run->mmio.phys_addr = gpa; @@ -1265,13 +1272,11 @@ static int tdx_emulate_mmio(struct kvm_vcpu *vcpu) vcpu->run->mmio.is_write = write; vcpu->run->exit_reason = KVM_EXIT_MMIO; - if (write) { + if (write) memcpy(vcpu->run->mmio.data, &val, size); - } else { - vcpu->mmio_fragments[0].gpa = gpa; - vcpu->mmio_fragments[0].len = size; + else trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, size, gpa, NULL); - } + return 0;
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 67 lines · Binbin Wu
Quoted 25 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
Export
kvm_io_bus_readandkvm_mmiotracepoint and wire up TDX PV MMIO hypercall to the KVM backend functions.kvm_io_bus_read/write() searches KVM device emulated in kernel of the given MMIO address and emulates the MMIO. As TDX PV MMIO also needs it, export kvm_io_bus_read().
kvm_io_bus_write()is already exported. TDX PV MMIO emulates some of MMIO itself. To add trace point consistently with x86 kvm, exportkvm_mmiotracepoint.Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
arch/x86/kvm/vmx/tdx.c| 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 1 +
virt/kvm/kvm_main.c | 2 +3 files changed, 117 insertions(+)arch/x86/kvm/vmx/tdx.c +0
-0arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1226
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +7
-0@@ -1217,6 +1217,118 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) return ret; } +static int tdx_complete_mmio(struct kvm_vcpu *vcpu) +{ + unsigned long val = 0; + gpa_t gpa; + int size; + + KVM_BUG_ON(vcpu->mmio_needed != 1, vcpu->kvm); + vcpu->mmio_needed = 0;
mmio_neededis used by instruction emulator to setup the complete callback. Since TDX handle MMIO in a PV way,mmio_neededis not needed here.arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1228
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ + if (!vcpu->mmio_is_write) {It's also needed by instruction emulator, we can use
vcpu->run->mmio.is_writeinstead.arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1231
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ gpa = vcpu->mmio_fragments[0].gpa; + size = vcpu->mmio_fragments[0].len;Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed. Just use
vcpu->run->mmio.phys_addrandvcpu->run->mmio.len?arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1236
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ + memcpy(&val, vcpu->run->mmio.data, size); + tdvmcall_set_return_val(vcpu, val); + trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); + }Tracepoint for
KVM_TRACE_MMIO_WRITEis missing when it is handled in userspace.Also, the return code is only set when the emulation is done in kernel, but not set when it's handled in userspace.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1237
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ return 1; +}How about the fixup as following:
Review static int tdx_emulate_io(struct kvm_vcpu · line 1174
Quoted 2 lines
(quoted diff) +0
-0@@ -1173,19 +1173,18 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) static int tdx_complete_mmio(struct kvm_vcpu *vcpu) { unsigned long val = 0; - gpa_t gpa; - int size; - - vcpu->mmio_needed = 0; - - if (!vcpu->mmio_is_write) { - gpa = vcpu->mmio_fragments[0].gpa; - size = vcpu->mmio_fragments[0].len; + gpa_t gpa = vcpu->run->mmio.phys_addr; + int size = vcpu->run->mmio.len; + if (vcpu->run->mmio.is_write) { + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, &val); + } else { memcpy(&val, vcpu->run->mmio.data, size); tdvmcall_set_return_val(vcpu, val); trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); } + + tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); return 1; }
Sorry for the mess.
@@ -1173,19 +1173,18 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) static int tdx_complete_mmio(struct kvm_vcpu *vcpu)
{
unsigned long val = 0;
- gpa_t gpa;
- int size;
-
- vcpu->mmio_needed = 0;
-
- if (!vcpu->mmio_is_write) {
- gpa = vcpu->mmio_fragments[0].gpa;
- size = vcpu->mmio_fragments[0].len;
+ gpa_t gpa = vcpu->run->mmio.phys_addr;
+ int size = vcpu->run->mmio.len;
+ if (vcpu->run->mmio.is_write) {
+ trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, &val); + } else {
memcpy(&val, vcpu->run->mmio.data, size); tdvmcall_set_return_val(vcpu, val); trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); }
+
+ tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); return 1;
}
Quoted 62 lines
Quoted 40 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +static inline int tdx_mmio_write (struct kvm_vcpu *vcpu,gpa_t gpa,int size, + unsigned long val) +{ + if (kvm_iodevice_write(vcpu, &vcpu->arch.apic->dev, gpa, size,&val) &&
+kvm_io_bus_write(vcpu, KVM_MMIO_BUS, gpa, size, &val)) + return-EOPNOTSUPP;arch/x86/kvm/vmx/tdx.c +6
-0+ + trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, &val); + return 0; +} + +static inline int tdx_mmio_read(struct kvm_vcpu *vcpu, gpa_t gpa,int size)
arch/x86/kvm/vmx/tdx.c +4
-0+{ + unsigned long val; + + if (kvm_iodevice_read(vcpu, &vcpu->arch.apic->dev, gpa, size,&val) &&
+kvm_io_bus_read(vcpu, KVM_MMIO_BUS, gpa, size, &val)) + return-EOPNOTSUPP;arch/x86/kvm/vmx/tdx.c +15
-0+ + tdvmcall_set_return_val(vcpu, val); + trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); + return 0; +} + +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ + struct kvm_memory_slot *slot; + int size, write, r; + unsigned long val; + gpa_t gpa; + + KVM_BUG_ON(vcpu->mmio_needed, vcpu->kvm); +[...]
Quoted 4 lines
+
+ /* Request the device emulation to userspace device model. */ +vcpu->mmio_needed= 1;
+vcpu->mmio_is_write= write;Then they can be dropped.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1322
Quoted 12 lines
+
vcpu->arch.complete_userspace_io=tdx_complete_mmio;arch/x86/kvm/vmx/tdx.c +11
-0+ + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write; + vcpu->run->exit_reason = KVM_EXIT_MMIO; + + if (write) { + memcpy(vcpu->run->mmio.data, &val, size); + } else { + vcpu->mmio_fragments[0].gpa = gpa; + vcpu->mmio_fragments[0].len = size;These two lines can be dropped as well.
Quoted 15 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1331
Quoted 9 lines
+
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, size, gpa, NULL); + }
+ return 0;arch/x86/kvm/vmx/tdx.c +6
-0+ +error: + tdvmcall_set_return_code(vcpu, TDVMCALL_INVALID_OPERAND); + return 1; +} +- /* Request the device emulation to userspace device model. */ -
vcpu->mmio_needed= 1; -vcpu->mmio_is_write= write;vcpu->arch.complete_userspace_io=tdx_complete_mmio;vcpu->run->mmio.phys_addr= gpa; @@ -1265,13 +1272,11 @@ static inttdx_emulate_mmio(struct kvm_vcpu *vcpu)vcpu->run->mmio.is_write= write;vcpu->run->exit_reason=KVM_EXIT_MMIO; - if (write) { + if (write)memcpy(vcpu->run->mmio.data, &val, size); - } else { - vcpu->mmio_fragments[0].gpa = gpa; - vcpu->mmio_fragments[0].len = size; + elsetrace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, size, gpa, NULL); - } + return 0;
- /* Request the device emulation to userspace device model. */ - vcpu->mmio_needed = 1;
- vcpu->mmio_is_write = write; vcpu->arch.complete_userspace_io = tdx_complete_mmio;
vcpu->run->mmio.phys_addr = gpa;
@@ -1265,13 +1272,11 @@ static int tdx_emulate_mmio(struct kvm_vcpu *vcpu) vcpu->run->mmio.is_write = write; vcpu->run->exit_reason = KVM_EXIT_MMIO;
- if (write) {
+ if (write)
memcpy(vcpu->run->mmio.data, &val, size);
- } else {
- vcpu->mmio_fragments[0].gpa = gpa;
- vcpu->mmio_fragments[0].len = size;
+ else
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, size, gpa, NULL); - }
+
return 0;
Edgecombe, Rick P
maintainerRe: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
In reply to Binbin WuQuoted 6 lines · Binbin Wu
Quoted 2 lines
+ gpa = vcpu->mmio_fragments[0].gpa; + size = vcpu->mmio_fragments[0].len;
Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed. Just use
vcpu->run->mmio.phys_addrandvcpu->run->mmio.len?
Can we add a comment or something to that check, on why KVM doesn't handle it? Is it documented somewhere in the TDX ABI that it is not expected to be supported?
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 9 lines · Rick P
Quoted 5 lines · Binbin Wu
Quoted 2 lines
+ gpa = vcpu->mmio_fragments[0].gpa; + size = vcpu->mmio_fragments[0].len;
Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed. Just use
vcpu->run->mmio.phys_addrandvcpu->run->mmio.len?Can we add a comment or something to that check, on why KVM doesn't handle it? Is it documented somewhere in the TDX ABI that it is not expected to be supported?
TDX GHCI doesn't have such restriction.
According to the reply from Isaku in the below link, I think current restriction is due to software implementation for simplicity.
https://lore.kernel.org/kvm/[email protected]/
+ /* Disallow MMIO crossing page boundary for simplicity. */
+ if (((gpa + size - 1) ^ gpa) & PAGE_MASK)
goto error;
According to https://lore.kernel.org/all/165550567214.4207.3700499203810719676.tip-bot2@tip-bot2/,
for Linux as TDX guest, it rejects EPT violation #VEs that split pages based on the reason "MMIO accesses are supposed to be naturally aligned and therefore never cross page boundaries" to handle the load_unaligned_zeropad() case.
I am not sure "MMIO accesses are supposed to be naturally aligned" is true for all other OS as TDX guest, though.
Any suggestion?
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 21 lines · Binbin Wu
Quoted 9 lines · Rick P
Quoted 5 lines · Binbin Wu
Quoted 2 lines
+ gpa = vcpu->mmio_fragments[0].gpa; + size = vcpu->mmio_fragments[0].len;
Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed. Just use
vcpu->run->mmio.phys_addrandvcpu->run->mmio.len?Can we add a comment or something to that check, on why KVM doesn't handle it? Is it documented somewhere in the TDX ABI that it is not expected to be supported?
TDX GHCI doesn't have such restriction.
According to the reply from Isaku in the below link, I think current restriction is due to software implementation for simplicity.
https://lore.kernel.org/kvm/[email protected]/+ /* Disallow MMIO crossing page boundary for simplicity. */ + if (((gpa + size - 1) ^ gpa) &
PAGE_MASK)
goto error;
Quoted 8 lines
According to https://lore.kernel.org/all/165550567214.4207.3700499203810719676.tip-bot2@tip-bot2/,
for Linux as TDX guest, it rejects EPT violation #VEs that split pages based on the reason "MMIO accesses are supposed to be naturally aligned and therefore never cross page boundaries" to handle theload_unaligned_zeropad()case.I am not sure "MMIO accesses are supposed to be naturally aligned" is true for all other OS as TDX guest, though.
Any suggestion?
Had some discussion with Gao, Chao.
For TDX PV MMIO hypercall, it has got the GPA already.
I.e, we don't need to worry about case of "contiguous in virtual memory, but not be contiguous in physical memory".
Also, the size of the PV MMIO access is limited to 1, 2, 4, 8 bytes. No need to be split.
So, for TDX, there is no need to use vcpu->mmio_fragments[] even if the MMIO access crosses page boundary. The check for "Disallow MMIO crossing page boundary" can be removed and no extra fragments handling needed.
Edgecombe, Rick P
maintainerRe: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 34 lines · Binbin Wu
Quoted 31 lines · Binbin Wu
Quoted 10 lines · Rick P
Quoted 5 lines · Binbin Wu
Quoted 2 lines
+ gpa = vcpu->mmio_fragments[0].gpa; + size = vcpu->mmio_fragments[0].len;
Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed. Just use
vcpu->run->mmio.phys_addrandvcpu->run->mmio.len?Can we add a comment or something to that check, on why KVM doesn't handle it?
Is it documented somewhere in the TDX ABI that it is not expected to be supported?TDX GHCI doesn't have such restriction.
According to the reply from Isaku in the below link, I think current restriction is due to software implementation for simplicity.
https://lore.kernel.org/kvm/[email protected]/+ /* Disallow MMIO crossing page boundary for simplicity. */ + if (((gpa + size - 1) ^ gpa) &
PAGE_MASK)
goto error;According to
https://lore.kernel.org/all/165550567214.4207.3700499203810719676.tip-bot2@tip-bot2/
,
for Linux as TDX guest, it rejects EPT violation #VEs that split pages based on the reason "MMIO accesses are supposed to be naturally aligned and therefore never cross page boundaries" to handle theload_unaligned_zeropad()case.
Quoted 4 lines
Quoted 4 lines
I am not sure "MMIO accesses are supposed to be naturally aligned" is true for all other OS as TDX guest, though.
Any suggestion?
It doesn't seem likely a guest will rely on such MMIO to cause an error, so should be safe to wait until its needed. Let's leave a comment that it is not expected to be needed by guests, so just return an error for simplicity.
Quoted 7 lines
Quoted 2 lines
Had some discussion with Gao, Chao.
For TDX PV MMIO hypercall, it has got the GPA already.
I.e, we don't need to worry about case of "contiguous in virtual memory, but not be contiguous in physical memory".
Right, the guest should have to split it into two calls I guess. If the MMIO is physically contiguous anyway though, a guest could try still to make a request that spans two page. (assuming there is not some other HW restriction for that kind of access)
Quoted 10 lines
Also, the size of the PV MMIO access is limited to 1, 2, 4, 8 bytes. No need to be split.
So, for TDX, there is no need to use vcpu->mmio_fragments[] even if the MMIO access crosses page boundary.
The check for "Disallow MMIO crossing page boundary" can be removed and no extra fragments handling needed.
Ok, thanks.
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
In reply to Binbin WuQuoted 32 lines · Binbin Wu
Quoted 29 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
Export
kvm_io_bus_readandkvm_mmiotracepoint and wire up TDX PV MMIO hypercall to the KVM backend functions.kvm_io_bus_read/write() searches KVM device emulated in kernel of the given MMIO address and emulates the MMIO. As TDX PV MMIO also needs it, export
kvm_io_bus_read().kvm_io_bus_write()is already exported. TDX PV MMIO emulates some of MMIO itself. To add trace point consistently with x86 kvm, exportkvm_mmiotracepoint.Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>--- arch/x86/kvm/vmx/tdx.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 1 + virt/kvm/kvm_main.c | 2 + 3 files changed, 117 insertions(+)arch/x86/kvm/vmx/tdx.c +2
-0@@ -1217,6 +1217,118 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu)return ret; } +static int tdx_complete_mmio (struct kvm_vcpu *vcpu) +{
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1227
Quoted 6 lines
arch/x86/kvm/vmx/tdx.c +6
-0+unsigned long val =0 ; +gpa_t gpa; +int size; + +KVM_BUG_ON (vcpu->mmio_needed !=1 , vcpu->kvm); + vcpu->mmio_needed =0 ;
mmio_neededis used by instruction emulator to setup the complete callback. Since TDX handle MMIO in a PV way,mmio_neededis not needed here.
Ok, we don't need to update mmio_needed.
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1228
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ +if (!vcpu->mmio_is_write) {It's also needed by instruction emulator, we can use
vcpu->run->mmio.is_writeinstead.
No because vcpu->run->mmio is shared with user space. KVM need to stash it independently.
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1231
Quoted 3 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ gpa = vcpu->mmio_fragments[0 ].gpa; + size = vcpu->mmio_fragments[0 ].len;Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed.
Just usevcpu->run->mmio.phys_addrandvcpu->run->mmio.len?
ditto.
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1236
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +memcpy (&val, vcpu->run->mmio.data, size); +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); + }Tracepoint for
KVM_TRACE_MMIO_WRITEis missing when it is handled in userspace.
tdx_mmio_write() has it before existing to the user space. It matches with how write_mmio() behaves in x86.c.
Hmm, to match with other code, we should remove trace_kvm_mmio(KVM_TRACE_MMIO_READ) and keep KVM_TRACE_MMIO_READ_UNSATISFIED in tdx_emulate_mmio(). That's how read_prepare() and read_exit_mmio() behaves.
For MMIO read
- When kernel can handle the MMIO, KVM_TRACE_MMIO_READ with data.
- When exiting to the user space, KVM_TRACE_MMIO_READ_UNSATISFIED before the exit. No trace after the user space handled the MMIO.
For MMIO write
- KVM_TRACE_MMIO_WRITE before handling it.
Quoted 56 lines
Also, the return code is only set when the emulation is done in kernel, but not set when it's handled in userspace.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1237
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+return 1 ; +}How about the fixup as following:
(quoted diff) +0
-1@@ -1173,19 +1173,18 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) static int tdx_complete_mmio(struct kvm_vcpu *vcpu) { unsigned long val = 0; - gpa_t gpa; - int size; - - vcpu->mmio_needed = 0; - - if(!vcpu->mmio_is_write) { - gpa = vcpu->mmio_fragments[0].gpa; - size = vcpu->mmio_fragments[0].len; +
gpa_tgpa =vcpu->run->mmio.phys_addr; + int size =vcpu->run->mmio.len; + if (vcpu->run->mmio.is_write) { +trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, &val); + } else {memcpy(&val, vcpu->run->mmio.data, size);tdvmcall_set_return_val(vcpu, val);trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); } + +tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); return 1; }Quoted 34 lines
arch/x86/kvm/vmx/tdx.c +34
-0+ +static inline int tdx_mmio_write (struct kvm_vcpu *vcpu,gpa_t gpa,int size, +unsigned long val) +{ +if (kvm_iodevice_write (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_write (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , size, gpa, &val); +return 0 ; +} + +static inline int tdx_mmio_read (struct kvm_vcpu *vcpu,gpa_t gpa,int size) +{ +unsigned long val; + +if (kvm_iodevice_read (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_read (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); +return 0 ; +} + +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ +struct kvm_memory_slot *slot; +int size, write, r; +unsigned long val; +gpa_t gpa; + +KVM_BUG_ON (vcpu->mmio_needed, vcpu->kvm); +[...]
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1310
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+ +/* Request the device emulation to userspace device model. */ + vcpu->mmio_needed =1 ; + vcpu->mmio_is_write = write;Then they can be dropped.
We may drop mmio_needed. mmio_is_write is needed as above.
Quoted 5 lines
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ vcpu->arch.complete_userspace_io = tdx_complete_mmio; + + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write;
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1322
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +7
-0+ vcpu->run->exit_reason =KVM_EXIT_MMIO ; + +if (write) { +memcpy (vcpu->run->mmio.data, &val, size); + }else { + vcpu->mmio_fragments[0 ].gpa = gpa; + vcpu->mmio_fragments[0 ].len = size;These two lines can be dropped as well.
ditto.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 71 lines · Isaku Yamahata
Quoted 31 lines · Binbin Wu
Quoted 29 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Sean Christopherson <[email protected]>
Export
kvm_io_bus_readandkvm_mmiotracepoint and wire up TDX PV MMIO hypercall to the KVM backend functions.kvm_io_bus_read/write() searches KVM device emulated in kernel of the given MMIO address and emulates the MMIO. As TDX PV MMIO also needs it, export
kvm_io_bus_read().kvm_io_bus_write()is already exported. TDX PV MMIO emulates some of MMIO itself. To add trace point consistently with x86 kvm, exportkvm_mmiotracepoint.Signed-off-by Sean Christopherson <[email protected]>Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>--- arch/x86/kvm/vmx/tdx.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/x86.c | 1 + virt/kvm/kvm_main.c | 2 + 3 files changed, 117 insertions(+)arch/x86/kvm/vmx/tdx.c +2
-0@@ -1217,6 +1217,118 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu)return ret; } +static int tdx_complete_mmio (struct kvm_vcpu *vcpu) +{Quoted 8 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1227
Quoted 6 lines
arch/x86/kvm/vmx/tdx.c +6
-0+unsigned long val =0 ; +gpa_t gpa; +int size; + +KVM_BUG_ON (vcpu->mmio_needed !=1 , vcpu->kvm); + vcpu->mmio_needed =0 ;
mmio_neededis used by instruction emulator to setup the complete callback. Since TDX handle MMIO in a PV way,mmio_neededis not needed here.Ok, we don't need to update
mmio_needed.Quoted 4 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1228
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ +if (!vcpu->mmio_is_write) {It's also needed by instruction emulator, we can use
vcpu->run->mmio.is_writeinstead.No because
vcpu->run->mmiois shared with user space. KVM need to stash it independently.Quoted 5 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1231
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+ gpa = vcpu->mmio_fragments[0 ].gpa; + size = vcpu->mmio_fragments[0 ].len;Since MMIO cross page boundary is not allowed according to the input checks from TDVMCALL, these mmio_fragments[] is not needed.
Just usevcpu->run->mmio.phys_addrandvcpu->run->mmio.len?ditto.
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1236
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +memcpy (&val, vcpu->run->mmio.data, size); +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); + }Tracepoint for
KVM_TRACE_MMIO_WRITEis missing when it is handled in userspace.
tdx_mmio_write()has it before existing to the user space. It matches with howwrite_mmio()behaves inx86.c.
Quoted 8 lines
Hmm, to match with other code, we should remove
trace_kvm_mmio(KVM_TRACE_MMIO_READ)and keepKVM_TRACE_MMIO_READ_UNSATISFIEDintdx_emulate_mmio(). That's howread_prepare()andread_exit_mmio()behaves.For MMIO read
- When kernel can handle the MMIO,KVM_TRACE_MMIO_READwith data.
- When exiting to the user space,KVM_TRACE_MMIO_READ_UNSATISFIEDbefore the exit. No trace after the user space handled the MMIO.
For MMIO read, in the emulator, there is still a trace after the userspace handled the MMIO. In complete_emulated_mmio(), if all fragments have been handled, it will
set vcpu->mmio_read_completed to 1 and call complete_emulated_io(). complete_emulated_io
kvm_emulate_instruction(vcpu, EMULTYPE_NO_DECODE)
x86_emulate_instruction
x86_emulate_insn
emulator_read_write
read_prepare
At this point, vcpu->mmio_read_completed is 1,
it traces KVM_TRACE_MMIO_READ with data
and then clear vcpu->mmio_read_completed
So to align with emulator, we should keep the trace for KVM_TRACE_MMIO_READ.
Quoted 83 lines
For MMIO write
-KVM_TRACE_MMIO_WRITEbefore handling it.Quoted 55 lines
Also, the return code is only set when the emulation is done in kernel, but not set when it's handled in userspace.
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1237
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +2
-0+return 1 ; +}How about the fixup as following:
(quoted diff) +0
-1@@ -1173,19 +1173,18 @@ static int tdx_emulate_io(struct kvm_vcpu *vcpu) static int tdx_complete_mmio(struct kvm_vcpu *vcpu) { unsigned long val = 0; - gpa_t gpa; - int size; - - vcpu->mmio_needed = 0; - - if(!vcpu->mmio_is_write) { - gpa = vcpu->mmio_fragments[0].gpa; - size = vcpu->mmio_fragments[0].len; +
gpa_tgpa =vcpu->run->mmio.phys_addr; + int size =vcpu->run->mmio.len; + if (vcpu->run->mmio.is_write) { +trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, &val); + } else {memcpy(&val, vcpu->run->mmio.data, size);tdvmcall_set_return_val(vcpu, val);trace_kvm_mmio(KVM_TRACE_MMIO_READ, size, gpa, &val); } + +tdvmcall_set_return_code(vcpu, TDVMCALL_SUCCESS); return 1; }Quoted 34 lines
arch/x86/kvm/vmx/tdx.c +34
-0+ +static inline int tdx_mmio_write (struct kvm_vcpu *vcpu,gpa_t gpa,int size, +unsigned long val) +{ +if (kvm_iodevice_write (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_write (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +trace_kvm_mmio (KVM_TRACE_MMIO_WRITE , size, gpa, &val); +return 0 ; +} + +static inline int tdx_mmio_read (struct kvm_vcpu *vcpu,gpa_t gpa,int size) +{ +unsigned long val; + +if (kvm_iodevice_read (vcpu, &vcpu->arch.apic->dev, gpa, size, &val) && +kvm_io_bus_read (vcpu,KVM_MMIO_BUS , gpa, size, &val)) +return -EOPNOTSUPP ; + +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); +return 0 ; +} + +static int tdx_emulate_mmio (struct kvm_vcpu *vcpu) +{ +struct kvm_memory_slot *slot; +int size, write, r; +unsigned long val; +gpa_t gpa; + +KVM_BUG_ON (vcpu->mmio_needed, vcpu->kvm); +[...]
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1310
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+ +/* Request the device emulation to userspace device model. */ + vcpu->mmio_needed =1 ; + vcpu->mmio_is_write = write;Then they can be dropped.
We may drop
mmio_needed.mmio_is_writeis needed as above.Quoted 5 lines
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ vcpu->arch.complete_userspace_io = tdx_complete_mmio; + + vcpu->run->mmio.phys_addr = gpa; + vcpu->run->mmio.len = size; + vcpu->run->mmio.is_write = write;Quoted 8 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1322
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c +7
-0+ vcpu->run->exit_reason =KVM_EXIT_MMIO ; + +if (write) { +memcpy (vcpu->run->mmio.data, &val, size); + }else { + vcpu->mmio_fragments[0 ].gpa = gpa; + vcpu->mmio_fragments[0 ].len = size;These two lines can be dropped as well.
ditto.
Re: [PATCH v19 110/130] KVM: TDX: Handle TDX PV MMIO hypercall
Quoted 33 lines · Binbin Wu
Quoted 10 lines
Quoted 7 lines
arch/x86/kvm/vmx/tdx.c static int tdx_emulate_io(struct kvm_vcpu *vcpu) · line 1236
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+ +memcpy (&val, vcpu->run->mmio.data, size); +tdvmcall_set_return_val (vcpu, val); +trace_kvm_mmio (KVM_TRACE_MMIO_READ , size, gpa, &val); + }Tracepoint for
KVM_TRACE_MMIO_WRITEis missing when it is handled in userspace.
tdx_mmio_write()has it before existing to the user space. It matches with howwrite_mmio()behaves inx86.c.Quoted 8 lines
Hmm, to match with other code, we should remove
trace_kvm_mmio(KVM_TRACE_MMIO_READ)and keepKVM_TRACE_MMIO_READ_UNSATISFIEDintdx_emulate_mmio(). That's howread_prepare()andread_exit_mmio()behaves.For MMIO read
- When kernel can handle the MMIO,KVM_TRACE_MMIO_READwith data.
- When exiting to the user space,KVM_TRACE_MMIO_READ_UNSATISFIEDbefore the exit. No trace after the user space handled the MMIO.For MMIO read, in the emulator, there is still a trace after the userspace handled the MMIO.
Incomplete_emulated_mmio(), if all fragments have been handled, it will setvcpu->mmio_read_completedto 1 and callcomplete_emulated_io().complete_emulated_io
kvm_emulate_instruction(vcpu, EMULTYPE_NO_DECODE)x86_emulate_instruction
x86_emulate_insn
emulator_read_write
read_prepare
At this point,vcpu->mmio_read_completedis 1, it tracesKVM_TRACE_MMIO_READwith data and then clearvcpu->mmio_read_completed
Quoted 1 line
So to align with emulator, we should keep the trace for
KVM_TRACE_MMIO_READ.
Oops, you're right. Agreed to keep it.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 111/130 KVM: TDX: Implement callbacks for MSR operations for TDX
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Implements set_msr/get_msr/has_emulated_msr methods for TDX to handle hypercall from guest TD for paravirtualized rdmsr and wrmsr. The TDX module virtualizes MSRs. For some MSRs, it injects #VE to the guest TD upon RDMSR or WRMSR. The exact list of such MSRs are defined in the spec.
Upon #VE, the guest TD may execute hypercalls, TDG.VP.VMCALL<INSTRUCTION.RDMSR> and TDG.VP.VMCALL<INSTRUCTION.WRMSR>, which are defined in GHCI (Guest-Host Communication Interface) so that the host VMM (e.g. KVM) can virtualize the MSRs.
There are three classes of MSRs virtualization.
- non-configurable: TDX module directly virtualizes it. VMM can't configure. the value set by KVM_SET_MSR_INDEX_LIST is ignored.
- configurable: TDX module directly virtualizes it. VMM can configure at the VM creation time. The value set by KVM_SET_MSR_INDEX_LIST is used.
- #VE case
Guest TD would issue TDG.VP.VMCALL<INSTRUCTION.{WRMSR,RDMSR> and VMM handles the MSR hypercall. The value set by KVM_SET_MSR_INDEX_LIST is used.
--- arch/x86/kvm/vmx/main.c | 44 +++++++++++++++++++++--- arch/x86/kvm/vmx/tdx.c | 70 ++++++++++++++++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 6 ++++ arch/x86/kvm/x86.c | 1 - arch/x86/kvm/x86.h | 2 ++ 5 files changed, 118 insertions(+), 5 deletions(-)
arch/x86/kvm/vmx/main.c +40 -4
@@ -247,6 +247,42 @@ static void vt_handle_exit_irqoff(struct kvm_vcpu *vcpu)vmx_handle_exit_irqoff (vcpu); } +static int vt_set_msr (struct kvm_vcpu *vcpu,struct msr_data *msr_info) +{ +if (unlikely (is_td_vcpu (vcpu))) +return tdx_set_msr (vcpu, msr_info); + +return vmx_set_msr (vcpu, msr_info); +} + +/* +* The kvm parameter can be NULL (module initialization, or invocation before +* VM creation). Be sure to check the kvm parameter before using it. +*/ +static bool vt_has_emulated_msr (struct kvm *kvm,u32 index) +{ +if (kvm &&is_td (kvm)) +return tdx_has_emulated_msr (index, true); + +return vmx_has_emulated_msr (kvm, index); +} + +static int vt_get_msr (struct kvm_vcpu *vcpu,struct msr_data *msr_info) +{ +if (unlikely (is_td_vcpu (vcpu))) +return tdx_get_msr (vcpu, msr_info); + +return vmx_get_msr (vcpu, msr_info); +} + +static void vt_msr_filter_changed (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_msr_filter_changed (vcpu); +} +static void vt_apicv_pre_state_restore (struct kvm_vcpu *vcpu) {struct pi_desc *pi =vcpu_to_pi_desc (vcpu);
@@ -541,7 +577,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {/* TDX cpu enablement is done by tdx_hardware_setup(). */ .hardware_enable = vmx_hardware_enable, .hardware_disable = vt_hardware_disable, - .has_emulated_msr = vmx_has_emulated_msr, + .has_emulated_msr = vt_has_emulated_msr, .is_vm_type_supported = vt_is_vm_type_supported, .max_vcpus = vt_max_vcpus,
@@ -563,8 +599,8 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .update_exception_bitmap = vmx_update_exception_bitmap, .get_msr_feature = vmx_get_msr_feature, - .get_msr = vmx_get_msr, - .set_msr = vmx_set_msr, + .get_msr = vt_get_msr, + .set_msr = vt_set_msr, .get_segment_base = vmx_get_segment_base, .get_segment = vmx_get_segment, .set_segment = vmx_set_segment,
@@ -674,7 +710,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .apic_init_signal_blocked = vmx_apic_init_signal_blocked, .migrate_timers = vmx_migrate_timers, - .msr_filter_changed = vmx_msr_filter_changed, + .msr_filter_changed = vt_msr_filter_changed, .complete_emulated_msr = kvm_complete_insn_gp, .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector,
arch/x86/kvm/vmx/tdx.c +70 -0
76 lines not on this page · raw message
arch/x86/kvm/vmx/x86_ops.h +6 -0
@@ -165,6 +165,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,bool write); +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);
@@ -214,6 +217,9 @@ static inline void tdx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mostatic 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,bool write) {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 ; }
arch/x86/kvm/x86.c +0 -1
@@ -90,7 +90,6 @@#include "trace.h" #define MAX_IO_MSRS 256 -#define KVM_MAX_MCE_BANKS 32struct kvm_caps kvm_caps__read_mostly = { .supported_mce_cap =MCG_CTL_P |MCG_SER_P ,
arch/x86/kvm/x86.h +2 -0
@@ -9,6 +9,8 @@#include "kvm_cache_regs.h" #include "kvm_emulate.h" +#define KVM_MAX_MCE_BANKS 32 +bool __kvm_is_vm_type_supported (unsigned long type);struct kvm_caps {
Signature · 1 line
2.25.1
Re: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
Quoted 34 lines · On Mon, Feb 26, 2024 at 12:26:53AM -0800, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +34
-0+bool tdx_has_emulated_msr (u32 index,bool write) +{ +switch (index) { +case MSR_IA32_UCODE_REV : +case MSR_IA32_ARCH_CAPABILITIES : +case MSR_IA32_POWER_CTL : +case MSR_IA32_CR_PAT : +case MSR_IA32_TSC_DEADLINE : +case MSR_IA32_MISC_ENABLE : +case MSR_PLATFORM_INFO : +case MSR_MISC_FEATURES_ENABLES : +case MSR_IA32_MCG_CAP : +case MSR_IA32_MCG_STATUS : +case MSR_IA32_MCG_CTL : +case MSR_IA32_MCG_EXT_CTL : + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: +/* MSR_IA32_MCx_{CTL, STATUS, ADDR, MISC, CTL2} */ +return true; + case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: +/* +* x2APIC registers that are virtualized by the CPU can't be +* emulated, KVM doesn't have access to the virtual APIC page. +*/ +switch (index) { +case X2APIC_MSR (APIC_TASKPRI ): +case X2APIC_MSR (APIC_PROCPRI ): +case X2APIC_MSR (APIC_EOI ): + case X2APIC_MSR(APIC_ISR) ... X2APIC_MSR(APIC_ISR + APIC_ISR_NR): + case X2APIC_MSR(APIC_TMR) ... X2APIC_MSR(APIC_TMR + APIC_ISR_NR): + case X2APIC_MSR(APIC_IRR) ... X2APIC_MSR(APIC_IRR + APIC_ISR_NR): +return false; +default : +return true;
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1933
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ } +case MSR_IA32_APICBASE : +case MSR_EFER : +return !write; + case 0x4b564d00 ... 0x4b564dff: +/* KVM custom MSRs */ +return tdx_is_emulated_kvm_msr (index, write); +default : +return false; + }
The only call site with a non-Null KVM parameter is:
r = static_call(kvm_x86_has_emulated_msr)(kvm, MSR_IA32_SMBASE);
Only MSR_IA32_SMBASE needs to be handled. So, this function is much more complicated than it should be.
Sean Christopherson
maintainerRe: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
Quoted 48 lines · Chao Gao
Quoted 34 lines · On Mon, Feb 26, 2024 at 12:26:53AM -0800, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +34
-0+bool tdx_has_emulated_msr (u32 index,bool write) +{ +switch (index) { +case MSR_IA32_UCODE_REV : +case MSR_IA32_ARCH_CAPABILITIES : +case MSR_IA32_POWER_CTL : +case MSR_IA32_CR_PAT : +case MSR_IA32_TSC_DEADLINE : +case MSR_IA32_MISC_ENABLE : +case MSR_PLATFORM_INFO : +case MSR_MISC_FEATURES_ENABLES : +case MSR_IA32_MCG_CAP : +case MSR_IA32_MCG_STATUS : +case MSR_IA32_MCG_CTL : +case MSR_IA32_MCG_EXT_CTL : + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: +/* MSR_IA32_MCx_{CTL, STATUS, ADDR, MISC, CTL2} */ +return true; + case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: +/* +* x2APIC registers that are virtualized by the CPU can't be +* emulated, KVM doesn't have access to the virtual APIC page. +*/ +switch (index) { +case X2APIC_MSR (APIC_TASKPRI ): +case X2APIC_MSR (APIC_PROCPRI ): +case X2APIC_MSR (APIC_EOI ): + case X2APIC_MSR(APIC_ISR) ... X2APIC_MSR(APIC_ISR + APIC_ISR_NR): + case X2APIC_MSR(APIC_TMR) ... X2APIC_MSR(APIC_TMR + APIC_ISR_NR): + case X2APIC_MSR(APIC_IRR) ... X2APIC_MSR(APIC_IRR + APIC_ISR_NR): +return false; +default : +return true;arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1933
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ } +case MSR_IA32_APICBASE : +case MSR_EFER : +return !write; + case 0x4b564d00 ... 0x4b564dff: +/* KVM custom MSRs */ +return tdx_is_emulated_kvm_msr (index, write); +default : +return false; + }The only call site with a non-Null KVM parameter is:
Quoted 4 lines
r = static_call(kvm_x86_has_emulated_msr)(kvm, MSR_IA32_SMBASE);Only
MSR_IA32_SMBASEneeds to be handled. So, this function is much more complicated than it should be.
No, because it's also used by tdx_{g,s}et_msr().
Sean Christopherson
maintainerRe: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
In reply to [email protected]Quoted 46 lines · On Mon, Feb 26, 2024, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +38
-0@@ -1877,6 +1877,76 @@ void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, *error_code =0 ; } +static bool tdx_is_emulated_kvm_msr (u32 index,bool write) +{ +switch (index) { +case MSR_KVM_POLL_CONTROL : +return true; +default : +return false; + } +} + +bool tdx_has_emulated_msr (u32 index,bool write) +{ +switch (index) { +case MSR_IA32_UCODE_REV : +case MSR_IA32_ARCH_CAPABILITIES : +case MSR_IA32_POWER_CTL : +case MSR_IA32_CR_PAT : +case MSR_IA32_TSC_DEADLINE : +case MSR_IA32_MISC_ENABLE : +case MSR_PLATFORM_INFO : +case MSR_MISC_FEATURES_ENABLES : +case MSR_IA32_MCG_CAP : +case MSR_IA32_MCG_STATUS : +case MSR_IA32_MCG_CTL : +case MSR_IA32_MCG_EXT_CTL : + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: +/* MSR_IA32_MCx_{CTL, STATUS, ADDR, MISC, CTL2} */ +return true; + case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: +/* +* x2APIC registers that are virtualized by the CPU can't be +* emulated, KVM doesn't have access to the virtual APIC page. +*/ +switch (index) { +case X2APIC_MSR (APIC_TASKPRI ): +case X2APIC_MSR (APIC_PROCPRI ): +case X2APIC_MSR (APIC_EOI ):
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1927
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ case X2APIC_MSR(APIC_ISR) ... X2APIC_MSR(APIC_ISR + APIC_ISR_NR): + case X2APIC_MSR(APIC_TMR) ... X2APIC_MSR(APIC_TMR + APIC_ISR_NR): + case X2APIC_MSR(APIC_IRR) ... X2APIC_MSR(APIC_IRR + APIC_ISR_NR): +return false; +default : +return true; + } +case MSR_IA32_APICBASE : +case MSR_EFER : +return !write;
Meh, for literally two MSRs, just open code them in tdx_set_msr() and drop the @write param. Or alternatively add:
static bool tdx_is_read_only_msr(u32 msr){
{
return msr == MSR_IA32_APICBASE || msr == MSR_EFER;
}arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1928
Quoted 1 line
arch/x86/kvm/vmx/tdx.c +1
-0+ case 0x4b564d00 ... 0x4b564dff:
This is silly, just do
case MSR_KVM_POLL_CONTROL: return false;
and let everything else go through the default statement, no?
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1940
Quoted 12 lines
arch/x86/kvm/vmx/tdx.c +12
-0+/* KVM custom MSRs */ +return tdx_is_emulated_kvm_msr (index, write); +default : +return false; + } +} + +int tdx_get_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) +{ +if (tdx_has_emulated_msr (msr->index, false)) +return kvm_get_msr_common (vcpu, msr); +return 1 ;
Please invert these and make the happy path the not-taken path, i.e.
if (!tdx_has_emulated_msr(msr->index)) return 1; return kvm_get_msr_common(vcpu, msr);
The standard kernel pattern is
if (error)
return <error thingie>
return <happy thingie>
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1945
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+} + +int tdx_set_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) +{ +if (tdx_has_emulated_msr (msr->index, true))
As above:
if (tdx_is_read_only_msr(msr->index)) return 1; if (!tdx_has_emulated_msr(msr->index)) return 1; return kvm_set_msr_common(vcpu, msr);
Quoted 17 lines
arch/x86/kvm/x86.c +0
-1@@ -90,7 +90,6 @@#include "trace.h" #define MAX_IO_MSRS 256 -#define KVM_MAX_MCE_BANKS 32struct kvm_caps kvm_caps__read_mostly = { .supported_mce_cap =MCG_CTL_P |MCG_SER_P ,arch/x86/kvm/x86.h +0
-0
arch/x86/kvm/x86.h line 12
Quoted 4 lines
arch/x86/kvm/x86.h +1
-0@@ -9,6 +9,8 @@#include "kvm_cache_regs.h" #include "kvm_emulate.h" +#define KVM_MAX_MCE_BANKS 32
Split this to a separate. Yes, it's trivial, but that's _exactly_ why it should be in a separate patch. The more trivial refactoring you split out, the more we can apply _now_ and take off your hands.
Re: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
Quoted 61 lines · Sean Christopherson
Quoted 46 lines · On Mon, Feb 26, 2024, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +38
-0@@ -1877,6 +1877,76 @@ void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, *error_code =0 ; } +static bool tdx_is_emulated_kvm_msr (u32 index,bool write) +{ +switch (index) { +case MSR_KVM_POLL_CONTROL : +return true; +default : +return false; + } +} + +bool tdx_has_emulated_msr (u32 index,bool write) +{ +switch (index) { +case MSR_IA32_UCODE_REV : +case MSR_IA32_ARCH_CAPABILITIES : +case MSR_IA32_POWER_CTL : +case MSR_IA32_CR_PAT : +case MSR_IA32_TSC_DEADLINE : +case MSR_IA32_MISC_ENABLE : +case MSR_PLATFORM_INFO : +case MSR_MISC_FEATURES_ENABLES : +case MSR_IA32_MCG_CAP : +case MSR_IA32_MCG_STATUS : +case MSR_IA32_MCG_CTL : +case MSR_IA32_MCG_EXT_CTL : + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: +/* MSR_IA32_MCx_{CTL, STATUS, ADDR, MISC, CTL2} */ +return true; + case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: +/* +* x2APIC registers that are virtualized by the CPU can't be +* emulated, KVM doesn't have access to the virtual APIC page. +*/ +switch (index) { +case X2APIC_MSR (APIC_TASKPRI ): +case X2APIC_MSR (APIC_PROCPRI ): +case X2APIC_MSR (APIC_EOI ):arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1927
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ case X2APIC_MSR(APIC_ISR) ... X2APIC_MSR(APIC_ISR + APIC_ISR_NR): + case X2APIC_MSR(APIC_TMR) ... X2APIC_MSR(APIC_TMR + APIC_ISR_NR): + case X2APIC_MSR(APIC_IRR) ... X2APIC_MSR(APIC_IRR + APIC_ISR_NR): +return false; +default : +return true; + } +case MSR_IA32_APICBASE : +case MSR_EFER : +return !write;Meh, for literally two MSRs, just open code them in
tdx_set_msr()and drop the @write param. Or alternatively add:
Quoted 4 lines
static bool
tdx_is_read_only_msr(u32 msr){{ return msr == MSR_IA32_APICBASE || msr == MSR_EFER; }
Sure will add.
Quoted 9 lines
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1928
Quoted 2 lines
arch/x86/kvm/vmx/tdx.c +1
-0+ case 0x4b564d00 ... 0x4b564dff:This is silly, just do
case MSR_KVM_POLL_CONTROL: return false;and let everything else go through the default statement, no?
Now tdx_is_emulated_kvm_msr() is trivial, will open code it.
Quoted 35 lines
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1940
Quoted 12 lines
arch/x86/kvm/vmx/tdx.c +12
-0+/* KVM custom MSRs */ +return tdx_is_emulated_kvm_msr (index, write); +default : +return false; + } +} + +int tdx_get_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) +{ +if (tdx_has_emulated_msr (msr->index, false)) +return kvm_get_msr_common (vcpu, msr); +return 1 ;Please invert these and make the happy path the not-taken path, i.e.
if (!tdx_has_emulated_msr(msr->index)) return 1; return kvm_get_msr_common(vcpu, msr);The standard kernel pattern is
if (error)
return <error thingie>return <happy thingie>
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1945
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c +5
-0+} + +int tdx_set_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) +{ +if (tdx_has_emulated_msr (msr->index, true))As above:
Quoted 7 lines
if (tdx_is_read_only_msr(msr->index)) return 1; if (!tdx_has_emulated_msr(msr->index)) return 1; return kvm_set_msr_common(vcpu, msr);
Sure, will update them.
Quoted 20 lines
Quoted 20 lines
arch/x86/kvm/x86.c +0
-1@@ -90,7 +90,6 @@#include "trace.h" #define MAX_IO_MSRS 256 -#define KVM_MAX_MCE_BANKS 32struct kvm_caps kvm_caps__read_mostly = { .supported_mce_cap =MCG_CTL_P |MCG_SER_P ,arch/x86/kvm/x86.h +0
-0@@ -9,6 +9,8 @@#include "kvm_cache_regs.h" #include "kvm_emulate.h"
Quoted 5 lines
arch/x86/kvm/x86.h line 12
Quoted 1 line
arch/x86/kvm/x86.h +1
-0+#define KVM_MAX_MCE_BANKS 32Split this to a separate. Yes, it's trivial, but that's _exactly_ why it should be in a separate patch. The more trivial refactoring you split out, the more we can apply _now_ and take off your hands.
Will split it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
Quoted 63 lines · Isaku Yamahata
Quoted 60 lines · Sean Christopherson
Quoted 46 lines · On Mon, Feb 26, 2024, [email protected] wrote:
arch/x86/kvm/vmx/tdx.c +38
-0@@ -1877,6 +1877,76 @@ void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, *error_code =0 ; } +static bool tdx_is_emulated_kvm_msr (u32 index,bool write) +{ +switch (index) { +case MSR_KVM_POLL_CONTROL : +return true; +default : +return false; + } +} + +bool tdx_has_emulated_msr (u32 index,bool write) +{ +switch (index) { +case MSR_IA32_UCODE_REV : +case MSR_IA32_ARCH_CAPABILITIES : +case MSR_IA32_POWER_CTL : +case MSR_IA32_CR_PAT : +case MSR_IA32_TSC_DEADLINE : +case MSR_IA32_MISC_ENABLE : +case MSR_PLATFORM_INFO : +case MSR_MISC_FEATURES_ENABLES : +case MSR_IA32_MCG_CAP : +case MSR_IA32_MCG_STATUS : +case MSR_IA32_MCG_CTL : +case MSR_IA32_MCG_EXT_CTL : + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: +/* MSR_IA32_MCx_{CTL, STATUS, ADDR, MISC, CTL2} */ +return true; + case APIC_BASE_MSR ... APIC_BASE_MSR + 0xff: +/* +* x2APIC registers that are virtualized by the CPU can't be +* emulated, KVM doesn't have access to the virtual APIC page. +*/ +switch (index) { +case X2APIC_MSR (APIC_TASKPRI ): +case X2APIC_MSR (APIC_PROCPRI ): +case X2APIC_MSR (APIC_EOI ):arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1927
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+ case X2APIC_MSR(APIC_ISR) ... X2APIC_MSR(APIC_ISR + APIC_ISR_NR): + case X2APIC_MSR(APIC_TMR) ... X2APIC_MSR(APIC_TMR + APIC_ISR_NR): + case X2APIC_MSR(APIC_IRR) ... X2APIC_MSR(APIC_IRR + APIC_ISR_NR): +return false; +default : +return true; + } +case MSR_IA32_APICBASE : +case MSR_EFER : +return !write;Meh, for literally two MSRs, just open code them in
tdx_set_msr()and drop the @write param. Or alternatively add:
Quoted 11 lines
Quoted 4 lines
static bool
tdx_is_read_only_msr(u32 msr){{ return msr == MSR_IA32_APICBASE || msr == MSR_EFER; }Sure will add.
Quoted 5 lines
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1928
Quoted 1 line
arch/x86/kvm/vmx/tdx.c +1
-0+ case 0x4b564d00 ... 0x4b564dff:This is silly, just do
case MSR_KVM_POLL_CONTROL: return false;
Shoud return true here, right?
Quoted 13 lines
Quoted 2 lines
and let everything else go through the default statement, no?
Now
tdx_is_emulated_kvm_msr()is trivial, will open code it.Quoted 8 lines
Quoted 8 lines
arch/x86/kvm/vmx/tdx.c +7
-0+/* KVM custom MSRs */ +return tdx_is_emulated_kvm_msr (index, write); +default : +return false; + } +} +
Re: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
In reply to [email protected]Quoted 7 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Implements set_msr/get_msr/has_emulated_msr methods for TDX to handle hypercall from guest TD for paravirtualized rdmsr and wrmsr. The TDX module virtualizes MSRs. For some MSRs, it injects #VE to the guest TD upon RDMSR or WRMSR. The exact list of such MSRs are defined in the spec.
Quoted 8 lines
Upon #VE, the guest TD may execute hypercalls, TDG.VP.VMCALL<INSTRUCTION.RDMSR> and TDG.VP.VMCALL<INSTRUCTION.WRMSR>, which are defined in GHCI (Guest-Host Communication Interface) so that the host VMM (e.g. KVM) can virtualize the MSRs.
There are three classes of MSRs virtualization.
- non-configurable: TDX module directly virtualizes it. VMM can't configure. the value set byKVM_SET_MSR_INDEX_LISTis ignored.
There is no KVM_SET_MSR_INDEX_LIST in current kvm code. Do you mean KVM_SET_MSRS?
Quoted 7 lines
- configurable: TDX module directly virtualizes it. VMM can configure at the VM creation time. The value set by
KVM_SET_MSR_INDEX_LISTis used.
- #VE case
Guest TD would issue TDG.VP.VMCALL<INSTRUCTION.{WRMSR,RDMSR> and VMM handles the MSR hypercall. The value set byKVM_SET_MSR_INDEX_LISTis used.
Re: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
Quoted 15 lines · Binbin Wu
Quoted 12 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Implements set_msr/get_msr/has_emulated_msr methods for TDX to handle hypercall from guest TD for paravirtualized rdmsr and wrmsr. The TDX module virtualizes MSRs. For some MSRs, it injects #VE to the guest TD upon RDMSR or WRMSR. The exact list of such MSRs are defined in the spec.
Upon #VE, the guest TD may execute hypercalls, TDG.VP.VMCALL<INSTRUCTION.RDMSR> and TDG.VP.VMCALL<INSTRUCTION.WRMSR>, which are defined in GHCI (Guest-Host Communication Interface) so that the host VMM (e.g. KVM) can virtualize the MSRs.
Quoted 6 lines
Quoted 3 lines
There are three classes of MSRs virtualization.
- non-configurable: TDX module directly virtualizes it. VMM can't configure. the value set byKVM_SET_MSR_INDEX_LISTis ignored.There is no
KVM_SET_MSR_INDEX_LISTin current kvm code. Do you meanKVM_SET_MSRS?
Yes, will fix it. Thank you for catching it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 111/130] KVM: TDX: Implement callbacks for MSR operations for TDX
In reply to [email protected]Quoted 25 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
Implements set_msr/get_msr/has_emulated_msr methods for TDX to handle hypercall from guest TD for paravirtualized rdmsr and wrmsr. The TDX module virtualizes MSRs. For some MSRs, it injects #VE to the guest TD upon RDMSR or WRMSR. The exact list of such MSRs are defined in the spec.
Upon #VE, the guest TD may execute hypercalls, TDG.VP.VMCALL<INSTRUCTION.RDMSR> and TDG.VP.VMCALL<INSTRUCTION.WRMSR>, which are defined in GHCI (Guest-Host Communication Interface) so that the host VMM (e.g. KVM) can virtualize the MSRs.
There are three classes of MSRs virtualization.
- non-configurable: TDX module directly virtualizes it. VMM can't configure. the value set byKVM_SET_MSR_INDEX_LISTis ignored.
- configurable: TDX module directly virtualizes it. VMM can configure at the VM creation time. The value set byKVM_SET_MSR_INDEX_LISTis used.
- #VE case
Guest TD would issue TDG.VP.VMCALL<INSTRUCTION.{WRMSR,RDMSR> and VMM handles the MSR hypercall. The value set byKVM_SET_MSR_INDEX_LISTis used.Signed-off-by Isaku Yamahata <[email protected]>Reviewed-by Paolo Bonzini <[email protected]>---
[...]
Quoted 4 lines
arch/x86/kvm/vmx/tdx.c +4
-0+ +bool tdx_has_emulated_msr (u32 index,bool write) +{ +switch (index) {
arch/x86/kvm/vmx/tdx.c void tdx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason, · line 1902
Quoted 10 lines
arch/x86/kvm/vmx/tdx.c +10
-0+case MSR_IA32_UCODE_REV : +case MSR_IA32_ARCH_CAPABILITIES : +case MSR_IA32_POWER_CTL : +case MSR_IA32_CR_PAT : +case MSR_IA32_TSC_DEADLINE : +case MSR_IA32_MISC_ENABLE : +case MSR_PLATFORM_INFO : +case MSR_MISC_FEATURES_ENABLES : +case MSR_IA32_MCG_CAP : +case MSR_IA32_MCG_STATUS :
It not about this patch directly.
Intel SDM says:
"An attempt to write to IA32_MCG_STATUS with any value other than 0 would result in #GP".
But in set_msr_mce(), IA32_MCG_STATUS is set without any check. Should it be checked against 0 if it is not host_initiated?
PATCH v19 112/130 KVM: TDX: Handle TDX PV rdmsr/wrmsr hypercall
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Wire up TDX PV rdmsr/wrmsr hypercall to the KVM backend function.
--- arch/x86/kvm/vmx/tdx.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
arch/x86/kvm/vmx/tdx.c +39 -0
@@ -1329,6 +1329,41 @@ static int tdx_emulate_mmio(struct kvm_vcpu *vcpu)return 1 ; } +static int tdx_emulate_rdmsr (struct kvm_vcpu *vcpu) +{ +u32 index =tdvmcall_a0_read (vcpu); +u64 data; + +if (!kvm_msr_allowed (vcpu, index,KVM_MSR_FILTER_READ ) || +kvm_get_msr (vcpu, index, &data)) { +trace_kvm_msr_read_ex (index); +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } +trace_kvm_msr_read (index, data); + +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +tdvmcall_set_return_val (vcpu, data); +return 1 ; +} + +static int tdx_emulate_wrmsr (struct kvm_vcpu *vcpu) +{ +u32 index =tdvmcall_a0_read (vcpu); +u64 data =tdvmcall_a1_read (vcpu); + +if (!kvm_msr_allowed (vcpu, index,KVM_MSR_FILTER_WRITE ) || +kvm_set_msr (vcpu, index, data)) { +trace_kvm_msr_write_ex (index, data); +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +return 1 ; + } + +trace_kvm_msr_write (index, data); +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +return 1 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))
@@ -1343,6 +1378,10 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)return tdx_emulate_io (vcpu);case EXIT_REASON_EPT_VIOLATION :return tdx_emulate_mmio (vcpu); +case EXIT_REASON_MSR_READ : +return tdx_emulate_rdmsr (vcpu); +case EXIT_REASON_MSR_WRITE : +return tdx_emulate_wrmsr (vcpu);default :break ; }
Signature · 1 line
2.25.1
PATCH v19 113/130 KVM: TDX: Handle MSR MTRRCap and MTRRDefType access
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Handle MTRRCap RO MSR to return all features are unsupported and handle MTRRDefType MSR to accept only E=1,FE=0,type=writeback. enable MTRR, disable Fixed range MTRRs, default memory type=writeback
TDX virtualizes that cpuid to report MTRR to guest TD and TDX enforces guest CR0.CD=0. If guest tries to set CR0.CD=1, it results in #GP. While updating MTRR requires to set CR0.CD=1 (and other cache flushing operations). It means guest TD can't update MTRR. Virtualize MTRR as all features disabled and default memory type as writeback.
--- arch/x86/kvm/vmx/tdx.c | 99 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 17 deletions(-)
arch/x86/kvm/vmx/tdx.c +82 -17
@@ -611,18 +611,7 @@ u8 tdx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)if (!kvm_arch_has_noncoherent_dma (vcpu->kvm))return (MTRR_TYPE_WRBACK <<VMX_EPT_MT_EPTE_SHIFT ) |VMX_EPT_IPAT_BIT ; -/* -* TDX enforces CR0.CD = 0 and KVM MTRR emulation enforces writeback. -* TODO: implement MTRR MSR emulation so that -* MTRRCap: SMRR=0: SMRR interface unsupported -* WC=0: write combining unsupported -* FIX=0: Fixed range registers unsupported -* VCNT=0: number of variable range regitsers = 0 -* MTRRDefType: E=1, FE=0, type=writeback only. Don't allow other value. -* E=1: enable MTRR -* FE=0: disable fixed range MTRRs -* type: default memory type=writeback -*/ +/* TDX enforces CR0.CD = 0 and KVM MTRR emulation enforces writeback. */ return MTRR_TYPE_WRBACK <<VMX_EPT_MT_EPTE_SHIFT ; }
@@ -1932,7 +1921,9 @@ bool tdx_has_emulated_msr(u32 index, bool write)case MSR_IA32_UCODE_REV :case MSR_IA32_ARCH_CAPABILITIES :case MSR_IA32_POWER_CTL : +case MSR_MTRRcap:case MSR_IA32_CR_PAT : +case MSR_MTRRdefType:case MSR_IA32_TSC_DEADLINE :case MSR_IA32_MISC_ENABLE :case MSR_PLATFORM_INFO :
@@ -1974,16 +1965,47 @@ bool tdx_has_emulated_msr(u32 index, bool write)int tdx_get_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) { -if (tdx_has_emulated_msr (msr->index, false)) -return kvm_get_msr_common (vcpu, msr); -return 1 ; +switch (msr->index) { +case MSR_MTRRcap: +/* +* Override kvm_mtrr_get_msr() which hardcodes the value. +* Report SMRR = 0, WC = 0, FIX = 0 VCNT = 0 to disable MTRR +* effectively. +*/ + msr->data =0 ; +return 0 ; +default : +if (tdx_has_emulated_msr (msr->index, false)) +return kvm_get_msr_common (vcpu, msr); +return 1 ; + } }int tdx_set_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) { -if (tdx_has_emulated_msr (msr->index, true)) +switch (msr->index) { +case MSR_MTRRdefType: +/* +* Allow writeback only for all memory. +* Because it's reported that fixed range MTRR isn't supported +* and VCNT=0, enforce MTRRDefType.FE = 0 and don't care +* variable range MTRRs. Only default memory type matters. +* +* bit 11 E: MTRR enable/disable +* bit 12 FE: Fixed-range MTRRs enable/disable +* (E, FE) = (1, 1): enable MTRR and Fixed range MTRR +* (E, FE) = (1, 0): enable MTRR, disable Fixed range MTRR +* (E, FE) = (0, *): disable all MTRRs. all physical memory +* is UC +*/ +if (msr->data != ((1 <<11 ) |MTRR_TYPE_WRBACK )) +return 1 ;return kvm_set_msr_common (vcpu, msr); -return 1 ; +default : +if (tdx_has_emulated_msr (msr->index, true)) +return kvm_set_msr_common (vcpu, msr); +return 1 ; + } }static int tdx_get_capabilities (struct kvm_tdx_cmd *cmd)
@@ -2704,6 +2726,45 @@ static int tdx_td_vcpu_init(struct kvm_vcpu *vcpu, u64 vcpu_rcx)return ret; } +static int tdx_vcpu_init_mtrr (struct kvm_vcpu *vcpu) +{ +struct msr_data msr; +int ret; +int i; + +/* +* To avoid confusion with reporting VNCT = 0, explicitly disable +* vaiale-range reisters. +*/ +for (i =0 ; i <KVM_NR_VAR_MTRR ; i++) { +/* phymask */ + msr = (struct msr_data ) { + .host_initiated = true, + .index =0x200 +2 * i +1 , + .data =0 ,/* valid = 0 to disable. */ + }; + ret =kvm_set_msr_common (vcpu, &msr); +if (ret) +return -EINVAL ; + } + +/* Set MTRR to use writeback on reset. */ + msr = (struct msr_data ) { + .host_initiated = true, + .index = MSR_MTRRdefType, +/* +* Set E(enable MTRR)=1, FE(enable fixed range MTRR)=0, default +* type=writeback on reset to avoid UC. Note E=0 means all +* memory is UC. +*/ + .data = (1 <<11 ) |MTRR_TYPE_WRBACK , + }; + ret =kvm_set_msr_common (vcpu, &msr); +if (ret) +return -EINVAL ; +return 0 ; +} +int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp) {struct msr_data apic_base_msr;
@@ -2741,6 +2802,10 @@ int tdx_vcpu_ioctl(struct kvm_vcpu *vcpu, void __user *argp)if (kvm_set_apic_base (vcpu, &apic_base_msr))return -EINVAL ; + ret =tdx_vcpu_init_mtrr (vcpu); +if (ret) +return ret; + ret =tdx_td_vcpu_init (vcpu, (u64 )cmd.data);if (ret)return ret;
Signature · 1 line
2.25.1
PATCH v19 114/130 KVM: TDX: Handle MSR IA32_FEAT_CTL MSR and IA32_MCG_EXT_CTL
In reply to [email protected]From: Isaku Yamahata <[email protected]>
MCE and MCA is advertised via cpuid based on the TDX module spec. Guest kernel can access IA32_FEAT_CTL for checking if LMCE is enabled by platform and IA32_MCG_EXT_CTL to enable LMCE. Make TDX KVM handle them. Otherwise guest MSR access to them with TDG.VP.VMCALL<MSR> on VE results in GP in guest.
Because LMCE is disabled with qemu by default, "-cpu lmce=on" to qemu command line is needed to reproduce it.
--- arch/x86/kvm/vmx/tdx.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
arch/x86/kvm/vmx/tdx.c +20 -0
@@ -1952,6 +1952,7 @@ bool tdx_has_emulated_msr(u32 index, bool write)default :return true; } +case MSR_IA32_FEAT_CTL :case MSR_IA32_APICBASE :case MSR_EFER :return !write;
@@ -1966,6 +1967,20 @@ bool tdx_has_emulated_msr(u32 index, bool write)int tdx_get_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) {switch (msr->index) { +case MSR_IA32_FEAT_CTL : +/* +* MCE and MCA are advertised via cpuid. guest kernel could +* check if LMCE is enabled or not. +*/ + msr->data =FEAT_CTL_LOCKED ; +if (vcpu->arch.mcg_cap &MCG_LMCE_P ) + msr->data |=FEAT_CTL_LMCE_ENABLED ; +return 0 ; +case MSR_IA32_MCG_EXT_CTL : +if (!msr->host_initiated && !(vcpu->arch.mcg_cap &MCG_LMCE_P )) +return 1 ; + msr->data = vcpu->arch.mcg_ext_ctl; +return 0 ;case MSR_MTRRcap:/* * Override kvm_mtrr_get_msr() which hardcodes the value.
@@ -1984,6 +1999,11 @@ int tdx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)int tdx_set_msr (struct kvm_vcpu *vcpu,struct msr_data *msr) {switch (msr->index) { +case MSR_IA32_MCG_EXT_CTL : +if (!msr->host_initiated && !(vcpu->arch.mcg_cap &MCG_LMCE_P )) +return 1 ; + vcpu->arch.mcg_ext_ctl = msr->data; +return 0 ;case MSR_MTRRdefType:/* * Allow writeback only for all memory.
Signature · 1 line
2.25.1
PATCH v19 115/130 KVM: TDX: Handle TDG.VP.VMCALL<GetTdVmCallInfo> hypercall
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Implement TDG.VP.VMCALL<GetTdVmCallInfo> hypercall. If the input value is zero, return success code and zero in output registers.
TDG.VP.VMCALL<GetTdVmCallInfo> hypercall is a subleaf of TDG.VP.VMCALL to enumerate which TDG.VP.VMCALL sub leaves are supported. This hypercall is for future enhancement of the Guest-Host-Communication Interface (GHCI) specification. The GHCI version of 344426-001US defines it to require input R12 to be zero and to return zero in output registers, R11, R12, R13, and R14 so that guest TD enumerates no enhancement.
---
v19:
- rename TDG_VP_VMCALL_GET_TD_VM_CALL_INFO => TDVMCALL_GET_TD_VM_CALL_INFO
--- arch/x86/include/asm/shared/tdx.h | 1 + arch/x86/kvm/vmx/tdx.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+)
arch/x86/include/asm/shared/tdx.h +1 -0
@@ -22,6 +22,7 @@#define TDCS_NOTIFY_ENABLES 0x9100000000000010/* 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
arch/x86/kvm/vmx/tdx.c +16 -0
@@ -1353,6 +1353,20 @@ static int tdx_emulate_wrmsr(struct kvm_vcpu *vcpu)return 1 ; } +static int tdx_get_td_vm_call_info (struct kvm_vcpu *vcpu) +{ +if (tdvmcall_a0_read (vcpu)) +tdvmcall_set_return_code (vcpu,TDVMCALL_INVALID_OPERAND ); +else { +tdvmcall_set_return_code (vcpu,TDVMCALL_SUCCESS ); +kvm_r11_write (vcpu,0 ); +tdvmcall_a0_write (vcpu,0 ); +tdvmcall_a1_write (vcpu,0 ); +tdvmcall_a2_write (vcpu,0 ); + } +return 1 ; +} +static int handle_tdvmcall (struct kvm_vcpu *vcpu) {if (tdvmcall_exit_type (vcpu))
@@ -1371,6 +1385,8 @@ static int handle_tdvmcall(struct kvm_vcpu *vcpu)return tdx_emulate_rdmsr (vcpu);case EXIT_REASON_MSR_WRITE :return tdx_emulate_wrmsr (vcpu); +case TDVMCALL_GET_TD_VM_CALL_INFO : +return tdx_get_td_vm_call_info (vcpu);default :break ; }
Signature · 1 line
2.25.1
PATCH v19 116/130 KVM: TDX: Silently discard SMI request
In reply to [email protected]From: Isaku Yamahata <[email protected]>
TDX doesn't support system-management mode (SMM) and system-management interrupt (SMI) in guest TDs. Because guest state (vcpu state, memory state) is protected, it must go through the TDX module APIs to change guest state, injecting SMI and changing vcpu mode into SMM. The TDX module doesn't provide a way for VMM to inject SMI into guest TD and a way for VMM to switch guest vcpu mode into SMM.
We have two options in KVM when handling SMM or SMI in the guest TD or the device model (e.g. QEMU): 1) silently ignore the request or 2) return a meaningful error.
For simplicity, we implemented the option 1).
--- arch/x86/kvm/smm.h | 7 +++++- arch/x86/kvm/vmx/main.c | 45 ++++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 29 ++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 12 ++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-)
arch/x86/kvm/smm.h +6 -1
@@ -142,7 +142,12 @@ union kvm_smram {static inline int kvm_inject_smi (struct kvm_vcpu *vcpu) { -kvm_make_request (KVM_REQ_SMI , vcpu); +/* +* If SMM isn't supported (e.g. TDX), silently discard SMI request. +* Assume that SMM supported = MSR_IA32_SMBASE supported. +*/ +if (static_call (kvm_x86_has_emulated_msr)(vcpu->kvm,MSR_IA32_SMBASE )) +kvm_make_request (KVM_REQ_SMI , vcpu);return 0 ; }
arch/x86/kvm/vmx/main.c +41 -4
57 lines not on this page · raw message
arch/x86/kvm/vmx/tdx.c +29 -0
@@ -2044,6 +2044,35 @@ int tdx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) } } +#ifdef CONFIG_KVM_SMM +int tdx_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +/* SMI isn't supported for TDX. */ +WARN_ON_ONCE (1 ); +return false; +} + +int tdx_enter_smm (struct kvm_vcpu *vcpu,union kvm_smram *smram) +{ +/* smi_allowed() is always false for TDX as above. */ +WARN_ON_ONCE (1 ); +return 0 ; +} + +int tdx_leave_smm (struct kvm_vcpu *vcpu,const union kvm_smram *smram) +{ +WARN_ON_ONCE (1 ); +return 0 ; +} + +void tdx_enable_smi_window (struct kvm_vcpu *vcpu) +{ +/* SMI isn't supported for TDX. Silently discard SMI request. */ +WARN_ON_ONCE (1 ); + vcpu->arch.smi_pending = false; +} +#endif +static int tdx_get_capabilities (struct kvm_tdx_cmd *cmd) {struct kvm_tdx_capabilities __user *user_caps;
arch/x86/kvm/vmx/x86_ops.h +12 -0
@@ -241,4 +241,16 @@ int tdx_pre_memory_mapping(struct kvm_vcpu *vcpu,void tdx_post_memory_mapping (struct kvm_vcpu *vcpu,struct kvm_memory_mapping *mapping) {}#endif +#if defined(CONFIG_INTEL_TDX_HOST ) && defined(CONFIG_KVM_SMM ) +int tdx_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection); +int tdx_enter_smm (struct kvm_vcpu *vcpu,union kvm_smram *smram); +int tdx_leave_smm (struct kvm_vcpu *vcpu,const union kvm_smram *smram); +void tdx_enable_smi_window (struct kvm_vcpu *vcpu); +#else +static inline int tdx_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) {return false; } +static inline int tdx_enter_smm (struct kvm_vcpu *vcpu,union kvm_smram *smram) {return 0 ; } +static inline int tdx_leave_smm (struct kvm_vcpu *vcpu,const union kvm_smram *smram) {return 0 ; } +static inline void tdx_enable_smi_window (struct kvm_vcpu *vcpu) {} +#endif +#endif /* __KVM_X86_VMX_X86_OPS_H */
Signature · 1 line
2.25.1
Re: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
Quoted 7 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
TDX doesn't support system-management mode (SMM) and system-management interrupt (SMI) in guest TDs. Because guest state (vcpu state, memory state) is protected, it must go through the TDX module APIs to change guest state, injecting SMI and changing vcpu mode into SMM. The TDX module doesn't provide a way for VMM to inject SMI into guest TD and a way for VMM
^ or
Quoted 10 lines
to switch guest vcpu mode into SMM.
We have two options in KVM when handling SMM or SMI in the guest TD or the device model (e.g. QEMU): 1) silently ignore the request or 2) return a meaningful error.
For simplicity, we implemented the option 1).
Signed-off-by Isaku Yamahata <[email protected]>---
[...]
arch/x86/kvm/vmx/main.c static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) · line 315
Quoted 6 lines
arch/x86/kvm/vmx/main.c +6
-0+ +static void vt_enable_smi_window (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +tdx_enable_smi_window (vcpu); +return ;
Can "return tdx_enable_smi_window(vcpu);" directly.
Quoted 7 lines
arch/x86/kvm/vmx/main.c +7
-0+ } + +/* RSM will cause a vmexit anyway. */ +vmx_enable_smi_window (vcpu); +} +#endif +
[...]
arch/x86/kvm/vmx/x86_ops.h int tdx_pre_memory_mapping(struct kvm_vcpu *vcpu, · line 249
Quoted 7 lines
arch/x86/kvm/vmx/x86_ops.h +6
-0+#if defined(CONFIG_INTEL_TDX_HOST ) && defined(CONFIG_KVM_SMM ) +int tdx_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection); +int tdx_enter_smm (struct kvm_vcpu *vcpu,union kvm_smram *smram); +int tdx_leave_smm (struct kvm_vcpu *vcpu,const union kvm_smram *smram); +void tdx_enable_smi_window (struct kvm_vcpu *vcpu); +#else
#elif defined(CONFIG_KVM_SMM)
These functions are only needed when CONFIG_KVM_SMM is defined.
Quoted 7 lines
arch/x86/kvm/vmx/x86_ops.h +6
-0+static inline int tdx_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) {return false; } +static inline int tdx_enter_smm (struct kvm_vcpu *vcpu,union kvm_smram *smram) {return 0 ; } +static inline int tdx_leave_smm (struct kvm_vcpu *vcpu,const union kvm_smram *smram) {return 0 ; } +static inline void tdx_enable_smi_window (struct kvm_vcpu *vcpu) {} +#endif +#endif /* __KVM_X86_VMX_X86_OPS_H */
Sean Christopherson
maintainerRe: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
In reply to [email protected]Quoted 29 lines · On Mon, Feb 26, 2024, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
TDX doesn't support system-management mode (SMM) and system-management interrupt (SMI) in guest TDs. Because guest state (vcpu state, memory state) is protected, it must go through the TDX module APIs to change guest state, injecting SMI and changing vcpu mode into SMM. The TDX module doesn't provide a way for VMM to inject SMI into guest TD and a way for VMM to switch guest vcpu mode into SMM.
We have two options in KVM when handling SMM or SMI in the guest TD or the device model (e.g. QEMU): 1) silently ignore the request or 2) return a meaningful error.
For simplicity, we implemented the option 1).
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/smm.h | 7 +++++- arch/x86/kvm/vmx/main.c | 45 ++++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 29 ++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 12 ++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-)arch/x86/kvm/smm.h +0
-0@@ -142,7 +142,12 @@ union kvm_smram {
arch/x86/kvm/smm.h union kvm_smram { · line 151
Quoted 10 lines
arch/x86/kvm/smm.h +6
-1static inline int kvm_inject_smi (struct kvm_vcpu *vcpu) { -kvm_make_request (KVM_REQ_SMI , vcpu); +/* +* If SMM isn't supported (e.g. TDX), silently discard SMI request. +* Assume that SMM supported = MSR_IA32_SMBASE supported. +*/ +if (static_call (kvm_x86_has_emulated_msr)(vcpu->kvm,MSR_IA32_SMBASE )) +kvm_make_request (KVM_REQ_SMI , vcpu);return 0 ;
No, just do what KVM already does for CONFIG_KVM_SMM=n, and return -ENOTTY. The *entire* point of have a return code is to handle setups that don't support SMM.
if (!static_call(kvm_x86_has_emulated_msr)(vcpu->kvm, MSR_IA32_SMBASE))) return -ENOTTY;
And with that, I would drop the comment, it's pretty darn clear what "assumption" is being made. In quotes because it's not an assumption, it's literally KVM's implementation.
And then the changelog can say "do what KVM does for CONFIG_KVM_SMM=n" without having to explain why we decided to do something completely arbitrary for TDX.
Quoted 7 lines
arch/x86/kvm/smm.h +0
-0}arch/x86/kvm/vmx/main.c +0
-0
arch/x86/kvm/vmx/main.c static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) · line 290
Quoted 8 lines
arch/x86/kvm/vmx/main.c +5
-0@@ -283,6 +283,43 @@ static void vt_msr_filter_changed(struct kvm_vcpu *vcpu)vmx_msr_filter_changed (vcpu); } +#ifdef CONFIG_KVM_SMM +static int vt_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +if (is_td_vcpu (vcpu)) +return tdx_smi_allowed (vcpu, for_injection);
Adding stubs for something that TDX will never support is silly. Bug the VM and return an error.
if (KVM_BUG_ON(is_td_vcpu(vcpu))) return -EIO;
And I wouldn't even bother with vt_* wrappers, just put that right in vmx_*(). Same thing for everything below.
Re: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
Quoted 47 lines · Sean Christopherson
Quoted 29 lines · On Mon, Feb 26, 2024, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
TDX doesn't support system-management mode (SMM) and system-management interrupt (SMI) in guest TDs. Because guest state (vcpu state, memory state) is protected, it must go through the TDX module APIs to change guest state, injecting SMI and changing vcpu mode into SMM. The TDX module doesn't provide a way for VMM to inject SMI into guest TD and a way for VMM to switch guest vcpu mode into SMM.
We have two options in KVM when handling SMM or SMI in the guest TD or the device model (e.g. QEMU): 1) silently ignore the request or 2) return a meaningful error.
For simplicity, we implemented the option 1).
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/smm.h | 7 +++++- arch/x86/kvm/vmx/main.c | 45 ++++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 29 ++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 12 ++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-)arch/x86/kvm/smm.h +0
-0@@ -142,7 +142,12 @@ union kvm_smram {arch/x86/kvm/smm.h union kvm_smram { · line 151
Quoted 10 lines
arch/x86/kvm/smm.h +6
-1static inline int kvm_inject_smi (struct kvm_vcpu *vcpu) { -kvm_make_request (KVM_REQ_SMI , vcpu); +/* +* If SMM isn't supported (e.g. TDX), silently discard SMI request. +* Assume that SMM supported = MSR_IA32_SMBASE supported. +*/ +if (static_call (kvm_x86_has_emulated_msr)(vcpu->kvm,MSR_IA32_SMBASE )) +kvm_make_request (KVM_REQ_SMI , vcpu);return 0 ;No, just do what KVM already does for
CONFIG_KVM_SMM=n, and return-ENOTTY. The *entire* point of have a return code is to handle setups that don't support SMM.if (!static_call(kvm_x86_has_emulated_msr)(vcpu->kvm, MSR_IA32_SMBASE))) return -ENOTTY;
Quoted 6 lines
And with that, I would drop the comment, it's pretty darn clear what "assumption" is being made. In quotes because it's not an assumption, it's literally KVM's implementation.
And then the changelog can say "do what KVM does for
CONFIG_KVM_SMM=n" without having to explain why we decided to do something completely arbitrary for TDX.
Ok.
Quoted 19 lines
Quoted 7 lines
arch/x86/kvm/smm.h +0
-0}arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) · line 290
Quoted 8 lines
arch/x86/kvm/vmx/main.c +5
-0@@ -283,6 +283,43 @@ static void vt_msr_filter_changed(struct kvm_vcpu *vcpu)vmx_msr_filter_changed (vcpu); } +#ifdef CONFIG_KVM_SMM +static int vt_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +if (is_td_vcpu (vcpu)) +return tdx_smi_allowed (vcpu, for_injection);Adding stubs for something that TDX will never support is silly. Bug the VM and return an error.
Quoted 5 lines
if (KVM_BUG_ON(is_td_vcpu(vcpu))) return -EIO;And I wouldn't even bother with vt_* wrappers, just put that right in vmx_*(). Same thing for everything below.
Will drop them. Those are traces to support guest debug. It's future topic and we have arch.guest_state_protected check now.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
In reply to Sean ChristophersonQuoted 71 lines · Sean Christopherson
Quoted 29 lines · On Mon, Feb 26, 2024, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
TDX doesn't support system-management mode (SMM) and system-management interrupt (SMI) in guest TDs. Because guest state (vcpu state, memory state) is protected, it must go through the TDX module APIs to change guest state, injecting SMI and changing vcpu mode into SMM. The TDX module doesn't provide a way for VMM to inject SMI into guest TD and a way for VMM to switch guest vcpu mode into SMM.
We have two options in KVM when handling SMM or SMI in the guest TD or the device model (e.g. QEMU): 1) silently ignore the request or 2) return a meaningful error.
For simplicity, we implemented the option 1).
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/smm.h | 7 +++++- arch/x86/kvm/vmx/main.c | 45 ++++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 29 ++++++++++++++++++++++++ arch/x86/kvm/vmx/x86_ops.h | 12 ++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-)arch/x86/kvm/smm.h +0
-0@@ -142,7 +142,12 @@ union kvm_smram {arch/x86/kvm/smm.h union kvm_smram { · line 151
Quoted 10 lines
arch/x86/kvm/smm.h +6
-1static inline int kvm_inject_smi (struct kvm_vcpu *vcpu) { -kvm_make_request (KVM_REQ_SMI , vcpu); +/* +* If SMM isn't supported (e.g. TDX), silently discard SMI request. +* Assume that SMM supported = MSR_IA32_SMBASE supported. +*/ +if (static_call (kvm_x86_has_emulated_msr)(vcpu->kvm,MSR_IA32_SMBASE )) +kvm_make_request (KVM_REQ_SMI , vcpu);return 0 ;No, just do what KVM already does for
CONFIG_KVM_SMM=n, and return-ENOTTY. The *entire* point of have a return code is to handle setups that don't support SMM.if (!static_call(kvm_x86_has_emulated_msr)(vcpu->kvm, MSR_IA32_SMBASE))) return -ENOTTY;And with that, I would drop the comment, it's pretty darn clear what "assumption" is being made. In quotes because it's not an assumption, it's literally KVM's implementation.
And then the changelog can say "do what KVM does for
CONFIG_KVM_SMM=n" without having to explain why we decided to do something completely arbitrary for TDX.Quoted 7 lines
arch/x86/kvm/smm.h +0
-0}arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) · line 290
Quoted 8 lines
arch/x86/kvm/vmx/main.c +5
-0@@ -283,6 +283,43 @@ static void vt_msr_filter_changed(struct kvm_vcpu *vcpu)vmx_msr_filter_changed (vcpu); } +#ifdef CONFIG_KVM_SMM +static int vt_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +if (is_td_vcpu (vcpu)) +return tdx_smi_allowed (vcpu, for_injection);Adding stubs for something that TDX will never support is silly. Bug the VM and return an error.
Quoted 2 lines
if (KVM_BUG_ON(is_td_vcpu(vcpu))) return -EIO;
is_td_vcpu() is defined in tdx.h.
Do you mind using open code to check whether the VM is TD in vmx.c? "vcpu->kvm->arch.vm_type == KVM_X86_TDX_VM"
Quoted 4 lines
And I wouldn't even bother with vt_* wrappers, just put that right in vmx_*(). Same thing for everything below.
Paolo Bonzini
maintainerRe: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
Quoted 18 lines · Binbin Wu
Quoted 18 lines
Quoted 7 lines
}arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) · line 290
Quoted 8 lines
arch/x86/kvm/vmx/main.c +5
-0@@ -283,6 +283,43 @@ static void vt_msr_filter_changed(struct kvm_vcpu *vcpu)vmx_msr_filter_changed (vcpu); } +#ifdef CONFIG_KVM_SMM +static int vt_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +if (is_td_vcpu (vcpu)) +return tdx_smi_allowed (vcpu, for_injection);Adding stubs for something that TDX will never support is silly. Bug the VM and return an error.
Quoted 6 lines
Quoted 2 lines
if (KVM_BUG_ON(is_td_vcpu(vcpu))) return -EIO;
is_td_vcpu()is defined intdx.h.
Do you mind using open code to check whether the VM is TD invmx.c? "vcpu->kvm->arch.vm_type==KVM_X86_TDX_VM"
I'd move it to some place that main.c can see. Or vmx.c as Sean says below, but I am not sure I like the idea too much.
Paolo
Quoted 2 lines
Quoted 2 lines
And I wouldn't even bother with vt_* wrappers, just put that right in vmx_*(). Same thing for everything below.
If it's a KVM_BUG_ON()
Re: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
Quoted 19 lines · Paolo Bonzini
Quoted 18 lines · Binbin Wu
Quoted 18 lines
Quoted 7 lines
}arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_msr_filter_changed(struct kvm_vcpu *vcpu) · line 290
Quoted 8 lines
arch/x86/kvm/vmx/main.c +5
-0@@ -283,6 +283,43 @@ static void vt_msr_filter_changed(struct kvm_vcpu *vcpu)vmx_msr_filter_changed (vcpu); } +#ifdef CONFIG_KVM_SMM +static int vt_smi_allowed (struct kvm_vcpu *vcpu,bool for_injection) +{ +if (is_td_vcpu (vcpu)) +return tdx_smi_allowed (vcpu, for_injection);Adding stubs for something that TDX will never support is silly. Bug the VM and return an error.
Quoted 6 lines
Quoted 5 lines
Quoted 2 lines
if (KVM_BUG_ON(is_td_vcpu(vcpu))) return -EIO;
is_td_vcpu()is defined intdx.h.
Do you mind using open code to check whether the VM is TD invmx.c? "vcpu->kvm->arch.vm_type==KVM_X86_TDX_VM"I'd move it to some place that
main.ccan see.
is_td_vcpu() can be seen in main.c
Quoted 2 lines
Or
vmx.cas Sean says
below, but I am not sure I like the idea too much.
Which you may not like? Remove the vt_* wrapper or use KVM_BUG_ON()?
Quoted 7 lines
Paolo
Quoted 2 lines
Quoted 2 lines
And I wouldn't even bother with vt_* wrappers, just put that right in vmx_*(). Same thing for everything below.
If it's a
KVM_BUG_ON()
Paolo Bonzini
maintainerRe: [PATCH v19 116/130] KVM: TDX: Silently discard SMI request
Quoted 8 lines · Binbin Wu
Quoted 5 lines · Paolo Bonzini
Quoted 3 lines · Binbin Wu
is_td_vcpu()is defined intdx.h.
Do you mind using open code to check whether the VM is TD invmx.c? "vcpu->kvm->arch.vm_type==KVM_X86_TDX_VM"I'd move it to some place that
main.ccan see.
is_td_vcpu()can be seen inmain.c
Ok.
Quoted 4 lines
Quoted 2 lines
Or
vmx.cas Sean says
below, but I am not sure I like the idea too much.Which you may not like? Remove the vt_* wrapper or use
KVM_BUG_ON()?
Removing the vt_* wrapper.
Paolo
PATCH v19 117/130 KVM: TDX: Silently ignore INIT/SIPI
In reply to [email protected]From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/lapic.c | 19 +++++++++++------- arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/vmx/main.c | 32 ++++++++++++++++++++++++++++-- arch/x86/kvm/vmx/tdx.c | 4 ++-- 6 files changed, 48 insertions(+), 11 deletions(-)
arch/x86/include/asm/kvm-x86-ops.h +1 -0
@@ -149,6 +149,7 @@ KVM_X86_OP_OPTIONAL(migrate_timers) KVM_X86_OP(msr_filter_changed) KVM_X86_OP(complete_emulated_msr) KVM_X86_OP(vcpu_deliver_sipi_vector) +KVM_X86_OP(vcpu_deliver_init) KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);KVM_X86_OP_OPTIONAL (get_untagged_addr)KVM_X86_OP_OPTIONAL_RET0 (gmem_max_level)
arch/x86/include/asm/kvm_host.h +2 -0
@@ -1836,6 +1836,7 @@ struct kvm_x86_ops {int (*complete_emulated_msr)(struct kvm_vcpu *vcpu,int err);void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu,u8 vector); +void (*vcpu_deliver_init)(struct kvm_vcpu *vcpu);/* * Returns vCPU specific APICv inhibit reasons
@@ -2092,6 +2093,7 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);void kvm_set_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg);int kvm_load_segment_descriptor (struct kvm_vcpu *vcpu,u16 selector,int seg);void kvm_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector); +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu);int kvm_task_switch (struct kvm_vcpu *vcpu,u16 tss_selector,int idt_index,int reason,bool has_error_code,u32 error_code);
arch/x86/kvm/lapic.c +12 -7
@@ -3268,6 +3268,16 @@ int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len)return 0 ; } +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +kvm_vcpu_reset (vcpu, true); +if (kvm_vcpu_is_bsp (vcpu)) + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +else + vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; +} +EXPORT_SYMBOL_GPL (kvm_vcpu_deliver_init); +int kvm_apic_accept_events (struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic;
@@ -3299,13 +3309,8 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu)return 0 ; } -if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) { -kvm_vcpu_reset (vcpu, true); -if (kvm_vcpu_is_bsp (apic->vcpu)) - vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; -else - vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; - } +if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) +static_call (kvm_x86_vcpu_deliver_init)(vcpu);if (test_and_clear_bit (KVM_APIC_SIPI , &apic->pending_events)) {if (vcpu->arch.mp_state ==KVM_MP_STATE_INIT_RECEIVED ) {/* evaluate pending_events before reading the vector */
arch/x86/kvm/svm/svm.c +1 -0
@@ -5037,6 +5037,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = kvm_vcpu_deliver_init, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, };
arch/x86/kvm/vmx/main.c +30 -2
@@ -320,6 +320,14 @@ static void vt_enable_smi_window(struct kvm_vcpu *vcpu) }#endif +static bool vt_apic_init_signal_blocked (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return true; + +return vmx_apic_init_signal_blocked (vcpu); +} +static void vt_apicv_pre_state_restore (struct kvm_vcpu *vcpu) {struct pi_desc *pi =vcpu_to_pi_desc (vcpu);
@@ -348,6 +356,25 @@ static void vt_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,vmx_deliver_interrupt (apic, delivery_mode, trig_mode, vector); } +static void vt_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector) +{ +if (is_td_vcpu (vcpu)) +return ; + +kvm_vcpu_deliver_sipi_vector (vcpu, vector); +} + +static void vt_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +/* TDX doesn't support INIT. Ignore INIT event */ + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +return ; + } + +kvm_vcpu_deliver_init (vcpu); +} +static void vt_flush_tlb_all (struct kvm_vcpu *vcpu) {if (is_td_vcpu (vcpu)) {
@@ -744,13 +771,14 @@ struct kvm_x86_ops vt_x86_ops __initdata = {#endif .check_emulate_instruction = vmx_check_emulate_instruction, - .apic_init_signal_blocked = vmx_apic_init_signal_blocked, + .apic_init_signal_blocked = vt_apic_init_signal_blocked, .migrate_timers = vmx_migrate_timers, .msr_filter_changed = vt_msr_filter_changed, .complete_emulated_msr = kvm_complete_insn_gp, - .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector, + .vcpu_deliver_sipi_vector = vt_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = vt_vcpu_deliver_init, .get_untagged_addr = vmx_get_untagged_addr,
arch/x86/kvm/vmx/tdx.c +2 -2
@@ -769,8 +769,8 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu)void tdx_vcpu_reset (struct kvm_vcpu *vcpu,bool init_event) { -/* Ignore INIT silently because TDX doesn't support INIT event. */ -if (init_event) +/* vcpu_deliver_init method silently discards INIT event. */ +if (KVM_BUG_ON (init_event, vcpu->kvm))return ;if (KVM_BUG_ON (is_td_vcpu_created (to_tdx (vcpu)), vcpu->kvm))return ;
Signature · 1 line
2.25.1
Re: [PATCH v19 117/130] KVM: TDX: Silently ignore INIT/SIPI
Quoted 12 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
Signed-off-by Isaku Yamahata <[email protected]>---
[...]
arch/x86/kvm/vmx/main.c static void vt_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 371
Quoted 6 lines
arch/x86/kvm/vmx/main.c +6
-0+ +static void vt_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +/* TDX doesn't support INIT. Ignore INIT event */ + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ;
Why change the mp_state to KVM_MP_STATE_RUNNABLE here?
And I am not sure whether we can say the INIT event is ignored since mp_state could be modified.
Quoted 6 lines
arch/x86/kvm/vmx/main.c +6
-0+return ; + } + +kvm_vcpu_deliver_init (vcpu); +} +
Re: [PATCH v19 117/130] KVM: TDX: Silently ignore INIT/SIPI
Quoted 24 lines · Binbin Wu
Quoted 12 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
Signed-off-by Isaku Yamahata <[email protected]>---
[...]
arch/x86/kvm/vmx/main.c static void vt_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, · line 371
Quoted 6 lines
arch/x86/kvm/vmx/main.c +6
-0+ +static void vt_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +/* TDX doesn't support INIT. Ignore INIT event */ + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ;Why change the
mp_statetoKVM_MP_STATE_RUNNABLEhere?
Quoted 2 lines
And I am not sure whether we can say the INIT event is ignored since
mp_statecould be modified.
We should drop the line. Now it's not necessary and KVM_TDX_INIT_VCPU change it.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 117/130] KVM: TDX: Silently ignore INIT/SIPI
In reply to [email protected]Quoted 106 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/lapic.c | 19 +++++++++++------- arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/vmx/main.c | 32 ++++++++++++++++++++++++++++-- arch/x86/kvm/vmx/tdx.c | 4 ++-- 6 files changed, 48 insertions(+), 11 deletions(-)arch/x86/include/asm/kvm-x86-ops.h +1
-0@@ -149,6 +149,7 @@ KVM_X86_OP_OPTIONAL(migrate_timers) KVM_X86_OP(msr_filter_changed) KVM_X86_OP(complete_emulated_msr) KVM_X86_OP(vcpu_deliver_sipi_vector) +KVM_X86_OP(vcpu_deliver_init) KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);KVM_X86_OP_OPTIONAL (get_untagged_addr)KVM_X86_OP_OPTIONAL_RET0 (gmem_max_level)arch/x86/include/asm/kvm_host.h +2
-0@@ -1836,6 +1836,7 @@ struct kvm_x86_ops {int (*complete_emulated_msr)(struct kvm_vcpu *vcpu,int err);void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu,u8 vector); +void (*vcpu_deliver_init)(struct kvm_vcpu *vcpu);/* * Returns vCPU specific APICv inhibit reasons @@ -2092,6 +2093,7 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);void kvm_set_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg);int kvm_load_segment_descriptor (struct kvm_vcpu *vcpu,u16 selector,int seg);void kvm_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector); +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu);int kvm_task_switch (struct kvm_vcpu *vcpu,u16 tss_selector,int idt_index,int reason,bool has_error_code,u32 error_code);arch/x86/kvm/lapic.c +12
-7@@ -3268,6 +3268,16 @@ int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len)return 0 ; } +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +kvm_vcpu_reset (vcpu, true); +if (kvm_vcpu_is_bsp (vcpu)) + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +else + vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; +} +EXPORT_SYMBOL_GPL (kvm_vcpu_deliver_init); +int kvm_apic_accept_events (struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic;@@ -3299,13 +3309,8 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu)return 0 ; } -if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) { -kvm_vcpu_reset (vcpu, true); -if (kvm_vcpu_is_bsp (apic->vcpu)) - vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; -else - vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; - } +if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) +static_call (kvm_x86_vcpu_deliver_init)(vcpu);if (test_and_clear_bit (KVM_APIC_SIPI , &apic->pending_events)) {if (vcpu->arch.mp_state ==KVM_MP_STATE_INIT_RECEIVED ) {/* evaluate pending_events before reading the vector */ arch/x86/kvm/svm/svm.c +1
-0@@ -5037,6 +5037,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = kvm_vcpu_deliver_init, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, };arch/x86/kvm/vmx/main.c +0
-0
arch/x86/kvm/vmx/main.c static void vt_enable_smi_window(struct kvm_vcpu *vcpu) · line 326
Quoted 7 lines
arch/x86/kvm/vmx/main.c +4
-0@@ -320,6 +320,14 @@ static void vt_enable_smi_window(struct kvm_vcpu *vcpu) }#endif +static bool vt_apic_init_signal_blocked (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return true;
Since for TD, INIT is always blocked, then in kvm_apic_accept_events(), the code path to handle INIT/SIPI delivery will not be called, i.e, the OPs .vcpu_deliver_init() and .vcpu_deliver_sipi_vector() are never called for TD. Seems no need to add the new interface vcpu_deliver_init or the new wrapper vt_vcpu_deliver_sipi_vector().
And consider the INIT/SIPI for TD:
- Normally, for TD, INIT ans SIPI should not be set in APIC's pending_events.
Maybe we can call KVM_BUG_ON() in vt_apic_init_signal_blocked() for TD?
- If INIT and SIPI are allowed be set in APIC's pending_events for somehow, the current code has a problem, it will never clear INIT bit in APIC's pending_events. Then kvm_apic_accept_events() needs to execute more check code if INIT was once set.
INIT bit should be cleared with this assumption.
Quoted 65 lines
arch/x86/kvm/vmx/main.c +26
-2+ +return vmx_apic_init_signal_blocked (vcpu); +} +static void vt_apicv_pre_state_restore (struct kvm_vcpu *vcpu) {struct pi_desc *pi =vcpu_to_pi_desc (vcpu);@@ -348,6 +356,25 @@ static void vt_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode,vmx_deliver_interrupt (apic, delivery_mode, trig_mode, vector); } +static void vt_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector) +{ +if (is_td_vcpu (vcpu)) +return ; + +kvm_vcpu_deliver_sipi_vector (vcpu, vector); +} + +static void vt_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) { +/* TDX doesn't support INIT. Ignore INIT event */ + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +return ; + } + +kvm_vcpu_deliver_init (vcpu); +} +static void vt_flush_tlb_all (struct kvm_vcpu *vcpu) {if (is_td_vcpu (vcpu)) {@@ -744,13 +771,14 @@ struct kvm_x86_ops vt_x86_ops __initdata = {#endif .check_emulate_instruction = vmx_check_emulate_instruction, - .apic_init_signal_blocked = vmx_apic_init_signal_blocked, + .apic_init_signal_blocked = vt_apic_init_signal_blocked, .migrate_timers = vmx_migrate_timers, .msr_filter_changed = vt_msr_filter_changed, .complete_emulated_msr = kvm_complete_insn_gp, - .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector, + .vcpu_deliver_sipi_vector = vt_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = vt_vcpu_deliver_init, .get_untagged_addr = vmx_get_untagged_addr,arch/x86/kvm/vmx/tdx.c +2
-2@@ -769,8 +769,8 @@ void tdx_vcpu_free(struct kvm_vcpu *vcpu)void tdx_vcpu_reset (struct kvm_vcpu *vcpu,bool init_event) { -/* Ignore INIT silently because TDX doesn't support INIT event. */ -if (init_event) +/* vcpu_deliver_init method silently discards INIT event. */ +if (KVM_BUG_ON (init_event, vcpu->kvm))return ;if (KVM_BUG_ON (is_td_vcpu_created (to_tdx (vcpu)), vcpu->kvm))return ;
Re: [PATCH v19 117/130] KVM: TDX: Silently ignore INIT/SIPI
Quoted 116 lines · Binbin Wu
Quoted 99 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/lapic.c | 19 +++++++++++------- arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/vmx/main.c | 32 ++++++++++++++++++++++++++++-- arch/x86/kvm/vmx/tdx.c | 4 ++-- 6 files changed, 48 insertions(+), 11 deletions(-)arch/x86/include/asm/kvm-x86-ops.h +1
-0@@ -149,6 +149,7 @@ KVM_X86_OP_OPTIONAL(migrate_timers) KVM_X86_OP(msr_filter_changed) KVM_X86_OP(complete_emulated_msr) KVM_X86_OP(vcpu_deliver_sipi_vector) +KVM_X86_OP(vcpu_deliver_init) KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);KVM_X86_OP_OPTIONAL (get_untagged_addr)KVM_X86_OP_OPTIONAL_RET0 (gmem_max_level)arch/x86/include/asm/kvm_host.h +2
-0@@ -1836,6 +1836,7 @@ struct kvm_x86_ops {int (*complete_emulated_msr)(struct kvm_vcpu *vcpu,int err);void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu,u8 vector); +void (*vcpu_deliver_init)(struct kvm_vcpu *vcpu);/* * Returns vCPU specific APICv inhibit reasons @@ -2092,6 +2093,7 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);void kvm_set_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg);int kvm_load_segment_descriptor (struct kvm_vcpu *vcpu,u16 selector,int seg);void kvm_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector); +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu);int kvm_task_switch (struct kvm_vcpu *vcpu,u16 tss_selector,int idt_index,int reason,bool has_error_code,u32 error_code);arch/x86/kvm/lapic.c +12
-7@@ -3268,6 +3268,16 @@ int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len)return 0 ; } +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +kvm_vcpu_reset (vcpu, true); +if (kvm_vcpu_is_bsp (vcpu)) + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +else + vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; +} +EXPORT_SYMBOL_GPL (kvm_vcpu_deliver_init); +int kvm_apic_accept_events (struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic;@@ -3299,13 +3309,8 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu)return 0 ; } -if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) { -kvm_vcpu_reset (vcpu, true); -if (kvm_vcpu_is_bsp (apic->vcpu)) - vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; -else - vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; - } +if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) +static_call (kvm_x86_vcpu_deliver_init)(vcpu);if (test_and_clear_bit (KVM_APIC_SIPI , &apic->pending_events)) {if (vcpu->arch.mp_state ==KVM_MP_STATE_INIT_RECEIVED ) {/* evaluate pending_events before reading the vector */ arch/x86/kvm/svm/svm.c +1
-0@@ -5037,6 +5037,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = kvm_vcpu_deliver_init, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, };arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_enable_smi_window(struct kvm_vcpu *vcpu) · line 325
Quoted 6 lines
arch/x86/kvm/vmx/main.c +4
-0@@ -320,6 +320,14 @@ static void vt_enable_smi_window(struct kvm_vcpu *vcpu) }#endif +static bool vt_apic_init_signal_blocked (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return true;Since for TD, INIT is always blocked, then in
kvm_apic_accept_events(), the code path to handle INIT/SIPI delivery will not be called, i.e, the OPs .vcpu_deliver_init() and .vcpu_deliver_sipi_vector() are never called for TD.
Seems no need to add the new interfacevcpu_deliver_initor the new wrappervt_vcpu_deliver_sipi_vector().
Quoted 10 lines
And consider the INIT/SIPI for TD:
- Normally, for TD, INIT ans SIPI should not be set in APIC'spending_events.
Maybe we can callKVM_BUG_ON()invt_apic_init_signal_blocked()for TD?
- If INIT and SIPI are allowed be set in APIC'spending_eventsfor somehow, the current code has a problem, it will never clear INIT bit in APIC'spending_events.
Thenkvm_apic_accept_events()needs to execute more check code if INIT was once set.
INIT bit should be cleared with this assumption.
KVM_SET_MP_STATE and KVM_SET_VCPU_EVENTS can set INIT/SIPI by the user space. If we change those two IOCTLs to reject INIT/SIPI for TDX, we can go for the above "Normally" option. Because I didn't want to touch the common functions, I ended in extra callbacks. The user space shouldn't use those two IOCTLs for TDX, though.
Signature · 1 line
Isaku Yamahata <[email protected]>
Re: [PATCH v19 117/130] KVM: TDX: Silently ignore INIT/SIPI
Quoted 128 lines · Isaku Yamahata
Quoted 114 lines · Binbin Wu
Quoted 99 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/lapic.c | 19 +++++++++++------- arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/vmx/main.c | 32 ++++++++++++++++++++++++++++-- arch/x86/kvm/vmx/tdx.c | 4 ++-- 6 files changed, 48 insertions(+), 11 deletions(-)arch/x86/include/asm/kvm-x86-ops.h +1
-0@@ -149,6 +149,7 @@ KVM_X86_OP_OPTIONAL(migrate_timers) KVM_X86_OP(msr_filter_changed) KVM_X86_OP(complete_emulated_msr) KVM_X86_OP(vcpu_deliver_sipi_vector) +KVM_X86_OP(vcpu_deliver_init) KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);KVM_X86_OP_OPTIONAL (get_untagged_addr)KVM_X86_OP_OPTIONAL_RET0 (gmem_max_level)arch/x86/include/asm/kvm_host.h +2
-0@@ -1836,6 +1836,7 @@ struct kvm_x86_ops {int (*complete_emulated_msr)(struct kvm_vcpu *vcpu,int err);void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu,u8 vector); +void (*vcpu_deliver_init)(struct kvm_vcpu *vcpu);/* * Returns vCPU specific APICv inhibit reasons @@ -2092,6 +2093,7 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);void kvm_set_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg);int kvm_load_segment_descriptor (struct kvm_vcpu *vcpu,u16 selector,int seg);void kvm_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector); +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu);int kvm_task_switch (struct kvm_vcpu *vcpu,u16 tss_selector,int idt_index,int reason,bool has_error_code,u32 error_code);arch/x86/kvm/lapic.c +12
-7@@ -3268,6 +3268,16 @@ int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len)return 0 ; } +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +kvm_vcpu_reset (vcpu, true); +if (kvm_vcpu_is_bsp (vcpu)) + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +else + vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; +} +EXPORT_SYMBOL_GPL (kvm_vcpu_deliver_init); +int kvm_apic_accept_events (struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic;@@ -3299,13 +3309,8 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu)return 0 ; } -if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) { -kvm_vcpu_reset (vcpu, true); -if (kvm_vcpu_is_bsp (apic->vcpu)) - vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; -else - vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; - } +if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) +static_call (kvm_x86_vcpu_deliver_init)(vcpu);if (test_and_clear_bit (KVM_APIC_SIPI , &apic->pending_events)) {if (vcpu->arch.mp_state ==KVM_MP_STATE_INIT_RECEIVED ) {/* evaluate pending_events before reading the vector */ arch/x86/kvm/svm/svm.c +1
-0@@ -5037,6 +5037,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = kvm_vcpu_deliver_init, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, };arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_enable_smi_window(struct kvm_vcpu *vcpu) · line 325
Quoted 6 lines
arch/x86/kvm/vmx/main.c +4
-0@@ -320,6 +320,14 @@ static void vt_enable_smi_window(struct kvm_vcpu *vcpu) }#endif +static bool vt_apic_init_signal_blocked (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return true;Since for TD, INIT is always blocked, then in
kvm_apic_accept_events(), the code path to handle INIT/SIPI delivery will not be called, i.e, the OPs .vcpu_deliver_init() and .vcpu_deliver_sipi_vector() are never called for TD.
Seems no need to add the new interfacevcpu_deliver_initor the new wrappervt_vcpu_deliver_sipi_vector().Quoted 10 lines
And consider the INIT/SIPI for TD:
- Normally, for TD, INIT ans SIPI should not be set in APIC'spending_events.
Maybe we can callKVM_BUG_ON()invt_apic_init_signal_blocked()for TD?
- If INIT and SIPI are allowed be set in APIC'spending_eventsfor somehow, the current code has a problem, it will never clear INIT bit in APIC'spending_events.
Thenkvm_apic_accept_events()needs to execute more check code if INIT was
once set.
INIT bit should be cleared with this assumption.
Quoted 5 lines
KVM_SET_MP_STATEandKVM_SET_VCPU_EVENTScan set INIT/SIPI by the user space. If we change those two IOCTLs to reject INIT/SIPI for TDX, we can go for the above "Normally" option. Because I didn't want to touch the common functions, I ended in extra callbacks. The user space shouldn't use those two IOCTLs for TDX, though.
Even though INIT/SIPI can be set by userspace to APIC's pending_events, for TD, INIT is always blocked, then in kvm_apic_accept_events(), the code path to handle INIT/SIPI delivery will not be called, i.e, the OPs .vcpu_deliver_init() and .vcpu_deliver_sipi_vector() are never called for TD.
Can we just drop the new OPs .vcpu_deliver_init() and the new wrapper vt_vcpu_deliver_sipi_vector()?
Re: [PATCH v19 117/130] KVM: TDX: Silently ignore INIT/SIPI
Quoted 137 lines · Binbin Wu
Quoted 128 lines · Isaku Yamahata
Quoted 114 lines · Binbin Wu
Quoted 99 lines · On 2/26/2024 4:26 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. Given that TDX guest is paravirtualized to boot AP, the option 1 is chosen for simplicity.
Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/include/asm/kvm-x86-ops.h | 1 + arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/lapic.c | 19 +++++++++++------- arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/vmx/main.c | 32 ++++++++++++++++++++++++++++-- arch/x86/kvm/vmx/tdx.c | 4 ++-- 6 files changed, 48 insertions(+), 11 deletions(-)arch/x86/include/asm/kvm-x86-ops.h +1
-0@@ -149,6 +149,7 @@ KVM_X86_OP_OPTIONAL(migrate_timers) KVM_X86_OP(msr_filter_changed) KVM_X86_OP(complete_emulated_msr) KVM_X86_OP(vcpu_deliver_sipi_vector) +KVM_X86_OP(vcpu_deliver_init) KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons);KVM_X86_OP_OPTIONAL (get_untagged_addr)KVM_X86_OP_OPTIONAL_RET0 (gmem_max_level)arch/x86/include/asm/kvm_host.h +2
-0@@ -1836,6 +1836,7 @@ struct kvm_x86_ops {int (*complete_emulated_msr)(struct kvm_vcpu *vcpu,int err);void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu,u8 vector); +void (*vcpu_deliver_init)(struct kvm_vcpu *vcpu);/* * Returns vCPU specific APICv inhibit reasons @@ -2092,6 +2093,7 @@ void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);void kvm_set_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg);int kvm_load_segment_descriptor (struct kvm_vcpu *vcpu,u16 selector,int seg);void kvm_vcpu_deliver_sipi_vector (struct kvm_vcpu *vcpu,u8 vector); +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu);int kvm_task_switch (struct kvm_vcpu *vcpu,u16 tss_selector,int idt_index,int reason,bool has_error_code,u32 error_code);arch/x86/kvm/lapic.c +12
-7@@ -3268,6 +3268,16 @@ int kvm_lapic_set_pv_eoi(struct kvm_vcpu *vcpu, u64 data, unsigned long len)return 0 ; } +void kvm_vcpu_deliver_init (struct kvm_vcpu *vcpu) +{ +kvm_vcpu_reset (vcpu, true); +if (kvm_vcpu_is_bsp (vcpu)) + vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; +else + vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; +} +EXPORT_SYMBOL_GPL (kvm_vcpu_deliver_init); +int kvm_apic_accept_events (struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic;@@ -3299,13 +3309,8 @@ int kvm_apic_accept_events(struct kvm_vcpu *vcpu)return 0 ; } -if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) { -kvm_vcpu_reset (vcpu, true); -if (kvm_vcpu_is_bsp (apic->vcpu)) - vcpu->arch.mp_state =KVM_MP_STATE_RUNNABLE ; -else - vcpu->arch.mp_state =KVM_MP_STATE_INIT_RECEIVED ; - } +if (test_and_clear_bit (KVM_APIC_INIT , &apic->pending_events)) +static_call (kvm_x86_vcpu_deliver_init)(vcpu);if (test_and_clear_bit (KVM_APIC_SIPI , &apic->pending_events)) {if (vcpu->arch.mp_state ==KVM_MP_STATE_INIT_RECEIVED ) {/* evaluate pending_events before reading the vector */ arch/x86/kvm/svm/svm.c +1
-0@@ -5037,6 +5037,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .complete_emulated_msr = svm_complete_emulated_msr, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, + .vcpu_deliver_init = kvm_vcpu_deliver_init, .vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons, };arch/x86/kvm/vmx/main.c +0
-0arch/x86/kvm/vmx/main.c static void vt_enable_smi_window(struct kvm_vcpu *vcpu) · line 325
Quoted 6 lines
arch/x86/kvm/vmx/main.c +4
-0@@ -320,6 +320,14 @@ static void vt_enable_smi_window(struct kvm_vcpu *vcpu) }#endif +static bool vt_apic_init_signal_blocked (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return true;Since for TD, INIT is always blocked, then in
kvm_apic_accept_events(), the code path to handle INIT/SIPI delivery will not be called, i.e, the OPs .vcpu_deliver_init() and .vcpu_deliver_sipi_vector() are never called for TD.
Seems no need to add the new interfacevcpu_deliver_initor the new wrappervt_vcpu_deliver_sipi_vector().Quoted 10 lines
And consider the INIT/SIPI for TD:
- Normally, for TD, INIT ans SIPI should not be set in APIC'spending_events.
Maybe we can callKVM_BUG_ON()invt_apic_init_signal_blocked()for TD?
- If INIT and SIPI are allowed be set in APIC'spending_eventsfor somehow, the current code has a problem, it will never clear INIT bit in APIC'spending_events.
Thenkvm_apic_accept_events()needs to execute more check code if INIT was
once set.
INIT bit should be cleared with this assumption.Quoted 5 lines
KVM_SET_MP_STATEandKVM_SET_VCPU_EVENTScan set INIT/SIPI by the user space. If we change those two IOCTLs to reject INIT/SIPI for TDX, we can go for the above "Normally" option. Because I didn't want to touch the common functions, I ended in extra callbacks. The user space shouldn't use those two IOCTLs for TDX, though.
Quoted 8 lines
Even though INIT/SIPI can be set by userspace to APIC's
pending_events, for TD, INIT is always blocked, then inkvm_apic_accept_events(), the code path to handle INIT/SIPI delivery will not be called, i.e, the OPs .vcpu_deliver_init() and .vcpu_deliver_sipi_vector() are never called for TD.Can we just drop the new OPs .vcpu_deliver_init() and the new wrapper
vt_vcpu_deliver_sipi_vector()?
Ah, you're right. We simply always block INIT/SIPI. We should update the commit message. How about this? Added option 3.
The TDX module API doesn't provide API for VMM to inject INIT IPI and SIPI. Instead it defines the different protocols to boot application processors. Ignore INIT and SIPI events for the TDX guest.
There are two options. 1) (silently) ignore INIT/SIPI request or 2) return error to guest TDs somehow. 3) Always block INIT/SIPI always. Given that TDX guest is paravirtualized to boot AP, the option 3 is chosen for simplicity.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 118/130 KVM: TDX: Add methods to ignore accesses to CPU state
In reply to [email protected]From: Isaku Yamahata <[email protected]>
TDX protects TDX guest state from VMM. Implement access methods for TDX guest state to ignore them or return zero. Because those methods can be called by kvm ioctls to set/get cpu registers, they don't have KVM_BUG_ON except one method.
--- arch/x86/kvm/vmx/main.c | 289 +++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 48 +++++- arch/x86/kvm/vmx/x86_ops.h | 13 ++ 3 files changed, 321 insertions(+), 29 deletions(-)
arch/x86/kvm/vmx/main.c +262 -27
@@ -375,6 +375,200 @@ static void vt_vcpu_deliver_init(struct kvm_vcpu *vcpu)kvm_vcpu_deliver_init (vcpu); } +static void vt_vcpu_after_set_cpuid (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_vcpu_after_set_cpuid (vcpu); +} + +static void vt_update_exception_bitmap (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_update_exception_bitmap (vcpu); +} + +static u64 vt_get_segment_base (struct kvm_vcpu *vcpu,int seg) +{ +if (is_td_vcpu (vcpu)) +return tdx_get_segment_base (vcpu, seg); + +return vmx_get_segment_base (vcpu, seg); +} + +static void vt_get_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var, +int seg) +{ +if (is_td_vcpu (vcpu)) { +tdx_get_segment (vcpu, var, seg); +return ; + } + +vmx_get_segment (vcpu, var, seg); +} + +static void vt_set_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var, +int seg) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_segment (vcpu, var, seg); +} + +static int vt_get_cpl (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return tdx_get_cpl (vcpu); + +return vmx_get_cpl (vcpu); +} + +static void vt_get_cs_db_l_bits (struct kvm_vcpu *vcpu,int *db,int *l) +{ +if (is_td_vcpu (vcpu)) { + *db =0 ; + *l =0 ; +return ; + } + +vmx_get_cs_db_l_bits (vcpu, db, l); +} + +static bool vt_is_valid_cr0 (struct kvm_vcpu *vcpu,unsigned long cr0) +{ +if (is_td_vcpu (vcpu)) +return true; + +return vmx_is_valid_cr0 (vcpu, cr0); +} + +static void vt_set_cr0 (struct kvm_vcpu *vcpu,unsigned long cr0) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_cr0 (vcpu, cr0); +} + +static bool vt_is_valid_cr4 (struct kvm_vcpu *vcpu,unsigned long cr4) +{ +if (is_td_vcpu (vcpu)) +return true; + +return vmx_is_valid_cr4 (vcpu, cr4); +} + +static void vt_set_cr4 (struct kvm_vcpu *vcpu,unsigned long cr4) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_cr4 (vcpu, cr4); +} + +static int vt_set_efer (struct kvm_vcpu *vcpu,u64 efer) +{ +if (is_td_vcpu (vcpu)) +return 0 ; + +return vmx_set_efer (vcpu, efer); +} + +static void vt_get_idt (struct kvm_vcpu *vcpu,struct desc_ptr *dt) +{ +if (is_td_vcpu (vcpu)) { +memset (dt,0 ,sizeof (*dt)); +return ; + } + +vmx_get_idt (vcpu, dt); +} + +static void vt_set_idt (struct kvm_vcpu *vcpu,struct desc_ptr *dt) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_idt (vcpu, dt); +} + +static void vt_get_gdt (struct kvm_vcpu *vcpu,struct desc_ptr *dt) +{ +if (is_td_vcpu (vcpu)) { +memset (dt,0 ,sizeof (*dt)); +return ; + } + +vmx_get_gdt (vcpu, dt); +} + +static void vt_set_gdt (struct kvm_vcpu *vcpu,struct desc_ptr *dt) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_gdt (vcpu, dt); +} + +static void vt_set_dr7 (struct kvm_vcpu *vcpu,unsigned long val) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_dr7 (vcpu, val); +} + +static void vt_sync_dirty_debug_regs (struct kvm_vcpu *vcpu) +{ +/* +* MOV-DR exiting is always cleared for TD guest, even in debug mode. +* Thus KVM_DEBUGREG_WONT_EXIT can never be set and it should never +* reach here for TD vcpu. +*/ +if (KVM_BUG_ON (is_td_vcpu (vcpu), vcpu->kvm)) +return ; + +vmx_sync_dirty_debug_regs (vcpu); +} + +static void vt_cache_reg (struct kvm_vcpu *vcpu,enum kvm_reg reg) +{ +if (is_td_vcpu (vcpu)) { +tdx_cache_reg (vcpu, reg); +return ; + } + +vmx_cache_reg (vcpu, reg); +} + +static unsigned long vt_get_rflags (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return tdx_get_rflags (vcpu); + +return vmx_get_rflags (vcpu); +} + +static void vt_set_rflags (struct kvm_vcpu *vcpu,unsigned long rflags) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_set_rflags (vcpu, rflags); +} + +static bool vt_get_if_flag (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return false; + +return vmx_get_if_flag (vcpu); +} +static void vt_flush_tlb_all (struct kvm_vcpu *vcpu) {if (is_td_vcpu (vcpu)) {
@@ -521,6 +715,14 @@ static void vt_inject_irq(struct kvm_vcpu *vcpu, bool reinjected)vmx_inject_irq (vcpu, reinjected); } +static void vt_inject_exception (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_inject_exception (vcpu); +} +static void vt_cancel_injection (struct kvm_vcpu *vcpu) {if (is_td_vcpu (vcpu))
@@ -567,6 +769,39 @@ static void vt_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,vmx_get_exit_info (vcpu, reason, info1, info2, intr_info, error_code); } + +static void vt_update_cr8_intercept (struct kvm_vcpu *vcpu,int tpr,int irr) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_update_cr8_intercept (vcpu, tpr, irr); +} + +static void vt_load_eoi_exitmap (struct kvm_vcpu *vcpu,u64 *eoi_exit_bitmap) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_load_eoi_exitmap (vcpu, eoi_exit_bitmap); +} + +static int vt_set_tss_addr (struct kvm *kvm,unsigned int addr) +{ +if (is_td (kvm)) +return 0 ; + +return vmx_set_tss_addr (kvm, addr); +} + +static int vt_set_identity_map_addr (struct kvm *kvm,u64 ident_addr) +{ +if (is_td (kvm)) +return 0 ; + +return vmx_set_identity_map_addr (kvm, ident_addr); +} +static u8 vt_get_mt_mask (struct kvm_vcpu *vcpu,gfn_t gfn,bool is_mmio) {if (is_td_vcpu (vcpu))
@@ -661,30 +896,30 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .vcpu_load = vt_vcpu_load, .vcpu_put = vt_vcpu_put, - .update_exception_bitmap = vmx_update_exception_bitmap, + .update_exception_bitmap = vt_update_exception_bitmap, .get_msr_feature = vmx_get_msr_feature, .get_msr = vt_get_msr, .set_msr = vt_set_msr, - .get_segment_base = vmx_get_segment_base, - .get_segment = vmx_get_segment, - .set_segment = vmx_set_segment, - .get_cpl = vmx_get_cpl, - .get_cs_db_l_bits = vmx_get_cs_db_l_bits, - .is_valid_cr0 = vmx_is_valid_cr0, - .set_cr0 = vmx_set_cr0, - .is_valid_cr4 = vmx_is_valid_cr4, - .set_cr4 = vmx_set_cr4, - .set_efer = vmx_set_efer, - .get_idt = vmx_get_idt, - .set_idt = vmx_set_idt, - .get_gdt = vmx_get_gdt, - .set_gdt = vmx_set_gdt, - .set_dr7 = vmx_set_dr7, - .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs, - .cache_reg = vmx_cache_reg, - .get_rflags = vmx_get_rflags, - .set_rflags = vmx_set_rflags, - .get_if_flag = vmx_get_if_flag, + .get_segment_base = vt_get_segment_base, + .get_segment = vt_get_segment, + .set_segment = vt_set_segment, + .get_cpl = vt_get_cpl, + .get_cs_db_l_bits = vt_get_cs_db_l_bits, + .is_valid_cr0 = vt_is_valid_cr0, + .set_cr0 = vt_set_cr0, + .is_valid_cr4 = vt_is_valid_cr4, + .set_cr4 = vt_set_cr4, + .set_efer = vt_set_efer, + .get_idt = vt_get_idt, + .set_idt = vt_set_idt, + .get_gdt = vt_get_gdt, + .set_gdt = vt_set_gdt, + .set_dr7 = vt_set_dr7, + .sync_dirty_debug_regs = vt_sync_dirty_debug_regs, + .cache_reg = vt_cache_reg, + .get_rflags = vt_get_rflags, + .set_rflags = vt_set_rflags, + .get_if_flag = vt_get_if_flag, .flush_tlb_all = vt_flush_tlb_all, .flush_tlb_current = vt_flush_tlb_current,
@@ -701,7 +936,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .patch_hypercall = vmx_patch_hypercall, .inject_irq = vt_inject_irq, .inject_nmi = vt_inject_nmi, - .inject_exception = vmx_inject_exception, + .inject_exception = vt_inject_exception, .cancel_injection = vt_cancel_injection, .interrupt_allowed = vt_interrupt_allowed, .nmi_allowed = vt_nmi_allowed,
@@ -709,11 +944,11 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .set_nmi_mask = vt_set_nmi_mask, .enable_nmi_window = vt_enable_nmi_window, .enable_irq_window = vt_enable_irq_window, - .update_cr8_intercept = vmx_update_cr8_intercept, + .update_cr8_intercept = vt_update_cr8_intercept, .set_virtual_apic_mode = vmx_set_virtual_apic_mode, .set_apic_access_page_addr = vmx_set_apic_access_page_addr, .refresh_apicv_exec_ctrl = vmx_refresh_apicv_exec_ctrl, - .load_eoi_exitmap = vmx_load_eoi_exitmap, + .load_eoi_exitmap = vt_load_eoi_exitmap, .apicv_pre_state_restore = vt_apicv_pre_state_restore, .required_apicv_inhibits = VMX_REQUIRED_APICV_INHIBITS, .hwapic_irr_update = vmx_hwapic_irr_update,
@@ -724,13 +959,13 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .dy_apicv_has_pending_interrupt = pi_has_pending_interrupt, .protected_apic_has_interrupt = vt_protected_apic_has_interrupt, - .set_tss_addr = vmx_set_tss_addr, - .set_identity_map_addr = vmx_set_identity_map_addr, + .set_tss_addr = vt_set_tss_addr, + .set_identity_map_addr = vt_set_identity_map_addr, .get_mt_mask = vt_get_mt_mask, .get_exit_info = vt_get_exit_info, - .vcpu_after_set_cpuid = vmx_vcpu_after_set_cpuid, + .vcpu_after_set_cpuid = vt_vcpu_after_set_cpuid, .has_wbinvd_exit = cpu_has_vmx_wbinvd_exit,
arch/x86/kvm/vmx/tdx.c +46 -2
60 lines not on this page · raw message
arch/x86/kvm/vmx/x86_ops.h +13 -0
@@ -169,6 +169,12 @@ bool tdx_has_emulated_msr(u32 index, bool write);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_get_cpl (struct kvm_vcpu *vcpu); +void tdx_cache_reg (struct kvm_vcpu *vcpu,enum kvm_reg reg); +unsigned long tdx_get_rflags (struct kvm_vcpu *vcpu); +u64 tdx_get_segment_base (struct kvm_vcpu *vcpu,int seg); +void tdx_get_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg); +int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp);void tdx_flush_tlb (struct kvm_vcpu *vcpu);
@@ -221,6 +227,13 @@ static inline bool tdx_has_emulated_msr(u32 index, bool write) { 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_get_cpl (struct kvm_vcpu *vcpu) {return 0 ; } +static inline void tdx_cache_reg (struct kvm_vcpu *vcpu,enum kvm_reg reg) {} +static inline unsigned long tdx_get_rflags (struct kvm_vcpu *vcpu) {return 0 ; } +static inline u64 tdx_get_segment_base (struct kvm_vcpu *vcpu,int seg) {return 0 ; } +static inline void tdx_get_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var, +int seg) {} +static inline int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp) {return -EOPNOTSUPP ; }static inline void tdx_flush_tlb (struct kvm_vcpu *vcpu) {}
Signature · 1 line
2.25.1
Re: [PATCH v19 118/130] KVM: TDX: Add methods to ignore accesses to CPU state
Quoted 32 lines · On 2/26/2024 4:27 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
TDX protects TDX guest state from VMM. Implement access methods for TDX guest state to ignore them or return zero. Because those methods can be called by kvm ioctls to set/get cpu registers, they don't have
KVM_BUG_ONexcept one method.Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/vmx/main.c | 289 +++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 48 +++++- arch/x86/kvm/vmx/x86_ops.h | 13 ++ 3 files changed, 321 insertions(+), 29 deletions(-)arch/x86/kvm/vmx/main.c +10
-0@@ -375,6 +375,200 @@ static void vt_vcpu_deliver_init(struct kvm_vcpu *vcpu)kvm_vcpu_deliver_init (vcpu); } +static void vt_vcpu_after_set_cpuid (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_vcpu_after_set_cpuid (vcpu); +} + +static void vt_update_exception_bitmap (struct kvm_vcpu *vcpu) +{
arch/x86/kvm/vmx/main.c static void vt_vcpu_deliver_init(struct kvm_vcpu *vcpu) · line 397
Quoted 10 lines
arch/x86/kvm/vmx/main.c +10
-0+if (is_td_vcpu (vcpu)) +return ; + +vmx_update_exception_bitmap (vcpu); +} + +static u64 vt_get_segment_base (struct kvm_vcpu *vcpu,int seg) +{ +if (is_td_vcpu (vcpu)) +return tdx_get_segment_base (vcpu, seg);
Could just return 0?
Not need to add a function, since it's only called here and it's more straight forward to read the code without jump to the definition of these functions.
Similarly, we can use open code for tdx_get_cpl(), tdx_get_rflags() and tdx_get_segment(), which return 0 or memset with 0.
Quoted 4 lines
arch/x86/kvm/vmx/main.c +4
-0+ +return vmx_get_segment_base (vcpu, seg); +} +
[...]
Quoted 6 lines
arch/x86/kvm/vmx/main.c +5
-0+int tdx_get_cpl (struct kvm_vcpu *vcpu) +{ +return 0 ; +} +
[...]
Quoted 50 lines
arch/x86/kvm/vmx/main.c +16
-0+ +unsigned long tdx_get_rflags (struct kvm_vcpu *vcpu) +{ +return 0 ; +} + +u64 tdx_get_segment_base (struct kvm_vcpu *vcpu,int seg) +{ +return 0 ; +} + +void tdx_get_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg) +{ +memset (var,0 ,sizeof (*var)); +} +static int tdx_get_capabilities (struct kvm_tdx_cmd *cmd) {struct kvm_tdx_capabilities __user *user_caps;arch/x86/kvm/vmx/x86_ops.h +13
-0@@ -169,6 +169,12 @@ bool tdx_has_emulated_msr(u32 index, bool write);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_get_cpl (struct kvm_vcpu *vcpu); +void tdx_cache_reg (struct kvm_vcpu *vcpu,enum kvm_reg reg); +unsigned long tdx_get_rflags (struct kvm_vcpu *vcpu); +u64 tdx_get_segment_base (struct kvm_vcpu *vcpu,int seg); +void tdx_get_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var,int seg); +int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp);void tdx_flush_tlb (struct kvm_vcpu *vcpu);@@ -221,6 +227,13 @@ static inline bool tdx_has_emulated_msr(u32 index, bool write) { 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_get_cpl (struct kvm_vcpu *vcpu) {return 0 ; } +static inline void tdx_cache_reg (struct kvm_vcpu *vcpu,enum kvm_reg reg) {} +static inline unsigned long tdx_get_rflags (struct kvm_vcpu *vcpu) {return 0 ; } +static inline u64 tdx_get_segment_base (struct kvm_vcpu *vcpu,int seg) {return 0 ; } +static inline void tdx_get_segment (struct kvm_vcpu *vcpu,struct kvm_segment *var, +int seg) {} +static inline int tdx_vcpu_ioctl (struct kvm_vcpu *vcpu,void __user *argp) {return -EOPNOTSUPP ; }static inline void tdx_flush_tlb (struct kvm_vcpu *vcpu) {}
Re: [PATCH v19 118/130] KVM: TDX: Add methods to ignore accesses to CPU state
Quoted 49 lines · Binbin Wu
Quoted 31 lines · On 2/26/2024 4:27 PM, [email protected] wrote:
From: Isaku Yamahata <[email protected]>
TDX protects TDX guest state from VMM. Implement access methods for TDX guest state to ignore them or return zero. Because those methods can be called by kvm ioctls to set/get cpu registers, they don't have
KVM_BUG_ONexcept one method.Signed-off-by Isaku Yamahata <[email protected]>--- arch/x86/kvm/vmx/main.c | 289 +++++++++++++++++++++++++++++++++---- arch/x86/kvm/vmx/tdx.c | 48 +++++- arch/x86/kvm/vmx/x86_ops.h | 13 ++ 3 files changed, 321 insertions(+), 29 deletions(-)arch/x86/kvm/vmx/main.c +10
-0@@ -375,6 +375,200 @@ static void vt_vcpu_deliver_init(struct kvm_vcpu *vcpu)kvm_vcpu_deliver_init (vcpu); } +static void vt_vcpu_after_set_cpuid (struct kvm_vcpu *vcpu) +{ +if (is_td_vcpu (vcpu)) +return ; + +vmx_vcpu_after_set_cpuid (vcpu); +} + +static void vt_update_exception_bitmap (struct kvm_vcpu *vcpu) +{arch/x86/kvm/vmx/main.c static void vt_vcpu_deliver_init(struct kvm_vcpu *vcpu) · line 396
Quoted 10 lines
arch/x86/kvm/vmx/main.c +10
-0+if (is_td_vcpu (vcpu)) +return ; + +vmx_update_exception_bitmap (vcpu); +} + +static u64 vt_get_segment_base (struct kvm_vcpu *vcpu,int seg) +{ +if (is_td_vcpu (vcpu)) +return tdx_get_segment_base (vcpu, seg);Could just return 0?
Not need to add a function, since it's only called here and it's more straight forward to read the code without jump to the definition of these functions.
Quoted 2 lines
Similarly, we can use open code for
tdx_get_cpl(),tdx_get_rflags()andtdx_get_segment(), which return 0 or memset with 0.
Yes, we should drop them. They came from the TDX guest debug. But right now we drop the support and we have guard with vcpu->arch.guest_state_protected.
guest debug is a future topic now.
Signature · 1 line
Isaku Yamahata <[email protected]>
PATCH v19 119/130 KVM: TDX: Add methods to ignore guest instruction emulation
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Because TDX protects TDX guest state from VMM, instructions in guest memory cannot be emulated. Implement methods to ignore guest instruction emulator.
--- arch/x86/kvm/vmx/main.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-)
arch/x86/kvm/vmx/main.c +26 -2
@@ -320,6 +320,30 @@ static void vt_enable_smi_window(struct kvm_vcpu *vcpu) }#endif +static int vt_check_emulate_instruction (struct kvm_vcpu *vcpu,int emul_type, +void *insn,int insn_len) +{ +if (is_td_vcpu (vcpu)) +return X86EMUL_RETRY_INSTR ; + +return vmx_check_emulate_instruction (vcpu, emul_type, insn, insn_len); +} + +static int vt_check_intercept (struct kvm_vcpu *vcpu, +struct x86_instruction_info *info, +enum x86_intercept_stage stage, +struct x86_exception *exception) +{ +/* +* This call back is triggered by the x86 instruction emulator. TDX +* doesn't allow guest memory inspection. +*/ +if (KVM_BUG_ON (is_td_vcpu (vcpu), vcpu->kvm)) +return X86EMUL_UNHANDLEABLE ; + +return vmx_check_intercept (vcpu, info, stage, exception); +} +static bool vt_apic_init_signal_blocked (struct kvm_vcpu *vcpu) {if (is_td_vcpu (vcpu))
@@ -976,7 +1000,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .load_mmu_pgd = vt_load_mmu_pgd, - .check_intercept = vmx_check_intercept, + .check_intercept = vt_check_intercept, .handle_exit_irqoff = vt_handle_exit_irqoff, .request_immediate_exit = vt_request_immediate_exit,
@@ -1005,7 +1029,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .enable_smi_window = vt_enable_smi_window, #endif - .check_emulate_instruction = vmx_check_emulate_instruction, + .check_emulate_instruction = vt_check_emulate_instruction, .apic_init_signal_blocked = vt_apic_init_signal_blocked, .migrate_timers = vmx_migrate_timers,
Signature · 1 line
2.25.1
PATCH v19 120/130 KVM: TDX: Add a method to ignore dirty logging
In reply to [email protected]From: Isaku Yamahata <[email protected]>
Currently TDX KVM doesn't support tracking dirty pages (yet). Implement a method to ignore it. Because the flag for kvm memory slot to enable dirty logging isn't accepted for TDX, warn on the method is called for TDX.
--- arch/x86/kvm/vmx/main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
arch/x86/kvm/vmx/main.c +9 -1
@@ -834,6 +834,14 @@ static u8 vt_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)return vmx_get_mt_mask (vcpu, gfn, is_mmio); } +static void vt_update_cpu_dirty_logging (struct kvm_vcpu *vcpu) +{ +if (KVM_BUG_ON (is_td_vcpu (vcpu), vcpu->kvm)) +return ; + +vmx_update_cpu_dirty_logging (vcpu); +} +static int vt_mem_enc_ioctl (struct kvm *kvm,void __user *argp) {if (!is_td (kvm))
@@ -1008,7 +1016,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { .sched_in = vt_sched_in, .cpu_dirty_log_size = PML_ENTITY_NUM, - .update_cpu_dirty_logging = vmx_update_cpu_dirty_logging, + .update_cpu_dirty_logging = vt_update_cpu_dirty_logging, .nested_ops = &vmx_nested_ops,
Signature · 1 line
2.25.1
Edgecombe, Rick P
maintainerRe: [PATCH v19 120/130] KVM: TDX: Add a method to ignore dirty logging
arch/x86/kvm/vmx/main.c static u8 vt_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) · line 843
Quoted 8 lines · On Mon, 2024-02-26 at 00:27 -0800, [email protected] wrote:
arch/x86/kvm/vmx/main.c +7
-0+static void vt_update_cpu_dirty_logging (struct kvm_vcpu *vcpu) +{ + if (KVM_BUG_ON(is_td_vcpu(vcpu), vcpu->kvm)) + return; + + vmx_update_cpu_dirty_logging(vcpu); +}
Discussed this first part offline, but logging it here. Since guest_memfd cannot have dirty logging, this is essentially bugging the VM if somehow they manage anyway. But it should be blocked via the code in check_memory_region_flags().
On the subject of warnings and KVM_BUG_ON(), my feeling so far is that this series is quite aggressive about these. Is it due the complexity of the series? I think maybe we can remove some of the simple ones, but not sure if there was already some discussion on what level is appropriate.
Re: [PATCH v19 120/130] KVM: TDX: Add a method to ignore dirty logging
Quoted 14 lines · Rick P
arch/x86/kvm/vmx/main.c static u8 vt_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) · line 843
Quoted 8 lines · On Mon, 2024-02-26 at 00:27 -0800, [email protected] wrote:
arch/x86/kvm/vmx/main.c +7
-0+static void vt_update_cpu_dirty_logging (struct kvm_vcpu *vcpu) +{ + if (KVM_BUG_ON(is_td_vcpu(vcpu), vcpu->kvm)) + return; + + vmx_update_cpu_dirty_logging(vcpu); +}Discussed this first part offline, but logging it here. Since
guest_memfdcannot have dirty logging, this is essentially bugging the VM if somehow they manage anyway. But it should be blocked via the code incheck_memory_region_flags().
Will drop this patch.
Quoted 5 lines
On the subject of warnings and
KVM_BUG_ON(), my feeling so far is that this series is quite aggressive about these. Is it due the complexity of the series? I think maybe we can remove some of the simple ones, but not sure if there was already some discussion on what level is appropriate.
KVM_BUG_ON() was helpful at the early stage. Because we don't hit them recently, it's okay to remove them. Will remove them.
Signature · 1 line
Isaku Yamahata <[email protected]>
Edgecombe, Rick P
maintainerRe: [PATCH v19 120/130] KVM: TDX: Add a method to ignore dirty logging
Quoted 9 lines · Isaku Yamahata
Quoted 8 lines
On the subject of warnings and
KVM_BUG_ON(), my feeling so far is that
this series is quite aggressive about these. Is it due the complexity
of the series? I think maybe we can remove some of the simple ones, but
not sure if there was already some discussion on what level is appropriate.
Quoted 3 lines
KVM_BUG_ON()was helpful at the early stage. Because we don't hit them
recently, it's okay to remove them. Will remove them.
Hmm. We probably need to do it case by case.
Re: [PATCH v19 120/130] KVM: TDX: Add a method to ignore dirty logging
Quoted 10 lines · Rick P
Quoted 9 lines · Isaku Yamahata
Quoted 8 lines
On the subject of warnings and
KVM_BUG_ON(), my feeling so far is that
this series is quite aggressive about these. Is it due the complexity
of the series? I think maybe we can remove some of the simple ones, but
not sure if there was already some discussion on what level is appropriate.
Quoted 5 lines
Quoted 3 lines
KVM_BUG_ON()was helpful at the early stage. Because we don't hit them
recently, it's okay to remove them. Will remove them.Hmm. We probably need to do it case by case.
I categorize as follows. Unless otherwise, I'll update this series.
- dirty log check
As we will drop this ptach, we'll have no call site.
- KVM_BUG_ON() in main.c
We should drop them because their logic isn't complex.
- KVM_BUG_ON() in tdx.c
- The error check of the return value from SEAMCALL
We should keep it as it's unexpected error from TDX module. When we hit this, we should mark the guest bugged and prevent further operation. It's hard to deduce the reason. TDX mdoule might be broken.
- Other check
We should drop them.
Signature · 1 line
Isaku Yamahata <[email protected]>
Edgecombe, Rick P
maintainerRe: [PATCH v19 120/130] KVM: TDX: Add a method to ignore dirty logging
Quoted 7 lines · Isaku Yamahata
I categorize as follows. Unless otherwise, I'll update this series.
- dirty log check
As we will drop this ptach, we'll have no call site.-
KVM_BUG_ON()inmain.c
We should drop them because their logic isn't complex.
What about "KVM: TDX: Add methods to ignore guest instruction emulation"? Is it cleanly blocked somehow?
Quoted 8 lines
-KVM_BUG_ON()intdx.c
- The error check of the return value from SEAMCALL
We should keep it as it's unexpected error from TDX module. When we hit
this, we should mark the guest bugged and prevent further operation. It's
hard to deduce the reason. TDX mdoule might be broken.
Yes. Makes sense.
Quoted 3 lines
- Other check
We should drop them.
Offhand, I'm not sure what is in this category.