On Tue, Apr 15, 2025 at 02:51:22PM +0300, Elena Reshetova wrote:
> SGX architecture introduced a new instruction called EUPDATESVN
> to Ice Lake. It allows updating security SVN version, given that EPC
> is completely empty. The latter is required for security reasons
> in order to reason that enclave security posture is as secure as the
> security SVN version of the TCB that created it.

"Ice Lake introduced ENCLS[EUPDATESVN], which allows to to update the
microcode version for an online system" ?

Now you are saying it allows updating SVN which is null information.

> 
> Additionally it is important to ensure that while ENCLS[EUPDATESVN]
> runs, no concurrent page creation happens in EPC, because it might
> result in #GP delivered to the creator. Legacy SW might not be prepared
> to handle such unexpected #GPs and therefore this patch introduces
> a locking mechanism to ensure no concurrent EPC allocations can happen.
> 
> It is also ensured that ENCLS[EUPDATESVN] is not called when running
> in a VM since it does not have a meaning in this context (microcode
> updates application is limited to the host OS) and will create
> unnecessary load.
> 
> This patch is based on previous submision by Cathy Zhang
> https://lore.kernel.org/all/20220520103904.1216-1-cathy.zh...@intel.com/
> 
> Signed-off-by: Elena Reshetova <elena.reshet...@intel.com>
> ---
>  arch/x86/include/asm/sgx.h      | 41 +++++++++++------
>  arch/x86/kernel/cpu/sgx/encls.h |  6 +++
>  arch/x86/kernel/cpu/sgx/main.c  | 82 ++++++++++++++++++++++++++++++++-
>  arch/x86/kernel/cpu/sgx/sgx.h   |  1 +
>  4 files changed, 114 insertions(+), 16 deletions(-)
> 
> diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
> index 6a0069761508..5caf5c31ebc6 100644
> --- a/arch/x86/include/asm/sgx.h
> +++ b/arch/x86/include/asm/sgx.h
> @@ -26,23 +26,26 @@
>  #define SGX_CPUID_EPC_SECTION        0x1
>  /* The bitmask for the EPC section type. */
>  #define SGX_CPUID_EPC_MASK   GENMASK(3, 0)
> +/* EUPDATESVN presence indication */
> +#define SGX_CPUID_EUPDATESVN BIT(10)
>  
>  enum sgx_encls_function {
> -     ECREATE = 0x00,
> -     EADD    = 0x01,
> -     EINIT   = 0x02,
> -     EREMOVE = 0x03,
> -     EDGBRD  = 0x04,
> -     EDGBWR  = 0x05,
> -     EEXTEND = 0x06,
> -     ELDU    = 0x08,
> -     EBLOCK  = 0x09,
> -     EPA     = 0x0A,
> -     EWB     = 0x0B,
> -     ETRACK  = 0x0C,
> -     EAUG    = 0x0D,
> -     EMODPR  = 0x0E,
> -     EMODT   = 0x0F,
> +     ECREATE         = 0x00,
> +     EADD            = 0x01,
> +     EINIT           = 0x02,
> +     EREMOVE         = 0x03,
> +     EDGBRD          = 0x04,
> +     EDGBWR          = 0x05,
> +     EEXTEND         = 0x06,
> +     ELDU            = 0x08,
> +     EBLOCK          = 0x09,
> +     EPA             = 0x0A,
> +     EWB             = 0x0B,
> +     ETRACK          = 0x0C,
> +     EAUG            = 0x0D,
> +     EMODPR          = 0x0E,
> +     EMODT           = 0x0F,
> +     EUPDATESVN      = 0x18,
>  };
>  
>  /**
> @@ -73,6 +76,11 @@ enum sgx_encls_function {
>   *                           public key does not match IA32_SGXLEPUBKEYHASH.
>   * %SGX_PAGE_NOT_MODIFIABLE: The EPC page cannot be modified because it
>   *                           is in the PENDING or MODIFIED state.
> + * %SGX_INSUFFICIENT_ENTROPY:        Insufficient entropy in RNG.
> + * %SGX_EPC_NOT_READY:               EPC is not ready for SVN update.
> + * %SGX_NO_UPDATE:           EUPDATESVN was successful, but CPUSVN was not
> + *                           updated because current SVN was not newer than
> + *                           CPUSVN.
>   * %SGX_UNMASKED_EVENT:              An unmasked event, e.g. INTR, was 
> received
>   */
>  enum sgx_return_code {
> @@ -81,6 +89,9 @@ enum sgx_return_code {
>       SGX_CHILD_PRESENT               = 13,
>       SGX_INVALID_EINITTOKEN          = 16,
>       SGX_PAGE_NOT_MODIFIABLE         = 20,
> +     SGX_INSUFFICIENT_ENTROPY        = 29,
> +     SGX_EPC_NOT_READY               = 30,
> +     SGX_NO_UPDATE                   = 31,
>       SGX_UNMASKED_EVENT              = 128,
>  };
>  
> diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h
> index 99004b02e2ed..3d83c76dc91f 100644
> --- a/arch/x86/kernel/cpu/sgx/encls.h
> +++ b/arch/x86/kernel/cpu/sgx/encls.h
> @@ -233,4 +233,10 @@ static inline int __eaug(struct sgx_pageinfo *pginfo, 
> void *addr)
>       return __encls_2(EAUG, pginfo, addr);
>  }
>  
> +/* Update CPUSVN at runtime. */
> +static inline int __eupdatesvn(void)
> +{
> +     return __encls_ret_1(EUPDATESVN, "");
> +}
> +
>  #endif /* _X86_ENCLS_H */
> diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
> index b61d3bad0446..c21f4f6226b0 100644
> --- a/arch/x86/kernel/cpu/sgx/main.c
> +++ b/arch/x86/kernel/cpu/sgx/main.c
> @@ -32,6 +32,11 @@ static DEFINE_XARRAY(sgx_epc_address_space);
>  static LIST_HEAD(sgx_active_page_list);
>  static DEFINE_SPINLOCK(sgx_reclaimer_lock);
>  
> +/* This lock is held to prevent new EPC pages from being created
> + * during the execution of ENCLS[EUPDATESVN].
> + */
> +static DEFINE_SPINLOCK(sgx_epc_eupdatesvn_lock);
> +
>  static atomic_long_t sgx_nr_used_pages = ATOMIC_LONG_INIT(0);
>  static unsigned long sgx_nr_total_pages;
>  
> @@ -444,6 +449,7 @@ static struct sgx_epc_page 
> *__sgx_alloc_epc_page_from_node(int nid)
>  {
>       struct sgx_numa_node *node = &sgx_numa_nodes[nid];
>       struct sgx_epc_page *page = NULL;
> +     bool ret;
>  
>       spin_lock(&node->lock);
>  
> @@ -452,12 +458,33 @@ static struct sgx_epc_page 
> *__sgx_alloc_epc_page_from_node(int nid)
>               return NULL;
>       }
>  
> +     if (!atomic_long_inc_not_zero(&sgx_nr_used_pages)) {
> +             spin_lock(&sgx_epc_eupdatesvn_lock);
> +             /*
> +              * Only call sgx_updatesvn() once the first enclave's
> +              * page is allocated from EPC
> +              */
> +             if (atomic_long_read(&sgx_nr_used_pages) == 0) {
> +                     ret = sgx_updatesvn();
> +                     if (!ret) {
> +                             /*
> +                              * sgx_updatesvn() returned unknown error, smth
> +                              * must be broken, do not allocate a page from 
> EPC
> +                              */
> +                             spin_unlock(&sgx_epc_eupdatesvn_lock);
> +                             spin_unlock(&node->lock);
> +                             return NULL;
> +                     }
> +             }
> +             atomic_long_inc(&sgx_nr_used_pages);
> +             spin_unlock(&sgx_epc_eupdatesvn_lock);
> +     }
> +
>       page = list_first_entry(&node->free_page_list, struct sgx_epc_page, 
> list);
>       list_del_init(&page->list);
>       page->flags = 0;
>  
>       spin_unlock(&node->lock);
> -     atomic_long_inc(&sgx_nr_used_pages);
>  
>       return page;
>  }
> @@ -970,3 +997,56 @@ static int __init sgx_init(void)
>  }
>  
>  device_initcall(sgx_init);
> +
> +/**
> + * sgx_updatesvn() - Issue ENCLS[EUPDATESVN]
> + * If EPC is ready, this instruction will update CPUSVN to the currently
> + * loaded microcode update SVN and generate new cryptographic assets.
> + *
> + * Return:
> + * True: success or EUPDATESVN can be safely retried next time
> + * False: Unexpected error occurred
> + */
> +bool sgx_updatesvn(void)
> +{
> +     int retry = 10;
> +     int ret;
> +
> +     lockdep_assert_held(&sgx_epc_eupdatesvn_lock);
> +
> +     if (!(cpuid_eax(SGX_CPUID) & SGX_CPUID_EUPDATESVN))
> +             return true;
> +
> +     /*
> +      * Do not execute ENCLS[EUPDATESVN] if running in a VM since
> +      * microcode updates are only meaningful to be applied on the host.
> +      */
> +     if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
> +             return true;
> +
> +     do {
> +             ret = __eupdatesvn();
> +             if (ret != SGX_INSUFFICIENT_ENTROPY)
> +                     break;
> +
> +     } while (--retry);
> +
> +     switch (ret) {
> +     case 0:
> +             pr_info("EUPDATESVN: success\n");
> +             break;
> +     case SGX_EPC_NOT_READY:
> +     case SGX_INSUFFICIENT_ENTROPY:
> +     case SGX_EPC_PAGE_CONFLICT:
> +             ENCLS_WARN(ret, "EUPDATESVN");
> +             break;
> +     case SGX_NO_UPDATE:
> +             break;
> +     default:
> +             ENCLS_WARN(ret, "EUPDATESVN");
> +             return false;

I'm lost why only ENCLS_WARN() is practiced here. For spurious error
code at minimum EPC allocation should be turned off really. Something
is likely working incorrectly in the driver.

> +     }
> +
> +     return true;
> +
> +}
> diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
> index d2dad21259a8..d7d631c973d0 100644
> --- a/arch/x86/kernel/cpu/sgx/sgx.h
> +++ b/arch/x86/kernel/cpu/sgx/sgx.h
> @@ -103,5 +103,6 @@ static inline int __init sgx_vepc_init(void)
>  #endif
>  
>  void sgx_update_lepubkeyhash(u64 *lepubkeyhash);
> +bool sgx_updatesvn(void);
>  
>  #endif /* _X86_SGX_H */
> -- 
> 2.45.2
> 

BR, Jarkko

Reply via email to