]>
Commit | Line | Data |
---|---|---|
0731742a XL |
1 | /* Copyright (c) Fortanix, Inc. |
2 | * | |
3 | * This Source Code Form is subject to the terms of the Mozilla Public | |
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | |
6 | #![cfg_attr( | |
7 | not(feature = "docs"), | |
8 | doc = "**You are viewing the internals documentation." | |
9 | )] | |
10 | #![cfg_attr( | |
11 | not(feature = "docs"), | |
12 | doc = "You probably want to compile the documentation with the “docs” feature.**" | |
13 | )] | |
14 | #![cfg_attr(not(feature = "docs"), doc = "---")] | |
15 | //! The Fortanix SGX ABI (compiler target `x86_64-fortanix-unknown-sgx`) is an | |
16 | //! interface for Intel SGX enclaves. It is a small yet functional interface | |
17 | //! suitable for writing larger enclaves. In contrast to other enclave | |
f2b60f7d | 18 | //! interfaces, this interface is primarily designed for running entire |
0731742a XL |
19 | //! applications in an enclave. |
20 | //! | |
21 | //! The Fortanix SGX ABI specification consists of two parts: | |
22 | //! | |
23 | //! 1. The calling convention (see FORTANIX-SGX-ABI.md) | |
24 | //! 2. The execution environment and [usercalls](struct.Usercalls.html) (this document) | |
25 | //! | |
26 | //! Whereas the calling convention describes how information is passed to and | |
27 | //! from the enclave, this document ascribes meaning to those values. | |
28 | //! | |
29 | //! The execution environment and usercalls have been designed with the | |
30 | //! following goals in mind: | |
31 | //! | |
32 | //! 1. *Compatible with most Rust code:* Rust code that doesn't link to other C | |
33 | //! libraries and that doesn't use files (see no. 5) should compile out of | |
34 | //! the box. | |
35 | //! 2. *Designed for SGX:* The SGX environment is unique and not compatible | |
36 | //! with other application environments out there. The primitives specified | |
37 | //! in this document are designed to work well with SGX, not to be similar | |
38 | //! to or compatible with primitives known from other environments. | |
39 | //! 3. *Designed for network services:* The most interesting usecase for SGX is | |
40 | //! to run applications remotely in an untrusted environment, e.g. the | |
41 | //! cloud. Therefore, there is a primary focus on supporting functionality | |
42 | //! needed in those situations. | |
43 | //! 4. *No filesystem:* Encrypted filesystems are hard. Especially in SGX, | |
44 | //! consistency and freshness are big concerns. In this initial version, | |
45 | //! there is no filesystem support, which is fine for most network services, | |
46 | //! which would want to keep their state with a database service anyway. | |
47 | //! Support might be added in the future. | |
48 | //! 5. *Not POSIX:* The POSIX API is huge and contains many elements that are | |
49 | //! not directly supported by the SGX instruction set, such as fork and | |
50 | //! mmap. It is explicitly a non-goal of this specification to support all | |
51 | //! of POSIX. | |
52 | //! 6. *Designed to be portable:* Enclaves don't interact directly with the OS, | |
53 | //! so there should be no need to recompile an enclave when running it with | |
54 | //! a different OS. This specification does not require any particular | |
55 | //! primitives or behavior from the OS. | |
56 | //! | |
57 | //! Like on regular operating systems, there are two types of enclaves: | |
58 | //! *executable*-type and *library*-type. The main difference between the two | |
59 | //! different types is how the enclave may be entered. Once an enclave TCS is | |
60 | //! entered, the different types act virtually identically. More information on | |
61 | //! the two different types, TCSs, and enclave entry may be found in the | |
62 | //! [`entry`](entry/index.html) module. | |
63 | //! | |
64 | //! Once an enclave TCS is entered, it may performs *synchronous usercalls* as | |
65 | //! described in the calling convention. The TCS maintains its execution state | |
66 | //! between doing a usercall and returning from the usercall. Only when the TCS | |
67 | //! exits, either through a non-usercall exit or through the | |
68 | //! [`exit`](struct.Usercalls.html#method.exit) usercall, is the TCS state | |
69 | //! destroyed. This is depicted in the following diagram. | |
70 | //! | |
3dfed10e | 71 | //! ![Enclave execution lifecycle](https://edp.fortanix.com/img/docs/enclave-execution-lifecycle.png) |
0731742a XL |
72 | //! |
73 | //! Enclaves may also perform *asynchronous usercalls*. This is detailed in the | |
74 | //! [`async`](async/index.html) module. Most usercalls can be submitted either | |
75 | //! synchronously or asynchronously. | |
76 | #![allow(unused)] | |
77 | #![no_std] | |
78 | #![cfg_attr(feature = "rustc-dep-of-std", feature(staged_api))] | |
79 | #![cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
3dfed10e XL |
80 | #![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg", |
81 | html_favicon_url = "https://edp.fortanix.com/favicon.ico", | |
82 | html_root_url = "https://edp.fortanix.com/docs/api/")] | |
0731742a XL |
83 | |
84 | use core::ptr::NonNull; | |
f2b60f7d | 85 | use core::sync::atomic::{AtomicU64, AtomicUsize}; |
0731742a XL |
86 | |
87 | macro_rules! invoke_with_abi_spec [ | |
88 | ( $m:ident ) => [ $m![ | |
89 | ||
90 | /// Specification of TCS and entry function requirements. | |
91 | /// | |
92 | /// Once an enclave has called the | |
93 | /// [`exit`](../../struct.Usercalls.html#method.exit) usercall, if userspace | |
94 | /// enters a TCS normally, the enclave must panic. If userspace returns a | |
95 | /// usercall on a TCS, the enclave may decide whether to handle it normally or | |
96 | /// to panic. | |
97 | pub mod entry { | |
98 | /// Specifies the entry points for libraries. | |
99 | /// | |
100 | /// The specification for library support is **experimental** and is | |
101 | /// subject to change. | |
102 | /// | |
103 | /// When a user application wishes to call into the enclave library, | |
104 | /// userspace may use any available TCS. Libraries may keep state between | |
105 | /// invocations, but the library must not assume that subsequent calls will | |
106 | /// go to the same TCS. | |
107 | /// | |
108 | /// The use of asynchronous usercalls with libraries is not recommended, as | |
109 | /// userspace will not be able to wake up the appropriate thread in a | |
110 | /// multi-threaded library scenario. | |
111 | /// | |
112 | /// Automatically launching threads using the | |
113 | /// [`launch_thread`](../../struct.Usercalls.html#method.launch_thread) | |
114 | /// usercall is not supported. Libraries that want to leverage | |
115 | /// multi-threading must rely on application support to call into the | |
116 | /// enclave from different threads. | |
117 | pub mod library { | |
118 | /// The entry point of every TCS. | |
119 | /// | |
120 | /// If a library wishes to expose multiple different functions, it must | |
121 | /// implement this by multiplexing on one of the input parameters. It | |
122 | /// is recommended to use `p1` for this purpose. | |
123 | /// | |
124 | /// The `_ignore` parameter may be set to any value by userspace. The | |
125 | /// value observed by the enclave may be different from the value | |
126 | /// passed by userspace and must therefore be ignored by the enclave. | |
127 | pub fn entry(p1: u64, p2: u64, p3: u64, _ignore: u64, p4: u64, p5: u64) -> (u64, u64) { unimplemented!() } | |
128 | } | |
129 | ||
130 | /// Specifies the entry points for executables. | |
131 | pub mod executable { | |
132 | use ByteBuffer; | |
133 | ||
134 | /// The main entry point of the enclave. This will be the entry point | |
135 | /// of the first TCS. | |
136 | /// | |
137 | /// The enclave must not return from this entry. Instead, it must call | |
138 | /// the [`exit`](../../struct.Usercalls.html#method.exit) usercall. If | |
139 | /// the enclave does return from this TCS, and userspace subsequently | |
140 | /// re-enters this TCS, the enclave must panic. | |
141 | /// | |
142 | /// Arbitrary “command-line arguments” may be passed in from userspace. | |
143 | /// The enclave must ensure that the all buffers pointed to are | |
144 | /// outside the enclave. The enclave should deallocate each | |
145 | /// [`ByteBuffer`] as specified by the type. The enclave should | |
146 | /// deallocate the main buffer by calling | |
147 | /// [`free`]`(args, len * size_of::<ByteBuffer>, 1)`. | |
148 | /// | |
149 | /// [`free`]: ../../struct.Usercalls.html#method.free | |
150 | /// [`ByteBuffer`]: ../../struct.ByteBuffer.html | |
151 | pub fn main_entry(args: *const ByteBuffer, len: usize) -> ! { unimplemented!() } | |
152 | ||
153 | /// The entry point of additional threads of the enclave, for non-first | |
154 | /// TCSs. | |
155 | /// | |
156 | /// When returning from this TCS, userspace may re-enter this TCS after | |
157 | /// another call to [`launch_thread`]. | |
158 | /// | |
159 | /// The enclave must keep track of whether it expects another thread to | |
160 | /// be launched, e.g. by keeping track of how many times it called | |
161 | /// [`launch_thread`]. If a TCS with this entry point is entered even | |
3dfed10e | 162 | /// though the enclave didn't request it, the enclave must panic. |
0731742a XL |
163 | /// |
164 | /// [`launch_thread`]: ../../struct.Usercalls.html#method.launch_thread | |
165 | pub fn thread_entry() { unimplemented!() } | |
166 | } | |
167 | } | |
168 | ||
169 | /// An arbitrary-sized buffer of bytes in userspace, allocated by userspace. | |
170 | /// | |
171 | /// This type is used when userspace may return arbitrary-sized data from a | |
3dfed10e XL |
172 | /// usercall. When reading from the buffer, if `len` is not `0`, the enclave |
173 | /// must ensure the entire buffer is in the user memory range. Once the enclave | |
174 | /// is done with the buffer, it should deallocate the buffer buffer by calling | |
0731742a XL |
175 | /// [`free`]`(data, len, 1)`. |
176 | /// | |
3dfed10e XL |
177 | /// If `len` is `0`, the enclave should ignore `data`. It should not call |
178 | /// `free`. | |
179 | /// | |
0731742a XL |
180 | /// [`free`]: ./struct.Usercalls.html#method.launch_thread |
181 | #[repr(C)] | |
182 | #[derive(Copy, Clone)] | |
183 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
184 | pub struct ByteBuffer { | |
185 | pub data: *const u8, | |
186 | pub len: usize | |
187 | } | |
188 | ||
f2b60f7d FG |
189 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] |
190 | unsafe impl Send for ByteBuffer {} | |
191 | ||
0731742a XL |
192 | /// Error code definitions and space allocation. |
193 | /// | |
194 | /// Only non-zero positive values are valid errors. The variants are designed | |
195 | /// to map to [std::io::ErrorKind]. See the source for the value mapping. | |
196 | /// | |
197 | /// [std::io::ErrorKind]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html | |
198 | #[repr(i32)] | |
199 | #[derive(Copy, Clone, Eq, PartialEq)] | |
200 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
201 | pub enum Error { | |
202 | PermissionDenied = 0x01, | |
203 | NotFound = 0x02, | |
204 | Interrupted = 0x04, | |
205 | WouldBlock = 0x0b, | |
206 | AlreadyExists = 0x11, | |
207 | InvalidInput = 0x16, | |
208 | BrokenPipe = 0x20, | |
209 | AddrInUse = 0x62, | |
210 | AddrNotAvailable = 0x63, | |
211 | ConnectionAborted = 0x67, | |
212 | ConnectionReset = 0x68, | |
213 | NotConnected = 0x6b, | |
214 | TimedOut = 0x6e, | |
215 | ConnectionRefused = 0x6f, | |
216 | InvalidData = 0x2000_0000, | |
217 | WriteZero = 0x2000_0001, | |
218 | UnexpectedEof = 0x2000_0002, | |
219 | /// This value is reserved for `Other`, but all undefined values also map | |
220 | /// to `Other`. | |
221 | Other = 0x3fff_ffff, | |
222 | /// Start of the range of values reserved for user-defined errors. | |
223 | UserRangeStart = 0x4000_0000, | |
224 | /// End (inclusive) of the range of values reserved for user-defined errors. | |
225 | UserRangeEnd = 0x7fff_ffff, | |
226 | } | |
227 | ||
f2b60f7d | 228 | /// A value indicating that the operation was successful. |
0731742a XL |
229 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] |
230 | pub const RESULT_SUCCESS: Result = 0; | |
231 | ||
232 | /// The first return value of usercalls that might fail. | |
233 | /// | |
234 | /// [`RESULT_SUCCESS`](constant.RESULT_SUCCESS.html) or an error code from the | |
235 | /// [`Error`](enum.Error.html) type. | |
236 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
237 | pub type Result = i32; | |
238 | ||
239 | /// The list of all usercalls. | |
240 | /// | |
241 | /// *This is not a real structure, it's just a convenient way to group | |
242 | /// functions with `rustdoc`.* | |
243 | /// | |
244 | /// The usercall number is passed in the first register. Up to 4 arguments may | |
245 | /// be passed in the remaining registers. Unspecified arguments and return | |
246 | /// values must be 0. Userspace must check the arguments and the enclave must | |
247 | /// check the return value. | |
248 | /// | |
249 | /// The usercall number may be one of the predefined numbers associated with | |
250 | /// one of the usercalls defined below, or, if bit | |
251 | /// [`USERCALL_USER_DEFINED`](constant.USERCALL_USER_DEFINED.html) is set, an | |
252 | /// otherwise arbitrary number with an application-defined meaning. | |
253 | /// | |
254 | /// Raw pointers must always point to user memory. When receiving raw pointers | |
255 | /// from userspace, the enclave must verify that the entire pointed-to memory | |
256 | /// space is outside the enclave memory range. It must then copy all data in | |
257 | /// user memory to enclave memory before operating on it. | |
258 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
259 | pub struct Usercalls; | |
260 | ||
261 | /// Usercall numbers with this bit set will never be defined by this specification. | |
262 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
263 | pub const USERCALL_USER_DEFINED: u64 = 0x8000_0000; | |
264 | ||
265 | /// A file descriptor. | |
266 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
267 | pub type Fd = u64; | |
268 | ||
269 | /// Standard input file descriptor. Input read this way is not secure. | |
270 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
271 | pub const FD_STDIN: Fd = 0; | |
272 | /// Standard output file descriptor. This is not a secure output channel. | |
273 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
274 | pub const FD_STDOUT: Fd = 1; | |
275 | /// Standard error file descriptor. This is not a secure output channel. | |
276 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
277 | pub const FD_STDERR: Fd = 2; | |
278 | ||
279 | /// # Streams | |
280 | /// | |
281 | /// The enclave must not assume anything about data read or written using these | |
282 | /// calls. Data written may be piped directly to `/dev/null` by userspace, or | |
283 | /// it may be published in the local newspaper. Similarly, data read may be | |
284 | /// arbitrarily deleted, inserted, or changed. The enclave must use additional | |
285 | /// security primitives such as sealing or TLS to obtain stronger guarantees. | |
286 | /// | |
287 | /// When a stream is read from by multiple threads simultaneously, the read | |
288 | /// calls may be serialized in any order. This means the data returned by a | |
289 | /// read call appeared that way in the stream, and every single byte in the | |
290 | /// stream will be read and be read only once. However, the order in which all | |
291 | /// stream data is returned is not defined. The same applies when | |
292 | /// simultaneously submitting multiple read calls for the same stream | |
293 | /// asynchronously. This all applies similarly to writing to a stream. | |
294 | /// | |
295 | /// To make sure to be able to re-assemble the stream, the enclave can take one | |
296 | /// of the following approaches: | |
297 | /// | |
298 | /// 1. Submit all read calls to a single stream on a single thread. | |
299 | /// 2. Serializing read calls by synchronizing access to a single stream. | |
300 | /// | |
301 | /// In addition, the enclave should use cryptographic integrity protection of | |
302 | /// the stream data to ensure the stream data has not been tampered with. | |
303 | impl Usercalls { | |
304 | /// Read up to `len` bytes from stream `fd`. | |
305 | /// | |
306 | /// `buf` must point to a buffer in userspace with a size of at least | |
f2b60f7d | 307 | /// `len`. On a successful return, the number of bytes written is returned. |
0731742a | 308 | /// The enclave must check that the returned length is no more than `len`. |
3dfed10e XL |
309 | /// If `len` is `0`, this call should block until the stream is ready for |
310 | /// reading. If `len` is `0` or end of stream is reached, `0` may be | |
311 | /// returned. | |
0731742a XL |
312 | /// |
313 | /// The enclave may mix calls to [`read`](#method.read) and | |
314 | /// [`read_alloc`](#method.read_alloc). | |
315 | pub fn read(fd: Fd, buf: *mut u8, len: usize) -> (Result, usize) { unimplemented!() } | |
316 | ||
317 | /// Read some data from stream `fd`, letting the callee choose the amount. | |
318 | /// | |
319 | /// `buf` must point to a [`ByteBuffer`] in userspace, and `buf.data` must | |
320 | /// contain `null`. On success, userspace will allocate memory for the read | |
321 | /// data and populate `ByteBuffer` appropriately. The enclave must handle | |
322 | /// and deallocate the buffer according to the `ByteBuffer` documentation. | |
323 | /// | |
324 | /// Since every read operation using this usercall requires two usercalls, | |
325 | /// it is recommended to only call this usercall asynchronously. | |
326 | /// | |
327 | /// The enclave may mix calls to [`read`](#method.read) and | |
328 | /// [`read_alloc`](#method.read_alloc). | |
329 | /// | |
330 | /// [`ByteBuffer`]: ./struct.ByteBuffer.html | |
331 | pub fn read_alloc(fd: Fd, buf: *mut ByteBuffer) -> Result { unimplemented!() } | |
332 | ||
333 | /// Write up to `len` bytes to stream `fd`. | |
334 | /// | |
335 | /// `buf` must point to a buffer in userspace with a size of at least | |
f2b60f7d | 336 | /// `len`. On a successful return, the number of bytes written is returned. |
0731742a | 337 | /// The enclave must check that the returned length is no more than `len`. |
3dfed10e XL |
338 | /// If `len` is `0`, this call should block until the stream is ready for |
339 | /// writing. If `len` is `0` or the stream is closed, `0` may be returned. | |
0731742a XL |
340 | pub fn write(fd: Fd, buf: *const u8, len: usize) -> (Result, usize) { unimplemented!() } |
341 | ||
342 | /// Flush stream `fd`, ensuring that all intermediately buffered contents | |
343 | /// reach their destination. | |
344 | pub fn flush(fd: Fd) -> Result { unimplemented!() } | |
345 | ||
346 | /// Close stream `fd`. | |
347 | /// | |
348 | /// Once the stream is closed, no further data may be read or written. | |
349 | /// Userspace may reuse the `fd` in the future for a different stream. | |
350 | pub fn close(fd: Fd) { unimplemented!() } | |
351 | } | |
352 | ||
353 | /// # Networking | |
354 | /// | |
355 | /// In keeping with the design goals for this specification, the | |
356 | /// networking/socket interface doesn't use `sockaddr` types and doesn't | |
357 | /// have a separate API for name resolution. Userspace can't be trusted to | |
358 | /// do name resolution correctly, and even if it did, *userspace can't be | |
359 | /// trusted to actually connect streams to the correct address* specified by | |
360 | /// the enclave. Therefore, addresses specified should merely be treated as a | |
361 | /// suggestion, and additional measures must be taken by an enclave to verify | |
362 | /// the stream is connected to the correct peer, e.g. TLS. | |
363 | /// | |
364 | /// The networking API works with strings as addresses. All byte buffers | |
365 | /// representing network addresses should contain a valid UTF-8 string. The | |
366 | /// enclave should panic if it is passed an invalid string by userspace. It is | |
367 | /// suggested that userspace supports at least the following notations: | |
368 | /// | |
369 | /// * `hostname:port-number` (e.g. `example.com:123`) | |
370 | /// * `dotted-octet-ipv4-address:port-number` (e.g. `192.0.2.1:123`) | |
371 | /// * `[ipv6-address]:port-number` (e.g. `[2001:db8::1]:123`) | |
372 | /// | |
373 | /// Additionally, other forms may be accepted, for example service names: | |
374 | /// | |
375 | /// * `fully-qualified-service-name` (e.g. `_example._tcp.example.com`) | |
376 | /// * `address:service-name` (e.g. `address:example`) | |
377 | /// | |
378 | /// # Errors | |
379 | /// | |
380 | /// Networking calls taking an address may return the [`InvalidInput`] error if | |
381 | /// the address could not be interpreted by userspace. | |
382 | /// | |
383 | /// [`InvalidInput`]: enum.Error.html#variant.InvalidInput | |
384 | impl Usercalls { | |
385 | /// Setup a listening socket. | |
386 | /// | |
387 | /// The socket is bound to the address specified in `addr`. `addr` must be | |
388 | /// a buffer in user memory with a size of at least `len`. | |
389 | /// | |
390 | /// On success, a file descriptor is returned which may be passed to | |
391 | /// [`accept_stream`](#method.accept_stream) or [`close`](#method.close). | |
392 | /// | |
393 | /// The enclave may optionally request the local socket address be returned | |
394 | /// in `local_addr`. On success, if `local_addr` is not NULL, userspace | |
395 | /// will allocate memory for the address and populate [`ByteBuffer`] | |
396 | /// appropriately. The enclave must handle and deallocate the buffer | |
397 | /// according to the `ByteBuffer` documentation. | |
398 | /// | |
399 | /// The enclave must not make any security decisions based on the local | |
400 | /// address received. | |
401 | /// | |
402 | /// [`ByteBuffer`]: ./struct.ByteBuffer.html | |
403 | pub fn bind_stream(addr: *const u8, len: usize, local_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() } | |
404 | ||
405 | /// Accept a new connection from a listening socket. | |
406 | /// | |
407 | /// `fd` should be a file descriptor previously returned from | |
408 | /// [`bind_stream`](#method.bind_stream). | |
409 | /// | |
410 | /// The enclave may optionally request the local or peer socket addresses | |
411 | /// be returned in `local_addr` or `peer_addr`, respectively. On success, | |
412 | /// if `local_addr` and/or `peer_addr` is not NULL, userspace will allocate | |
413 | /// memory for the address and populate the respective [`ByteBuffer`] | |
414 | /// appropriately. | |
415 | /// | |
416 | /// The enclave must handle and deallocate each buffer according to the | |
417 | /// `ByteBuffer` documentation. | |
418 | /// | |
419 | /// The enclave must not make any security decisions based on the local or | |
420 | /// peer address received. | |
421 | /// | |
422 | /// [`ByteBuffer`]: ./struct.ByteBuffer.html | |
423 | pub fn accept_stream(fd: Fd, local_addr: *mut ByteBuffer, peer_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() } | |
424 | ||
425 | /// Create a new stream connection to the specified address. | |
426 | /// | |
427 | /// The enclave may optionally request the local or peer socket addresses | |
428 | /// be returned in `local_addr` or `peer_addr`, respectively. On success, | |
429 | /// if `local_addr` and/or `peer_addr` is not NULL, userspace will allocate | |
430 | /// memory for the address and populate the respective [`ByteBuffer`] | |
431 | /// appropriately. | |
432 | /// | |
433 | /// The enclave must handle and deallocate each buffer according to the | |
434 | /// `ByteBuffer` documentation. | |
435 | /// | |
436 | /// The enclave must not make any security decisions based on the local or | |
437 | /// peer address received. | |
438 | /// | |
439 | /// [`ByteBuffer`]: ./struct.ByteBuffer.html | |
440 | pub fn connect_stream(addr: *const u8, len: usize, local_addr: *mut ByteBuffer, peer_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() } | |
441 | } | |
442 | ||
443 | /// The absolute address of a TCS in the current enclave. | |
444 | // FIXME: `u8` should be some `extern type` instead. | |
445 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
446 | pub type Tcs = NonNull<u8>; | |
447 | ||
448 | /// An event that will be triggered by userspace when the usercall queue is not | |
449 | /// or no longer full. | |
450 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
451 | pub const EV_USERCALLQ_NOT_FULL: u64 = 0b0000_0000_0000_0001; | |
452 | /// An event that will be triggered by userspace when the return queue is not | |
453 | /// or no longer empty. | |
454 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
455 | pub const EV_RETURNQ_NOT_EMPTY: u64 = 0b0000_0000_0000_0010; | |
456 | /// An event that enclaves can use for synchronization. | |
457 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
458 | pub const EV_UNPARK: u64 = 0b0000_0000_0000_0100; | |
f2b60f7d FG |
459 | /// An event that will be triggered by userspace when the cancel queue is not |
460 | /// or no longer full. | |
461 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
462 | pub const EV_CANCELQ_NOT_FULL: u64 = 0b0000_0000_0000_1000; | |
0731742a XL |
463 | |
464 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
465 | pub const WAIT_NO: u64 = 0; | |
466 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
467 | pub const WAIT_INDEFINITE: u64 = !0; | |
468 | ||
469 | /// # Execution control | |
470 | /// | |
471 | /// ## TCS event queues | |
472 | /// | |
473 | /// Userspace will maintain a queue for each running TCS with events to be | |
f2b60f7d FG |
474 | /// delivered. Each event is characterized by a bitset with at least one bit |
475 | /// set. Userspace or the enclave (using the `send` usercall) can put events on | |
476 | /// this queue. | |
477 | /// If the enclave isn't waiting for an event when an event is queued, the event | |
0731742a XL |
478 | /// remains on the queue until it delivered to the enclave in a later `wait` |
479 | /// usercall. If an enclave is waiting for an event, and the queue contains an | |
480 | /// event that is a subset of the waited-for event mask, that event is removed | |
481 | /// from the queue and execution control is returned to the enclave. | |
482 | /// | |
483 | /// Events not defined in this specification should not be generated. | |
484 | impl Usercalls { | |
485 | /// In [executables](entry/executable/index.html), this will instruct | |
486 | /// userspace to enter another TCS in another thread. This TCS should have | |
487 | /// the [`thread_entry`] entrypoint. As documented in [`thread_entry`], the | |
488 | /// enclave should keep track of how many threads it launched and reconcile | |
489 | /// this with the number of entries into [`thread_entry`]. If no free TCSes | |
490 | /// are immediately available, this may return an error. | |
491 | /// | |
f2b60f7d | 492 | /// This function will never be successful in [libraries]. See the |
0731742a XL |
493 | /// [`library`] documentation on how to use threads with libraries. |
494 | /// | |
495 | /// [`thread_entry`]: entry/executable/fn.thread_entry.html | |
496 | /// [libraries]: entry/library/index.html | |
497 | /// [`library`]: entry/library/index.html | |
498 | pub fn launch_thread() -> Result { unimplemented!() } | |
499 | ||
500 | /// Signals to userspace that this enclave needs to be destroyed. | |
501 | /// | |
502 | /// The enclave must not rely on userspace to terminate other threads still | |
503 | /// running. Similarly, the enclave must not trust that it will no longer | |
504 | /// be entered by userspace, and it must safeguard against that in the | |
505 | /// entrypoints. | |
506 | /// | |
507 | /// If `panic` is set to `true`, the enclave has exited due to a panic | |
508 | /// condition. If the enclave was running in debug mode, the enclave may | |
509 | /// have output a debug message according to the calling convention. | |
510 | pub fn exit(panic: bool) -> ! { unimplemented!() } | |
511 | ||
512 | /// Wait for an event to occur, or check if an event is currently pending. | |
513 | /// | |
f2b60f7d FG |
514 | /// `timeout` must be [`WAIT_NO`] or [`WAIT_INDEFINITE`] or a positive |
515 | /// value smaller than u64::MAX specifying number of nanoseconds to wait. | |
0731742a XL |
516 | /// |
517 | /// If `timeout` is [`WAIT_INDEFINITE`], this call will block and return | |
518 | /// once a matching event is queued on this TCS. If `timeout` is | |
519 | /// [`WAIT_NO`], this call will return immediately, and the return value | |
520 | /// will indicate if an event was pending. If it was, it has been dequeued. | |
f2b60f7d FG |
521 | /// If not, the [`WouldBlock`] error value will be returned. If `timeout` |
522 | /// is a value other than [`WAIT_NO`] and [`WAIT_INDEFINITE`], this call | |
523 | /// will block until either a matching event is queued in which case the | |
524 | /// return value will indicate the event, or the timeout is reached in | |
525 | /// which case the [`TimedOut`] error value will be returned. | |
0731742a XL |
526 | /// |
527 | /// A matching event is one whose bits are equal to or a subset of | |
528 | /// `event_mask`. If `event_mask` is `0`, this call will never return due | |
529 | /// to an event. If `timeout` is also [`WAIT_INDEFINITE`], this call will | |
530 | /// simply never return. | |
531 | /// | |
532 | /// Enclaves must not assume that this call only returns in response to | |
533 | /// valid events generated by the enclave. This call may return for invalid | |
534 | /// event sets, or before `timeout` has expired even though no event is | |
535 | /// pending. | |
536 | /// | |
537 | /// When executed synchronously, this gives userspace an opportunity to | |
538 | /// schedule something else in a cooperative multitasking environment. | |
539 | /// | |
540 | /// When executed asynchronously, this may trigger an | |
541 | /// [`EV_RETURNQ_NOT_EMPTY`] event on this or other TCSes. It is not | |
542 | /// recommended to execute this call asynchronously with a `timeout` value | |
543 | /// other than [`WAIT_NO`]. | |
544 | /// | |
545 | /// [`WAIT_NO`]: constant.WAIT_NO.html | |
546 | /// [`WAIT_INDEFINITE`]: constant.WAIT_INDEFINITE.html | |
547 | /// [`EV_RETURNQ_NOT_EMPTY`]: constant.EV_RETURNQ_NOT_EMPTY.html | |
548 | /// [`WouldBlock`]: enum.Error.html#variant.WouldBlock | |
f2b60f7d | 549 | /// [`TimedOut`]: enum.Error.html#variant.TimedOut |
0731742a XL |
550 | pub fn wait(event_mask: u64, timeout: u64) -> (Result, u64) { unimplemented!() } |
551 | ||
552 | /// Send an event to one or all TCSes. | |
553 | /// | |
554 | /// If `tcs` is `None`, send the event `event_set` to all TCSes of this | |
555 | /// enclave, otherwise, send it to the TCS specified in `tcs`. | |
556 | /// | |
557 | /// # Error | |
558 | /// | |
559 | /// This will return the [`InvalidInput`] error if `tcs` is set but doesn't | |
560 | /// specify a valid TCS address. | |
561 | /// | |
562 | /// [`InvalidInput`]: enum.Error.html#variant.InvalidInput | |
563 | pub fn send(event_set: u64, tcs: Option<Tcs>) -> Result { unimplemented!() } | |
564 | } | |
565 | ||
566 | /// # Miscellaneous | |
567 | impl Usercalls { | |
568 | /// This returns the number of nanoseconds since midnight UTC on January 1, | |
569 | /// 1970\. The enclave must not rely on the accuracy of this time for | |
570 | /// security purposes, such as checking credential expiry or preventing | |
571 | /// rollback. | |
572 | pub fn insecure_time() -> u64 { unimplemented!() } | |
573 | } | |
574 | ||
575 | /// # Memory | |
576 | /// | |
577 | /// The enclave must not use any memory outside the enclave, except for memory | |
578 | /// explicitly returned from usercalls. You can obtain arbitrary memory in | |
579 | /// userspace using [`alloc`](#method.alloc). | |
580 | impl Usercalls { | |
581 | /// Request user memory. | |
582 | /// | |
583 | /// Request an allocation in user memory of size `size` and with alignment | |
f2b60f7d | 584 | /// `align`. If successful, a pointer to this memory will be returned. The |
0731742a XL |
585 | /// enclave must check the pointer is correctly aligned and that the entire |
586 | /// range of memory pointed to is outside the enclave. | |
3dfed10e XL |
587 | /// |
588 | /// It is an error to call this function with `size` equal to `0`. | |
0731742a XL |
589 | pub fn alloc(size: usize, alignment: usize) -> (Result, *mut u8) { unimplemented!() } |
590 | ||
591 | /// Free user memory. | |
592 | /// | |
593 | /// This must be called to deallocate memory in userspace. The pointer | |
594 | /// `ptr` must have previously been returned by a usercall. The `size` and | |
595 | /// `alignment` specified must exactly match what was allocated. This | |
596 | /// function must be called exactly once for each user memory buffer. | |
3dfed10e XL |
597 | /// |
598 | /// Calling this function with `size` equal to `0` is a no-op. | |
0731742a XL |
599 | pub fn free(ptr: *mut u8, size: usize, alignment: usize) { unimplemented!() } |
600 | } | |
601 | ||
602 | /// Asynchronous usercall specification. | |
603 | /// | |
604 | /// An asynchronous usercall allows an enclave to submit a usercall without | |
f2b60f7d | 605 | /// exiting the enclave. This is necessary since enclave entries and exits are |
0731742a XL |
606 | /// slow (see academic work on [SCONE], [HotCalls]). In addition, the enclave |
607 | /// can perform other tasks while it waits for the usercall to complete. Those | |
608 | /// tasks may include issuing other usercalls, either synchronously or | |
609 | /// asynchronously. | |
610 | /// | |
611 | /// Two [MPSC queues] are [allocated per enclave]. One queue is used by any | |
612 | /// enclave thread to submit usercalls to userspace. Userspace will read the | |
613 | /// calls from this queue and handle them. Another queue is used by userspace | |
614 | /// to return completed usercalls to the enclave. | |
615 | /// | |
616 | /// Each call is identified by an enclave-specified `id`. Userspace must | |
617 | /// provide the same `id` when returning. The enclave must not submit multiple | |
618 | /// concurrent usercalls with the same `id`, but it may reuse an `id` once the | |
619 | /// original usercall with that `id` has returned. | |
620 | /// | |
f2b60f7d FG |
621 | /// An optional third queue can be used to cancel usercalls. To cancel an async |
622 | /// usercall, the enclave should send the usercall's id and number on this | |
623 | /// queue. If the usercall has already been processed, the enclave may still | |
624 | /// receive a successful result for the usercall. Otherwise, the userspace will | |
625 | /// cancel the usercall's execution and return an [`Interrupted`] error on the | |
626 | /// return queue to notify the enclave of the cancellation. Note that usercalls | |
627 | /// that do not return [`Result`] cannot be cancelled and if the enclave sends | |
628 | /// a cancellation for such a usercall, the userspace should simply ignore it. | |
629 | /// Additionally, userspace may choose to ignore cancellations for non-blocking | |
630 | /// usercalls. Userspace should be able to cancel a usercall that has been sent | |
631 | /// by the enclave but not yet received by the userspace, i.e. if cancellation | |
632 | /// is received before the usercall itself. To avoid keeping such cancellations | |
633 | /// forever and preventing the enclave from re-using usercall ids, userspace | |
634 | /// should synchronize cancel queue with the usercall queue such that the | |
635 | /// following invariant is maintained: whenever the enclave writes an id to the | |
636 | /// usercall or cancel queue, the enclave will not reuse that id until the | |
637 | /// usercall queue's read pointer has advanced to the write pointer at the time | |
638 | /// the id was written. | |
639 | /// | |
0731742a XL |
640 | /// *TODO*: Add diagram. |
641 | /// | |
642 | /// [MPSC queues]: struct.FifoDescriptor.html | |
643 | /// [allocated per enclave]: ../struct.Usercalls.html#method.async_queues | |
644 | /// [SCONE]: https://www.usenix.org/conference/osdi16/technical-sessions/presentation/arnautov | |
645 | /// [HotCalls]: http://www.ofirweisse.com/ISCA17_Ofir_Weisse.pdf | |
f2b60f7d FG |
646 | /// [`Interrupted`]: enum.Error.html#variant.Interrupted |
647 | /// [`Result`]: type.Result.html | |
0731742a XL |
648 | /// |
649 | /// # Enclave/userspace synchronization | |
650 | /// | |
651 | /// When the enclave needs to wait on a queue, it executes the [`wait()`] | |
652 | /// usercall synchronously, specifying [`EV_USERCALLQ_NOT_FULL`], | |
f2b60f7d FG |
653 | /// [`EV_RETURNQ_NOT_EMPTY`], [`EV_CANCELQ_NOT_FULL`], or any combination |
654 | /// thereof in the `event_mask`. Userspace will wake | |
0731742a XL |
655 | /// any or all threads waiting on the appropriate event when it is triggered. |
656 | /// | |
657 | /// When userspace needs to wait on a queue, it will park the current thread | |
658 | /// (or do whatever else is appropriate for the synchronization model currently | |
659 | /// in use by userspace). Any synchronous usercall will wake the blocked thread | |
660 | /// (or otherwise signal that either queue is ready). | |
661 | /// | |
662 | /// [`wait()`]: ../struct.Usercalls.html#method.wait | |
663 | /// [`EV_USERCALLQ_NOT_FULL`]: ../constant.EV_USERCALLQ_NOT_FULL.html | |
664 | /// [`EV_RETURNQ_NOT_EMPTY`]: ../constant.EV_RETURNQ_NOT_EMPTY.html | |
f2b60f7d | 665 | /// [`EV_CANCELQ_NOT_FULL`]: ../constant.EV_CANCELQ_NOT_FULL.html |
0731742a XL |
666 | pub mod async { |
667 | use super::*; | |
f2b60f7d | 668 | use core::sync::atomic::{AtomicU64, AtomicUsize}; |
0731742a | 669 | |
0731742a | 670 | #[repr(C)] |
0731742a | 671 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] |
f2b60f7d FG |
672 | pub struct WithId<T> { |
673 | pub id: AtomicU64, | |
674 | pub data: T, | |
675 | } | |
676 | ||
677 | /// A usercall. | |
678 | /// The elements correspond to the RDI, RSI, RDX, R8, and R9 registers | |
679 | /// in the synchronous calling convention. | |
680 | #[repr(C)] | |
681 | #[derive(Copy, Clone, Default)] | |
682 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
683 | pub struct Usercall(pub u64, pub u64, pub u64, pub u64, pub u64); | |
684 | ||
685 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
686 | impl From<Usercall> for (u64, u64, u64, u64, u64) { | |
687 | fn from(u: Usercall) -> Self { | |
688 | let Usercall(p1, p2, p3, p4, p5) = u; | |
689 | (p1, p2, p3, p4, p5) | |
690 | } | |
691 | } | |
692 | ||
693 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
694 | impl From<(u64, u64, u64, u64, u64)> for Usercall { | |
695 | fn from(p: (u64, u64, u64, u64, u64)) -> Self { | |
696 | Usercall(p.0, p.1, p.2, p.3, p.4) | |
697 | } | |
0731742a XL |
698 | } |
699 | ||
f2b60f7d FG |
700 | /// The return value of a usercall. |
701 | /// The elements correspond to the RSI and RDX registers in the | |
702 | /// synchronous calling convention. | |
0731742a | 703 | #[repr(C)] |
f2b60f7d | 704 | #[derive(Copy, Clone, Default)] |
0731742a | 705 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] |
f2b60f7d FG |
706 | pub struct Return(pub u64, pub u64); |
707 | ||
708 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
709 | impl From<Return> for (u64, u64) { | |
710 | fn from(r: Return) -> Self { | |
711 | let Return(r1, r2) = r; | |
712 | (r1, r2) | |
713 | } | |
0731742a XL |
714 | } |
715 | ||
f2b60f7d FG |
716 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] |
717 | impl From<(u64, u64)> for Return { | |
718 | fn from(r: (u64, u64)) -> Self { | |
719 | Return(r.0, r.1) | |
720 | } | |
721 | } | |
722 | ||
723 | /// Cancel a usercall previously sent to userspace. | |
724 | #[repr(C)] | |
725 | #[derive(Copy, Clone, Default)] | |
726 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
727 | pub struct Cancel; | |
728 | ||
0731742a XL |
729 | /// A circular buffer used as a FIFO queue with atomic reads and writes. |
730 | /// | |
731 | /// The read offset is the element that was most recently read by the | |
732 | /// receiving end of the queue. The write offset is the element that was | |
733 | /// most recently written by the sending end. If the two offsets are equal, | |
734 | /// the queue is either empty or full. | |
735 | /// | |
736 | /// The size of the buffer is such that not all the bits of the offset are | |
737 | /// necessary to encode the current offset. The next highest unused bit is | |
738 | /// used to keep track of the number of times the offset has wrapped | |
739 | /// around. If the offsets are the same and the bit is the same in the read | |
740 | /// and write offsets, the queue is empty. If the bit is different in the | |
741 | /// read and write offsets, the queue is full. | |
742 | /// | |
743 | /// The following procedures will operate the queues in a multiple producer | |
744 | /// single consumer (MPSC) fashion. | |
745 | /// | |
746 | /// ## Push operation | |
747 | /// | |
748 | /// To push an element onto the queue: | |
749 | /// | |
750 | /// 1. Load the current offsets. | |
751 | /// 2. If the queue is full, wait, then go to step 1. | |
752 | /// 3. Add 1 to the write offset and do an atomic compare-and-swap (CAS) | |
f2b60f7d | 753 | /// with the current offsets. If the CAS was not successful, go to step |
0731742a XL |
754 | /// 1\. |
755 | /// 4. Write the data, then the `id`. | |
756 | /// 5. If the queue was empty in step 1, signal the reader to wake up. | |
757 | /// | |
758 | /// ## Pop operation | |
759 | /// | |
760 | /// To pop an element off the queue: | |
761 | /// | |
762 | /// 1. Load the current offsets. | |
763 | /// 2. If the queue is empty, wait, then go to step 1. | |
764 | /// 3. Add 1 to the read offset. | |
765 | /// 4. Read the `id` at the new read offset. | |
766 | /// 5. If `id` is `0`, go to step 4 (spin). Spinning is OK because data is | |
767 | /// expected to be written imminently. | |
768 | /// 6. Read the data, then store `0` in the `id`. | |
f2b60f7d FG |
769 | /// 7. Store the new read offset, retrieving the old offsets. |
770 | /// 8. If the queue was full before step 7, signal the writer to wake up. | |
0731742a XL |
771 | #[repr(C)] |
772 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
773 | pub struct FifoDescriptor<T> { | |
774 | /// Pointer to the queue memory. Must have a size of | |
f2b60f7d FG |
775 | /// `len * size_of::<WithId<T>>()` bytes and have alignment |
776 | /// `align_of::<WithId<T>>`. | |
777 | pub data: *mut WithId<T>, | |
0731742a XL |
778 | /// The number of elements pointed to by `data`. Must be a power of two |
779 | /// less than or equal to 2³¹. | |
780 | pub len: usize, | |
781 | /// Actually a `(u32, u32)` tuple, aligned to allow atomic operations | |
782 | /// on both halves simultaneously. The first element (low dword) is | |
783 | /// the read offset and the second element (high dword) is the write | |
784 | /// offset. | |
785 | pub offsets: *const AtomicUsize, | |
786 | } | |
787 | ||
788 | // not using `#[derive]` because that would require T: Clone | |
789 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
790 | impl<T> Clone for FifoDescriptor<T> { | |
791 | fn clone(&self) -> Self { | |
792 | *self | |
793 | } | |
794 | } | |
795 | ||
796 | // not using `#[derive]` because that would require T: Copy | |
797 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
798 | impl<T> Copy for FifoDescriptor<T> {} | |
799 | ||
800 | /// # Asynchronous usercalls | |
801 | /// | |
802 | /// *Due to `rustdoc`, this section may appear at the top of the | |
803 | /// `Usercalls` documentation. You might want to read the other sections | |
804 | /// first and then come back to this one.* | |
805 | /// | |
806 | /// See also the [`async` module](async/index.html) documentation. | |
807 | impl Usercalls { | |
808 | /// Request FIFO queues for asynchronous usercalls. `usercall_queue` | |
809 | /// and `return_queue` must point to valid user memory with the correct | |
f2b60f7d FG |
810 | /// size and alignment for their types. `cancel_queue` is optional, but |
811 | /// if specified (not null) it must point to valid user memory with | |
812 | /// correct size and alignment. | |
813 | /// On return, userspace will have filled these structures with | |
814 | /// information about the queues. A single set of queues will be | |
815 | /// allocated per enclave. Once this usercall has returned successfully, | |
816 | /// calling this usercall again is equivalent to calling `exit(true)`. | |
0731742a XL |
817 | /// |
818 | /// May fail if the platform does not support asynchronous usercalls. | |
819 | /// | |
820 | /// The enclave must ensure that the data pointed to in the fields of | |
821 | /// [`FifoDescriptor`] is outside the enclave. | |
822 | /// | |
823 | /// [`FifoDescriptor`]: async/struct.FifoDescriptor.html | |
f2b60f7d FG |
824 | pub fn async_queues( |
825 | usercall_queue: *mut FifoDescriptor<Usercall>, | |
826 | return_queue: *mut FifoDescriptor<Return>, | |
827 | cancel_queue: *mut FifoDescriptor<Cancel> | |
828 | ) -> Result { unimplemented!() } | |
0731742a XL |
829 | } |
830 | } | |
831 | ||
832 | ]; ] ]; | |
833 | ||
834 | // docs: Just render the docs verbatim | |
835 | macro_rules! docs { | |
836 | ($($tt:tt)*) => ($($tt)*) | |
837 | } | |
838 | ||
839 | #[cfg(feature = "docs")] | |
840 | invoke_with_abi_spec!(docs); | |
841 | ||
842 | // types: flatten the module structure and ignore any items that are not types. | |
843 | macro_rules! types { | |
844 | // flatten modules | |
845 | ($(#[$meta:meta])* pub mod $modname:ident { $($contents:tt)* } $($remainder:tt)*) => | |
846 | { types!($($contents)*); types!($($remainder)*); }; | |
847 | // ignore impls | |
848 | ($(#[$meta:meta])* impl Usercalls { $($contents:tt)* } $($remainder:tt)* ) => | |
849 | { types!($($remainder)*); }; | |
850 | // ignore `struct Usercalls` | |
851 | ($(#[$meta:meta])* pub struct Usercalls; $($remainder:tt)* ) => | |
852 | { types!($($remainder)*); }; | |
853 | // ignore free functions | |
854 | ($(#[$meta:meta])* pub fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)* { unimplemented!() } $($remainder:tt)* ) => | |
855 | { types!($($remainder)*); }; | |
856 | // ignore use statements | |
857 | (use $($tt:tt)::*; $($remainder:tt)* ) => | |
858 | { types!($($remainder)*); }; | |
859 | // copy all other items verbatim | |
860 | ($item:item $($remainder:tt)*) => | |
861 | { $item types!($($remainder)*); }; | |
862 | () => {}; | |
863 | } | |
864 | ||
865 | #[cfg(not(feature = "docs"))] | |
866 | invoke_with_abi_spec!(types); | |
867 | ||
868 | // Define a macro that will call a second macro providing the list of all | |
869 | // function declarations inside all `impl Usercalls` blocks. | |
870 | macro_rules! define_invoke_with_usercalls { | |
871 | // collect all usercall function declarations in a list | |
872 | (@ [$($accumulated:tt)*] $(#[$meta1:meta])* impl Usercalls { $($(#[$meta2:meta])* pub fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)* { unimplemented!() } )* } $($remainder:tt)* ) => | |
873 | { define_invoke_with_usercalls!(@ [$($accumulated)* $(fn $f($($n: $t),*) $(-> $r)*;)*] $($remainder)*); }; | |
874 | // visit modules | |
875 | (@ $accumulated:tt $(#[$meta:meta])* pub mod $modname:ident { $($contents:tt)* } $($remainder:tt)*) => | |
876 | { define_invoke_with_usercalls!(@ $accumulated $($contents)* $($remainder)*); }; | |
877 | // ignore all other items | |
878 | (@ $accumulated:tt $item:item $($remainder:tt)*) => | |
879 | { define_invoke_with_usercalls!(@ $accumulated $($remainder)*); }; | |
880 | // Define the macro | |
881 | (@ $accumulated:tt) => { | |
882 | /// Call the macro `$m`, passing a semicolon-separated list of usercall | |
883 | /// function declarations. | |
884 | /// | |
885 | /// The passed in macro could for example use the following pattern: | |
886 | /// | |
887 | /// ```text | |
888 | /// ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*) | |
889 | /// ``` | |
890 | #[macro_export] | |
891 | #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] | |
892 | macro_rules! invoke_with_usercalls { | |
893 | ($m:ident) => { $m! $accumulated; } | |
894 | } | |
895 | }; | |
896 | // start collection with an empty list | |
897 | ($($tt:tt)*) => { | |
898 | define_invoke_with_usercalls!(@ [] $($tt)*); | |
899 | } | |
900 | } | |
901 | ||
902 | #[cfg(not(feature = "docs"))] | |
903 | invoke_with_abi_spec!(define_invoke_with_usercalls); |