7.1. Tutorial 1: Hello, World!
This tutorial explains how to build and run a simple “hello world” enclave. An enclave consists of an eapp and a runtime, but also needs the host that initializes and launches the enclave. Thus, each enclave source tree contains at least the host and eapp.
Before jumping into the tutorial, please complete Quick Start.
7.1.1. Prerequisite
The Eyrie runtime allows the enclave to be statically linked with
libc, and will then support a few standard functions such as
printf
. This is not a secure I/O interface, but is useful for
demos and benchmarking.
Set PATH
to include RISC-V tools and KEYSTONE_SDK_DIR
to point the
absolute path of the installed SDK.
Let’s take a look at the example provided in Keystone SDK.
ls sdk/examples/hello
You can find two directories and CMakeLists.txt
.
7.1.2. Enclave Application: hello.c
Open hello.c
file in sdk/exmamples/hello/eapp/
. This is the source code of the enclave
application.
#include <stdio.h>
int main()
{
printf("hello, world!\n");
return 0;
}
This is the standard C program that we will run isolated in an enclave.
7.1.3. Host Application: host.cpp
Open host.cpp
in sdk/examples/hello/host/
. This is the source code of the host application.
#include "keystone.h"
#include "edge_call.h"
int main(int argc, char** argv)
{
Keystone::Enclave enclave;
Keystone::Params params;
params.setFreeMemSize(1024*1024);
params.setUntrustedMem(DEFAULT_UNTRUSTED_PTR, 1024*1024);
enclave.init(argv[1], argv[2], params);
enclave.registerOcallDispatch(incoming_call_dispatch);
edge_call_init_internals((uintptr_t) enclave.getSharedBuffer(),
enclave.getSharedBufferSize());
enclave.run();
return 0;
}
keystone.h
contains Keystone::Enclave
class which has several member functions to control the
enclave.
Following code initializes the enclave memory with the eapp/runtime.
Keystone::Enclave enclave;
Keystone::Params params;
enclave.init(<eapp binary>, <runtime binary>, params);
Keystone::Params
class is defined in sdk/include/host/params.h
, and contains enclave parameters
such as the size of free memory and the address/size of the untrusted shared buffer.
These parameters can be configured by following lines:
params.setFreeMemSize(1024*1024);
params.setUntrustedMem(DEFAULT_UNTRUSTED_PTR, 1024*1024);
In order to handle the edge calls (including system calls), the enclave must register the edge call handler and initialize the buffer addresses. This is done as following:
enclave.registerOcallDispatch(incoming_call_dispatch);
edge_call_init_internals((uintptr_t) enclave.getSharedBuffer(),
enclave.getSharedBufferSize());
Finally, the host launches the enclave by
enclave.run();
7.1.4. Enclave Package
CMakeLists.txt
contains packaging commands using makeself
.
makeself
generates a self-extracting archive with a start-up command.
In order to build the example, try the following in the build directory:
make hello-package
This will generate an enclave package named hello.ke
under <build directory>/examples/hello
.
hello.ke
is an self-extracting archive file for the enclave.
Next, copy the package into the buildroot overlay directory.
# in the build directory
cp examples/hello/hello.ke ./overlay/root
Running make image
in your build directory will generate the buildroot disk
image containing the copied package.
# in your <build directory>
make image
7.1.5. Deploying Enclave
Boot the machine with QEMU.
./scripts/run-qemu.sh
Insert the Keystone driver
# [inside QEMU]
insmod keystone-driver.ko
Deploy the enclave
# [inside QEMU]
./hello.ke
You’ll see the enclave running!
Verifying archive integrity... All good.
Uncompressing Keystone Enclave Package
hello, world!