summaryrefslogtreecommitdiffstats
path: root/vendor/fortanix-sgx-abi/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/fortanix-sgx-abi/src
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/fortanix-sgx-abi/src')
-rw-r--r--vendor/fortanix-sgx-abi/src/lib.rs825
1 files changed, 825 insertions, 0 deletions
diff --git a/vendor/fortanix-sgx-abi/src/lib.rs b/vendor/fortanix-sgx-abi/src/lib.rs
new file mode 100644
index 000000000..362defb8f
--- /dev/null
+++ b/vendor/fortanix-sgx-abi/src/lib.rs
@@ -0,0 +1,825 @@
+/* Copyright (c) Fortanix, Inc.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#![cfg_attr(
+ not(feature = "docs"),
+ doc = "**You are viewing the internals documentation."
+)]
+#![cfg_attr(
+ not(feature = "docs"),
+ doc = "You probably want to compile the documentation with the “docs” feature.**"
+)]
+#![cfg_attr(not(feature = "docs"), doc = "---")]
+//! The Fortanix SGX ABI (compiler target `x86_64-fortanix-unknown-sgx`) is an
+//! interface for Intel SGX enclaves. It is a small yet functional interface
+//! suitable for writing larger enclaves. In contrast to other enclave
+//! interfaces, this interface is primarly designed for running entire
+//! applications in an enclave.
+//!
+//! The Fortanix SGX ABI specification consists of two parts:
+//!
+//! 1. The calling convention (see FORTANIX-SGX-ABI.md)
+//! 2. The execution environment and [usercalls](struct.Usercalls.html) (this document)
+//!
+//! Whereas the calling convention describes how information is passed to and
+//! from the enclave, this document ascribes meaning to those values.
+//!
+//! The execution environment and usercalls have been designed with the
+//! following goals in mind:
+//!
+//! 1. *Compatible with most Rust code:* Rust code that doesn't link to other C
+//! libraries and that doesn't use files (see no. 5) should compile out of
+//! the box.
+//! 2. *Designed for SGX:* The SGX environment is unique and not compatible
+//! with other application environments out there. The primitives specified
+//! in this document are designed to work well with SGX, not to be similar
+//! to or compatible with primitives known from other environments.
+//! 3. *Designed for network services:* The most interesting usecase for SGX is
+//! to run applications remotely in an untrusted environment, e.g. the
+//! cloud. Therefore, there is a primary focus on supporting functionality
+//! needed in those situations.
+//! 4. *No filesystem:* Encrypted filesystems are hard. Especially in SGX,
+//! consistency and freshness are big concerns. In this initial version,
+//! there is no filesystem support, which is fine for most network services,
+//! which would want to keep their state with a database service anyway.
+//! Support might be added in the future.
+//! 5. *Not POSIX:* The POSIX API is huge and contains many elements that are
+//! not directly supported by the SGX instruction set, such as fork and
+//! mmap. It is explicitly a non-goal of this specification to support all
+//! of POSIX.
+//! 6. *Designed to be portable:* Enclaves don't interact directly with the OS,
+//! so there should be no need to recompile an enclave when running it with
+//! a different OS. This specification does not require any particular
+//! primitives or behavior from the OS.
+//!
+//! Like on regular operating systems, there are two types of enclaves:
+//! *executable*-type and *library*-type. The main difference between the two
+//! different types is how the enclave may be entered. Once an enclave TCS is
+//! entered, the different types act virtually identically. More information on
+//! the two different types, TCSs, and enclave entry may be found in the
+//! [`entry`](entry/index.html) module.
+//!
+//! Once an enclave TCS is entered, it may performs *synchronous usercalls* as
+//! described in the calling convention. The TCS maintains its execution state
+//! between doing a usercall and returning from the usercall. Only when the TCS
+//! exits, either through a non-usercall exit or through the
+//! [`exit`](struct.Usercalls.html#method.exit) usercall, is the TCS state
+//! destroyed. This is depicted in the following diagram.
+//!
+//! ![Enclave execution lifecycle](https://edp.fortanix.com/img/docs/enclave-execution-lifecycle.png)
+//!
+//! Enclaves may also perform *asynchronous usercalls*. This is detailed in the
+//! [`async`](async/index.html) module. Most usercalls can be submitted either
+//! synchronously or asynchronously.
+#![allow(unused)]
+#![no_std]
+#![cfg_attr(feature = "rustc-dep-of-std", feature(staged_api))]
+#![cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+#![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg",
+ html_favicon_url = "https://edp.fortanix.com/favicon.ico",
+ html_root_url = "https://edp.fortanix.com/docs/api/")]
+
+use core::ptr::NonNull;
+use core::sync::atomic::AtomicUsize;
+
+macro_rules! invoke_with_abi_spec [
+ ( $m:ident ) => [ $m![
+
+/// Specification of TCS and entry function requirements.
+///
+/// Once an enclave has called the
+/// [`exit`](../../struct.Usercalls.html#method.exit) usercall, if userspace
+/// enters a TCS normally, the enclave must panic. If userspace returns a
+/// usercall on a TCS, the enclave may decide whether to handle it normally or
+/// to panic.
+pub mod entry {
+ /// Specifies the entry points for libraries.
+ ///
+ /// The specification for library support is **experimental** and is
+ /// subject to change.
+ ///
+ /// When a user application wishes to call into the enclave library,
+ /// userspace may use any available TCS. Libraries may keep state between
+ /// invocations, but the library must not assume that subsequent calls will
+ /// go to the same TCS.
+ ///
+ /// The use of asynchronous usercalls with libraries is not recommended, as
+ /// userspace will not be able to wake up the appropriate thread in a
+ /// multi-threaded library scenario.
+ ///
+ /// Automatically launching threads using the
+ /// [`launch_thread`](../../struct.Usercalls.html#method.launch_thread)
+ /// usercall is not supported. Libraries that want to leverage
+ /// multi-threading must rely on application support to call into the
+ /// enclave from different threads.
+ pub mod library {
+ /// The entry point of every TCS.
+ ///
+ /// If a library wishes to expose multiple different functions, it must
+ /// implement this by multiplexing on one of the input parameters. It
+ /// is recommended to use `p1` for this purpose.
+ ///
+ /// The `_ignore` parameter may be set to any value by userspace. The
+ /// value observed by the enclave may be different from the value
+ /// passed by userspace and must therefore be ignored by the enclave.
+ pub fn entry(p1: u64, p2: u64, p3: u64, _ignore: u64, p4: u64, p5: u64) -> (u64, u64) { unimplemented!() }
+ }
+
+ /// Specifies the entry points for executables.
+ pub mod executable {
+ use ByteBuffer;
+
+ /// The main entry point of the enclave. This will be the entry point
+ /// of the first TCS.
+ ///
+ /// The enclave must not return from this entry. Instead, it must call
+ /// the [`exit`](../../struct.Usercalls.html#method.exit) usercall. If
+ /// the enclave does return from this TCS, and userspace subsequently
+ /// re-enters this TCS, the enclave must panic.
+ ///
+ /// Arbitrary “command-line arguments” may be passed in from userspace.
+ /// The enclave must ensure that the all buffers pointed to are
+ /// outside the enclave. The enclave should deallocate each
+ /// [`ByteBuffer`] as specified by the type. The enclave should
+ /// deallocate the main buffer by calling
+ /// [`free`]`(args, len * size_of::<ByteBuffer>, 1)`.
+ ///
+ /// [`free`]: ../../struct.Usercalls.html#method.free
+ /// [`ByteBuffer`]: ../../struct.ByteBuffer.html
+ pub fn main_entry(args: *const ByteBuffer, len: usize) -> ! { unimplemented!() }
+
+ /// The entry point of additional threads of the enclave, for non-first
+ /// TCSs.
+ ///
+ /// When returning from this TCS, userspace may re-enter this TCS after
+ /// another call to [`launch_thread`].
+ ///
+ /// The enclave must keep track of whether it expects another thread to
+ /// be launched, e.g. by keeping track of how many times it called
+ /// [`launch_thread`]. If a TCS with this entry point is entered even
+ /// though the enclave didn't request it, the enclave must panic.
+ ///
+ /// [`launch_thread`]: ../../struct.Usercalls.html#method.launch_thread
+ pub fn thread_entry() { unimplemented!() }
+ }
+}
+
+/// An arbitrary-sized buffer of bytes in userspace, allocated by userspace.
+///
+/// This type is used when userspace may return arbitrary-sized data from a
+/// usercall. When reading from the buffer, if `len` is not `0`, the enclave
+/// must ensure the entire buffer is in the user memory range. Once the enclave
+/// is done with the buffer, it should deallocate the buffer buffer by calling
+/// [`free`]`(data, len, 1)`.
+///
+/// If `len` is `0`, the enclave should ignore `data`. It should not call
+/// `free`.
+///
+/// [`free`]: ./struct.Usercalls.html#method.launch_thread
+#[repr(C)]
+#[derive(Copy, Clone)]
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub struct ByteBuffer {
+ pub data: *const u8,
+ pub len: usize
+}
+
+/// Error code definitions and space allocation.
+///
+/// Only non-zero positive values are valid errors. The variants are designed
+/// to map to [std::io::ErrorKind]. See the source for the value mapping.
+///
+/// [std::io::ErrorKind]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
+#[repr(i32)]
+#[derive(Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub enum Error {
+ PermissionDenied = 0x01,
+ NotFound = 0x02,
+ Interrupted = 0x04,
+ WouldBlock = 0x0b,
+ AlreadyExists = 0x11,
+ InvalidInput = 0x16,
+ BrokenPipe = 0x20,
+ AddrInUse = 0x62,
+ AddrNotAvailable = 0x63,
+ ConnectionAborted = 0x67,
+ ConnectionReset = 0x68,
+ NotConnected = 0x6b,
+ TimedOut = 0x6e,
+ ConnectionRefused = 0x6f,
+ InvalidData = 0x2000_0000,
+ WriteZero = 0x2000_0001,
+ UnexpectedEof = 0x2000_0002,
+ /// This value is reserved for `Other`, but all undefined values also map
+ /// to `Other`.
+ Other = 0x3fff_ffff,
+ /// Start of the range of values reserved for user-defined errors.
+ UserRangeStart = 0x4000_0000,
+ /// End (inclusive) of the range of values reserved for user-defined errors.
+ UserRangeEnd = 0x7fff_ffff,
+}
+
+/// A value indicating that the operation was succesful.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const RESULT_SUCCESS: Result = 0;
+
+/// The first return value of usercalls that might fail.
+///
+/// [`RESULT_SUCCESS`](constant.RESULT_SUCCESS.html) or an error code from the
+/// [`Error`](enum.Error.html) type.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub type Result = i32;
+
+/// The list of all usercalls.
+///
+/// *This is not a real structure, it's just a convenient way to group
+/// functions with `rustdoc`.*
+///
+/// The usercall number is passed in the first register. Up to 4 arguments may
+/// be passed in the remaining registers. Unspecified arguments and return
+/// values must be 0. Userspace must check the arguments and the enclave must
+/// check the return value.
+///
+/// The usercall number may be one of the predefined numbers associated with
+/// one of the usercalls defined below, or, if bit
+/// [`USERCALL_USER_DEFINED`](constant.USERCALL_USER_DEFINED.html) is set, an
+/// otherwise arbitrary number with an application-defined meaning.
+///
+/// Raw pointers must always point to user memory. When receiving raw pointers
+/// from userspace, the enclave must verify that the entire pointed-to memory
+/// space is outside the enclave memory range. It must then copy all data in
+/// user memory to enclave memory before operating on it.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub struct Usercalls;
+
+/// Usercall numbers with this bit set will never be defined by this specification.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const USERCALL_USER_DEFINED: u64 = 0x8000_0000;
+
+/// A file descriptor.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub type Fd = u64;
+
+/// Standard input file descriptor. Input read this way is not secure.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const FD_STDIN: Fd = 0;
+/// Standard output file descriptor. This is not a secure output channel.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const FD_STDOUT: Fd = 1;
+/// Standard error file descriptor. This is not a secure output channel.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const FD_STDERR: Fd = 2;
+
+/// # Streams
+///
+/// The enclave must not assume anything about data read or written using these
+/// calls. Data written may be piped directly to `/dev/null` by userspace, or
+/// it may be published in the local newspaper. Similarly, data read may be
+/// arbitrarily deleted, inserted, or changed. The enclave must use additional
+/// security primitives such as sealing or TLS to obtain stronger guarantees.
+///
+/// When a stream is read from by multiple threads simultaneously, the read
+/// calls may be serialized in any order. This means the data returned by a
+/// read call appeared that way in the stream, and every single byte in the
+/// stream will be read and be read only once. However, the order in which all
+/// stream data is returned is not defined. The same applies when
+/// simultaneously submitting multiple read calls for the same stream
+/// asynchronously. This all applies similarly to writing to a stream.
+///
+/// To make sure to be able to re-assemble the stream, the enclave can take one
+/// of the following approaches:
+///
+/// 1. Submit all read calls to a single stream on a single thread.
+/// 2. Serializing read calls by synchronizing access to a single stream.
+///
+/// In addition, the enclave should use cryptographic integrity protection of
+/// the stream data to ensure the stream data has not been tampered with.
+impl Usercalls {
+ /// Read up to `len` bytes from stream `fd`.
+ ///
+ /// `buf` must point to a buffer in userspace with a size of at least
+ /// `len`. On a succesful return, the number of bytes written is returned.
+ /// The enclave must check that the returned length is no more than `len`.
+ /// If `len` is `0`, this call should block until the stream is ready for
+ /// reading. If `len` is `0` or end of stream is reached, `0` may be
+ /// returned.
+ ///
+ /// The enclave may mix calls to [`read`](#method.read) and
+ /// [`read_alloc`](#method.read_alloc).
+ pub fn read(fd: Fd, buf: *mut u8, len: usize) -> (Result, usize) { unimplemented!() }
+
+ /// Read some data from stream `fd`, letting the callee choose the amount.
+ ///
+ /// `buf` must point to a [`ByteBuffer`] in userspace, and `buf.data` must
+ /// contain `null`. On success, userspace will allocate memory for the read
+ /// data and populate `ByteBuffer` appropriately. The enclave must handle
+ /// and deallocate the buffer according to the `ByteBuffer` documentation.
+ ///
+ /// Since every read operation using this usercall requires two usercalls,
+ /// it is recommended to only call this usercall asynchronously.
+ ///
+ /// The enclave may mix calls to [`read`](#method.read) and
+ /// [`read_alloc`](#method.read_alloc).
+ ///
+ /// [`ByteBuffer`]: ./struct.ByteBuffer.html
+ pub fn read_alloc(fd: Fd, buf: *mut ByteBuffer) -> Result { unimplemented!() }
+
+ /// Write up to `len` bytes to stream `fd`.
+ ///
+ /// `buf` must point to a buffer in userspace with a size of at least
+ /// `len`. On a succesful return, the number of bytes written is returned.
+ /// The enclave must check that the returned length is no more than `len`.
+ /// If `len` is `0`, this call should block until the stream is ready for
+ /// writing. If `len` is `0` or the stream is closed, `0` may be returned.
+ pub fn write(fd: Fd, buf: *const u8, len: usize) -> (Result, usize) { unimplemented!() }
+
+ /// Flush stream `fd`, ensuring that all intermediately buffered contents
+ /// reach their destination.
+ pub fn flush(fd: Fd) -> Result { unimplemented!() }
+
+ /// Close stream `fd`.
+ ///
+ /// Once the stream is closed, no further data may be read or written.
+ /// Userspace may reuse the `fd` in the future for a different stream.
+ pub fn close(fd: Fd) { unimplemented!() }
+}
+
+/// # Networking
+///
+/// In keeping with the design goals for this specification, the
+/// networking/socket interface doesn't use `sockaddr` types and doesn't
+/// have a separate API for name resolution. Userspace can't be trusted to
+/// do name resolution correctly, and even if it did, *userspace can't be
+/// trusted to actually connect streams to the correct address* specified by
+/// the enclave. Therefore, addresses specified should merely be treated as a
+/// suggestion, and additional measures must be taken by an enclave to verify
+/// the stream is connected to the correct peer, e.g. TLS.
+///
+/// The networking API works with strings as addresses. All byte buffers
+/// representing network addresses should contain a valid UTF-8 string. The
+/// enclave should panic if it is passed an invalid string by userspace. It is
+/// suggested that userspace supports at least the following notations:
+///
+/// * `hostname:port-number` (e.g. `example.com:123`)
+/// * `dotted-octet-ipv4-address:port-number` (e.g. `192.0.2.1:123`)
+/// * `[ipv6-address]:port-number` (e.g. `[2001:db8::1]:123`)
+///
+/// Additionally, other forms may be accepted, for example service names:
+///
+/// * `fully-qualified-service-name` (e.g. `_example._tcp.example.com`)
+/// * `address:service-name` (e.g. `address:example`)
+///
+/// # Errors
+///
+/// Networking calls taking an address may return the [`InvalidInput`] error if
+/// the address could not be interpreted by userspace.
+///
+/// [`InvalidInput`]: enum.Error.html#variant.InvalidInput
+impl Usercalls {
+ /// Setup a listening socket.
+ ///
+ /// The socket is bound to the address specified in `addr`. `addr` must be
+ /// a buffer in user memory with a size of at least `len`.
+ ///
+ /// On success, a file descriptor is returned which may be passed to
+ /// [`accept_stream`](#method.accept_stream) or [`close`](#method.close).
+ ///
+ /// The enclave may optionally request the local socket address be returned
+ /// in `local_addr`. On success, if `local_addr` is not NULL, userspace
+ /// will allocate memory for the address and populate [`ByteBuffer`]
+ /// appropriately. The enclave must handle and deallocate the buffer
+ /// according to the `ByteBuffer` documentation.
+ ///
+ /// The enclave must not make any security decisions based on the local
+ /// address received.
+ ///
+ /// [`ByteBuffer`]: ./struct.ByteBuffer.html
+ pub fn bind_stream(addr: *const u8, len: usize, local_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() }
+
+ /// Accept a new connection from a listening socket.
+ ///
+ /// `fd` should be a file descriptor previously returned from
+ /// [`bind_stream`](#method.bind_stream).
+ ///
+ /// The enclave may optionally request the local or peer socket addresses
+ /// be returned in `local_addr` or `peer_addr`, respectively. On success,
+ /// if `local_addr` and/or `peer_addr` is not NULL, userspace will allocate
+ /// memory for the address and populate the respective [`ByteBuffer`]
+ /// appropriately.
+ ///
+ /// The enclave must handle and deallocate each buffer according to the
+ /// `ByteBuffer` documentation.
+ ///
+ /// The enclave must not make any security decisions based on the local or
+ /// peer address received.
+ ///
+ /// [`ByteBuffer`]: ./struct.ByteBuffer.html
+ pub fn accept_stream(fd: Fd, local_addr: *mut ByteBuffer, peer_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() }
+
+ /// Create a new stream connection to the specified address.
+ ///
+ /// The enclave may optionally request the local or peer socket addresses
+ /// be returned in `local_addr` or `peer_addr`, respectively. On success,
+ /// if `local_addr` and/or `peer_addr` is not NULL, userspace will allocate
+ /// memory for the address and populate the respective [`ByteBuffer`]
+ /// appropriately.
+ ///
+ /// The enclave must handle and deallocate each buffer according to the
+ /// `ByteBuffer` documentation.
+ ///
+ /// The enclave must not make any security decisions based on the local or
+ /// peer address received.
+ ///
+ /// [`ByteBuffer`]: ./struct.ByteBuffer.html
+ pub fn connect_stream(addr: *const u8, len: usize, local_addr: *mut ByteBuffer, peer_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() }
+}
+
+/// The absolute address of a TCS in the current enclave.
+// FIXME: `u8` should be some `extern type` instead.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub type Tcs = NonNull<u8>;
+
+/// An event that will be triggered by userspace when the usercall queue is not
+/// or no longer full.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const EV_USERCALLQ_NOT_FULL: u64 = 0b0000_0000_0000_0001;
+/// An event that will be triggered by userspace when the return queue is not
+/// or no longer empty.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const EV_RETURNQ_NOT_EMPTY: u64 = 0b0000_0000_0000_0010;
+/// An event that enclaves can use for synchronization.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const EV_UNPARK: u64 = 0b0000_0000_0000_0100;
+
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const WAIT_NO: u64 = 0;
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const WAIT_INDEFINITE: u64 = !0;
+
+/// # Execution control
+///
+/// ## TCS event queues
+///
+/// Userspace will maintain a queue for each running TCS with events to be
+/// delivered. Each event is characterized by a bitset. Userspace or the
+/// enclave (using the `send` usercall) can put events on this queue. If the
+/// enclave isn't waiting for an event when an event is queued, the event
+/// remains on the queue until it delivered to the enclave in a later `wait`
+/// usercall. If an enclave is waiting for an event, and the queue contains an
+/// event that is a subset of the waited-for event mask, that event is removed
+/// from the queue and execution control is returned to the enclave.
+///
+/// Events not defined in this specification should not be generated.
+impl Usercalls {
+ /// In [executables](entry/executable/index.html), this will instruct
+ /// userspace to enter another TCS in another thread. This TCS should have
+ /// the [`thread_entry`] entrypoint. As documented in [`thread_entry`], the
+ /// enclave should keep track of how many threads it launched and reconcile
+ /// this with the number of entries into [`thread_entry`]. If no free TCSes
+ /// are immediately available, this may return an error.
+ ///
+ /// This function will never be succesful in [libraries]. See the
+ /// [`library`] documentation on how to use threads with libraries.
+ ///
+ /// [`thread_entry`]: entry/executable/fn.thread_entry.html
+ /// [libraries]: entry/library/index.html
+ /// [`library`]: entry/library/index.html
+ pub fn launch_thread() -> Result { unimplemented!() }
+
+ /// Signals to userspace that this enclave needs to be destroyed.
+ ///
+ /// The enclave must not rely on userspace to terminate other threads still
+ /// running. Similarly, the enclave must not trust that it will no longer
+ /// be entered by userspace, and it must safeguard against that in the
+ /// entrypoints.
+ ///
+ /// If `panic` is set to `true`, the enclave has exited due to a panic
+ /// condition. If the enclave was running in debug mode, the enclave may
+ /// have output a debug message according to the calling convention.
+ pub fn exit(panic: bool) -> ! { unimplemented!() }
+
+ /// Wait for an event to occur, or check if an event is currently pending.
+ ///
+ /// `timeout` must be [`WAIT_NO`] or [`WAIT_INDEFINITE`]. If it is another
+ /// value, userspace will return an error.
+ ///
+ /// If `timeout` is [`WAIT_INDEFINITE`], this call will block and return
+ /// once a matching event is queued on this TCS. If `timeout` is
+ /// [`WAIT_NO`], this call will return immediately, and the return value
+ /// will indicate if an event was pending. If it was, it has been dequeued.
+ /// If not, the [`WouldBlock`] error value will be returned.
+ ///
+ /// A matching event is one whose bits are equal to or a subset of
+ /// `event_mask`. If `event_mask` is `0`, this call will never return due
+ /// to an event. If `timeout` is also [`WAIT_INDEFINITE`], this call will
+ /// simply never return.
+ ///
+ /// Enclaves must not assume that this call only returns in response to
+ /// valid events generated by the enclave. This call may return for invalid
+ /// event sets, or before `timeout` has expired even though no event is
+ /// pending.
+ ///
+ /// When executed synchronously, this gives userspace an opportunity to
+ /// schedule something else in a cooperative multitasking environment.
+ ///
+ /// When executed asynchronously, this may trigger an
+ /// [`EV_RETURNQ_NOT_EMPTY`] event on this or other TCSes. It is not
+ /// recommended to execute this call asynchronously with a `timeout` value
+ /// other than [`WAIT_NO`].
+ ///
+ /// [`WAIT_NO`]: constant.WAIT_NO.html
+ /// [`WAIT_INDEFINITE`]: constant.WAIT_INDEFINITE.html
+ /// [`EV_RETURNQ_NOT_EMPTY`]: constant.EV_RETURNQ_NOT_EMPTY.html
+ /// [`WouldBlock`]: enum.Error.html#variant.WouldBlock
+ pub fn wait(event_mask: u64, timeout: u64) -> (Result, u64) { unimplemented!() }
+
+ /// Send an event to one or all TCSes.
+ ///
+ /// If `tcs` is `None`, send the event `event_set` to all TCSes of this
+ /// enclave, otherwise, send it to the TCS specified in `tcs`.
+ ///
+ /// # Error
+ ///
+ /// This will return the [`InvalidInput`] error if `tcs` is set but doesn't
+ /// specify a valid TCS address.
+ ///
+ /// [`InvalidInput`]: enum.Error.html#variant.InvalidInput
+ pub fn send(event_set: u64, tcs: Option<Tcs>) -> Result { unimplemented!() }
+}
+
+/// # Miscellaneous
+impl Usercalls {
+ /// This returns the number of nanoseconds since midnight UTC on January 1,
+ /// 1970\. The enclave must not rely on the accuracy of this time for
+ /// security purposes, such as checking credential expiry or preventing
+ /// rollback.
+ pub fn insecure_time() -> u64 { unimplemented!() }
+}
+
+/// # Memory
+///
+/// The enclave must not use any memory outside the enclave, except for memory
+/// explicitly returned from usercalls. You can obtain arbitrary memory in
+/// userspace using [`alloc`](#method.alloc).
+impl Usercalls {
+ /// Request user memory.
+ ///
+ /// Request an allocation in user memory of size `size` and with alignment
+ /// `align`. If succesful, a pointer to this memory will be returned. The
+ /// enclave must check the pointer is correctly aligned and that the entire
+ /// range of memory pointed to is outside the enclave.
+ ///
+ /// It is an error to call this function with `size` equal to `0`.
+ pub fn alloc(size: usize, alignment: usize) -> (Result, *mut u8) { unimplemented!() }
+
+ /// Free user memory.
+ ///
+ /// This must be called to deallocate memory in userspace. The pointer
+ /// `ptr` must have previously been returned by a usercall. The `size` and
+ /// `alignment` specified must exactly match what was allocated. This
+ /// function must be called exactly once for each user memory buffer.
+ ///
+ /// Calling this function with `size` equal to `0` is a no-op.
+ pub fn free(ptr: *mut u8, size: usize, alignment: usize) { unimplemented!() }
+}
+
+/// Asynchronous usercall specification.
+///
+/// An asynchronous usercall allows an enclave to submit a usercall without
+/// exiting the enclave. This is necessary since enclave entries and exists are
+/// slow (see academic work on [SCONE], [HotCalls]). In addition, the enclave
+/// can perform other tasks while it waits for the usercall to complete. Those
+/// tasks may include issuing other usercalls, either synchronously or
+/// asynchronously.
+///
+/// Two [MPSC queues] are [allocated per enclave]. One queue is used by any
+/// enclave thread to submit usercalls to userspace. Userspace will read the
+/// calls from this queue and handle them. Another queue is used by userspace
+/// to return completed usercalls to the enclave.
+///
+/// Each call is identified by an enclave-specified `id`. Userspace must
+/// provide the same `id` when returning. The enclave must not submit multiple
+/// concurrent usercalls with the same `id`, but it may reuse an `id` once the
+/// original usercall with that `id` has returned.
+///
+/// *TODO*: Add diagram.
+///
+/// [MPSC queues]: struct.FifoDescriptor.html
+/// [allocated per enclave]: ../struct.Usercalls.html#method.async_queues
+/// [SCONE]: https://www.usenix.org/conference/osdi16/technical-sessions/presentation/arnautov
+/// [HotCalls]: http://www.ofirweisse.com/ISCA17_Ofir_Weisse.pdf
+///
+/// # Enclave/userspace synchronization
+///
+/// When the enclave needs to wait on a queue, it executes the [`wait()`]
+/// usercall synchronously, specifying [`EV_USERCALLQ_NOT_FULL`],
+/// [`EV_RETURNQ_NOT_EMPTY`], or both in the `event_mask`. Userspace will wake
+/// any or all threads waiting on the appropriate event when it is triggered.
+///
+/// When userspace needs to wait on a queue, it will park the current thread
+/// (or do whatever else is appropriate for the synchronization model currently
+/// in use by userspace). Any synchronous usercall will wake the blocked thread
+/// (or otherwise signal that either queue is ready).
+///
+/// [`wait()`]: ../struct.Usercalls.html#method.wait
+/// [`EV_USERCALLQ_NOT_FULL`]: ../constant.EV_USERCALLQ_NOT_FULL.html
+/// [`EV_RETURNQ_NOT_EMPTY`]: ../constant.EV_RETURNQ_NOT_EMPTY.html
+pub mod async {
+ use super::*;
+ use core::sync::atomic::AtomicUsize;
+
+ /// An identified usercall.
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ pub struct Usercall {
+ /// `0` indicates this slot is empty.
+ pub id: u64,
+ /// The elements correspond to the RDI, RSI, RDX, R8, and R9 registers
+ /// in the synchronous calling convention.
+ pub args: (u64, u64, u64, u64, u64)
+ }
+
+ /// The return value of an identified usercall.
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ pub struct Return {
+ /// `0` indicates this slot is empty.
+ pub id: u64,
+ /// The elements correspond to the RSI and RDX registers in the
+ /// synchronous calling convention.
+ pub value: (u64, u64)
+ }
+
+ /// A circular buffer used as a FIFO queue with atomic reads and writes.
+ ///
+ /// The read offset is the element that was most recently read by the
+ /// receiving end of the queue. The write offset is the element that was
+ /// most recently written by the sending end. If the two offsets are equal,
+ /// the queue is either empty or full.
+ ///
+ /// The size of the buffer is such that not all the bits of the offset are
+ /// necessary to encode the current offset. The next highest unused bit is
+ /// used to keep track of the number of times the offset has wrapped
+ /// around. If the offsets are the same and the bit is the same in the read
+ /// and write offsets, the queue is empty. If the bit is different in the
+ /// read and write offsets, the queue is full.
+ ///
+ /// The following procedures will operate the queues in a multiple producer
+ /// single consumer (MPSC) fashion.
+ ///
+ /// ## Push operation
+ ///
+ /// To push an element onto the queue:
+ ///
+ /// 1. Load the current offsets.
+ /// 2. If the queue is full, wait, then go to step 1.
+ /// 3. Add 1 to the write offset and do an atomic compare-and-swap (CAS)
+ /// with the current offsets. If the CAS was not succesful, go to step
+ /// 1\.
+ /// 4. Write the data, then the `id`.
+ /// 5. If the queue was empty in step 1, signal the reader to wake up.
+ ///
+ /// ## Pop operation
+ ///
+ /// To pop an element off the queue:
+ ///
+ /// 1. Load the current offsets.
+ /// 2. If the queue is empty, wait, then go to step 1.
+ /// 3. Add 1 to the read offset.
+ /// 4. Read the `id` at the new read offset.
+ /// 5. If `id` is `0`, go to step 4 (spin). Spinning is OK because data is
+ /// expected to be written imminently.
+ /// 6. Read the data, then store `0` in the `id`.
+ /// 7. Store the new read offset.
+ /// 8. If the queue was full in step 1, signal the writer to wake up.
+ #[repr(C)]
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ pub struct FifoDescriptor<T> {
+ /// Pointer to the queue memory. Must have a size of
+ /// `len * size_of::<T>()` bytes and have alignment `align_of::<T>`.
+ pub data: *mut T,
+ /// The number of elements pointed to by `data`. Must be a power of two
+ /// less than or equal to 2³¹.
+ pub len: usize,
+ /// Actually a `(u32, u32)` tuple, aligned to allow atomic operations
+ /// on both halves simultaneously. The first element (low dword) is
+ /// the read offset and the second element (high dword) is the write
+ /// offset.
+ pub offsets: *const AtomicUsize,
+ }
+
+ // not using `#[derive]` because that would require T: Clone
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ impl<T> Clone for FifoDescriptor<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+
+ // not using `#[derive]` because that would require T: Copy
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ impl<T> Copy for FifoDescriptor<T> {}
+
+ /// # Asynchronous usercalls
+ ///
+ /// *Due to `rustdoc`, this section may appear at the top of the
+ /// `Usercalls` documentation. You might want to read the other sections
+ /// first and then come back to this one.*
+ ///
+ /// See also the [`async` module](async/index.html) documentation.
+ impl Usercalls {
+ /// Request FIFO queues for asynchronous usercalls. `usercall_queue`
+ /// and `return_queue` must point to valid user memory with the correct
+ /// size and alignment for their types. On return, userspace will have
+ /// filled these structures with information about the queues. A single
+ /// set of queues will be allocated per enclave. Once this usercall has
+ /// returned succesfully, calling this usercall again is equivalent to
+ /// calling `exit(true)`.
+ ///
+ /// May fail if the platform does not support asynchronous usercalls.
+ ///
+ /// The enclave must ensure that the data pointed to in the fields of
+ /// [`FifoDescriptor`] is outside the enclave.
+ ///
+ /// [`FifoDescriptor`]: async/struct.FifoDescriptor.html
+ pub fn async_queues(usercall_queue: *mut FifoDescriptor<Usercall>, return_queue: *mut FifoDescriptor<Return>) -> Result { unimplemented!() }
+ }
+}
+
+]; ] ];
+
+// docs: Just render the docs verbatim
+macro_rules! docs {
+ ($($tt:tt)*) => ($($tt)*)
+}
+
+#[cfg(feature = "docs")]
+invoke_with_abi_spec!(docs);
+
+// types: flatten the module structure and ignore any items that are not types.
+macro_rules! types {
+ // flatten modules
+ ($(#[$meta:meta])* pub mod $modname:ident { $($contents:tt)* } $($remainder:tt)*) =>
+ { types!($($contents)*); types!($($remainder)*); };
+ // ignore impls
+ ($(#[$meta:meta])* impl Usercalls { $($contents:tt)* } $($remainder:tt)* ) =>
+ { types!($($remainder)*); };
+ // ignore `struct Usercalls`
+ ($(#[$meta:meta])* pub struct Usercalls; $($remainder:tt)* ) =>
+ { types!($($remainder)*); };
+ // ignore free functions
+ ($(#[$meta:meta])* pub fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)* { unimplemented!() } $($remainder:tt)* ) =>
+ { types!($($remainder)*); };
+ // ignore use statements
+ (use $($tt:tt)::*; $($remainder:tt)* ) =>
+ { types!($($remainder)*); };
+ // copy all other items verbatim
+ ($item:item $($remainder:tt)*) =>
+ { $item types!($($remainder)*); };
+ () => {};
+}
+
+#[cfg(not(feature = "docs"))]
+invoke_with_abi_spec!(types);
+
+// Define a macro that will call a second macro providing the list of all
+// function declarations inside all `impl Usercalls` blocks.
+macro_rules! define_invoke_with_usercalls {
+ // collect all usercall function declarations in a list
+ (@ [$($accumulated:tt)*] $(#[$meta1:meta])* impl Usercalls { $($(#[$meta2:meta])* pub fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)* { unimplemented!() } )* } $($remainder:tt)* ) =>
+ { define_invoke_with_usercalls!(@ [$($accumulated)* $(fn $f($($n: $t),*) $(-> $r)*;)*] $($remainder)*); };
+ // visit modules
+ (@ $accumulated:tt $(#[$meta:meta])* pub mod $modname:ident { $($contents:tt)* } $($remainder:tt)*) =>
+ { define_invoke_with_usercalls!(@ $accumulated $($contents)* $($remainder)*); };
+ // ignore all other items
+ (@ $accumulated:tt $item:item $($remainder:tt)*) =>
+ { define_invoke_with_usercalls!(@ $accumulated $($remainder)*); };
+ // Define the macro
+ (@ $accumulated:tt) => {
+ /// Call the macro `$m`, passing a semicolon-separated list of usercall
+ /// function declarations.
+ ///
+ /// The passed in macro could for example use the following pattern:
+ ///
+ /// ```text
+ /// ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*)
+ /// ```
+ #[macro_export]
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ macro_rules! invoke_with_usercalls {
+ ($m:ident) => { $m! $accumulated; }
+ }
+ };
+ // start collection with an empty list
+ ($($tt:tt)*) => {
+ define_invoke_with_usercalls!(@ [] $($tt)*);
+ }
+}
+
+#[cfg(not(feature = "docs"))]
+invoke_with_abi_spec!(define_invoke_with_usercalls);