1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _X86_ENCLS_H |
3 | #define _X86_ENCLS_H |
4 | |
5 | #include <linux/bitops.h> |
6 | #include <linux/err.h> |
7 | #include <linux/io.h> |
8 | #include <linux/rwsem.h> |
9 | #include <linux/types.h> |
10 | #include <asm/asm.h> |
11 | #include <asm/traps.h> |
12 | #include "sgx.h" |
13 | |
14 | /* Retrieve the encoded trapnr from the specified return code. */ |
15 | #define ENCLS_TRAPNR(r) ((r) & ~SGX_ENCLS_FAULT_FLAG) |
16 | |
17 | /* Issue a WARN() about an ENCLS function. */ |
18 | #define ENCLS_WARN(r, name) { \ |
19 | do { \ |
20 | int _r = (r); \ |
21 | WARN_ONCE(_r, "%s returned %d (0x%x)\n", (name), _r, _r); \ |
22 | } while (0); \ |
23 | } |
24 | |
25 | /* |
26 | * encls_faulted() - Check if an ENCLS leaf faulted given an error code |
27 | * @ret: the return value of an ENCLS leaf function call |
28 | * |
29 | * Return: |
30 | * - true: ENCLS leaf faulted. |
31 | * - false: Otherwise. |
32 | */ |
33 | static inline bool encls_faulted(int ret) |
34 | { |
35 | return ret & SGX_ENCLS_FAULT_FLAG; |
36 | } |
37 | |
38 | /** |
39 | * encls_failed() - Check if an ENCLS function failed |
40 | * @ret: the return value of an ENCLS function call |
41 | * |
42 | * Check if an ENCLS function failed. This happens when the function causes a |
43 | * fault that is not caused by an EPCM conflict or when the function returns a |
44 | * non-zero value. |
45 | */ |
46 | static inline bool encls_failed(int ret) |
47 | { |
48 | if (encls_faulted(ret)) |
49 | return ENCLS_TRAPNR(ret) != X86_TRAP_PF; |
50 | |
51 | return !!ret; |
52 | } |
53 | |
54 | /** |
55 | * __encls_ret_N - encode an ENCLS function that returns an error code in EAX |
56 | * @rax: function number |
57 | * @inputs: asm inputs for the function |
58 | * |
59 | * Emit assembly for an ENCLS function that returns an error code, e.g. EREMOVE. |
60 | * And because SGX isn't complex enough as it is, function that return an error |
61 | * code also modify flags. |
62 | * |
63 | * Return: |
64 | * 0 on success, |
65 | * SGX error code on failure |
66 | */ |
67 | #define __encls_ret_N(rax, inputs...) \ |
68 | ({ \ |
69 | int ret; \ |
70 | asm volatile( \ |
71 | "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ |
72 | "2:\n" \ |
73 | _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_FAULT_SGX) \ |
74 | : "=a"(ret) \ |
75 | : "a"(rax), inputs \ |
76 | : "memory", "cc"); \ |
77 | ret; \ |
78 | }) |
79 | |
80 | #define __encls_ret_1(rax, rcx) \ |
81 | ({ \ |
82 | __encls_ret_N(rax, "c"(rcx)); \ |
83 | }) |
84 | |
85 | #define __encls_ret_2(rax, rbx, rcx) \ |
86 | ({ \ |
87 | __encls_ret_N(rax, "b"(rbx), "c"(rcx)); \ |
88 | }) |
89 | |
90 | #define __encls_ret_3(rax, rbx, rcx, rdx) \ |
91 | ({ \ |
92 | __encls_ret_N(rax, "b"(rbx), "c"(rcx), "d"(rdx)); \ |
93 | }) |
94 | |
95 | /** |
96 | * __encls_N - encode an ENCLS function that doesn't return an error code |
97 | * @rax: function number |
98 | * @rbx_out: optional output variable |
99 | * @inputs: asm inputs for the function |
100 | * |
101 | * Emit assembly for an ENCLS function that does not return an error code, e.g. |
102 | * ECREATE. Leaves without error codes either succeed or fault. @rbx_out is an |
103 | * optional parameter for use by EDGBRD, which returns the requested value in |
104 | * RBX. |
105 | * |
106 | * Return: |
107 | * 0 on success, |
108 | * trapnr with SGX_ENCLS_FAULT_FLAG set on fault |
109 | */ |
110 | #define __encls_N(rax, rbx_out, inputs...) \ |
111 | ({ \ |
112 | int ret; \ |
113 | asm volatile( \ |
114 | "1: .byte 0x0f, 0x01, 0xcf;\n\t" \ |
115 | " xor %%eax,%%eax;\n" \ |
116 | "2:\n" \ |
117 | _ASM_EXTABLE_TYPE(1b, 2b, EX_TYPE_FAULT_SGX) \ |
118 | : "=a"(ret), "=b"(rbx_out) \ |
119 | : "a"(rax), inputs \ |
120 | : "memory"); \ |
121 | ret; \ |
122 | }) |
123 | |
124 | #define __encls_2(rax, rbx, rcx) \ |
125 | ({ \ |
126 | unsigned long ign_rbx_out; \ |
127 | __encls_N(rax, ign_rbx_out, "b"(rbx), "c"(rcx)); \ |
128 | }) |
129 | |
130 | #define __encls_1_1(rax, data, rcx) \ |
131 | ({ \ |
132 | unsigned long rbx_out; \ |
133 | int ret = __encls_N(rax, rbx_out, "c"(rcx)); \ |
134 | if (!ret) \ |
135 | data = rbx_out; \ |
136 | ret; \ |
137 | }) |
138 | |
139 | /* Initialize an EPC page into an SGX Enclave Control Structure (SECS) page. */ |
140 | static inline int __ecreate(struct sgx_pageinfo *pginfo, void *secs) |
141 | { |
142 | return __encls_2(ECREATE, pginfo, secs); |
143 | } |
144 | |
145 | /* Hash a 256 byte region of an enclave page to SECS:MRENCLAVE. */ |
146 | static inline int __eextend(void *secs, void *addr) |
147 | { |
148 | return __encls_2(EEXTEND, secs, addr); |
149 | } |
150 | |
151 | /* |
152 | * Associate an EPC page to an enclave either as a REG or TCS page |
153 | * populated with the provided data. |
154 | */ |
155 | static inline int __eadd(struct sgx_pageinfo *pginfo, void *addr) |
156 | { |
157 | return __encls_2(EADD, pginfo, addr); |
158 | } |
159 | |
160 | /* Finalize enclave build, initialize enclave for user code execution. */ |
161 | static inline int __einit(void *sigstruct, void *token, void *secs) |
162 | { |
163 | return __encls_ret_3(EINIT, sigstruct, secs, token); |
164 | } |
165 | |
166 | /* Disassociate EPC page from its enclave and mark it as unused. */ |
167 | static inline int __eremove(void *addr) |
168 | { |
169 | return __encls_ret_1(EREMOVE, addr); |
170 | } |
171 | |
172 | /* Copy data to an EPC page belonging to a debug enclave. */ |
173 | static inline int __edbgwr(void *addr, unsigned long *data) |
174 | { |
175 | return __encls_2(EDGBWR, *data, addr); |
176 | } |
177 | |
178 | /* Copy data from an EPC page belonging to a debug enclave. */ |
179 | static inline int __edbgrd(void *addr, unsigned long *data) |
180 | { |
181 | return __encls_1_1(EDGBRD, *data, addr); |
182 | } |
183 | |
184 | /* Track that software has completed the required TLB address clears. */ |
185 | static inline int __etrack(void *addr) |
186 | { |
187 | return __encls_ret_1(ETRACK, addr); |
188 | } |
189 | |
190 | /* Load, verify, and unblock an EPC page. */ |
191 | static inline int __eldu(struct sgx_pageinfo *pginfo, void *addr, |
192 | void *va) |
193 | { |
194 | return __encls_ret_3(ELDU, pginfo, addr, va); |
195 | } |
196 | |
197 | /* Make EPC page inaccessible to enclave, ready to be written to memory. */ |
198 | static inline int __eblock(void *addr) |
199 | { |
200 | return __encls_ret_1(EBLOCK, addr); |
201 | } |
202 | |
203 | /* Initialize an EPC page into a Version Array (VA) page. */ |
204 | static inline int __epa(void *addr) |
205 | { |
206 | unsigned long rbx = SGX_PAGE_TYPE_VA; |
207 | |
208 | return __encls_2(EPA, rbx, addr); |
209 | } |
210 | |
211 | /* Invalidate an EPC page and write it out to main memory. */ |
212 | static inline int __ewb(struct sgx_pageinfo *pginfo, void *addr, |
213 | void *va) |
214 | { |
215 | return __encls_ret_3(EWB, pginfo, addr, va); |
216 | } |
217 | |
218 | /* Restrict the EPCM permissions of an EPC page. */ |
219 | static inline int __emodpr(struct sgx_secinfo *secinfo, void *addr) |
220 | { |
221 | return __encls_ret_2(EMODPR, secinfo, addr); |
222 | } |
223 | |
224 | /* Change the type of an EPC page. */ |
225 | static inline int __emodt(struct sgx_secinfo *secinfo, void *addr) |
226 | { |
227 | return __encls_ret_2(EMODT, secinfo, addr); |
228 | } |
229 | |
230 | /* Zero a page of EPC memory and add it to an initialized enclave. */ |
231 | static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr) |
232 | { |
233 | return __encls_2(EAUG, pginfo, addr); |
234 | } |
235 | |
236 | #endif /* _X86_ENCLS_H */ |
237 | |