diff --git a/ocall/Android.mk b/ocall/Android.mk new file mode 100644 index 0000000..f9bd6c1 --- /dev/null +++ b/ocall/Android.mk @@ -0,0 +1,21 @@ +###################### optee-hello-world ###################### +LOCAL_PATH := $(call my-dir) + +OPTEE_CLIENT_EXPORT = $(LOCAL_PATH)/../../optee_client/out/export + +include $(CLEAR_VARS) +LOCAL_CFLAGS += -DANDROID_BUILD +LOCAL_CFLAGS += -Wall + +LOCAL_SRC_FILES += host/main.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/ta/include \ + $(OPTEE_CLIENT_EXPORT)/include \ + +LOCAL_SHARED_LIBRARIES := libteec +LOCAL_MODULE := optee_example_ocall +LOCAL_VENDOR_MODULE := true +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) + +include $(LOCAL_PATH)/ta/Android.mk diff --git a/ocall/CMakeLists.txt b/ocall/CMakeLists.txt new file mode 100644 index 0000000..583563c --- /dev/null +++ b/ocall/CMakeLists.txt @@ -0,0 +1,13 @@ +project (optee_example_ocall C) + +set (SRC host/main.c) + +add_executable (${PROJECT_NAME} ${SRC}) + +target_include_directories(${PROJECT_NAME} + PRIVATE ta/include + PRIVATE include) + +target_link_libraries (${PROJECT_NAME} PRIVATE teec) + +install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/ocall/Makefile b/ocall/Makefile new file mode 100644 index 0000000..b188683 --- /dev/null +++ b/ocall/Makefile @@ -0,0 +1,15 @@ +export V?=0 + +# If _HOST or _TA specific compilers are not specified, then use CROSS_COMPILE +HOST_CROSS_COMPILE ?= $(CROSS_COMPILE) +TA_CROSS_COMPILE ?= $(CROSS_COMPILE) + +.PHONY: all +all: + $(MAKE) -C host CROSS_COMPILE="$(HOST_CROSS_COMPILE)" --no-builtin-variables + $(MAKE) -C ta CROSS_COMPILE="$(TA_CROSS_COMPILE)" LDFLAGS="" + +.PHONY: clean +clean: + $(MAKE) -C host clean + $(MAKE) -C ta clean diff --git a/ocall/host/Makefile b/ocall/host/Makefile new file mode 100644 index 0000000..c4c8239 --- /dev/null +++ b/ocall/host/Makefile @@ -0,0 +1,28 @@ +CC ?= $(CROSS_COMPILE)gcc +LD ?= $(CROSS_COMPILE)ld +AR ?= $(CROSS_COMPILE)ar +NM ?= $(CROSS_COMPILE)nm +OBJCOPY ?= $(CROSS_COMPILE)objcopy +OBJDUMP ?= $(CROSS_COMPILE)objdump +READELF ?= $(CROSS_COMPILE)readelf + +OBJS = main.o + +CFLAGS += -Wall -I../ta/include -I$(TEEC_EXPORT)/include -I./include +#Add/link other required libraries here +LDADD += -lteec -L$(TEEC_EXPORT)/lib + +BINARY = optee_example_hello_world + +.PHONY: all +all: $(BINARY) + +$(BINARY): $(OBJS) + $(CC) -o $@ $< $(LDADD) + +.PHONY: clean +clean: + rm -f $(OBJS) $(BINARY) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/ocall/host/main.c b/ocall/host/main.c new file mode 100644 index 0000000..082247f --- /dev/null +++ b/ocall/host/main.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2020, Microsoft Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#include +#include + +#include + +#define PTR_ADD(ptr, offs) ((void *)((uintptr_t)(ptr) + (uintptr_t)(offs))) +#define GET_BUF(p) (PTR_ADD((p).memref.parent->buffer, (p).memref.offset)) + +static void print_uuid(TEEC_UUID *uuid) +{ + printf("%x-%x-%x-%x%x-%x%x%x%x%x%x", + uuid->timeLow, + uuid->timeMid, + uuid->timeHiAndVersion, + uuid->clockSeqAndNode[0], + uuid->clockSeqAndNode[1], + uuid->clockSeqAndNode[2], + uuid->clockSeqAndNode[3], + uuid->clockSeqAndNode[4], + uuid->clockSeqAndNode[5], + uuid->clockSeqAndNode[6], + uuid->clockSeqAndNode[7]); +} + +/* + * This function is called by the TEE Client API whenever an OCALL arrives from + * the TA. + * + * The 'taUUID' parameter carries the UUID of the TA that sent the OCALL. Since + * a TA can open a session to another TA, it is possible to receive OCALLs from + * other TAs that your TA calls into, if any. + * + * The 'commandId' indicates which function the TA wishes the CA to run. + * + * 'ctxData' is the arbitrary pointer that was set via the TEE context OCALL + * setting, if any. Similarly, 'sessionData' is the arbitrary pointer set via + * the session data setting, if it was supplied, or NULL. + * + * NOTE: Notice that the OCALL carries memory references. Currently, OP-TEE + * takes any memref parameter that originates in the TA and marshals it to + * the CA in a single shared memory object. Hence, the two buffers passed + * from the TA wind up at different offsets within the same memory + * reference. Therefore, it is necessary to take the offset into + * consideration while manipulating these buffers. + */ +TEEC_Result ocall_handler(TEEC_UUID *taUUID, uint32_t commandId, + uint32_t paramTypes, + TEEC_Parameter params[TEEC_CONFIG_PAYLOAD_REF_COUNT], + void *ctxData, void *sessionData) +{ + printf("Received an OCALL for Command Id: %u\n", commandId); + + printf("The TA that sent it is: "); + print_uuid(taUUID); + printf("\n"); + + uint32_t expected_pt; + + char *msg = "This string was sent by the CA"; + + switch (commandId) { + case CA_OCALL_CMD_REPLY_TA: + expected_pt = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, + TEEC_VALUE_INOUT, + TEEC_MEMREF_TEMP_INPUT, + TEEC_MEMREF_TEMP_INOUT); + if (paramTypes != expected_pt) { + printf("Bad param types\n"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + if (!params[2].memref.parent || !params[3].memref.parent) { + printf("No buffer(s)\n"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + printf("Input values: %u, %u\n", params[0].value.a, + params[0].value.b); + printf("Inout values: %u, %u\n", params[1].value.a, + params[1].value.b); + + printf("Input string: %s\n", (char *)GET_BUF(params[2])); + printf("Input size: %zu\n", params[2].memref.size); + + printf("Inout string: %s\n", (char *)GET_BUF(params[3])); + printf("Inout size: %zu\n", params[3].memref.size); + + /* Set the OCALL's INOUT parameters */ + params[1].value.a = 0x3; + params[1].value.b = 0x4; + + params[3].memref.size = strlen(msg) + 1; + memcpy(GET_BUF(params[3]), msg, params[3].memref.size); + + printf("OCALL handled\n"); + break; + default: + printf("Bad function ID\n"); + return TEEC_ERROR_BAD_PARAMETERS; + } + return TEEC_SUCCESS; +} + +int main(int argc, char* argv[]) +{ + TEEC_Context ctx; + TEEC_Session sess; + TEEC_UUID uuid = TA_OCALL_UUID; + TEEC_Operation op = { 0 }; + + TEEC_Result res; + uint32_t err_origin; + + char buf[128]; + char *msg1 = "This string was sent by the CA"; + const char *msg2 = "The CA thinks this is a fun riddle"; + + /* + * The TEE context OCALL setting allows setting the callback handler for + * when an OCALL arrives from the TA. This handler is effectively the + * equivalent of TA_InvokeCommandEntryPoint. Additionally, one may set + * an arbitrary pointer that will be passed to the OCALL handler when + * invoked. + * + * NOTE: You must pass this setting to the TEE context initialization + * routine to receive OCALLs. + */ + TEEC_ContextSettingOcall ocall_setting = { + .handler = ocall_handler, + .data = &ctx, + }; + + /* Array of TEE context settings */ + TEEC_ContextSetting ctx_settings = { + .type = TEEC_CONTEXT_SETTING_OCALL, + .u.ocall = &ocall_setting, + }; + + /* Initialize a TEE context with settings */ + res = TEEC_InitializeContext2(NULL, &ctx, &ctx_settings, 1); + if (res != TEEC_SUCCESS) + errx(1, "TEEC_InitializeContext failed with code 0x%x", res); + + /* + * The session data settings allows attaching an arbitrary pointer to + * the session. This pointer will be passed to the OCALL handler when + * invoked. + * + * NOTE: This is optional; you can use TEEC_OpenSession as well even if + * you expect OCALLs. + */ + TEEC_SessionSettingData data_setting = { + .data = &sess + }; + + /* Array of session settings */ + TEEC_SessionSetting session_settings = { + .type = TEEC_SESSION_SETTING_DATA, + .u.data = &data_setting, + }; + + /* Open a session with settings */ + res = TEEC_OpenSession2(&ctx, &sess, &uuid, TEEC_LOGIN_PUBLIC, NULL, + NULL, &err_origin, &session_settings, 1); + if (res != TEEC_SUCCESS) + errx(1, "TEEC_OpenSessionEx failed with code 0x%x origin 0x%x", + res, err_origin); + + /* + * Set up the parameters for the function invocation. These are just to + * show that the CA can pass parameters to the TA and that during the + * function invocation that carries those parameters to the TA, the TA + * can make an OCALL with parameters of its own choosing. That is, the + * parameters passed from the CA to the TA do not interfere with those + * passed from the TA to the CA, and vice-versa. + */ + op.paramTypes = TEEC_PARAM_TYPES( + TEEC_VALUE_INPUT, + TEEC_VALUE_INOUT, + TEEC_MEMREF_TEMP_INPUT, + TEEC_MEMREF_TEMP_INOUT); + + op.params[0].value.a = 0x3; + op.params[0].value.b = 0x4; + + op.params[1].value.a = 0x5; + op.params[1].value.b = 0x6; + + op.params[2].tmpref.buffer = msg1; + op.params[2].tmpref.size = strlen(msg1) + 1; + + op.params[3].tmpref.buffer = buf; + op.params[3].tmpref.size = sizeof(buf); + memcpy(buf, msg2, strlen(msg2) + 1); + + /* Ask the TA to call us back */ + res = TEEC_InvokeCommand(&sess, TA_OCALL_CMD_CALL_CA, &op, &err_origin); + if (res != TEEC_SUCCESS) + errx(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x", + res, err_origin); + + /* + * The code below executes after the OCALL has been handled in the + * callback at the top of this file. + */ + + /* + * Print out the values of the INOUT parameters of the original function + * invocation that we got from the TA.. + */ + printf("INOUT parameters from the original function invocation:\n"); + printf("Inout values: %u, %u\n", op.params[1].value.a, + op.params[1].value.b); + + printf("Inout string: %s\n", (char *)op.params[3].tmpref.buffer); + printf("Inout size: %zu\n", op.params[3].tmpref.size); + + /* All done */ + TEEC_CloseSession(&sess); + + TEEC_FinalizeContext(&ctx); + + return 0; +} diff --git a/ocall/ta/Android.mk b/ocall/ta/Android.mk new file mode 100644 index 0000000..826bb94 --- /dev/null +++ b/ocall/ta/Android.mk @@ -0,0 +1,4 @@ +LOCAL_PATH := $(call my-dir) + +local_module := 9b2c0652-3b9b-4d83-971e-e56c40512793.ta +include $(BUILD_OPTEE_MK) diff --git a/ocall/ta/Makefile b/ocall/ta/Makefile new file mode 100644 index 0000000..1c7a5f0 --- /dev/null +++ b/ocall/ta/Makefile @@ -0,0 +1,13 @@ +CFG_TEE_TA_LOG_LEVEL ?= 4 +CPPFLAGS += -DCFG_TEE_TA_LOG_LEVEL=$(CFG_TEE_TA_LOG_LEVEL) + +# The UUID for the Trusted Application +BINARY=9b2c0652-3b9b-4d83-971e-e56c40512793 + +-include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk + +ifeq ($(wildcard $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk), ) +clean: + @echo 'Note: $$(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk not found, cannot clean TA' + @echo 'Note: TA_DEV_KIT_DIR=$(TA_DEV_KIT_DIR)' +endif diff --git a/ocall/ta/include/ocall_ta.h b/ocall/ta/include/ocall_ta.h new file mode 100644 index 0000000..d7b38b6 --- /dev/null +++ b/ocall/ta/include/ocall_ta.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020, Microsoft Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef TA_OCALL_H +#define TA_OCALL_H + +/* 9b2c0652-3b9b-4d83-971e-e56c40512793 */ +#define TA_OCALL_UUID \ + { 0x9b2c0652, 0x3b9b, 0x4d83, \ + { 0x97, 0x1e, 0xe5, 0x6c, 0x40, 0x51, 0x27, 0x93 } } + +#define TA_OCALL_CMD_CALL_CA 0 + +#define CA_OCALL_CMD_REPLY_TA 100 + +#endif /*TA_OCALL_H*/ diff --git a/ocall/ta/ocall_ta.c b/ocall/ta/ocall_ta.c new file mode 100644 index 0000000..36b7ac4 --- /dev/null +++ b/ocall/ta/ocall_ta.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020, Microsoft Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +static TEE_Result call_ca(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + TEE_Param ocall_params[TEE_NUM_PARAMS]; + + TEE_Result res; + uint32_t eorig; + + /* Expected parameter types for the function invocation */ + const uint32_t expected_pt = + TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_INOUT); + + if (param_types != expected_pt) + return TEE_ERROR_BAD_PARAMETERS; + + /* Parameter types for the OCALL (could be different from the above) */ + const uint32_t ocall_param_types = + TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_INOUT); + + if (!params[2].memref.buffer || !params[3].memref.buffer) { + EMSG("No buffer(s)"); + return TEE_ERROR_BAD_PARAMETERS; + } + + char buf1[128]; + char buf2[128]; + + const char *msg1 = "This string was sent by the TA"; + const char *msg2 = "The TA thinks this is a fun riddle"; + + DMSG("Input values: %u, %u", params[0].value.a, params[0].value.b); + DMSG("Inout values: %u, %u", params[1].value.a, params[1].value.b); + + DMSG("Input string: %s", (char *)params[2].memref.buffer); + DMSG("Input size: %u", params[2].memref.size); + + DMSG("Inout string: %s", (char *)params[3].memref.buffer); + DMSG("Inout size: %u", params[3].memref.size); + + /* Set the invocation's INOUT parameters */ + params[1].value.a = 0xE; + params[1].value.b = 0xF; + + params[3].memref.size = strlen(msg2) + 1; + memcpy(params[3].memref.buffer, msg2, params[3].memref.size); + + memcpy(buf1, msg1, strlen(msg1) + 1); + memcpy(buf2, msg2, strlen(msg2) + 1); + + /* Set the OCALL's INPUT/INOUT parameters */ + ocall_params[0].value.a = 0x1; + ocall_params[0].value.b = 0x2; + + ocall_params[1].value.a = 0xA; + ocall_params[1].value.b = 0xB; + + ocall_params[2].memref.buffer = buf1; + ocall_params[2].memref.size = sizeof(buf1); + + ocall_params[3].memref.buffer = buf2; + ocall_params[3].memref.size = sizeof(buf2); + + res = TEE_InvokeCACommand(TEE_TIMEOUT_INFINITE, CA_OCALL_CMD_REPLY_TA, + ocall_param_types, ocall_params, &eorig); + if (res != TEE_SUCCESS) { + EMSG("TEE_InvokeCACommand failed with code 0x%x origin 0x%x", + res, eorig); + return res; + } + + DMSG("Output values: %u, %u", ocall_params[1].value.a, + ocall_params[1].value.b); + DMSG("Output string: \"%s\"", (char *)ocall_params[3].memref.buffer); + DMSG("Output size: %u\n", ocall_params[3].memref.size); + + return res; +} + +TEE_Result TA_CreateEntryPoint(void) +{ + return TEE_SUCCESS; +} + +void TA_DestroyEntryPoint(void) +{ + /* NOTHING */ +} + +TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types __unused, + TEE_Param params[4] __unused, + void **sess_ctx __unused) +{ + return TEE_SUCCESS; +} + +void TA_CloseSessionEntryPoint(void *sess_ctx __unused) +{ + /* NOTHING */ +} + +TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx __unused, uint32_t cmd_id, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + switch (cmd_id) { + case TA_OCALL_CMD_CALL_CA: + return call_ca(param_types, params); + default: + return TEE_ERROR_BAD_PARAMETERS; + } +} diff --git a/ocall/ta/sub.mk b/ocall/ta/sub.mk new file mode 100644 index 0000000..0dc3179 --- /dev/null +++ b/ocall/ta/sub.mk @@ -0,0 +1,2 @@ +global-incdirs-y += include +srcs-y += ocall_ta.c diff --git a/ocall/ta/user_ta_header_defines.h b/ocall/ta/user_ta_header_defines.h new file mode 100644 index 0000000..91a36be --- /dev/null +++ b/ocall/ta/user_ta_header_defines.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2020, Microsoft Corporation + * All rights reserved. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + + +/* + * The name of this file must not be modified + */ + +#ifndef USER_TA_HEADER_DEFINES_H +#define USER_TA_HEADER_DEFINES_H + +/* To get the TA UUID definition */ +#include + +#define TA_UUID TA_OCALL_UUID + +/* + * TA properties: multi-instance TA, no specific attribute + * TA_FLAG_EXEC_DDR is meaningless but mandated. + */ +#define TA_FLAGS TA_FLAG_EXEC_DDR + +/* Provisioned stack size */ +#define TA_STACK_SIZE (2 * 1024) + +/* Provisioned heap size for TEE_Malloc() and friends */ +#define TA_DATA_SIZE (32 * 1024) + +/* The gpd.ta.version property */ +#define TA_VERSION "1.0" + +/* The gpd.ta.description property */ +#define TA_DESCRIPTION "Example of OP-TEE OCALL Trusted Application" + +#endif /* USER_TA_HEADER_DEFINES_H */