Skip to content

Commit b1abc71

Browse files
committed
Add cancel queue to SGX ABI
1 parent 3fea433 commit b1abc71

File tree

2 files changed

+55
-16
lines changed

2 files changed

+55
-16
lines changed

intel-sgx/fortanix-sgx-abi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fortanix-sgx-abi"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
authors = ["Fortanix, Inc."]
55
license = "MPL-2.0"
66
description = """

intel-sgx/fortanix-sgx-abi/src/lib.rs

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
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"))]
230230
pub 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"))]
458458
pub 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"))]
461465
pub 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
639666
pub 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

Comments
 (0)