1// SPDX-License-Identifier: GPL-2.0
2/* Copyright(c) 2016-20 Intel Corporation. */
3
4#include <linux/acpi.h>
5#include <linux/miscdevice.h>
6#include <linux/mman.h>
7#include <linux/security.h>
8#include <linux/suspend.h>
9#include <asm/traps.h>
10#include "driver.h"
11#include "encl.h"
12
13u64 sgx_attributes_reserved_mask;
14u64 sgx_xfrm_reserved_mask = ~0x3;
15u32 sgx_misc_reserved_mask;
16
17static int __sgx_open(struct inode *inode, struct file *file)
18{
19 struct sgx_encl *encl;
20 int ret;
21
22 encl = kzalloc(sizeof(*encl), GFP_KERNEL);
23 if (!encl)
24 return -ENOMEM;
25
26 kref_init(kref: &encl->refcount);
27 xa_init(xa: &encl->page_array);
28 mutex_init(&encl->lock);
29 INIT_LIST_HEAD(list: &encl->va_pages);
30 INIT_LIST_HEAD(list: &encl->mm_list);
31 spin_lock_init(&encl->mm_lock);
32
33 ret = init_srcu_struct(&encl->srcu);
34 if (ret) {
35 kfree(objp: encl);
36 return ret;
37 }
38
39 file->private_data = encl;
40
41 return 0;
42}
43
44static int sgx_open(struct inode *inode, struct file *file)
45{
46 int ret;
47
48 ret = sgx_inc_usage_count();
49 if (ret)
50 return ret;
51
52 ret = __sgx_open(inode, file);
53 if (ret) {
54 sgx_dec_usage_count();
55 return ret;
56 }
57
58 return 0;
59}
60
61static int sgx_release(struct inode *inode, struct file *file)
62{
63 struct sgx_encl *encl = file->private_data;
64 struct sgx_encl_mm *encl_mm;
65
66 /*
67 * Drain the remaining mm_list entries. At this point the list contains
68 * entries for processes, which have closed the enclave file but have
69 * not exited yet. The processes, which have exited, are gone from the
70 * list by sgx_mmu_notifier_release().
71 */
72 for ( ; ; ) {
73 spin_lock(lock: &encl->mm_lock);
74
75 if (list_empty(head: &encl->mm_list)) {
76 encl_mm = NULL;
77 } else {
78 encl_mm = list_first_entry(&encl->mm_list,
79 struct sgx_encl_mm, list);
80 list_del_rcu(entry: &encl_mm->list);
81 }
82
83 spin_unlock(lock: &encl->mm_lock);
84
85 /* The enclave is no longer mapped by any mm. */
86 if (!encl_mm)
87 break;
88
89 synchronize_srcu(ssp: &encl->srcu);
90 mmu_notifier_unregister(subscription: &encl_mm->mmu_notifier, mm: encl_mm->mm);
91 kfree(objp: encl_mm);
92
93 /* 'encl_mm' is gone, put encl_mm->encl reference: */
94 kref_put(kref: &encl->refcount, release: sgx_encl_release);
95 }
96
97 kref_put(kref: &encl->refcount, release: sgx_encl_release);
98 return 0;
99}
100
101static int sgx_mmap(struct file *file, struct vm_area_struct *vma)
102{
103 struct sgx_encl *encl = file->private_data;
104 int ret;
105
106 ret = sgx_encl_may_map(encl, start: vma->vm_start, end: vma->vm_end, vm_flags: vma->vm_flags);
107 if (ret)
108 return ret;
109
110 ret = sgx_encl_mm_add(encl, mm: vma->vm_mm);
111 if (ret)
112 return ret;
113
114 vma->vm_ops = &sgx_vm_ops;
115 vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | VM_IO);
116 vma->vm_private_data = encl;
117
118 return 0;
119}
120
121static unsigned long sgx_get_unmapped_area(struct file *file,
122 unsigned long addr,
123 unsigned long len,
124 unsigned long pgoff,
125 unsigned long flags)
126{
127 if ((flags & MAP_TYPE) == MAP_PRIVATE)
128 return -EINVAL;
129
130 if (flags & MAP_FIXED)
131 return addr;
132
133 return mm_get_unmapped_area(filp: file, addr, len, pgoff, flags);
134}
135
136#ifdef CONFIG_COMPAT
137static long sgx_compat_ioctl(struct file *filep, unsigned int cmd,
138 unsigned long arg)
139{
140 return sgx_ioctl(filep, cmd, arg);
141}
142#endif
143
144static const struct file_operations sgx_encl_fops = {
145 .owner = THIS_MODULE,
146 .open = sgx_open,
147 .release = sgx_release,
148 .unlocked_ioctl = sgx_ioctl,
149#ifdef CONFIG_COMPAT
150 .compat_ioctl = sgx_compat_ioctl,
151#endif
152 .mmap = sgx_mmap,
153 .get_unmapped_area = sgx_get_unmapped_area,
154};
155
156static struct miscdevice sgx_dev_enclave = {
157 .minor = MISC_DYNAMIC_MINOR,
158 .name = "sgx_enclave",
159 .nodename = "sgx_enclave",
160 .fops = &sgx_encl_fops,
161};
162
163int __init sgx_drv_init(void)
164{
165 unsigned int eax, ebx, ecx, edx;
166 u64 attr_mask;
167 u64 xfrm_mask;
168 int ret;
169
170 if (!cpu_feature_enabled(X86_FEATURE_SGX_LC)) {
171 pr_info("SGX disabled: SGX launch control CPU feature is not available, /dev/sgx_enclave disabled.\n");
172 return -ENODEV;
173 }
174
175 cpuid_count(SGX_CPUID, count: 0, eax: &eax, ebx: &ebx, ecx: &ecx, edx: &edx);
176
177 if (!(eax & 1)) {
178 pr_info("SGX disabled: SGX1 instruction support not available, /dev/sgx_enclave disabled.\n");
179 return -ENODEV;
180 }
181
182 sgx_misc_reserved_mask = ~ebx | SGX_MISC_RESERVED_MASK;
183
184 cpuid_count(SGX_CPUID, count: 1, eax: &eax, ebx: &ebx, ecx: &ecx, edx: &edx);
185
186 attr_mask = (((u64)ebx) << 32) + (u64)eax;
187 sgx_attributes_reserved_mask = ~attr_mask | SGX_ATTR_RESERVED_MASK;
188
189 if (cpu_feature_enabled(X86_FEATURE_OSXSAVE)) {
190 xfrm_mask = (((u64)edx) << 32) + (u64)ecx;
191 sgx_xfrm_reserved_mask = ~xfrm_mask;
192 }
193
194 ret = misc_register(misc: &sgx_dev_enclave);
195 if (ret) {
196 pr_info("SGX disabled: Unable to register the /dev/sgx_enclave driver (%d).\n", ret);
197 return ret;
198 }
199
200 return 0;
201}
202

source code of linux/arch/x86/kernel/cpu/sgx/driver.c