diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:06:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:06:37 +0000 |
commit | 246f239d9f40f633160f0c18f87a20922d4e77bb (patch) | |
tree | 5a88572663584b3d4d28e5a20e10abab1be40884 /vendor/fortanix-sgx-abi/src | |
parent | Releasing progress-linux version 1.64.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-246f239d9f40f633160f0c18f87a20922d4e77bb.tar.xz rustc-246f239d9f40f633160f0c18f87a20922d4e77bb.zip |
Merging debian version 1.65.0+dfsg1-2.
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.rs | 164 |
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!() } } } |