Add a demo for the embedded mode
This commit is contained in:
parent
040fe89661
commit
f2b4e96ed0
@ -20,6 +20,7 @@ This set of demos shows how real-world apps can be easily run inside SGX enclave
|
|||||||
* `tensorflow_lite/`: A demo and benchmark of [Tensorflow Lite](https://www.tensorflow.org/lite) inference engine.
|
* `tensorflow_lite/`: A demo and benchmark of [Tensorflow Lite](https://www.tensorflow.org/lite) inference engine.
|
||||||
* `xgboost/`: A demo of [XGBoost](https://xgboost.readthedocs.io/en/latest/).
|
* `xgboost/`: A demo of [XGBoost](https://xgboost.readthedocs.io/en/latest/).
|
||||||
|
|
||||||
## SGX capability demos
|
## Other demos
|
||||||
|
|
||||||
* `remote_attestation/`: This project demonstrates how an app running upon Occlum can perform SGX remote attestation.
|
* `remote_attestation/`: This project demonstrates how an app running upon Occlum can perform SGX remote attestation.
|
||||||
|
* `embedded_mode/`: A cross-enclave memory throughput benchmark enabled by the embedded mode of Occlum.
|
||||||
|
2
demos/embedded_mode/.gitignore
vendored
Normal file
2
demos/embedded_mode/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.occlum
|
||||||
|
occlum_context
|
25
demos/embedded_mode/Makefile
Normal file
25
demos/embedded_mode/Makefile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.PHONY: all build_src test clean
|
||||||
|
|
||||||
|
all: occlum_context
|
||||||
|
|
||||||
|
occlum_context: build_src
|
||||||
|
@mkdir -p occlum_context
|
||||||
|
@cd occlum_context && \
|
||||||
|
occlum init && \
|
||||||
|
cp ../trusted_memcpy_bench/build/trusted_memcpy_bench image/bin/ && \
|
||||||
|
occlum build
|
||||||
|
@cp -r occlum_context/.occlum .occlum
|
||||||
|
|
||||||
|
build_src:
|
||||||
|
@$(MAKE) --no-print-directory -C trusted_memcpy_bench
|
||||||
|
@$(MAKE) --no-print-directory -C bench_driver
|
||||||
|
|
||||||
|
TOTAL_BYTES := 10000000000 # 10GB
|
||||||
|
test:
|
||||||
|
LD_LIBRARY_PATH=/opt/occlum/build/lib RUST_BACKTRACE=1 \
|
||||||
|
./bench_driver/build/bench_driver $(TOTAL_BYTES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@$(MAKE) --no-print-directory -C trusted_memcpy_bench clean
|
||||||
|
@$(MAKE) --no-print-directory -C bench_driver clean
|
||||||
|
@rm -rf .occlum occlum_context
|
65
demos/embedded_mode/README.md
Normal file
65
demos/embedded_mode/README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# A Demo for Occlum's Embedded Mode
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
There are two main approaches to building SGX applications: the SDK-based
|
||||||
|
approach (e.g., using Intel SGX SDK) and the LibOS-based approach (e.g., using
|
||||||
|
Occlum). The SDK-based approach usually requires developers to build an SGX
|
||||||
|
application by partitioning it into trusted and untrusted halves, while the
|
||||||
|
LibOS-based approach runs the entire application inside an enclave.
|
||||||
|
|
||||||
|
Both approaches have their pros and cons. The SDK-based approach lets the
|
||||||
|
developers decide which components are to be or not to be put into the enclave.
|
||||||
|
Thus, it provides the flexibility and customizability that is attractive to
|
||||||
|
advanced developers. However, this requires non-trivial efforts from the
|
||||||
|
developers, especially when porting existing applications or libraries into
|
||||||
|
enclaves. Furthermore, whichever the SDK being used, the developers are
|
||||||
|
typically bound to a specific programming language and only provided with a
|
||||||
|
subset of the functionality or features that are supported by the programming
|
||||||
|
language and its libraries.
|
||||||
|
|
||||||
|
In contrast, the LibOS-based approach offers binary-level or code-level
|
||||||
|
compatibility so that a legacy application or library can be ported into an
|
||||||
|
enclave with minimal effort. But as the whole application is hosted by the
|
||||||
|
LibOS inside the enclave, the developers are given no mechanism to efficiently
|
||||||
|
offloading some functionalities of the application outside the enclave.
|
||||||
|
|
||||||
|
## The Embedded Mode
|
||||||
|
|
||||||
|
The embedded mode of Occlum brings the advantages of the SDK-based approach to
|
||||||
|
the LibOS-based approach. As the name suggests, this mode enables developers to
|
||||||
|
embed Occlum in an SGX application: link Occlum as a shared library to the SGX
|
||||||
|
application and use Occlum's APIs to load and execute trusted programs in an
|
||||||
|
enclave. This gives the developers both a complete control over the untrusted
|
||||||
|
components outside the enclave and a Linux-compatible environment for the
|
||||||
|
trusted programs inside the enclave. The trusted programs and the untrusted
|
||||||
|
components can communicate with each other efficiently via shared (untrusted)
|
||||||
|
memory. In short, the embedded mode combines the best of the two approaches.
|
||||||
|
|
||||||
|
## This Demo
|
||||||
|
|
||||||
|
To demonstrate the usage and the advantage of the embedded mode, we provide a
|
||||||
|
benchmark program that measures the cross-enclave memory throughput, where data
|
||||||
|
is `memcpy`'ed from an untrusted component outside the enclave to a trusted
|
||||||
|
program inside the enclave.
|
||||||
|
|
||||||
|
The trusted program is under `trusted_memcpy_bench/`. Running upon Occlum, this
|
||||||
|
program is given an untrusted buffer outside the enclave and measures the I/O
|
||||||
|
throughput achieved by repeatedly `memcpy` from it.
|
||||||
|
|
||||||
|
The untrusted component is under `bench_driver/`, which is a normal Linux
|
||||||
|
program except that is linked with the Occlum PAL library and uses Occlum PAL
|
||||||
|
APIS to load and execute `trusted_memcpy_bench` program. The untrusted buffer
|
||||||
|
required by `trusted_memcpy_bench` is prepared by `bench_driver`.
|
||||||
|
|
||||||
|
## How to Build and Run
|
||||||
|
|
||||||
|
To build the two components, use the following command
|
||||||
|
```
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the benchmark, use the following command
|
||||||
|
```
|
||||||
|
make test
|
||||||
|
```
|
1
demos/embedded_mode/bench_driver/.gitignore
vendored
Normal file
1
demos/embedded_mode/bench_driver/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build
|
37
demos/embedded_mode/bench_driver/Makefile
Normal file
37
demos/embedded_mode/bench_driver/Makefile
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
SGX_SDK ?= /opt/intel/sgxsdk
|
||||||
|
OCCLUM_PREFIX ?= /opt/occlum
|
||||||
|
|
||||||
|
BUILD_DIR := build
|
||||||
|
BIN := $(BUILD_DIR)/bench_driver
|
||||||
|
|
||||||
|
C_SRCS := $(sort $(wildcard *.c))
|
||||||
|
C_OBJS := $(addprefix $(BUILD_DIR)/,$(C_SRCS:.c=.o))
|
||||||
|
|
||||||
|
C_FLAGS := -Wall \
|
||||||
|
-I$(SGX_SDK)/include \
|
||||||
|
-I$(OCCLUM_PREFIX)/include
|
||||||
|
LINK_FLAGS := $(C_FLAGS) -lpthread \
|
||||||
|
-L$(SGX_SDK)/lib64 -lsgx_urts -lsgx_uprotected_fs \
|
||||||
|
-L$(OCCLUM_PREFIX)/build/lib -locclum-pal
|
||||||
|
|
||||||
|
ALL_BUILD_SUBDIRS := $(sort $(patsubst %/,%,$(dir $(BIN) $(C_OBJS))))
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(BIN)
|
||||||
|
|
||||||
|
$(BIN) $(C_OBJS): $(ALL_BUILD_SUBDIRS)
|
||||||
|
|
||||||
|
$(BIN): $(C_OBJS)
|
||||||
|
@$(CC) $(C_OBJS) -o $@ $(LINK_FLAGS)
|
||||||
|
@echo "LINK => $@"
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: %.c
|
||||||
|
@$(CC) $(C_FLAGS) -c $< -o $@
|
||||||
|
@echo "CC <= $@"
|
||||||
|
|
||||||
|
$(ALL_BUILD_SUBDIRS):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@-$(RM) -rf $(BUILD_DIR)
|
76
demos/embedded_mode/bench_driver/main.c
Normal file
76
demos/embedded_mode/bench_driver/main.c
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include <linux/limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <occlum_pal_api.h>
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// Help message
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#define HELP_MSG \
|
||||||
|
"%s\n" \
|
||||||
|
"A benchmark program that measures the memory throughput across the enclave.\n" \
|
||||||
|
"\n" \
|
||||||
|
"Usage:\n" \
|
||||||
|
" %s <total_bytes>\n" \
|
||||||
|
"\n" \
|
||||||
|
"Arguments:\n" \
|
||||||
|
" <total_bytes> The total number of bytes that are copied from the outside of an enclave to the inside" \
|
||||||
|
"\n" \
|
||||||
|
"Note:\n" \
|
||||||
|
" This simple benchmark program showcases the power of the embedded mode of Occlum, " \
|
||||||
|
"which enables sharing memory between the inside and outside of an enclave." \
|
||||||
|
"The embedded mode makes it possible to build Occlum-based SGX apps " \
|
||||||
|
"that comprise of trusted and untrused halves.\n"
|
||||||
|
|
||||||
|
static void print_help_msg(const char* prog_name) {
|
||||||
|
fprintf(stderr, HELP_MSG, prog_name, prog_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// Main
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
// Parse arguments
|
||||||
|
const char* prog_name = (const char*)argv[0];
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "error: require one argument\n\n");
|
||||||
|
print_help_msg(prog_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
const char* total_bytes_str = argv[1];
|
||||||
|
|
||||||
|
// Init Occlum PAL
|
||||||
|
const char* occlum_instance_dir = ".occlum";
|
||||||
|
if (occlum_pal_init(occlum_instance_dir) < 0) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The buffer shared between the outside and inside the enclave
|
||||||
|
char shared_buf[1024 * 1024] = {0};
|
||||||
|
|
||||||
|
// Prepare cmd path and arguments
|
||||||
|
const char* cmd_path = "/bin/trusted_memcpy_bench";
|
||||||
|
char buf_ptr_str[32] = {0};
|
||||||
|
char buf_size_str[32] = {0};
|
||||||
|
snprintf(buf_ptr_str, sizeof buf_ptr_str, "%lu", (unsigned long) shared_buf);
|
||||||
|
snprintf(buf_size_str, sizeof buf_size_str, "%lu", sizeof shared_buf);
|
||||||
|
const char* cmd_args[] = {
|
||||||
|
buf_ptr_str, // buf_ptr
|
||||||
|
buf_size_str, // buf_size
|
||||||
|
total_bytes_str, // total_bytes
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use Occlum PAL to execute the cmd
|
||||||
|
int exit_status = 0;
|
||||||
|
if (occlum_pal_exec(cmd_path, cmd_args, &exit_status) < 0) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy Occlum PAL
|
||||||
|
occlum_pal_destroy();
|
||||||
|
|
||||||
|
return exit_status;
|
||||||
|
}
|
1
demos/embedded_mode/trusted_memcpy_bench/.gitignore
vendored
Normal file
1
demos/embedded_mode/trusted_memcpy_bench/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build
|
32
demos/embedded_mode/trusted_memcpy_bench/Makefile
Normal file
32
demos/embedded_mode/trusted_memcpy_bench/Makefile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Program targeted for Occlum need to be built with the Occlum toolchain
|
||||||
|
CC := occlum-gcc
|
||||||
|
|
||||||
|
BUILD_DIR := build
|
||||||
|
BIN := $(BUILD_DIR)/trusted_memcpy_bench
|
||||||
|
|
||||||
|
C_SRCS := $(wildcard *.c)
|
||||||
|
C_OBJS := $(addprefix $(BUILD_DIR)/,$(C_SRCS:.c=.o))
|
||||||
|
C_FLAGS := -Wall
|
||||||
|
LINK_FLAGS := $(C_FLAGS)
|
||||||
|
|
||||||
|
ALL_BUILD_SUBDIRS := $(sort $(patsubst %/,%,$(dir $(BIN) $(C_OBJS))))
|
||||||
|
|
||||||
|
.PHONY: all test clean
|
||||||
|
|
||||||
|
all: $(BIN)
|
||||||
|
|
||||||
|
$(BIN) $(C_OBJS): $(ALL_BUILD_SUBDIRS)
|
||||||
|
|
||||||
|
$(BIN): $(C_OBJS)
|
||||||
|
@$(CC) $(C_OBJS) -o $@ $(LINK_FLAGS)
|
||||||
|
@echo "LINK => $@"
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: %.c
|
||||||
|
@$(CC) $(C_FLAGS) -c $< -o $@
|
||||||
|
@echo "CC <= $@"
|
||||||
|
|
||||||
|
$(ALL_BUILD_SUBDIRS):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@-$(RM) -rf $(BUILD_DIR)
|
91
demos/embedded_mode/trusted_memcpy_bench/main.c
Normal file
91
demos/embedded_mode/trusted_memcpy_bench/main.c
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// Help message
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#define HELP_MSG \
|
||||||
|
"Usage: %s <buf_ptr> <buf_size> <total_bytes>\n" \
|
||||||
|
"\n" \
|
||||||
|
"Arguments:\n" \
|
||||||
|
" <buf_ptr> The pointer to an untrusted buffer outside the enclave\n" \
|
||||||
|
" <buf_size> The size of the untrusted buffer\n" \
|
||||||
|
" <total_bytes> The total number of bytes to copy from the buffer into the enclave\n"
|
||||||
|
|
||||||
|
static void print_help_msg(const char* prog_name) {
|
||||||
|
fprintf(stderr, HELP_MSG, prog_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// Data consumption
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#define MIN(x, y) ((x) <= (y) ? (x) : (y))
|
||||||
|
|
||||||
|
static int copy_into_enclave(const char* src_buf, size_t buf_size, size_t total_bytes) {
|
||||||
|
char* dst_buf = malloc(buf_size);
|
||||||
|
if (dst_buf == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: out of memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (total_bytes > 0) {
|
||||||
|
size_t copy_bytes = MIN(buf_size, total_bytes);
|
||||||
|
memcpy(dst_buf, src_buf, copy_bytes);
|
||||||
|
total_bytes -= copy_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(dst_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// Main
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
// Parse arguments
|
||||||
|
const char* prog_name = argv[0];
|
||||||
|
if (argc < 4) {
|
||||||
|
print_help_msg(prog_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
const char* buf_ptr = (const char*) strtoul(argv[1], NULL, 10);
|
||||||
|
size_t buf_size = (size_t) strtoul(argv[2], NULL, 10);
|
||||||
|
size_t total_bytes = (size_t) strtoul(argv[3], NULL, 10);
|
||||||
|
if (buf_ptr == NULL || buf_size == 0 || total_bytes == 0) {
|
||||||
|
print_help_msg(prog_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark memcpy from outside the enclave to inside the enclave
|
||||||
|
printf("Start copying data from the given buffer (ptr = %p, len = %lu) for a total of %lu bytes...\n",
|
||||||
|
buf_ptr, buf_size, total_bytes);
|
||||||
|
|
||||||
|
// Time begin
|
||||||
|
struct timeval time_begin, time_end;
|
||||||
|
gettimeofday(&time_begin, NULL);
|
||||||
|
// Do memcpy for a total of `total_bytes` bytes
|
||||||
|
int ret = copy_into_enclave(buf_ptr, buf_size, total_bytes);
|
||||||
|
if (ret < 0) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
// Time end
|
||||||
|
gettimeofday(&time_end, NULL);
|
||||||
|
printf("Done.\n");
|
||||||
|
|
||||||
|
// Calculate the throughput
|
||||||
|
unsigned long elapsed_us = (time_end.tv_sec - time_begin.tv_sec) * 1000000
|
||||||
|
+ (time_end.tv_usec - time_begin.tv_usec);
|
||||||
|
if (elapsed_us == 0) {
|
||||||
|
fprintf(stderr, "ERROR: elapsed time (in us) cannot be zero");
|
||||||
|
print_help_msg(prog_name);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
printf("Cross-enclave memcpy throughput = %lu MB/s\n", total_bytes / elapsed_us);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user