/* * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright(c) 2016-2017 Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * Contact Information: * Jarkko Sakkinen * Intel Finland Oy - BIC 0357606-4 - Westendinkatu 7, 02160 Espoo * * BSD LICENSE * * Copyright(c) 2016-2017 Intel Corporation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: * * Jarkko Sakkinen * Suresh Siddha * Serge Ayoun * Shay Katz-zamir * Sean Christopherson */ #include "asm/msr-index.h" #include "sgx.h" #include #include #include #include #include #include #include #include #include #define DRV_DESCRIPTION "Intel SGX Driver" #define DRV_VERSION "0.10" MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR("Jarkko Sakkinen "); MODULE_VERSION(DRV_VERSION); #ifndef X86_FEATURE_SGX #define X86_FEATURE_SGX (9 * 32 + 2) #endif #define FEATURE_CONTROL_SGX_ENABLE (1<<18) /* * Global data. */ struct workqueue_struct *sgx_add_page_wq; #define SGX_MAX_EPC_BANKS 8 struct sgx_epc_bank sgx_epc_banks[SGX_MAX_EPC_BANKS]; int sgx_nr_epc_banks; u64 sgx_encl_size_max_32; u64 sgx_encl_size_max_64; u64 sgx_xfrm_mask = 0x3; u32 sgx_misc_reserved; u32 sgx_xsave_size_tbl[64]; #ifdef CONFIG_COMPAT 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) { vma->vm_ops = &sgx_vm_ops; vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO | VM_DONTCOPY; 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))) return -EINVAL; /* On 64-bit architecture, allow mmap() to exceed 32-bit encl * limit only if the task is not running in 32-bit compatibility * mode. */ if (len > sgx_encl_size_max_32) #ifdef CONFIG_X86_64 if (test_thread_flag(TIF_ADDR32)) return -EINVAL; #else return -EINVAL; #endif #ifdef CONFIG_X86_64 if (len > sgx_encl_size_max_64) return -EINVAL; #endif 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_fops = { .owner = THIS_MODULE, .unlocked_ioctl = sgx_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = sgx_compat_ioctl, #endif .mmap = sgx_mmap, .get_unmapped_area = sgx_get_unmapped_area, }; static struct miscdevice sgx_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "isgx", .fops = &sgx_fops, .mode = 0666, }; static int sgx_pm_suspend(struct device *dev) { struct sgx_tgid_ctx *ctx; struct sgx_encl *encl; list_for_each_entry(ctx, &sgx_tgid_ctx_list, list) { list_for_each_entry(encl, &ctx->encl_list, encl_list) { sgx_invalidate(encl, false); encl->flags |= SGX_ENCL_SUSPEND; flush_work(&encl->add_page_work); } } return 0; } static SIMPLE_DEV_PM_OPS(sgx_drv_pm, sgx_pm_suspend, NULL); static int sgx_dev_init(struct device *parent) { unsigned int eax, ebx, ecx, edx; unsigned long pa; unsigned long size; int ret; int i; pr_info("intel_sgx: " DRV_DESCRIPTION " v" DRV_VERSION "\n"); cpuid_count(SGX_CPUID, SGX_CPUID_CAPABILITIES, &eax, &ebx, &ecx, &edx); /* Only allow misc bits supported by the driver. */ sgx_misc_reserved = ~ebx | SGX_MISC_RESERVED_MASK; #ifdef CONFIG_X86_64 sgx_encl_size_max_64 = 1ULL << ((edx >> 8) & 0xFF); #endif sgx_encl_size_max_32 = 1ULL << (edx & 0xFF); if (boot_cpu_has(X86_FEATURE_OSXSAVE)) { cpuid_count(SGX_CPUID, SGX_CPUID_ATTRIBUTES, &eax, &ebx, &ecx, &edx); sgx_xfrm_mask = (((u64)edx) << 32) + (u64)ecx; for (i = 2; i < 64; i++) { cpuid_count(0x0D, i, &eax, &ebx, &ecx, &edx); if ((1 << i) & sgx_xfrm_mask) sgx_xsave_size_tbl[i] = eax + ebx; } } for (i = 0; i < SGX_MAX_EPC_BANKS; i++) { cpuid_count(SGX_CPUID, i + SGX_CPUID_EPC_BANKS, &eax, &ebx, &ecx, &edx); if (!(eax & 0xf)) break; pa = ((u64)(ebx & 0xfffff) << 32) + (u64)(eax & 0xfffff000); size = ((u64)(edx & 0xfffff) << 32) + (u64)(ecx & 0xfffff000); dev_info(parent, "EPC bank 0x%lx-0x%lx\n", pa, pa + size); sgx_epc_banks[i].pa = pa; sgx_epc_banks[i].size = size; } sgx_nr_epc_banks = i; for (i = 0; i < sgx_nr_epc_banks; i++) { #ifdef CONFIG_X86_64 sgx_epc_banks[i].va = (unsigned long) ioremap_cache(sgx_epc_banks[i].pa, sgx_epc_banks[i].size); if (!sgx_epc_banks[i].va) { sgx_nr_epc_banks = i; ret = -ENOMEM; goto out_iounmap; } #endif ret = sgx_add_epc_bank(sgx_epc_banks[i].pa, sgx_epc_banks[i].size, i); if (ret) { sgx_nr_epc_banks = i + 1; goto out_iounmap; } } ret = sgx_page_cache_init(); if (ret) goto out_iounmap; sgx_add_page_wq = alloc_workqueue("intel_sgx-add-page-wq", WQ_UNBOUND | WQ_FREEZABLE, 1); if (!sgx_add_page_wq) { pr_err("intel_sgx: alloc_workqueue() failed\n"); ret = -ENOMEM; goto out_iounmap; } sgx_dev.parent = parent; ret = misc_register(&sgx_dev); if (ret) { pr_err("intel_sgx: misc_register() failed\n"); goto out_workqueue; } if (ret) goto out_workqueue; return 0; out_workqueue: destroy_workqueue(sgx_add_page_wq); out_iounmap: #ifdef CONFIG_X86_64 for (i = 0; i < sgx_nr_epc_banks; i++) iounmap((void *)sgx_epc_banks[i].va); #endif return ret; } static atomic_t sgx_init_flag = ATOMIC_INIT(0); static int sgx_drv_probe(struct platform_device *pdev) { unsigned int eax, ebx, ecx, edx; unsigned long fc; if (atomic_cmpxchg(&sgx_init_flag, 0, 1)) { pr_warn("intel_sgx: second initialization call skipped\n"); return 0; } if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) return -ENODEV; if (!boot_cpu_has(X86_FEATURE_SGX)) { pr_err("intel_sgx: the CPU is missing SGX\n"); return -ENODEV; } rdmsrl(MSR_IA32_FEATURE_CONTROL, fc); if (!(fc & FEATURE_CONTROL_LOCKED)) { pr_err("intel_sgx: the feature control MSR is not locked\n"); return -ENODEV; } if (!(fc & FEATURE_CONTROL_SGX_ENABLE)) { pr_err("intel_sgx: SGX is not enabled\n"); return -ENODEV; } cpuid(0, &eax, &ebx, &ecx, &edx); if (eax < SGX_CPUID) { pr_err("intel_sgx: CPUID is missing the SGX leaf\n"); return -ENODEV; } cpuid_count(SGX_CPUID, SGX_CPUID_CAPABILITIES, &eax, &ebx, &ecx, &edx); if (!(eax & 1)) { pr_err("intel_sgx: CPU does not support the SGX1 instructions\n"); return -ENODEV; } return sgx_dev_init(&pdev->dev); } static int sgx_drv_remove(struct platform_device *pdev) { int i; if (!atomic_cmpxchg(&sgx_init_flag, 1, 0)) { pr_warn("intel_sgx: second release call skipped\n"); return 0; } misc_deregister(&sgx_dev); destroy_workqueue(sgx_add_page_wq); #ifdef CONFIG_X86_64 for (i = 0; i < sgx_nr_epc_banks; i++) iounmap((void *)sgx_epc_banks[i].va); #endif sgx_page_cache_teardown(); 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 = "intel_sgx", .pm = &sgx_drv_pm, .acpi_match_table = ACPI_PTR(sgx_device_ids), }, }; static struct platform_device *pdev; int init_sgx_module(void) { platform_driver_register(&sgx_drv); pdev = platform_device_register_simple("intel_sgx", 0, NULL, 0); if (IS_ERR(pdev)) pr_err("platform_device_register_simple failed\n"); return 0; } void cleanup_sgx_module(void) { dev_set_uevent_suppress(&pdev->dev, true); platform_device_unregister(pdev); platform_driver_unregister(&sgx_drv); } module_init(init_sgx_module); module_exit(cleanup_sgx_module); MODULE_LICENSE("Dual BSD/GPL");