logo
Expand description

Asynchronous usercall specification.

An asynchronous usercall allows an enclave to submit a usercall without 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 asynchronously.

Two MPSC queues are allocated per enclave. One queue is used by any enclave thread to submit usercalls to userspace. Userspace will read the calls from this queue and handle them. Another queue is used by userspace to return completed usercalls to the enclave.

Each call is identified by an enclave-specified id. Userspace must provide the same id when returning. The enclave must not submit multiple 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.

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, 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 (or do whatever else is appropriate for the synchronization model currently in use by userspace). Any synchronous usercall will wake the blocked thread (or otherwise signal that either queue is ready).

Structs

Cancel a usercall previously sent to userspace.

A circular buffer used as a FIFO queue with atomic reads and writes.

The return value of a usercall. The elements correspond to the RSI and RDX registers in the synchronous calling convention.

A usercall. The elements correspond to the RDI, RSI, RDX, R8, and R9 registers in the synchronous calling convention.