Skip to main content

fortanix_sgx_abi/
lib.rs

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