13fe0778eSJarkko Sakkinen // SPDX-License-Identifier: GPL-2.0
23fe0778eSJarkko Sakkinen /* Copyright(c) 2016-20 Intel Corporation. */
33fe0778eSJarkko Sakkinen
43fe0778eSJarkko Sakkinen #include <linux/acpi.h>
53fe0778eSJarkko Sakkinen #include <linux/miscdevice.h>
63fe0778eSJarkko Sakkinen #include <linux/mman.h>
73fe0778eSJarkko Sakkinen #include <linux/security.h>
83fe0778eSJarkko Sakkinen #include <linux/suspend.h>
93fe0778eSJarkko Sakkinen #include <asm/traps.h>
103fe0778eSJarkko Sakkinen #include "driver.h"
113fe0778eSJarkko Sakkinen #include "encl.h"
123fe0778eSJarkko Sakkinen
139d0c151bSJarkko Sakkinen u64 sgx_attributes_reserved_mask;
149d0c151bSJarkko Sakkinen u64 sgx_xfrm_reserved_mask = ~0x3;
159d0c151bSJarkko Sakkinen u32 sgx_misc_reserved_mask;
169d0c151bSJarkko Sakkinen
sgx_open(struct inode * inode,struct file * file)173fe0778eSJarkko Sakkinen static int sgx_open(struct inode *inode, struct file *file)
183fe0778eSJarkko Sakkinen {
193fe0778eSJarkko Sakkinen struct sgx_encl *encl;
201728ab54SJarkko Sakkinen int ret;
213fe0778eSJarkko Sakkinen
223fe0778eSJarkko Sakkinen encl = kzalloc(sizeof(*encl), GFP_KERNEL);
233fe0778eSJarkko Sakkinen if (!encl)
243fe0778eSJarkko Sakkinen return -ENOMEM;
253fe0778eSJarkko Sakkinen
261728ab54SJarkko Sakkinen kref_init(&encl->refcount);
273fe0778eSJarkko Sakkinen xa_init(&encl->page_array);
283fe0778eSJarkko Sakkinen mutex_init(&encl->lock);
291728ab54SJarkko Sakkinen INIT_LIST_HEAD(&encl->va_pages);
301728ab54SJarkko Sakkinen INIT_LIST_HEAD(&encl->mm_list);
311728ab54SJarkko Sakkinen spin_lock_init(&encl->mm_lock);
321728ab54SJarkko Sakkinen
331728ab54SJarkko Sakkinen ret = init_srcu_struct(&encl->srcu);
341728ab54SJarkko Sakkinen if (ret) {
351728ab54SJarkko Sakkinen kfree(encl);
361728ab54SJarkko Sakkinen return ret;
371728ab54SJarkko Sakkinen }
383fe0778eSJarkko Sakkinen
393fe0778eSJarkko Sakkinen file->private_data = encl;
403fe0778eSJarkko Sakkinen
413fe0778eSJarkko Sakkinen return 0;
423fe0778eSJarkko Sakkinen }
433fe0778eSJarkko Sakkinen
sgx_release(struct inode * inode,struct file * file)443fe0778eSJarkko Sakkinen static int sgx_release(struct inode *inode, struct file *file)
453fe0778eSJarkko Sakkinen {
463fe0778eSJarkko Sakkinen struct sgx_encl *encl = file->private_data;
471728ab54SJarkko Sakkinen struct sgx_encl_mm *encl_mm;
483fe0778eSJarkko Sakkinen
491728ab54SJarkko Sakkinen /*
501728ab54SJarkko Sakkinen * Drain the remaining mm_list entries. At this point the list contains
511728ab54SJarkko Sakkinen * entries for processes, which have closed the enclave file but have
521728ab54SJarkko Sakkinen * not exited yet. The processes, which have exited, are gone from the
531728ab54SJarkko Sakkinen * list by sgx_mmu_notifier_release().
541728ab54SJarkko Sakkinen */
551728ab54SJarkko Sakkinen for ( ; ; ) {
561728ab54SJarkko Sakkinen spin_lock(&encl->mm_lock);
571728ab54SJarkko Sakkinen
581728ab54SJarkko Sakkinen if (list_empty(&encl->mm_list)) {
591728ab54SJarkko Sakkinen encl_mm = NULL;
601728ab54SJarkko Sakkinen } else {
611728ab54SJarkko Sakkinen encl_mm = list_first_entry(&encl->mm_list,
621728ab54SJarkko Sakkinen struct sgx_encl_mm, list);
631728ab54SJarkko Sakkinen list_del_rcu(&encl_mm->list);
643fe0778eSJarkko Sakkinen }
653fe0778eSJarkko Sakkinen
661728ab54SJarkko Sakkinen spin_unlock(&encl->mm_lock);
671728ab54SJarkko Sakkinen
681728ab54SJarkko Sakkinen /* The enclave is no longer mapped by any mm. */
691728ab54SJarkko Sakkinen if (!encl_mm)
701728ab54SJarkko Sakkinen break;
711728ab54SJarkko Sakkinen
721728ab54SJarkko Sakkinen synchronize_srcu(&encl->srcu);
731728ab54SJarkko Sakkinen mmu_notifier_unregister(&encl_mm->mmu_notifier, encl_mm->mm);
741728ab54SJarkko Sakkinen kfree(encl_mm);
752ade0d60SJarkko Sakkinen
762ade0d60SJarkko Sakkinen /* 'encl_mm' is gone, put encl_mm->encl reference: */
772ade0d60SJarkko Sakkinen kref_put(&encl->refcount, sgx_encl_release);
783fe0778eSJarkko Sakkinen }
793fe0778eSJarkko Sakkinen
801728ab54SJarkko Sakkinen kref_put(&encl->refcount, sgx_encl_release);
813fe0778eSJarkko Sakkinen return 0;
823fe0778eSJarkko Sakkinen }
833fe0778eSJarkko Sakkinen
sgx_mmap(struct file * file,struct vm_area_struct * vma)843fe0778eSJarkko Sakkinen static int sgx_mmap(struct file *file, struct vm_area_struct *vma)
853fe0778eSJarkko Sakkinen {
863fe0778eSJarkko Sakkinen struct sgx_encl *encl = file->private_data;
873fe0778eSJarkko Sakkinen int ret;
883fe0778eSJarkko Sakkinen
893fe0778eSJarkko Sakkinen ret = sgx_encl_may_map(encl, vma->vm_start, vma->vm_end, vma->vm_flags);
903fe0778eSJarkko Sakkinen if (ret)
913fe0778eSJarkko Sakkinen return ret;
923fe0778eSJarkko Sakkinen
931728ab54SJarkko Sakkinen ret = sgx_encl_mm_add(encl, vma->vm_mm);
941728ab54SJarkko Sakkinen if (ret)
951728ab54SJarkko Sakkinen return ret;
961728ab54SJarkko Sakkinen
973fe0778eSJarkko Sakkinen vma->vm_ops = &sgx_vm_ops;
98*1c71222eSSuren Baghdasaryan vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO);
993fe0778eSJarkko Sakkinen vma->vm_private_data = encl;
1003fe0778eSJarkko Sakkinen
1013fe0778eSJarkko Sakkinen return 0;
1023fe0778eSJarkko Sakkinen }
1033fe0778eSJarkko Sakkinen
sgx_get_unmapped_area(struct file * file,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)1043fe0778eSJarkko Sakkinen static unsigned long sgx_get_unmapped_area(struct file *file,
1053fe0778eSJarkko Sakkinen unsigned long addr,
1063fe0778eSJarkko Sakkinen unsigned long len,
1073fe0778eSJarkko Sakkinen unsigned long pgoff,
1083fe0778eSJarkko Sakkinen unsigned long flags)
1093fe0778eSJarkko Sakkinen {
1103fe0778eSJarkko Sakkinen if ((flags & MAP_TYPE) == MAP_PRIVATE)
1113fe0778eSJarkko Sakkinen return -EINVAL;
1123fe0778eSJarkko Sakkinen
1133fe0778eSJarkko Sakkinen if (flags & MAP_FIXED)
1143fe0778eSJarkko Sakkinen return addr;
1153fe0778eSJarkko Sakkinen
1163fe0778eSJarkko Sakkinen return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
1173fe0778eSJarkko Sakkinen }
1183fe0778eSJarkko Sakkinen
119888d2491SJarkko Sakkinen #ifdef CONFIG_COMPAT
sgx_compat_ioctl(struct file * filep,unsigned int cmd,unsigned long arg)120888d2491SJarkko Sakkinen static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
121888d2491SJarkko Sakkinen unsigned long arg)
122888d2491SJarkko Sakkinen {
123888d2491SJarkko Sakkinen return sgx_ioctl(filep, cmd, arg);
124888d2491SJarkko Sakkinen }
125888d2491SJarkko Sakkinen #endif
126888d2491SJarkko Sakkinen
1273fe0778eSJarkko Sakkinen static const struct file_operations sgx_encl_fops = {
1283fe0778eSJarkko Sakkinen .owner = THIS_MODULE,
1293fe0778eSJarkko Sakkinen .open = sgx_open,
1303fe0778eSJarkko Sakkinen .release = sgx_release,
131888d2491SJarkko Sakkinen .unlocked_ioctl = sgx_ioctl,
132888d2491SJarkko Sakkinen #ifdef CONFIG_COMPAT
133888d2491SJarkko Sakkinen .compat_ioctl = sgx_compat_ioctl,
134888d2491SJarkko Sakkinen #endif
1353fe0778eSJarkko Sakkinen .mmap = sgx_mmap,
1363fe0778eSJarkko Sakkinen .get_unmapped_area = sgx_get_unmapped_area,
1373fe0778eSJarkko Sakkinen };
1383fe0778eSJarkko Sakkinen
1393fe0778eSJarkko Sakkinen static struct miscdevice sgx_dev_enclave = {
1403fe0778eSJarkko Sakkinen .minor = MISC_DYNAMIC_MINOR,
1413fe0778eSJarkko Sakkinen .name = "sgx_enclave",
1423fe0778eSJarkko Sakkinen .nodename = "sgx_enclave",
1433fe0778eSJarkko Sakkinen .fops = &sgx_encl_fops,
1443fe0778eSJarkko Sakkinen };
1453fe0778eSJarkko Sakkinen
sgx_drv_init(void)1463fe0778eSJarkko Sakkinen int __init sgx_drv_init(void)
1473fe0778eSJarkko Sakkinen {
1489d0c151bSJarkko Sakkinen unsigned int eax, ebx, ecx, edx;
1499d0c151bSJarkko Sakkinen u64 attr_mask;
1509d0c151bSJarkko Sakkinen u64 xfrm_mask;
151c82c6186SJarkko Sakkinen int ret;
1529d0c151bSJarkko Sakkinen
1533fe0778eSJarkko Sakkinen if (!cpu_feature_enabled(X86_FEATURE_SGX_LC))
1543fe0778eSJarkko Sakkinen return -ENODEV;
1553fe0778eSJarkko Sakkinen
1569d0c151bSJarkko Sakkinen cpuid_count(SGX_CPUID, 0, &eax, &ebx, &ecx, &edx);
1579d0c151bSJarkko Sakkinen
1589d0c151bSJarkko Sakkinen if (!(eax & 1)) {
1599d0c151bSJarkko Sakkinen pr_err("SGX disabled: SGX1 instruction support not available.\n");
1609d0c151bSJarkko Sakkinen return -ENODEV;
1619d0c151bSJarkko Sakkinen }
1629d0c151bSJarkko Sakkinen
1639d0c151bSJarkko Sakkinen sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK;
1649d0c151bSJarkko Sakkinen
1659d0c151bSJarkko Sakkinen cpuid_count(SGX_CPUID, 1, &eax, &ebx, &ecx, &edx);
1669d0c151bSJarkko Sakkinen
1679d0c151bSJarkko Sakkinen attr_mask = (((u64)ebx) << 32) + (u64)eax;
1689d0c151bSJarkko Sakkinen sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK;
1699d0c151bSJarkko Sakkinen
1709d0c151bSJarkko Sakkinen if (cpu_feature_enabled(X86_FEATURE_OSXSAVE)) {
1719d0c151bSJarkko Sakkinen xfrm_mask = (((u64)edx) << 32) + (u64)ecx;
1729d0c151bSJarkko Sakkinen sgx_xfrm_reserved_mask = ~xfrm_mask;
1739d0c151bSJarkko Sakkinen }
1749d0c151bSJarkko Sakkinen
175c82c6186SJarkko Sakkinen ret = misc_register(&sgx_dev_enclave);
176c82c6186SJarkko Sakkinen if (ret)
177c82c6186SJarkko Sakkinen return ret;
178c82c6186SJarkko Sakkinen
179c82c6186SJarkko Sakkinen return 0;
1803fe0778eSJarkko Sakkinen }
181