diff mbox series

[v3,2/2] KVM: SEV: Configure "ALLOWED_SEV_FEATURES" VMCB Field

Message ID 20250207233410.130813-3-kim.phillips@amd.com (mailing list archive)
State New
Headers show
Series KVM: SEV: Add support for the ALLOWED_SEV_FEATURES feature | expand

Commit Message

Kim Phillips Feb. 7, 2025, 11:34 p.m. UTC
AMD EPYC 5th generation processors have introduced a feature that allows
the hypervisor to control the SEV_FEATURES that are set for, or by, a
guest [1].  ALLOWED_SEV_FEATURES can be used by the hypervisor to enforce
that SEV-ES and SEV-SNP guests cannot enable features that the
hypervisor does not want to be enabled.

When ALLOWED_SEV_FEATURES is enabled, a VMRUN will fail if any
non-reserved bits are 1 in SEV_FEATURES but are 0 in
ALLOWED_SEV_FEATURES.

Some SEV_FEATURES - currently PmcVirtualization and SecureAvic
(see Appendix B, Table B-4) - require an opt-in via ALLOWED_SEV_FEATURES,
i.e. are off-by-default, whereas all other features are effectively
on-by-default, but still honor ALLOWED_SEV_FEATURES.

[1] Section 15.36.20 "Allowed SEV Features", AMD64 Architecture
    Programmer's Manual, Pub. 24593 Rev. 3.42 - March 2024:
    https://bugzilla.kernel.org/attachment.cgi?id=306250

Co-developed-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Signed-off-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Signed-off-by: Kim Phillips <kim.phillips@amd.com>
---
 arch/x86/include/asm/svm.h |  5 ++++-
 arch/x86/kvm/svm/sev.c     | 17 +++++++++++++++++
 2 files changed, 21 insertions(+), 1 deletion(-)

Comments

Tom Lendacky Feb. 10, 2025, 6:08 p.m. UTC | #1
On 2/7/25 17:34, Kim Phillips wrote:
> AMD EPYC 5th generation processors have introduced a feature that allows
> the hypervisor to control the SEV_FEATURES that are set for, or by, a
> guest [1].  ALLOWED_SEV_FEATURES can be used by the hypervisor to enforce
> that SEV-ES and SEV-SNP guests cannot enable features that the
> hypervisor does not want to be enabled.
> 
> When ALLOWED_SEV_FEATURES is enabled, a VMRUN will fail if any
> non-reserved bits are 1 in SEV_FEATURES but are 0 in
> ALLOWED_SEV_FEATURES.
> 
> Some SEV_FEATURES - currently PmcVirtualization and SecureAvic
> (see Appendix B, Table B-4) - require an opt-in via ALLOWED_SEV_FEATURES,
> i.e. are off-by-default, whereas all other features are effectively
> on-by-default, but still honor ALLOWED_SEV_FEATURES.
> 
> [1] Section 15.36.20 "Allowed SEV Features", AMD64 Architecture
>     Programmer's Manual, Pub. 24593 Rev. 3.42 - March 2024:
>     https://bugzilla.kernel.org/attachment.cgi?id=306250
> 
> Co-developed-by: Kishon Vijay Abraham I <kvijayab@amd.com>
> Signed-off-by: Kishon Vijay Abraham I <kvijayab@amd.com>
> Signed-off-by: Kim Phillips <kim.phillips@amd.com>
> ---
>  arch/x86/include/asm/svm.h |  5 ++++-
>  arch/x86/kvm/svm/sev.c     | 17 +++++++++++++++++
>  2 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
> index e2fac21471f5..6d94a727cc1a 100644
> --- a/arch/x86/include/asm/svm.h
> +++ b/arch/x86/include/asm/svm.h
> @@ -158,7 +158,9 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
>  	u64 avic_physical_id;	/* Offset 0xf8 */
>  	u8 reserved_7[8];
>  	u64 vmsa_pa;		/* Used for an SEV-ES guest */
> -	u8 reserved_8[720];
> +	u8 reserved_8[40];
> +	u64 allowed_sev_features;	/* Offset 0x138 */
> +	u8 reserved_9[672];
>  	/*
>  	 * Offset 0x3e0, 32 bytes reserved
>  	 * for use by hypervisor/software.
> @@ -289,6 +291,7 @@ static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_
>  #define SVM_SEV_FEAT_RESTRICTED_INJECTION		BIT(3)
>  #define SVM_SEV_FEAT_ALTERNATE_INJECTION		BIT(4)
>  #define SVM_SEV_FEAT_DEBUG_SWAP				BIT(5)
> +#define SVM_SEV_FEAT_ALLOWED_SEV_FEATURES		BIT_ULL(63)

Hmmm... I believe it is safe to define this bit value, as the Allowed
SEV features VMCB field shows bits 61:0 being used for the allowed
features mask and we know that the SEV_FEATURES field is used in the SEV
Features MSR left-shifted 2 bits, so we only expect bits 61:0 to be used
and bits 62 and 63 will always be reserved. But, given that I think we
need two functions:

- get_allowed_sev_features()
  keeping it as you have it below, where it returns the
  sev->vmsa_features bitmap if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES is set
  or 0 if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES is not set.

- get_vmsa_sev_features()
  which removes the SVM_SEV_FEAT_ALLOWED_SEV_FEATURES bit, since it is
  not defined in the VMSA SEV_FEATURES definition.

>  
>  #define SVM_SEV_FEAT_INT_INJ_MODES		\
>  	(SVM_SEV_FEAT_RESTRICTED_INJECTION |	\
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index a2a794c32050..a9e16792cac0 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -894,9 +894,19 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
>  	return 0;
>  }
>  
> +static u64 allowed_sev_features(struct kvm_sev_info *sev)
> +{
> +	if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&

Not sure if the cpu_feature_enabled() check is necessary, as init should
have failed if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES wasn't set in
sev_supported_vmsa_features.

Thanks,
Tom

> +	    (sev->vmsa_features & SVM_SEV_FEAT_ALLOWED_SEV_FEATURES))
> +		return sev->vmsa_features;
> +
> +	return 0;
> +}
> +
>  static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  				    int *error)
>  {
> +	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
>  	struct sev_data_launch_update_vmsa vmsa;
>  	struct vcpu_svm *svm = to_svm(vcpu);
>  	int ret;
> @@ -906,6 +916,8 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
>  		return -EINVAL;
>  	}
>  
> +	svm->vmcb->control.allowed_sev_features = allowed_sev_features(sev);
> +
>  	/* Perform some pre-encryption checks against the VMSA */
>  	ret = sev_es_sync_vmsa(svm);
>  	if (ret)
> @@ -2447,6 +2459,8 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
>  		struct vcpu_svm *svm = to_svm(vcpu);
>  		u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT;
>  
> +		svm->vmcb->control.allowed_sev_features = allowed_sev_features(sev);
> +
>  		ret = sev_es_sync_vmsa(svm);
>  		if (ret)
>  			return ret;
> @@ -3069,6 +3083,9 @@ void __init sev_hardware_setup(void)
>  	sev_supported_vmsa_features = 0;
>  	if (sev_es_debug_swap_enabled)
>  		sev_supported_vmsa_features |= SVM_SEV_FEAT_DEBUG_SWAP;
> +
> +	if (sev_es_enabled && cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES))
> +		sev_supported_vmsa_features |= SVM_SEV_FEAT_ALLOWED_SEV_FEATURES;
>  }
>  
>  void sev_hardware_unsetup(void)
Sean Christopherson Feb. 11, 2025, 9:46 p.m. UTC | #2
On Mon, Feb 10, 2025, Tom Lendacky wrote:
> On 2/7/25 17:34, Kim Phillips wrote:
> > @@ -289,6 +291,7 @@ static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_
> >  #define SVM_SEV_FEAT_RESTRICTED_INJECTION		BIT(3)
> >  #define SVM_SEV_FEAT_ALTERNATE_INJECTION		BIT(4)
> >  #define SVM_SEV_FEAT_DEBUG_SWAP				BIT(5)
> > +#define SVM_SEV_FEAT_ALLOWED_SEV_FEATURES		BIT_ULL(63)
> 
> Hmmm... I believe it is safe to define this bit value, as the Allowed
> SEV features VMCB field shows bits 61:0 being used for the allowed
> features mask and we know that the SEV_FEATURES field is used in the SEV
> Features MSR left-shifted 2 bits, so we only expect bits 61:0 to be used
> and bits 62 and 63 will always be reserved. But, given that I think we
> need two functions:
> 
> - get_allowed_sev_features()
>   keeping it as you have it below, where it returns the
>   sev->vmsa_features bitmap if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES is set
>   or 0 if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES is not set.
> 
> - get_vmsa_sev_features()
>   which removes the SVM_SEV_FEAT_ALLOWED_SEV_FEATURES bit, since it is
>   not defined in the VMSA SEV_FEATURES definition.

Or just don't add wrappers that do more harm than good?

diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index a9e16792cac0..4d0b5a020b65 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -894,15 +894,6 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
        return 0;
 }
 
-static u64 allowed_sev_features(struct kvm_sev_info *sev)
-{
-       if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&
-           (sev->vmsa_features & SVM_SEV_FEAT_ALLOWED_SEV_FEATURES))
-               return sev->vmsa_features;
-
-       return 0;
-}
-
 static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
                                    int *error)
 {
@@ -916,7 +907,8 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
                return -EINVAL;
        }
 
-       svm->vmcb->control.allowed_sev_features = allowed_sev_features(sev);
+       if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES))
+               svm->vmcb->control.allowed_sev_features = sev->vmsa_features;
 
        /* Perform some pre-encryption checks against the VMSA */
        ret = sev_es_sync_vmsa(svm);
@@ -2459,7 +2451,8 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
                struct vcpu_svm *svm = to_svm(vcpu);
                u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT;
 
-               svm->vmcb->control.allowed_sev_features = allowed_sev_features(sev);
+               if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES))
+                       svm->vmcb->control.allowed_sev_features = sev->vmsa_features;
 
                ret = sev_es_sync_vmsa(svm);
                if (ret)

> >  #define SVM_SEV_FEAT_INT_INJ_MODES		\
> >  	(SVM_SEV_FEAT_RESTRICTED_INJECTION |	\
> > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> > index a2a794c32050..a9e16792cac0 100644
> > --- a/arch/x86/kvm/svm/sev.c
> > +++ b/arch/x86/kvm/svm/sev.c
> > @@ -894,9 +894,19 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
> >  	return 0;
> >  }
> >  
> > +static u64 allowed_sev_features(struct kvm_sev_info *sev)
> > +{
> > +	if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&
> 
> Not sure if the cpu_feature_enabled() check is necessary, as init should
> have failed if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES wasn't set in
> sev_supported_vmsa_features.

Two things missing from this series:

 1: KVM enforcement.  No way is KVM going to rely on userspace to opt-in to
    preventing the guest from enabling features.

 2: Backwards compatilibity if KVM unconditionally enforces ALLOWED_SEV_FEATURES.
    Although maybe there's nothing to do here?  I vaguely recall all of the gated
    features being unsupported, or something...
Kim Phillips Feb. 13, 2025, 11:03 p.m. UTC | #3
On 2/11/25 3:46 PM, Sean Christopherson wrote:
> On Mon, Feb 10, 2025, Tom Lendacky wrote:
>> On 2/7/25 17:34, Kim Phillips wrote:
>>> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
>>> index a2a794c32050..a9e16792cac0 100644
>>> --- a/arch/x86/kvm/svm/sev.c
>>> +++ b/arch/x86/kvm/svm/sev.c
>>> @@ -894,9 +894,19 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
>>>   	return 0;
>>>   }
>>>   
>>> +static u64 allowed_sev_features(struct kvm_sev_info *sev)
>>> +{
>>> +	if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&
>>
>> Not sure if the cpu_feature_enabled() check is necessary, as init should
>> have failed if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES wasn't set in
>> sev_supported_vmsa_features.
> 
> Two things missing from this series:
> 
>   1: KVM enforcement.  No way is KVM going to rely on userspace to opt-in to
>      preventing the guest from enabling features.
>   2: Backwards compatilibity if KVM unconditionally enforces ALLOWED_SEV_FEATURES.
>      Although maybe there's nothing to do here?  I vaguely recall all of the gated
>      features being unsupported, or something...

This contradicts your review comment from the previous version of the series [1].

If KVM enforces ALLOWED_SEV_FEATURES, it can break existing VMs, thus
the explicit userspace allowed-sev-features=on opt-in [2].

Thanks,

Kim

[1] https://lore.kernel.org/kvm/ZsfKYHFkWA-Rh23C@google.com/
[2] https://lore.kernel.org/kvm/20250207233327.130770-1-kim.phillips@amd.com/
Sean Christopherson Feb. 14, 2025, 12:55 a.m. UTC | #4
On Thu, Feb 13, 2025, Kim Phillips wrote:
> On 2/11/25 3:46 PM, Sean Christopherson wrote:
> > On Mon, Feb 10, 2025, Tom Lendacky wrote:
> > > On 2/7/25 17:34, Kim Phillips wrote:
> > > > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> > > > index a2a794c32050..a9e16792cac0 100644
> > > > --- a/arch/x86/kvm/svm/sev.c
> > > > +++ b/arch/x86/kvm/svm/sev.c
> > > > @@ -894,9 +894,19 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
> > > >   	return 0;
> > > >   }
> > > > +static u64 allowed_sev_features(struct kvm_sev_info *sev)
> > > > +{
> > > > +	if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&
> > > 
> > > Not sure if the cpu_feature_enabled() check is necessary, as init should
> > > have failed if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES wasn't set in
> > > sev_supported_vmsa_features.
> > 
> > Two things missing from this series:
> > 
> >   1: KVM enforcement.  No way is KVM going to rely on userspace to opt-in to
> >      preventing the guest from enabling features.
> >   2: Backwards compatilibity if KVM unconditionally enforces ALLOWED_SEV_FEATURES.
> >      Although maybe there's nothing to do here?  I vaguely recall all of the gated
> >      features being unsupported, or something...
> 
> This contradicts your review comment from the previous version of the series [1].

First off, my comment was anything but decisive.  I don't see how anyone can read
this and come away thinking "this is exactly what Sean wants".

  This may need additional uAPI so that userspace can opt-in.  Dunno.  I hope guests
  aren't abusing features, but IIUC, flipping this on has the potential to break
  existing VMs, correct?

Second, there's a clear question there that went unanswered.  Respond to questions
and elaborate as needed until we're all on the same page.  Don't just send patches.

Third, letting userspace opt-in to something doesn't necessarily mean giving
userspace full control.  Which is the entire reason I asked the question about
whether or not this can break userspace.  E.g. we can likely get away with only
making select features opt-in, and enforcing everything else by default.

I don't think RESTRICTED_INJECTION or ALTERNATE_INJECTION can work without KVM
cooperation, so enforcing those shouldn't break anything.

It's still not clear to me that we don't have a bug with DEBUG_SWAP.  AIUI,
DEBUG_SWAP is allowed by default.  I.e. if ALLOWED_FEATURES is unsupported, then
the guest can use DEBUG_SWAP via SVM_VMGEXIT_AP_CREATE without KVM's knowledge.

So _maybe_ we have to let userspace opt-in to enforcing DEBUG_SWAP, but I suspect
we can get away with fully enabling ALLOWED_FEATURES without userspace's blessing.

> If KVM enforces ALLOWED_SEV_FEATURES, it can break existing VMs, thus
> the explicit userspace allowed-sev-features=on opt-in [2].
> 
> Thanks,
> 
> Kim
> 
> [1] https://lore.kernel.org/kvm/ZsfKYHFkWA-Rh23C@google.com/
> [2] https://lore.kernel.org/kvm/20250207233327.130770-1-kim.phillips@amd.com/
Kim Phillips Feb. 14, 2025, 9:59 p.m. UTC | #5
On 2/13/25 6:55 PM, Sean Christopherson wrote:
> On Thu, Feb 13, 2025, Kim Phillips wrote:
>> On 2/11/25 3:46 PM, Sean Christopherson wrote:
>>> On Mon, Feb 10, 2025, Tom Lendacky wrote:
>>>> On 2/7/25 17:34, Kim Phillips wrote:
>>>>> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
>>>>> index a2a794c32050..a9e16792cac0 100644
>>>>> --- a/arch/x86/kvm/svm/sev.c
>>>>> +++ b/arch/x86/kvm/svm/sev.c
>>>>> @@ -894,9 +894,19 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm)
>>>>>    	return 0;
>>>>>    }
>>>>> +static u64 allowed_sev_features(struct kvm_sev_info *sev)
>>>>> +{
>>>>> +	if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&
>>>>
>>>> Not sure if the cpu_feature_enabled() check is necessary, as init should
>>>> have failed if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES wasn't set in
>>>> sev_supported_vmsa_features.
>>>
>>> Two things missing from this series:
>>>
>>>    1: KVM enforcement.  No way is KVM going to rely on userspace to opt-in to
>>>       preventing the guest from enabling features.
>>>    2: Backwards compatilibity if KVM unconditionally enforces ALLOWED_SEV_FEATURES.
>>>       Although maybe there's nothing to do here?  I vaguely recall all of the gated
>>>       features being unsupported, or something...
>>
>> This contradicts your review comment from the previous version of the series [1].
> 
> First off, my comment was anything but decisive.  I don't see how anyone can read
> this and come away thinking "this is exactly what Sean wants".
> 
>    This may need additional uAPI so that userspace can opt-in.  Dunno.  I hope guests
>    aren't abusing features, but IIUC, flipping this on has the potential to break
>    existing VMs, correct?
> 
> Second, there's a clear question there that went unanswered.  Respond to questions
> and elaborate as needed until we're all on the same page.  Don't just send patches.
> 
> Third, letting userspace opt-in to something doesn't necessarily mean giving
> userspace full control.  Which is the entire reason I asked the question about
> whether or not this can break userspace.  E.g. we can likely get away with only
> making select features opt-in, and enforcing everything else by default.
> 
> I don't think RESTRICTED_INJECTION or ALTERNATE_INJECTION can work without KVM
> cooperation, so enforcing those shouldn't break anything.
> 
> It's still not clear to me that we don't have a bug with DEBUG_SWAP.  AIUI,
> DEBUG_SWAP is allowed by default.  I.e. if ALLOWED_FEATURES is unsupported, then
> the guest can use DEBUG_SWAP via SVM_VMGEXIT_AP_CREATE without KVM's knowledge.
> 
> So _maybe_ we have to let userspace opt-in to enforcing DEBUG_SWAP, but I suspect
> we can get away with fully enabling ALLOWED_FEATURES without userspace's blessing.

If I hardcode DEBUG_SWAP (bit 5) in the vmsa->sev_features assignment
in wakeup_cpu_via_vmgexit(), such guest boots successfully with the
kvm_amd module's debug_swap parameter set.

The guest *doesn't* boot if I also turn on allowed_sev_features=1 with
qemu and this patchseries.

So, the answer is yes, always enforcing ALLOWED_SEV_FEATURES does break
existing guests, thus the userspace opt-in for it.

Thanks,

Kim

> 
>> If KVM enforces ALLOWED_SEV_FEATURES, it can break existing VMs, thus
>> the explicit userspace allowed-sev-features=on opt-in [2].
>>
>> Thanks,
>>
>> Kim
>>
>> [1] https://lore.kernel.org/kvm/ZsfKYHFkWA-Rh23C@google.com/
>> [2] https://lore.kernel.org/kvm/20250207233327.130770-1-kim.phillips@amd.com/
Naveen N Rao (AMD) Feb. 17, 2025, 6:43 a.m. UTC | #6
On Thu, Feb 13, 2025 at 04:55:13PM -0800, Sean Christopherson wrote:
> On Thu, Feb 13, 2025, Kim Phillips wrote:
> > On 2/11/25 3:46 PM, Sean Christopherson wrote:
> > > On Mon, Feb 10, 2025, Tom Lendacky wrote:
> > > > On 2/7/25 17:34, Kim Phillips wrote:
> 
> Third, letting userspace opt-in to something doesn't necessarily mean giving
> userspace full control.  Which is the entire reason I asked the question about
> whether or not this can break userspace.  E.g. we can likely get away with only
> making select features opt-in, and enforcing everything else by default.
> 
> I don't think RESTRICTED_INJECTION or ALTERNATE_INJECTION can work without KVM
> cooperation, so enforcing those shouldn't break anything.
> 
> It's still not clear to me that we don't have a bug with DEBUG_SWAP.  AIUI,
> DEBUG_SWAP is allowed by default.  I.e. if ALLOWED_FEATURES is unsupported, then
> the guest can use DEBUG_SWAP via SVM_VMGEXIT_AP_CREATE without KVM's knowledge.

In sev_es_prepare_switch_to_guest(), we save host debug register state 
(DR0-DR3) only if KVM is aware of DEBUG_SWAP being enabled in the guest 
(via vmsa_features). So, from what I can tell, it looks like the guest 
will end up overwriting host state if it enables DEBUG_SWAP without 
KVM's knowledge?

Not sure if that's reason enough to enforce ALLOWED_SEV_FEATURES for 
DEBUG_SWAP :)

If ALLOWED_SEV_FEATURES is not supported, we may still have to 
unconditionally save the host DR0-DR3 registers.


- Naveen
Sean Christopherson Feb. 18, 2025, 4:38 p.m. UTC | #7
On Mon, Feb 17, 2025, Naveen N Rao wrote:
> On Thu, Feb 13, 2025 at 04:55:13PM -0800, Sean Christopherson wrote:
> > On Thu, Feb 13, 2025, Kim Phillips wrote:
> > > On 2/11/25 3:46 PM, Sean Christopherson wrote:
> > > > On Mon, Feb 10, 2025, Tom Lendacky wrote:
> > > > > On 2/7/25 17:34, Kim Phillips wrote:
> > 
> > Third, letting userspace opt-in to something doesn't necessarily mean giving
> > userspace full control.  Which is the entire reason I asked the question about
> > whether or not this can break userspace.  E.g. we can likely get away with only
> > making select features opt-in, and enforcing everything else by default.
> > 
> > I don't think RESTRICTED_INJECTION or ALTERNATE_INJECTION can work without KVM
> > cooperation, so enforcing those shouldn't break anything.
> > 
> > It's still not clear to me that we don't have a bug with DEBUG_SWAP.  AIUI,
> > DEBUG_SWAP is allowed by default.  I.e. if ALLOWED_FEATURES is unsupported, then
> > the guest can use DEBUG_SWAP via SVM_VMGEXIT_AP_CREATE without KVM's knowledge.
> 
> In sev_es_prepare_switch_to_guest(), we save host debug register state 
> (DR0-DR3) only if KVM is aware of DEBUG_SWAP being enabled in the guest 
> (via vmsa_features). So, from what I can tell, it looks like the guest 
> will end up overwriting host state if it enables DEBUG_SWAP without 
> KVM's knowledge?

Yes, that's what I'm effectively "asking".

> Not sure if that's reason enough to enforce ALLOWED_SEV_FEATURES for 
> DEBUG_SWAP :)
> 
> If ALLOWED_SEV_FEATURES is not supported, we may still have to 
> unconditionally save the host DR0-DR3 registers.

Yes, that's my understanding of the situation.  If the CPU supports DEBUG_SWAP,
KVM must assume the guest can enable it without KVM's knowledge.
Sean Christopherson Feb. 18, 2025, 5:07 p.m. UTC | #8
On Fri, Feb 14, 2025, Kim Phillips wrote:
> On 2/13/25 6:55 PM, Sean Christopherson wrote:
> > On Thu, Feb 13, 2025, Kim Phillips wrote:
> > > > > Not sure if the cpu_feature_enabled() check is necessary, as init should
> > > > > have failed if SVM_SEV_FEAT_ALLOWED_SEV_FEATURES wasn't set in
> > > > > sev_supported_vmsa_features.
> > > > 
> > > > Two things missing from this series:
> > > > 
> > > >    1: KVM enforcement.  No way is KVM going to rely on userspace to opt-in to
> > > >       preventing the guest from enabling features.
> > > >    2: Backwards compatilibity if KVM unconditionally enforces ALLOWED_SEV_FEATURES.
> > > >       Although maybe there's nothing to do here?  I vaguely recall all of the gated
> > > >       features being unsupported, or something...
> > > 
> > > This contradicts your review comment from the previous version of the series [1].
> > 
> > First off, my comment was anything but decisive.  I don't see how anyone can read
> > this and come away thinking "this is exactly what Sean wants".
> > 
> >    This may need additional uAPI so that userspace can opt-in.  Dunno.  I hope guests
> >    aren't abusing features, but IIUC, flipping this on has the potential to break
> >    existing VMs, correct?
> > 
> > Second, there's a clear question there that went unanswered.  Respond to questions
> > and elaborate as needed until we're all on the same page.  Don't just send patches.
> > 
> > Third, letting userspace opt-in to something doesn't necessarily mean giving
> > userspace full control.  Which is the entire reason I asked the question about
> > whether or not this can break userspace.  E.g. we can likely get away with only
> > making select features opt-in, and enforcing everything else by default.
> > 
> > I don't think RESTRICTED_INJECTION or ALTERNATE_INJECTION can work without KVM
> > cooperation, so enforcing those shouldn't break anything.
> > 
> > It's still not clear to me that we don't have a bug with DEBUG_SWAP.  AIUI,
> > DEBUG_SWAP is allowed by default.  I.e. if ALLOWED_FEATURES is unsupported, then
> > the guest can use DEBUG_SWAP via SVM_VMGEXIT_AP_CREATE without KVM's knowledge.
> > 
> > So _maybe_ we have to let userspace opt-in to enforcing DEBUG_SWAP, but I suspect
> > we can get away with fully enabling ALLOWED_FEATURES without userspace's blessing.
> 
> If I hardcode DEBUG_SWAP (bit 5) in the vmsa->sev_features assignment
> in wakeup_cpu_via_vmgexit(), such guest boots successfully with the
> kvm_amd module's debug_swap parameter set.
> 
> The guest *doesn't* boot if I also turn on allowed_sev_features=1 with
> qemu and this patchseries.
> 
> So, the answer is yes, always enforcing ALLOWED_SEV_FEATURES does break
> existing guests, thus the userspace opt-in for it.

That is not an "existing" guest.  That's a deliberately misconfigured guest that
serves as testcase/reproducer.  IIUC, the BSP can't enable DEBUG_SWAP through a
backdoor, so I don't think it's at all sane/reasonable for the guest to expect
that enabling DEBUG_SWAP only on APs will function.  Ah, and KVM will still set
the DR7 intercepts, i.e. the guest can't read/write DR7, so this is definitely a
nonsensical/unsupported configuration.

So unless I've missed something, KVM can unconditionally enforce ALLOWED_SEV_FEATURES.
Sean Christopherson Feb. 18, 2025, 6:33 p.m. UTC | #9
On Mon, Feb 17, 2025, Naveen N Rao wrote:
> On Thu, Feb 13, 2025 at 04:55:13PM -0800, Sean Christopherson wrote:
> > On Thu, Feb 13, 2025, Kim Phillips wrote:
> > > On 2/11/25 3:46 PM, Sean Christopherson wrote:
> > > > On Mon, Feb 10, 2025, Tom Lendacky wrote:
> > > > > On 2/7/25 17:34, Kim Phillips wrote:
> > 
> > Third, letting userspace opt-in to something doesn't necessarily mean giving
> > userspace full control.  Which is the entire reason I asked the question about
> > whether or not this can break userspace.  E.g. we can likely get away with only
> > making select features opt-in, and enforcing everything else by default.
> > 
> > I don't think RESTRICTED_INJECTION or ALTERNATE_INJECTION can work without KVM
> > cooperation, so enforcing those shouldn't break anything.
> > 
> > It's still not clear to me that we don't have a bug with DEBUG_SWAP.  AIUI,
> > DEBUG_SWAP is allowed by default.  I.e. if ALLOWED_FEATURES is unsupported, then
> > the guest can use DEBUG_SWAP via SVM_VMGEXIT_AP_CREATE without KVM's knowledge.
> 
> In sev_es_prepare_switch_to_guest(), we save host debug register state 
> (DR0-DR3) only if KVM is aware of DEBUG_SWAP being enabled in the guest 
> (via vmsa_features). So, from what I can tell, it looks like the guest 
> will end up overwriting host state if it enables DEBUG_SWAP without 
> KVM's knowledge?
> 
> Not sure if that's reason enough to enforce ALLOWED_SEV_FEATURES for 
> DEBUG_SWAP :)
> 
> If ALLOWED_SEV_FEATURES is not supported, we may still have to 
> unconditionally save the host DR0-DR3 registers.

Aha!  We do not.  The existing KVM code is actually flawed in the opposite
direction, in that saving host DR0..DR3 during sev_es_prepare_switch_to_guest()
is superfluous.

DR7 is reset on #VMEXIT (swap type "C"), and so KVM only needs to ensure DR0..DR3
are restored before loading DR7 with the host's value.  KVM takes care of that in
common x86 code via hw_breakpoint_restore().

However, there is still a bug, as the AMD-specific *masks* are not restored.  KVM
doesn't support MSR_F16H_DRx_ADDR_MASK emulation or passthrough for normal guests,
so the guest can't set those values either, i.e. will get a #VC.  But the masks
do need to be restored, because the CPU will clobber them with '0' when DebugSwap
is enabled.

And of course the DR0..DR3 loads in sev_es_prepare_switch_to_guest() are a
complete waste of cycles.

*sigh*

Ugh, it gets worse.  The awfulness goes in both direction.  Unless I've misunderstood
how this all works, just because *KVM* enables DebugSwap for the BSP doesn't mean
the guest will enable DebugSwap for APs.  And so KVM _can't_ rely on DebugSwap to
actually swap DR0..DR3, because KVM has no way of knowing whether or not a given
vCPU actually has it enabled.  And that's true even when SEV_ALLOWED_FEATURES
comes along, which means treating DR0..DR3 as swap type B is utterly worthless.

What a mess.  I'll send a small series to try and clean things up.

Also, I told y'all so: https://lore.kernel.org/all/YWnbfCet84Vup6q9@google.com

P.S. Can someone please get the APM updated to actually explain WTF enabling
     Debug Virtualization in SEV_FEATURES does?  The APM does not at all match
     what y'all have told me.  A sane reading of the APM would be that DRs are
     *unconditionally* swap type B when DebugSwap is supported, whereas I've been
     told from the very beginning[*] that treating them as type B requires software
     enabling:

         AMD Milan introduces support for the swapping, as type 'B', of DR[0-3]
         and DR[0-3]_ADDR_MASK registers. It requires that SEV_FEATURES[5] be set
         in the VMSA.

[*] https://lore.kernel.org/all/20221201021948.9259-3-aik@amd.com
diff mbox series

Patch

diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index e2fac21471f5..6d94a727cc1a 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -158,7 +158,9 @@  struct __attribute__ ((__packed__)) vmcb_control_area {
 	u64 avic_physical_id;	/* Offset 0xf8 */
 	u8 reserved_7[8];
 	u64 vmsa_pa;		/* Used for an SEV-ES guest */
-	u8 reserved_8[720];
+	u8 reserved_8[40];
+	u64 allowed_sev_features;	/* Offset 0x138 */
+	u8 reserved_9[672];
 	/*
 	 * Offset 0x3e0, 32 bytes reserved
 	 * for use by hypervisor/software.
@@ -289,6 +291,7 @@  static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_
 #define SVM_SEV_FEAT_RESTRICTED_INJECTION		BIT(3)
 #define SVM_SEV_FEAT_ALTERNATE_INJECTION		BIT(4)
 #define SVM_SEV_FEAT_DEBUG_SWAP				BIT(5)
+#define SVM_SEV_FEAT_ALLOWED_SEV_FEATURES		BIT_ULL(63)
 
 #define SVM_SEV_FEAT_INT_INJ_MODES		\
 	(SVM_SEV_FEAT_RESTRICTED_INJECTION |	\
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index a2a794c32050..a9e16792cac0 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -894,9 +894,19 @@  static int sev_es_sync_vmsa(struct vcpu_svm *svm)
 	return 0;
 }
 
+static u64 allowed_sev_features(struct kvm_sev_info *sev)
+{
+	if (cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES) &&
+	    (sev->vmsa_features & SVM_SEV_FEAT_ALLOWED_SEV_FEATURES))
+		return sev->vmsa_features;
+
+	return 0;
+}
+
 static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
 				    int *error)
 {
+	struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
 	struct sev_data_launch_update_vmsa vmsa;
 	struct vcpu_svm *svm = to_svm(vcpu);
 	int ret;
@@ -906,6 +916,8 @@  static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu,
 		return -EINVAL;
 	}
 
+	svm->vmcb->control.allowed_sev_features = allowed_sev_features(sev);
+
 	/* Perform some pre-encryption checks against the VMSA */
 	ret = sev_es_sync_vmsa(svm);
 	if (ret)
@@ -2447,6 +2459,8 @@  static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
 		struct vcpu_svm *svm = to_svm(vcpu);
 		u64 pfn = __pa(svm->sev_es.vmsa) >> PAGE_SHIFT;
 
+		svm->vmcb->control.allowed_sev_features = allowed_sev_features(sev);
+
 		ret = sev_es_sync_vmsa(svm);
 		if (ret)
 			return ret;
@@ -3069,6 +3083,9 @@  void __init sev_hardware_setup(void)
 	sev_supported_vmsa_features = 0;
 	if (sev_es_debug_swap_enabled)
 		sev_supported_vmsa_features |= SVM_SEV_FEAT_DEBUG_SWAP;
+
+	if (sev_es_enabled && cpu_feature_enabled(X86_FEATURE_ALLOWED_SEV_FEATURES))
+		sev_supported_vmsa_features |= SVM_SEV_FEAT_ALLOWED_SEV_FEATURES;
 }
 
 void sev_hardware_unsetup(void)