4. Edge Calls¶
In Keystone, as in other enclave systems, function calls that cross in or out of the enclave are called edge calls.
For example, if an enclave wishes to send a network packet, it must communicate the data to transmit to an untrusted host process via an edge call.
The current version of Keystone supports calls from enclave->host, referred to internally as ocalls (outbound calls, names under discussion).
If your application requires behavior similar to calls from
host->enclave we suggest emulating these with a polling
ocall. (Keystone-Demo uses this methodology with a
wait_for_message
ocall).
All ocall wrapping code currently passes data through shared memory regions. When referencing data in these regions only offsets into the region are used, never virtual address pointers.
4.1. Example ocall Lifecycle¶
Note: This is quite specific to the Keystone runtime and driver design.
Consider an example “print unsigned long” ocall, print_value
. This
call exports a value from the enclave to be printed to stdout by the
host process.
The eapp calls ocall_print_value(val);
which is a edge wrapper
function.
ocall_print_value(val)
uses the system-call-like interface to the
runtime to run an ocall like ocall(OCALL_PRINT_VALUE, &val,
sizeof(unsigned long), 0, 0);
It passes a pointer to the value, the
size of the argument, and any needed return buffer information. (None
in this case)
The runtime then allocates an edge_call_t
structure in the shared
memory region, fills out the call type, copies the value into another
part of the shared memory, and sets up the offset to the argument
value. Note that edge calls do not use pointers, but instead offset
values into the shared memory region.
Finally, the runtime exits the enclave with an SBI_CALL
, passing a
value indicating the enclave is not shutting down, but executing an
ocall.
The Keystone kernel driver resumes execution, checks the exit status of the enclave, notes a pending ocall, and passes execution to the userspace host process.
The userspace host process consumes the edge_call_t
and dispatches
the registered ocall handler wrapper for OCALL_PRINT_VALUE
. The
wrapper generates a pointer to the argument value from the offset in
the shared memory region, and calls print_value
with the value as
an argument.
print_value
prints the given argument value.
On return, the host wrapper checks if any return values need to be
copied into the shared memory region (none in this case.) Sets the
edge_call_t
return status to SUCCESS, and returns into the
driver.
The driver re-enters the enclave runtime via an SBI_CALL
.
The runtime checks if any return information needs to be copied from the shared region into return buffers (none in this case) and then resumes the enclave ocall wrapper code.
Finally, the ocall wrapper code passes any return values to the
function that first called ocall_print_value
.