From e14157e26b52b172d94c5d38b03db4deb9a10932 Mon Sep 17 00:00:00 2001 From: Hernan Gatta Date: Mon, 9 Mar 2020 03:48:14 -0700 Subject: [PATCH] ocall: add an example to show how to use OCALLs The example first initializes a TEE context using the new settings API. It then opens a session against the example OCALL TA via the new API to configure sessions. Thereafter, it performs a function invocation into the OCALL TA to request that the latter call it back. The OCALL includes value and memref parameters that are passed in multiple directions. Via these parameters the TA and CA exchange information the same way they would during a normal CA-to-TA function invocation. Signed-off-by: Hernan Gatta --- ocall/Android.mk | 21 +++ ocall/CMakeLists.txt | 13 ++ ocall/Makefile | 15 ++ ocall/host/Makefile | 28 ++++ ocall/host/main.c | 237 ++++++++++++++++++++++++++++++ ocall/ta/Android.mk | 4 + ocall/ta/Makefile | 13 ++ ocall/ta/include/ocall_ta.h | 20 +++ ocall/ta/ocall_ta.c | 137 +++++++++++++++++ ocall/ta/sub.mk | 2 + ocall/ta/user_ta_header_defines.h | 39 +++++ 11 files changed, 529 insertions(+) create mode 100644 ocall/Android.mk create mode 100644 ocall/CMakeLists.txt create mode 100644 ocall/Makefile create mode 100644 ocall/host/Makefile create mode 100644 ocall/host/main.c create mode 100644 ocall/ta/Android.mk create mode 100644 ocall/ta/Makefile create mode 100644 ocall/ta/include/ocall_ta.h create mode 100644 ocall/ta/ocall_ta.c create mode 100644 ocall/ta/sub.mk create mode 100644 ocall/ta/user_ta_header_defines.h 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..b1de27f --- /dev/null +++ b/ocall/host/main.c @@ -0,0 +1,237 @@ +/* + * 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) +{ + const char *msg = "This string was sent by the CA"; + uint32_t expected_pt; + + printf("Received an OCALL for Command Id: %u\n", commandId); + printf("The TA that sent it is: "); + print_uuid(taUUID); + printf("\n"); + + 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) { + fprintf(stderr, "Bad parameter types\n"); + return TEEC_ERROR_BAD_PARAMETERS; + } + if (!params[2].memref.parent || !params[3].memref.parent) { + fprintf(stderr, "No buffer(s)\n"); + return TEEC_ERROR_BAD_PARAMETERS; + } + if (params[3].memref.size < strlen(msg) + 1) { + fprintf(stderr, "Bad parameters\n"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + /* Print out the OCALL's INPUT/INOUT 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: + fprintf(stderr, "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..e255576 --- /dev/null +++ b/ocall/ta/ocall_ta.c @@ -0,0 +1,137 @@ +/* + * 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]) +{ + char buf1[128] = { 0 }; + char buf2[128] = { 0 }; + + const char *msg1 = "This string was sent by the TA"; + const char *msg2 = "The TA thinks this is a fun riddle"; + + TEE_Param ocall_params[TEE_NUM_PARAMS]; + + TEE_Result res = TEE_SUCCESS; + uint32_t eorig = TEE_ORIGIN_TRUSTED_APP; + + /* 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); + + /* 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 (param_types != expected_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[2].memref.buffer || !params[3].memref.buffer) { + EMSG("No buffer(s)"); + return TEE_ERROR_BAD_PARAMETERS; + } + + if (params[3].memref.size < strlen(msg2) + 1) + return TEE_ERROR_BAD_PARAMETERS; + + 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); + + /* 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; + + memcpy(buf1, msg1, strlen(msg1) + 1); + memcpy(buf2, msg2, strlen(msg2) + 1); + + 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; + } + + if (!ocall_params[3].memref.buffer) { + EMSG("Bad parameters"); + return TEE_ERROR_BAD_PARAMETERS; + } + + 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 */