From: Sean Christopherson <sean.j.christopherson@intel.com> To: "Paolo Bonzini" <pbonzini@redhat.com>, "Radim Krčmář" <rkrcmar@redhat.com>, "Thomas Gleixner" <tglx@linutronix.de>, "Ingo Molnar" <mingo@redhat.com>, "Borislav Petkov" <bp@alien8.de>, x86@kernel.org, "Jarkko Sakkinen" <jarkko.sakkinen@linux.intel.com>, "Sean Christopherson" <sean.j.christopherson@intel.com>, "Joerg Roedel" <joro@8bytes.org> Cc: "H. Peter Anvin" <hpa@zytor.com>, kvm@vger.kernel.org, linux-kernel@vger.kernel.org, linux-sgx@vger.kernel.org, Andy Lutomirski <luto@amacapital.net> Subject: [RFC PATCH 15/21] KVM: VMX: Add SGX ENCLS[ECREATE] handler to enforce CPUID restrictions Date: Fri, 26 Jul 2019 22:52:08 -0700 Message-ID: <20190727055214.9282-16-sean.j.christopherson@intel.com> (raw) In-Reply-To: <20190727055214.9282-1-sean.j.christopherson@intel.com> Userspace can restrict what bits can be set in MISCSELECT, ATTRIBUTES and XFRM via CPUID. Intercept ECREATE when any of the aforementioned masks diverges from hardware in order to enforce the desired CPUID model, i.e. inject #GP if the guest attempts to set a bit that hasn't been enumerated as allowed-1 in CPUID. Add the handler in a dedicated SGX file under the VMX sub-directory so as to confine the ugliness of the SGX specific code (re-executing ENCLS leafs is messy due to the need to follow pointers from structs, get EPC pages, etc...) and to save compilation cycles when SGX functionality is disabled in the kernel. The ENCLS handlers will soon grow to ~300 lines of code when Launch Control support is added, and in the distant future could balloon significantly if/when EPC oversubscription is supported. Actual usage of the handler will be added in a future patch, i.e. when SGX virtualization is fully enabled. Note, access to the PROVISIONKEY is not yet supported. Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com> --- arch/x86/include/asm/kvm_host.h | 3 + arch/x86/include/asm/sgx_arch.h | 1 + arch/x86/kvm/Makefile | 2 + arch/x86/kvm/vmx/sgx.c | 223 ++++++++++++++++++++++++++++++++ 4 files changed, 229 insertions(+) create mode 100644 arch/x86/kvm/vmx/sgx.c diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 103df8cbdd24..27841a5d7851 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -928,6 +928,9 @@ struct kvm_arch { bool guest_can_read_msr_platform_info; bool exception_payload_enabled; + + /* Guest can access the SGX PROVISIONKEY. */ + bool sgx_provisioning_allowed; }; struct kvm_vm_stat { diff --git a/arch/x86/include/asm/sgx_arch.h b/arch/x86/include/asm/sgx_arch.h index 39f731580ea8..e06f3ff717b4 100644 --- a/arch/x86/include/asm/sgx_arch.h +++ b/arch/x86/include/asm/sgx_arch.h @@ -8,6 +8,7 @@ #ifndef _ASM_X86_SGX_ARCH_H #define _ASM_X86_SGX_ARCH_H +#include <linux/bits.h> #include <linux/types.h> #define SGX_CPUID 0x12 diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 31ecf7a76d5a..f919c3e6abd7 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -13,6 +13,8 @@ kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \ hyperv.o page_track.o debugfs.o kvm-intel-y += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o vmx/evmcs.o vmx/nested.o +kvm-intel-$(CONFIG_INTEL_SGX_VIRTUALIZATION) += vmx/sgx.o + kvm-amd-y += svm.o pmu_amd.o obj-$(CONFIG_KVM) += kvm.o diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c new file mode 100644 index 000000000000..5b08e7dcc3a3 --- /dev/null +++ b/arch/x86/kvm/vmx/sgx.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <asm/sgx.h> +#include <asm/sgx_arch.h> + +#include "cpuid.h" +#include "kvm_cache_regs.h" +#include "vmx.h" +#include "x86.h" + +/* + * ENCLS's memory operands use a fixed segment (DS) and a fixed + * address size based on the mode. Related prefixes are ignored. + */ +static int sgx_get_encls_gva(struct kvm_vcpu *vcpu, unsigned long offset, + int size, int alignment, gva_t *gva) +{ + struct kvm_segment s; + bool fault; + + vmx_get_segment(vcpu, &s, VCPU_SREG_DS); + + *gva = s.base + offset; + + if (!IS_ALIGNED(*gva, alignment)) { + fault = true; + } else if (is_long_mode(vcpu)) { + fault = is_noncanonical_address(*gva, vcpu); + } else { + *gva &= 0xffffffff; + fault = (s.unusable) || + (s.type != 2 && s.type != 3) || + (*gva > s.limit) || + ((s.base != 0 || s.limit != 0xffffffff) && + (((u64)*gva + size - 1) > s.limit + 1)); + } + if (fault) + kvm_inject_gp(vcpu, 0); + return fault ? -EINVAL : 0; +} + +static int sgx_read_gva(struct kvm_vcpu *vcpu, gva_t gva, void *data, + unsigned int size) +{ + struct x86_exception ex; + + if (kvm_read_guest_virt(vcpu, gva, data, size, &ex)) { + kvm_propagate_page_fault(vcpu, &ex); + return -EFAULT; + } + return 0; +} + +static int sgx_read_hva(struct kvm_vcpu *vcpu, unsigned long hva, void *data, + unsigned int size) +{ + if (__copy_from_user(data, (void __user *)hva, size)) { + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; + vcpu->run->internal.ndata = 2; + vcpu->run->internal.data[0] = hva; + vcpu->run->internal.data[1] = size; + return -EFAULT; + } + return 0; +} + +static int sgx_gva_to_hva(struct kvm_vcpu *vcpu, gva_t gva, bool write, + unsigned long *hva) +{ + struct x86_exception ex; + gpa_t gpa; + + if (write) + gpa = kvm_mmu_gva_to_gpa_write(vcpu, gva, &ex); + else + gpa = kvm_mmu_gva_to_gpa_read(vcpu, gva, &ex); + + if (gpa == UNMAPPED_GVA) { + kvm_propagate_page_fault(vcpu, &ex); + return -EFAULT; + } + + *hva = kvm_vcpu_gfn_to_hva(vcpu, PFN_DOWN(gpa)); + if (kvm_is_error_hva(*hva)) { + ex.vector = PF_VECTOR; + ex.error_code = PFERR_PRESENT_MASK; + if (write) + ex.error_code |= PFERR_WRITE_MASK; + ex.address = gva; + ex.error_code_valid = true; + ex.nested_page_fault = false; + kvm_propagate_page_fault(vcpu, &ex); + return -EFAULT; + } + + return 0; +} + +static int sgx_encls_postamble(struct kvm_vcpu *vcpu, int ret, int trapnr, + gva_t gva) +{ + struct x86_exception ex; + unsigned long rflags; + + if (ret == -EFAULT) + goto handle_fault; + + rflags = vmx_get_rflags(vcpu) & ~(X86_EFLAGS_CF | X86_EFLAGS_PF | + X86_EFLAGS_AF | X86_EFLAGS_SF | + X86_EFLAGS_OF); + if (ret) + rflags |= X86_EFLAGS_ZF; + else + rflags &= ~X86_EFLAGS_ZF; + vmx_set_rflags(vcpu, rflags); + + kvm_rax_write(vcpu, ret); + return kvm_skip_emulated_instruction(vcpu); + +handle_fault: + /* + * A non-EPCM #PF indicates a bad userspace HVA. This *should* check + * for PFEC.SGX and not assume any #PF on SGX2 originated in the EPC, + * but the error code isn't (yet) plumbed through the ENCLS helpers. + */ + if (trapnr == PF_VECTOR && !boot_cpu_has(X86_FEATURE_SGX2)) { + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION; + vcpu->run->internal.ndata = 0; + return 0; + } + + /* + * If the guest thinks it's running on SGX2 hardware, inject an SGX + * #PF if the fault matches an EPCM fault signature (#GP on SGX1, + * #PF on SGX2). The assumption is that EPCM faults are much more + * likely than a bad userspace address. + */ + if ((trapnr == PF_VECTOR || !boot_cpu_has(X86_FEATURE_SGX2)) && + guest_cpuid_has(vcpu, X86_FEATURE_SGX2)) { + ex.vector = PF_VECTOR; + ex.error_code = PFERR_PRESENT_MASK | PFERR_WRITE_MASK | + PFERR_SGX_MASK; + ex.address = gva; + ex.error_code_valid = true; + ex.nested_page_fault = false; + kvm_inject_page_fault(vcpu, &ex); + } else { + kvm_inject_gp(vcpu, 0); + } + return 1; +} + +int handle_encls_ecreate(struct kvm_vcpu *vcpu) +{ + struct kvm_cpuid_entry2 *sgx_12_0, *sgx_12_1; + unsigned long a_hva, m_hva, x_hva, secs_hva; + struct sgx_pageinfo pageinfo; + gva_t pageinfo_gva, secs_gva; + u64 attributes, xfrm; + int ret, trapnr; + u32 miscselect; + + sgx_12_0 = kvm_find_cpuid_entry(vcpu, 0x12, 0); + sgx_12_1 = kvm_find_cpuid_entry(vcpu, 0x12, 1); + if (!sgx_12_0 || !sgx_12_1) { + kvm_inject_gp(vcpu, 0); + return 1; + } + + if (sgx_get_encls_gva(vcpu, kvm_rbx_read(vcpu), 32, 32, &pageinfo_gva) || + sgx_get_encls_gva(vcpu, kvm_rcx_read(vcpu), 4096, 4096, &secs_gva)) + return 1; + + /* + * Copy the PAGEINFO to local memory, its pointers need to be + * translated, i.e. we need to do a deep copy/translate. + */ + if (sgx_read_gva(vcpu, pageinfo_gva, &pageinfo, sizeof(pageinfo))) + return 1; + + /* Translate the SECINFO, SOURCE and SECS pointers from GVA to HVA. */ + if (sgx_gva_to_hva(vcpu, pageinfo.metadata, false, + (unsigned long *)&pageinfo.metadata) || + sgx_gva_to_hva(vcpu, pageinfo.contents, false, + (unsigned long *)&pageinfo.contents) || + sgx_gva_to_hva(vcpu, secs_gva, true, &secs_hva)) + return 1; + + m_hva = pageinfo.contents + offsetof(struct sgx_secs, miscselect); + a_hva = pageinfo.contents + offsetof(struct sgx_secs, attributes); + x_hva = pageinfo.contents + offsetof(struct sgx_secs, xfrm); + + /* Exit to userspace if copying from a host userspace address fails. */ + if (sgx_read_hva(vcpu, m_hva, &miscselect, sizeof(miscselect)) || + sgx_read_hva(vcpu, a_hva, &attributes, sizeof(attributes)) || + sgx_read_hva(vcpu, x_hva, &xfrm, sizeof(xfrm))) + return 0; + + /* Enforce restriction of access to the PROVISIONKEY. */ + if (!vcpu->kvm->arch.sgx_provisioning_allowed && + (attributes & SGX_ATTR_PROVISIONKEY)) { + if (sgx_12_1->eax & SGX_ATTR_PROVISIONKEY) + pr_warn_once("KVM: SGX PROVISIONKEY advertised but not allowed\n"); + kvm_inject_gp(vcpu, 0); + return 1; + } + + /* Enforce CPUID restrictions on MISCSELECT, ATTRIBUTES and XFRM. */ + if ((u32)miscselect & ~sgx_12_0->ebx || + (u32)attributes & ~sgx_12_1->eax || + (u32)(attributes >> 32) & ~sgx_12_1->ebx || + (u32)xfrm & ~sgx_12_1->ecx || + (u32)(xfrm >> 32) & ~sgx_12_1->edx) { + kvm_inject_gp(vcpu, 0); + return 1; + } + + ret = sgx_ecreate(&pageinfo, (void __user *)secs_hva, &trapnr); + + return sgx_encls_postamble(vcpu, ret, trapnr, secs_gva); +} -- 2.22.0
next prev parent reply index Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-07-27 5:51 [RFC PATCH 00/21] x86/sgx: KVM: Add SGX virtualization Sean Christopherson 2019-07-27 5:51 ` [RFC PATCH 01/21] x86/sgx: Add defines for SGX device minor numbers Sean Christopherson 2019-07-27 5:51 ` [RFC PATCH 02/21] x86/sgx: Move bus registration and device init to common code Sean Christopherson 2019-07-27 5:51 ` [RFC PATCH 03/21] x86/sgx: Move provisioning device " Sean Christopherson 2019-07-27 5:51 ` [RFC PATCH 04/21] x86/sgx: Add /dev/sgx/virt_epc device to allocate "raw" EPC for VMs Sean Christopherson 2019-07-27 17:44 ` Andy Lutomirski 2019-07-29 17:05 ` Sean Christopherson 2019-07-27 5:51 ` [RFC PATCH 05/21] x86/sgx: Expose SGX architectural definitions to the kernel Sean Christopherson 2019-07-27 5:51 ` [RFC PATCH 06/21] KVM: x86: Add SGX sub-features leaf to reverse CPUID table Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 07/21] KVM: x86: Add WARN_ON_ONCE(index!=0) in __do_cpuid_ent Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 08/21] KVM: x86: Add kvm_x86_ops hook to short circuit emulation Sean Christopherson 2019-07-27 17:38 ` Andy Lutomirski 2019-07-30 2:49 ` Sean Christopherson 2019-08-16 0:47 ` Andy Lutomirski 2019-08-19 22:01 ` Sean Christopherson 2019-08-20 1:34 ` Andy Lutomirski 2019-08-20 1:41 ` Sean Christopherson 2019-07-30 3:08 ` Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 09/21] KVM: VMX: Add basic handling of VM-Exit from SGX enclave Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 10/21] KVM: x86: Export kvm_mmu_gva_to_gpa_{read,write}() for VMX/SGX Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 11/21] KVM: x86: Export kvm_propagate_fault (as kvm_propagate_page_fault) Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 12/21] KVM: x86: Define new #PF SGX error code bit Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 13/21] x86/sgx: Move the intermediate EINIT helper into the driver Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 14/21] x86/sgx: Add helpers to expose ECREATE and EINIT to KVM Sean Christopherson 2019-07-27 5:52 ` Sean Christopherson [this message] 2019-07-27 5:52 ` [RFC PATCH 16/21] KVM: VMX: Edd emulation of SGX Launch Control LE hash MSRs Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 17/21] KVM: VMX: Add handler for ENCLS[EINIT] to support SGX Launch Control Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 18/21] KVM: x86: Invoke kvm_x86_ops->cpuid_update() after kvm_update_cpuid() Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 19/21] KVM: VMX: Enable SGX virtualization for SGX1, SGX2 and LC Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 20/21] x86/sgx: Export sgx_set_attribute() for use by KVM Sean Christopherson 2019-07-27 5:52 ` [RFC PATCH 21/21] KVM: x86: Add capability to grant VM access to privileged SGX attribute Sean Christopherson 2019-07-27 17:32 ` Andy Lutomirski
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20190727055214.9282-16-sean.j.christopherson@intel.com \ --to=sean.j.christopherson@intel.com \ --cc=bp@alien8.de \ --cc=hpa@zytor.com \ --cc=jarkko.sakkinen@linux.intel.com \ --cc=joro@8bytes.org \ --cc=kvm@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-sgx@vger.kernel.org \ --cc=luto@amacapital.net \ --cc=mingo@redhat.com \ --cc=pbonzini@redhat.com \ --cc=rkrcmar@redhat.com \ --cc=tglx@linutronix.de \ --cc=x86@kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
KVM Archive on lore.kernel.org Archives are clonable: git clone --mirror https://lore.kernel.org/kvm/0 kvm/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 kvm kvm/ https://lore.kernel.org/kvm \ kvm@vger.kernel.org public-inbox-index kvm Example config snippet for mirrors Newsgroup available over NNTP: nntp://nntp.lore.kernel.org/org.kernel.vger.kvm AGPL code for this site: git clone https://public-inbox.org/public-inbox.git