OP-TEE TA Debugging: How to Read DMSG/IMSG Logs?

Hey,

I have successfully created a Trusted Application (TA) for OP-TEE following the developer guide.
The compilation and execution of the TA via the host application seem to work because I do not get any errors, and the "printf" command in the CA prints output to the terminal.

However, I am unable to see the debug output from DMSG() and IMSG() in my Trusted Application.
I would like to know how to correctly retrieve OP-TEE debug messages.

Additionally, in the developer guide, I read that some TAs need to be signed. Do I have to do that for my own created TA, or is signing only required for specific use cases?

Here is my code:

** Makefile - Root **

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

** host/Makefile **

CC ?= $(CROSS_COMPILE)gcc

CFLAGS += -Wall -I../ta/include -I$(TEEC_EXPORT)/include -I./include
CFLAGS += -fstack-protector-strong
LDADD += -lteec -L$(TEEC_EXPORT)/lib

BINARY = gpio_host_app
OBJS = main.o

.PHONY: all
all: $(BINARY)

$(BINARY): $(OBJS)
	$(CC) -o $@ $^ $(LDADD)

.PHONY: clean
clean:
	rm -f $(OBJS) $(BINARY)

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

** host/main.c **

#include <err.h>
#include <stdio.h>
#include <string.h>
#include <tee_client_api.h>
#include <gpio_ta.h>

int main(void) {
    TEEC_Context ctx;
    TEEC_Session sess;
    TEEC_Result res;
    TEEC_Operation op;
    uint32_t err_origin;
    TEEC_UUID uuid = GPIO_TA_UUID;

    res = TEEC_InitializeContext(NULL, &ctx);
    if (res != TEEC_SUCCESS)
        errx(1, "TEEC_InitializeContext failed with code 0x%x", res);

    res = TEEC_OpenSession(&ctx, &sess, &uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
    if (res != TEEC_SUCCESS)
        errx(1, "TEEC_OpenSession failed with code 0x%x origin 0x%x", res, err_origin);

    memset(&op, 0, sizeof(op));
    res = TEEC_InvokeCommand(&sess, GPIO_TA_VAR, &op, &err_origin);
    if (res != TEEC_SUCCESS)
        errx(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x", res, err_origin);

    TEEC_CloseSession(&sess);
    TEEC_FinalizeContext(&ctx);

    printf("GPIO set to HIGH successfully!\n");
    return 0;
}

** ta/Makefile **

BINARY=12345678-1234-1234-1234-567890abcdef

include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk

** ta/sub.mk**

srcs-y += gpio_ta.c
global-incdirs-y += include

** ta/user_ta_header_defines.h**

#ifndef USER_TA_HEADER_DEFINES_H
#define USER_TA_HEADER_DEFINES_H

#include <gpio_ta.h>

#define TA_UUID GPIO_TA_UUID

#define TA_FLAGS (TA_FLAG_EXEC_DDR | TA_FLAG_SINGLE_INSTANCE | TA_FLAG_MULTI_SESSION)
#define TA_STACK_SIZE (2 * 1024)
#define TA_DATA_SIZE (32 * 1024)

#define TA_CURRENT_TA_EXT_PROPERTIES \
    { "gp.ta.description", USER_TA_PROP_TYPE_STRING, "GPIO TA" }, \
    { "gp.ta.version", USER_TA_PROP_TYPE_U32, &(const uint32_t){ 0x0100 } }

#endif /* USER_TA_HEADER_DEFINES_H */

** ta/gpio_ta.c**

#include <tee_internal_api.h>
#include <tee_api.h>
#include <tee_internal_api_extensions.h>
#include <gpio_ta.h>

TEE_Result TA_CreateEntryPoint(void) {
    DMSG("TA_CreateEntryPoint has been called");
    return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void) {
    DMSG("TA_DestroyEntryPoint has been called");
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t param_types,
    TEE_Param __maybe_unused params[4],
    void __maybe_unused **session)
{
    uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE,
                        TEE_PARAM_TYPE_NONE);

    DMSG("Session open has been called");

    if (param_types != exp_param_types)
        return TEE_ERROR_BAD_PARAMETERS;

    (void)&params;
    (void)&session;

    IMSG("Session created successfully!");

    return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void *session) {
    (void)&session;
    DMSG("TA_CloseSessionEntryPoint has been called");
    IMSG("Closing session.");
}

TEE_Result TA_InvokeCommandEntryPoint(void *session, uint32_t command_id, uint32_t param_types,
                                      TEE_Param params[4]) {
    (void)&session;
    (void)&param_types;
    (void)&params;

    switch (command_id) {
        case GPIO_TA_VAR:
            DMSG("DMSG TEST");
            IMSG("IMSG TEST.");
            return TEE_SUCCESS;
        default:
            return TEE_ERROR_NOT_SUPPORTED;
    }
}

** ta/include/gpio_ta.h**

#ifndef GPIO_TA_H
#define GPIO_TA_H

#define GPIO_TA_VAR 0

#define GPIO_TA_UUID \
	{ 0x12345678, 0x1234, 0x1234, \
		{ 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef} }

#endif /* GPIO_TA_H */

hello nik01flink,

please check the Trusted Applications section of the OP-TEE official documentation for a detailed description with a hello_world example.

Hey JerryChang,

Thanks for your reply. I followed the page you suggested to create my TA. Before I started developing my own TA, I also copied the “hello world” example into my optee-samples folder, and it worked successfully. The output I got was:

Invoking TA to increment 42
TA incremented value to 43

Since the hello-world example worked without signing, and the TA I created prints "GPIO set to HIGH successfully" (which comes from a printf command in host/main.c), I assumed that signing is not necessary if I use the default signature.

Regarding the DMSG / IMSG debug prints, I couldn’t find much information online. Should the DMSG prints be visible when I run the following command?

dmesg | grep TEST

(I use "grep test" because I have a DMSG("DMSG TEST") statement in my TA_InvokeCommandEntryPoint function inside the .c file of my Trusted Application.)

Thanks in advance for your help!

hello nik01flink,

is it due to log level configuration? let’s give it a try by using EMSG("logs.."); to print the debug logs.

Hey,

I have added an EMSG into the TA_InvokeCommandEntryPoint function like this:

TEE_Result TA_InvokeCommandEntryPoint(void *session, uint32_t command_id, uint32_t param_types,
                                      TEE_Param params[4]) {
    (void)&session;
    (void)&param_types;
    (void)&params;

    switch (command_id) {
        case GPIO_TA_VAR:
            DMSG("DMSG-TEST");
            EMSG("EMSG-TEST");
            IMSG("IMSG-TEST");
            printf("printf-TEST");
            return TEE_SUCCESS;
        default:
            return TEE_ERROR_NOT_SUPPORTED;
    }
}

I’m not sure which command to use, so I’m testing them all. However, when I run the application, the only output I see is from the printf commands on the host side. Are debug commands inside the TA even displayed at all?

I also checked some more examples and noticed that I forgot to add the following lines to the TA-Makefile:

CFG_TEE_TA_LOG_LEVEL ?= 4 
CFG_TA_OPTEE_CORE_API_COMPAT_1_1=y

I added them, but it still had no effect. (also tried log level 3)

hello nik01flink,

is it due to this case GPIO_TA_VAR did not processed? how about adding debug prints to the TA entry point?

Okay, I have tried that as well now, but I am still getting the same result. The only outputs I see are the printf statements from the host side.

To further test this, I also tried it with the hello_world example. I added the following lines into the TA_InvokeCommandEntryPoint function like this:

TEE_Result TA_InvokeCommandEntryPoint(void __maybe_unused *sess_ctx,
			uint32_t cmd_id,
			uint32_t param_types, TEE_Param params[4])
{
	(void)&sess_ctx; /* Unused parameter */

	switch (cmd_id) {
	case TA_HELLO_WORLD_CMD_INC_VALUE:
		DMSG("DMSG-TEST");
		EMSG("EMSG-TEST");
		IMSG("IMSG-TEST");
		printf("printf-TEST");
		return inc_value(param_types, params);
	case TA_HELLO_WORLD_CMD_DEC_VALUE:
		return dec_value(param_types, params);
	default:
		return TEE_ERROR_BAD_PARAMETERS;
	}
}

Since the hello_world example is working as expected (incrementing a value), I can now be certain that the debug lines are actually being executed.
However, there is still no output from DMSG, EMSG, or IMSG in the terminal.

So, I assume that none of these debug commands actually result in visible output.

hello nik01flink,

the default optee logs level is 2, which means IMSG and EMSG should works.
may I know how you check the logs, that debug prints are only outputted to UART console.
please see-also Topic 251082.

Hey JerryChang,

I didn’t check the logs on a UART console since I don’t have a TTL cable for that. Is it possible to use a USB-A to USB-A cable with Minicom and Putty, for example, or do I specifically need a TTL cable to read the UART output?

I also didn’t really check the logs directly—I just assumed they would be printed in the terminal after running the application 😅

hello nik01flink,

you’ll need USB to TTL since there’re pins for general UART (i.e. /dev/ttyTHS0)

Hey,

okay .. did not know that it is only possible with UART. I ordered a UART adapter, connected it to the appropriate pins (TXD, RXD, GND) and with the help of minicom I can now see the debug commands on the host when I start the Trusted Application via minicom.

Thanks for your help

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.