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//! 
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 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);