1515//! The Fortanix SGX ABI (compiler target `x86_64-fortanix-unknown-sgx`) is an
1616//! interface for Intel SGX enclaves. It is a small yet functional interface
1717//! suitable for writing larger enclaves. In contrast to other enclave
18- //! interfaces, this interface is primarly designed for running entire
18+ //! interfaces, this interface is primarily designed for running entire
1919//! applications in an enclave.
2020//!
2121//! The Fortanix SGX ABI specification consists of two parts:
@@ -225,7 +225,7 @@ pub enum Error {
225225 UserRangeEnd = 0x7fff_ffff ,
226226}
227227
228- /// A value indicating that the operation was succesful .
228+ /// A value indicating that the operation was successful .
229229 #[ cfg_attr( feature = "rustc-dep-of-std" , unstable( feature = "sgx_platform" , issue = "56975" ) ) ]
230230pub const RESULT_SUCCESS : Result = 0 ;
231231
@@ -304,7 +304,7 @@ impl Usercalls {
304304 /// Read up to `len` bytes from stream `fd`.
305305 ///
306306 /// `buf` must point to a buffer in userspace with a size of at least
307- /// `len`. On a succesful return, the number of bytes written is returned.
307+ /// `len`. On a successful return, the number of bytes written is returned.
308308 /// The enclave must check that the returned length is no more than `len`.
309309 /// If `len` is `0`, this call should block until the stream is ready for
310310 /// reading. If `len` is `0` or end of stream is reached, `0` may be
@@ -333,7 +333,7 @@ impl Usercalls {
333333 /// Write up to `len` bytes to stream `fd`.
334334 ///
335335 /// `buf` must point to a buffer in userspace with a size of at least
336- /// `len`. On a succesful return, the number of bytes written is returned.
336+ /// `len`. On a successful return, the number of bytes written is returned.
337337 /// The enclave must check that the returned length is no more than `len`.
338338 /// If `len` is `0`, this call should block until the stream is ready for
339339 /// writing. If `len` is `0` or the stream is closed, `0` may be returned.
@@ -456,6 +456,10 @@ pub const EV_RETURNQ_NOT_EMPTY: u64 = 0b0000_0000_0000_0010;
456456/// An event that enclaves can use for synchronization.
457457 #[ cfg_attr( feature = "rustc-dep-of-std" , unstable( feature = "sgx_platform" , issue = "56975" ) ) ]
458458pub const EV_UNPARK : u64 = 0b0000_0000_0000_0100 ;
459+ /// An event that will be triggered by userspace when the cancel queue is not
460+ /// or no longer full.
461+ #[ cfg_attr( feature = "rustc-dep-of-std" , unstable( feature = "sgx_platform" , issue = "56975" ) ) ]
462+ pub const EV_CANCELQ_NOT_FULL : u64 = 0b0000_0000_0000_1000 ;
459463
460464#[ cfg_attr( feature = "rustc-dep-of-std" , unstable( feature = "sgx_platform" , issue = "56975" ) ) ]
461465pub const WAIT_NO : u64 = 0 ;
@@ -485,7 +489,7 @@ impl Usercalls {
485489 /// this with the number of entries into [`thread_entry`]. If no free TCSes
486490 /// are immediately available, this may return an error.
487491 ///
488- /// This function will never be succesful in [libraries]. See the
492+ /// This function will never be successful in [libraries]. See the
489493 /// [`library`] documentation on how to use threads with libraries.
490494 ///
491495 /// [`thread_entry`]: entry/executable/fn.thread_entry.html
@@ -577,7 +581,7 @@ impl Usercalls {
577581 /// Request user memory.
578582 ///
579583 /// Request an allocation in user memory of size `size` and with alignment
580- /// `align`. If succesful , a pointer to this memory will be returned. The
584+ /// `align`. If successful , a pointer to this memory will be returned. The
581585 /// enclave must check the pointer is correctly aligned and that the entire
582586 /// range of memory pointed to is outside the enclave.
583587 ///
@@ -598,7 +602,7 @@ impl Usercalls {
598602/// Asynchronous usercall specification.
599603///
600604/// An asynchronous usercall allows an enclave to submit a usercall without
601- /// exiting the enclave. This is necessary since enclave entries and exists are
605+ /// exiting the enclave. This is necessary since enclave entries and exits are
602606/// slow (see academic work on [SCONE], [HotCalls]). In addition, the enclave
603607/// can perform other tasks while it waits for the usercall to complete. Those
604608/// tasks may include issuing other usercalls, either synchronously or
@@ -614,18 +618,40 @@ impl Usercalls {
614618/// concurrent usercalls with the same `id`, but it may reuse an `id` once the
615619/// original usercall with that `id` has returned.
616620///
621+ /// An optional third queue can be used to cancel usercalls. To cancel an async
622+ /// usercall, the enclave should send the usercall's id and number on this
623+ /// queue. If the usercall has already been processed, the enclave may still
624+ /// receive a successful result for the usercall. Otherwise, the userspace will
625+ /// cancel the usercall's execution and return an [`Interrupted`] error on the
626+ /// return queue to notify the enclave of the cancellation. Note that usercalls
627+ /// that do not return [`Result`] cannot be cancelled and if the enclave sends
628+ /// a cancellation for such a usercall, the userspace should simply ignore it.
629+ /// Additionally, userspace may choose to ignore cancellations for non-blocking
630+ /// usercalls. Userspace should be able to cancel a usercall that has been sent
631+ /// by the enclave but not yet received by the userspace, i.e. if cancellation
632+ /// is received before the usercall itself. To avoid keeping such cancellations
633+ /// forever and preventing the enclave from re-using usercall ids, userspace
634+ /// should synchronize cancel queue with the usercall queue such that the
635+ /// following invariant is maintained: whenever the enclave writes an id to the
636+ /// usercall or cancel queue, the enclave will not reuse that id until the
637+ /// usercall queue's read pointer has advanced to the write pointer at the time
638+ /// the id was written.
639+ ///
617640/// *TODO*: Add diagram.
618641///
619642/// [MPSC queues]: struct.FifoDescriptor.html
620643/// [allocated per enclave]: ../struct.Usercalls.html#method.async_queues
621644/// [SCONE]: https://www.usenix.org/conference/osdi16/technical-sessions/presentation/arnautov
622645/// [HotCalls]: http://www.ofirweisse.com/ISCA17_Ofir_Weisse.pdf
646+ /// [`Interrupted`]: enum.Error.html#variant.Interrupted
647+ /// [`Result`]: type.Result.html
623648///
624649/// # Enclave/userspace synchronization
625650///
626651/// When the enclave needs to wait on a queue, it executes the [`wait()`]
627652/// usercall synchronously, specifying [`EV_USERCALLQ_NOT_FULL`],
628- /// [`EV_RETURNQ_NOT_EMPTY`], or both in the `event_mask`. Userspace will wake
653+ /// [`EV_RETURNQ_NOT_EMPTY`], [`EV_CANCELQ_NOT_FULL`], or any combination
654+ /// thereof in the `event_mask`. Userspace will wake
629655/// any or all threads waiting on the appropriate event when it is triggered.
630656///
631657/// When userspace needs to wait on a queue, it will park the current thread
@@ -636,6 +662,7 @@ impl Usercalls {
636662/// [`wait()`]: ../struct.Usercalls.html#method.wait
637663/// [`EV_USERCALLQ_NOT_FULL`]: ../constant.EV_USERCALLQ_NOT_FULL.html
638664/// [`EV_RETURNQ_NOT_EMPTY`]: ../constant.EV_RETURNQ_NOT_EMPTY.html
665+ /// [`EV_CANCELQ_NOT_FULL`]: ../constant.EV_CANCELQ_NOT_FULL.html
639666pub mod async {
640667 use super :: * ;
641668 use core:: sync:: atomic:: { AtomicU64 , AtomicUsize } ;
@@ -693,6 +720,12 @@ pub mod async {
693720 }
694721 }
695722
723+ /// Cancel a usercall previously sent to userspace.
724+ #[ repr( C ) ]
725+ #[ derive( Copy , Clone , Default ) ]
726+ #[ cfg_attr( feature = "rustc-dep-of-std" , unstable( feature = "sgx_platform" , issue = "56975" ) ) ]
727+ pub struct Cancel ;
728+
696729 /// A circular buffer used as a FIFO queue with atomic reads and writes.
697730 ///
698731 /// The read offset is the element that was most recently read by the
@@ -717,7 +750,7 @@ pub mod async {
717750 /// 1. Load the current offsets.
718751 /// 2. If the queue is full, wait, then go to step 1.
719752 /// 3. Add 1 to the write offset and do an atomic compare-and-swap (CAS)
720- /// with the current offsets. If the CAS was not succesful , go to step
753+ /// with the current offsets. If the CAS was not successful , go to step
721754 /// 1\.
722755 /// 4. Write the data, then the `id`.
723756 /// 5. If the queue was empty in step 1, signal the reader to wake up.
@@ -774,19 +807,25 @@ pub mod async {
774807 impl Usercalls {
775808 /// Request FIFO queues for asynchronous usercalls. `usercall_queue`
776809 /// and `return_queue` must point to valid user memory with the correct
777- /// size and alignment for their types. On return, userspace will have
778- /// filled these structures with information about the queues. A single
779- /// set of queues will be allocated per enclave. Once this usercall has
780- /// returned succesfully, calling this usercall again is equivalent to
781- /// calling `exit(true)`.
810+ /// size and alignment for their types. `cancel_queue` is optional, but
811+ /// if specified (not null) it must point to valid user memory with
812+ /// correct size and alignment.
813+ /// On return, userspace will have filled these structures with
814+ /// information about the queues. A single set of queues will be
815+ /// allocated per enclave. Once this usercall has returned successfully,
816+ /// calling this usercall again is equivalent to calling `exit(true)`.
782817 ///
783818 /// May fail if the platform does not support asynchronous usercalls.
784819 ///
785820 /// The enclave must ensure that the data pointed to in the fields of
786821 /// [`FifoDescriptor`] is outside the enclave.
787822 ///
788823 /// [`FifoDescriptor`]: async/struct.FifoDescriptor.html
789- pub fn async_queues( usercall_queue: * mut FifoDescriptor <Usercall >, return_queue: * mut FifoDescriptor <Return >) -> Result { unimplemented!( ) }
824+ pub fn async_queues(
825+ usercall_queue: * mut FifoDescriptor <Usercall >,
826+ return_queue: * mut FifoDescriptor <Return >,
827+ cancel_queue: * mut FifoDescriptor <Cancel >
828+ ) -> Result { unimplemented!( ) }
790829 }
791830}
792831
0 commit comments