KVM/VMX: Allow exposing EDECCSSA user leaf function to KVM guest
authorKai Huang <kai.huang@intel.com>
Tue, 1 Nov 2022 02:24:22 +0000 (15:24 +1300)
committerDave Hansen <dave.hansen@linux.intel.com>
Fri, 4 Nov 2022 22:33:56 +0000 (15:33 -0700)
The new Asynchronous Exit (AEX) notification mechanism (AEX-notify)
allows one enclave to receive a notification in the ERESUME after the
enclave exit due to an AEX.  EDECCSSA is a new SGX user leaf function
(ENCLU[EDECCSSA]) to facilitate the AEX notification handling.  The new
EDECCSSA is enumerated via CPUID(EAX=0x12,ECX=0x0):EAX[11].

Besides Allowing reporting the new AEX-notify attribute to KVM guests,
also allow reporting the new EDECCSSA user leaf function to KVM guests
so the guest can fully utilize the AEX-notify mechanism.

Similar to existing X86_FEATURE_SGX1 and X86_FEATURE_SGX2, introduce a
new scattered X86_FEATURE_SGX_EDECCSSA bit for the new EDECCSSA, and
report it in KVM's supported CPUIDs.

Note, no additional KVM enabling is required to allow the guest to use
EDECCSSA.  It's impossible to trap ENCLU (without completely preventing
the guest from using SGX).  Advertise EDECCSSA as supported purely so
that userspace doesn't need to special case EDECCSSA, i.e. doesn't need
to manually check host CPUID.

The inability to trap ENCLU also means that KVM can't prevent the guest
from using EDECCSSA, but that virtualization hole is benign as far as
KVM is concerned.  EDECCSSA is simply a fancy way to modify internal
enclave state.

More background about how do AEX-notify and EDECCSSA work:

SGX maintains a Current State Save Area Frame (CSSA) for each enclave
thread.  When AEX happens, the enclave thread context is saved to the
CSSA and the CSSA is increased by 1.  For a normal ERESUME which doesn't
deliver AEX notification, it restores the saved thread context from the
previously saved SSA and decreases the CSSA.  If AEX-notify is enabled
for one enclave, the ERESUME acts differently.  Instead of restoring the
saved thread context and decreasing the CSSA, it acts like EENTER which
doesn't decrease the CSSA but establishes a clean slate thread context
using the CSSA for the enclave to handle the notification.  After some
handling, the enclave must discard the "new-established" SSA and switch
back to the previously saved SSA (upon AEX).  Otherwise, the enclave
will run out of SSA space upon further AEXs and eventually fail to run.

To solve this problem, the new EDECCSSA essentially decreases the CSSA.
It can be used by the enclave notification handler to switch back to the
previous saved SSA when needed, i.e. after it handles the notification.

Signed-off-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Sean Christopherson <seanjc@google.com>
Acked-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/all/20221101022422.858944-1-kai.huang%40intel.com
arch/x86/include/asm/cpufeatures.h
arch/x86/kernel/cpu/cpuid-deps.c
arch/x86/kernel/cpu/scattered.c
arch/x86/kvm/cpuid.c
arch/x86/kvm/reverse_cpuid.h

index b71f4f2ecdd571a44a808e443118278ea30cbe04..d0d7edd0d6416e4899b88eb2a5b1a07ab8c59686 100644 (file)
 #define X86_FEATURE_UNRET              (11*32+15) /* "" AMD BTB untrain return */
 #define X86_FEATURE_USE_IBPB_FW                (11*32+16) /* "" Use IBPB during runtime firmware calls */
 #define X86_FEATURE_RSB_VMEXIT_LITE    (11*32+17) /* "" Fill RSB on VM exit when EIBRS is enabled */
+#define X86_FEATURE_SGX_EDECCSSA       (11*32+18) /* "" SGX EDECCSSA user leaf function */
 
 /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */
 #define X86_FEATURE_AVX_VNNI           (12*32+ 4) /* AVX VNNI instructions */
index c881bcafba7d702fce65f36c178c3f4a84aa98e9..d9522111712927aad3a6a39f8bc8ccdc9ff46232 100644 (file)
@@ -75,6 +75,7 @@ static const struct cpuid_dep cpuid_deps[] = {
        { X86_FEATURE_SGX_LC,                   X86_FEATURE_SGX       },
        { X86_FEATURE_SGX1,                     X86_FEATURE_SGX       },
        { X86_FEATURE_SGX2,                     X86_FEATURE_SGX1      },
+       { X86_FEATURE_SGX_EDECCSSA,             X86_FEATURE_SGX1      },
        { X86_FEATURE_XFD,                      X86_FEATURE_XSAVES    },
        { X86_FEATURE_XFD,                      X86_FEATURE_XGETBV1   },
        { X86_FEATURE_AMX_TILE,                 X86_FEATURE_XFD       },
index fc01f81f6e2a382b4daccdac85a9fe0e79d95fc2..f53944fb8f7f9541fac485dbe36c3334068f3201 100644 (file)
@@ -40,6 +40,7 @@ static const struct cpuid_bit cpuid_bits[] = {
        { X86_FEATURE_PER_THREAD_MBA,   CPUID_ECX,  0, 0x00000010, 3 },
        { X86_FEATURE_SGX1,             CPUID_EAX,  0, 0x00000012, 0 },
        { X86_FEATURE_SGX2,             CPUID_EAX,  1, 0x00000012, 0 },
+       { X86_FEATURE_SGX_EDECCSSA,     CPUID_EAX, 11, 0x00000012, 0 },
        { X86_FEATURE_HW_PSTATE,        CPUID_EDX,  7, 0x80000007, 0 },
        { X86_FEATURE_CPB,              CPUID_EDX,  9, 0x80000007, 0 },
        { X86_FEATURE_PROC_FEEDBACK,    CPUID_EDX, 11, 0x80000007, 0 },
index 7345bec5380a8ffccae6b13ad02705ebe50995be..44151d2f2fed051c99622bd1700ddc67b37bef9a 100644 (file)
@@ -665,7 +665,7 @@ void kvm_set_cpu_caps(void)
        );
 
        kvm_cpu_cap_init_scattered(CPUID_12_EAX,
-               SF(SGX1) | SF(SGX2)
+               SF(SGX1) | SF(SGX2) | SF(SGX_EDECCSSA)
        );
 
        kvm_cpu_cap_mask(CPUID_8000_0001_ECX,
index a19d473d01847d120543c7d42422809ee8f348b9..4e5b8444f161c3b01b30c2b58d9aa695f0000b4e 100644 (file)
@@ -23,6 +23,7 @@ enum kvm_only_cpuid_leafs {
 /* Intel-defined SGX sub-features, CPUID level 0x12 (EAX). */
 #define KVM_X86_FEATURE_SGX1           KVM_X86_FEATURE(CPUID_12_EAX, 0)
 #define KVM_X86_FEATURE_SGX2           KVM_X86_FEATURE(CPUID_12_EAX, 1)
+#define KVM_X86_FEATURE_SGX_EDECCSSA   KVM_X86_FEATURE(CPUID_12_EAX, 11)
 
 struct cpuid_reg {
        u32 function;
@@ -78,6 +79,8 @@ static __always_inline u32 __feature_translate(int x86_feature)
                return KVM_X86_FEATURE_SGX1;
        else if (x86_feature == X86_FEATURE_SGX2)
                return KVM_X86_FEATURE_SGX2;
+       else if (x86_feature == X86_FEATURE_SGX_EDECCSSA)
+               return KVM_X86_FEATURE_SGX_EDECCSSA;
 
        return x86_feature;
 }