API Reference
Trusted applications
Custom Pseudo Trusted Application
Trusted Applications (TA) normally run in user mode in the Trusted Execution Environment. They are dynamically loaded when executed. When you want to run them in kernel mode (to write peripheral drivers for example), a Pseudo TA (PTA) is necessary. During compilation, PTA’s are statically linked against the OP-TEE kernel.
It is possible to create a PTA by adding the sources to optee_os/core/pta
and including them in the Makefile in the same directory. This way the PTA is included in the os when it is built. It is then possible to create an application for the normal world that calls this PTA using its UUID and the right parameters, just like a normal TA.
The example host application is created by copying an existing example application from optee_examples
and removing the ta
subfolder (as there will be no TA compiled separately). The Makefile of the host application should be updated with the right binary name and the build instructions for a TA should be removed from the parent (C)Makefiles.
Example PTA
optee_os/core/pta/testpta.c
This example shows a simple PTA that prints some text when called from the normal world.
#include <kernel/pseudo_ta.h>
#define PTA_NAME "test.pta"
#define PTA_TEST_PRINT_UUID { 0xd4be3f91, 0xc4e1, 0x436c, \
{ 0xb2, 0x92, 0xbf, 0xf5, 0x3e, 0x43, 0x04, 0xd5 } }
#define PTA_TEST_PRINT 0
static TEE_Result test_print(uint32_t type, TEE_Param p[TEE_NUM_PARAMS]) {
("This is a test of a pseudo trusted application.");
IMSG
return TEE_SUCCESS;
}
/*
* Trusted Application Etnry Points
*/
static TEE_Result invoke_command(void *session __unused, uint32_t cmd,
uint32_t ptypes,
[TEE_NUM_PARAMS]) {
TEE_Param paramsswitch (cmd) {
case PTA_TEST_PRINT:
return test_print(ptypes, params);
break;
default:
break;
}
return TEE_ERROR_NOT_IMPLEMENTED;
}
(.uuid = PTA_TEST_PRINT_UUID, .name = PTA_NAME,
pseudo_ta_register.flags = PTA_DEFAULT_FLAGS, .invoke_command_entry_point = invoke_command);
Append to optee_os/core/pta/sub.mk
ifeq ($(PLATFORM),vexpress-qemu_virt)
srcs-y += testpta.c
endif
Example Host application
host/main.c
/*
* Copyright (c) 2016, Linaro Limited
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <err.h>
#include <stdio.h>
#include <string.h>
/* OP-TEE TEE client API (built by optee_client) */
#include <tee_client_api.h>
#define PTA_TEST_PRINT_UUID { 0xd4be3f91, 0xc4e1, 0x436c, \
{ 0xb2, 0x92, 0xbf, 0xf5, 0x3e, 0x43, 0x04, 0xd5 } }
#define PTA_TEST_PRINT 0
int main(void)
{
;
TEEC_Result res;
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op= PTA_TEST_PRINT_UUID;
TEEC_UUID uuid uint32_t err_origin;
/* Initialize a context connecting us to the TEE */
= TEEC_InitializeContext(NULL, &ctx);
res if (res != TEEC_SUCCESS)
(1, "TEEC_InitializeContext failed with code 0x%x", res);
errx
/*
* Open a session to the "hello world" TA, the TA will print "hello
* world!" in the log when the session is created.
*/
= TEEC_OpenSession(&ctx, &sess, &uuid,
res , NULL, NULL, &err_origin);
TEEC_LOGIN_PUBLICif (res != TEEC_SUCCESS)
(1, "TEEC_Opensession failed with code 0x%x origin 0x%x",
errx, err_origin);
res
/*
* Execute a function in the TA by invoking it, in this case
* we're incrementing a number.
*
* The value of command ID part and how the parameters are
* interpreted is part of the interface provided by the TA.
*/
/* Clear the TEEC_Operation struct */
(&op, 0, sizeof(op));
memset
/*
* Prepare the argument. Pass a value in the first parameter,
* the remaining three parameters are unused.
*/
.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE,
op, TEEC_NONE);
TEEC_NONE
/*
* TA_HELLO_WORLD_CMD_INC_VALUE is the actual function in the TA to be
* called.
*/
("Invoking PTA to print test.");
printf= TEEC_InvokeCommand(&sess, PTA_TEST_PRINT, &op, &err_origin);
res if (res != TEEC_SUCCESS)
(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x",
errx, err_origin);
res("Test printed.");
printf
/*
* We're done with the TA, close the session and
* destroy the context.
*
* The TA will print "Goodbye!" in the log when the
* session is closed.
*/
(&sess);
TEEC_CloseSession
(&ctx);
TEEC_FinalizeContext
return 0;
}
Makefile
V?=0
export
# If _HOST or _TA specific compilers are not specified, then use CROSS_COMPILE
HOST_CROSS_COMPILE ?= $(CROSS_COMPILE)
.PHONY: all
all:
$(MAKE) -C host CROSS_COMPILE="$(HOST_CROSS_COMPILE)" --no-builtin-variables
.PHONY: clean
clean:
$(MAKE) -C host clean
host/Makefile
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_testpta
.PHONY: all
all: $(BINARY)
$(BINARY): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $< $(LDADD)
.PHONY: clean
clean:
$(OBJS) $(BINARY)
rm -f
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
CMakeLists.txt
project (optee_example_testpta C)
set (SRC host/main.c)
${PROJECT_NAME} ${SRC})
add_executable (
${PROJECT_NAME}
target_include_directories(# PRIVATE ta/include
include)
PRIVATE
${PROJECT_NAME} PRIVATE teec)
target_link_libraries (
${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) install (TARGETS
Android.mk
###################### optee-testpta ######################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DANDROID_BUILD
LOCAL_CFLAGS += -Wall
LOCAL_SRC_FILES += host/main.c
# LOCAL_C_INCLUDES := $(LOCAL_PATH)/ta/include
LOCAL_SHARED_LIBRARIES := libteec
LOCAL_MODULE := optee_example_testpta
LOCAL_VENDOR_MODULE := true
LOCAL_MODULE_TAGS := optional
include $(BUILD_EXECUTABLE)
# include $(LOCAL_PATH)/ta/Android.mk
Scheduling
Mr-TEE uses FreeRTOS as its scheduler, so its API is available to all OP-TEE OS kernel applications. The code snippets are based on the ones provided in the official FreeRTOS documentation: FreeRTOS Developer Docs.
This page lists a few important types and functions to get started. Further information on FreeRTOS can be found at About FreeRTOS Kernel.
Tasks
Signature
void task(void *parameters);
Parameter | Type | Description |
---|---|---|
parameters | void * |
Params given to the task whenever it is created. |
Methods
xTaskCreate
Register a task with the scheduler. The task will be scheduled according to its priority from the moment this function is called.
(
BaseType_t xTaskCreate,
TaskFunction_t pvTaskCodeconst char * const pcName,
,
configSTACK_DEPTH_TYPE usStackDepthvoid * pvParameters,
,
UBaseType_t uxPriority* pxCreatedTask
TaskHandle_t );
Parameter | Type | Description |
---|---|---|
pvTaskCode | TaskFunction_t |
The function with the control logic of the task. |
pcName | const char * const |
The name of the task. |
usStackDepth | configSTACK_DEPTH_TYPE |
The type of stack. |
pvParameters | void * |
The parameter to pass when the task is first called. |
uxPriority | UBaseType_t |
The task priority. |
pxCreatedTask | TaskHandle_t * |
A handle to the created task, used for task management. |
vTaskDelete
Delete a task, stopping its execution and removing all its memory allocations.
void vTaskDelete(TaskHandle_t xTaskToDelete);
Parameter | Type | Description |
---|---|---|
xTaskToDelete | TaskHandle_t |
The handle to the task to delete. |
vTaskDelay
Delay the execution of a task for a given amount of ticks.
void vTaskDelay(const TickType_t xTicksToDelay);
Parameter | Type | Description |
---|---|---|
xTicksToDelay | const TickType_t |
The amount of ticks to delay execution for. |
xTaskDelayUntil
Delay a task until a given time. Will not delay if that time is in the past.
(
BaseType_t xTaskDelayUntil* const pxPreviousWakeTime,
TickType_t const TickType_t xTimeIncrement
);
Parameter | Type | Description |
---|---|---|
pxPreviousWakeTime | TickType_t * const |
Pointer to last time task was revived. |
xTimeIncrement | const TickType_t |
Amount of ticks past the previous wake time the task should wait. |
Return | Description |
---|---|
pdTrue |
Task was actually delayed. |
pdFalse |
Task was not delayed. |
Example: Scheduling a task
#include <FreeRTOS/task.h>
#include <FreeRTOS/FreeRTOS.h>
void task( void * parameters )
{
while (true)
{
// Code to execute
(n); // Delay execution of task by n ticks.
vTaskDelay}
// Break out of loop and delete task when not needed.
(NULL);
vTaskDelete}
int main (void) {
;
BaseType_t xReturn= NULL;
TaskHandle_t xHandle
= xTaskCreate(
xReturn , /* Function that implements the task. */
task"Task name", /* Text name for the task. */
, /* Stack size in words, not bytes. */
configMINMAL_STACK_SIZE(void *) NULL, /* Parameter passed into the task. */
(UBaseType_t) 1, /* Priority at which the task is created. */
&xHandle /* Used to pass out the created task's handle. */
);
if( xReturn != pdPASS )
{
("Could not create task\n");
EMSGreturn -1;
}
return 0;
}
Shared Secure Peripherals
SSP Notifier
Normal World Notifier
norm_notif_register
int norm_notif_register(uuid_t uuid, void (*notif_handler)(void));
Register a normal world driver for receiving notifications from a secure driver.
Parameter | Type | Description |
---|---|---|
uuid | uuid_t |
The UUID of the secure driver. |
notif_handler | void (* handler)(void) |
The handler to call whenever a notification is received. |
Return | Description |
---|---|
0 | The normal world driver has been registered successfully. |
-EINVAL |
There already is a normal world driver registered for the given uuid. |
Secure World Notifier
sec_notify
(TEE_UUID uuid); TEE_Result sec_notify
Notify the normal world.
Parameter | Type | Description |
---|---|---|
uuid | uuid_t |
The UUID of the secure driver. |
Return | Description |
---|---|
TEE_SUCCESS |
The notification was sent successfully. |
!TEE_SUCCESS |
Something went wrong. |
Example: Notifying normal world driver
Normal world driver
#include <linux/init.h>
#include <linux/module.h>
#include <linux/uuid.h>
#include <normal-notifier.h>
static const uuid_t sec_drv_uuid = UUID_INIT(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7);
static void notif_handler(void)
{
// Retrieve data from secure driver, do some processing etc.
}
static int __init mod_init(void)
{
int ret;
if ((ret = norm_notif_register(sec_drv_uuid, notif_handler)) < 0) {
("Could not register with the normal world notifier\n.");
pr_errreturn ret;
}
}
(mod_init); module_init
Secure world driver
#include <kernel/pseudo_ta.h>
#include <kernel/interrupt.h>
#include <drivers/secure_notifier.h>
static const TEE_UUID uuid = {a, b, c, {d0,d1,d2,d3,d4,d5,d6,d7}}
static enum itr_return handler(struct itr_handler *handler) {
// Do stuff with peripheral.
// If necessary:
if (sec_notify(uuid)) {
("Error sending notification.\n");
EMSG}
return ITRR_HANDLED;
}
static struct itr_handler itr = {
.it = <interrupt_number>,
.flags = ITRF_TRIGGER_LEVEL,
.handler = handler,
};
(console_itr);
DECLARE_KEEP_PAGER
static TEE_Result invoke_command(
void *pSessionContext,
uint32_t cmd,
uint32_t ptypes,
[TEE_NUM_PARAMS]
TEE_Param params)
{
switch (cmd) {
case ...:
// Do stuff
default:
return TEE_ERROR_NOT_IMPLEMENTED;
}
return TEE_SUCCESS;
}
static TEE_Result create_entry_point(void)
{
(&itr);
itr_add(console_itr.it);
itr_enable
return TEE_SUCCESS;
}
(.uuid = uuid, .name = "Name",
pseudo_ta_register.flags = PTA_DEFAULT_FLAGS,
.invoke_command_entry_point = invoke_command,
.create_entry_point = create_entry_point);