summaryrefslogtreecommitdiffstats
path: root/vendor/fortanix-sgx-abi/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vendor/fortanix-sgx-abi/src/lib.rs164
1 files changed, 121 insertions, 43 deletions
diff --git a/vendor/fortanix-sgx-abi/src/lib.rs b/vendor/fortanix-sgx-abi/src/lib.rs
index 362defb8f..3511ae34b 100644
--- a/vendor/fortanix-sgx-abi/src/lib.rs
+++ b/vendor/fortanix-sgx-abi/src/lib.rs
@@ -15,7 +15,7 @@
//! 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
+//! interfaces, this interface is primarily designed for running entire
//! applications in an enclave.
//!
//! The Fortanix SGX ABI specification consists of two parts:
@@ -82,7 +82,7 @@
html_root_url = "https://edp.fortanix.com/docs/api/")]
use core::ptr::NonNull;
-use core::sync::atomic::AtomicUsize;
+use core::sync::atomic::{AtomicU64, AtomicUsize};
macro_rules! invoke_with_abi_spec [
( $m:ident ) => [ $m![
@@ -186,6 +186,9 @@ pub struct ByteBuffer {
pub len: usize
}
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+unsafe impl Send for ByteBuffer {}
+
/// Error code definitions and space allocation.
///
/// Only non-zero positive values are valid errors. The variants are designed
@@ -222,7 +225,7 @@ pub enum Error {
UserRangeEnd = 0x7fff_ffff,
}
-/// A value indicating that the operation was succesful.
+/// A value indicating that the operation was successful.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const RESULT_SUCCESS: Result = 0;
@@ -301,7 +304,7 @@ 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.
+ /// `len`. On a successful 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
@@ -330,7 +333,7 @@ impl Usercalls {
/// 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.
+ /// `len`. On a successful 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.
@@ -453,6 +456,10 @@ 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;
+/// An event that will be triggered by userspace when the cancel queue is not
+/// or no longer full.
+#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+pub const EV_CANCELQ_NOT_FULL: u64 = 0b0000_0000_0000_1000;
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const WAIT_NO: u64 = 0;
@@ -464,9 +471,10 @@ pub const WAIT_INDEFINITE: u64 = !0;
/// ## 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
+/// delivered. Each event is characterized by a bitset with at least one bit
+/// set. 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
@@ -481,7 +489,7 @@ impl Usercalls {
/// 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
+ /// This function will never be successful in [libraries]. See the
/// [`library`] documentation on how to use threads with libraries.
///
/// [`thread_entry`]: entry/executable/fn.thread_entry.html
@@ -503,14 +511,18 @@ impl Usercalls {
/// 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.
+ /// `timeout` must be [`WAIT_NO`] or [`WAIT_INDEFINITE`] or a positive
+ /// value smaller than u64::MAX specifying number of nanoseconds to wait.
///
/// 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.
+ /// If not, the [`WouldBlock`] error value will be returned. If `timeout`
+ /// is a value other than [`WAIT_NO`] and [`WAIT_INDEFINITE`], this call
+ /// will block until either a matching event is queued in which case the
+ /// return value will indicate the event, or the timeout is reached in
+ /// which case the [`TimedOut`] 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
@@ -534,6 +546,7 @@ impl Usercalls {
/// [`WAIT_INDEFINITE`]: constant.WAIT_INDEFINITE.html
/// [`EV_RETURNQ_NOT_EMPTY`]: constant.EV_RETURNQ_NOT_EMPTY.html
/// [`WouldBlock`]: enum.Error.html#variant.WouldBlock
+ /// [`TimedOut`]: enum.Error.html#variant.TimedOut
pub fn wait(event_mask: u64, timeout: u64) -> (Result, u64) { unimplemented!() }
/// Send an event to one or all TCSes.
@@ -568,7 +581,7 @@ 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
+ /// `align`. If successful, 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.
///
@@ -589,7 +602,7 @@ impl Usercalls {
/// 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
+/// exiting the enclave. This is necessary since enclave entries and exits 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
@@ -605,18 +618,40 @@ impl Usercalls {
/// concurrent usercalls with the same `id`, but it may reuse an `id` once the
/// original usercall with that `id` has returned.
///
+/// An optional third queue can be used to cancel usercalls. To cancel an async
+/// usercall, the enclave should send the usercall's id and number on this
+/// queue. If the usercall has already been processed, the enclave may still
+/// receive a successful result for the usercall. Otherwise, the userspace will
+/// cancel the usercall's execution and return an [`Interrupted`] error on the
+/// return queue to notify the enclave of the cancellation. Note that usercalls
+/// that do not return [`Result`] cannot be cancelled and if the enclave sends
+/// a cancellation for such a usercall, the userspace should simply ignore it.
+/// Additionally, userspace may choose to ignore cancellations for non-blocking
+/// usercalls. Userspace should be able to cancel a usercall that has been sent
+/// by the enclave but not yet received by the userspace, i.e. if cancellation
+/// is received before the usercall itself. To avoid keeping such cancellations
+/// forever and preventing the enclave from re-using usercall ids, userspace
+/// should synchronize cancel queue with the usercall queue such that the
+/// following invariant is maintained: whenever the enclave writes an id to the
+/// usercall or cancel queue, the enclave will not reuse that id until the
+/// usercall queue's read pointer has advanced to the write pointer at the time
+/// the id was written.
+///
/// *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
+/// [`Interrupted`]: enum.Error.html#variant.Interrupted
+/// [`Result`]: type.Result.html
///
/// # 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
+/// [`EV_RETURNQ_NOT_EMPTY`], [`EV_CANCELQ_NOT_FULL`], or any combination
+/// thereof 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
@@ -627,34 +662,70 @@ impl Usercalls {
/// [`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
+/// [`EV_CANCELQ_NOT_FULL`]: ../constant.EV_CANCELQ_NOT_FULL.html
pub mod async {
use super::*;
- use core::sync::atomic::AtomicUsize;
+ use core::sync::atomic::{AtomicU64, 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)
+ pub struct WithId<T> {
+ pub id: AtomicU64,
+ pub data: T,
+ }
+
+ /// A usercall.
+ /// The elements correspond to the RDI, RSI, RDX, R8, and R9 registers
+ /// in the synchronous calling convention.
+ #[repr(C)]
+ #[derive(Copy, Clone, Default)]
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ pub struct Usercall(pub u64, pub u64, pub u64, pub u64, pub u64);
+
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ impl From<Usercall> for (u64, u64, u64, u64, u64) {
+ fn from(u: Usercall) -> Self {
+ let Usercall(p1, p2, p3, p4, p5) = u;
+ (p1, p2, p3, p4, p5)
+ }
+ }
+
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ impl From<(u64, u64, u64, u64, u64)> for Usercall {
+ fn from(p: (u64, u64, u64, u64, u64)) -> Self {
+ Usercall(p.0, p.1, p.2, p.3, p.4)
+ }
}
- /// The return value of an identified usercall.
+ /// The return value of a usercall.
+ /// The elements correspond to the RSI and RDX registers in the
+ /// synchronous calling convention.
#[repr(C)]
- #[derive(Copy, Clone)]
+ #[derive(Copy, Clone, Default)]
#[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)
+ pub struct Return(pub u64, pub u64);
+
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ impl From<Return> for (u64, u64) {
+ fn from(r: Return) -> Self {
+ let Return(r1, r2) = r;
+ (r1, r2)
+ }
}
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ impl From<(u64, u64)> for Return {
+ fn from(r: (u64, u64)) -> Self {
+ Return(r.0, r.1)
+ }
+ }
+
+ /// Cancel a usercall previously sent to userspace.
+ #[repr(C)]
+ #[derive(Copy, Clone, Default)]
+ #[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
+ pub struct Cancel;
+
/// 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
@@ -679,7 +750,7 @@ pub mod async {
/// 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
+ /// with the current offsets. If the CAS was not successful, 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.
@@ -695,14 +766,15 @@ pub mod async {
/// 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.
+ /// 7. Store the new read offset, retrieving the old offsets.
+ /// 8. If the queue was full before step 7, 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,
+ /// `len * size_of::<WithId<T>>()` bytes and have alignment
+ /// `align_of::<WithId<T>>`.
+ pub data: *mut WithId<T>,
/// The number of elements pointed to by `data`. Must be a power of two
/// less than or equal to 2³¹.
pub len: usize,
@@ -735,11 +807,13 @@ pub mod async {
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)`.
+ /// size and alignment for their types. `cancel_queue` is optional, but
+ /// if specified (not null) it must point to valid user memory with
+ /// correct size and alignment.
+ /// 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 successfully,
+ /// calling this usercall again is equivalent to calling `exit(true)`.
///
/// May fail if the platform does not support asynchronous usercalls.
///
@@ -747,7 +821,11 @@ pub mod async {
/// [`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!() }
+ pub fn async_queues(
+ usercall_queue: *mut FifoDescriptor<Usercall>,
+ return_queue: *mut FifoDescriptor<Return>,
+ cancel_queue: *mut FifoDescriptor<Cancel>
+ ) -> Result { unimplemented!() }
}
}