[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 07/15] xen: x86: add functions to populate and destroy EPC for domain
Add per-domain structure to store SGX per-domain info. Currently only domain's EPC base and size are stored. Also add new functions for further use: - hvm_populate_epc # populate EPC when EPC base & size are notified. - hvm_reset_epc # Reset domain's EPC to be invalid. Used when domain goes to S3-S5, or being destroyed. - hvm_destroy_epc # destroy and free domain's EPC. Signed-off-by: Kai Huang <kai.huang@xxxxxxxxxxxxxxx> --- xen/arch/x86/hvm/vmx/sgx.c | 315 +++++++++++++++++++++++++++++++++++++ xen/arch/x86/hvm/vmx/vmx.c | 3 + xen/include/asm-x86/hvm/vmx/sgx.h | 14 ++ xen/include/asm-x86/hvm/vmx/vmcs.h | 2 + 4 files changed, 334 insertions(+) diff --git a/xen/arch/x86/hvm/vmx/sgx.c b/xen/arch/x86/hvm/vmx/sgx.c index f4c9b2f933..14379151e8 100644 --- a/xen/arch/x86/hvm/vmx/sgx.c +++ b/xen/arch/x86/hvm/vmx/sgx.c @@ -9,6 +9,8 @@ #include <asm/msr.h> #include <xen/errno.h> #include <xen/mm.h> +#include <xen/sched.h> +#include <asm/p2m.h> #include <asm/hvm/vmx/sgx.h> #include <asm/hvm/vmx/vmcs.h> @@ -90,6 +92,319 @@ void unmap_epc_page(void *addr) /* Nothing */ } +/* ENCLS opcode */ +#define ENCLS .byte 0x0f, 0x01, 0xcf + +/* + * ENCLS leaf functions + * + * However currently we only needs EREMOVE.. + */ +enum { + ECREATE = 0x0, + EADD = 0x1, + EINIT = 0x2, + EREMOVE = 0x3, + EDGBRD = 0x4, + EDGBWR = 0x5, + EEXTEND = 0x6, + ELDU = 0x8, + EBLOCK = 0x9, + EPA = 0xA, + EWB = 0xB, + ETRACK = 0xC, + EAUG = 0xD, + EMODPR = 0xE, + EMODT = 0xF, +}; + +/* + * ENCLS error code + * + * Currently we only need SGX_CHILD_PRESENT + */ +#define SGX_CHILD_PRESENT 13 + +static inline int __encls(unsigned long rax, unsigned long rbx, + unsigned long rcx, unsigned long rdx) +{ + int ret; + + asm volatile ( "ENCLS;\n\t" + : "=a" (ret) + : "a" (rax), "b" (rbx), "c" (rcx), "d" (rdx) + : "memory", "cc"); + + return ret; +} + +static inline int __eremove(void *epc) +{ + unsigned long rbx = 0, rdx = 0; + + return __encls(EREMOVE, rbx, (unsigned long)epc, rdx); +} + +static int sgx_eremove(struct epc_page *epg) +{ + void *addr = map_epc_page_to_xen(epg); + int ret; + + BUG_ON(!addr); + + ret = __eremove(addr); + + unmap_epc_page(addr); + + return ret; +} + +/* + * Reset domain's EPC with EREMOVE. free_epc indicates whether to free EPC + * pages during reset. This will be called when domain goes into S3-S5 state + * (with free_epc being false), and when domain is destroyed (with free_epc + * being true). + * + * It is possible that EREMOVE will be called for SECS when it still has + * children present, in which case SGX_CHILD_PRESENT will be returned. In this + * case, SECS page is kept to a tmp list and after all EPC pages have been + * called with EREMOVE, we call EREMOVE for all the SECS pages again, and this + * time SGX_CHILD_PRESENT should never occur as all children should have been + * removed. + * + * If unexpected error returned by EREMOVE, it means the EPC page becomes + * abnormal, so it will not be freed even free_epc is true, as further use of + * this EPC can cause unexpected error, potentially damaging other domains. + */ +static int __hvm_reset_epc(struct domain *d, unsigned long epc_base_pfn, + unsigned long epc_npages, bool_t free_epc) +{ + struct list_head secs_list; + struct list_head *p, *tmp; + unsigned long i; + int ret = 0; + + INIT_LIST_HEAD(&secs_list); + + for ( i = 0; i < epc_npages; i++ ) + { + struct epc_page *epg; + unsigned long gfn; + mfn_t mfn; + p2m_type_t t; + int r; + + gfn = i + epc_base_pfn; + mfn = get_gfn_query(d, gfn, &t); + if ( unlikely(mfn_eq(mfn, INVALID_MFN)) ) + { + printk("Domain %d: Reset EPC error: invalid MFN for gfn 0x%lx\n", + d->domain_id, gfn); + put_gfn(d, gfn); + ret = -EFAULT; + continue; + } + + if ( unlikely(!p2m_is_epc(t)) ) + { + printk("Domain %d: Reset EPC error: (gfn 0x%lx, mfn 0x%lx): " + "is not p2m_epc.\n", d->domain_id, gfn, mfn_x(mfn)); + put_gfn(d, gfn); + ret = -EFAULT; + continue; + } + + put_gfn(d, gfn); + + epg = epc_mfn_to_page(mfn_x(mfn)); + + /* EREMOVE the EPC page to make it invalid */ + r = sgx_eremove(epg); + if ( r == SGX_CHILD_PRESENT ) + { + list_add_tail(&epg->list, &secs_list); + continue; + } + + if ( r ) + { + printk("Domain %d: Reset EPC error: (gfn 0x%lx, mfn 0x%lx): " + "EREMOVE returns %d\n", d->domain_id, gfn, mfn_x(mfn), r); + ret = r; + if ( free_epc ) + printk("WARNING: EPC (mfn 0x%lx) becomes abnormal. " + "Remove it from useable EPC.", mfn_x(mfn)); + continue; + } + + if ( free_epc ) + { + /* If EPC page is going to be freed, then also remove the mapping */ + if ( clear_epc_p2m_entry(d, gfn, mfn) ) + { + printk("Domain %d: Reset EPC error: (gfn 0x%lx, mfn 0x%lx): " + "clear p2m entry failed.\n", d->domain_id, gfn, + mfn_x(mfn)); + ret = -EFAULT; + } + free_epc_page(epg); + } + } + + list_for_each_safe(p, tmp, &secs_list) + { + struct epc_page *epg = list_entry(p, struct epc_page, list); + int r; + + r = sgx_eremove(epg); + if ( r ) + { + printk("Domain %d: Reset EPC error: mfn 0x%lx: " + "EREMOVE returns %d for SECS page\n", + d->domain_id, epc_page_to_mfn(epg), r); + ret = r; + list_del(p); + + if ( free_epc ) + printk("WARNING: EPC (mfn 0x%lx) becomes abnormal. " + "Remove it from useable EPC.", + epc_page_to_mfn(epg)); + continue; + } + + if ( free_epc ) + free_epc_page(epg); + } + + return ret; +} + +static void __hvm_unpopulate_epc(struct domain *d, unsigned long epc_base_pfn, + unsigned long populated_npages) +{ + unsigned long i; + + for ( i = 0; i < populated_npages; i++ ) + { + struct epc_page *epg; + unsigned long gfn; + mfn_t mfn; + p2m_type_t t; + + gfn = i + epc_base_pfn; + mfn = get_gfn_query(d, gfn, &t); + if ( unlikely(mfn_eq(mfn, INVALID_MFN)) ) + { + /* + * __hvm_unpopulate_epc only called when creating the domain on + * failure, therefore we can just ignore this error. + */ + printk("%s: Domain %u gfn 0x%lx returns invalid mfn\n", __func__, + d->domain_id, gfn); + put_gfn(d, gfn); + continue; + } + + if ( unlikely(!p2m_is_epc(t)) ) + { + printk("%s: Domain %u gfn 0x%lx returns non-EPC p2m type: %d\n", + __func__, d->domain_id, gfn, (int)t); + put_gfn(d, gfn); + continue; + } + + put_gfn(d, gfn); + + if ( clear_epc_p2m_entry(d, gfn, mfn) ) + { + printk("clear_epc_p2m_entry failed: gfn 0x%lx, mfn 0x%lx\n", + gfn, mfn_x(mfn)); + continue; + } + + epg = epc_mfn_to_page(mfn_x(mfn)); + free_epc_page(epg); + } +} + +static int __hvm_populate_epc(struct domain *d, unsigned long epc_base_pfn, + unsigned long epc_npages) +{ + unsigned long i; + int ret; + + for ( i = 0; i < epc_npages; i++ ) + { + struct epc_page *epg = alloc_epc_page(); + unsigned long mfn; + + if ( !epg ) + { + printk("%s: Out of EPC\n", __func__); + ret = -ENOMEM; + goto err; + } + + mfn = epc_page_to_mfn(epg); + ret = set_epc_p2m_entry(d, i + epc_base_pfn, _mfn(mfn)); + if ( ret ) + { + printk("%s: set_epc_p2m_entry failed with %d: gfn 0x%lx, " + "mfn 0x%lx\n", __func__, ret, i + epc_base_pfn, mfn); + free_epc_page(epg); + goto err; + } + } + + return 0; + +err: + __hvm_unpopulate_epc(d, epc_base_pfn, i); + return ret; +} + +int hvm_populate_epc(struct domain *d, unsigned long epc_base_pfn, + unsigned long epc_npages) +{ + struct sgx_domain *sgx = to_sgx(d); + int ret; + + if ( hvm_epc_populated(d) ) + return -EBUSY; + + if ( !epc_base_pfn || !epc_npages ) + return -EINVAL; + + if ( (ret = __hvm_populate_epc(d, epc_base_pfn, epc_npages)) ) + return ret; + + sgx->epc_base_pfn = epc_base_pfn; + sgx->epc_npages = epc_npages; + + return 0; +} + +/* + * +* + * This function returns error immediately if there's any unexpected error + * during this process. + */ +int hvm_reset_epc(struct domain *d, bool_t free_epc) +{ + struct sgx_domain *sgx = to_sgx(d); + + if ( !hvm_epc_populated(d) ) + return 0; + + return __hvm_reset_epc(d, sgx->epc_base_pfn, sgx->epc_npages, free_epc); +} + +void hvm_destroy_epc(struct domain *d) +{ + hvm_reset_epc(d, true); +} + static bool_t sgx_enabled_in_bios(void) { uint64_t val, sgx_enabled = IA32_FEATURE_CONTROL_SGX_ENABLE | diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index c53b24955a..243643111d 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -416,6 +416,9 @@ static int vmx_domain_initialise(struct domain *d) static void vmx_domain_destroy(struct domain *d) { + if ( hvm_epc_populated(d) ) + hvm_destroy_epc(d); + if ( !has_vlapic(d) ) return; diff --git a/xen/include/asm-x86/hvm/vmx/sgx.h b/xen/include/asm-x86/hvm/vmx/sgx.h index ff420e006e..40f860662a 100644 --- a/xen/include/asm-x86/hvm/vmx/sgx.h +++ b/xen/include/asm-x86/hvm/vmx/sgx.h @@ -13,6 +13,7 @@ #include <xen/init.h> #include <asm/processor.h> #include <xen/list.h> +#include <public/hvm/params.h> /* HVM_PARAM_SGX */ #define SGX_CPUID 0x12 @@ -61,4 +62,17 @@ struct epc_page *epc_mfn_to_page(unsigned long mfn); void *map_epc_page_to_xen(struct epc_page *epg); void unmap_epc_page(void *addr); +struct sgx_domain { + unsigned long epc_base_pfn; + unsigned long epc_npages; +}; + +#define to_sgx(d) (&((d)->arch.hvm_domain.vmx.sgx)) +#define hvm_epc_populated(d) (!!((d)->arch.hvm_domain.vmx.sgx.epc_base_pfn)) + +int hvm_populate_epc(struct domain *d, unsigned long epc_base_pfn, + unsigned long epc_npages); +int hvm_reset_epc(struct domain *d, bool_t free_epc); +void hvm_destroy_epc(struct domain *d); + #endif /* __ASM_X86_HVM_VMX_SGX_H__ */ diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h index 889091da42..6cfa5c3310 100644 --- a/xen/include/asm-x86/hvm/vmx/vmcs.h +++ b/xen/include/asm-x86/hvm/vmx/vmcs.h @@ -20,6 +20,7 @@ #include <asm/hvm/io.h> #include <irq_vectors.h> +#include <asm/hvm/vmx/sgx.h> extern void vmcs_dump_vcpu(struct vcpu *v); extern void setup_vmcs_dump(void); @@ -62,6 +63,7 @@ struct vmx_domain { unsigned long apic_access_mfn; /* VMX_DOMAIN_* */ unsigned int status; + struct sgx_domain sgx; }; struct pi_desc { -- 2.11.0 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |