From patchwork Wed Apr 17 10:39:11 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905143 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B811917E0 for ; Wed, 17 Apr 2019 10:39:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A19A82884E for ; Wed, 17 Apr 2019 10:39:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9585828952; Wed, 17 Apr 2019 10:39:55 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 392CC2884E for ; Wed, 17 Apr 2019 10:39:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731553AbfDQKjy (ORCPT ); Wed, 17 Apr 2019 06:39:54 -0400 Received: from mga03.intel.com ([134.134.136.65]:55143 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726695AbfDQKjy (ORCPT ); Wed, 17 Apr 2019 06:39:54 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:39:53 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119678" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:39:48 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Kai Huang , Jarkko Sakkinen Subject: [PATCH v20 01/28] x86/cpufeatures: Add Intel-defined SGX feature bit Date: Wed, 17 Apr 2019 13:39:11 +0300 Message-Id: <20190417103938.7762-2-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kai Huang X86_FEATURE_SGX reflects whether or not the CPU supports Intel's Software Guard eXtensions (SGX). Signed-off-by: Kai Huang Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Reviewed-by: Borislav Petkov --- arch/x86/include/asm/cpufeatures.h | 1 + arch/x86/include/asm/disabled-features.h | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 981ff9479648..a16325db4cff 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -236,6 +236,7 @@ /* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */ #define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/ #define X86_FEATURE_TSC_ADJUST ( 9*32+ 1) /* TSC adjustment MSR 0x3B */ +#define X86_FEATURE_SGX ( 9*32+ 2) /* Software Guard Extensions */ #define X86_FEATURE_BMI1 ( 9*32+ 3) /* 1st group bit manipulation extensions */ #define X86_FEATURE_HLE ( 9*32+ 4) /* Hardware Lock Elision */ #define X86_FEATURE_AVX2 ( 9*32+ 5) /* AVX2 instructions */ diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h index a5ea841cc6d2..74de07d0f390 100644 --- a/arch/x86/include/asm/disabled-features.h +++ b/arch/x86/include/asm/disabled-features.h @@ -62,6 +62,12 @@ # define DISABLE_PTI (1 << (X86_FEATURE_PTI & 31)) #endif +#ifdef CONFIG_INTEL_SGX +# define DISABLE_SGX_CORE 0 +#else +# define DISABLE_SGX_CORE (1 << (X86_FEATURE_SGX & 31)) +#endif + /* * Make sure to add features to the correct mask */ @@ -74,7 +80,7 @@ #define DISABLED_MASK6 0 #define DISABLED_MASK7 (DISABLE_PTI) #define DISABLED_MASK8 0 -#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP) +#define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE) #define DISABLED_MASK10 0 #define DISABLED_MASK11 0 #define DISABLED_MASK12 0 From patchwork Wed Apr 17 10:39:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905199 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9B02B161F for ; Wed, 17 Apr 2019 10:45:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8495B28767 for ; Wed, 17 Apr 2019 10:45:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 78BC5287FD; Wed, 17 Apr 2019 10:45:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D272628767 for ; Wed, 17 Apr 2019 10:45:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728373AbfDQKpF (ORCPT ); Wed, 17 Apr 2019 06:45:05 -0400 Received: from mga17.intel.com ([192.55.52.151]:9667 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726695AbfDQKpF (ORCPT ); Wed, 17 Apr 2019 06:45:05 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:02 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119702" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:39:54 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 02/28] x86/cpufeatures: Add SGX sub-features (as Linux-defined bits) Date: Wed, 17 Apr 2019 13:39:12 +0300 Message-Id: <20190417103938.7762-3-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson CPUID_12_EAX is an Intel-defined feature bits leaf dedicated for SGX that enumerates the SGX instruction sets that are supported by the CPU, e.g. SGX1, SGX2, etc... Because Linux currently only cares about two bits (SGX1 and SGX2) and there are currently only four documented bits in total, relocate the bits to Linux-defined word 8 to conserve space. But, keep the bit positions identical between the Intel-defined value and the Linux-defined value, e.g. keep SGX1 at bit 0. This allows KVM to use its existing code for probing guest CPUID bits using Linux's X86_FEATURE_* definitions. To do so, shift around some existing bits to effectively reserve bits 0-7 of word 8 for SGX sub-features. Signed-off-by: Sean Christopherson Signed-off-by: Jarkko Sakkinen --- arch/x86/include/asm/cpufeatures.h | 22 ++++++++++++++++------ arch/x86/include/asm/disabled-features.h | 6 +++++- arch/x86/kernel/cpu/scattered.c | 2 ++ tools/arch/x86/include/asm/cpufeatures.h | 21 +++++++++++++++------ 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index a16325db4cff..313c58c04b51 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -222,12 +222,22 @@ #define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */ #define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */ -/* Virtualization flags: Linux defined, word 8 */ -#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ -#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */ -#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */ -#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */ -#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */ +/* + * Scattered Intel features: Linux defined, word 8. + * + * Note that the bit location of the SGX features is meaningful as KVM expects + * the Linux defined bit to match the Intel defined bit, e.g. X86_FEATURE_SGX1 + * must remain at bit 0, SGX2 at bit 1, etc... + */ +#define X86_FEATURE_SGX1 ( 8*32+ 0) /* SGX1 leaf functions */ +#define X86_FEATURE_SGX2 ( 8*32+ 1) /* SGX2 leaf functions */ +/* Bits [0:7] are reserved for SGX */ + +#define X86_FEATURE_TPR_SHADOW ( 8*32+ 8) /* Intel TPR Shadow */ +#define X86_FEATURE_VNMI ( 8*32+ 9) /* Intel Virtual NMI */ +#define X86_FEATURE_FLEXPRIORITY ( 8*32+10) /* Intel FlexPriority */ +#define X86_FEATURE_EPT ( 8*32+11) /* Intel Extended Page Table */ +#define X86_FEATURE_VPID ( 8*32+12) /* Intel Virtual Processor ID */ #define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer VMMCALL to VMCALL */ #define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */ diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h index 74de07d0f390..926f9dc4d75a 100644 --- a/arch/x86/include/asm/disabled-features.h +++ b/arch/x86/include/asm/disabled-features.h @@ -34,12 +34,16 @@ # define DISABLE_CYRIX_ARR (1<<(X86_FEATURE_CYRIX_ARR & 31)) # define DISABLE_CENTAUR_MCR (1<<(X86_FEATURE_CENTAUR_MCR & 31)) # define DISABLE_PCID 0 +# define DISABLE_SGX1 0 +# define DISABLE_SGX2 0 #else # define DISABLE_VME 0 # define DISABLE_K6_MTRR 0 # define DISABLE_CYRIX_ARR 0 # define DISABLE_CENTAUR_MCR 0 # define DISABLE_PCID (1<<(X86_FEATURE_PCID & 31)) +# define DISABLE_SGX1 (1<<(X86_FEATURE_SGX1 & 31)) +# define DISABLE_SGX2 (1<<(X86_FEATURE_SGX2 & 31)) #endif /* CONFIG_X86_64 */ #ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS @@ -79,7 +83,7 @@ #define DISABLED_MASK5 0 #define DISABLED_MASK6 0 #define DISABLED_MASK7 (DISABLE_PTI) -#define DISABLED_MASK8 0 +#define DISABLED_MASK8 (DISABLE_SGX1|DISABLE_SGX2) #define DISABLED_MASK9 (DISABLE_MPX|DISABLE_SMAP|DISABLE_SGX_CORE) #define DISABLED_MASK10 0 #define DISABLED_MASK11 0 diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c index 94aa1c72ca98..9a5f6cf947b9 100644 --- a/arch/x86/kernel/cpu/scattered.c +++ b/arch/x86/kernel/cpu/scattered.c @@ -31,6 +31,8 @@ static const struct cpuid_bit cpuid_bits[] = { { X86_FEATURE_CDP_L3, CPUID_ECX, 2, 0x00000010, 1 }, { X86_FEATURE_CDP_L2, CPUID_ECX, 2, 0x00000010, 2 }, { X86_FEATURE_MBA, CPUID_EBX, 3, 0x00000010, 0 }, + { X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 }, + { X86_FEATURE_SGX2, CPUID_EAX, 1, 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 }, diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 981ff9479648..748180a4a1f5 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -222,12 +222,21 @@ #define X86_FEATURE_L1TF_PTEINV ( 7*32+29) /* "" L1TF workaround PTE inversion */ #define X86_FEATURE_IBRS_ENHANCED ( 7*32+30) /* Enhanced IBRS */ -/* Virtualization flags: Linux defined, word 8 */ -#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ -#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */ -#define X86_FEATURE_FLEXPRIORITY ( 8*32+ 2) /* Intel FlexPriority */ -#define X86_FEATURE_EPT ( 8*32+ 3) /* Intel Extended Page Table */ -#define X86_FEATURE_VPID ( 8*32+ 4) /* Intel Virtual Processor ID */ +/* + * Scattered Intel features: Linux defined, word 8. + * + * Note that the bit numbers of the SGX features are meaningful as KVM expects + * the Linux defined bit to match the Intel defined bit, e.g. X86_FEATURE_SGX1 + * must remain at bit 0, SGX2 at bit 1, etc... + */ +#define X86_FEATURE_SGX1 ( 8*32+ 0) /* SGX1 leaf functions */ +#define X86_FEATURE_SGX2 ( 8*32+ 1) /* SGX2 leaf functions */ + +#define X86_FEATURE_TPR_SHADOW ( 8*32+ 8) /* Intel TPR Shadow */ +#define X86_FEATURE_VNMI ( 8*32+ 9) /* Intel Virtual NMI */ +#define X86_FEATURE_FLEXPRIORITY ( 8*32+10) /* Intel FlexPriority */ +#define X86_FEATURE_EPT ( 8*32+11) /* Intel Extended Page Table */ +#define X86_FEATURE_VPID ( 8*32+12) /* Intel Virtual Processor ID */ #define X86_FEATURE_VMMCALL ( 8*32+15) /* Prefer VMMCALL to VMCALL */ #define X86_FEATURE_XENPV ( 8*32+16) /* "" Xen paravirtual guest */ From patchwork Wed Apr 17 10:39:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905145 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C6CEF17E0 for ; Wed, 17 Apr 2019 10:40:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AB8DF289F5 for ; Wed, 17 Apr 2019 10:40:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9C72A28AD4; Wed, 17 Apr 2019 10:40:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3D61B289F5 for ; Wed, 17 Apr 2019 10:40:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731657AbfDQKkL (ORCPT ); Wed, 17 Apr 2019 06:40:11 -0400 Received: from mga05.intel.com ([192.55.52.43]:7809 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731632AbfDQKkL (ORCPT ); Wed, 17 Apr 2019 06:40:11 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:10 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119777" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:03 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 03/28] x86/msr: Add IA32_FEATURE_CONTROL.SGX_ENABLE definition Date: Wed, 17 Apr 2019 13:39:13 +0300 Message-Id: <20190417103938.7762-4-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Add a new IA32_FEATURE_CONTROL bit, SGX_ENABLE, which must be set in order to execute SGX instructions, i.e. ENCL{S,U,V}. The existence of the bit is enumerated by CPUID as X86_FEATURE_SGX. Like all other flags in IA32_FEATURE_CONTROL, the MSR must be locked for SGX_ENABLE to take effect. Signed-off-by: Sean Christopherson Signed-off-by: Jarkko Sakkinen --- arch/x86/include/asm/msr-index.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index ca5bc0eacb95..6efaa8026c64 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -525,6 +525,7 @@ #define FEATURE_CONTROL_LOCKED (1<<0) #define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1) #define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2) +#define FEATURE_CONTROL_SGX_ENABLE (1<<18) #define FEATURE_CONTROL_LMCE (1<<20) #define MSR_IA32_APICBASE 0x0000001b From patchwork Wed Apr 17 10:39:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905147 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D089313B5 for ; Wed, 17 Apr 2019 10:40:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B85D7289F5 for ; Wed, 17 Apr 2019 10:40:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AC3CB28AD4; Wed, 17 Apr 2019 10:40:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9A3A4289F5 for ; Wed, 17 Apr 2019 10:40:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731742AbfDQKkT (ORCPT ); Wed, 17 Apr 2019 06:40:19 -0400 Received: from mga06.intel.com ([134.134.136.31]:40784 "EHLO mga06.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731632AbfDQKkT (ORCPT ); Wed, 17 Apr 2019 06:40:19 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:18 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119834" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:11 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Kai Huang , Jarkko Sakkinen Subject: [PATCH v20 04/28] x86/cpufeatures: Add Intel-defined SGX_LC feature bit Date: Wed, 17 Apr 2019 13:39:14 +0300 Message-Id: <20190417103938.7762-5-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Kai Huang X86_FEATURE_SGX_LC reflects whether or not the CPU supports SGX Launch Control, i.e. enumerates the existence of IA32_FEATURE_CONTROL's SGX_LE_WR bit and the IA32_SGXLEPUBKEYHASH MSRs. Signed-off-by: Kai Huang Signed-off-by: Jarkko Sakkinen --- arch/x86/include/asm/cpufeatures.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 313c58c04b51..c5080842ecad 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -346,6 +346,7 @@ #define X86_FEATURE_CLDEMOTE (16*32+25) /* CLDEMOTE instruction */ #define X86_FEATURE_MOVDIRI (16*32+27) /* MOVDIRI instruction */ #define X86_FEATURE_MOVDIR64B (16*32+28) /* MOVDIR64B instruction */ +#define X86_FEATURE_SGX_LC (16*32+30) /* Software Guard Extensions Launch Control */ /* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */ #define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* MCA overflow recovery support */ From patchwork Wed Apr 17 10:39:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905149 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E9BC713B5 for ; Wed, 17 Apr 2019 10:40:27 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D334E289F5 for ; Wed, 17 Apr 2019 10:40:27 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C616728AD4; Wed, 17 Apr 2019 10:40:27 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5E398289F5 for ; Wed, 17 Apr 2019 10:40:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731782AbfDQKk1 (ORCPT ); Wed, 17 Apr 2019 06:40:27 -0400 Received: from mga06.intel.com ([134.134.136.31]:40793 "EHLO mga06.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729584AbfDQKk0 (ORCPT ); Wed, 17 Apr 2019 06:40:26 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:26 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119867" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:20 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 05/28] x86/msr: Add SGX Launch Control MSR definitions Date: Wed, 17 Apr 2019 13:39:15 +0300 Message-Id: <20190417103938.7762-6-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Add a new IA32_FEATURE_CONTROL bit, SGX_LE_WR. When set, SGX_LE_WR allows software to write the SGXLEPUBKEYHASH MSRs (see below). The The existence of the bit is enumerated by CPUID as X86_FEATURE_SGX_LC. Like all other flags in IA32_FEATURE_CONTROL, the MSR must be locked for SGX_LE_WR to take effect. Add four MSRs, SGXLEPUBKEYHASH{0,1,2,3}, or in human readable form, the SGX Launch Enclave Public Key Hash MSRs. These MSRs correspond to the key that is used by the CPU to determine whether or not to allow software to enter an enclave. When ENCLS[EINIT] is executed, which is a prerequisite to entering the enclave, the CPU compares the key (technically its hash) used to sign the enclave with the key hash stored in the MSRs, and will reject EINIT if the keys do not match. Enclaves can also be blessed by proxy, in which case a Launch Enclave generates and signs an EINIT TOKEN. If a valid token is provided, ENCLS[EINIT] compares the signer of the token against the MSRs instead of the signer of the enclave. The SGXLEPUBKEYHASH MSRs only exist on CPUs that support SGX Launch Control, enumerated by X86_FEATURE_SGX_LC. CPUs without Launch Control use a hardcoded key for the ENCLS[EINIT] checks. An internal hardcoded key is also used as the reset value for the hash MSRs when they exist. As a final note, the SGX_LEPUBKEYHASH MSRs can also be written by pre-boot firmware prior to activating SGX (SGX activation is done by setting bit 0 in MSR 0x7A). Thus, firmware can lock the MSRs to a non-Intel value by writing the MSRs and locking IA32_FEATURE_CONTROL without setting SGX_LE_WR. Signed-off-by: Sean Christopherson Co-developed-by: Haim Cohen Signed-off-by: Haim Cohen Signed-off-by: Jarkko Sakkinen --- arch/x86/include/asm/msr-index.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 6efaa8026c64..a4a22441d000 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -525,6 +525,7 @@ #define FEATURE_CONTROL_LOCKED (1<<0) #define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1) #define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2) +#define FEATURE_CONTROL_SGX_LE_WR (1<<17) #define FEATURE_CONTROL_SGX_ENABLE (1<<18) #define FEATURE_CONTROL_LMCE (1<<20) @@ -538,6 +539,12 @@ #define MSR_IA32_UCODE_WRITE 0x00000079 #define MSR_IA32_UCODE_REV 0x0000008b +/* Intel SGX Launch Enclave Public Key Hash MSRs */ +#define MSR_IA32_SGXLEPUBKEYHASH0 0x0000008C +#define MSR_IA32_SGXLEPUBKEYHASH1 0x0000008D +#define MSR_IA32_SGXLEPUBKEYHASH2 0x0000008E +#define MSR_IA32_SGXLEPUBKEYHASH3 0x0000008F + #define MSR_IA32_SMM_MONITOR_CTL 0x0000009b #define MSR_IA32_SMBASE 0x0000009e From patchwork Wed Apr 17 10:39:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905151 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7A8F113B5 for ; Wed, 17 Apr 2019 10:40:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 626B4289F5 for ; Wed, 17 Apr 2019 10:40:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 55CA428AD4; Wed, 17 Apr 2019 10:40:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EA741289F5 for ; Wed, 17 Apr 2019 10:40:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731806AbfDQKke (ORCPT ); Wed, 17 Apr 2019 06:40:34 -0400 Received: from mga04.intel.com ([192.55.52.120]:54616 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729584AbfDQKke (ORCPT ); Wed, 17 Apr 2019 06:40:34 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:33 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119892" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:27 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Dave Hansen , Jarkko Sakkinen Subject: [PATCH v20 06/28] x86/mm: x86/sgx: Add new 'PF_SGX' page fault error code bit Date: Wed, 17 Apr 2019 13:39:16 +0300 Message-Id: <20190417103938.7762-7-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson The SGX bit is set in the #PF error code if and only if the fault is detected by the Enclave Page Cache Map (EPCM), a hardware-managed table that enforces the paging permissions defined by the enclave, e.g. to prevent the kernel from changing the permissions of an enclave's page(s). Despite triggering a #PF, a #PF with PF_SGX has nothing to do with paging. Cc: Dave Hansen Signed-off-by: Sean Christopherson Signed-off-by: Jarkko Sakkinen --- arch/x86/include/asm/traps.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 7d6f3f3fad78..c3e02912e9c7 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -171,5 +171,6 @@ enum x86_pf_error_code { X86_PF_RSVD = 1 << 3, X86_PF_INSTR = 1 << 4, X86_PF_PK = 1 << 5, + X86_PF_SGX = 1 << 15, }; #endif /* _ASM_X86_TRAPS_H */ From patchwork Wed Apr 17 10:39:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905153 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B39DC17E0 for ; Wed, 17 Apr 2019 10:40:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 99954289F5 for ; Wed, 17 Apr 2019 10:40:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8D6A628AD4; Wed, 17 Apr 2019 10:40:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 13990289F5 for ; Wed, 17 Apr 2019 10:40:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731382AbfDQKkm (ORCPT ); Wed, 17 Apr 2019 06:40:42 -0400 Received: from mga14.intel.com ([192.55.52.115]:43025 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730926AbfDQKkm (ORCPT ); Wed, 17 Apr 2019 06:40:42 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:42 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119925" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:34 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Andy Lutomirski , Dave Hansen , Jarkko Sakkinen Subject: [PATCH v20 07/28] x86/mm: x86/sgx: Signal SIGSEGV for userspace #PFs w/ PF_SGX Date: Wed, 17 Apr 2019 13:39:17 +0300 Message-Id: <20190417103938.7762-8-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson The PF_SGX bit is set if and only if the #PF is detected by the SGX Enclave Page Cache Map (EPCM). The EPCM is a hardware-managed table that enforces accesses to an enclave's EPC pages in addition to the software-managed kernel page tables, i.e. the effective permissions for an EPC page are a logical AND of the kernel's page tables and the corresponding EPCM entry. The EPCM is consulted only after an access walks the kernel's page tables, i.e.: a. the access was allowed by the kernel b. the kernel's tables have become less restrictive than the EPCM c. the kernel cannot fixup the cause of the fault Noteably, (b) implies that either the kernel has botched the EPC mappings or the EPCM has been invalidated (see below). Regardless of why the fault occurred, userspace needs to be alerted so that it can take appropriate action, e.g. restart the enclave. This is reinforced by (c) as the kernel doesn't really have any other reasonable option, i.e. signalling SIGSEGV is actually the least severe action possible. Although the primary purpose of the EPCM is to prevent a malicious or compromised kernel from attacking an enclave, e.g. by modifying the enclave's page tables, do not WARN on a #PF w/ PF_SGX set. The SGX architecture effectively allows the CPU to invalidate all EPCM entries at will and requires that software be prepared to handle an EPCM fault at any time. The architecture defines this behavior because the EPCM is encrypted with an ephemeral key that isn't exposed to software. As such, the EPCM entries cannot be preserved across transitions that result in a new key being used, e.g. CPU power down as part of an S3 transition or when a VM is live migrated to a new physical system. Cc: Andy Lutomirski Cc: Dave Hansen Signed-off-by: Sean Christopherson Signed-off-by: Jarkko Sakkinen --- arch/x86/mm/fault.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 667f1da36208..78e2807fbede 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1214,6 +1214,19 @@ access_error(unsigned long error_code, struct vm_area_struct *vma) if (error_code & X86_PF_PK) return 1; + /* + * Access is blocked by the Enclave Page Cache Map (EPCM), i.e. the + * access is allowed by the PTE but not the EPCM. This usually happens + * when the EPCM is yanked out from under us, e.g. by hardware after a + * suspend/resume cycle. In any case, software, i.e. the kernel, can't + * fix the source of the fault as the EPCM can't be directly modified + * by software. Handle the fault as an access error in order to signal + * userspace, e.g. so that userspace can rebuild their enclave(s), even + * though userspace may not have actually violated access permissions. + */ + if (unlikely(error_code & X86_PF_SGX)) + return 1; + /* * Make sure to check the VMA so that we do not perform * faults just to hit a X86_PF_PK as soon as we fill in a From patchwork Wed Apr 17 10:39:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905155 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 95EBF17E0 for ; Wed, 17 Apr 2019 10:40:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7D801289F5 for ; Wed, 17 Apr 2019 10:40:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 715B728AD4; Wed, 17 Apr 2019 10:40:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0ABBF289F5 for ; Wed, 17 Apr 2019 10:40:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729782AbfDQKku (ORCPT ); Wed, 17 Apr 2019 06:40:50 -0400 Received: from mga18.intel.com ([134.134.136.126]:64357 "EHLO mga18.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729563AbfDQKku (ORCPT ); Wed, 17 Apr 2019 06:40:50 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:49 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119938" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:43 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 08/28] x86/cpu/intel: Detect SGX support and update caps appropriately Date: Wed, 17 Apr 2019 13:39:18 +0300 Message-Id: <20190417103938.7762-9-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Similar to other large Intel features such as VMX and TXT, SGX must be explicitly enabled in IA32_FEATURE_CONTROL MSR to be truly usable. Clear all SGX related capabilities if SGX is not fully enabled in IA32_FEATURE_CONTROL or if the SGX1 instruction set isn't supported (impossible on bare metal, theoretically possible in a VM if the VMM is doing something weird). Like SGX itself, SGX Launch Control must be explicitly enabled via a flag in IA32_FEATURE_CONTROL. Clear the SGX_LC capability if Launch Control is not fully enabled (or obviously if SGX itself is disabled). Note that clearing X86_FEATURE_SGX_LC creates a bit of a conundrum regarding the SGXLEPUBKEYHASH MSRs, as it may be desirable to read the MSRs even if they are not writable, e.g. to query the configured key, but clearing the capability leaves no breadcrum for discerning whether or not the MSRs exist. But, such usage will be rare (KVM is the only known case at this time) and not performance critical, so it's not unreasonable to require the use of rdmsr_safe(). Clearing the cap bit eliminates the need for an additional flag to track whether or not Launch Control is truly enabled, which is what we care about the vast majority of the time. Signed-off-by: Sean Christopherson Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- arch/x86/kernel/cpu/intel.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index fc3c07fe7df5..702497f34a96 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -596,6 +596,42 @@ static void detect_tme(struct cpuinfo_x86 *c) c->x86_phys_bits -= keyid_bits; } +static void __maybe_unused detect_sgx(struct cpuinfo_x86 *c) +{ + unsigned long long fc; + + rdmsrl(MSR_IA32_FEATURE_CONTROL, fc); + if (!(fc & FEATURE_CONTROL_LOCKED)) { + pr_err_once("sgx: The feature control MSR is not locked\n"); + goto err_unsupported; + } + + if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) { + pr_err_once("sgx: SGX is not enabled in IA32_FEATURE_CONTROL MSR\n"); + goto err_unsupported; + } + + if (!cpu_has(c, X86_FEATURE_SGX1)) { + pr_err_once("sgx: SGX1 instruction set is not supported\n"); + goto err_unsupported; + } + + if (!(fc & FEATURE_CONTROL_SGX_LE_WR)) { + pr_info_once("sgx: The launch control MSRs are not writable\n"); + goto err_msrs_rdonly; + } + + return; + +err_unsupported: + setup_clear_cpu_cap(X86_FEATURE_SGX); + setup_clear_cpu_cap(X86_FEATURE_SGX1); + setup_clear_cpu_cap(X86_FEATURE_SGX2); + +err_msrs_rdonly: + setup_clear_cpu_cap(X86_FEATURE_SGX_LC); +} + static void init_intel_energy_perf(struct cpuinfo_x86 *c) { u64 epb; @@ -763,6 +799,9 @@ static void init_intel(struct cpuinfo_x86 *c) if (cpu_has(c, X86_FEATURE_TME)) detect_tme(c); + if (IS_ENABLED(CONFIG_INTEL_SGX) && cpu_has(c, X86_FEATURE_SGX)) + detect_sgx(c); + init_intel_energy_perf(c); init_intel_misc_features(c); From patchwork Wed Apr 17 10:39:19 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905157 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0EF5813B5 for ; Wed, 17 Apr 2019 10:40:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EB9D0289F5 for ; Wed, 17 Apr 2019 10:40:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DF03128AD4; Wed, 17 Apr 2019 10:40:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 647BC289F5 for ; Wed, 17 Apr 2019 10:40:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731852AbfDQKk6 (ORCPT ); Wed, 17 Apr 2019 06:40:58 -0400 Received: from mga04.intel.com ([192.55.52.120]:54628 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729563AbfDQKk6 (ORCPT ); Wed, 17 Apr 2019 06:40:58 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:40:57 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119946" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:51 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 09/28] x86/sgx: Add ENCLS architectural error codes Date: Wed, 17 Apr 2019 13:39:19 +0300 Message-Id: <20190417103938.7762-10-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The SGX architecture defines an extensive set of error codes that are used by ENCL{S,U,V} instructions to provide software with (somewhat) precise error information. Though they are architectural, define the known error codes in a separate file from sgx_arch.h so that they can be exposed to userspace. For some ENCLS leafs, e.g. EINIT, returning the exact error code on failure can enable userspace to make informed decisions when an operation fails. Signed-off-by: Jarkko Sakkinen Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- arch/x86/include/uapi/asm/sgx_errno.h | 91 +++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 arch/x86/include/uapi/asm/sgx_errno.h diff --git a/arch/x86/include/uapi/asm/sgx_errno.h b/arch/x86/include/uapi/asm/sgx_errno.h new file mode 100644 index 000000000000..48b87aed58d7 --- /dev/null +++ b/arch/x86/include/uapi/asm/sgx_errno.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright(c) 2018 Intel Corporation. + * + * Contains the architecturally defined error codes that are returned by SGX + * instructions, e.g. ENCLS, and may be propagated to userspace via errno. + */ + +#ifndef _UAPI_ASM_X86_SGX_ERRNO_H +#define _UAPI_ASM_X86_SGX_ERRNO_H + +/** + * enum sgx_encls_leaves - return codes for ENCLS, ENCLU and ENCLV + * %SGX_SUCCESS: No error. + * %SGX_INVALID_SIG_STRUCT: SIGSTRUCT contains an invalid value. + * %SGX_INVALID_ATTRIBUTE: Enclave is not attempting to access a resource + * for which it is not authorized. + * %SGX_BLKSTATE: EPC page is already blocked. + * %SGX_INVALID_MEASUREMENT: SIGSTRUCT or EINITTOKEN contains an incorrect + * measurement. + * %SGX_NOTBLOCKABLE: EPC page type is not one which can be blocked. + * %SGX_PG_INVLD: EPC page is invalid (and cannot be blocked). + * %SGX_EPC_PAGE_CONFLICT: EPC page in use by another SGX instruction. + * %SGX_INVALID_SIGNATURE: Enclave's signature does not validate with + * public key enclosed in SIGSTRUCT. + * %SGX_MAC_COMPARE_FAIL: MAC check failed when reloading EPC page. + * %SGX_PAGE_NOT_BLOCKED: EPC page is not marked as blocked. + * %SGX_NOT_TRACKED: ETRACK has not been completed on the EPC page. + * %SGX_VA_SLOT_OCCUPIED: Version array slot contains a valid entry. + * %SGX_CHILD_PRESENT: Enclave has child pages present in the EPC. + * %SGX_ENCLAVE_ACT: Logical processors are currently executing + * inside the enclave. + * %SGX_ENTRYEPOCH_LOCKED: SECS locked for EPOCH update, i.e. an ETRACK is + * currently executing on the SECS. + * %SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's + * public key does not match IA32_SGXLEPUBKEYHASH. + * %SGX_PREV_TRK_INCMPL: All processors did not complete the previous + * tracking sequence. + * %SGX_PG_IS_SECS: Target EPC page is an SECS and cannot be + * blocked. + * %SGX_PAGE_ATTRIBUTES_MISMATCH: Attributes of the EPC page do not match + * the expected values. + * %SGX_PAGE_NOT_MODIFIABLE: EPC page cannot be modified because it is in + * the PENDING or MODIFIED state. + * %SGX_PAGE_NOT_DEBUGGABLE: EPC page cannot be modified because it is in + * the PENDING or MODIFIED state. + * %SGX_INVALID_COUNTER: {In,De}crementing a counter would cause it to + * {over,under}flow. + * %SGX_PG_NONEPC: Target page is not an EPC page. + * %SGX_TRACK_NOT_REQUIRED: Target page type does not require tracking. + * %SGX_INVALID_CPUSVN: Security version number reported by CPU is less + * than what is required by the enclave. + * %SGX_INVALID_ISVSVN: Security version number of enclave is less than + * what is required by the KEYREQUEST struct. + * %SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received + * while the instruction was executing. + * %SGX_INVALID_KEYNAME: Requested key is not supported by hardware. + */ +enum sgx_return_codes { + SGX_SUCCESS = 0, + SGX_INVALID_SIG_STRUCT = 1, + SGX_INVALID_ATTRIBUTE = 2, + SGX_BLKSTATE = 3, + SGX_INVALID_MEASUREMENT = 4, + SGX_NOTBLOCKABLE = 5, + SGX_PG_INVLD = 6, + SGX_EPC_PAGE_CONFLICT = 7, + SGX_INVALID_SIGNATURE = 8, + SGX_MAC_COMPARE_FAIL = 9, + SGX_PAGE_NOT_BLOCKED = 10, + SGX_NOT_TRACKED = 11, + SGX_VA_SLOT_OCCUPIED = 12, + SGX_CHILD_PRESENT = 13, + SGX_ENCLAVE_ACT = 14, + SGX_ENTRYEPOCH_LOCKED = 15, + SGX_INVALID_EINITTOKEN = 16, + SGX_PREV_TRK_INCMPL = 17, + SGX_PG_IS_SECS = 18, + SGX_PAGE_ATTRIBUTES_MISMATCH = 19, + SGX_PAGE_NOT_MODIFIABLE = 20, + SGX_PAGE_NOT_DEBUGGABLE = 21, + SGX_INVALID_COUNTER = 25, + SGX_PG_NONEPC = 26, + SGX_TRACK_NOT_REQUIRED = 27, + SGX_INVALID_CPUSVN = 32, + SGX_INVALID_ISVSVN = 64, + SGX_UNMASKED_EVENT = 128, + SGX_INVALID_KEYNAME = 256, +}; + +#endif /* _UAPI_ASM_X86_SGX_ERRNO_H */ From patchwork Wed Apr 17 10:39:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905159 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 552C117E0 for ; Wed, 17 Apr 2019 10:41:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3C910289F5 for ; Wed, 17 Apr 2019 10:41:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2F47F28AD4; Wed, 17 Apr 2019 10:41:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 07005289F5 for ; Wed, 17 Apr 2019 10:41:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729563AbfDQKlD (ORCPT ); Wed, 17 Apr 2019 06:41:03 -0400 Received: from mga04.intel.com ([192.55.52.120]:54628 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729522AbfDQKlD (ORCPT ); Wed, 17 Apr 2019 06:41:03 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:02 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132119964" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:40:57 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 10/28] x86/sgx: Add SGX1 and SGX2 architectural data structures Date: Wed, 17 Apr 2019 13:39:20 +0300 Message-Id: <20190417103938.7762-11-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Define the data structures used by various ENCLS functions needed for Linux to support all SGX1 and SGX2 ENCLS leaf functions. This is not an exhaustive representation of all SGX data structures as several are only consumed by ENCLU (userspace), e.g. REPORT and KEYREQUEST, while others are only consumed by future features, e.g. RDINFO. Signed-off-by: Jarkko Sakkinen Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- arch/x86/kernel/cpu/sgx/arch.h | 424 +++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 arch/x86/kernel/cpu/sgx/arch.h diff --git a/arch/x86/kernel/cpu/sgx/arch.h b/arch/x86/kernel/cpu/sgx/arch.h new file mode 100644 index 000000000000..39f731580ea8 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/arch.h @@ -0,0 +1,424 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/** + * Copyright(c) 2016-18 Intel Corporation. + * + * Contains data structures defined by the SGX architecture. Data structures + * defined by the Linux software stack should not be placed here. + */ +#ifndef _ASM_X86_SGX_ARCH_H +#define _ASM_X86_SGX_ARCH_H + +#include + +#define SGX_CPUID 0x12 +#define SGX_CPUID_FIRST_VARIABLE_SUB_LEAF 2 + +/** + * enum sgx_sub_leaf_types - SGX CPUID variable sub-leaf types + * %SGX_CPUID_SUB_LEAF_INVALID: Indicates this sub-leaf is invalid. + * %SGX_CPUID_SUB_LEAF_EPC_SECTION: Sub-leaf enumerates an EPC section. + */ +enum sgx_sub_leaf_types { + SGX_CPUID_SUB_LEAF_INVALID = 0x0, + SGX_CPUID_SUB_LEAF_EPC_SECTION = 0x1, +}; + +#define SGX_CPUID_SUB_LEAF_TYPE_MASK GENMASK(3, 0) + +/** + * enum sgx_encls_leaves - ENCLS leaf functions + * %SGX_ECREATE: Create an enclave. + * %SGX_EADD: Add a page to an uninitialized enclave. + * %SGX_EINIT: Initialize an enclave, i.e. launch an enclave. + * %SGX_EREMOVE: Remove a page from an enclave. + * %SGX_EDBGRD: Read a word from an enclve (peek). + * %SGX_EDBGWR: Write a word to an enclave (poke). + * %SGX_EEXTEND: Measure 256 bytes of an added enclave page. + * %SGX_ELDB: Load a swapped page in blocked state. + * %SGX_ELDU: Load a swapped page in unblocked state. + * %SGX_EBLOCK: Change page state to blocked i.e. entering hardware + * threads cannot access it and create new TLB entries. + * %SGX_EPA: Create a Version Array (VA) page used to store isvsvn + * number for a swapped EPC page. + * %SGX_EWB: Swap an enclave page to the regular memory. Checks that + * all threads have exited that were in the previous + * shoot-down sequence. + * %SGX_ETRACK: Start a new shoot down sequence. Used to together with + * EBLOCK to make sure that a page is safe to swap. + * %SGX_EAUG: Add a page to an initialized enclave. + * %SGX_EMODPR: Restrict an EPC page's permissions. + * %SGX_EMODT: Modify the page type of an EPC page. + */ +enum sgx_encls_leaves { + SGX_ECREATE = 0x00, + SGX_EADD = 0x01, + SGX_EINIT = 0x02, + SGX_EREMOVE = 0x03, + SGX_EDGBRD = 0x04, + SGX_EDGBWR = 0x05, + SGX_EEXTEND = 0x06, + SGX_ELDB = 0x07, + SGX_ELDU = 0x08, + SGX_EBLOCK = 0x09, + SGX_EPA = 0x0A, + SGX_EWB = 0x0B, + SGX_ETRACK = 0x0C, + SGX_EAUG = 0x0D, + SGX_EMODPR = 0x0E, + SGX_EMODT = 0x0F, +}; + +#define SGX_MODULUS_SIZE 384 + +/** + * enum sgx_miscselect - additional information to an SSA frame + * %SGX_MISC_EXINFO: Report #PF or #GP to the SSA frame. + * + * Save State Area (SSA) is a stack inside the enclave used to store processor + * state when an exception or interrupt occurs. This enum defines additional + * information stored to an SSA frame. + */ +enum sgx_miscselect { + SGX_MISC_EXINFO = BIT(0), +}; + +#define SGX_MISC_RESERVED_MASK GENMASK_ULL(63, 1) + +#define SGX_SSA_GPRS_SIZE 182 +#define SGX_SSA_MISC_EXINFO_SIZE 16 + +/** + * enum sgx_attributes - the attributes field in &struct sgx_secs + * %SGX_ATTR_INIT: Enclave can be entered (is initialized). + * %SGX_ATTR_DEBUG: Allow ENCLS(EDBGRD) and ENCLS(EDBGWR). + * %SGX_ATTR_MODE64BIT: Tell that this a 64-bit enclave. + * %SGX_ATTR_PROVISIONKEY: Allow to use provisioning keys for remote + * attestation. + * %SGX_ATTR_KSS: Allow to use key separation and sharing (KSS). + * %SGX_ATTR_EINITTOKENKEY: Allow to use token signing key that is used to + * sign cryptographic tokens that can be passed to + * EINIT as an authorization to run an enclave. + */ +enum sgx_attribute { + SGX_ATTR_INIT = BIT(0), + SGX_ATTR_DEBUG = BIT(1), + SGX_ATTR_MODE64BIT = BIT(2), + SGX_ATTR_PROVISIONKEY = BIT(4), + SGX_ATTR_EINITTOKENKEY = BIT(5), + SGX_ATTR_KSS = BIT(7), +}; + +#define SGX_ATTR_RESERVED_MASK (BIT_ULL(3) | BIT_ULL(7) | GENMASK_ULL(63, 8)) +#define SGX_ATTR_ALLOWED_MASK (SGX_ATTR_DEBUG | SGX_ATTR_MODE64BIT | \ + SGX_ATTR_KSS) +#define SGX_SECS_RESERVED1_SIZE 24 +#define SGX_SECS_RESERVED2_SIZE 32 +#define SGX_SECS_RESERVED3_SIZE 96 +#define SGX_SECS_RESERVED4_SIZE 3836 + +/** + * struct sgx_secs - SGX Enclave Control Structure (SECS) + * @size: size of the address space + * @base: base address of the address space + * @ssa_frame_size: size of an SSA frame + * @miscselect: additional information stored to an SSA frame + * @attributes: attributes for enclave + * @xfrm: XSave-Feature Request Mask (subset of XCR0) + * @mrenclave: SHA256-hash of the enclave contents + * @mrsigner: SHA256-hash of the public key used to sign the SIGSTRUCT + * @isvprodid: a user-defined value that is used in key derivation + * @isvsvn: a user-defined value that is used in key derivation + * + * SGX Enclave Control Structure (SECS) is a special enclave page that is not + * visible in the address space. In fact, this structure defines the address + * range and other global attributes for the enclave and it is the first EPC + * page created for any enclave. It is moved from a temporary buffer to an EPC + * by the means of ENCLS(ECREATE) leaf. + */ +struct sgx_secs { + u64 size; + u64 base; + u32 ssa_frame_size; + u32 miscselect; + u8 reserved1[SGX_SECS_RESERVED1_SIZE]; + u64 attributes; + u64 xfrm; + u32 mrenclave[8]; + u8 reserved2[SGX_SECS_RESERVED2_SIZE]; + u32 mrsigner[8]; + u8 reserved3[SGX_SECS_RESERVED3_SIZE]; + u16 isvprodid; + u16 isvsvn; + u8 reserved4[SGX_SECS_RESERVED4_SIZE]; +} __packed; + +/** + * enum sgx_tcs_flags - execution flags for TCS + * %SGX_TCS_DBGOPTIN: If enabled allows single-stepping and breakpoints + * inside an enclave. It is cleared by EADD but can + * be set later with EDBGWR. + */ +enum sgx_tcs_flags { + SGX_TCS_DBGOPTIN = 0x01, +}; + +#define SGX_TCS_RESERVED_MASK GENMASK_ULL(63, 1) +#define SGX_TCS_RESERVED_SIZE 4024 + +/** + * struct sgx_tcs - Thread Control Structure (TCS) + * @state: used to mark an entered TCS + * @flags: execution flags (cleared by EADD) + * @ssa_offset: SSA stack offset relative to the enclave base + * @ssa_index: the current SSA frame index (cleard by EADD) + * @nr_ssa_frames: the number of frame in the SSA stack + * @entry_offset: entry point offset relative to the enclave base + * @exit_addr: address outside the enclave to exit on an exception or + * interrupt + * @fs_offset: offset relative to the enclave base to become FS + * segment inside the enclave + * @gs_offset: offset relative to the enclave base to become GS + * segment inside the enclave + * @fs_limit: size to become a new FS-limit (only 32-bit enclaves) + * @gs_limit: size to become a new GS-limit (only 32-bit enclaves) + * + * Thread Control Structure (TCS) is an enclave page visible in its address + * space that defines an entry point inside the enclave. A thread enters inside + * an enclave by supplying address of TCS to ENCLU(EENTER). A TCS can be entered + * by only one thread at a time. + */ +struct sgx_tcs { + u64 state; + u64 flags; + u64 ssa_offset; + u32 ssa_index; + u32 nr_ssa_frames; + u64 entry_offset; + u64 exit_addr; + u64 fs_offset; + u64 gs_offset; + u32 fs_limit; + u32 gs_limit; + u8 reserved[SGX_TCS_RESERVED_SIZE]; +} __packed; + +/** + * struct sgx_pageinfo - an enclave page descriptor + * @addr: address of the enclave page + * @contents: pointer to the page contents + * @metadata: pointer either to a SECINFO or PCMD instance + * @secs: address of the SECS page + */ +struct sgx_pageinfo { + u64 addr; + u64 contents; + u64 metadata; + u64 secs; +} __packed __aligned(32); + + +/** + * enum sgx_page_type - bits in the SECINFO flags defining the page type + * %SGX_PAGE_TYPE_SECS: a SECS page + * %SGX_PAGE_TYPE_TCS: a TCS page + * %SGX_PAGE_TYPE_REG: a regular page + * %SGX_PAGE_TYPE_VA: a VA page + * %SGX_PAGE_TYPE_TRIM: a page in trimmed state + */ +enum sgx_page_type { + SGX_PAGE_TYPE_SECS, + SGX_PAGE_TYPE_TCS, + SGX_PAGE_TYPE_REG, + SGX_PAGE_TYPE_VA, + SGX_PAGE_TYPE_TRIM, +}; + +#define SGX_NR_PAGE_TYPES 5 +#define SGX_PAGE_TYPE_MASK GENMASK(7, 0) + +/** + * enum sgx_secinfo_flags - the flags field in &struct sgx_secinfo + * %SGX_SECINFO_R: allow read + * %SGX_SECINFO_W: allow write + * %SGX_SECINFO_X: allow execution + * %SGX_SECINFO_SECS: a SECS page + * %SGX_SECINFO_TCS: a TCS page + * %SGX_SECINFO_REG: a regular page + * %SGX_SECINFO_VA: a VA page + * %SGX_SECINFO_TRIM: a page in trimmed state + */ +enum sgx_secinfo_flags { + SGX_SECINFO_R = BIT(0), + SGX_SECINFO_W = BIT(1), + SGX_SECINFO_X = BIT(2), + SGX_SECINFO_SECS = (SGX_PAGE_TYPE_SECS << 8), + SGX_SECINFO_TCS = (SGX_PAGE_TYPE_TCS << 8), + SGX_SECINFO_REG = (SGX_PAGE_TYPE_REG << 8), + SGX_SECINFO_VA = (SGX_PAGE_TYPE_VA << 8), + SGX_SECINFO_TRIM = (SGX_PAGE_TYPE_TRIM << 8), +}; + +#define SGX_SECINFO_PERMISSION_MASK GENMASK_ULL(2, 0) +#define SGX_SECINFO_PAGE_TYPE_MASK (SGX_PAGE_TYPE_MASK << 8) +#define SGX_SECINFO_RESERVED_MASK ~(SGX_SECINFO_PERMISSION_MASK | \ + SGX_SECINFO_PAGE_TYPE_MASK) +#define SGX_SECINFO_RESERVED_SIZE 56 + +/** + * struct sgx_secinfo - describes attributes of an EPC page + * @flags: permissions and type + * + * Used together with ENCLS leaves that add or modify an EPC page to an + * enclave to define page permissions and type. + */ +struct sgx_secinfo { + u64 flags; + u8 reserved[SGX_SECINFO_RESERVED_SIZE]; +} __packed __aligned(64); + +#define SGX_PCMD_RESERVED_SIZE 40 + +/** + * struct sgx_pcmd - Paging Crypto Metadata (PCMD) + * @enclave_id: enclave identifier + * @mac: MAC over PCMD, page contents and isvsvn + * + * PCMD is stored for every swapped page to the regular memory. When ELDU loads + * the page back it recalculates the MAC by using a isvsvn number stored in a + * VA page. Together these two structures bring integrity and rollback + * protection. + */ +struct sgx_pcmd { + struct sgx_secinfo secinfo; + u64 enclave_id; + u8 reserved[SGX_PCMD_RESERVED_SIZE]; + u8 mac[16]; +} __packed __aligned(128); + +#define SGX_SIGSTRUCT_RESERVED1_SIZE 84 +#define SGX_SIGSTRUCT_RESERVED2_SIZE 20 +#define SGX_SIGSTRUCT_RESERVED3_SIZE 32 +#define SGX_SIGSTRUCT_RESERVED4_SIZE 12 + +/** + * struct sgx_sigstruct_header - defines author of the enclave + * @header1: constant byte string + * @vendor: must be either 0x0000 or 0x8086 + * @date: YYYYMMDD in BCD + * @header2: costant byte string + * @swdefined: software defined value + */ +struct sgx_sigstruct_header { + u64 header1[2]; + u32 vendor; + u32 date; + u64 header2[2]; + u32 swdefined; + u8 reserved1[84]; +} __packed; + +/** + * struct sgx_sigstruct_body - defines contents of the enclave + * @miscselect: additional information stored to an SSA frame + * @misc_mask: required miscselect in SECS + * @attributes: attributes for enclave + * @xfrm: XSave-Feature Request Mask (subset of XCR0) + * @attributes_mask: required attributes in SECS + * @xfrm_mask: required XFRM in SECS + * @mrenclave: SHA256-hash of the enclave contents + * @isvprodid: a user-defined value that is used in key derivation + * @isvsvn: a user-defined value that is used in key derivation + */ +struct sgx_sigstruct_body { + u32 miscselect; + u32 misc_mask; + u8 reserved2[20]; + u64 attributes; + u64 xfrm; + u64 attributes_mask; + u64 xfrm_mask; + u8 mrenclave[32]; + u8 reserved3[32]; + u16 isvprodid; + u16 isvsvn; +} __packed; + +/** + * struct sgx_sigstruct - an enclave signature + * @header: defines author of the enclave + * @modulus: the modulus of the public key + * @exponent: the exponent of the public key + * @signature: the signature calculated over the fields except modulus, + * @body: defines contents of the enclave + * @q1: a value used in RSA signature verification + * @q2: a value used in RSA signature verification + * + * Header and body are the parts that are actual signed. The remaining fields + * define the signature of the enclave. + */ +struct sgx_sigstruct { + struct sgx_sigstruct_header header; + u8 modulus[SGX_MODULUS_SIZE]; + u32 exponent; + u8 signature[SGX_MODULUS_SIZE]; + struct sgx_sigstruct_body body; + u8 reserved4[12]; + u8 q1[SGX_MODULUS_SIZE]; + u8 q2[SGX_MODULUS_SIZE]; +} __packed; + +#define SGX_EINITTOKEN_RESERVED1_SIZE 11 +#define SGX_EINITTOKEN_RESERVED2_SIZE 32 +#define SGX_EINITTOKEN_RESERVED3_SIZE 32 +#define SGX_EINITTOKEN_RESERVED4_SIZE 24 + +/** + * struct sgx_einittoken - a token permitting to launch an enclave + * @valid: one if valid and zero if invalid + * @attributes: attributes for enclave + * @xfrm: XSave-Feature Request Mask (subset of XCR0) + * @mrenclave: SHA256-hash of the enclave contents + * @mrsigner: SHA256-hash of the public key used to sign the + * SIGSTRUCT + * @le_cpusvn: a value that reflects the SGX implementation + * running in in the CPU + * @le_isvprodid: a user-defined value that is used in key + * derivation + * @le_isvsvn: a user-defined value that is used in key + * derivation + * @le_keyed_miscselect: LE's miscselect masked with the token keys + * miscselect + * @le_keyed_attributes: LE's attributes masked with the token keys + * attributes + * @le_keyed_xfrm: LE's XFRM masked with the token keys xfrm + * @salt: random salt for wear-out protection + * @mac: CMAC over the preceding fields + * + * An enclave with EINITTOKENKEY attribute can access a key with the same name + * by using ENCLS(EGETKEY) and use this to sign cryptographic tokens that can + * be passed to ENCLS(EINIT) to permit the launch of other enclaves. This is + * the only viable way to launch enclaves if IA32_SGXLEPUBKEYHASHn MSRs are + * locked assuming that there is a Launch Enclave (LE) available that can be + * used for generating these tokens. + */ +struct sgx_einittoken { + u32 valid; + u32 reserved1[SGX_EINITTOKEN_RESERVED1_SIZE]; + u64 attributes; + u64 xfrm; + u8 mrenclave[32]; + u8 reserved2[SGX_EINITTOKEN_RESERVED2_SIZE]; + u8 mrsigner[32]; + u8 reserved3[SGX_EINITTOKEN_RESERVED3_SIZE]; + u8 le_cpusvn[16]; + u16 le_isvprodid; + u16 le_isvsvn; + u8 reserved4[SGX_EINITTOKEN_RESERVED4_SIZE]; + u32 le_keyed_miscselect; + u64 le_keyed_attributes; + u64 le_keyed_xfrm; + u8 salt[32]; + u8 mac[16]; +} __packed __aligned(512); + +#endif /* _ASM_X86_SGX_ARCH_H */ From patchwork Wed Apr 17 10:39:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905161 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A825217E0 for ; Wed, 17 Apr 2019 10:41:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 91473289F5 for ; Wed, 17 Apr 2019 10:41:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 84F9F28AD4; Wed, 17 Apr 2019 10:41:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AFD82289F5 for ; Wed, 17 Apr 2019 10:41:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729779AbfDQKlJ (ORCPT ); Wed, 17 Apr 2019 06:41:09 -0400 Received: from mga04.intel.com ([192.55.52.120]:54636 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731508AbfDQKlJ (ORCPT ); Wed, 17 Apr 2019 06:41:09 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:08 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120010" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:03 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 11/28] x86/sgx: Add wrappers for ENCLS leaf functions Date: Wed, 17 Apr 2019 13:39:21 +0300 Message-Id: <20190417103938.7762-12-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP ENCLS is an umbrella instruction for a variety of cpl0 SGX functions. The ENCLS function that is executed is specified in EAX, with each function potentially having more leaf-specific operands beyond EAX. ENCLS introduces its own (positive value) error codes that (some) leafs use to return failure information in EAX. Leafs that return an error code also modify RFLAGS. And finally, ENCLS generates ENCLS-specific non-fatal #GPs and #PFs, i.e. a bug-free kernel may encounter faults on ENCLS that must be handled gracefully. Because of the complexity involved in encoding ENCLS and handling its assortment of failure paths, executing any given leaf is not a simple matter of emitting ENCLS. To enable adding support for ENCLS leafs with minimal fuss, add a two-layer macro system along with an encoding scheme to allow wrappers to return trap numbers along ENCLS-specific error codes. The bottom layer of the macro system splits between the leafs that return an error code and those that do not. The second layer generates the correct input/output annotations based on the number of operands for each leaf function. Signed-off-by: Jarkko Sakkinen Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- arch/x86/kernel/cpu/sgx/Makefile | 1 + arch/x86/kernel/cpu/sgx/encls.c | 21 +++ arch/x86/kernel/cpu/sgx/encls.h | 244 +++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 arch/x86/kernel/cpu/sgx/Makefile create mode 100644 arch/x86/kernel/cpu/sgx/encls.c create mode 100644 arch/x86/kernel/cpu/sgx/encls.h diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile new file mode 100644 index 000000000000..4432d935894e --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/Makefile @@ -0,0 +1 @@ +obj-y += encls.o diff --git a/arch/x86/kernel/cpu/sgx/encls.c b/arch/x86/kernel/cpu/sgx/encls.c new file mode 100644 index 000000000000..5045f1365e07 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encls.c @@ -0,0 +1,21 @@ +#include +#include +#include "encls.h" +#include "sgx.h" + +/** + * encls_failed() - Check if an ENCLS leaf function failed + * @ret: the return value of an ENCLS leaf function call + * + * Check if an ENCLS leaf function failed. This is a condition where the leaf + * function causes a fault that is not caused by an EPCM conflict. + * + * Return: true if there was a fault other than an EPCM conflict + */ +bool encls_failed(int ret) +{ + int epcm_trapnr = boot_cpu_has(X86_FEATURE_SGX2) ? + X86_TRAP_PF : X86_TRAP_GP; + + return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr; +} diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h new file mode 100644 index 000000000000..aea3b9d09936 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encls.h @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +#ifndef _X86_ENCLS_H +#define _X86_ENCLS_H + +#include +#include +#include +#include +#include +#include +#include "arch.h" + +/** + * ENCLS_FAULT_FLAG - flag signifying an ENCLS return code is a trapnr + * + * ENCLS has its own (positive value) error codes and also generates + * ENCLS specific #GP and #PF faults. And the ENCLS values get munged + * with system error codes as everything percolates back up the stack. + * Unfortunately (for us), we need to precisely identify each unique + * error code, e.g. the action taken if EWB fails varies based on the + * type of fault and on the exact SGX error code, i.e. we can't simply + * convert all faults to -EFAULT. + * + * To make all three error types coexist, we set bit 30 to identify an + * ENCLS fault. Bit 31 (technically bits N:31) is used to differentiate + * between positive (faults and SGX error codes) and negative (system + * error codes) values. + */ +#define ENCLS_FAULT_FLAG 0x40000000 + +/** + * Retrieve the encoded trapnr from the specified return code. + */ +#define ENCLS_TRAPNR(r) ((r) & ~ENCLS_FAULT_FLAG) + +/* Issue a WARN() about an ENCLS leaf. */ +#define ENCLS_WARN(r, name) { \ + do { \ + int _r = (r); \ + WARN(_r, "sgx: %s returned %d (0x%x)\n", (name), _r, \ + _r); \ + } while (0); \ +} + +/** + * encls_faulted() - Check if ENCLS leaf function faulted + * @ret: the return value of an ENCLS leaf function call + * + * Return: true if the fault flag is set + */ +static inline bool encls_faulted(int ret) +{ + return (ret & ENCLS_FAULT_FLAG) != 0; +} + +/** + * encls_returned_code() - Check if an ENCLS leaf function returned a code + * @ret: the return value of an ENCLS leaf function call + * + * Check if an ENCLS leaf function returned an error or information code. + * + * Return: true if there was a fault other than an EPCM conflict + */ +static inline bool encls_returned_code(int ret) +{ + return !encls_faulted(ret) && ret; +} + +bool encls_failed(int ret); + +/** + * __encls_ret_N - encode an ENCLS leaf that returns an error code in EAX + * @rax: leaf number + * @inputs: asm inputs for the leaf + * + * Emit assembly for an ENCLS leaf that returns an error code, e.g. EREMOVE. + * And because SGX isn't complex enough as it is, leafs that return an error + * code also modify flags. + * + * Return: + * 0 on success, + * SGX error code on failure + */ +#define __encls_ret_N(rax, inputs...) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE_FAULT(1b, 3b) \ + : "=a"(ret) \ + : "a"(rax), inputs \ + : "memory", "cc"); \ + ret; \ + }) + +#define __encls_ret_1(rax, rcx) \ + ({ \ + __encls_ret_N(rax, "c"(rcx)); \ + }) + +#define __encls_ret_2(rax, rbx, rcx) \ + ({ \ + __encls_ret_N(rax, "b"(rbx), "c"(rcx)); \ + }) + +#define __encls_ret_3(rax, rbx, rcx, rdx) \ + ({ \ + __encls_ret_N(rax, "b"(rbx), "c"(rcx), "d"(rdx)); \ + }) + +/** + * __encls_N - encode an ENCLS leaf that doesn't return an error code + * @rax: leaf number + * @rbx_out: optional output variable + * @inputs: asm inputs for the leaf + * + * Emit assembly for an ENCLS leaf that does not return an error code, + * e.g. ECREATE. Leaves without error codes either succeed or fault. + * @rbx_out is an optional parameter for use by EDGBRD, which returns + * the the requested value in RBX. + * + * Return: + * 0 on success, + * trapnr with ENCLS_FAULT_FLAG set on fault + */ +#define __encls_N(rax, rbx_out, inputs...) \ + ({ \ + int ret; \ + asm volatile( \ + "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ + " xor %%eax,%%eax;\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \ + " jmp 2b\n" \ + ".previous\n" \ + _ASM_EXTABLE_FAULT(1b, 3b) \ + : "=a"(ret), "=b"(rbx_out) \ + : "a"(rax), inputs \ + : "memory"); \ + ret; \ + }) + +#define __encls_2(rax, rbx, rcx) \ + ({ \ + unsigned long ign_rbx_out; \ + __encls_N(rax, ign_rbx_out, "b"(rbx), "c"(rcx)); \ + }) + +#define __encls_1_1(rax, data, rcx) \ + ({ \ + unsigned long rbx_out; \ + int ret = __encls_N(rax, rbx_out, "c"(rcx)); \ + if (!ret) \ + data = rbx_out; \ + ret; \ + }) + +static inline int __ecreate(struct sgx_pageinfo *pginfo, void *secs) +{ + return __encls_2(SGX_ECREATE, pginfo, secs); +} + +static inline int __eextend(void *secs, void *addr) +{ + return __encls_2(SGX_EEXTEND, secs, addr); +} + +static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr) +{ + return __encls_2(SGX_EADD, pginfo, addr); +} + +static inline int __einit(void *sigstruct, struct sgx_einittoken *einittoken, + void *secs) +{ + return __encls_ret_3(SGX_EINIT, sigstruct, secs, einittoken); +} + +static inline int __eremove(void *addr) +{ + return __encls_ret_1(SGX_EREMOVE, addr); +} + +static inline int __edbgwr(void *addr, unsigned long *data) +{ + return __encls_2(SGX_EDGBWR, *data, addr); +} + +static inline int __edbgrd(void *addr, unsigned long *data) +{ + return __encls_1_1(SGX_EDGBRD, *data, addr); +} + +static inline int __etrack(void *addr) +{ + return __encls_ret_1(SGX_ETRACK, addr); +} + +static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr, + void *va) +{ + return __encls_ret_3(SGX_ELDU, pginfo, addr, va); +} + +static inline int __eblock(void *addr) +{ + return __encls_ret_1(SGX_EBLOCK, addr); +} + +static inline int __epa(void *addr) +{ + unsigned long rbx = SGX_PAGE_TYPE_VA; + + return __encls_2(SGX_EPA, rbx, addr); +} + +static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr, + void *va) +{ + return __encls_ret_3(SGX_EWB, pginfo, addr, va); +} + +static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr) +{ + return __encls_2(SGX_EAUG, pginfo, addr); +} + +static inline int __emodpr(struct sgx_secinfo *secinfo, void *addr) +{ + return __encls_ret_2(SGX_EMODPR, secinfo, addr); +} + +static inline int __emodt(struct sgx_secinfo *secinfo, void *addr) +{ + return __encls_ret_2(SGX_EMODT, secinfo, addr); +} + +#endif /* _X86_ENCLS_H */ From patchwork Wed Apr 17 10:39:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905163 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BD93613B5 for ; Wed, 17 Apr 2019 10:41:17 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A4CDE289F5 for ; Wed, 17 Apr 2019 10:41:17 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 986F028AD4; Wed, 17 Apr 2019 10:41:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AF0BB289F5 for ; Wed, 17 Apr 2019 10:41:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730813AbfDQKlQ (ORCPT ); Wed, 17 Apr 2019 06:41:16 -0400 Received: from mga02.intel.com ([134.134.136.20]:29408 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729522AbfDQKlQ (ORCPT ); Wed, 17 Apr 2019 06:41:16 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:15 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120030" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:09 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 12/28] x86/sgx: Enumerate and track EPC sections Date: Wed, 17 Apr 2019 13:39:22 +0300 Message-Id: <20190417103938.7762-13-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Enumerate Enclave Page Cache (EPC) sections via CPUID and add the data structures necessary to track EPC pages so that they can be allocated, freed and managed. As a system may have multiple EPC sections, invoke CPUID on SGX sub-leafs until an invalid leaf is encountered. On NUMA systems, a node can have at most one bank. A bank can be at most part of two nodes. SGX supports both nodes with a single memory controller and also sub-cluster nodes with severals memory controllers on a single die. For simplicity, support a maximum of eight EPC sections. Current client hardware supports only a single section, while upcoming server hardware will support at most eight sections. Bounding the number of sections also allows the section ID to be embedded along with a page's offset in a single unsigned long, enabling easy retrieval of both the VA and PA for a given page. Signed-off-by: Sean Christopherson Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen Co-developed-by: Suresh Siddha Signed-off-by: Suresh Siddha Co-developed-by: Serge Ayoun Signed-off-by: Serge Ayoun --- arch/x86/Kconfig | 12 +++ arch/x86/kernel/cpu/Makefile | 1 + arch/x86/kernel/cpu/sgx/Makefile | 2 +- arch/x86/kernel/cpu/sgx/main.c | 158 ++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/sgx/reclaim.c | 84 ++++++++++++++++ arch/x86/kernel/cpu/sgx/sgx.h | 67 +++++++++++++ 6 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 arch/x86/kernel/cpu/sgx/main.c create mode 100644 arch/x86/kernel/cpu/sgx/reclaim.c create mode 100644 arch/x86/kernel/cpu/sgx/sgx.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5ad92419be19..5d90a20621cb 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1921,6 +1921,18 @@ config X86_INTEL_MEMORY_PROTECTION_KEYS If unsure, say y. +config INTEL_SGX + bool "Intel SGX core functionality" + depends on X86_64 && CPU_SUP_INTEL + ---help--- + Intel(R) SGX is a set of CPU instructions that can be used by + applications to set aside private regions of code and data, referred + to as enclaves. An enclave's private memory can only be accessed by + code running within the enclave. Accesses from outside the enclave, + including other enclaves, are disallowed by hardware. + + If unsure, say N. + config EFI bool "EFI runtime service support" depends on ACPI diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index cfd24f9f7614..d1163c0fd5d6 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_X86_MCE) += mce/ obj-$(CONFIG_MTRR) += mtrr/ obj-$(CONFIG_MICROCODE) += microcode/ obj-$(CONFIG_X86_CPU_RESCTRL) += resctrl/ +obj-$(CONFIG_INTEL_SGX) += sgx/ obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile index 4432d935894e..fa930e292110 100644 --- a/arch/x86/kernel/cpu/sgx/Makefile +++ b/arch/x86/kernel/cpu/sgx/Makefile @@ -1 +1 @@ -obj-y += encls.o +obj-y += encls.o main.o reclaim.o diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c new file mode 100644 index 000000000000..e2317f6e4374 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/main.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-17 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include "arch.h" +#include "sgx.h" + +struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; +EXPORT_SYMBOL_GPL(sgx_epc_sections); + +int sgx_nr_epc_sections; + +static __init void sgx_free_epc_section(struct sgx_epc_section *section) +{ + struct sgx_epc_page *page; + + while (!list_empty(§ion->page_list)) { + page = list_first_entry(§ion->page_list, + struct sgx_epc_page, list); + list_del(&page->list); + kfree(page); + } + + while (!list_empty(§ion->unsanitized_page_list)) { + page = list_first_entry(§ion->unsanitized_page_list, + struct sgx_epc_page, list); + list_del(&page->list); + kfree(page); + } + + memunmap(section->va); +} + +static __init int sgx_init_epc_section(u64 addr, u64 size, unsigned long index, + struct sgx_epc_section *section) +{ + unsigned long nr_pages = size >> PAGE_SHIFT; + struct sgx_epc_page *page; + unsigned long i; + + section->va = memremap(addr, size, MEMREMAP_WB); + if (!section->va) + return -ENOMEM; + + section->pa = addr; + spin_lock_init(§ion->lock); + INIT_LIST_HEAD(§ion->page_list); + INIT_LIST_HEAD(§ion->unsanitized_page_list); + + for (i = 0; i < nr_pages; i++) { + page = kzalloc(sizeof(*page), GFP_KERNEL); + if (!page) + goto out; + page->desc = (addr + (i << PAGE_SHIFT)) | index; + list_add_tail(&page->list, §ion->unsanitized_page_list); + section->free_cnt++; + } + + return 0; +out: + sgx_free_epc_section(section); + return -ENOMEM; +} + +static __init void sgx_page_cache_teardown(void) +{ + int i; + + for (i = 0; i < sgx_nr_epc_sections; i++) + sgx_free_epc_section(&sgx_epc_sections[i]); +} + +/** + * A section metric is concatenated in a way that @low bits 12-31 define the + * bits 12-31 of the metric and @high bits 0-19 define the bits 32-51 of the + * metric. + */ +static inline u64 sgx_calc_section_metric(u64 low, u64 high) +{ + return (low & GENMASK_ULL(31, 12)) + + ((high & GENMASK_ULL(19, 0)) << 32); +} + +static __init int sgx_page_cache_init(void) +{ + u32 eax, ebx, ecx, edx, type; + u64 pa, size; + int ret; + int i; + + BUILD_BUG_ON(SGX_MAX_EPC_SECTIONS > (SGX_EPC_SECTION_MASK + 1)); + + for (i = 0; i < (SGX_MAX_EPC_SECTIONS + 1); i++) { + cpuid_count(SGX_CPUID, i + SGX_CPUID_FIRST_VARIABLE_SUB_LEAF, + &eax, &ebx, &ecx, &edx); + + type = eax & SGX_CPUID_SUB_LEAF_TYPE_MASK; + if (type == SGX_CPUID_SUB_LEAF_INVALID) + break; + if (type != SGX_CPUID_SUB_LEAF_EPC_SECTION) { + pr_err_once("sgx: Unknown sub-leaf type: %u\n", type); + return -ENODEV; + } + if (i == SGX_MAX_EPC_SECTIONS) { + pr_warn("sgx: More than " + __stringify(SGX_MAX_EPC_SECTIONS) + " EPC sections\n"); + break; + } + + pa = sgx_calc_section_metric(eax, ebx); + size = sgx_calc_section_metric(ecx, edx); + pr_info("sgx: EPC section 0x%llx-0x%llx\n", pa, pa + size - 1); + + ret = sgx_init_epc_section(pa, size, i, &sgx_epc_sections[i]); + if (ret) { + sgx_page_cache_teardown(); + return ret; + } + + sgx_nr_epc_sections++; + } + + if (!sgx_nr_epc_sections) { + pr_err("sgx: There are zero EPC sections.\n"); + return -ENODEV; + } + + return 0; +} + +static __init int sgx_init(void) +{ + int ret; + + if (!boot_cpu_has(X86_FEATURE_SGX)) + return false; + + ret = sgx_page_cache_init(); + if (ret) + return ret; + + ret = sgx_page_reclaimer_init(); + if (ret) { + sgx_page_cache_teardown(); + return ret; + } + + return 0; +} + +arch_initcall(sgx_init); diff --git a/arch/x86/kernel/cpu/sgx/reclaim.c b/arch/x86/kernel/cpu/sgx/reclaim.c new file mode 100644 index 000000000000..042769f03be9 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/reclaim.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-19 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "encls.h" +#include "sgx.h" + +static struct task_struct *ksgxswapd_tsk; + +static void sgx_sanitize_section(struct sgx_epc_section *section) +{ + struct sgx_epc_page *page, *tmp; + LIST_HEAD(secs_list); + int ret; + + while (!list_empty(§ion->unsanitized_page_list)) { + if (kthread_should_stop()) + return; + + spin_lock(§ion->lock); + + page = list_first_entry(§ion->unsanitized_page_list, + struct sgx_epc_page, list); + + ret = __eremove(sgx_epc_addr(page)); + if (!ret) + list_move(&page->list, §ion->page_list); + else + list_move_tail(&page->list, &secs_list); + + spin_unlock(§ion->lock); + + cond_resched(); + } + + list_for_each_entry_safe(page, tmp, &secs_list, list) { + if (kthread_should_stop()) + return; + + ret = __eremove(sgx_epc_addr(page)); + if (!WARN_ON_ONCE(ret)) { + spin_lock(§ion->lock); + list_move(&page->list, §ion->page_list); + spin_unlock(§ion->lock); + } else { + list_del(&page->list); + kfree(page); + } + + cond_resched(); + } +} + +static int ksgxswapd(void *p) +{ + int i; + + set_freezable(); + + for (i = 0; i < sgx_nr_epc_sections; i++) + sgx_sanitize_section(&sgx_epc_sections[i]); + + return 0; +} + +int sgx_page_reclaimer_init(void) +{ + struct task_struct *tsk; + + tsk = kthread_run(ksgxswapd, NULL, "ksgxswapd"); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); + + ksgxswapd_tsk = tsk; + + return 0; +} diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h new file mode 100644 index 000000000000..3009ec816339 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/sgx.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +#ifndef _X86_SGX_H +#define _X86_SGX_H + +#include +#include +#include +#include +#include +#include +#include + +struct sgx_epc_page { + unsigned long desc; + struct list_head list; +}; + +/** + * struct sgx_epc_section + * + * The firmware can define multiple chunks of EPC to the different areas of the + * physical memory e.g. for memory areas of the each node. This structure is + * used to store EPC pages for one EPC section and virtual memory area where + * the pages have been mapped. + */ +struct sgx_epc_section { + unsigned long pa; + void *va; + struct list_head page_list; + struct list_head unsanitized_page_list; + unsigned long free_cnt; + spinlock_t lock; +}; + +#define SGX_MAX_EPC_SECTIONS 8 + +extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; + +/** + * enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor + * %SGX_EPC_SECTION_MASK: SGX allows to have multiple EPC sections in the + * physical memory. The existing and near-future + * hardware defines at most eight sections, hence + * three bits to hold a section. + */ +enum sgx_epc_page_desc { + SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0), + /* bits 12-63 are reserved for the physical page address of the page */ +}; + +static inline struct sgx_epc_section *sgx_epc_section(struct sgx_epc_page *page) +{ + return &sgx_epc_sections[page->desc & SGX_EPC_SECTION_MASK]; +} + +static inline void *sgx_epc_addr(struct sgx_epc_page *page) +{ + struct sgx_epc_section *section = sgx_epc_section(page); + + return section->va + (page->desc & PAGE_MASK) - section->pa; +} + +extern int sgx_nr_epc_sections; + +int sgx_page_reclaimer_init(void); + +#endif /* _X86_SGX_H */ From patchwork Wed Apr 17 10:39:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905165 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EFF8917E0 for ; Wed, 17 Apr 2019 10:41:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DA7C6289F5 for ; Wed, 17 Apr 2019 10:41:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CE2D128AD4; Wed, 17 Apr 2019 10:41:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BA0CF289F5 for ; Wed, 17 Apr 2019 10:41:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731912AbfDQKlX (ORCPT ); Wed, 17 Apr 2019 06:41:23 -0400 Received: from mga09.intel.com ([134.134.136.24]:33516 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729522AbfDQKlX (ORCPT ); Wed, 17 Apr 2019 06:41:23 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:22 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120049" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:16 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 13/28] x86/sgx: Add functions to allocate and free EPC pages Date: Wed, 17 Apr 2019 13:39:23 +0300 Message-Id: <20190417103938.7762-14-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP At this time there is no support for reclaiming pages prior to the owner explicitly freeing the page. As for freeing pages, because freeing a page is expected to succeed in the vast majority of cases and because most call sites will not be equipped to handle failure, provide a variant for freeing a page that warns on failure, e.g. due to ENCLS[EREMOVE] failing. Signed-off-by: Jarkko Sakkinen Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson --- arch/x86/kernel/cpu/sgx/main.c | 90 ++++++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/sgx/sgx.h | 4 ++ 2 files changed, 94 insertions(+) diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c index e2317f6e4374..6b4727df72ca 100644 --- a/arch/x86/kernel/cpu/sgx/main.c +++ b/arch/x86/kernel/cpu/sgx/main.c @@ -9,6 +9,7 @@ #include #include #include "arch.h" +#include "encls.h" #include "sgx.h" struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; @@ -16,6 +17,95 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections); int sgx_nr_epc_sections; +static struct sgx_epc_page *sgx_section_get_page( + struct sgx_epc_section *section) +{ + struct sgx_epc_page *page; + + if (!section->free_cnt) + return NULL; + + page = list_first_entry(§ion->page_list, + struct sgx_epc_page, list); + list_del_init(&page->list); + section->free_cnt--; + return page; +} + +/** + * sgx_alloc_page - Allocate an EPC page + * + * Try to grab a page from the free EPC page list. + * + * Return: + * a pointer to a &struct sgx_epc_page instance, + * -errno on error + */ +struct sgx_epc_page *sgx_alloc_page(void) +{ + struct sgx_epc_section *section; + struct sgx_epc_page *page; + int i; + + for (i = 0; i < sgx_nr_epc_sections; i++) { + section = &sgx_epc_sections[i]; + spin_lock(§ion->lock); + page = sgx_section_get_page(section); + spin_unlock(§ion->lock); + + if (page) + return page; + } + + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL_GPL(sgx_alloc_page); + +/** + * __sgx_free_page - Free an EPC page + * @page: pointer a previously allocated EPC page + * + * EREMOVE an EPC page and insert it back to the list of free pages. + * + * Return: + * 0 on success + * SGX error code if EREMOVE fails + */ +int __sgx_free_page(struct sgx_epc_page *page) +{ + struct sgx_epc_section *section = sgx_epc_section(page); + int ret; + + ret = __eremove(sgx_epc_addr(page)); + if (ret) + return ret; + + spin_lock(§ion->lock); + list_add_tail(&page->list, §ion->page_list); + section->free_cnt++; + spin_unlock(§ion->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(__sgx_free_page); + +/** + * sgx_free_page - Free an EPC page and WARN on failure + * @page: pointer to a previously allocated EPC page + * + * EREMOVE an EPC page and insert it back to the list of free pages, and WARN + * if EREMOVE fails. For use when the call site cannot (or chooses not to) + * handle failure, i.e. the page is leaked on failure. + */ +void sgx_free_page(struct sgx_epc_page *page) +{ + int ret; + + ret = __sgx_free_page(page); + WARN(ret > 0, "sgx: EREMOVE returned %d (0x%x)", ret, ret); +} +EXPORT_SYMBOL_GPL(sgx_free_page); + static __init void sgx_free_epc_section(struct sgx_epc_section *section) { struct sgx_epc_page *page; diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h index 3009ec816339..210510a28ce0 100644 --- a/arch/x86/kernel/cpu/sgx/sgx.h +++ b/arch/x86/kernel/cpu/sgx/sgx.h @@ -64,4 +64,8 @@ extern int sgx_nr_epc_sections; int sgx_page_reclaimer_init(void); +struct sgx_epc_page *sgx_alloc_page(void); +int __sgx_free_page(struct sgx_epc_page *page); +void sgx_free_page(struct sgx_epc_page *page); + #endif /* _X86_SGX_H */ From patchwork Wed Apr 17 10:39:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905167 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A5E2017E0 for ; Wed, 17 Apr 2019 10:41:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8CB2D289F5 for ; Wed, 17 Apr 2019 10:41:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8128128AD4; Wed, 17 Apr 2019 10:41:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0C688289F5 for ; Wed, 17 Apr 2019 10:41:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731605AbfDQKlb (ORCPT ); Wed, 17 Apr 2019 06:41:31 -0400 Received: from mga12.intel.com ([192.55.52.136]:43378 "EHLO mga12.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729522AbfDQKlb (ORCPT ); Wed, 17 Apr 2019 06:41:31 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:30 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120060" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:24 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 14/28] x86/sgx: Add sgx_einit() for initializing enclaves Date: Wed, 17 Apr 2019 13:39:24 +0300 Message-Id: <20190417103938.7762-15-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Add a helper function to perform ENCLS(EINIT) with the correct LE hash MSR values. ENCLS[EINIT] initializes an enclave, verifying the enclave's measurement and preparing it for execution, i.e. the enclave cannot be run until it has been initialized. The measurement aspect of EINIT references the MSR_IA32_SGXLEPUBKEYHASH* MSRs, with the CPU comparing CPU compares the key (technically its hash) used to sign the enclave[1] with the key hash stored in the MSRs, and will reject EINIT if the keys do not match. A per-cpu cache is used to avoid writing the MSRs as writing the MSRs is extraordinarily expensive, e.g. 300-400 cycles per MSR. Because the cache may become stale, force update the MSRs and retry EINIT if the first EINIT fails due to an "invalid token". An invalid token error does not necessarily mean the MSRs need to be updated, but the cost of an unnecessary write is minimal relative to the cost of EINIT itself. [1] For EINIT's purposes, the effective signer of the enclave may be the enclave's owner, or a separate Launch Enclave that has created an EINIT token for the target enclave. When using an EINIT token, the key used to sign the token must match the MSRs in order for EINIT to succeed. Signed-off-by: Sean Christopherson Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- arch/x86/kernel/cpu/sgx/main.c | 51 ++++++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/sgx/sgx.h | 2 ++ 2 files changed, 53 insertions(+) diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c index 6b4727df72ca..d3ed742e90fe 100644 --- a/arch/x86/kernel/cpu/sgx/main.c +++ b/arch/x86/kernel/cpu/sgx/main.c @@ -17,6 +17,9 @@ EXPORT_SYMBOL_GPL(sgx_epc_sections); int sgx_nr_epc_sections; +/* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */ +static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache); + static struct sgx_epc_page *sgx_section_get_page( struct sgx_epc_section *section) { @@ -106,6 +109,54 @@ void sgx_free_page(struct sgx_epc_page *page) } EXPORT_SYMBOL_GPL(sgx_free_page); +static void sgx_update_lepubkeyhash_msrs(u64 *lepubkeyhash, bool enforce) +{ + u64 *cache; + int i; + + cache = per_cpu(sgx_lepubkeyhash_cache, smp_processor_id()); + for (i = 0; i < 4; i++) { + if (enforce || (lepubkeyhash[i] != cache[i])) { + wrmsrl(MSR_IA32_SGXLEPUBKEYHASH0 + i, lepubkeyhash[i]); + cache[i] = lepubkeyhash[i]; + } + } +} + +/** + * sgx_einit - initialize an enclave + * @sigstruct: a pointer a SIGSTRUCT + * @token: a pointer an EINITTOKEN (optional) + * @secs: a pointer a SECS + * @lepubkeyhash: the desired value for IA32_SGXLEPUBKEYHASHx MSRs + * + * Execute ENCLS[EINIT], writing the IA32_SGXLEPUBKEYHASHx MSRs according + * to @lepubkeyhash (if possible and necessary). + * + * Return: + * 0 on success, + * -errno or SGX error on failure + */ +int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token, + struct sgx_epc_page *secs, u64 *lepubkeyhash) +{ + int ret; + + if (!boot_cpu_has(X86_FEATURE_SGX_LC)) + return __einit(sigstruct, token, sgx_epc_addr(secs)); + + preempt_disable(); + sgx_update_lepubkeyhash_msrs(lepubkeyhash, false); + ret = __einit(sigstruct, token, sgx_epc_addr(secs)); + if (ret == SGX_INVALID_EINITTOKEN) { + sgx_update_lepubkeyhash_msrs(lepubkeyhash, true); + ret = __einit(sigstruct, token, sgx_epc_addr(secs)); + } + preempt_enable(); + return ret; +} +EXPORT_SYMBOL(sgx_einit); + static __init void sgx_free_epc_section(struct sgx_epc_section *section) { struct sgx_epc_page *page; diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h index 210510a28ce0..41d4130c33a2 100644 --- a/arch/x86/kernel/cpu/sgx/sgx.h +++ b/arch/x86/kernel/cpu/sgx/sgx.h @@ -67,5 +67,7 @@ int sgx_page_reclaimer_init(void); struct sgx_epc_page *sgx_alloc_page(void); int __sgx_free_page(struct sgx_epc_page *page); void sgx_free_page(struct sgx_epc_page *page); +int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token, + struct sgx_epc_page *secs, u64 *lepubkeyhash); #endif /* _X86_SGX_H */ From patchwork Wed Apr 17 10:39:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905169 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 74F7913B5 for ; Wed, 17 Apr 2019 10:41:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5670D28AD1 for ; Wed, 17 Apr 2019 10:41:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 467EE289F5; Wed, 17 Apr 2019 10:41:43 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9DBC6289F5 for ; Wed, 17 Apr 2019 10:41:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731582AbfDQKlk (ORCPT ); Wed, 17 Apr 2019 06:41:40 -0400 Received: from mga09.intel.com ([134.134.136.24]:33525 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729522AbfDQKlk (ORCPT ); Wed, 17 Apr 2019 06:41:40 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:38 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120080" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:31 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 15/28] x86/sgx: Add the Linux SGX Enclave Driver Date: Wed, 17 Apr 2019 13:39:25 +0300 Message-Id: <20190417103938.7762-16-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Intel Software Guard eXtensions (SGX) is a set of CPU instructions that can be used by applications to set aside private regions of code and data. The code outside the enclave is disallowed to access the memory inside the enclave by the CPU access control. This commit adds the Linux SGX Enclave Driver that provides an ioctl API to manage enclaves. The address range for an enclave, commonly referred as ELRANGE in the documentation (e.g. Intel SDM), is reserved with mmap() against /dev/sgx/enclave. After that a set ioctls is used to build the enclave to the ELRANGE. Signed-off-by: Jarkko Sakkinen Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Co-developed-by: Serge Ayoun Signed-off-by: Serge Ayoun Co-developed-by: Shay Katz-zamir Signed-off-by: Shay Katz-zamir Co-developed-by: Suresh Siddha Signed-off-by: Suresh Siddha --- Documentation/ioctl/ioctl-number.txt | 1 + arch/x86/Kconfig | 15 + arch/x86/include/uapi/asm/sgx.h | 57 ++ arch/x86/kernel/cpu/sgx/Makefile | 3 +- arch/x86/kernel/cpu/sgx/driver/Makefile | 3 + arch/x86/kernel/cpu/sgx/driver/driver.h | 38 ++ arch/x86/kernel/cpu/sgx/driver/ioctl.c | 750 ++++++++++++++++++++++++ arch/x86/kernel/cpu/sgx/driver/main.c | 358 +++++++++++ arch/x86/kernel/cpu/sgx/encl.c | 349 +++++++++++ arch/x86/kernel/cpu/sgx/encl.h | 98 ++++ arch/x86/kernel/cpu/sgx/encls.c | 1 + arch/x86/kernel/cpu/sgx/main.c | 3 + arch/x86/kernel/cpu/sgx/sgx.h | 1 + 13 files changed, 1676 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/uapi/asm/sgx.h create mode 100644 arch/x86/kernel/cpu/sgx/driver/Makefile create mode 100644 arch/x86/kernel/cpu/sgx/driver/driver.h create mode 100644 arch/x86/kernel/cpu/sgx/driver/ioctl.c create mode 100644 arch/x86/kernel/cpu/sgx/driver/main.c create mode 100644 arch/x86/kernel/cpu/sgx/encl.c create mode 100644 arch/x86/kernel/cpu/sgx/encl.h diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index c9558146ac58..ef2694221cd0 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -312,6 +312,7 @@ Code Seq#(hex) Include File Comments 0xA3 90-9F linux/dtlk.h 0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem +0xA4 00-02 uapi/asm/sgx.h conflict! 0xAA 00-3F linux/uapi/linux/userfaultfd.h 0xAB 00-1F linux/nbd.h 0xAC 00-1F linux/raw.h diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5d90a20621cb..adea370b331f 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1933,6 +1933,21 @@ config INTEL_SGX If unsure, say N. +config INTEL_SGX_DRIVER + tristate "Intel(R) SGX Driver" + default n + depends on X86_64 && CPU_SUP_INTEL && INTEL_SGX + select CRYPTO + select CRYPTO_SHA256 + ---help--- + This options enables the kernel SGX driver that allows to construct + enclaves to the process memory by using a device node (by default + /dev/sgx) and a set of ioctls. The driver requires that the MSRs + specifying the public key hash for the launch enclave are writable so + that Linux has the full control to run enclaves. + + If unsure, say N. + config EFI bool "EFI runtime service support" depends on ACPI diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h new file mode 100644 index 000000000000..7bf627ac4958 --- /dev/null +++ b/arch/x86/include/uapi/asm/sgx.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2016-18 Intel Corporation. + */ +#ifndef _UAPI_ASM_X86_SGX_H +#define _UAPI_ASM_X86_SGX_H + +#include +#include + +#define SGX_MAGIC 0xA4 + +#define SGX_IOC_ENCLAVE_CREATE \ + _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create) +#define SGX_IOC_ENCLAVE_ADD_PAGE \ + _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page) +#define SGX_IOC_ENCLAVE_INIT \ + _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init) + +/* IOCTL return values */ +#define SGX_POWER_LOST_ENCLAVE 0x40000000 + +/** + * struct sgx_enclave_create - parameter structure for the + * %SGX_IOC_ENCLAVE_CREATE ioctl + * @src: address for the SECS page data + */ +struct sgx_enclave_create { + __u64 src; +}; + +/** + * struct sgx_enclave_add_page - parameter structure for the + * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl + * @addr: address within the ELRANGE + * @src: address for the page data + * @secinfo: address for the SECINFO data + * @mrmask: bitmask for the measured 256 byte chunks + */ +struct sgx_enclave_add_page { + __u64 addr; + __u64 src; + __u64 secinfo; + __u16 mrmask; +} __attribute__((__packed__)); + + +/** + * struct sgx_enclave_init - parameter structure for the + * %SGX_IOC_ENCLAVE_INIT ioctl + * @sigstruct: address for the SIGSTRUCT data + */ +struct sgx_enclave_init { + __u64 sigstruct; +}; + +#endif /* _UAPI_ASM_X86_SGX_H */ diff --git a/arch/x86/kernel/cpu/sgx/Makefile b/arch/x86/kernel/cpu/sgx/Makefile index fa930e292110..e5d1e862969c 100644 --- a/arch/x86/kernel/cpu/sgx/Makefile +++ b/arch/x86/kernel/cpu/sgx/Makefile @@ -1 +1,2 @@ -obj-y += encls.o main.o reclaim.o +obj-y += encl.o encls.o main.o reclaim.o +obj-$(CONFIG_INTEL_SGX_DRIVER) += driver/ diff --git a/arch/x86/kernel/cpu/sgx/driver/Makefile b/arch/x86/kernel/cpu/sgx/driver/Makefile new file mode 100644 index 000000000000..01ebbbb06a47 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/driver/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_INTEL_SGX_DRIVER) += sgx.o +sgx-$(CONFIG_INTEL_SGX_DRIVER) += ioctl.o +sgx-$(CONFIG_INTEL_SGX_DRIVER) += main.o diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h new file mode 100644 index 000000000000..507712eb0a68 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/driver/driver.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/** + * Copyright(c) 2016-19 Intel Corporation. + */ +#ifndef __ARCH_INTEL_SGX_H__ +#define __ARCH_INTEL_SGX_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../arch.h" +#include "../encl.h" +#include "../encls.h" +#include "../sgx.h" + +#define SGX_DRV_NR_DEVICES 2 +#define SGX_EINIT_SPIN_COUNT 20 +#define SGX_EINIT_SLEEP_COUNT 50 +#define SGX_EINIT_SLEEP_TIME 20 + +extern struct workqueue_struct *sgx_encl_wq; +extern u64 sgx_encl_size_max_32; +extern u64 sgx_encl_size_max_64; +extern u32 sgx_misc_reserved_mask; +extern u64 sgx_attributes_reserved_mask; +extern u64 sgx_xfrm_reserved_mask; +extern u32 sgx_xsave_size_tbl[64]; + +extern const struct file_operations sgx_fs_provision_fops; + +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); + +#endif /* __ARCH_X86_INTEL_SGX_H__ */ diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c new file mode 100644 index 000000000000..f88226518b21 --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c @@ -0,0 +1,750 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-19 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "driver.h" + +struct sgx_add_page_req { + struct sgx_encl *encl; + struct sgx_encl_page *encl_page; + struct sgx_secinfo secinfo; + unsigned long mrmask; + struct list_head list; +}; + +static bool sgx_process_add_page_req(struct sgx_add_page_req *req, + struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *encl_page = req->encl_page; + struct sgx_encl *encl = req->encl; + unsigned long page_index = sgx_encl_get_index(encl, encl_page); + struct sgx_secinfo secinfo; + struct sgx_pageinfo pginfo; + struct page *backing; + unsigned long addr; + int ret; + int i; + + if (encl->flags & (SGX_ENCL_SUSPEND | SGX_ENCL_DEAD)) + return false; + + addr = SGX_ENCL_PAGE_ADDR(encl_page); + + backing = sgx_encl_get_backing_page(encl, page_index); + if (IS_ERR(backing)) + return false; + + /* + * The SECINFO field must be 64-byte aligned, copy it to a local + * variable that is guaranteed to be aligned as req->secinfo may + * or may not be 64-byte aligned, e.g. req may have been allocated + * via kzalloc which is not aware of __aligned attributes. + */ + memcpy(&secinfo, &req->secinfo, sizeof(secinfo)); + + pginfo.secs = (unsigned long)sgx_epc_addr(encl->secs.epc_page); + pginfo.addr = addr; + pginfo.metadata = (unsigned long)&secinfo; + pginfo.contents = (unsigned long)kmap_atomic(backing); + ret = __eadd(&pginfo, sgx_epc_addr(epc_page)); + kunmap_atomic((void *)(unsigned long)pginfo.contents); + + put_page(backing); + + if (ret) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EADD"); + return false; + } + + for_each_set_bit(i, &req->mrmask, 16) { + ret = __eextend(sgx_epc_addr(encl->secs.epc_page), + sgx_epc_addr(epc_page) + (i * 0x100)); + if (ret) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EEXTEND"); + return false; + } + } + + encl_page->encl = encl; + encl_page->epc_page = epc_page; + encl->secs_child_cnt++; + + return true; +} + +static void sgx_add_page_worker(struct work_struct *work) +{ + struct sgx_add_page_req *req; + bool skip_rest = false; + bool is_empty = false; + struct sgx_encl *encl; + struct sgx_epc_page *epc_page; + + encl = container_of(work, struct sgx_encl, work); + + do { + schedule(); + + mutex_lock(&encl->lock); + if (encl->flags & SGX_ENCL_DEAD) + skip_rest = true; + + req = list_first_entry(&encl->add_page_reqs, + struct sgx_add_page_req, list); + list_del(&req->list); + is_empty = list_empty(&encl->add_page_reqs); + mutex_unlock(&encl->lock); + + if (skip_rest) + goto next; + + epc_page = sgx_alloc_page(); + + mutex_lock(&encl->lock); + + if (IS_ERR(epc_page)) { + sgx_encl_destroy(encl); + skip_rest = true; + } else if (!sgx_process_add_page_req(req, epc_page)) { + sgx_free_page(epc_page); + sgx_encl_destroy(encl); + skip_rest = true; + } + + mutex_unlock(&encl->lock); + +next: + kfree(req); + } while (!kref_put(&encl->refcount, sgx_encl_release) && !is_empty); +} + +static u32 sgx_calc_ssaframesize(u32 miscselect, u64 xfrm) +{ + u32 size_max = PAGE_SIZE; + u32 size; + int i; + + for (i = 2; i < 64; i++) { + if (!((1 << i) & xfrm)) + continue; + + size = SGX_SSA_GPRS_SIZE + sgx_xsave_size_tbl[i]; + if (miscselect & SGX_MISC_EXINFO) + size += SGX_SSA_MISC_EXINFO_SIZE; + + if (size > size_max) + size_max = size; + } + + return PFN_UP(size_max); +} + +static int sgx_validate_secs(const struct sgx_secs *secs, + unsigned long ssaframesize) +{ + if (secs->size < (2 * PAGE_SIZE) || !is_power_of_2(secs->size)) + return -EINVAL; + + if (secs->base & (secs->size - 1)) + return -EINVAL; + + if (secs->miscselect & sgx_misc_reserved_mask || + secs->attributes & sgx_attributes_reserved_mask || + secs->xfrm & sgx_xfrm_reserved_mask) + return -EINVAL; + + if (secs->attributes & SGX_ATTR_MODE64BIT) { + if (secs->size > sgx_encl_size_max_64) + return -EINVAL; + } else if (secs->size > sgx_encl_size_max_32) + return -EINVAL; + + if (!(secs->xfrm & XFEATURE_MASK_FP) || + !(secs->xfrm & XFEATURE_MASK_SSE) || + (((secs->xfrm >> XFEATURE_BNDREGS) & 1) != + ((secs->xfrm >> XFEATURE_BNDCSR) & 1))) + return -EINVAL; + + if (!secs->ssa_frame_size || ssaframesize > secs->ssa_frame_size) + return -EINVAL; + + if (memchr_inv(secs->reserved1, 0, SGX_SECS_RESERVED1_SIZE) || + memchr_inv(secs->reserved2, 0, SGX_SECS_RESERVED2_SIZE) || + memchr_inv(secs->reserved3, 0, SGX_SECS_RESERVED3_SIZE) || + memchr_inv(secs->reserved4, 0, SGX_SECS_RESERVED4_SIZE)) + return -EINVAL; + + return 0; +} + +static struct sgx_encl_page *sgx_encl_page_alloc(struct sgx_encl *encl, + unsigned long addr) +{ + struct sgx_encl_page *encl_page; + int ret; + + if (radix_tree_lookup(&encl->page_tree, PFN_DOWN(addr))) + return ERR_PTR(-EEXIST); + encl_page = kzalloc(sizeof(*encl_page), GFP_KERNEL); + if (!encl_page) + return ERR_PTR(-ENOMEM); + encl_page->desc = addr; + encl_page->encl = encl; + ret = radix_tree_insert(&encl->page_tree, PFN_DOWN(encl_page->desc), + encl_page); + if (ret) { + kfree(encl_page); + return ERR_PTR(ret); + } + return encl_page; +} + +static int sgx_encl_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct sgx_encl *encl = container_of(nb, struct sgx_encl, pm_notifier); + + if (action != PM_SUSPEND_PREPARE && action != PM_HIBERNATION_PREPARE) + return NOTIFY_DONE; + + mutex_lock(&encl->lock); + sgx_encl_destroy(encl); + encl->flags |= SGX_ENCL_SUSPEND; + mutex_unlock(&encl->lock); + flush_work(&encl->work); + return NOTIFY_DONE; +} + +static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) +{ + unsigned long encl_size = secs->size + PAGE_SIZE; + struct sgx_epc_page *secs_epc; + struct sgx_encl_mm *encl_mm; + unsigned long ssaframesize; + struct sgx_pageinfo pginfo; + struct sgx_secinfo secinfo; + struct file *backing; + long ret; + + mutex_lock(&encl->lock); + + if (encl->flags & SGX_ENCL_CREATED) { + ret = -EFAULT; + goto err_out; + } + + ssaframesize = sgx_calc_ssaframesize(secs->miscselect, secs->xfrm); + if (sgx_validate_secs(secs, ssaframesize)) { + ret = -EINVAL; + goto err_out; + } + + backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5), + VM_NORESERVE); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + goto err_out; + } + + encl->backing = backing; + + INIT_WORK(&encl->work, sgx_add_page_worker); + + encl_mm = sgx_encl_mm_add(encl, current->mm); + if (IS_ERR(encl_mm)) { + ret = PTR_ERR(encl_mm); + goto err_out; + } + + secs_epc = sgx_alloc_page(); + if (IS_ERR(secs_epc)) { + ret = PTR_ERR(secs_epc); + goto err_out; + } + + encl->secs.epc_page = secs_epc; + + pginfo.addr = 0; + pginfo.contents = (unsigned long)secs; + pginfo.metadata = (unsigned long)&secinfo; + pginfo.secs = 0; + memset(&secinfo, 0, sizeof(secinfo)); + + ret = __ecreate((void *)&pginfo, sgx_epc_addr(secs_epc)); + if (ret) { + pr_debug("ECREATE returned %ld\n", ret); + goto err_out; + } + + if (secs->attributes & SGX_ATTR_DEBUG) + encl->flags |= SGX_ENCL_DEBUG; + + encl->pm_notifier.notifier_call = &sgx_encl_pm_notifier; + ret = register_pm_notifier(&encl->pm_notifier); + if (ret) { + encl->pm_notifier.notifier_call = NULL; + goto err_out; + } + + encl->secs.encl = encl; + encl->secs_attributes = secs->attributes; + encl->allowed_attributes = SGX_ATTR_ALLOWED_MASK; + encl->base = secs->base; + encl->size = secs->size; + encl->ssaframesize = secs->ssa_frame_size; + encl->flags |= SGX_ENCL_CREATED; + + mutex_unlock(&encl->lock); + return 0; + +err_out: + if (encl->secs.epc_page) { + sgx_free_page(encl->secs.epc_page); + encl->secs.epc_page = NULL; + } + + if (encl->backing) { + fput(encl->backing); + encl->backing = NULL; + } + + if (!list_empty(&encl->mm_list)) { + encl_mm = list_first_entry(&encl->mm_list, struct sgx_encl_mm, + list); + list_del(&encl_mm->list); + kfree(encl_mm); + } + + mutex_unlock(&encl->lock); + return ret; +} + +/** + * sgx_ioc_enclave_create - handler for %SGX_IOC_ENCLAVE_CREATE + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to an &sgx_enclave_create instance + * + * Allocate kernel data structures for a new enclave and execute ECREATE after + * verifying the correctness of the provided SECS. + * + * Note, enforcement of restricted and disallowed attributes is deferred until + * sgx_ioc_enclave_init(), only the architectural correctness of the SECS is + * checked by sgx_ioc_enclave_create(). + * + * Return: + * 0 on success, + * -errno otherwise + */ +static long sgx_ioc_enclave_create(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_create *createp = (struct sgx_enclave_create *)arg; + struct sgx_encl *encl = filep->private_data; + struct page *secs_page; + struct sgx_secs *secs; + int ret; + + secs_page = alloc_page(GFP_HIGHUSER); + if (!secs_page) + return -ENOMEM; + + secs = kmap(secs_page); + if (copy_from_user(secs, (void __user *)createp->src, sizeof(*secs))) { + ret = -EFAULT; + goto out; + } + + + ret = sgx_encl_create(encl, secs); + +out: + kunmap(secs_page); + __free_page(secs_page); + return ret; +} + +static int sgx_validate_secinfo(struct sgx_secinfo *secinfo) +{ + u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; + u64 perm = secinfo->flags & SGX_SECINFO_PERMISSION_MASK; + int i; + + if ((secinfo->flags & SGX_SECINFO_RESERVED_MASK) || + ((perm & SGX_SECINFO_W) && !(perm & SGX_SECINFO_R)) || + (page_type != SGX_SECINFO_TCS && page_type != SGX_SECINFO_TRIM && + page_type != SGX_SECINFO_REG)) + return -EINVAL; + + for (i = 0; i < SGX_SECINFO_RESERVED_SIZE; i++) + if (secinfo->reserved[i]) + return -EINVAL; + + return 0; +} + +static bool sgx_validate_offset(struct sgx_encl *encl, unsigned long offset) +{ + if (offset & (PAGE_SIZE - 1)) + return false; + + if (offset >= encl->size) + return false; + + return true; +} + +static int sgx_validate_tcs(struct sgx_encl *encl, struct sgx_tcs *tcs) +{ + int i; + + if (tcs->flags & SGX_TCS_RESERVED_MASK) + return -EINVAL; + + if (tcs->flags & SGX_TCS_DBGOPTIN) + return -EINVAL; + + if (!sgx_validate_offset(encl, tcs->ssa_offset)) + return -EINVAL; + + if (!sgx_validate_offset(encl, tcs->fs_offset)) + return -EINVAL; + + if (!sgx_validate_offset(encl, tcs->gs_offset)) + return -EINVAL; + + if ((tcs->fs_limit & 0xFFF) != 0xFFF) + return -EINVAL; + + if ((tcs->gs_limit & 0xFFF) != 0xFFF) + return -EINVAL; + + for (i = 0; i < SGX_TCS_RESERVED_SIZE; i++) + if (tcs->reserved[i]) + return -EINVAL; + + return 0; +} + +static int __sgx_encl_add_page(struct sgx_encl *encl, + struct sgx_encl_page *encl_page, + void *data, + struct sgx_secinfo *secinfo, + unsigned int mrmask) +{ + unsigned long page_index = sgx_encl_get_index(encl, encl_page); + u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; + struct sgx_add_page_req *req = NULL; + struct page *backing; + void *backing_ptr; + int empty; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + backing = sgx_encl_get_backing_page(encl, page_index); + if (IS_ERR(backing)) { + kfree(req); + return PTR_ERR(backing); + } + + backing_ptr = kmap(backing); + memcpy(backing_ptr, data, PAGE_SIZE); + kunmap(backing); + if (page_type == SGX_SECINFO_TCS) + encl_page->desc |= SGX_ENCL_PAGE_TCS; + memcpy(&req->secinfo, secinfo, sizeof(*secinfo)); + req->encl = encl; + req->encl_page = encl_page; + req->mrmask = mrmask; + empty = list_empty(&encl->add_page_reqs); + kref_get(&encl->refcount); + list_add_tail(&req->list, &encl->add_page_reqs); + if (empty) + queue_work(sgx_encl_wq, &encl->work); + set_page_dirty(backing); + put_page(backing); + return 0; +} + +static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr, + void *data, struct sgx_secinfo *secinfo, + unsigned int mrmask) +{ + u64 page_type = secinfo->flags & SGX_SECINFO_PAGE_TYPE_MASK; + struct sgx_encl_page *encl_page; + int ret; + + if (sgx_validate_secinfo(secinfo)) + return -EINVAL; + if (page_type == SGX_SECINFO_TCS) { + ret = sgx_validate_tcs(encl, data); + if (ret) + return ret; + } + + mutex_lock(&encl->lock); + + if (!(encl->flags & SGX_ENCL_CREATED) || + (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD))) { + ret = -EFAULT; + goto out; + } + + encl_page = sgx_encl_page_alloc(encl, addr); + if (IS_ERR(encl_page)) { + ret = PTR_ERR(encl_page); + goto out; + } + + ret = __sgx_encl_add_page(encl, encl_page, data, secinfo, mrmask); + if (ret) { + radix_tree_delete(&encl_page->encl->page_tree, + PFN_DOWN(encl_page->desc)); + kfree(encl_page); + } + +out: + mutex_unlock(&encl->lock); + return ret; +} + +/** + * sgx_ioc_enclave_add_page - handler for %SGX_IOC_ENCLAVE_ADD_PAGE + * + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to an &sgx_enclave_add_page instance + * + * Add a page to an uninitialized enclave (EADD), and optionally extend the + * enclave's measurement with the contents of the page (EEXTEND). EADD and + * EEXTEND are done asynchronously via worker threads. A successful + * sgx_ioc_enclave_add_page() only indicates the page has been added to the + * work queue, it does not guarantee adding the page to the enclave will + * succeed. + * + * Return: + * 0 on success, + * -errno otherwise + */ +static long sgx_ioc_enclave_add_page(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_add_page *addp = (void *)arg; + struct sgx_encl *encl = filep->private_data; + struct sgx_secinfo secinfo; + struct page *data_page; + void *data; + int ret; + + if (copy_from_user(&secinfo, (void __user *)addp->secinfo, + sizeof(secinfo))) + return -EFAULT; + + data_page = alloc_page(GFP_HIGHUSER); + if (!data_page) + return -ENOMEM; + + data = kmap(data_page); + + if (copy_from_user((void *)data, (void __user *)addp->src, PAGE_SIZE)) { + ret = -EFAULT; + goto out; + } + + ret = sgx_encl_add_page(encl, addp->addr, data, &secinfo, addp->mrmask); + if (ret) + goto out; + +out: + kunmap(data_page); + __free_page(data_page); + return ret; +} + +static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus, + void *hash) +{ + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tfm; + shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash); +} + +static int sgx_get_key_hash(const void *modulus, void *hash) +{ + struct crypto_shash *tfm; + int ret; + + tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + ret = __sgx_get_key_hash(tfm, modulus, hash); + + crypto_free_shash(tfm); + return ret; +} + +static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct, + struct sgx_einittoken *token) +{ + u64 mrsigner[4]; + int ret; + int i; + int j; + + /* Check that the required attributes have been authorized. */ + if (encl->secs_attributes & ~encl->allowed_attributes) + return -EINVAL; + + ret = sgx_get_key_hash(sigstruct->modulus, mrsigner); + if (ret) + return ret; + + flush_work(&encl->work); + + mutex_lock(&encl->lock); + + if (!(encl->flags & SGX_ENCL_CREATED) || + (encl->flags & (SGX_ENCL_INITIALIZED | SGX_ENCL_DEAD))) { + ret = -EFAULT; + goto err_out; + } + + for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) { + for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) { + ret = sgx_einit(sigstruct, token, encl->secs.epc_page, + mrsigner); + if (ret == SGX_UNMASKED_EVENT) + continue; + else + break; + } + + if (ret != SGX_UNMASKED_EVENT) + break; + + msleep_interruptible(SGX_EINIT_SLEEP_TIME); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto err_out; + } + } + + if (encls_faulted(ret)) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EINIT"); + + sgx_encl_destroy(encl); + ret = -EFAULT; + } else if (encls_returned_code(ret)) { + pr_debug("EINIT returned %d\n", ret); + } else { + encl->flags |= SGX_ENCL_INITIALIZED; + } + +err_out: + mutex_unlock(&encl->lock); + return ret; +} + +/** + * sgx_ioc_enclave_init - handler for %SGX_IOC_ENCLAVE_INIT + * + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to an &sgx_enclave_init instance + * + * Flush any outstanding enqueued EADD operations and perform EINIT. The + * Launch Enclave Public Key Hash MSRs are rewritten as necessary to match + * the enclave's MRSIGNER, which is caculated from the provided sigstruct. + * + * Return: + * 0 on success, + * SGX error code on EINIT failure, + * -errno otherwise + */ +static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_init *initp = (struct sgx_enclave_init *)arg; + struct sgx_encl *encl = filep->private_data; + struct sgx_einittoken *einittoken; + struct sgx_sigstruct *sigstruct; + struct page *initp_page; + int ret; + + initp_page = alloc_page(GFP_HIGHUSER); + if (!initp_page) + return -ENOMEM; + + sigstruct = kmap(initp_page); + einittoken = (struct sgx_einittoken *) + ((unsigned long)sigstruct + PAGE_SIZE / 2); + memset(einittoken, 0, sizeof(*einittoken)); + + if (copy_from_user(sigstruct, (void __user *)initp->sigstruct, + sizeof(*sigstruct))) { + ret = -EFAULT; + goto out; + } + + + ret = sgx_encl_init(encl, sigstruct, einittoken); + +out: + kunmap(initp_page); + __free_page(initp_page); + return ret; +} + +typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd, + unsigned long arg); + +long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + char data[256]; + sgx_ioc_t handler = NULL; + long ret; + + switch (cmd) { + case SGX_IOC_ENCLAVE_CREATE: + handler = sgx_ioc_enclave_create; + break; + case SGX_IOC_ENCLAVE_ADD_PAGE: + handler = sgx_ioc_enclave_add_page; + break; + case SGX_IOC_ENCLAVE_INIT: + handler = sgx_ioc_enclave_init; + break; + default: + return -ENOIOCTLCMD; + } + + if (copy_from_user(data, (void __user *)arg, _IOC_SIZE(cmd))) + return -EFAULT; + + ret = handler(filep, cmd, (unsigned long)((void *)data)); + if (!ret && (cmd & IOC_OUT)) { + if (copy_to_user((void __user *)arg, data, _IOC_SIZE(cmd))) + return -EFAULT; + } + + return ret; +} diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c new file mode 100644 index 000000000000..d371add399cd --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/driver/main.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-18 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include "driver.h" + +MODULE_DESCRIPTION("Intel SGX Enclave Driver"); +MODULE_AUTHOR("Jarkko Sakkinen "); +MODULE_LICENSE("Dual BSD/GPL"); + +struct workqueue_struct *sgx_encl_wq; +u64 sgx_encl_size_max_32; +u64 sgx_encl_size_max_64; +u32 sgx_misc_reserved_mask; +u64 sgx_attributes_reserved_mask; +u64 sgx_xfrm_reserved_mask = ~0x3; +u32 sgx_xsave_size_tbl[64]; + +static int sgx_open(struct inode *inode, struct file *file) +{ + struct sgx_encl *encl; + + encl = kzalloc(sizeof(*encl), GFP_KERNEL); + if (!encl) + return -ENOMEM; + + kref_init(&encl->refcount); + INIT_LIST_HEAD(&encl->add_page_reqs); + INIT_RADIX_TREE(&encl->page_tree, GFP_KERNEL); + mutex_init(&encl->lock); + INIT_LIST_HEAD(&encl->mm_list); + spin_lock_init(&encl->mm_lock); + + file->private_data = encl; + + return 0; +} + +static int sgx_release(struct inode *inode, struct file *file) +{ + struct sgx_encl *encl = file->private_data; + + kref_put(&encl->refcount, sgx_encl_release); + + return 0; +} + +#ifdef CONFIG_COMPAT +static long sgx_compat_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return sgx_ioctl(filep, cmd, arg); +} +#endif + +static int sgx_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sgx_encl *encl = file->private_data; + + vma->vm_ops = &sgx_vm_ops; + vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO; + vma->vm_private_data = encl; + + kref_get(&encl->refcount); + + return 0; +} + +static unsigned long sgx_get_unmapped_area(struct file *file, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + if (len < 2 * PAGE_SIZE || len & (len - 1) || flags & MAP_PRIVATE) + return -EINVAL; + + addr = current->mm->get_unmapped_area(file, addr, 2 * len, pgoff, + flags); + if (IS_ERR_VALUE(addr)) + return addr; + + addr = (addr + (len - 1)) & ~(len - 1); + + return addr; +} + +static const struct file_operations sgx_encl_fops = { + .owner = THIS_MODULE, + .open = sgx_open, + .release = sgx_release, + .unlocked_ioctl = sgx_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sgx_compat_ioctl, +#endif + .mmap = sgx_mmap, + .get_unmapped_area = sgx_get_unmapped_area, +}; + +static const struct file_operations sgx_provision_fops = { + .owner = THIS_MODULE, +}; + +static struct bus_type sgx_bus_type = { + .name = "sgx", +}; + +struct sgx_dev_ctx { + struct device encl_dev; + struct cdev encl_cdev; + struct device provision_dev; + struct cdev provision_cdev; + struct kref refcount; +}; + +static dev_t sgx_devt; + +static void sgx_dev_ctx_free(struct kref *ref) +{ + struct sgx_dev_ctx *ctx = container_of(ref, struct sgx_dev_ctx, + refcount); + + kfree(ctx); +} + +static void sgx_dev_release(struct device *dev) +{ + struct sgx_dev_ctx *ctx = container_of(dev, struct sgx_dev_ctx, + encl_dev); + + kref_put(&ctx->refcount, sgx_dev_ctx_free); +} + +static int sgx_dev_populate(const char *name, struct device *dev, + struct cdev *cdev, struct device *parent, + const struct file_operations *fops, + int minor) +{ + int ret; + + device_initialize(dev); + + dev->bus = &sgx_bus_type; + dev->parent = parent; + dev->devt = MKDEV(MAJOR(sgx_devt), minor); + dev->release = sgx_dev_release; + + ret = dev_set_name(dev, name); + if (ret) { + put_device(dev); + return ret; + } + + cdev_init(cdev, fops); + cdev->owner = THIS_MODULE; + return 0; +} + +static struct sgx_dev_ctx *sgx_dev_ctx_alloc(struct device *parent) +{ + struct sgx_dev_ctx *ctx; + int ret; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + kref_init(&ctx->refcount); + kref_get(&ctx->refcount); + + ret = sgx_dev_populate("sgx/enclave", &ctx->encl_dev, &ctx->encl_cdev, + parent, &sgx_encl_fops, 0); + if (ret) + return ERR_PTR(ret); + + ret = sgx_dev_populate("sgx/provision", &ctx->provision_dev, + &ctx->provision_cdev, parent, + &sgx_provision_fops, 1); + if (ret) { + put_device(&ctx->encl_dev); + return ERR_PTR(ret); + } + + dev_set_drvdata(parent, ctx); + + return ctx; +} + +static struct sgx_dev_ctx *sgxm_dev_ctx_alloc(struct device *parent) +{ + struct sgx_dev_ctx *ctx; + int rc; + + ctx = sgx_dev_ctx_alloc(parent); + if (IS_ERR(ctx)) + return ctx; + + rc = devm_add_action_or_reset(parent, (void (*)(void *))put_device, + &ctx->encl_dev); + if (rc) + return ERR_PTR(rc); + + rc = devm_add_action_or_reset(parent, (void (*)(void *))put_device, + &ctx->provision_dev); + if (rc) + return ERR_PTR(rc); + + return ctx; +} + +static int sgx_dev_init(struct device *parent) +{ + struct sgx_dev_ctx *sgx_dev; + unsigned int eax; + unsigned int ebx; + unsigned int ecx; + unsigned int edx; + u64 attr_mask; + u64 xfrm_mask; + int ret; + int i; + + cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx); + sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK; + sgx_encl_size_max_64 = 1ULL << ((edx >> 8) & 0xFF); + sgx_encl_size_max_32 = 1ULL << (edx & 0xFF); + + cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx); + + attr_mask = (((u64)ebx) << 32) + (u64)eax; + sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK; + + if (boot_cpu_has(X86_FEATURE_OSXSAVE)) { + xfrm_mask = (((u64)edx) << 32) + (u64)ecx; + + for (i = 2; i < 64; i++) { + cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx); + if ((1 << i) & xfrm_mask) + sgx_xsave_size_tbl[i] = eax + ebx; + } + + sgx_xfrm_reserved_mask = ~xfrm_mask; + } + + sgx_dev = sgxm_dev_ctx_alloc(parent); + if (IS_ERR(sgx_dev)) + return PTR_ERR(sgx_dev); + + sgx_encl_wq = alloc_workqueue("sgx-encl-wq", + WQ_UNBOUND | WQ_FREEZABLE, 1); + if (!sgx_encl_wq) + return -ENOMEM; + + ret = cdev_device_add(&sgx_dev->encl_cdev, &sgx_dev->encl_dev); + if (ret) + goto err_encl_dev_add; + + return 0; + +err_encl_dev_add: + destroy_workqueue(sgx_encl_wq); + + return ret; +} + +static int sgx_drv_probe(struct platform_device *pdev) +{ + if (!sgx_enabled) { + pr_info("sgx: SGX is not enabled in the core\n"); + return -ENODEV; + } + + if (!boot_cpu_has(X86_FEATURE_SGX_LC)) { + pr_info("sgx: The public key MSRs are not writable\n"); + return -ENODEV; + } + + return sgx_dev_init(&pdev->dev); +} + +static int sgx_drv_remove(struct platform_device *pdev) +{ + struct sgx_dev_ctx *ctx = dev_get_drvdata(&pdev->dev); + + cdev_device_del(&ctx->encl_cdev, &ctx->encl_dev); + destroy_workqueue(sgx_encl_wq); + + return 0; +} + +#ifdef CONFIG_ACPI +static struct acpi_device_id sgx_device_ids[] = { + {"INT0E0C", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, sgx_device_ids); +#endif + +static struct platform_driver sgx_drv = { + .probe = sgx_drv_probe, + .remove = sgx_drv_remove, + .driver = { + .name = "sgx", + .acpi_match_table = ACPI_PTR(sgx_device_ids), + }, +}; + +static int __init sgx_drv_subsys_init(void) +{ + int ret; + + ret = bus_register(&sgx_bus_type); + if (ret) + return ret; + + ret = alloc_chrdev_region(&sgx_devt, 0, SGX_DRV_NR_DEVICES, "sgx"); + if (ret < 0) { + bus_unregister(&sgx_bus_type); + return ret; + } + + return 0; +} + +static void sgx_drv_subsys_exit(void) +{ + bus_unregister(&sgx_bus_type); + unregister_chrdev_region(sgx_devt, SGX_DRV_NR_DEVICES); +} + +static int __init sgx_drv_init(void) +{ + int ret; + + ret = sgx_drv_subsys_init(); + if (ret) + return ret; + + ret = platform_driver_register(&sgx_drv); + if (ret) + sgx_drv_subsys_exit(); + + return ret; +} +module_init(sgx_drv_init); + +static void __exit sgx_drv_exit(void) +{ + platform_driver_unregister(&sgx_drv); + sgx_drv_subsys_exit(); +} +module_exit(sgx_drv_exit); diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c new file mode 100644 index 000000000000..cc3bd4a4d82b --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-18 Intel Corporation. + +#include +#include +#include +#include +#include "arch.h" +#include "encl.h" +#include "sgx.h" + +static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl, + unsigned long addr) +{ + struct sgx_encl_page *entry; + + /* If process was forked, VMA is still there but vm_private_data is set + * to NULL. + */ + if (!encl) + return ERR_PTR(-EFAULT); + + if ((encl->flags & SGX_ENCL_DEAD) || + !(encl->flags & SGX_ENCL_INITIALIZED)) + return ERR_PTR(-EFAULT); + + entry = radix_tree_lookup(&encl->page_tree, addr >> PAGE_SHIFT); + if (!entry) + return ERR_PTR(-EFAULT); + + /* Page is already resident in the EPC. */ + if (entry->epc_page) + return entry; + + return ERR_PTR(-EFAULT); +} + +struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl, + struct mm_struct *mm) +{ + struct sgx_encl_mm *encl_mm; + + encl_mm = kzalloc(sizeof(*encl_mm), GFP_KERNEL); + if (!encl_mm) + return ERR_PTR(-ENOMEM); + + encl_mm->encl = encl; + encl_mm->mm = mm; + kref_init(&encl_mm->refcount); + + spin_lock(&encl->mm_lock); + list_add(&encl_mm->list, &encl->mm_list); + spin_unlock(&encl->mm_lock); + + return encl_mm; +} +EXPORT_SYMBOL_GPL(sgx_encl_mm_add); + +void sgx_encl_mm_release(struct kref *ref) +{ + struct sgx_encl_mm *encl_mm = + container_of(ref, struct sgx_encl_mm, refcount); + + spin_lock(&encl_mm->encl->mm_lock); + list_del(&encl_mm->list); + spin_unlock(&encl_mm->encl->mm_lock); + + kfree(encl_mm); +} + +static struct sgx_encl_mm *sgx_encl_get_mm(struct sgx_encl *encl, + struct mm_struct *mm) +{ + struct sgx_encl_mm *encl_mm = NULL; + struct sgx_encl_mm *prev_mm = NULL; + int iter; + + while (true) { + encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter); + if (prev_mm) + kref_put(&prev_mm->refcount, sgx_encl_mm_release); + prev_mm = encl_mm; + + if (iter == SGX_ENCL_MM_ITER_DONE) + break; + + if (iter == SGX_ENCL_MM_ITER_RESTART) + continue; + + if (mm == encl_mm->mm) + return encl_mm; + } + + return NULL; +} + +static void sgx_vma_open(struct vm_area_struct *vma) +{ + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_mm *encl_mm; + + if (!encl) + return; + + if (encl->flags & SGX_ENCL_DEAD) + goto error; + + encl_mm = sgx_encl_get_mm(encl, vma->vm_mm); + if (!encl_mm) { + encl_mm = sgx_encl_mm_add(encl, vma->vm_mm); + if (IS_ERR(encl_mm)) + goto error; + } + + kref_get(&encl->refcount); + return; + +error: + vma->vm_private_data = NULL; +} + +static void sgx_vma_close(struct vm_area_struct *vma) +{ + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_mm *encl_mm; + + if (!encl) + return; + + encl_mm = sgx_encl_get_mm(encl, vma->vm_mm); + if (encl_mm) { + kref_put(&encl_mm->refcount, sgx_encl_mm_release); + + /* Release kref for the VMA. */ + kref_put(&encl_mm->refcount, sgx_encl_mm_release); + } + + kref_put(&encl->refcount, sgx_encl_release); +} + +static unsigned int sgx_vma_fault(struct vm_fault *vmf) +{ + unsigned long addr = (unsigned long)vmf->address; + struct vm_area_struct *vma = vmf->vma; + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_page *entry; + int ret = VM_FAULT_NOPAGE; + unsigned long pfn; + + if (!encl) + return VM_FAULT_SIGBUS; + + mutex_lock(&encl->lock); + + entry = sgx_encl_load_page(encl, addr); + if (IS_ERR(entry)) { + if (unlikely(PTR_ERR(entry) != -EBUSY)) + ret = VM_FAULT_SIGBUS; + + goto out; + } + + if (!follow_pfn(vma, addr, &pfn)) + goto out; + + ret = vmf_insert_pfn(vma, addr, PFN_DOWN(entry->epc_page->desc)); + if (ret != VM_FAULT_NOPAGE) { + ret = VM_FAULT_SIGBUS; + goto out; + } + +out: + mutex_unlock(&encl->lock); + return ret; +} + +const struct vm_operations_struct sgx_vm_ops = { + .close = sgx_vma_close, + .open = sgx_vma_open, + .fault = sgx_vma_fault, +}; +EXPORT_SYMBOL_GPL(sgx_vm_ops); + +/** + * sgx_encl_find - find an enclave + * @mm: mm struct of the current process + * @addr: address in the ELRANGE + * @vma: the resulting VMA + * + * Find an enclave identified by the given address. Give back a VMA that is + * part of the enclave and located in that address. The VMA is given back if it + * is a proper enclave VMA even if an &sgx_encl instance does not exist yet + * (enclave creation has not been performed). + * + * Return: + * 0 on success, + * -EINVAL if an enclave was not found, + * -ENOENT if the enclave has not been created yet + */ +int sgx_encl_find(struct mm_struct *mm, unsigned long addr, + struct vm_area_struct **vma) +{ + struct vm_area_struct *result; + struct sgx_encl *encl; + + result = find_vma(mm, addr); + if (!result || result->vm_ops != &sgx_vm_ops || addr < result->vm_start) + return -EINVAL; + + encl = result->vm_private_data; + *vma = result; + + return encl ? 0 : -ENOENT; +} +EXPORT_SYMBOL_GPL(sgx_encl_find); + +/** + * sgx_encl_destroy() - destroy enclave resources + * @encl: an &sgx_encl instance + */ +void sgx_encl_destroy(struct sgx_encl *encl) +{ + struct sgx_encl_page *entry; + struct radix_tree_iter iter; + void **slot; + + encl->flags |= SGX_ENCL_DEAD; + + radix_tree_for_each_slot(slot, &encl->page_tree, &iter, 0) { + entry = *slot; + if (entry->epc_page) { + if (!__sgx_free_page(entry->epc_page)) { + encl->secs_child_cnt--; + entry->epc_page = NULL; + + } + + radix_tree_delete(&entry->encl->page_tree, + PFN_DOWN(entry->desc)); + } + } + + if (!encl->secs_child_cnt && encl->secs.epc_page) { + sgx_free_page(encl->secs.epc_page); + encl->secs.epc_page = NULL; + } +} +EXPORT_SYMBOL_GPL(sgx_encl_destroy); + +/** + * sgx_encl_release - Destroy an enclave instance + * @kref: address of a kref inside &sgx_encl + * + * Used together with kref_put(). Frees all the resources associated with the + * enclave and the instance itself. + */ +void sgx_encl_release(struct kref *ref) +{ + struct sgx_encl *encl = container_of(ref, struct sgx_encl, refcount); + struct sgx_encl_mm *encl_mm; + + if (encl->pm_notifier.notifier_call) + unregister_pm_notifier(&encl->pm_notifier); + + sgx_encl_destroy(encl); + + if (encl->backing) + fput(encl->backing); + + WARN(!list_empty(&encl->mm_list), "sgx: mm_list non-empty"); + + kfree(encl); +} +EXPORT_SYMBOL_GPL(sgx_encl_release); + +/** + * sgx_encl_get_index() - Convert a page descriptor to a page index + * @encl: an enclave + * @page: an enclave page + * + * Given an enclave page descriptor, convert it to a page index used to access + * backing storage. The backing page for SECS is located after the enclave + * pages. + */ +pgoff_t sgx_encl_get_index(struct sgx_encl *encl, struct sgx_encl_page *page) +{ + if (!PFN_DOWN(page->desc)) + return PFN_DOWN(encl->size); + + return PFN_DOWN(page->desc - encl->base); +} +EXPORT_SYMBOL_GPL(sgx_encl_get_index); + +/** + * sgx_encl_encl_get_backing_page() - Pin the backing page + * @encl: an enclave + * @index: page index + * + * Return: the pinned backing page + */ +struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index) +{ + struct inode *inode = encl->backing->f_path.dentry->d_inode; + struct address_space *mapping = inode->i_mapping; + gfp_t gfpmask = mapping_gfp_mask(mapping); + + return shmem_read_mapping_page_gfp(mapping, index, gfpmask); +} +EXPORT_SYMBOL_GPL(sgx_encl_get_backing_page); + +/** + * sgx_encl_next_mm() - Iterate to the next mm + * @encl: an enclave + * @mm: an mm list entry + * @iter: iterator status + * + * Return: the enclave mm or NULL + */ +struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl, + struct sgx_encl_mm *encl_mm, int *iter) +{ + struct list_head *entry; + + WARN(!encl, "%s: encl is NULL", __func__); + WARN(!iter, "%s: iter is NULL", __func__); + + spin_lock(&encl->mm_lock); + + entry = encl_mm ? encl_mm->list.next : encl->mm_list.next; + WARN(!entry, "%s: entry is NULL", __func__); + + if (entry == &encl->mm_list) { + spin_unlock(&encl->mm_lock); + *iter = SGX_ENCL_MM_ITER_DONE; + return NULL; + } + + encl_mm = list_entry(entry, struct sgx_encl_mm, list); + + if (!kref_get_unless_zero(&encl_mm->refcount)) { + spin_unlock(&encl->mm_lock); + *iter = SGX_ENCL_MM_ITER_RESTART; + return NULL; + } + + spin_unlock(&encl->mm_lock); + *iter = SGX_ENCL_MM_ITER_NEXT; + return encl_mm; +} diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h new file mode 100644 index 000000000000..1f96991839ad --- /dev/null +++ b/arch/x86/kernel/cpu/sgx/encl.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/** + * Copyright(c) 2016-19 Intel Corporation. + */ +#ifndef _X86_ENCL_H +#define _X86_ENCL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * enum sgx_encl_page_desc - defines bits for an enclave page's descriptor + * %SGX_ENCL_PAGE_TCS: The page is a TCS page. + * %SGX_ENCL_PAGE_ADDR_MASK: Holds the virtual address of the page. + * + * The page address for SECS is zero and is used by the subsystem to recognize + * the SECS page. + */ +enum sgx_encl_page_desc { + SGX_ENCL_PAGE_TCS = BIT(0), + /* Bits 11:3 are available when the page is not swapped. */ + SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK, +}; + +#define SGX_ENCL_PAGE_ADDR(encl_page) \ + ((encl_page)->desc & SGX_ENCL_PAGE_ADDR_MASK) +#define SGX_ENCL_PAGE_VA_OFFSET(encl_page) \ + ((encl_page)->desc & SGX_ENCL_PAGE_VA_OFFSET_MASK) + +struct sgx_encl_page { + unsigned long desc; + struct sgx_epc_page *epc_page; + struct sgx_encl *encl; +}; + +enum sgx_encl_flags { + SGX_ENCL_CREATED = BIT(0), + SGX_ENCL_INITIALIZED = BIT(1), + SGX_ENCL_DEBUG = BIT(2), + SGX_ENCL_SUSPEND = BIT(3), + SGX_ENCL_DEAD = BIT(4), +}; + +struct sgx_encl_mm { + struct sgx_encl *encl; + struct mm_struct *mm; + struct kref refcount; + struct list_head list; +}; + +struct sgx_encl { + unsigned int flags; + u64 secs_attributes; + u64 allowed_attributes; + unsigned int page_cnt; + unsigned int secs_child_cnt; + struct mutex lock; + struct list_head mm_list; + spinlock_t mm_lock; + struct file *backing; + struct kref refcount; + unsigned long base; + unsigned long size; + unsigned long ssaframesize; + struct radix_tree_root page_tree; + struct list_head add_page_reqs; + struct work_struct work; + struct sgx_encl_page secs; + struct notifier_block pm_notifier; +}; + +extern const struct vm_operations_struct sgx_vm_ops; + +enum sgx_encl_mm_iter { + SGX_ENCL_MM_ITER_DONE = 0, + SGX_ENCL_MM_ITER_NEXT = 1, + SGX_ENCL_MM_ITER_RESTART = 2, +}; + +int sgx_encl_find(struct mm_struct *mm, unsigned long addr, + struct vm_area_struct **vma); +void sgx_encl_destroy(struct sgx_encl *encl); +void sgx_encl_release(struct kref *ref); +pgoff_t sgx_encl_get_index(struct sgx_encl *encl, struct sgx_encl_page *page); +struct page *sgx_encl_get_backing_page(struct sgx_encl *encl, pgoff_t index); +struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl, + struct sgx_encl_mm *encl_mm, int *iter); +struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl, + struct mm_struct *mm); +void sgx_encl_mm_release(struct kref *ref); + +#endif /* _X86_ENCL_H */ diff --git a/arch/x86/kernel/cpu/sgx/encls.c b/arch/x86/kernel/cpu/sgx/encls.c index 5045f1365e07..698cc526bfbf 100644 --- a/arch/x86/kernel/cpu/sgx/encls.c +++ b/arch/x86/kernel/cpu/sgx/encls.c @@ -19,3 +19,4 @@ bool encls_failed(int ret) return encls_faulted(ret) && ENCLS_TRAPNR(ret) != epcm_trapnr; } +EXPORT_SYMBOL_GPL(encls_failed); diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c index d3ed742e90fe..d911a1038712 100644 --- a/arch/x86/kernel/cpu/sgx/main.c +++ b/arch/x86/kernel/cpu/sgx/main.c @@ -14,6 +14,8 @@ struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; EXPORT_SYMBOL_GPL(sgx_epc_sections); +bool sgx_enabled; +EXPORT_SYMBOL_GPL(sgx_enabled); int sgx_nr_epc_sections; @@ -293,6 +295,7 @@ static __init int sgx_init(void) return ret; } + sgx_enabled = true; return 0; } diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h index 41d4130c33a2..62a574ed230a 100644 --- a/arch/x86/kernel/cpu/sgx/sgx.h +++ b/arch/x86/kernel/cpu/sgx/sgx.h @@ -35,6 +35,7 @@ struct sgx_epc_section { #define SGX_MAX_EPC_SECTIONS 8 extern struct sgx_epc_section sgx_epc_sections[SGX_MAX_EPC_SECTIONS]; +extern bool sgx_enabled; /** * enum sgx_epc_page_desc - bits and masks for an EPC page's descriptor From patchwork Wed Apr 17 10:39:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905171 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B76E517E0 for ; Wed, 17 Apr 2019 10:41:49 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A16D4289F5 for ; Wed, 17 Apr 2019 10:41:49 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 93E3C28AD4; Wed, 17 Apr 2019 10:41:49 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BE54E28AD1 for ; Wed, 17 Apr 2019 10:41:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731693AbfDQKls (ORCPT ); Wed, 17 Apr 2019 06:41:48 -0400 Received: from mga17.intel.com ([192.55.52.151]:9722 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729522AbfDQKls (ORCPT ); Wed, 17 Apr 2019 06:41:48 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:47 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120105" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:40 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen , James Morris , "Serge E . Hallyn" , linux-security-module@vger.kernel.org Subject: [PATCH v20 16/28] x86/sgx: Add provisioning Date: Wed, 17 Apr 2019 13:39:26 +0300 Message-Id: <20190417103938.7762-17-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP In order to provide a mechanism for devilering provisoning rights: 1. Add a new device file /dev/sgx/provision that works as a token for allowing an enclave to have the provisioning privileges. 2. Add a new ioctl called SGX_IOC_ENCLAVE_SET_ATTRIBUTE that accepts the following data structure: struct sgx_enclave_set_attribute { __u64 addr; __u64 attribute_fd; }; A daemon could sit on top of /dev/sgx/provision and send a file descriptor of this file to a process that needs to be able to provision enclaves. The way this API is used is straight-forward. Lets assume that dev_fd is a handle to /dev/sgx/enclave and prov_fd is a handle to /dev/sgx/provision. You would allow SGX_IOC_ENCLAVE_CREATE to initialize an enclave with the PROVISIONKEY attribute by params.addr = ; params.token_fd = prov_fd; ioctl(dev_fd, SGX_IOC_ENCLAVE_SET_ATTRIBUTE, ¶ms); Cc: James Morris Cc: Serge E. Hallyn Cc: linux-security-module@vger.kernel.org Suggested-by: Andy Lutomirski Signed-off-by: Jarkko Sakkinen --- arch/x86/include/uapi/asm/sgx.h | 11 ++++++ arch/x86/kernel/cpu/sgx/driver/driver.h | 2 +- arch/x86/kernel/cpu/sgx/driver/ioctl.c | 51 +++++++++++++++++++++++++ arch/x86/kernel/cpu/sgx/driver/main.c | 11 +++++- 4 files changed, 73 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h index 7bf627ac4958..3b80acde8671 100644 --- a/arch/x86/include/uapi/asm/sgx.h +++ b/arch/x86/include/uapi/asm/sgx.h @@ -16,6 +16,8 @@ _IOW(SGX_MAGIC, 0x01, struct sgx_enclave_add_page) #define SGX_IOC_ENCLAVE_INIT \ _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init) +#define SGX_IOC_ENCLAVE_SET_ATTRIBUTE \ + _IOW(SGX_MAGIC, 0x03, struct sgx_enclave_set_attribute) /* IOCTL return values */ #define SGX_POWER_LOST_ENCLAVE 0x40000000 @@ -54,4 +56,13 @@ struct sgx_enclave_init { __u64 sigstruct; }; +/** + * struct sgx_enclave_set_attribute - parameter structure for the + * %SGX_IOC_ENCLAVE_SET_ATTRIBUTE ioctl + * @attribute_fd: file handle of the attribute file in the securityfs + */ +struct sgx_enclave_set_attribute { + __u64 attribute_fd; +}; + #endif /* _UAPI_ASM_X86_SGX_H */ diff --git a/arch/x86/kernel/cpu/sgx/driver/driver.h b/arch/x86/kernel/cpu/sgx/driver/driver.h index 507712eb0a68..153b4a48aa6f 100644 --- a/arch/x86/kernel/cpu/sgx/driver/driver.h +++ b/arch/x86/kernel/cpu/sgx/driver/driver.h @@ -31,7 +31,7 @@ extern u64 sgx_attributes_reserved_mask; extern u64 sgx_xfrm_reserved_mask; extern u32 sgx_xsave_size_tbl[64]; -extern const struct file_operations sgx_fs_provision_fops; +extern const struct file_operations sgx_provision_fops; long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c index f88226518b21..65c9fb7b2a95 100644 --- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c +++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c @@ -714,6 +714,54 @@ static long sgx_ioc_enclave_init(struct file *filep, unsigned int cmd, return ret; } +/** + * sgx_ioc_enclave_set_attribute - handler for %SGX_IOC_ENCLAVE_SET_ATTRIBUTE + * @filep: open file to /dev/sgx + * @cmd: the command value + * @arg: pointer to a struct sgx_enclave_set_attribute instance + * + * Mark the enclave as being allowed to access a restricted attribute bit. + * The requested attribute is specified via the attribute_fd field in the + * provided struct sgx_enclave_set_attribute. The attribute_fd must be a + * handle to an SGX attribute file, e.g. “/dev/sgx/provision". + * + * Failure to explicitly request access to a restricted attribute will cause + * sgx_ioc_enclave_init() to fail. Currently, the only restricted attribute + * is access to the PROVISION_KEY. + * + * Note, access to the EINITTOKEN_KEY is disallowed entirely. + * + * Return: 0 on success, -errno otherwise + */ +static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct sgx_enclave_set_attribute *params = (void *)arg; + struct file *attribute_file; + struct sgx_encl *encl; + int ret; + + attribute_file = fget(params->attribute_fd); + if (!attribute_file->f_op) + return -EINVAL; + + if (attribute_file->f_op != &sgx_provision_fops) { + ret = -EINVAL; + goto out; + } + + ret = sgx_encl_get(params->addr, &encl); + if (ret) + goto out; + + encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY; + kref_put(&encl->refcount, sgx_encl_release); + +out: + fput(attribute_file); + return ret; +} + typedef long (*sgx_ioc_t)(struct file *filep, unsigned int cmd, unsigned long arg); @@ -733,6 +781,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case SGX_IOC_ENCLAVE_INIT: handler = sgx_ioc_enclave_init; break; + case SGX_IOC_ENCLAVE_SET_ATTRIBUTE: + handler = sgx_ioc_enclave_set_attribute; + break; default: return -ENOIOCTLCMD; } diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c index d371add399cd..8b79c4a60037 100644 --- a/arch/x86/kernel/cpu/sgx/driver/main.c +++ b/arch/x86/kernel/cpu/sgx/driver/main.c @@ -103,7 +103,7 @@ static const struct file_operations sgx_encl_fops = { .get_unmapped_area = sgx_get_unmapped_area, }; -static const struct file_operations sgx_provision_fops = { +const struct file_operations sgx_provision_fops = { .owner = THIS_MODULE, }; @@ -261,8 +261,16 @@ static int sgx_dev_init(struct device *parent) if (ret) goto err_encl_dev_add; + ret = cdev_device_add(&sgx_dev->provision_cdev, + &sgx_dev->provision_dev); + if (ret) + goto err_provision_dev_add; + return 0; +err_provision_dev_add: + cdev_device_del(&sgx_dev->encl_cdev, &sgx_dev->encl_dev); + err_encl_dev_add: destroy_workqueue(sgx_encl_wq); @@ -289,6 +297,7 @@ static int sgx_drv_remove(struct platform_device *pdev) struct sgx_dev_ctx *ctx = dev_get_drvdata(&pdev->dev); cdev_device_del(&ctx->encl_cdev, &ctx->encl_dev); + cdev_device_del(&ctx->provision_cdev, &ctx->provision_dev); destroy_workqueue(sgx_encl_wq); return 0; From patchwork Wed Apr 17 10:39:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905175 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BBE6213B5 for ; Wed, 17 Apr 2019 10:41:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A2DE8289F5 for ; Wed, 17 Apr 2019 10:41:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9601828AD4; Wed, 17 Apr 2019 10:41:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 45A2C289F5 for ; Wed, 17 Apr 2019 10:41:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731760AbfDQKl4 (ORCPT ); Wed, 17 Apr 2019 06:41:56 -0400 Received: from mga17.intel.com ([192.55.52.151]:9722 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731566AbfDQKlz (ORCPT ); Wed, 17 Apr 2019 06:41:55 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:41:55 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120121" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:48 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 17/28] x86/sgx: Add swapping code to the core and SGX driver Date: Wed, 17 Apr 2019 13:39:27 +0300 Message-Id: <20190417103938.7762-18-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Because the kernel is untrusted, swapping pages in/out of the Enclave Page Cache (EPC) has specialized requirements: * The kernel cannot directly access EPC memory, i.e. cannot copy data to/from the EPC. * To evict a page from the EPC, the kernel must "prove" to hardware that are no valid TLB entries for said page since a stale TLB entry would allow an attacker to bypass SGX access controls. * When loading a page back into the EPC, hardware must be able to verify the integrity and freshness of the data. * When loading an enclave page, e.g. regular pages and Thread Control Structures (TCS), hardware must be able to associate the page with a Secure Enclave Control Structure (SECS). To satisfy the above requirements, the CPU provides dedicated ENCLS functions to support paging data in/out of the EPC: * EBLOCK: Mark a page as blocked in the EPC Map (EPCM). Attempting to access a blocked page that misses the TLB will fault. * ETRACK: Activate blocking tracking. Hardware verifies that all translations for pages marked as "blocked" have been flushed from the TLB. * EPA: Add version array page to the EPC. As the name suggests, a VA page is an 512-entry array of version numbers that are used to uniquely identify pages evicted from the EPC. * EWB: Write back a page from EPC to memory, e.g. RAM. Software must supply a VA slot, memory to hold the a Paging Crypto Metadata (PCMD) of the page and obviously backing for the evicted page. * ELD{B,U}: Load a page in {un}blocked state from memory to EPC. The driver only uses the ELDU variant as there is no use case for loading a page as "blocked" in a bare metal environment. To top things off, all of the above ENCLS functions are subject to strict concurrency rules, e.g. many operations will #GP fault if two or more operations attempt to access common pages/structures. To put it succinctly, paging in/out of the EPC requires coordinating with the SGX driver where all of an enclave's tracking resides. But, simply shoving all reclaim logic into the driver is not desirable as doing so has unwanted long term implications: * Oversubscribing EPC to KVM guests, i.e. virtualizing SGX in KVM and swapping a guest's EPC pages (without the guest's cooperation) needs the same high level flows for reclaim but has painfully different semantics in the details. * Accounting EPC, i.e. adding an EPC cgroup controller, is desirable as EPC is effectively a specialized memory type and even more scarce than system memory. Providing a single touchpoint for EPC accounting regardless of end consumer greatly simplifies the EPC controller. * Allowing the userspace-facing driver to be built as a loaded module is desirable, e.g. for debug, testing and development. The cgroup infrastructure does not support dependencies on loadable modules. * Separating EPC swapping from the driver once it has been tightly coupled to the driver is non-trivial (speaking from experience). So, although the SGX driver is currently the sole consumer of EPC, encapsulate EPC swapping in the driver to minimize the dependencies between the core SGX code and driver, and do so in a way that can be extended to an abstracted interface with minimal effort. To that end, add functions to swap EPC pages to the driver. The user of these functions will be the core SGX subsystem, which will be enabled in a future patch. * sgx_encl_page_{get,put}() - Attempt to pin/unpin (the owner of) an EPC page so that it can be operated on by a reclaimer. * sgx_encl_page_reclaim() - Mark a page as being reclaimed. The page is considered reclaimable if it hasn't been accessed recently and it isn't reserved by the driver for other use. * sgx_encl_page_block() - EBLOCK an EPC page * sgx_encl_page_write() - Evict an EPC page to the regular memory via EWB. Activates ETRACK (via sgx_encl_track()) if necessary. Since we also need to be able to fault pages back into the EPC, add a page fault handler to allocate an EPC page and ELDU a previously evicted page. Wire up the EPC manager's reclaim flow to the SGX driver's swapping functionality. In the long term there will be multiple users of the EPC manager, e.g. SGX driver and KVM, thus the interface between the EPC manager and the driver is fairly genericized and decoupled. But to avoid adding unusued infrastructure, do not add any indirection between the EPC manager and the SGX driver. This has the unfortunate and odd side effect of preventing the SGX driver from being compiled as a loadable module. However, this should be a temporary situation that is remedied when a second user of EPC is added, i.e. KVM. The swapper thread ksgxswapd reclaims pages on the event when the number of free EPC pages goes below %SGX_NR_LOW_PAGES up until it reaches %SGX_NR_HIGH_PAGES. Pages are reclaimed in LRU fashion from a global list. The consumers take care of calling EBLOCK (block page from new accesses), ETRACK (restart counting the entering hardware threads) and EWB (write page to the regular memory) because executing these operations usually (if not always) requires to do some subsystem-internal locking operations. Cc: Andrew Morton Signed-off-by: Jarkko Sakkinen Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Co-developed-by: Serge Ayoun Signed-off-by: Serge Ayoun Co-developed-by: Shay Katz-zamir Signed-off-by: Shay Katz-zamir --- arch/x86/kernel/cpu/sgx/driver/ioctl.c | 65 +++- arch/x86/kernel/cpu/sgx/driver/main.c | 1 + arch/x86/kernel/cpu/sgx/encl.c | 267 ++++++++++++++++- arch/x86/kernel/cpu/sgx/encl.h | 38 +++ arch/x86/kernel/cpu/sgx/main.c | 92 ++++-- arch/x86/kernel/cpu/sgx/reclaim.c | 400 ++++++++++++++++++++++++- arch/x86/kernel/cpu/sgx/sgx.h | 18 +- 7 files changed, 852 insertions(+), 29 deletions(-) diff --git a/arch/x86/kernel/cpu/sgx/driver/ioctl.c b/arch/x86/kernel/cpu/sgx/driver/ioctl.c index 65c9fb7b2a95..3a01c3dd579d 100644 --- a/arch/x86/kernel/cpu/sgx/driver/ioctl.c +++ b/arch/x86/kernel/cpu/sgx/driver/ioctl.c @@ -21,6 +21,51 @@ struct sgx_add_page_req { struct list_head list; }; +static int sgx_encl_grow(struct sgx_encl *encl) +{ + struct sgx_va_page *va_page; + int ret; + + BUILD_BUG_ON(SGX_VA_SLOT_COUNT != + (SGX_ENCL_PAGE_VA_OFFSET_MASK >> 3) + 1); + + mutex_lock(&encl->lock); + if (encl->flags & SGX_ENCL_DEAD) { + mutex_unlock(&encl->lock); + return -EFAULT; + } + + if (!(encl->page_cnt % SGX_VA_SLOT_COUNT)) { + mutex_unlock(&encl->lock); + + va_page = kzalloc(sizeof(*va_page), GFP_KERNEL); + if (!va_page) + return -ENOMEM; + va_page->epc_page = sgx_alloc_va_page(); + if (IS_ERR(va_page->epc_page)) { + ret = PTR_ERR(va_page->epc_page); + kfree(va_page); + return ret; + } + + mutex_lock(&encl->lock); + if (encl->flags & SGX_ENCL_DEAD) { + sgx_free_page(va_page->epc_page); + kfree(va_page); + mutex_unlock(&encl->lock); + return -EFAULT; + } else if (encl->page_cnt % SGX_VA_SLOT_COUNT) { + sgx_free_page(va_page->epc_page); + kfree(va_page); + } else { + list_add(&va_page->list, &encl->va_pages); + } + } + encl->page_cnt++; + mutex_unlock(&encl->lock); + return 0; +} + static bool sgx_process_add_page_req(struct sgx_add_page_req *req, struct sgx_epc_page *epc_page) { @@ -79,6 +124,7 @@ static bool sgx_process_add_page_req(struct sgx_add_page_req *req, encl_page->encl = encl; encl_page->epc_page = epc_page; encl->secs_child_cnt++; + sgx_mark_page_reclaimable(encl_page->epc_page); return true; } @@ -109,7 +155,7 @@ static void sgx_add_page_worker(struct work_struct *work) if (skip_rest) goto next; - epc_page = sgx_alloc_page(); + epc_page = sgx_alloc_page(req->encl_page, true); mutex_lock(&encl->lock); @@ -237,6 +283,10 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) struct file *backing; long ret; + ret = sgx_encl_grow(encl); + if (ret) + return ret; + mutex_lock(&encl->lock); if (encl->flags & SGX_ENCL_CREATED) { @@ -267,7 +317,7 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) goto err_out; } - secs_epc = sgx_alloc_page(); + secs_epc = sgx_alloc_page(&encl->secs, true); if (IS_ERR(secs_epc)) { ret = PTR_ERR(secs_epc); goto err_out; @@ -495,6 +545,10 @@ static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long addr, return ret; } + ret = sgx_encl_grow(encl); + if (ret) + return ret; + mutex_lock(&encl->lock); if (!(encl->flags & SGX_ENCL_CREATED) || @@ -737,8 +791,8 @@ static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd, unsigned long arg) { struct sgx_enclave_set_attribute *params = (void *)arg; + struct sgx_encl *encl = filep->private_data; struct file *attribute_file; - struct sgx_encl *encl; int ret; attribute_file = fget(params->attribute_fd); @@ -750,12 +804,7 @@ static long sgx_ioc_enclave_set_attribute(struct file *filep, unsigned int cmd, goto out; } - ret = sgx_encl_get(params->addr, &encl); - if (ret) - goto out; - encl->allowed_attributes |= SGX_ATTR_PROVISIONKEY; - kref_put(&encl->refcount, sgx_encl_release); out: fput(attribute_file); diff --git a/arch/x86/kernel/cpu/sgx/driver/main.c b/arch/x86/kernel/cpu/sgx/driver/main.c index 8b79c4a60037..afe844aa81d6 100644 --- a/arch/x86/kernel/cpu/sgx/driver/main.c +++ b/arch/x86/kernel/cpu/sgx/driver/main.c @@ -32,6 +32,7 @@ static int sgx_open(struct inode *inode, struct file *file) kref_init(&encl->refcount); INIT_LIST_HEAD(&encl->add_page_reqs); + INIT_LIST_HEAD(&encl->va_pages); INIT_RADIX_TREE(&encl->page_tree, GFP_KERNEL); mutex_init(&encl->lock); INIT_LIST_HEAD(&encl->mm_list); diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c index cc3bd4a4d82b..16e8524687c1 100644 --- a/arch/x86/kernel/cpu/sgx/encl.c +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -7,11 +7,91 @@ #include #include "arch.h" #include "encl.h" +#include "encls.h" #include "sgx.h" +static int __sgx_encl_eldu(struct sgx_encl_page *encl_page, + struct sgx_epc_page *epc_page) +{ + unsigned long addr = SGX_ENCL_PAGE_ADDR(encl_page); + unsigned long va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page); + struct sgx_encl *encl = encl_page->encl; + pgoff_t page_index = sgx_encl_get_index(encl, encl_page); + pgoff_t pcmd_index = sgx_pcmd_index(encl, page_index); + unsigned long pcmd_offset = sgx_pcmd_offset(page_index); + struct sgx_pageinfo pginfo; + struct page *backing; + struct page *pcmd; + int ret; + + backing = sgx_encl_get_backing_page(encl, page_index); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + goto err_backing; + } + + pcmd = sgx_encl_get_backing_page(encl, pcmd_index); + if (IS_ERR(pcmd)) { + ret = PTR_ERR(pcmd); + goto err_pcmd; + } + + pginfo.addr = addr; + pginfo.contents = (unsigned long)kmap_atomic(backing); + pginfo.metadata = (unsigned long)kmap_atomic(pcmd) + pcmd_offset; + pginfo.secs = addr ? (unsigned long)sgx_epc_addr(encl->secs.epc_page) : + 0; + + ret = __eldu(&pginfo, sgx_epc_addr(epc_page), + sgx_epc_addr(encl_page->va_page->epc_page) + va_offset); + if (ret) { + if (encls_failed(ret) || encls_returned_code(ret)) + ENCLS_WARN(ret, "ELDU"); + + ret = -EFAULT; + } + + kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset)); + kunmap_atomic((void *)(unsigned long)pginfo.contents); + + put_page(pcmd); + +err_pcmd: + put_page(backing); + +err_backing: + return ret; +} + +static struct sgx_epc_page *sgx_encl_eldu(struct sgx_encl_page *encl_page) +{ + unsigned long va_offset = SGX_ENCL_PAGE_VA_OFFSET(encl_page); + struct sgx_encl *encl = encl_page->encl; + struct sgx_epc_page *epc_page; + int ret; + + epc_page = sgx_alloc_page(encl_page, false); + if (IS_ERR(epc_page)) + return epc_page; + + ret = __sgx_encl_eldu(encl_page, epc_page); + if (ret) { + sgx_free_page(epc_page); + return ERR_PTR(ret); + } + + sgx_free_va_slot(encl_page->va_page, va_offset); + list_move(&encl_page->va_page->list, &encl->va_pages); + encl_page->desc &= ~SGX_ENCL_PAGE_VA_OFFSET_MASK; + encl_page->epc_page = epc_page; + + return epc_page; +} + static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl, unsigned long addr) { + struct sgx_epc_page *epc_page; struct sgx_encl_page *entry; /* If process was forked, VMA is still there but vm_private_data is set @@ -29,10 +109,27 @@ static struct sgx_encl_page *sgx_encl_load_page(struct sgx_encl *encl, return ERR_PTR(-EFAULT); /* Page is already resident in the EPC. */ - if (entry->epc_page) + if (entry->epc_page) { + if (entry->desc & SGX_ENCL_PAGE_RECLAIMED) + return ERR_PTR(-EBUSY); + return entry; + } + + if (!(encl->secs.epc_page)) { + epc_page = sgx_encl_eldu(&encl->secs); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); + } + + epc_page = entry->epc_page ? entry->epc_page : sgx_encl_eldu(entry); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); - return ERR_PTR(-EFAULT); + encl->secs_child_cnt++; + sgx_mark_page_reclaimable(entry->epc_page); + + return entry; } struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl, @@ -169,6 +266,8 @@ static unsigned int sgx_vma_fault(struct vm_fault *vmf) goto out; } + sgx_encl_test_and_clear_young(vma->vm_mm, entry); + out: mutex_unlock(&encl->lock); return ret; @@ -220,6 +319,7 @@ EXPORT_SYMBOL_GPL(sgx_encl_find); */ void sgx_encl_destroy(struct sgx_encl *encl) { + struct sgx_va_page *va_page; struct sgx_encl_page *entry; struct radix_tree_iter iter; void **slot; @@ -244,6 +344,15 @@ void sgx_encl_destroy(struct sgx_encl *encl) sgx_free_page(encl->secs.epc_page); encl->secs.epc_page = NULL; } + + + while (!list_empty(&encl->va_pages)) { + va_page = list_first_entry(&encl->va_pages, struct sgx_va_page, + list); + list_del(&va_page->list); + sgx_free_page(va_page->epc_page); + kfree(va_page); + } } EXPORT_SYMBOL_GPL(sgx_encl_destroy); @@ -347,3 +456,157 @@ struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl, *iter = SGX_ENCL_MM_ITER_NEXT; return encl_mm; } + +static int sgx_encl_test_and_clear_young_cb(pte_t *ptep, pgtable_t token, + unsigned long addr, void *data) +{ + pte_t pte; + int ret; + + ret = pte_young(*ptep); + if (ret) { + pte = pte_mkold(*ptep); + set_pte_at((struct mm_struct *)data, addr, ptep, pte); + } + + return ret; +} + +/** + * sgx_encl_test_and_clear_young() - Test and reset the accessed bit + * @mm: mm_struct that is checked + * @page: enclave page to be tested for recent access + * + * Checks the Access (A) bit from the PTE corresponding to the enclave page and + * clears it. + * + * Return: 1 if the page has been recently accessed and 0 if not. + */ +int sgx_encl_test_and_clear_young(struct mm_struct *mm, + struct sgx_encl_page *page) +{ + unsigned long addr = SGX_ENCL_PAGE_ADDR(page); + struct sgx_encl *encl = page->encl; + struct vm_area_struct *vma; + int ret; + + ret = sgx_encl_find(mm, addr, &vma); + if (ret) + return 0; + + if (encl != vma->vm_private_data) + return 0; + + ret = apply_to_page_range(vma->vm_mm, addr, PAGE_SIZE, + sgx_encl_test_and_clear_young_cb, vma->vm_mm); + if (ret < 0) + return 0; + + return ret; +} + +/** + * sgx_encl_reserve_page() - Reserve an enclave page + * @encl: an enclave + * @addr: a page address + * + * Load an enclave page and lock the enclave so that the page can be used by + * EDBG* and EMOD*. + * + * Return: + * an enclave page on success + * -EFAULT if the load fails + */ +struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl, + unsigned long addr) +{ + struct sgx_encl_page *entry; + + for ( ; ; ) { + mutex_lock(&encl->lock); + + entry = sgx_encl_load_page(encl, addr); + if (PTR_ERR(entry) != -EBUSY) + break; + + mutex_unlock(&encl->lock); + } + + if (IS_ERR(entry)) + mutex_unlock(&encl->lock); + + return entry; +} +EXPORT_SYMBOL(sgx_encl_reserve_page); + +/** + * sgx_alloc_page - allocate a VA page + * + * Allocates an &sgx_epc_page instance and converts it to a VA page. + * + * Return: + * a &struct sgx_va_page instance, + * -errno otherwise + */ +struct sgx_epc_page *sgx_alloc_va_page(void) +{ + struct sgx_epc_page *epc_page; + int ret; + + epc_page = sgx_alloc_page(NULL, true); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); + + ret = __epa(sgx_epc_addr(epc_page)); + if (ret) { + WARN_ONCE(1, "sgx: EPA returned %d (0x%x)", ret, ret); + sgx_free_page(epc_page); + return ERR_PTR(-EFAULT); + } + + return epc_page; +} +EXPORT_SYMBOL_GPL(sgx_alloc_va_page); + +/** + * sgx_alloc_va_slot - allocate a VA slot + * @va_page: a &struct sgx_va_page instance + * + * Allocates a slot from a &struct sgx_va_page instance. + * + * Return: offset of the slot inside the VA page + */ +unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page) +{ + int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT); + + if (slot < SGX_VA_SLOT_COUNT) + set_bit(slot, va_page->slots); + + return slot << 3; +} + +/** + * sgx_free_va_slot - free a VA slot + * @va_page: a &struct sgx_va_page instance + * @offset: offset of the slot inside the VA page + * + * Frees a slot from a &struct sgx_va_page instance. + */ +void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset) +{ + clear_bit(offset >> 3, va_page->slots); +} + +/** + * sgx_va_page_full - is the VA page full? + * @va_page: a &struct sgx_va_page instance + * + * Return: true if all slots have been taken + */ +bool sgx_va_page_full(struct sgx_va_page *va_page) +{ + int slot = find_first_zero_bit(va_page->slots, SGX_VA_SLOT_COUNT); + + return slot == SGX_VA_SLOT_COUNT; +} diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h index 1f96991839ad..c557f0374d74 100644 --- a/arch/x86/kernel/cpu/sgx/encl.h +++ b/arch/x86/kernel/cpu/sgx/encl.h @@ -17,6 +17,10 @@ /** * enum sgx_encl_page_desc - defines bits for an enclave page's descriptor * %SGX_ENCL_PAGE_TCS: The page is a TCS page. + * %SGX_ENCL_PAGE_RECLAIMED: The page is in the process of being + * reclaimed. + * %SGX_ENCL_PAGE_VA_OFFSET_MASK: Holds the offset in the Version Array + * (VA) page for a swapped page. * %SGX_ENCL_PAGE_ADDR_MASK: Holds the virtual address of the page. * * The page address for SECS is zero and is used by the subsystem to recognize @@ -25,6 +29,8 @@ enum sgx_encl_page_desc { SGX_ENCL_PAGE_TCS = BIT(0), /* Bits 11:3 are available when the page is not swapped. */ + SGX_ENCL_PAGE_RECLAIMED = BIT(3), + SGX_ENCL_PAGE_VA_OFFSET_MASK = GENMASK_ULL(11, 3), SGX_ENCL_PAGE_ADDR_MASK = PAGE_MASK, }; @@ -36,6 +42,7 @@ enum sgx_encl_page_desc { struct sgx_encl_page { unsigned long desc; struct sgx_epc_page *epc_page; + struct sgx_va_page *va_page; struct sgx_encl *encl; }; @@ -68,15 +75,37 @@ struct sgx_encl { unsigned long base; unsigned long size; unsigned long ssaframesize; + struct list_head va_pages; struct radix_tree_root page_tree; struct list_head add_page_reqs; struct work_struct work; struct sgx_encl_page secs; struct notifier_block pm_notifier; + cpumask_t cpumask; +}; + +#define SGX_VA_SLOT_COUNT 512 + +struct sgx_va_page { + struct sgx_epc_page *epc_page; + DECLARE_BITMAP(slots, SGX_VA_SLOT_COUNT); + struct list_head list; }; extern const struct vm_operations_struct sgx_vm_ops; +static inline pgoff_t sgx_pcmd_index(struct sgx_encl *encl, + pgoff_t page_index) +{ + return PFN_DOWN(encl->size) + 1 + (page_index >> 5); +} + +static inline unsigned long sgx_pcmd_offset(pgoff_t page_index) +{ + return (page_index & (PAGE_SIZE / sizeof(struct sgx_pcmd) - 1)) * + sizeof(struct sgx_pcmd); +} + enum sgx_encl_mm_iter { SGX_ENCL_MM_ITER_DONE = 0, SGX_ENCL_MM_ITER_NEXT = 1, @@ -94,5 +123,14 @@ struct sgx_encl_mm *sgx_encl_next_mm(struct sgx_encl *encl, struct sgx_encl_mm *sgx_encl_mm_add(struct sgx_encl *encl, struct mm_struct *mm); void sgx_encl_mm_release(struct kref *ref); +int sgx_encl_test_and_clear_young(struct mm_struct *mm, + struct sgx_encl_page *page); +struct sgx_encl_page *sgx_encl_reserve_page(struct sgx_encl *encl, + unsigned long addr); + +struct sgx_epc_page *sgx_alloc_va_page(void); +unsigned int sgx_alloc_va_slot(struct sgx_va_page *va_page); +void sgx_free_va_slot(struct sgx_va_page *va_page, unsigned int offset); +bool sgx_va_page_full(struct sgx_va_page *va_page); #endif /* _X86_ENCL_H */ diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c index d911a1038712..07adb35c260b 100644 --- a/arch/x86/kernel/cpu/sgx/main.c +++ b/arch/x86/kernel/cpu/sgx/main.c @@ -22,7 +22,7 @@ int sgx_nr_epc_sections; /* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */ static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache); -static struct sgx_epc_page *sgx_section_get_page( +static struct sgx_epc_page *sgx_section_try_take_page( struct sgx_epc_section *section) { struct sgx_epc_page *page; @@ -30,23 +30,14 @@ static struct sgx_epc_page *sgx_section_get_page( if (!section->free_cnt) return NULL; - page = list_first_entry(§ion->page_list, - struct sgx_epc_page, list); + page = list_first_entry(§ion->page_list, struct sgx_epc_page, + list); list_del_init(&page->list); section->free_cnt--; return page; } -/** - * sgx_alloc_page - Allocate an EPC page - * - * Try to grab a page from the free EPC page list. - * - * Return: - * a pointer to a &struct sgx_epc_page instance, - * -errno on error - */ -struct sgx_epc_page *sgx_alloc_page(void) +static struct sgx_epc_page *sgx_try_alloc_page(void *owner) { struct sgx_epc_section *section; struct sgx_epc_page *page; @@ -55,14 +46,61 @@ struct sgx_epc_page *sgx_alloc_page(void) for (i = 0; i < sgx_nr_epc_sections; i++) { section = &sgx_epc_sections[i]; spin_lock(§ion->lock); - page = sgx_section_get_page(section); + page = sgx_section_try_take_page(section); spin_unlock(§ion->lock); - if (page) + if (page) { + page->owner = owner; return page; + } } - return ERR_PTR(-ENOMEM); + return NULL; +} + +/** + * sgx_alloc_page - Allocate an EPC page + * @owner: the owner of the EPC page + * @reclaim: reclaim pages if necessary + * + * Try to grab a page from the free EPC page list. If there is a free page + * available, it is returned to the caller. The @reclaim parameter hints + * the EPC memory manager to swap pages when required. + * + * Return: + * a pointer to a &struct sgx_epc_page instance, + * -errno on error + */ +struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim) +{ + struct sgx_epc_page *entry; + + for ( ; ; ) { + entry = sgx_try_alloc_page(owner); + if (entry) + break; + + if (list_empty(&sgx_active_page_list)) + return ERR_PTR(-ENOMEM); + + if (!reclaim) { + entry = ERR_PTR(-EBUSY); + break; + } + + if (signal_pending(current)) { + entry = ERR_PTR(-ERESTARTSYS); + break; + } + + sgx_reclaim_pages(); + schedule(); + } + + if (sgx_calc_free_cnt() < SGX_NR_LOW_PAGES) + wake_up(&ksgxswapd_waitq); + + return entry; } EXPORT_SYMBOL_GPL(sgx_alloc_page); @@ -70,10 +108,12 @@ EXPORT_SYMBOL_GPL(sgx_alloc_page); * __sgx_free_page - Free an EPC page * @page: pointer a previously allocated EPC page * - * EREMOVE an EPC page and insert it back to the list of free pages. + * EREMOVE an EPC page and insert it back to the list of free pages. If the + * page is reclaimable, delete it from the active page list. * * Return: * 0 on success + * -EBUSY if the page cannot be removed from the active list * SGX error code if EREMOVE fails */ int __sgx_free_page(struct sgx_epc_page *page) @@ -81,6 +121,23 @@ int __sgx_free_page(struct sgx_epc_page *page) struct sgx_epc_section *section = sgx_epc_section(page); int ret; + /* + * Remove the page from the active list if necessary. If the page + * is actively being reclaimed, i.e. RECLAIMABLE is set but the + * page isn't on the active list, return -EBUSY as we can't free + * the page at this time since it is "owned" by the reclaimer. + */ + spin_lock(&sgx_active_page_list_lock); + if (page->desc & SGX_EPC_PAGE_RECLAIMABLE) { + if (list_empty(&page->list)) { + spin_unlock(&sgx_active_page_list_lock); + return -EBUSY; + } + list_del(&page->list); + page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE; + } + spin_unlock(&sgx_active_page_list_lock); + ret = __eremove(sgx_epc_addr(page)); if (ret) return ret; @@ -107,6 +164,7 @@ void sgx_free_page(struct sgx_epc_page *page) int ret; ret = __sgx_free_page(page); + WARN(ret < 0, "sgx: cannot free page, reclaim in-progress"); WARN(ret > 0, "sgx: EREMOVE returned %d (0x%x)", ret, ret); } EXPORT_SYMBOL_GPL(sgx_free_page); diff --git a/arch/x86/kernel/cpu/sgx/reclaim.c b/arch/x86/kernel/cpu/sgx/reclaim.c index 042769f03be9..219d31495838 100644 --- a/arch/x86/kernel/cpu/sgx/reclaim.c +++ b/arch/x86/kernel/cpu/sgx/reclaim.c @@ -9,9 +9,13 @@ #include #include #include -#include "encls.h" +#include "driver/driver.h" #include "sgx.h" +LIST_HEAD(sgx_active_page_list); +DEFINE_SPINLOCK(sgx_active_page_list_lock); +DECLARE_WAIT_QUEUE_HEAD(ksgxswapd_waitq); + static struct task_struct *ksgxswapd_tsk; static void sgx_sanitize_section(struct sgx_epc_section *section) @@ -58,6 +62,12 @@ static void sgx_sanitize_section(struct sgx_epc_section *section) } } +static inline bool sgx_should_reclaim(void) +{ + return sgx_calc_free_cnt() < SGX_NR_HIGH_PAGES && + !list_empty(&sgx_active_page_list); +} + static int ksgxswapd(void *p) { int i; @@ -67,6 +77,19 @@ static int ksgxswapd(void *p) for (i = 0; i < sgx_nr_epc_sections; i++) sgx_sanitize_section(&sgx_epc_sections[i]); + while (!kthread_should_stop()) { + if (try_to_freeze()) + continue; + + wait_event_freezable(ksgxswapd_waitq, kthread_should_stop() || + sgx_should_reclaim()); + + if (sgx_should_reclaim()) + sgx_reclaim_pages(); + + cond_resched(); + } + return 0; } @@ -82,3 +105,378 @@ int sgx_page_reclaimer_init(void) return 0; } + +/** + * sgx_mark_page_reclaimable() - Mark a page as reclaimable + * @page: EPC page + * + * Mark a page as reclaimable and add it to the active page list. Pages + * are automatically removed from the active list when freed. + */ +void sgx_mark_page_reclaimable(struct sgx_epc_page *page) +{ + spin_lock(&sgx_active_page_list_lock); + page->desc |= SGX_EPC_PAGE_RECLAIMABLE; + list_add_tail(&page->list, &sgx_active_page_list); + spin_unlock(&sgx_active_page_list_lock); +} +EXPORT_SYMBOL_GPL(sgx_mark_page_reclaimable); + +bool sgx_reclaimer_get(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + struct sgx_encl *encl = encl_page->encl; + + return kref_get_unless_zero(&encl->refcount) != 0; +} + +void sgx_reclaimer_put(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + struct sgx_encl *encl = encl_page->encl; + + kref_put(&encl->refcount, sgx_encl_release); +} + +static bool sgx_reclaimer_evict(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *page = epc_page->owner; + struct sgx_encl *encl = page->encl; + struct sgx_encl_mm *encl_mm = NULL; + struct sgx_encl_mm *prev_mm = NULL; + bool ret = true; + int iter; + + while (true) { + encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter); + if (prev_mm) + kref_put(&prev_mm->refcount, sgx_encl_mm_release); + prev_mm = encl_mm; + + if (iter == SGX_ENCL_MM_ITER_DONE) + break; + + if (iter == SGX_ENCL_MM_ITER_RESTART) + continue; + + if (!mmget_not_zero(encl_mm->mm)) + continue; + + down_read(&encl_mm->mm->mmap_sem); + ret = !sgx_encl_test_and_clear_young(encl_mm->mm, page); + up_read(&encl_mm->mm->mmap_sem); + + mmput(encl_mm->mm); + + if (!ret || (encl->flags & SGX_ENCL_DEAD)) { + kref_put(&encl_mm->refcount, sgx_encl_mm_release); + break; + } + } + + /* + * Do not reclaim this page if it has been recently accessed by any + * mm_struct *and* if the enclave is still alive. No need to take + * the enclave's lock, worst case scenario reclaiming pages from a + * dead enclave is delayed slightly. A live enclave with a recently + * accessed page is more common and avoiding lock contention in that + * case is a boon to performance. + */ + if (!ret && !(encl->flags & SGX_ENCL_DEAD)) + return false; + + mutex_lock(&encl->lock); + page->desc |= SGX_ENCL_PAGE_RECLAIMED; + mutex_unlock(&encl->lock); + + return true; +} + +static void sgx_reclaimer_block(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *page = epc_page->owner; + unsigned long addr = SGX_ENCL_PAGE_ADDR(page); + struct sgx_encl *encl = page->encl; + struct sgx_encl_mm *encl_mm = NULL; + struct sgx_encl_mm *prev_mm = NULL; + struct vm_area_struct *vma; + int iter; + int ret; + + while (true) { + encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter); + if (prev_mm) + kref_put(&prev_mm->refcount, sgx_encl_mm_release); + prev_mm = encl_mm; + + if (iter == SGX_ENCL_MM_ITER_DONE) + break; + + if (iter == SGX_ENCL_MM_ITER_RESTART) + continue; + + if (!mmget_not_zero(encl_mm->mm)) + continue; + + down_read(&encl_mm->mm->mmap_sem); + + ret = sgx_encl_find(encl_mm->mm, addr, &vma); + if (!ret && encl == vma->vm_private_data) + zap_vma_ptes(vma, addr, PAGE_SIZE); + + up_read(&encl_mm->mm->mmap_sem); + + mmput(encl_mm->mm); + } + + mutex_lock(&encl->lock); + + if (!(encl->flags & SGX_ENCL_DEAD)) { + ret = __eblock(sgx_epc_addr(epc_page)); + if (encls_failed(ret)) + ENCLS_WARN(ret, "EBLOCK"); + } + + mutex_unlock(&encl->lock); +} + +static int __sgx_encl_ewb(struct sgx_encl *encl, struct sgx_epc_page *epc_page, + struct sgx_va_page *va_page, unsigned int va_offset) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + pgoff_t page_index = sgx_encl_get_index(encl, encl_page); + pgoff_t pcmd_index = sgx_pcmd_index(encl, page_index); + unsigned long pcmd_offset = sgx_pcmd_offset(page_index); + struct sgx_pageinfo pginfo; + struct page *backing; + struct page *pcmd; + int ret; + + backing = sgx_encl_get_backing_page(encl, page_index); + if (IS_ERR(backing)) { + ret = PTR_ERR(backing); + goto err_backing; + } + + pcmd = sgx_encl_get_backing_page(encl, pcmd_index); + if (IS_ERR(pcmd)) { + ret = PTR_ERR(pcmd); + goto err_pcmd; + } + + pginfo.addr = 0; + pginfo.contents = (unsigned long)kmap_atomic(backing); + pginfo.metadata = (unsigned long)kmap_atomic(pcmd) + pcmd_offset; + pginfo.secs = 0; + ret = __ewb(&pginfo, sgx_epc_addr(epc_page), + sgx_epc_addr(va_page->epc_page) + va_offset); + kunmap_atomic((void *)(unsigned long)(pginfo.metadata - pcmd_offset)); + kunmap_atomic((void *)(unsigned long)pginfo.contents); + + set_page_dirty(pcmd); + put_page(pcmd); + set_page_dirty(backing); + +err_pcmd: + put_page(backing); + +err_backing: + return ret; +} + +static void sgx_ipi_cb(void *info) +{ +} + +static const cpumask_t *sgx_encl_ewb_cpumask(struct sgx_encl *encl) +{ + cpumask_t *cpumask = &encl->cpumask; + struct sgx_encl_mm *encl_mm = NULL; + struct sgx_encl_mm *prev_mm = NULL; + int iter; + + cpumask_clear(cpumask); + + while (true) { + encl_mm = sgx_encl_next_mm(encl, prev_mm, &iter); + if (prev_mm) + kref_put(&prev_mm->refcount, sgx_encl_mm_release); + prev_mm = encl_mm; + + if (iter == SGX_ENCL_MM_ITER_DONE) + break; + + if (iter == SGX_ENCL_MM_ITER_RESTART) + continue; + + if (!mmget_not_zero(encl_mm->mm)) + continue; + + cpumask_or(cpumask, cpumask, mm_cpumask(encl_mm->mm)); + + mmput(encl_mm->mm); + } + + return cpumask; +} + +static void sgx_encl_ewb(struct sgx_epc_page *epc_page, bool do_free) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + struct sgx_encl *encl = encl_page->encl; + struct sgx_va_page *va_page; + unsigned int va_offset; + int ret; + + encl_page->desc &= ~SGX_ENCL_PAGE_RECLAIMED; + + if (!(encl->flags & SGX_ENCL_DEAD)) { + va_page = list_first_entry(&encl->va_pages, struct sgx_va_page, + list); + va_offset = sgx_alloc_va_slot(va_page); + if (sgx_va_page_full(va_page)) + list_move_tail(&va_page->list, &encl->va_pages); + + ret = __sgx_encl_ewb(encl, epc_page, va_page, va_offset); + if (ret == SGX_NOT_TRACKED) { + ret = __etrack(sgx_epc_addr(encl->secs.epc_page)); + if (ret) { + if (encls_failed(ret) || + encls_returned_code(ret)) + ENCLS_WARN(ret, "ETRACK"); + } + + ret = __sgx_encl_ewb(encl, epc_page, va_page, + va_offset); + if (ret == SGX_NOT_TRACKED) { + /* + * Slow path, send IPIs to kick cpus out of the + * enclave. Note, it's imperative that the cpu + * mask is generated *after* ETRACK, else we'll + * miss cpus that entered the enclave between + * generating the mask and incrementing epoch. + */ + on_each_cpu_mask(sgx_encl_ewb_cpumask(encl), + sgx_ipi_cb, NULL, 1); + ret = __sgx_encl_ewb(encl, epc_page, va_page, + va_offset); + } + } + + if (ret) + if (encls_failed(ret) || encls_returned_code(ret)) + ENCLS_WARN(ret, "EWB"); + + encl_page->desc |= va_offset; + encl_page->va_page = va_page; + } else if (!do_free) { + ret = __eremove(sgx_epc_addr(epc_page)); + WARN(ret, "EREMOVE returned %d\n", ret); + } + + if (do_free) + sgx_free_page(epc_page); + + encl_page->epc_page = NULL; +} + +static void sgx_reclaimer_write(struct sgx_epc_page *epc_page) +{ + struct sgx_encl_page *encl_page = epc_page->owner; + struct sgx_encl *encl = encl_page->encl; + + mutex_lock(&encl->lock); + + sgx_encl_ewb(epc_page, false); + encl->secs_child_cnt--; + if (!encl->secs_child_cnt && + (encl->flags & (SGX_ENCL_DEAD | SGX_ENCL_INITIALIZED))) { + sgx_encl_ewb(encl->secs.epc_page, true); + } + + mutex_unlock(&encl->lock); +} + +/** + * sgx_reclaim_pages() - Reclaim EPC pages from the consumers + * Takes a fixed chunk of pages from the global list of consumed EPC pages and + * tries to swap them. Only the pages that are either being freed by the + * consumer or actively used are skipped. + */ +void sgx_reclaim_pages(void) +{ + struct sgx_epc_page *chunk[SGX_NR_TO_SCAN + 1]; + struct sgx_epc_page *epc_page; + struct sgx_epc_section *section; + int i, j; + + spin_lock(&sgx_active_page_list_lock); + for (i = 0, j = 0; i < SGX_NR_TO_SCAN; i++) { + if (list_empty(&sgx_active_page_list)) + break; + + epc_page = list_first_entry(&sgx_active_page_list, + struct sgx_epc_page, list); + list_del_init(&epc_page->list); + + if (sgx_reclaimer_get(epc_page)) + chunk[j++] = epc_page; + else + /* The owner is freeing the page. No need to add the + * page back to the list of reclaimable pages. + */ + epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE; + } + spin_unlock(&sgx_active_page_list_lock); + + for (i = 0; i < j; i++) { + epc_page = chunk[i]; + if (sgx_reclaimer_evict(epc_page)) + continue; + + sgx_reclaimer_put(epc_page); + + spin_lock(&sgx_active_page_list_lock); + list_add_tail(&epc_page->list, &sgx_active_page_list); + spin_unlock(&sgx_active_page_list_lock); + + chunk[i] = NULL; + } + + for (i = 0; i < j; i++) { + epc_page = chunk[i]; + if (epc_page) + sgx_reclaimer_block(epc_page); + } + + for (i = 0; i < j; i++) { + epc_page = chunk[i]; + if (epc_page) { + sgx_reclaimer_write(epc_page); + sgx_reclaimer_put(epc_page); + epc_page->desc &= ~SGX_EPC_PAGE_RECLAIMABLE; + + section = sgx_epc_section(epc_page); + + spin_lock(§ion->lock); + list_add_tail(&epc_page->list, + §ion->page_list); + section->free_cnt++; + spin_unlock(§ion->lock); + } + } +} + +unsigned long sgx_calc_free_cnt(void) +{ + struct sgx_epc_section *section; + unsigned long free_cnt = 0; + int i; + + for (i = 0; i < sgx_nr_epc_sections; i++) { + section = &sgx_epc_sections[i]; + free_cnt += section->free_cnt; + } + + return free_cnt; +} diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h index 62a574ed230a..8a1dff1e5e8a 100644 --- a/arch/x86/kernel/cpu/sgx/sgx.h +++ b/arch/x86/kernel/cpu/sgx/sgx.h @@ -12,6 +12,7 @@ struct sgx_epc_page { unsigned long desc; + struct sgx_encl_page *owner; struct list_head list; }; @@ -43,9 +44,14 @@ extern bool sgx_enabled; * physical memory. The existing and near-future * hardware defines at most eight sections, hence * three bits to hold a section. + * %SGX_EPC_PAGE_RECLAIMABLE: The page has been been marked as reclaimable. + * Pages need to be colored this way because a page + * can be out of the active page list in the + * process of being swapped out. */ enum sgx_epc_page_desc { SGX_EPC_SECTION_MASK = GENMASK_ULL(3, 0), + SGX_EPC_PAGE_RECLAIMABLE = BIT(4), /* bits 12-63 are reserved for the physical page address of the page */ }; @@ -61,11 +67,21 @@ static inline void *sgx_epc_addr(struct sgx_epc_page *page) return section->va + (page->desc & PAGE_MASK) - section->pa; } +#define SGX_NR_TO_SCAN 16 +#define SGX_NR_LOW_PAGES 32 +#define SGX_NR_HIGH_PAGES 64 + extern int sgx_nr_epc_sections; +extern struct list_head sgx_active_page_list; +extern spinlock_t sgx_active_page_list_lock; +extern struct wait_queue_head(ksgxswapd_waitq); int sgx_page_reclaimer_init(void); +void sgx_mark_page_reclaimable(struct sgx_epc_page *page); +unsigned long sgx_calc_free_cnt(void); +void sgx_reclaim_pages(void); -struct sgx_epc_page *sgx_alloc_page(void); +struct sgx_epc_page *sgx_alloc_page(void *owner, bool reclaim); int __sgx_free_page(struct sgx_epc_page *page); void sgx_free_page(struct sgx_epc_page *page); int sgx_einit(struct sgx_sigstruct *sigstruct, struct sgx_einittoken *token, From patchwork Wed Apr 17 10:39:28 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905177 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 52BF317E0 for ; Wed, 17 Apr 2019 10:42:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3EAEA289F5 for ; Wed, 17 Apr 2019 10:42:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 323CC28AD4; Wed, 17 Apr 2019 10:42:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C2BBE289F5 for ; Wed, 17 Apr 2019 10:42:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731792AbfDQKmD (ORCPT ); Wed, 17 Apr 2019 06:42:03 -0400 Received: from mga02.intel.com ([134.134.136.20]:29437 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731566AbfDQKmD (ORCPT ); Wed, 17 Apr 2019 06:42:03 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:02 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120156" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:41:56 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 18/28] x86/sgx: ptrace() support for the SGX driver Date: Wed, 17 Apr 2019 13:39:28 +0300 Message-Id: <20190417103938.7762-19-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add VMA callbacks for ptrace() that can be used with debug enclaves. With debug enclaves data can be read and write the memory word at a time by using ENCLS(EDBGRD) and ENCLS(EDBGWR) leaf instructions. Signed-off-by: Jarkko Sakkinen --- arch/x86/kernel/cpu/sgx/encl.c | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c index 16e8524687c1..7216bdf07bd0 100644 --- a/arch/x86/kernel/cpu/sgx/encl.c +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -273,10 +273,107 @@ static unsigned int sgx_vma_fault(struct vm_fault *vmf) return ret; } +static int sgx_edbgrd(struct sgx_encl *encl, struct sgx_encl_page *page, + unsigned long addr, void *data) +{ + unsigned long offset; + int ret; + + offset = addr & ~PAGE_MASK; + + if ((page->desc & SGX_ENCL_PAGE_TCS) && + offset > offsetof(struct sgx_tcs, gs_limit)) + return -ECANCELED; + + ret = __edbgrd(sgx_epc_addr(page->epc_page) + offset, data); + if (ret) + return -EIO; + + return 0; +} + +static int sgx_edbgwr(struct sgx_encl *encl, struct sgx_encl_page *page, + unsigned long addr, void *data) +{ + unsigned long offset; + int ret; + + offset = addr & ~PAGE_MASK; + + /* Writing anything else than flags will cause #GP */ + if ((page->desc & SGX_ENCL_PAGE_TCS) && + offset != offsetof(struct sgx_tcs, flags)) + return -ECANCELED; + + ret = __edbgwr(sgx_epc_addr(page->epc_page) + offset, data); + if (ret) + return -EIO; + + return 0; +} + +static int sgx_vma_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + struct sgx_encl *encl = vma->vm_private_data; + struct sgx_encl_page *entry = NULL; + unsigned long align; + char data[sizeof(unsigned long)]; + int offset; + int cnt; + int ret = 0; + int i; + + /* If process was forked, VMA is still there but vm_private_data is set + * to NULL. + */ + if (!encl) + return -EFAULT; + + if (!(encl->flags & SGX_ENCL_DEBUG) || + !(encl->flags & SGX_ENCL_INITIALIZED) || + (encl->flags & SGX_ENCL_DEAD)) + return -EFAULT; + + for (i = 0; i < len; i += cnt) { + entry = sgx_encl_reserve_page(encl, (addr + i) & PAGE_MASK); + if (IS_ERR(entry)) { + ret = PTR_ERR(entry); + break; + } + + align = ALIGN_DOWN(addr + i, sizeof(unsigned long)); + offset = (addr + i) & (sizeof(unsigned long) - 1); + cnt = sizeof(unsigned long) - offset; + cnt = min(cnt, len - i); + + ret = sgx_edbgrd(encl, entry, align, data); + if (ret) + goto out; + + if (write) { + memcpy(data + offset, buf + i, cnt); + ret = sgx_edbgwr(encl, entry, align, data); + if (ret) + goto out; + } else + memcpy(buf + i, data + offset, cnt); + +out: + mutex_unlock(&encl->lock); + + if (ret) + break; + } + + return ret < 0 ? ret : i; +} + const struct vm_operations_struct sgx_vm_ops = { .close = sgx_vma_close, .open = sgx_vma_open, .fault = sgx_vma_fault, + .access = sgx_vma_access, }; EXPORT_SYMBOL_GPL(sgx_vm_ops); From patchwork Wed Apr 17 10:39:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905179 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A511C17E0 for ; Wed, 17 Apr 2019 10:42:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8809428AD1 for ; Wed, 17 Apr 2019 10:42:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 76643289F5; Wed, 17 Apr 2019 10:42:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6EACF289F5 for ; Wed, 17 Apr 2019 10:42:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731808AbfDQKmL (ORCPT ); Wed, 17 Apr 2019 06:42:11 -0400 Received: from mga12.intel.com ([192.55.52.136]:43405 "EHLO mga12.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731566AbfDQKmK (ORCPT ); Wed, 17 Apr 2019 06:42:10 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:09 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120179" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:03 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Andy Lutomirski , Jarkko Sakkinen , Dave Hansen Subject: [PATCH v20 19/28] x86/vdso: Add support for exception fixup in vDSO functions Date: Wed, 17 Apr 2019 13:39:29 +0300 Message-Id: <20190417103938.7762-20-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson The basic concept and implementation is very similar to the kernel's exception fixup mechanism. The key differences are that the kernel handler is hardcoded and the fixup entry addresses are relative to the overall table as opposed to individual entries. Hardcoding the kernel handler avoids the need to figure out how to get userspace code to point at a kernel function. Given that the expected usage is to propagate information to userspace, dumping all fault information into registers is likely the desired behavior for the vast majority of yet-to-be-created functions. Use registers DI, SI and DX to communicate fault information, which follows Linux's ABI for register consumption and hopefully avoids conflict with hardware features that might leverage the fixup capabilities, e.g. register usage for SGX instructions was at least partially designed with calling conventions in mind. Making fixup addresses relative to the overall table allows the table to be stripped from the final vDSO image (it's a kernel construct) without complicating the offset logic, e.g. entry-relative addressing would also need to account for the table's location relative to the image. Regarding stripping the table, modify vdso2c to extract the table from the raw, a.k.a. unstripped, data and dump it as a standalone byte array in the resulting .c file. The original base of the table, its length and a pointer to the byte array are captured in struct vdso_image. Alternatively, the table could be dumped directly into the struct, but because the number of entries can vary per image, that would require either hardcoding a max sized table into the struct definition or defining the table as a flexible length array. The flexible length array approach has zero benefits, e.g. the base/size are still needed, and prevents reusing the extraction code, while hardcoding the max size adds ongoing maintenance just to avoid exporting the explicit size. The immediate use case is for Intel Software Guard Extensions (SGX). SGX introduces a new CPL3-only "enclave" mode that runs as a sort of black box shared object that is hosted by an untrusted "normal" CPl3 process. Entering an enclave can only be done through SGX-specific instructions, EENTER and ERESUME, and is a non-trivial process. Because of the complexity of transitioning to/from an enclave, the vast majority of enclaves are expected to utilize a library to handle the actual transitions. This is roughly analogous to how e.g. libc implementations are used by most applications. Another crucial characteristic of SGX enclaves is that they can generate exceptions as part of their normal (at least as "normal" as SGX can be) operation that need to be handled *in* the enclave and/or are unique to SGX. And because they are essentially fancy shared objects, a process can host any number of enclaves, each of which can execute multiple threads simultaneously. Putting everything together, userspace enclaves will utilize a library that must be prepared to handle any and (almost) all exceptions any time at least one thread may be executing in an enclave. Leveraging signals to handle the enclave exceptions is unpleasant, to put it mildly, e.g. the SGX library must constantly (un)register its signal handler based on whether or not at least one thread is executing in an enclave, and filter and forward exceptions that aren't related to its enclaves. This becomes particularly nasty when using multiple levels of libraries that register signal handlers, e.g. running an enclave via cgo inside of the Go runtime. Enabling exception fixup in vDSO allows the kernel to provide a vDSO function that wraps the low-level transitions to/from the enclave, i.e. the EENTER and ERESUME instructions. The vDSO function can intercept exceptions that would otherwise generate a signal and return the fault information directly to its caller, thus avoiding the need to juggle signal handlers. Note that unlike the kernel's _ASM_EXTABLE_HANDLE implementation, the 'C' version of _ASM_VDSO_EXTABLE_HANDLE doesn't use a pre-compiled assembly macro. Duplicating four lines of code is simpler than adding the necessary infrastructure to generate pre-compiled assembly and the intended benefit of massaging GCC's inlining algorithm is unlikely to realized in the vDSO any time soon, if ever. Suggested-by: Andy Lutomirski Cc: Andy Lutomirski Cc: Jarkko Sakkinen Cc: Dave Hansen Cc: Josh Triplett Signed-off-by: Sean Christopherson --- arch/x86/entry/vdso/Makefile | 4 +- arch/x86/entry/vdso/extable.c | 37 +++++++++++++++++ arch/x86/entry/vdso/extable.h | 29 ++++++++++++++ arch/x86/entry/vdso/vdso-layout.lds.S | 9 ++++- arch/x86/entry/vdso/vdso2c.h | 58 +++++++++++++++++++++++---- arch/x86/include/asm/vdso.h | 5 +++ 6 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 arch/x86/entry/vdso/extable.c create mode 100644 arch/x86/entry/vdso/extable.h diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 5bfe2243a08f..34bcf87d358c 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -20,7 +20,7 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o # files to link into kernel -obj-y += vma.o +obj-y += vma.o extable.o OBJECT_FILES_NON_STANDARD_vma.o := n # vDSO images to build @@ -115,7 +115,7 @@ $(obj)/%-x32.o: $(obj)/%.o FORCE targets += vdsox32.lds $(vobjx32s-y) -$(obj)/%.so: OBJCOPYFLAGS := -S +$(obj)/%.so: OBJCOPYFLAGS := -S --remove-section __ex_table $(obj)/%.so: $(obj)/%.so.dbg $(call if_changed,objcopy) diff --git a/arch/x86/entry/vdso/extable.c b/arch/x86/entry/vdso/extable.c new file mode 100644 index 000000000000..49284d560d36 --- /dev/null +++ b/arch/x86/entry/vdso/extable.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +struct vdso_exception_table_entry { + int insn, fixup; +}; + +bool fixup_vdso_exception(struct pt_regs *regs, int trapnr, + unsigned long error_code, unsigned long fault_addr) +{ + const struct vdso_image *image = current->mm->context.vdso_image; + const struct vdso_exception_table_entry *extable; + unsigned int nr_entries, i; + unsigned long base; + + if (!current->mm->context.vdso) + return false; + + base = (unsigned long)current->mm->context.vdso + image->extable_base; + nr_entries = image->extable_len / (sizeof(*extable)); + extable = image->extable; + + for (i = 0; i < nr_entries; i++) { + if (regs->ip == base + extable[i].insn) { + regs->ip = base + extable[i].fixup; + regs->di = trapnr; + regs->si = error_code; + regs->dx = fault_addr; + return true; + } + } + + return false; +} diff --git a/arch/x86/entry/vdso/extable.h b/arch/x86/entry/vdso/extable.h new file mode 100644 index 000000000000..aafdac396948 --- /dev/null +++ b/arch/x86/entry/vdso/extable.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __VDSO_EXTABLE_H +#define __VDSO_EXTABLE_H + +/* + * Inject exception fixup for vDSO code. Unlike normal exception fixup, + * vDSO uses a dedicated handler the addresses are relative to the overall + * exception table, not each individual entry. + */ +#ifdef __ASSEMBLY__ +#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \ + ASM_VDSO_EXTABLE_HANDLE from to + +.macro ASM_VDSO_EXTABLE_HANDLE from:req to:req + .pushsection __ex_table, "a" + .long (\from) - __ex_table + .long (\to) - __ex_table + .popsection +.endm +#else +#define _ASM_VDSO_EXTABLE_HANDLE(from, to) \ + ".pushsection __ex_table, \"a\"\n" \ + ".long (" #from ") - __ex_table\n" \ + ".long (" #to ") - __ex_table\n" \ + ".popsection\n" +#endif + +#endif /* __VDSO_EXTABLE_H */ + diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S index 93c6dc7812d0..8ef849064501 100644 --- a/arch/x86/entry/vdso/vdso-layout.lds.S +++ b/arch/x86/entry/vdso/vdso-layout.lds.S @@ -63,11 +63,18 @@ SECTIONS * stuff that isn't used at runtime in between. */ - .text : { *(.text*) } :text =0x90909090, + .text : { + *(.text*) + *(.fixup) + } :text =0x90909090, + + .altinstructions : { *(.altinstructions) } :text .altinstr_replacement : { *(.altinstr_replacement) } :text + __ex_table : { *(__ex_table) } :text + /DISCARD/ : { *(.discard) *(.discard.*) diff --git a/arch/x86/entry/vdso/vdso2c.h b/arch/x86/entry/vdso/vdso2c.h index fa847a620f40..eca2f808bec3 100644 --- a/arch/x86/entry/vdso/vdso2c.h +++ b/arch/x86/entry/vdso/vdso2c.h @@ -5,6 +5,41 @@ * are built for 32-bit userspace. */ +static void BITSFUNC(copy)(FILE *outfile, const unsigned char *data, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) { + if (i % 10 == 0) + fprintf(outfile, "\n\t"); + fprintf(outfile, "0x%02X, ", (int)(data)[i]); + } +} + + +/* + * Extract a section from the input data into a standalone blob. Used to + * capture kernel-only data that needs to persist indefinitely, e.g. the + * exception fixup tables, but only in the kernel, i.e. the section can + * be stripped from the final vDSO image. + */ +static void BITSFUNC(extract)(const unsigned char *data, size_t data_len, + FILE *outfile, ELF(Shdr) *sec, const char *name) +{ + unsigned long offset; + size_t len; + + offset = (unsigned long)GET_LE(&sec->sh_offset); + len = (size_t)GET_LE(&sec->sh_size); + + if (offset + len > data_len) + fail("section to extract overruns input data"); + + fprintf(outfile, "static const unsigned char %s[%lu] = {", name, len); + BITSFUNC(copy)(outfile, data + offset, len); + fprintf(outfile, "\n};\n\n"); +} + static void BITSFUNC(go)(void *raw_addr, size_t raw_len, void *stripped_addr, size_t stripped_len, FILE *outfile, const char *name) @@ -14,9 +49,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, unsigned long mapping_size; ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr; int i; - unsigned long j; ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr, - *alt_sec = NULL; + *alt_sec = NULL, *extable_sec = NULL; ELF(Dyn) *dyn = 0, *dyn_end = 0; const char *secstrings; INT_BITS syms[NSYMS] = {}; @@ -78,6 +112,8 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, if (!strcmp(secstrings + GET_LE(&sh->sh_name), ".altinstructions")) alt_sec = sh; + if (!strcmp(secstrings + GET_LE(&sh->sh_name), "__ex_table")) + extable_sec = sh; } if (!symtab_hdr) @@ -149,13 +185,11 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, fprintf(outfile, "static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {", mapping_size); - for (j = 0; j < stripped_len; j++) { - if (j % 10 == 0) - fprintf(outfile, "\n\t"); - fprintf(outfile, "0x%02X, ", - (int)((unsigned char *)stripped_addr)[j]); - } + BITSFUNC(copy)(outfile, stripped_addr, stripped_len); fprintf(outfile, "\n};\n\n"); + if (extable_sec) + BITSFUNC(extract)(raw_addr, raw_len, outfile, + extable_sec, "extable"); fprintf(outfile, "const struct vdso_image %s = {\n", name); fprintf(outfile, "\t.data = raw_data,\n"); @@ -166,6 +200,14 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len, fprintf(outfile, "\t.alt_len = %lu,\n", (unsigned long)GET_LE(&alt_sec->sh_size)); } + if (extable_sec) { + fprintf(outfile, "\t.extable_base = %lu,\n", + (unsigned long)GET_LE(&extable_sec->sh_offset)); + fprintf(outfile, "\t.extable_len = %lu,\n", + (unsigned long)GET_LE(&extable_sec->sh_size)); + fprintf(outfile, "\t.extable = extable,\n"); + } + for (i = 0; i < NSYMS; i++) { if (required_syms[i].export && syms[i]) fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n", diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h index 27566e57e87d..1c8a6a8f7b59 100644 --- a/arch/x86/include/asm/vdso.h +++ b/arch/x86/include/asm/vdso.h @@ -15,6 +15,8 @@ struct vdso_image { unsigned long size; /* Always a multiple of PAGE_SIZE */ unsigned long alt, alt_len; + unsigned long extable_base, extable_len; + const void *extable; long sym_vvar_start; /* Negative offset to the vvar area */ @@ -45,6 +47,9 @@ extern void __init init_vdso_image(const struct vdso_image *image); extern int map_vdso_once(const struct vdso_image *image, unsigned long addr); +extern bool fixup_vdso_exception(struct pt_regs *regs, int trapnr, + unsigned long error_code, + unsigned long fault_addr); #endif /* __ASSEMBLER__ */ #endif /* _ASM_X86_VDSO_H */ From patchwork Wed Apr 17 10:39:30 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905181 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C57AC13B5 for ; Wed, 17 Apr 2019 10:42:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AF52C289F5 for ; Wed, 17 Apr 2019 10:42:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A321A28AD4; Wed, 17 Apr 2019 10:42:18 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4B71B289F5 for ; Wed, 17 Apr 2019 10:42:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731812AbfDQKmR (ORCPT ); Wed, 17 Apr 2019 06:42:17 -0400 Received: from mga12.intel.com ([192.55.52.136]:43413 "EHLO mga12.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731810AbfDQKmR (ORCPT ); Wed, 17 Apr 2019 06:42:17 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:17 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120203" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:10 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com Subject: [PATCH v20 20/28] x86/fault: Add helper function to sanitize error code Date: Wed, 17 Apr 2019 13:39:30 +0300 Message-Id: <20190417103938.7762-21-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson ...to prepare for vDSO exception fixup, which will expose the error code to userspace and runs before set_signal_archinfo(), i.e. suppresses the signal when fixup is successful. Signed-off-by: Sean Christopherson --- arch/x86/mm/fault.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 78e2807fbede..5babd515f616 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -719,6 +719,18 @@ pgtable_bad(struct pt_regs *regs, unsigned long error_code, oops_end(flags, regs, sig); } +static void sanitize_error_code(unsigned long address, + unsigned long *error_code) +{ + /* + * To avoid leaking information about the kernel page + * table layout, pretend that user-mode accesses to + * kernel addresses are always protection faults. + */ + if (address >= TASK_SIZE_MAX) + *error_code |= X86_PF_PROT; +} + static void set_signal_archinfo(unsigned long address, unsigned long error_code) { @@ -771,6 +783,8 @@ no_context(struct pt_regs *regs, unsigned long error_code, * faulting through the emulate_vsyscall() logic. */ if (current->thread.sig_on_uaccess_err && signal) { + sanitize_error_code(address, &error_code); + set_signal_archinfo(address, error_code); /* XXX: hwpoison faults will set the wrong code. */ @@ -920,13 +934,7 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, if (is_errata100(regs, address)) return; - /* - * To avoid leaking information about the kernel page table - * layout, pretend that user-mode accesses to kernel addresses - * are always protection faults. - */ - if (address >= TASK_SIZE_MAX) - error_code |= X86_PF_PROT; + sanitize_error_code(address, &error_code); if (likely(show_unhandled_signals)) show_signal_msg(regs, error_code, address, tsk); @@ -1045,6 +1053,8 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address, if (is_prefetch(regs, error_code, address)) return; + sanitize_error_code(address, &error_code); + set_signal_archinfo(address, error_code); #ifdef CONFIG_MEMORY_FAILURE From patchwork Wed Apr 17 10:39:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905183 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CED6113B5 for ; Wed, 17 Apr 2019 10:42:26 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B860828AD1 for ; Wed, 17 Apr 2019 10:42:26 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AC28A28AD5; Wed, 17 Apr 2019 10:42:26 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4832228AD1 for ; Wed, 17 Apr 2019 10:42:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731823AbfDQKmZ (ORCPT ); Wed, 17 Apr 2019 06:42:25 -0400 Received: from mga04.intel.com ([192.55.52.120]:54691 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731509AbfDQKmZ (ORCPT ); Wed, 17 Apr 2019 06:42:25 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:25 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120218" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:18 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Andy Lutomirski , Jarkko Sakkinen , Dave Hansen Subject: [PATCH v20 21/28] x86/fault: Attempt to fixup unhandled #PF in vDSO before signaling Date: Wed, 17 Apr 2019 13:39:31 +0300 Message-Id: <20190417103938.7762-22-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson vDSO functions can now leverage an exception fixup mechanism similar to kernel exception fixup. For vDSO exception fixup, the initial user is Intel's Software Guard Extensions (SGX), which will wrap the low-level transitions to/from the enclave, i.e. EENTER and ERESUME instructions, in a vDSO function and leverage fixup to intercept exceptions that would otherwise generate a signal. This allows the vDSO wrapper to return the fault information directly to its caller, obviating the need for SGX applications and libraries to juggle signal handlers. Attempt to fixup vDSO exceptions immediately prior to populating and sending signal information. Except for the delivery mechanism, an exception in a vDSO function should be treated like any other exception in userspace, e.g. any fault that is successfully handled by the kernel should not be directly visible to userspace. Suggested-by: Andy Lutomirski Cc: Andy Lutomirski Cc: Jarkko Sakkinen Cc: Dave Hansen Cc: Josh Triplett Signed-off-by: Sean Christopherson --- arch/x86/mm/fault.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 5babd515f616..13be0c67379e 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -28,6 +28,7 @@ #include /* vma_pkey() */ #include /* efi_recover_from_page_fault()*/ #include /* store_idt(), ... */ +#include /* fixup_vdso_exception() */ #define CREATE_TRACE_POINTS #include @@ -936,6 +937,9 @@ __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code, sanitize_error_code(address, &error_code); + if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address)) + return; + if (likely(show_unhandled_signals)) show_signal_msg(regs, error_code, address, tsk); @@ -1055,6 +1059,9 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address, sanitize_error_code(address, &error_code); + if (fixup_vdso_exception(regs, X86_TRAP_PF, error_code, address)) + return; + set_signal_archinfo(address, error_code); #ifdef CONFIG_MEMORY_FAILURE From patchwork Wed Apr 17 10:39:32 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905185 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2C9D717E0 for ; Wed, 17 Apr 2019 10:42:35 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 140BD289F5 for ; Wed, 17 Apr 2019 10:42:35 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 07F0828AD4; Wed, 17 Apr 2019 10:42:35 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 89B62289F5 for ; Wed, 17 Apr 2019 10:42:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731848AbfDQKme (ORCPT ); Wed, 17 Apr 2019 06:42:34 -0400 Received: from mga12.intel.com ([192.55.52.136]:43432 "EHLO mga12.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731836AbfDQKmd (ORCPT ); Wed, 17 Apr 2019 06:42:33 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:33 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120232" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:26 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Andy Lutomirski , Jarkko Sakkinen , Dave Hansen Subject: [PATCH v20 22/28] x86/traps: Attempt to fixup exceptions in vDSO before signaling Date: Wed, 17 Apr 2019 13:39:32 +0300 Message-Id: <20190417103938.7762-23-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson vDSO functions can now leverage an exception fixup mechanism similar to kernel exception fixup. For vDSO exception fixup, the initial user is Intel's Software Guard Extensions (SGX), which will wrap the low-level transitions to/from the enclave, i.e. EENTER and ERESUME instructions, in a vDSO function and leverage fixup to intercept exceptions that would otherwise generate a signal. This allows the vDSO wrapper to return the fault information directly to its caller, obviating the need for SGX applications and libraries to juggle signal handlers. Attempt to fixup vDSO exceptions immediately prior to populating and sending signal information. Except for the delivery mechanism, an exception in a vDSO function should be treated like any other exception in userspace, e.g. any fault that is successfully handled by the kernel should not be directly visible to userspace. Although it's debatable whether or not all exceptions are of interest to enclaves, defer to the vDSO fixup to decide whether to do fixup or generate a signal. Future users of vDSO fixup, if there ever are any, will undoubtedly have different requirements than SGX enclaves, e.g. the fixup vs. signal logic can be made function specific if/when necessary. Suggested-by: Andy Lutomirski Cc: Andy Lutomirski Cc: Jarkko Sakkinen Cc: Dave Hansen Cc: Josh Triplett Signed-off-by: Sean Christopherson --- arch/x86/kernel/traps.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index d26f9e9c3d83..02eda456c119 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -61,6 +61,7 @@ #include #include #include +#include #ifdef CONFIG_X86_64 #include @@ -210,6 +211,9 @@ do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str, tsk->thread.error_code = error_code; tsk->thread.trap_nr = trapnr; die(str, regs, error_code); + } else { + if (fixup_vdso_exception(regs, trapnr, error_code, 0)) + return 0; } /* @@ -561,6 +565,9 @@ do_general_protection(struct pt_regs *regs, long error_code) return; } + if (fixup_vdso_exception(regs, X86_TRAP_GP, error_code, 0)) + return; + tsk->thread.error_code = error_code; tsk->thread.trap_nr = X86_TRAP_GP; @@ -775,6 +782,10 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) SIGTRAP) == NOTIFY_STOP) goto exit; + if (user_mode(regs) && + fixup_vdso_exception(regs, X86_TRAP_DB, error_code, 0)) + goto exit; + /* * Let others (NMI) know that the debug stack is in use * as we may switch to the interrupt stack. @@ -855,6 +866,9 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr) if (!si_code) return; + if (fixup_vdso_exception(regs, trapnr, error_code, 0)) + return; + force_sig_fault(SIGFPE, si_code, (void __user *)uprobe_get_trap_addr(regs), task); } From patchwork Wed Apr 17 10:39:33 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905187 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 61C7A17E0 for ; Wed, 17 Apr 2019 10:42:48 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4906D289F5 for ; Wed, 17 Apr 2019 10:42:48 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3CE4C28AD4; Wed, 17 Apr 2019 10:42:48 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2D82D28AD1 for ; Wed, 17 Apr 2019 10:42:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731789AbfDQKmn (ORCPT ); Wed, 17 Apr 2019 06:42:43 -0400 Received: from mga04.intel.com ([192.55.52.120]:54702 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731509AbfDQKmn (ORCPT ); Wed, 17 Apr 2019 06:42:43 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:43 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120247" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:34 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Andy Lutomirski , Jarkko Sakkinen , Dave Hansen , Haitao Huang , Jethro Beekman , "Dr . Greg Wettstein" Subject: [PATCH v20 23/28] x86/vdso: Add __vdso_sgx_enter_enclave() to wrap SGX enclave transitions Date: Wed, 17 Apr 2019 13:39:33 +0300 Message-Id: <20190417103938.7762-24-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Intel Software Guard Extensions (SGX) introduces a new CPL3-only enclave mode that runs as a sort of black box shared object that is hosted by an untrusted normal CPL3 process. Skipping over a great deal of gory architecture details[1], SGX was designed in such a way that the host process can utilize a library to build, launch and run an enclave. This is roughly analogous to how e.g. libc implementations are used by most applications so that the application can focus on its business logic. The big gotcha is that because enclaves can generate *and* handle exceptions, any SGX library must be prepared to handle nearly any exception at any time (well, any time a thread is executing in an enclave). In Linux, this means the SGX library must register a signal handler in order to intercept relevant exceptions and forward them to the enclave (or in some cases, take action on behalf of the enclave). Unfortunately, Linux's signal mechanism doesn't mesh well with libraries, e.g. signal handlers are process wide, are difficult to chain, etc... This becomes particularly nasty when using multiple levels of libraries that register signal handlers, e.g. running an enclave via cgo inside of the Go runtime. In comes vDSO to save the day. Now that vDSO can fixup exceptions, add a function, __vdso_sgx_enter_enclave(), to wrap enclave transitions and intercept any exceptions that occur when running the enclave. __vdso_sgx_enter_enclave() does NOT adhere to the x86-64 ABI and instead uses a custom calling convention. The primary motivation is to avoid issues that arise due to asynchronous enclave exits. The x86-64 ABI requires that EFLAGS.DF, MXCSR and FCW be preserved by the callee, and unfortunately for the vDSO, the aformentioned registers/bits are not restored after an asynchronous exit, e.g. EFLAGS.DF is in an unknown state while MXCSR and FCW are reset to their init values. So the vDSO cannot simply pass the buck by requiring enclaves to adhere to the x86-64 ABI. That leaves three somewhat reasonable options: 1) Save/restore non-volatile GPRs, MXCSR and FCW, and clear EFLAGS.DF + 100% compliant with the x86-64 ABI + Callable from any code + Minimal documentation required - Restoring MXCSR/FCW is likely unnecessary 99% of the time - Slow 2) Save/restore non-volatile GPRs and clear EFLAGS.DF + Mostly compliant with the x86-64 ABI + Callable from any code that doesn't use SIMD registers - Need to document deviations from x86-64 ABI, i.e. MXCSR and FCW 3) Require the caller to save/restore everything. + Fast + Userspace can pass all GPRs to the enclave (minus EAX, RBX and RCX) - Custom ABI - For all intents and purposes must be called from an assembly wrapper __vdso_sgx_enter_enclave() implements option (3). The custom ABI is mostly a documentation issue, and even that is offset by the fact that being more similar to hardware's ENCLU[EENTER/ERESUME] ABI reduces the amount of documentation needed for the vDSO, e.g. options (2) and (3) would need to document which registers are marshalled to/from enclaves. Requiring an assembly wrapper imparts minimal pain on userspace as SGX libraries and/or applications need a healthy chunk of assembly, e.g. in the enclave, regardless of the vDSO's implementation. Note, the C-like pseudocode describing the assembly routine is wrapped in a non-existent macro instead of in a comment to trick kernel-doc into auto-parsing the documentation and function prototype. This is a double win as the pseudocode is intended to aid kernel developers, not userland enclave developers. [1] Documentation/x86/sgx/1.Architecture.rst Suggested-by: Andy Lutomirski Cc: Andy Lutomirski Cc: Jarkko Sakkinen Cc: Dave Hansen Cc: Josh Triplett Cc: Haitao Huang Cc: Jethro Beekman Cc: Dr. Greg Wettstein Signed-off-by: Sean Christopherson --- arch/x86/entry/vdso/Makefile | 2 + arch/x86/entry/vdso/vdso.lds.S | 1 + arch/x86/entry/vdso/vsgx_enter_enclave.S | 101 +++++++++++++++++++++++ arch/x86/include/uapi/asm/sgx.h | 18 ++++ 4 files changed, 122 insertions(+) create mode 100644 arch/x86/entry/vdso/vsgx_enter_enclave.S diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 34bcf87d358c..fb5b9960b192 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -18,6 +18,7 @@ VDSO32-$(CONFIG_IA32_EMULATION) := y # files to link into the vdso vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o +vobjs-$(VDSO64-y) += vsgx_enter_enclave.o # files to link into kernel obj-y += vma.o extable.o @@ -85,6 +86,7 @@ CFLAGS_REMOVE_vdso-note.o = -pg CFLAGS_REMOVE_vclock_gettime.o = -pg CFLAGS_REMOVE_vgetcpu.o = -pg CFLAGS_REMOVE_vvar.o = -pg +CFLAGS_REMOVE_vsgx_enter_enclave.o = -pg # # X32 processes use x32 vDSO to access 64bit kernel data. diff --git a/arch/x86/entry/vdso/vdso.lds.S b/arch/x86/entry/vdso/vdso.lds.S index d3a2dce4cfa9..50952a995a6c 100644 --- a/arch/x86/entry/vdso/vdso.lds.S +++ b/arch/x86/entry/vdso/vdso.lds.S @@ -25,6 +25,7 @@ VERSION { __vdso_getcpu; time; __vdso_time; + __vdso_sgx_enter_enclave; local: *; }; } diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S new file mode 100644 index 000000000000..fe0bf6671d6d --- /dev/null +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include + +#include "extable.h" + +#define EX_LEAF 0*8 +#define EX_TRAPNR 0*8+4 +#define EX_ERROR_CODE 0*8+6 +#define EX_ADDRESS 1*8 + +.code64 +.section .text, "ax" + +#ifdef SGX_KERNEL_DOC +/** + * __vdso_sgx_enter_enclave() - Enter an SGX enclave + * + * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME + * @tcs: **IN \%rbx** - TCS, must be non-NULL + * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer + * + * Return: + * **OUT \%eax** - + * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is + * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults + * + * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the + * x86-64 ABI, i.e. cannot be called from standard C code. As noted above, + * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with + * the return value passed via ``%eax``. All registers except ``%rsp`` must + * be treated as volatile from the caller's perspective, including but not + * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave + * being run **must** preserve the untrusted ``%rsp`` and stack. + */ +__vdso_sgx_enter_enclave(u32 leaf, void *tcs, + struct sgx_enclave_exception *ex_info) +{ + if (leaf != SGX_EENTER && leaf != SGX_ERESUME) + return -EINVAL; + + if (!tcs) + return -EINVAL; + + try { + ENCLU[leaf]; + } catch (exception) { + if (e) + *e = exception; + return -EFAULT; + } + + return 0; +} +#endif +ENTRY(__vdso_sgx_enter_enclave) + /* EENTER <= leaf <= ERESUME */ + cmp $0x2, %eax + jb bad_input + + cmp $0x3, %eax + ja bad_input + + /* TCS must be non-NULL */ + test %rbx, %rbx + je bad_input + + /* Save @exception_info */ + push %rcx + + /* Load AEP for ENCLU */ + lea 1f(%rip), %rcx +1: enclu + + add $0x8, %rsp + xor %eax, %eax + ret + +bad_input: + mov $(-EINVAL), %rax + ret + +.pushsection .fixup, "ax" + /* Re-load @exception_info and fill it (if it's non-NULL) */ +2: pop %rcx + test %rcx, %rcx + je 3f + + mov %eax, EX_LEAF(%rcx) + mov %di, EX_TRAPNR(%rcx) + mov %si, EX_ERROR_CODE(%rcx) + mov %rdx, EX_ADDRESS(%rcx) +3: mov $(-EFAULT), %rax + ret +.popsection + +_ASM_VDSO_EXTABLE_HANDLE(1b, 2b) + +ENDPROC(__vdso_sgx_enter_enclave) diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h index 3b80acde8671..9ed690a38c70 100644 --- a/arch/x86/include/uapi/asm/sgx.h +++ b/arch/x86/include/uapi/asm/sgx.h @@ -65,4 +65,22 @@ struct sgx_enclave_set_attribute { __u64 attribute_fd; }; +/** + * struct sgx_enclave_exception - structure to report exceptions encountered in + * __vdso_sgx_enter_enclave() + * + * @leaf: ENCLU leaf from \%eax at time of exception + * @trapnr: exception trap number, a.k.a. fault vector + * @error_code: exception error code + * @address: exception address, e.g. CR2 on a #PF + * @reserved: reserved for future use + */ +struct sgx_enclave_exception { + __u32 leaf; + __u16 trapnr; + __u16 error_code; + __u64 address; + __u64 reserved[2]; +}; + #endif /* _UAPI_ASM_X86_SGX_H */ From patchwork Wed Apr 17 10:39:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905189 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 287D713B5 for ; Wed, 17 Apr 2019 10:42:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0FAB8289F5 for ; Wed, 17 Apr 2019 10:42:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 01BF228AD4; Wed, 17 Apr 2019 10:42:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0A420289F5 for ; Wed, 17 Apr 2019 10:42:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731576AbfDQKmw (ORCPT ); Wed, 17 Apr 2019 06:42:52 -0400 Received: from mga06.intel.com ([134.134.136.31]:40901 "EHLO mga06.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731509AbfDQKmw (ORCPT ); Wed, 17 Apr 2019 06:42:52 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:51 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120262" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:44 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 24/28] selftests/x86: Add a selftest for SGX Date: Wed, 17 Apr 2019 13:39:34 +0300 Message-Id: <20190417103938.7762-25-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a selftest for SGX. It is a trivial test where a simple enclave copies one 64-bit word of memory between two memory locations given to the enclave as arguments. Signed-off-by: Jarkko Sakkinen --- tools/testing/selftests/x86/Makefile | 10 + tools/testing/selftests/x86/sgx/Makefile | 48 ++ tools/testing/selftests/x86/sgx/defines.h | 39 ++ tools/testing/selftests/x86/sgx/encl.c | 20 + tools/testing/selftests/x86/sgx/encl.lds | 33 ++ .../selftests/x86/sgx/encl_bootstrap.S | 94 ++++ tools/testing/selftests/x86/sgx/encl_piggy.S | 18 + tools/testing/selftests/x86/sgx/encl_piggy.h | 14 + tools/testing/selftests/x86/sgx/main.c | 279 ++++++++++ tools/testing/selftests/x86/sgx/sgx_call.S | 15 + tools/testing/selftests/x86/sgx/sgxsign.c | 508 ++++++++++++++++++ .../testing/selftests/x86/sgx/signing_key.pem | 39 ++ 12 files changed, 1117 insertions(+) create mode 100644 tools/testing/selftests/x86/sgx/Makefile create mode 100644 tools/testing/selftests/x86/sgx/defines.h create mode 100644 tools/testing/selftests/x86/sgx/encl.c create mode 100644 tools/testing/selftests/x86/sgx/encl.lds create mode 100644 tools/testing/selftests/x86/sgx/encl_bootstrap.S create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.S create mode 100644 tools/testing/selftests/x86/sgx/encl_piggy.h create mode 100644 tools/testing/selftests/x86/sgx/main.c create mode 100644 tools/testing/selftests/x86/sgx/sgx_call.S create mode 100644 tools/testing/selftests/x86/sgx/sgxsign.c create mode 100644 tools/testing/selftests/x86/sgx/signing_key.pem diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 186520198de7..4fc9a42f56ea 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -1,4 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 + +SUBDIRS_64 := sgx + all: include ../lib.mk @@ -67,6 +70,13 @@ all_32: $(BINARIES_32) all_64: $(BINARIES_64) +all_64: $(SUBDIRS_64) + @for DIR in $(SUBDIRS_64); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@; \ + done + EXTRA_CLEAN := $(BINARIES_32) $(BINARIES_64) $(BINARIES_32): $(OUTPUT)/%_32: %.c diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile new file mode 100644 index 000000000000..1fd6f2708e81 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/Makefile @@ -0,0 +1,48 @@ +top_srcdir = ../../../../.. + +include ../../lib.mk + +HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC +ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \ + -fno-stack-protector -mrdrnd $(INCLUDES) + +TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx +all_64: $(TEST_CUSTOM_PROGS) + +$(TEST_CUSTOM_PROGS): $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o \ + $(OUTPUT)/encl_piggy.o + $(CC) $(HOST_CFLAGS) -o $@ $^ + +$(OUTPUT)/main.o: main.c + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/sgx_call.o: sgx_call.S + $(CC) $(HOST_CFLAGS) -c $< -o $@ + +$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss + $(CC) $(HOST_CFLAGS) -c encl_piggy.S -o $@ + +$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign + objcopy --remove-section=.got.plt -O binary $< $@ + +$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o + $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^ + +$(OUTPUT)/encl.o: encl.c + $(CC) $(ENCL_CFLAGS) -c $< -o $@ + +$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S + $(CC) $(ENCL_CFLAGS) -c $< -o $@ + +$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin $(OUTPUT)/sgxsign + $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss + +$(OUTPUT)/sgxsign: sgxsign.c + $(CC) -o $@ $< -lcrypto + +EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \ + $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \ + $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \ + $(OUTPUT)/sgxsign + +.PHONY: clean diff --git a/tools/testing/selftests/x86/sgx/defines.h b/tools/testing/selftests/x86/sgx/defines.h new file mode 100644 index 000000000000..3ff73a9d9b93 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/defines.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2016-19 Intel Corporation. + */ + +#ifndef TYPES_H +#define TYPES_H + +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#define __aligned(x) __attribute__((__aligned__(x))) +#define __packed __attribute__((packed)) + +/* Derived from asm-generic/bitsperlong.h. */ +#if __x86_64__ +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif +#define BITS_PER_LONG_LONG 64 + +/* Taken from linux/bits.h. */ +#define BIT(nr) (1UL << (nr)) +#define BIT_ULL(nr) (1ULL << (nr)) +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#define GENMASK_ULL(h, l) \ + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h" +#include "../../../../../arch/x86/include/uapi/asm/sgx.h" + +#endif /* TYPES_H */ diff --git a/tools/testing/selftests/x86/sgx/encl.c b/tools/testing/selftests/x86/sgx/encl.c new file mode 100644 index 000000000000..ede915399742 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/encl.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-18 Intel Corporation. + +#include +#include "defines.h" + +static void *memcpy(void *dest, const void *src, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + ((char *)dest)[i] = ((char *)src)[i]; + + return dest; +} + +void encl_body(void *rdi, void *rsi) +{ + memcpy(rsi, rdi, 8); +} diff --git a/tools/testing/selftests/x86/sgx/encl.lds b/tools/testing/selftests/x86/sgx/encl.lds new file mode 100644 index 000000000000..2ee01ac3ec79 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/encl.lds @@ -0,0 +1,33 @@ +OUTPUT_FORMAT(elf64-x86-64) + +SECTIONS +{ + . = 0; + .tcs : { + *(.tcs*) + } + + . = ALIGN(4096); + .text : { + *(.text*) + *(.rodata*) + } + + . = ALIGN(4096); + .data : { + *(.data*) + } + + /DISCARD/ : { + *(.data*) + *(.comment*) + *(.note*) + *(.debug*) + *(.eh_frame*) + } +} + +ASSERT(!DEFINED(.altinstructions), "ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.altinstr_replacement), "ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.discard.retpoline_safe), "RETPOLINE ALTERNATIVES are not supported in enclaves") +ASSERT(!DEFINED(.discard.nospec), "RETPOLINE ALTERNATIVES are not supported in enclaves") diff --git a/tools/testing/selftests/x86/sgx/encl_bootstrap.S b/tools/testing/selftests/x86/sgx/encl_bootstrap.S new file mode 100644 index 000000000000..d07f970ccdf9 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/encl_bootstrap.S @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2016-18 Intel Corporation. + */ + + .macro ENCLU + .byte 0x0f, 0x01, 0xd7 + .endm + + .section ".tcs", "a" + .balign 4096 + + .fill 1, 8, 0 # STATE (set by CPU) + .fill 1, 8, 0 # FLAGS + .quad encl_ssa # OSSA + .fill 1, 4, 0 # CSSA (set by CPU) + .fill 1, 4, 1 # NSSA + .quad encl_entry # OENTRY + .fill 1, 8, 0 # AEP (set by EENTER and ERESUME) + .fill 1, 8, 0 # OFSBASE + .fill 1, 8, 0 # OGSBASE + .fill 1, 4, 0xFFFFFFFF # FSLIMIT + .fill 1, 4, 0xFFFFFFFF # GSLIMIT + .fill 4024, 1, 0 # Reserved + + .text + +encl_entry: + # RBX contains the base address for TCS, which is also the first address + # inside the enclave. By adding the value of le_stack_end to it, we get + # the absolute address for the stack. + lea (encl_stack)(%rbx), %rax + xchg %rsp, %rax + push %rax + + push %rcx # push the address after EENTER + push %rbx # push the enclave base address + + call encl_body + + pop %rbx # pop the enclave base address + + # Restore XSAVE registers to a synthetic state. + mov $0xFFFFFFFF, %rax + mov $0xFFFFFFFF, %rdx + lea (xsave_area)(%rbx), %rdi + fxrstor (%rdi) + + # Clear GPRs. + xor %rcx, %rcx + xor %rdx, %rdx + xor %rdi, %rdi + xor %rsi, %rsi + xor %r8, %r8 + xor %r9, %r9 + xor %r10, %r10 + xor %r11, %r11 + xor %r12, %r12 + xor %r13, %r13 + xor %r14, %r14 + xor %r15, %r15 + + # Reset status flags. + add %rdx, %rdx # OF = SF = AF = CF = 0; ZF = PF = 1 + + # Prepare EEXIT target by popping the address of the instruction after + # EENTER to RBX. + pop %rbx + + # Restore the caller stack. + pop %rax + mov %rax, %rsp + + # EEXIT + mov $4, %rax + enclu + + .section ".data", "aw" + +encl_ssa: + .space 4096 + +xsave_area: + .fill 1, 4, 0x037F # FCW + .fill 5, 4, 0 + .fill 1, 4, 0x1F80 # MXCSR + .fill 1, 4, 0xFFFF # MXCSR_MASK + .fill 123, 4, 0 + .fill 1, 4, 0x80000000 # XCOMP_BV[63] = 1, compaction mode + .fill 12, 4, 0 + + .balign 4096 + .space 8192 +encl_stack: diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.S b/tools/testing/selftests/x86/sgx/encl_piggy.S new file mode 100644 index 000000000000..542001658afb --- /dev/null +++ b/tools/testing/selftests/x86/sgx/encl_piggy.S @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2016-18 Intel Corporation. + */ + + .section ".rodata", "a" + +encl_bin: + .globl encl_bin + .incbin "encl.bin" +encl_bin_end: + .globl encl_bin_end + +encl_ss: + .globl encl_ss + .incbin "encl.ss" +encl_ss_end: + .globl encl_ss_end diff --git a/tools/testing/selftests/x86/sgx/encl_piggy.h b/tools/testing/selftests/x86/sgx/encl_piggy.h new file mode 100644 index 000000000000..ee8224f8cc8d --- /dev/null +++ b/tools/testing/selftests/x86/sgx/encl_piggy.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2016-18 Intel Corporation. + */ + +#ifndef ENCL_PIGGY_H +#define ENCL_PIGGY_H + +extern unsigned char encl_bin[]; +extern unsigned char encl_bin_end[]; +extern unsigned char encl_ss[]; +extern unsigned char encl_ss_end[]; + +#endif /* ENCL_PIGGY_H */ diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c new file mode 100644 index 000000000000..e2265f841fb0 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/main.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-18 Intel Corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "encl_piggy.h" +#include "defines.h" +#include "../../../../../arch/x86/kernel/cpu/sgx/arch.h" +#include "../../../../../arch/x86/include/uapi/asm/sgx.h" + +static const uint64_t MAGIC = 0x1122334455667788ULL; + +struct vdso_symtab { + Elf64_Sym *elf_symtab; + const char *elf_symstrtab; + Elf64_Word *elf_hashtab; +}; + +static void *vdso_get_base_addr(char *envp[]) +{ + Elf64_auxv_t *auxv; + int i; + + for (i = 0; envp[i]; i++); + auxv = (Elf64_auxv_t *)&envp[i + 1]; + + for (i = 0; auxv[i].a_type != AT_NULL; i++) { + if (auxv[i].a_type == AT_SYSINFO_EHDR) + return (void *)auxv[i].a_un.a_val; + } + + return NULL; +} + +static Elf64_Dyn *vdso_get_dyntab(void *addr) +{ + Elf64_Ehdr *ehdr = addr; + Elf64_Phdr *phdrtab = addr + ehdr->e_phoff; + int i; + + for (i = 0; i < ehdr->e_phnum; i++) + if (phdrtab[i].p_type == PT_DYNAMIC) + return addr + phdrtab[i].p_offset; + + return NULL; +} + +static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) +{ + int i; + + for (i = 0; dyntab[i].d_tag != DT_NULL; i++) + if (dyntab[i].d_tag == tag) + return addr + dyntab[i].d_un.d_ptr; + + return NULL; +} + +static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) +{ + Elf64_Dyn *dyntab = vdso_get_dyntab(addr); + + symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); + if (!symtab->elf_symtab) + return false; + + symtab->elf_symstrtab = vdso_get_dyn(addr, dyntab, DT_STRTAB); + if (!symtab->elf_symstrtab) + return false; + + symtab->elf_hashtab = vdso_get_dyn(addr, dyntab, DT_HASH); + if (!symtab->elf_hashtab) + return false; + + return true; +} + +static unsigned long elf_sym_hash(const char *name) +{ + unsigned long h = 0, high; + + while (*name) { + h = (h << 4) + *name++; + high = h & 0xf0000000; + + if (high) + h ^= high >> 24; + + h &= ~high; + } + + return h; +} + +static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name) +{ + Elf64_Word bucketnum = symtab->elf_hashtab[0]; + Elf64_Word *buckettab = &symtab->elf_hashtab[2]; + Elf64_Word *chaintab = &symtab->elf_hashtab[2 + bucketnum]; + Elf64_Sym *sym; + Elf64_Word i; + + for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF; + i = chaintab[i]) { + sym = &symtab->elf_symtab[i]; + if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name])) + return sym; + } + + return NULL; +} + +static bool encl_create(int dev_fd, unsigned long bin_size, + struct sgx_secs *secs) +{ + struct sgx_enclave_create ioc; + void *base; + int rc; + + memset(secs, 0, sizeof(*secs)); + secs->ssa_frame_size = 1; + secs->attributes = SGX_ATTR_MODE64BIT; + secs->xfrm = 3; + + for (secs->size = 4096; secs->size < bin_size; ) + secs->size <<= 1; + + base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_SHARED, dev_fd, 0); + if (base == MAP_FAILED) { + perror("mmap"); + return false; + } + + secs->base = (uint64_t)base; + + ioc.src = (unsigned long)secs; + rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_CREATE, &ioc); + if (rc) { + fprintf(stderr, "ECREATE failed rc=%d.\n", rc); + munmap(base, secs->size); + return false; + } + + return true; +} + +static bool encl_add_page(int dev_fd, unsigned long addr, void *data, + uint64_t flags) +{ + struct sgx_enclave_add_page ioc; + struct sgx_secinfo secinfo; + int rc; + + memset(&secinfo, 0, sizeof(secinfo)); + secinfo.flags = flags; + + ioc.secinfo = (unsigned long)&secinfo; + ioc.mrmask = 0xFFFF; + ioc.addr = addr; + ioc.src = (uint64_t)data; + + rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_ADD_PAGE, &ioc); + if (rc) { + fprintf(stderr, "EADD failed rc=%d.\n", rc); + return false; + } + + return true; +} + +static bool encl_load(struct sgx_secs *secs, unsigned long bin_size) +{ + struct sgx_enclave_init ioc; + uint64_t offset; + uint64_t flags; + int dev_fd; + int rc; + + dev_fd = open("/dev/sgx/enclave", O_RDWR); + if (dev_fd < 0) { + fprintf(stderr, "Unable to open /dev/sgx\n"); + return false; + } + + if (!encl_create(dev_fd, bin_size, secs)) + goto out_dev_fd; + + for (offset = 0; offset < bin_size; offset += 0x1000) { + if (!offset) + flags = SGX_SECINFO_TCS; + else + flags = SGX_SECINFO_REG | SGX_SECINFO_R | + SGX_SECINFO_W | SGX_SECINFO_X; + + if (!encl_add_page(dev_fd, secs->base + offset, + encl_bin + offset, flags)) + goto out_map; + } + + ioc.sigstruct = (uint64_t)&encl_ss; + rc = ioctl(dev_fd, SGX_IOC_ENCLAVE_INIT, &ioc); + if (rc) { + printf("EINIT failed rc=%d\n", rc); + goto out_map; + } + + close(dev_fd); + return true; +out_map: + munmap((void *)secs->base, secs->size); +out_dev_fd: + close(dev_fd); + return false; +} + +void sgx_call(void *rdi, void *rsi, void *tcs, + struct sgx_enclave_exception *exception, + void *eenter); + +int main(int argc, char *argv[], char *envp[]) +{ + unsigned long bin_size = encl_bin_end - encl_bin; + unsigned long ss_size = encl_ss_end - encl_ss; + struct sgx_enclave_exception exception; + Elf64_Sym *eenter_sym; + struct vdso_symtab symtab; + struct sgx_secs secs; + uint64_t result = 0; + void *eenter; + void *addr; + + memset(&exception, 0, sizeof(exception)); + + addr = vdso_get_base_addr(envp); + if (!addr) + exit(1); + + if (!vdso_get_symtab(addr, &symtab)) + exit(1); + + eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave"); + if (!eenter_sym) + exit(1); + eenter = addr + eenter_sym->st_value; + + printf("Binary size %lu (0x%lx), SIGSTRUCT size %lu\n", bin_size, + bin_size, ss_size); + if (ss_size != sizeof(struct sgx_sigstruct)) { + fprintf(stderr, "The size of SIGSTRUCT should be %lu\n", + sizeof(struct sgx_sigstruct)); + exit(1); + } + + printf("Loading the enclave.\n"); + if (!encl_load(&secs, bin_size)) + exit(1); + + printf("Input: 0x%lx\n", MAGIC); + sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception, + eenter); + if (result != MAGIC) { + fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); + exit(1); + } + + printf("Output: 0x%lx\n", result); + exit(0); +} diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S new file mode 100644 index 000000000000..14bd0a044199 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/sgx_call.S @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/** +* Copyright(c) 2016-18 Intel Corporation. +*/ + + .text + + .global sgx_call +sgx_call: + push %rbx + mov $0x02, %rax + mov %rdx, %rbx + call *%r8 + pop %rbx + ret diff --git a/tools/testing/selftests/x86/sgx/sgxsign.c b/tools/testing/selftests/x86/sgx/sgxsign.c new file mode 100644 index 000000000000..0b89823fc703 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/sgxsign.c @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// Copyright(c) 2016-18 Intel Corporation. + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "defines.h" + +struct sgx_sigstruct_payload { + struct sgx_sigstruct_header header; + struct sgx_sigstruct_body body; +}; + +static const char *sign_key_pass; + +static bool check_crypto_errors(void) +{ + int err; + bool had_errors = false; + const char *filename; + int line; + char str[256]; + + for ( ; ; ) { + if (ERR_peek_error() == 0) + break; + + had_errors = true; + err = ERR_get_error_line(&filename, &line); + ERR_error_string_n(err, str, sizeof(str)); + fprintf(stderr, "crypto: %s: %s:%d\n", str, filename, line); + } + + return had_errors; +} + +static void exit_usage(const char *program) +{ + fprintf(stderr, + "Usage: %s/sign-le \n", program); + exit(1); +} + +static int pem_passwd_cb(char *buf, int size, int rwflag, void *u) +{ + if (!sign_key_pass) + return -1; + + strncpy(buf, sign_key_pass, size); + /* no retry */ + sign_key_pass = NULL; + + return strlen(buf) >= size ? size - 1 : strlen(buf); +} + +static inline const BIGNUM *get_modulus(RSA *key) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + return key->n; +#else + const BIGNUM *n; + + RSA_get0_key(key, &n, NULL, NULL); + return n; +#endif +} + +static RSA *load_sign_key(const char *path) +{ + FILE *f; + RSA *key; + + f = fopen(path, "rb"); + if (!f) { + fprintf(stderr, "Unable to open %s\n", path); + return NULL; + } + key = RSA_new(); + if (!PEM_read_RSAPrivateKey(f, &key, pem_passwd_cb, NULL)) + return NULL; + fclose(f); + + if (BN_num_bytes(get_modulus(key)) != SGX_MODULUS_SIZE) { + fprintf(stderr, "Invalid key size %d\n", + BN_num_bytes(get_modulus(key))); + RSA_free(key); + return NULL; + } + + return key; +} + +static void reverse_bytes(void *data, int length) +{ + int i = 0; + int j = length - 1; + uint8_t temp; + uint8_t *ptr = data; + + while (i < j) { + temp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = temp; + i++; + j--; + } +} + +enum mrtags { + MRECREATE = 0x0045544145524345, + MREADD = 0x0000000044444145, + MREEXTEND = 0x00444E4554584545, +}; + +static bool mrenclave_update(EVP_MD_CTX *ctx, const void *data) +{ + if (!EVP_DigestUpdate(ctx, data, 64)) { + fprintf(stderr, "digest update failed\n"); + return false; + } + + return true; +} + +static bool mrenclave_commit(EVP_MD_CTX *ctx, uint8_t *mrenclave) +{ + unsigned int size; + + if (!EVP_DigestFinal_ex(ctx, (unsigned char *)mrenclave, &size)) { + fprintf(stderr, "digest commit failed\n"); + return false; + } + + if (size != 32) { + fprintf(stderr, "invalid digest size = %u\n", size); + return false; + } + + return true; +} + +struct mrecreate { + uint64_t tag; + uint32_t ssaframesize; + uint64_t size; + uint8_t reserved[44]; +} __attribute__((__packed__)); + + +static bool mrenclave_ecreate(EVP_MD_CTX *ctx, uint64_t blob_size) +{ + struct mrecreate mrecreate; + uint64_t encl_size; + + for (encl_size = 0x1000; encl_size < blob_size; ) + encl_size <<= 1; + + memset(&mrecreate, 0, sizeof(mrecreate)); + mrecreate.tag = MRECREATE; + mrecreate.ssaframesize = 1; + mrecreate.size = encl_size; + + if (!EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)) + return false; + + return mrenclave_update(ctx, &mrecreate); +} + +struct mreadd { + uint64_t tag; + uint64_t offset; + uint64_t flags; /* SECINFO flags */ + uint8_t reserved[40]; +} __attribute__((__packed__)); + +static bool mrenclave_eadd(EVP_MD_CTX *ctx, uint64_t offset, uint64_t flags) +{ + struct mreadd mreadd; + + memset(&mreadd, 0, sizeof(mreadd)); + mreadd.tag = MREADD; + mreadd.offset = offset; + mreadd.flags = flags; + + return mrenclave_update(ctx, &mreadd); +} + +struct mreextend { + uint64_t tag; + uint64_t offset; + uint8_t reserved[48]; +} __attribute__((__packed__)); + +static bool mrenclave_eextend(EVP_MD_CTX *ctx, uint64_t offset, uint8_t *data) +{ + struct mreextend mreextend; + int i; + + for (i = 0; i < 0x1000; i += 0x100) { + memset(&mreextend, 0, sizeof(mreextend)); + mreextend.tag = MREEXTEND; + mreextend.offset = offset + i; + + if (!mrenclave_update(ctx, &mreextend)) + return false; + + if (!mrenclave_update(ctx, &data[i + 0x00])) + return false; + + if (!mrenclave_update(ctx, &data[i + 0x40])) + return false; + + if (!mrenclave_update(ctx, &data[i + 0x80])) + return false; + + if (!mrenclave_update(ctx, &data[i + 0xC0])) + return false; + } + + return true; +} + +/** + * measure_encl - measure enclave + * @path: path to the enclave + * @mrenclave: measurement + * + * Calculates MRENCLAVE. Assumes that the very first page is a TCS page and + * following pages are regular pages. Does not measure the contents of the + * enclave as the signing tool is used at the moment only for the launch + * enclave, which is pass-through (everything gets a token). + */ +static bool measure_encl(const char *path, uint8_t *mrenclave) +{ + FILE *file; + struct stat sb; + EVP_MD_CTX *ctx; + uint64_t flags; + uint64_t offset; + uint8_t data[0x1000]; + int rc; + + ctx = EVP_MD_CTX_create(); + if (!ctx) + return false; + + file = fopen(path, "rb"); + if (!file) { + perror("fopen"); + EVP_MD_CTX_destroy(ctx); + return false; + } + + rc = stat(path, &sb); + if (rc) { + perror("stat"); + goto out; + } + + if (!sb.st_size || sb.st_size & 0xfff) { + fprintf(stderr, "Invalid blob size %lu\n", sb.st_size); + goto out; + } + + if (!mrenclave_ecreate(ctx, sb.st_size)) + goto out; + + for (offset = 0; offset < sb.st_size; offset += 0x1000) { + if (!offset) + flags = SGX_SECINFO_TCS; + else + flags = SGX_SECINFO_REG | SGX_SECINFO_R | + SGX_SECINFO_W | SGX_SECINFO_X; + + if (!mrenclave_eadd(ctx, offset, flags)) + goto out; + + rc = fread(data, 1, 0x1000, file); + if (!rc) + break; + if (rc < 0x1000) + goto out; + + if (!mrenclave_eextend(ctx, offset, data)) + goto out; + } + + if (!mrenclave_commit(ctx, mrenclave)) + goto out; + + fclose(file); + EVP_MD_CTX_destroy(ctx); + return true; +out: + fclose(file); + EVP_MD_CTX_destroy(ctx); + return false; +} + +/** + * sign_encl - sign enclave + * @sigstruct: pointer to SIGSTRUCT + * @key: 3072-bit RSA key + * @signature: byte array for the signature + * + * Calculates EMSA-PKCSv1.5 signature for the given SIGSTRUCT. The result is + * stored in big-endian format so that it can be further passed to OpenSSL + * libcrypto functions. + */ +static bool sign_encl(const struct sgx_sigstruct *sigstruct, RSA *key, + uint8_t *signature) +{ + struct sgx_sigstruct_payload payload; + unsigned int siglen; + uint8_t digest[SHA256_DIGEST_LENGTH]; + bool ret; + + memcpy(&payload.header, &sigstruct->header, sizeof(sigstruct->header)); + memcpy(&payload.body, &sigstruct->body, sizeof(sigstruct->body)); + + SHA256((unsigned char *)&payload, sizeof(payload), digest); + + ret = RSA_sign(NID_sha256, digest, SHA256_DIGEST_LENGTH, signature, + &siglen, key); + + return ret; +} + +struct q1q2_ctx { + BN_CTX *bn_ctx; + BIGNUM *m; + BIGNUM *s; + BIGNUM *q1; + BIGNUM *qr; + BIGNUM *q2; +}; + +static void free_q1q2_ctx(struct q1q2_ctx *ctx) +{ + BN_CTX_free(ctx->bn_ctx); + BN_free(ctx->m); + BN_free(ctx->s); + BN_free(ctx->q1); + BN_free(ctx->qr); + BN_free(ctx->q2); +} + +static bool alloc_q1q2_ctx(const uint8_t *s, const uint8_t *m, + struct q1q2_ctx *ctx) +{ + ctx->bn_ctx = BN_CTX_new(); + ctx->s = BN_bin2bn(s, SGX_MODULUS_SIZE, NULL); + ctx->m = BN_bin2bn(m, SGX_MODULUS_SIZE, NULL); + ctx->q1 = BN_new(); + ctx->qr = BN_new(); + ctx->q2 = BN_new(); + + if (!ctx->bn_ctx || !ctx->s || !ctx->m || !ctx->q1 || !ctx->qr || + !ctx->q2) { + free_q1q2_ctx(ctx); + return false; + } + + return true; +} + +static bool calc_q1q2(const uint8_t *s, const uint8_t *m, uint8_t *q1, + uint8_t *q2) +{ + struct q1q2_ctx ctx; + + if (!alloc_q1q2_ctx(s, m, &ctx)) { + fprintf(stderr, "Not enough memory for Q1Q2 calculation\n"); + return false; + } + + if (!BN_mul(ctx.q1, ctx.s, ctx.s, ctx.bn_ctx)) + goto out; + + if (!BN_div(ctx.q1, ctx.qr, ctx.q1, ctx.m, ctx.bn_ctx)) + goto out; + + if (BN_num_bytes(ctx.q1) > SGX_MODULUS_SIZE) { + fprintf(stderr, "Too large Q1 %d bytes\n", + BN_num_bytes(ctx.q1)); + goto out; + } + + if (!BN_mul(ctx.q2, ctx.s, ctx.qr, ctx.bn_ctx)) + goto out; + + if (!BN_div(ctx.q2, NULL, ctx.q2, ctx.m, ctx.bn_ctx)) + goto out; + + if (BN_num_bytes(ctx.q2) > SGX_MODULUS_SIZE) { + fprintf(stderr, "Too large Q2 %d bytes\n", + BN_num_bytes(ctx.q2)); + goto out; + } + + BN_bn2bin(ctx.q1, q1); + BN_bn2bin(ctx.q2, q2); + + free_q1q2_ctx(&ctx); + return true; +out: + free_q1q2_ctx(&ctx); + return false; +} + +static bool save_sigstruct(const struct sgx_sigstruct *sigstruct, + const char *path) +{ + FILE *f = fopen(path, "wb"); + + if (!f) { + fprintf(stderr, "Unable to open %s\n", path); + return false; + } + + fwrite(sigstruct, sizeof(*sigstruct), 1, f); + fclose(f); + return true; +} + +int main(int argc, char **argv) +{ + uint64_t header1[2] = {0x000000E100000006, 0x0000000000010000}; + uint64_t header2[2] = {0x0000006000000101, 0x0000000100000060}; + struct sgx_sigstruct ss; + const char *program; + int opt; + RSA *sign_key; + + memset(&ss, 0, sizeof(ss)); + ss.header.header1[0] = header1[0]; + ss.header.header1[1] = header1[1]; + ss.header.header2[0] = header2[0]; + ss.header.header2[1] = header2[1]; + ss.exponent = 3; + +#ifndef CONFIG_EINITTOKENKEY + ss.body.attributes = SGX_ATTR_MODE64BIT; +#else + ss.body.attributes = SGX_ATTR_MODE64BIT | SGX_ATTR_EINITTOKENKEY; +#endif + ss.body.xfrm = 3, + + sign_key_pass = getenv("KBUILD_SGX_SIGN_PIN"); + program = argv[0]; + + do { + opt = getopt(argc, argv, ""); + switch (opt) { + case -1: + break; + default: + exit_usage(program); + } + } while (opt != -1); + + argc -= optind; + argv += optind; + + if (argc < 3) + exit_usage(program); + + /* sanity check only */ + if (check_crypto_errors()) + exit(1); + + sign_key = load_sign_key(argv[0]); + if (!sign_key) + goto out; + + BN_bn2bin(get_modulus(sign_key), ss.modulus); + + if (!measure_encl(argv[1], ss.body.mrenclave)) + goto out; + + if (!sign_encl(&ss, sign_key, ss.signature)) + goto out; + + if (!calc_q1q2(ss.signature, ss.modulus, ss.q1, ss.q2)) + goto out; + + /* convert to little endian */ + reverse_bytes(ss.signature, SGX_MODULUS_SIZE); + reverse_bytes(ss.modulus, SGX_MODULUS_SIZE); + reverse_bytes(ss.q1, SGX_MODULUS_SIZE); + reverse_bytes(ss.q2, SGX_MODULUS_SIZE); + + if (!save_sigstruct(&ss, argv[2])) + goto out; + exit(0); +out: + check_crypto_errors(); + exit(1); +} diff --git a/tools/testing/selftests/x86/sgx/signing_key.pem b/tools/testing/selftests/x86/sgx/signing_key.pem new file mode 100644 index 000000000000..d76f21f19187 --- /dev/null +++ b/tools/testing/selftests/x86/sgx/signing_key.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4wIBAAKCAYEApalGbq7Q+usM91CPtksu3D+b0Prc8gAFL6grM3mg85A5Bx8V +cfMXPgtrw8EYFwQxDAvzZWwl+9VfOX0ECrFRBkOHcOiG0SnADN8+FLj1UiNUQwbp +S6OzhNWuRcSbGraSOyUlVlV0yMQSvewyzGklOaXBe30AJqzIBc8QfdSxKuP8rs0Z +ga6k/Bl73osrYKByILJTUUeZqjLERsE6GebsdzbWgKn8qVqng4ZS4yMNg6LeRlH3 ++9CIPgg4jwpSLHcp7dq2qTIB9a0tGe9ayp+5FbucpB6U7ePold0EeRN6RlJGDF9k +L93v8P5ykz5G5gYZ2g0K1X2sHIWV4huxPgv5PXgdyQYbK+6olqj0d5rjYuwX57Ul +k6SroPS1U6UbdCjG5txM+BNGU0VpD0ZhrIRw0leQdnNcCO9sTJuInZrgYacSVJ7u +mtB+uCt+uzUesc+l+xPRYA+9e14lLkZp7AAmo9FvL816XDI09deehJ3i/LmHKCRN +tuqC5TprRjFwUr6dAgEDAoIBgG5w2Z8fNfycs0+LCnmHdJLVEotR6KFVWMpwHMz7 +wKJgJgS/Y6FMuilc8oKAuroCy11dTO5IGVKOP3uorVx2NgQtBPXwWeDGgAiU1A3Q +o4wXjYIEm4fCd63jyYPYZ2ckYXzDbjmOTdstYdPyzIhGGNEZK6eoqsRzMAPfYFPj +IMdCqHSIu6vJw1K7p+myHOsVoWshjODaZnF3LYSA0WaZ8vokjwBxUxuRxQJZjJds +s60XPtmL+qfgWtQFewoG4XL6GuD8FcXccynRRtzrLtFNPIl9BQfWfjBBhTC1/Te1 +0Z6XbZvpdUTD9OfLB7SbR2OUFNpKQgriO0iYVdbW3cr7uu38Zwp4W1TX73DPjoi6 +KNooP6SGWd4mRJW2+dUmSYS4QNG8eVVZswKcploEIXlAKRsOe4kzJJ1iETugIe85 +uX8nd1WYEp65xwoRUg8hqng0MeyveVbXqNKuJG6tzNDt9kgFYo+hmC/oouAW2Dtc +T9jdRAwKJXqA2Eg6OkgXCEv+kwKBwQDYaQiFMlFhsmLlqI+EzCUh7c941/cL7m6U +7j98+8ngl0HgCEcrc10iJVCKakQW3YbPzAx3XkKTaGjWazvvrFarXIGlOud64B8a +iWyQ7VdlnmZnNEdk+C83tI91OQeaTKqRLDGzKh29Ry/jL8Pcbazt+kDgxa0H7qJp +roADUanLQuNkYubpbhFBh3xpa2EExaVq6rF7nIVsD8W9TrbmPKA4LgH7z0iy544D +kVCNYsTjYDdUWP+WiSor8kCnnpjnN9sCgcEAw/eNezUD1UDf6OYFC9+5JZJFn4Tg +mZMyN93JKIb199ffwnjtHUSjcyiWeesXucpzwtGbTcwQnDisSW4oneYKLSEBlBaq +scqiUugyGZZOthFSCbdXYXMViK2vHrKlkse7GxVlROKcEhM/pRBrmjaGO8eWR+D4 +FO2wCXzVs3KgV6j779frw0vC54oHOxc9+Lu1rSHp4i+600koyvL/zF6U/5tZXIvN +YW2yoiQJnjCmVA1pwbwV6KAUTPDTMnBK+YjnAoHBAJBGBa4hi5Z27JkbCliIGMFJ +NPs6pLKe9GNJf6in2+sPgUAFhMeiPhbDiwbxgrnpBIqICE+ULGJFmzmc0p/IOceT +ARjR76dAFLxbnbXzj5kURETNhO36yiUjCk4mBRGIcbYddndxaSjaH+zKgpLzyJ6m +1esuc1qfFvEfAAI2cTIsl5hB70ZJYNZaUvDyQK3ZGPHxy6e9rkgKg9OJz0QoatAe +q/002yHvtAJg4F5B2JeVejg7VQ8GHB1MKxppu0TP5wKBwQCCpQj8zgKOKz/wmViy +lSYZDC5qWJW7t3bP6TDFr06lOpUsUJ4TgxeiGw778g/RMaKB4RIz3WBoJcgw9BsT +7rFza1ZiucchMcGMmswRDt8kC4wGejpA92Owc8oUdxkMhSdnY5jYlxK2t3/DYEe8 +JFl9L7mFQKVjSSAGUzkiTGrlG1Kf5UfXh9dFBq98uilQfSPIwUaWynyM23CHTKqI +Pw3/vOY9sojrnncWwrEUIG7is5vWfWPwargzSzd29YdRBe8CgcEAuRVewK/YeNOX +B7ZG6gKKsfsvrGtY7FPETzLZAHjoVXYNea4LVZ2kn4hBXXlvw/4HD+YqcTt4wmif +5JQlDvjNobUiKJZpzy7hklVhF7wZFl4pCF7Yh43q9iQ7gKTaeUG7MiaK+G8Zz8aY +HW9rsiihbdZkccMvnPfO9334XMxl3HtBRzLstjUlbLB7Sdh+7tZ3JQidCOFNs5pE +XyWwnASPu4tKfDahH1UUTp1uJcq/6716CSWg080avYxFcn75qqsb +-----END RSA PRIVATE KEY----- From patchwork Wed Apr 17 10:39:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905191 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 563FF17E0 for ; Wed, 17 Apr 2019 10:43:01 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4142D28AD1 for ; Wed, 17 Apr 2019 10:43:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 32A45289F5; Wed, 17 Apr 2019 10:43:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D9F07289F5 for ; Wed, 17 Apr 2019 10:43:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731680AbfDQKnA (ORCPT ); Wed, 17 Apr 2019 06:43:00 -0400 Received: from mga17.intel.com ([192.55.52.151]:9776 "EHLO mga17.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731509AbfDQKnA (ORCPT ); Wed, 17 Apr 2019 06:43:00 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:42:59 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120276" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:42:52 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 25/28] x86/sgx: Update MAINTAINERS Date: Wed, 17 Apr 2019 13:39:35 +0300 Message-Id: <20190417103938.7762-26-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add the maintainer information for the SGX subsystem. Signed-off-by: Jarkko Sakkinen --- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3671fdea5010..eb3b80811653 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8044,6 +8044,18 @@ L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-intel-mid.c +INTEL SGX +M: Jarkko Sakkinen +M: Sean Christopherson +L: linux-sgx@vger.kernel.org +S: Maintained +Q: https://patchwork.kernel.org/project/intel-sgx/list/ +T: git https://github.com/jsakkine-intel/linux-sgx.git +F: arch/x86/include/asm/sgx.h +F: arch/x86/include/uapi/asm/sgx.h +F: arch/x86/kernel/cpu/sgx/* +K: \bSGX_ + INTERCONNECT API M: Georgi Djakov S: Maintained From patchwork Wed Apr 17 10:39:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905193 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8C1D917E0 for ; Wed, 17 Apr 2019 10:43:11 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6F1CF289F5 for ; Wed, 17 Apr 2019 10:43:11 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6311428AD4; Wed, 17 Apr 2019 10:43:11 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B621E289F5 for ; Wed, 17 Apr 2019 10:43:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731509AbfDQKnJ (ORCPT ); Wed, 17 Apr 2019 06:43:09 -0400 Received: from mga14.intel.com ([192.55.52.115]:43114 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729091AbfDQKnJ (ORCPT ); Wed, 17 Apr 2019 06:43:09 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:43:08 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120336" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:43:00 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com Subject: [PATCH v20 26/28] docs: x86/sgx: Add Architecture documentation Date: Wed, 17 Apr 2019 13:39:36 +0300 Message-Id: <20190417103938.7762-27-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Document microarchitectural features of SGX relevant to the kernel. They are documented in detail enough to understand the implementation. Signed-off-by: Sean Christopherson --- Documentation/index.rst | 1 + Documentation/x86/index.rst | 10 + Documentation/x86/sgx/1.Architecture.rst | 431 +++++++++++++++++++++++ Documentation/x86/sgx/index.rst | 16 + 4 files changed, 458 insertions(+) create mode 100644 Documentation/x86/index.rst create mode 100644 Documentation/x86/sgx/1.Architecture.rst create mode 100644 Documentation/x86/sgx/index.rst diff --git a/Documentation/index.rst b/Documentation/index.rst index 80a421cb935e..3511400dc092 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -102,6 +102,7 @@ implementation. :maxdepth: 2 sh/index + x86/index Filesystem Documentation ------------------------ diff --git a/Documentation/x86/index.rst b/Documentation/x86/index.rst new file mode 100644 index 000000000000..6f3251c4b7b9 --- /dev/null +++ b/Documentation/x86/index.rst @@ -0,0 +1,10 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================= +x86 Documentation +================= + +.. toctree:: + :maxdepth: 1 + + sgx/index diff --git a/Documentation/x86/sgx/1.Architecture.rst b/Documentation/x86/sgx/1.Architecture.rst new file mode 100644 index 000000000000..a4de6c610231 --- /dev/null +++ b/Documentation/x86/sgx/1.Architecture.rst @@ -0,0 +1,431 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============ +Architecture +============ + +Introduction +============ + +SGX is a set of instructions and mechanisms that enable ring 3 applications to +set aside private regions of code and data for the purpose of establishing and +running enclaves. An enclave is a secure entity whose private memory can only +be accessed by code running within the enclave. Accesses from outside the +enclave, including software running at a higher privilege level and other +enclaves, are disallowed by hardware. + +SGX also provides for local and remote attestation. `Attestation`_ allows an +enclave to attest its identity, that it has not been tampered with, that it is +running on a genuine platform with Intel SGX enabled, and the security +properties of the platform on which it is running. + +You can determine if your CPU supports SGX by querying ``/proc/cpuinfo``: + + ``cat /proc/cpuinfo | grep sgx`` + + +Enclave Page Cache +================== + +SGX utilizes an Enclave Page Cache (EPC) to store pages that are associated +with an enclave. The EPC is secure storage whose exact physical implementation +is micro-architecture specific (see `EPC Implemenations`_). Similar to normal +system memory, the EPC is managed by privileged software using conventional +paging mechanisms, e.g. the kernel can grant/deny access to EPC memory by +manipulating a process' page tables, and can swap pages in/out of the EPC in +order to oversubscribe the EPC. + +Unlikely regular memory, hardware prevents arbitrary insertion, eviction, +deletion, access, etc... to/from the EPC. Software must instead use dedicated +`SGX instructions`_ to operate on the EPC, which enables the processor to +provide SGX's security guarantees by enforcing various restrictions and +behaviors, e.g. limits concurrent accesses to EPC pages and ensures proper TLB +flushing when moving pages in/out of the EPC. + +Accesses to EPC pages are allowed if and only if the access is classified as an +"enclave access". There are two categories of allowed enclave accesses: direct +and indirect. Direct enclave accesses are generated if and only the processor +is executing in Enclave Mode (see `Enclave execution`_). Indirect enclave +accesses are generated by various ENCL{S,U,V} functions, many of which can be +executed outside of Enclave Mode. + +Non-enclave accesses to the EPC result in undefined behavior. Conversely, +enclave accesses to non-EPC memory result in a page fault (#PF)[1]_. Page +faults due to invalid enclave accesses set the PF_SGX flag (bit 15) in the page +fault error code[2]_. + +Although all EPC implementations will undoubtedly encrypt the EPC itself, all +all EPC code/data is stored unencrypted in the processor's caches. I.e. SGX +relies on the aforementioned mechanisms to protect an enclave's secrets while +they are resident in the cache. + +Note, EPC pages are always 4KB sized and aligned. Software can map EPC using +using large pages, but the processor always operates on a 4KB granularity when +working with EPC pages. + + +SGX instructions +================ + +SGX introduces three new instructions, ENCLS, ENCLU and ENCLV, for Supervisor, +User and Virtualization respectively. ENCL{S,U,V} are umbrella instructions, +using a single opcode as the front end to a variety of SGX functions. The leaf +function to execute is specified via %eax, with %rbx, %rcx and %rdx optionally +used for leaf-specific purposes. + +Note that supervisor software, i.e. the kernel, creates and manages enclaves, +but only user-level software can execute/enter an enclave. + +ENCLS Leafs +----------- + + - ECREATE: create an enclave + - EADD: add page to an uninitialized enclave + - EAUG: add page to an initialized enclave + - EEXTEND: extended the measurement of an (uninitialized) enclave + - EINIT: verify and initialize enclave + - EDBG{RD,WR}: read/write from/to a debug enclave’s memory + - EMODPR: restrict an EPC page’s permissions + - EMODT: modify an EPC page’s type + - EBLOCK: mark a page as blocked in EPCM + - ETRACK{C}: activate blocking tracing + - EWB: write back page from EPC to regular memory + - ELD{B,U}{C}: load page in {un}blocked state from system memory to EPC + - EPA: add version array (use to track evicted EPC pages) + - EREMOVE: remove a page from EPC + - ERDINFO: retrieve info about an EPC page from EPCM + +ENCLU Leafs +----------- + - EENTER: enter an enclave + - ERESUME: resume execution of an interrupted enclave + - EEXIT: exit an enclave + - EGETKEY: retrieve a cryptographic key from the processor + - EREPORT: generate a cryptographic report describing an enclave + - EMODPE: extend an EPC page's permissions + - EACCEPT: accept changes to an EPC page + - EACCEPTCOPY: copy an existing EPC page to an uninitialized EPC page + +ENCLV Leafs +----------- + - E{DEC,INC}VIRTCHILD: {dec,inc}rement SECS virtual refcount + - ESETCONTEXT: set SECS’ context pointer + + +EPC page types +============== + +All pages in the EPC have an explicit page type identifying the type of page. +The type of page affects the page's accessibility, concurrency requirements, +lifecycle, etc... + +SGX Enclave Control Structure (SECS) + An enclave is defined and referenced by an SGX Enclave Control Structure. + When creating an enclave (via ECREATE), software provides a source SECS for + the enclave, which is copied into a target EPC page. The source SECS + contains security and measurement information, as well as attributes and + properties of the enclave. Once the SECS is copied into the EPC, it's used + by the processor to store enclave metadata, e.g. the number of EPC pages + associated with the enclave, and is no longer directly accessible by + software. + +Regular (REG) + Regular EPC pages contain the code and data of an enclave. Code and data + pages can be added to an uninitialized enclave (prior to EINIT) via EADD. + Post EINIT, pages can be added to an enclave via EAUG. Pages added via + EAUG must be explicitly accepted by the enclave via EACCEPT or EACCEPTCOPY. + +Thread Control Structure (TCS) + Thread Control Structure pages define the entry points to an enclave and + track the execution state of an enclave thread. A TCS can only be used by + a single logical CPU at any given time, but otherwise has no attachment to + any particular logical CPU. Like regular pages, TCS pages are added to + enclaves via EADD and EINIT. + +Version Array (VA) + Version Array pages contain 512 slots, each of which can contain a version + number for a page evicted from the EPC. A version number is a unique 8-byte + value that is fed into the MAC computation used to verify the contents of an + evicted page when reloading said page into the EPC. VA pages are the only + page type not directly associated with an enclave, and are allocated in the + EPC via EPA. Note that VA pages can also be evicted from the EPC, but + doing so requires another VA page/slot to hold the version number of the VA + page being evicted. + +Trim (TRIM) + The Trim page type indicates that a page has been trimmed from the enclave’s + address space and is no longer accessible to enclave software, i.e. is about + to be removed from the enclave (via EREMOVE). Removing pages from a running + enclaves requires the enclave to explicit accept the removal (via EACCEPT). + The intermediate Trim type allows software to batch deallocation operations + to improve efficiency, e.g. minimize transitions between userspace, enclave + and kernel. + + +Enclave Page Cache Map +====================== + +The processor tracks EPC pages via the Enclave Page Cache Map (EPCM). The EPCM +is a processor-managed structure that enforces access restrictions to EPC pages +in addition to the software-managed page tables. The EPCM contains one entry +per EPC page, and although the details are implementation specific, all +implementations contain the following architectural information: + + - The status of EPC page with respect to validity and accessibility. + - An SECS identifier of the enclave to which the page belongs. + - The type of page: regular, SECS, TCS, VA or TRIM + - The linear address through which the enclave is allowed to access the page. + - The specified read/write/execute permissions on that page. + +Access violations, e.g. insufficient permissions or incorrect linear address, +detected via the EPCM result in a page fault (#PF)[1]_ exception being signaled +by the processor. Page faults due to EPCM violations set the PF_SGX flag +(bit 15) in the page fault error code[2]_. + +The EPCM is consulted if and only if walking the software-managed page tables, +i.e. the kernel's page tables, succeeds. I.e. the effective permissions for an +EPC page are a logical AND of the kernel's page tables and the corresponding +EPCM entry. This allows the kernel to make its page tables more restrictive +without triggering an EPCM violation, e.g. it may mark an entry as not-present +prior to evicting a page from the EPC. + +**IMPORTANT** For all intents and purposes the SGX architecture allows the +processor to invalidate all EPCM entries at will, i.e. requires that software +be prepared to handle an EPCM fault at any time. Most processors are expected +to implement the EPC{M} as a subset of system DRAM that is encrypted with an +ephemeral key, i.e. a key that is randomly generated at processor reset. As a +result of using an ephemeral key, the contents of the EPC{M} are lost when the +processor is powered down as part of an S3 transition or when a virtual machine +is live migrated to a new physical system. + + +Enclave initialization +====================== + +Because software cannot directly access the EPC except when executing in an +enclave, an enclave must be built using ENCLS functions (ECREATE and EADD) as +opposed to simply copying the enclave from the filesystem to memory. Once an +enclave is built, it must be initialized (via EINIT) before userspace can enter +the enclave and begin `Enclave execution`_. + +During the enclave build process, two "measurements", i.e. SHA-256 hashes, are +taken of the enclave: MRENCLAVE and MRSIGNER. MRENCLAVE measures the enclave's +contents, e.g. code/data explicitly added to the measurement (via EEXTEND), as +well as metadata from the enclave's build process, e.g. pages offsets (relative +to the enclave's base) and page permissions of all pages added to the enclave +(via EADD). MRENCLAVE is initialized by ECREATE and finalized by EINIT. +MRSIGNER is simply the SHA-256 hash of the public key used to sign the enclave. + +EINIT accepts two parameters in addition to the SECS of the target enclave: an +Enclave Signature Struct (SIGSTRUCT) and an EINIT token (EINITTOKEN). +SIGSTRUCT is a structure created and signed by the enclave's developer. Among +other fields, SIGSTRUCT contains the expected MRENCLAVE of the enclave and the +MRSIGNER of the enclave. SIGSTRUCT's MRENCLAVE is used by the processor to +verify that the enclave was properly built (at runtime), and its SIGSTRUCT is +copied to the SECS upon successful EINIT. EINITTOKEN is an optional parameter +that is consumed as part of `Launch Control`_. + + +Enclave execution +================= + +Enclaves execute in a bespoke sub-mode of ring 3, appropriately named Enclave +Mode. Enclave Mode changes behavior in key ways to support SGX's security +guarantees and to reduce the probability of unintentional disclosure of +sensitive data. + +A notable cornerstone of Enclave Mode is the Enclave Linear Range (ELRANGE). +An enclave is associated with one, and only one, contiguous linear address +range, its ELRANGE. The ELRANGE is specified via the SIZE and BASEADDR fields +in the SECS (provided to ECREATE). The processor queries the active enclave's +ELRANGE to differentiate enclave and non-enclave accesses, i.e. accesses that +originate in Enclave Mode *and* whose linear address falls within ELRANGE are +considered (direct) enclave accesses. Note, the processor also generates +(indirect) enclave accesses when executing ENCL* instructions, which may occur +outside of Enclave Mode, e.g. when copying the SECS to its target EPC page +during ECREATE. + +Enclave Mode changes include, but are not limited to: + + - Permits direct software access to EPC pages owned by the enclave + - Ensures enclave accesses map to the EPC (EPCM violation, i.e. #PF w/ PF_SGX) + - Prevents executing code outside the enclave's ELRANGE (#GP fault) + - Changes the behavior of exceptions/events + - Causes many instructions to become illegal, i.e. generate an exception + - Supresses all instruction breakpoints* + - Suppresses data breakpoints within enclave's ELRANGE* + + * For non-debug enclaves. + +Transitions to/from Enclave Mode have semantics that are a lovely blend of +SYSCALL, SYSRET and VM-Exit. In normal execution, entering and exiting Enclave +Mode can only be done through EENTER and EEXIT respectively. EENTER+EEXIT is +analogous to SYSCALL+SYSRET, e.g. EENTER/SYSCALL load RCX with the next RIP and +EEXIT/SYSRET load RIP from R{B,C}X, and EENTER can only jump to a predefined +location controlled by the enclave/kernel. + +But when an exception, interrupt, VM-Exit, etc... occurs, enclave transitions +behave more like VM-Exit and VMRESUME. To maintain the black box nature of the +enclave, the processor automatically switches register context when any of the +aforementioned events occur (the SDM refers to such events as Enclave Exiting +Events (EEE)). + +To handle an EEE, the processor performs an Asynchronous Enclave Exits (AEX). +Note, although exceptions and traps are synchronous from a processor execution +perspective, the are asynchronous from the enclave's perspective as the enclave +is not provided an opportunity to save/fuzz state prior to exiting the enclave. +On an AEX, the processor exits the enclave to a predefined %rip called the +Asynchronous Exiting Pointer (AEP). The AEP is specified at enclave entry (via +EENTER/ERESUME) and saved into the associated TCS, similar to how a hypervisor +specifies the VM-Exit target (via VMCS.HOST_RIP at VMLAUNCH/VMRESUME), i.e. the +the AEP is an exit location controlled by the enclave's untrusted runtime. + +On an AEX, the processor fully exits the enclave prior to vectoring the event, +i.e. from the event handler's perspective the event occurred at the AEP. Thus, +IRET/RSM/VMRESUME (from the event handler) returns control to the enclave's +untrusted runtime, which can take appropriate action, e.g. immediately ERESUME +the enclave on interrupts, forward expected exceptions to the enclave, restart +the enclave on fatal exceptions, and so on and so forth. + +To preserve the enclave's state across AEX events, the processor automatically +saves architectural into a State Save Area (SSA). Because SGX supports nested +AEX events, e.g. the untrusted runtime can re-EENTER the enclave after an AEX, +which can in turn trigger an AEX, the TCS holds a pointer to a stack of SSA +frames (as opposed to a single SSA), an index to the current SSA frame and the +total number of available frames. When an AEX occurs, the processor saves the +architectural state into the TCS's current SSA frame. The untrusted runtime +can then pop the last SSA frame (off the TCS's stack) via ERESUME, i.e. restart +the enclave after the AEX is handled. + + +Launch Control +============== + +SGX provides a set of controls, referred to as Launch Control, that governs the +initialization of enclaves. The processor internally stores a SHA-256 hash of +a 3072-bit RSA public key, i.e. a MRSIGNER, often referred to as the "LE pubkey +hash". The LE pubkey hash is used during EINIT to prevent launching an enclave +without proper authorization. In order for EINIT to succeed, the enclave's +MRSIGNER (from SIGSTRUCT) *or* the MRSIGNER of the enclave's EINITTOKEN must +match the LE pubkey hash. + +An EINITTOKEN can only be created by a so called Launch Enclave (LE). A LE is +an enclave with SECS.ATTRIBUTES.EINITTOKEN_KEY=1, which grants it access to the +EINITTOKEN_KEY (retrieved via EGETKEY). EINITTOKENs provide a ready-built +mechanism for userspace to bless enclaves without requiring additional kernel +infrastructure. + +Processors that support SGX Launch Control Configuration, enumerated by the +SGX_LC flag (bit 30 in CPUID 0x7.0x0.ECX), expose the LE pubkey hash as a set +of four MSRs, aptly named IA32_SGXLEPUBKEYHASH[0-3]. The reset value of the +MSRs is an internally defined (Intel) key (processors that don't support +SGX_LC also use an internally defined key, it's just not exposed to software). + +While the IA32_SGXLEPUBKEYHASH MSRs are readable on any platform that supports +SGX_LC, the MSRs are only writable if the IA32_FEATURE_CONTROL is locked with +bit 17 ("SGX Launch Control Enable" per the SDM, or more accurately "SGX LE +pubkey hash writable") set to '1'. Note, the MSRs are also writable prior to +`SGX activation`_. + +Note, while "Launch Control Configuration" is the official feature name used by +the Intel SDM, other documentation may use the term "Flexible Launch Control", +or even simply "Launch Control". Colloquially, the vast majority of usage of +the term "Launch Control" is synonymous with "Launch Control Configuration". + + +EPC oversubscription +==================== + +SGX supports the concept of EPC oversubscription. Analogous to swapping system +DRAM to disk, enclave pages can be swapped from the EPC to memory, and later +reloaded from memory to the EPC. But because the kernel is untrusted, swapping +pages in/out of the EPC has specialized requirements: + + - The kernel cannot directly access EPC memory, i.e. cannot copy data to/from + the EPC. + - The kernel must "prove" to hardware that there are no valid TLB entries for + said page prior to eviction (a stale TLB entry would allow an attacker to + bypass SGX access controls). + - When loading a page back into the EPC, hardware must be able to verify + the integrity and freshness of the data. + - When loading an enclave page, e.g. regular and TCS pages, hardware must be + able to associate the page with an SECS, i.e. refcount an enclaves pages. + +To satisfy the above requirements, the CPU provides dedicated ENCLS functions +to support paging data in/out of the EPC: + + - EBLOCK: Mark a page as blocked in the EPC Map (EPCM). Attempting to access + a blocked page that misses the TLB will fault. + - ETRACK: Activate TLB tracking. Hardware verifies that all translations for + pages marked as "blocked" have been flushed from the TLB. + - EPA: Add Version Array page to the EPC (see `EPC page types`_) + - EWB: Write back a page from EPC to memory, e.g. RAM. Software must + supply a VA slot, memory to hold the Paging Crypto Metadata (PCMD) of the + page and obviously backing for the evicted page. + - ELD*: Load a page in {un}blocked state from memory to EPC. + +Swapped EPC pages are {de,en}crypted on their way in/out of the EPC, e.g. EWB +encrypts and ELDU decrypts. The version number (stored in a VA page) and PCMD +structure associated with an evicted EPC page seal a page (prevent undetected +modification) and ensure its freshness (prevent rollback to a stale version of +the page) while the page resides in unprotected storage, e.g. memory or disk. + + +Attestation +=========== + +SGX provides mechanisms that allow software to implement what Intel refers to +as Local Attestation (used by enclaves running on a the same physical platform +to securely identify one another) and Remote Attestation (a process by which an +enclave attests itself to a remote entity in order to gain the trust of said +entity). + +The details of Local Attestation and Remote Attestation are far beyond the +scope of this document. Please see Intel's Software Developer's Manual and/or +use your search engine of choice to learn more about SGX's attestation +capabilities. + + +EPC Implemenations +================== + +PRM with MEE +-------------- + +Initial hardware support for SGX implements the EPC by reserving a chunk of +system DRAM, referred to as Processor Reserved Memory (PRM). A percentage of +PRM is consumed by the processor to implement the EPCM, with the remainder of +PRM being exposed to software as the EPC. PRM is configured by firmware via +dedicated PRM Range Registers (PRMRRs). The PRMRRs are locked which are locked as part of SGX activation, i.e. +resizing the PRM, and thus EPC, requires rebooting the system. + +An autonomous hardware unit called the Memory Encryption Engine (MEE) protects +the confidentiality, integrity, and freshness of the PRM, e.g. {de,en}crypts +data as it is read/written from/to DRAM to provide confidentiality. + + +SGX activation +============== + +Before SGX can be fully enabled, e.g. via FEATURE_CONTROL, the platform must +undergo explicit SGX activation. SGX activation is a mechanism by which the +processor verifies and locks the platform configuration set by pre-boot +firmware, e.g. to ensure it satisfies SGX's security requirements. Before +SGX is activated (and its configuration locked), firmware can modify the +PRMRRs, e.g. to set the base/size of the PRM and thus EPC, and can also write +the SGX_LEPUBKEYHASH MSRs. Notably, the latter allows pre-boot firmware to +lock the SGX_LEPUBKEYHASH MSRs to a non-Intel value by writing the MSRs and +locking MSR_IA32_FEATURE_CONTROL without setting the "SGX LE pubkey hash +writable" flag, i.e. making the SGX_LEPUBKEYHASH MSRs readonly. + + +Footnotes +========= + +.. [1] All processors that do not support the SGX2 ISA take an errata and + signal #GP(0) instead of #PF(PF_SGX) when vectoring EPCM violations and + faults due to enclave-accesses to non-EPC memory. + +.. [2] Note that despite being vectored as a #PF, a #PF with PF_SGX has nothing + to do with conventional paging. + diff --git a/Documentation/x86/sgx/index.rst b/Documentation/x86/sgx/index.rst new file mode 100644 index 000000000000..c5dfef62e612 --- /dev/null +++ b/Documentation/x86/sgx/index.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +Software Guard Extensions +========================= + +Intel(R) SGX is a set of architectural extensions that enables applications to +establish secure containers, a.k.a. enclaves. SGX enclaves provide security +guarantees such as integrity and confidentiality, even when running on a system +where privileged software, e.g. kernel, hypervisor, etc... is untrusted and +potentially malicious. + +.. toctree:: + :maxdepth: 1 + + 1.Architecture From patchwork Wed Apr 17 10:39:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905195 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 531A913B5 for ; Wed, 17 Apr 2019 10:43:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 38A8E289F5 for ; Wed, 17 Apr 2019 10:43:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 2C60F28AD4; Wed, 17 Apr 2019 10:43:18 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B3AA4289F5 for ; Wed, 17 Apr 2019 10:43:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731791AbfDQKnR (ORCPT ); Wed, 17 Apr 2019 06:43:17 -0400 Received: from mga04.intel.com ([192.55.52.120]:54717 "EHLO mga04.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731659AbfDQKnR (ORCPT ); Wed, 17 Apr 2019 06:43:17 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga104.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:43:16 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="132120357" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by orsmga007.jf.intel.com with ESMTP; 17 Apr 2019 03:43:09 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 27/28] docs: x86/sgx: Document kernel internals Date: Wed, 17 Apr 2019 13:39:37 +0300 Message-Id: <20190417103938.7762-28-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sean Christopherson Document some of the more tricky parts of the kernel implementation internals. Signed-off-by: Sean Christopherson Co-developed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- Documentation/x86/sgx/2.Kernel-internals.rst | 56 ++++++++++++++++++++ Documentation/x86/sgx/index.rst | 1 + 2 files changed, 57 insertions(+) create mode 100644 Documentation/x86/sgx/2.Kernel-internals.rst diff --git a/Documentation/x86/sgx/2.Kernel-internals.rst b/Documentation/x86/sgx/2.Kernel-internals.rst new file mode 100644 index 000000000000..de359bf605ca --- /dev/null +++ b/Documentation/x86/sgx/2.Kernel-internals.rst @@ -0,0 +1,56 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================ +Kernel Internals +================ + +CPU configuration +================= + +Because SGX has an ever evolving and expanding feature set, it's possible for +a BIOS or VMM to configure a system in such a way that not all CPUs are equal, +e.g. where Launch Control is only enabled on a subset of CPUs. Linux does +*not* support such a heterogeneous system configuration, nor does it even +attempt to play nice in the face of a misconfigured system. With the exception +of Launch Control's hash MSRs, which can vary per CPU, Linux assumes that all +CPUs have a configuration that is identical to the boot CPU. + +EPC oversubscription +==================== + +SGX allows to have larger enclaves than amount of available EPC by providing a +subset of leaf instruction for swapping EPC pages to the system memory. The +details of these instructions are discussed in the architecture document. Due +to the unique requirements for swapping EPC pages, and because EPC pages do not +have associated page structures, management of the EPC is not handled by the +standard memory subsystem. + +SGX directly handles swapping of EPC pages, including a thread to initiate the +reclaiming process and a rudimentary LRU mechanism. When the amount of free EPC +pages goes below a low watermark the swapping thread starts reclaiming pages. +The pages that have not been recently accessed (i.e. do not have the A bit set) +are selected as victim pages. Each enclave holds an shmem file as a backing +storage for reclaimed pages. + +Launch Control +============== + +The current kernel implementation supports only writable MSRs. The launch is +performed by setting the MSRs to the hash of the public key modulus of the +enclave signer and a token with the valid bit set to zero. Because kernel makes +ultimately all the launch decisions token are not needed for anything. We +don't need or have a launch enclave for generating them as the MSRs must always +be writable. + +Provisioning +============ + +The use of provisioning must be controlled because it allows to get access to +the provisioning keys to attest to a remote party that the software is running +inside a legit enclave. This could be used by a malware network to ensure that +its nodes are running inside legit enclaves. + +The driver introduces a special device file /dev/sgx/provision and a special +ioctl SGX_IOC_ENCLAVE_SET_ATTRIBUTE to accomplish this. A file descriptor +pointing to /dev/sgx/provision is passed to ioctl from which kernel authorizes +the PROVISION_KEY attribute to the enclave. diff --git a/Documentation/x86/sgx/index.rst b/Documentation/x86/sgx/index.rst index c5dfef62e612..5d660e83d984 100644 --- a/Documentation/x86/sgx/index.rst +++ b/Documentation/x86/sgx/index.rst @@ -14,3 +14,4 @@ potentially malicious. :maxdepth: 1 1.Architecture + 2.Kernel-internals From patchwork Wed Apr 17 10:39:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 10905197 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8579F13B5 for ; Wed, 17 Apr 2019 10:43:23 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 714AA289F5 for ; Wed, 17 Apr 2019 10:43:23 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 64F8228AD4; Wed, 17 Apr 2019 10:43:23 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0C720289F5 for ; Wed, 17 Apr 2019 10:43:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731878AbfDQKnW (ORCPT ); Wed, 17 Apr 2019 06:43:22 -0400 Received: from mga05.intel.com ([192.55.52.43]:7938 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731813AbfDQKnW (ORCPT ); Wed, 17 Apr 2019 06:43:22 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 17 Apr 2019 03:43:22 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.60,361,1549958400"; d="scan'208";a="150131067" Received: from jsakkine-mobl1.tm.intel.com (HELO localhost) ([10.237.50.189]) by FMSMGA003.fm.intel.com with ESMTP; 17 Apr 2019 03:43:17 -0700 From: Jarkko Sakkinen To: linux-kernel@vger.kernel.org, x86@kernel.org, linux-sgx@vger.kernel.org Cc: akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, nhorman@redhat.com, npmccallum@redhat.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, andriy.shevchenko@linux.intel.com, tglx@linutronix.de, kai.svahn@intel.com, bp@alien8.de, josh@joshtriplett.org, luto@kernel.org, kai.huang@intel.com, rientjes@google.com, Jarkko Sakkinen Subject: [PATCH v20 28/28] docs: x86/sgx: Document the enclave API Date: Wed, 17 Apr 2019 13:39:38 +0300 Message-Id: <20190417103938.7762-29-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> References: <20190417103938.7762-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Document the enclave driver API i.e. the set of ioctl's used to create and manage enclaves and set their privileges Signed-off-by: Jarkko Sakkinen --- Documentation/x86/sgx/3.API.rst | 27 +++++++++++++++++++++++++++ Documentation/x86/sgx/index.rst | 1 + 2 files changed, 28 insertions(+) create mode 100644 Documentation/x86/sgx/3.API.rst diff --git a/Documentation/x86/sgx/3.API.rst b/Documentation/x86/sgx/3.API.rst new file mode 100644 index 000000000000..b113aeb05f54 --- /dev/null +++ b/Documentation/x86/sgx/3.API.rst @@ -0,0 +1,27 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=== +API +=== + +The enclave life-cycle starts by opening `/dev/sgx/enclave`. After this there is +already a data structure inside kernel tracking the enclave that is initially +uncreated. After this a set of ioctl's can be used to create, populate and +initialize the enclave. + +You can close (if you want) the fd after you've mmap()'d. As long as the file is +open the enclave stays alive so you might want to do that after you don't need +it anymore. Even munmap() won't destruct the enclave if the file is open. +Neither will closing the fd as long as you have mmap() done over the fd (even +if it does not across the range defined in SECS). + +Finally, there is ioctl to authorize priviliged attributes: +`SGX_IOC_ENCLAVE_SET_ATTRIBUTE`. Each of them is presented by a file inside +`/dev/sgx/`. Right now there is only one such file `/dev/sgx/provision`, which +controls the `PROVISON_KEY` attribute. + +.. kernel-doc:: arch/x86/kernel/cpu/sgx/driver/ioctl.c + :functions: sgx_ioc_enclave_create + sgx_ioc_enclave_add_page + sgx_ioc_enclave_init + sgx_ioc_enclave_set_attribute diff --git a/Documentation/x86/sgx/index.rst b/Documentation/x86/sgx/index.rst index 5d660e83d984..de0b78328611 100644 --- a/Documentation/x86/sgx/index.rst +++ b/Documentation/x86/sgx/index.rst @@ -15,3 +15,4 @@ potentially malicious. 1.Architecture 2.Kernel-internals + 3.API