xref: /openbmc/linux/arch/x86/kernel/cpu/sgx/driver.c (revision 1c71222e)
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