LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux TEE子系统:TEE子系统、OPTEE驱动、tee-supplicant

Linux TEE(以RISC-V为例)解决方案大致如下:

  • TEE中运行TOS,比如OPTEE OS,及运行于其上的TA(Trusted Application)。
  • SecureMonitor,比如OpenSBI,负责安全和非安全切换,以及安全任务分发。
  • Linux kernel中TEE子系统,负责将CA安全请求转达到SecureMonitor。
  • CA是TOS的Client Application,负责发起安全请求。

Linux侧OPTEE的实现包括:

  • tee-supplicant基于libteec.so,打开/dev/teepriv0和Kernel OPT-EE驱动交互。
  • OPT-EE CA基于libteec.so,打开/dev/tee0和Kernel OPTEE驱动交互。
  • Linux kernel OPTEE驱动,

下面重点分析Linux kernel中的TEE子系统。

Linux TEE子系统主要处理TEE驱动注册、LInux和TEE之间共享内存管理、提供一套用于TEE的标准API。

Linux内核中的TEE子系统可以分为两部分:

  • TEE框架:包括总线、Classh、TEE内核API、TEE设备属性、TEE文件操作函数集等。
  • TEE驱动:特定TEE OS对应的Linux驱动,基于TEE框架创建设备,提供CA和TEE OS的交互。本文以OP-TEE为例。

1. TEE子系统

1.1 TEE子系统初始化

TEE框架初始化通过tee_init()进行,主要创建tee_class、TEE字符设备号范围、注册TEE总线。

tee_init
  class_register
  alloc_chrdev_region--分配tee字符设备号,最多32个子设备。
  bus_register--注册tee总线。
tee_exit
  bus_unregister
  unregister_chrdev_region
  class_unregister

TEE总线负责TEE驱动(比如OP-TEE驱动)和TEE设备直接的匹配以及TEE uevent事件上报操作。

static int tee_client_device_match(struct device *dev,
                   struct device_driver *drv)
{
    const struct tee_client_device_id *id_table;
    struct tee_client_device *tee_device;

    id_table = to_tee_client_driver(drv)->id_table;
    tee_device = to_tee_client_device(dev);

    while (!uuid_is_null(&id_table->uuid)) {
        if (uuid_equal(&tee_device->id.uuid, &id_table->uuid))------------遍历id_table,判断是否匹配tee_device->id.uuid。如匹配则表示设备和驱动匹配成功。
            return 1;
        id_table++;
    }

    return 0;
}

static int tee_client_device_uevent(struct device *dev,
                    struct kobj_uevent_env *env)
{
    uuid_t *dev_id = &to_tee_client_device(dev)->id.uuid;

    return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id);-------------通知用于空间TEE设备dev_id产生。
}

struct bus_type tee_bus_type = {
    .name        = "tee",
    .match        = tee_client_device_match,
    .uevent        = tee_client_device_uevent,
};
EXPORT_SYMBOL_GPL(tee_bus_type);

 1.2 TEE子系统API

TEE子系统提供的API包括:

  • TEE设备:
    • tee_device_alloc:用于分配并初始化一个新的TEE设备结构体。
    • tee_device_register:注册一个TEE设备。
    • tee_device_unregister:注销一个TEE设备。
    • teedev_open:打开一个TEE设备上下文。
    • teedev_close_context:关闭一个TEE上下文。
  • TEE shm:
    • tee_shm_pool_alloc_res_mem:分配一个共享内存池,用于TEE设备之间的内存共享。
    • tee_shm_pool_free:释放一个共享内存池。
struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
                    struct device *dev,
                    struct tee_shm_pool *pool,
                    void *driver_data);
int tee_device_register(struct tee_device *teedev);
void tee_device_unregister(struct tee_device *teedev);
struct tee_context *teedev_open(struct tee_device *teedev);
void teedev_close_context(struct tee_context *ctx);
struct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr,
                        phys_addr_t paddr, size_t size,
                        int min_alloc_order);
static inline void tee_shm_pool_free(struct tee_shm_pool *pool)
struct tee_shm *tee_shm_alloc_priv_buf(struct tee_context *ctx, size_t size);
int tee_dyn_shm_alloc_helper(struct tee_shm *shm, size_t size, size_t align,
                 int (*shm_register)(struct tee_context *ctx,
                         struct tee_shm *shm,
                         struct page **pages,
                         size_t num_pages,
                         unsigned long start));
void tee_dyn_shm_free_helper(struct tee_shm *shm,
                 int (*shm_unregister)(struct tee_context *ctx,
                           struct tee_shm *shm));

tee_device_alloc接受一个TEE设备描述符(`teedesc`),一个设备结构体(`dev`),一个共享内存池(`pool`),以及一个指向驱动数据的指针(`driver_data`)。返回值是一个指向新分配的`tee_device`结构:

tee_device_alloc
  --分配sturct tee_device,并初始化名称层。
  cdev_init--初始化tee字符设备,操作函数集为tee_fops。
  device_initialize
  init_completion
  mutex_init
  idr_init

tee_fops是tee设备的操作函数集:

static const struct file_operations tee_fops = {
    .owner = THIS_MODULE,
    .open = tee_open,
    .release = tee_release,
    .unlocked_ioctl = tee_ioctl,
    .compat_ioctl = compat_ptr_ioctl,
};

重点在tee_ioctl对ioctl命令的处理:

  • TEE_IOC_VERSION:用于获取TEE驱动的版本信息和能力。这个命令允许用户空间程序查询TEE驱动的实现ID和能力,以确保兼容性。
  • TEE_IOC_SHM_ALLOC:分配共享内存。此命令用于在TEE客户端和TEE环境之间分配一块共享内存,以便它们可以交换数据。
  • TEE_IOC_OPEN_SESSION:打开会话。此命令用于在TEE环境中打开一个会话,以便与特定的可信应用(TA)进行交互。
  • TEE_IOC_INVOKE:调用函数。此命令用于在TEE环境中的可信应用上调用一个函数,执行特定的操作。
  • TEE_IOC_CANCEL:取消请求。此命令用于取消正在进行的会话打开或函数调用。
  • TEE_IOC_CLOSE_SESSION:关闭会话。此命令用于关闭与TEE环境中的可信应用的会话。
  • TEE_IOC_SUPPL_RECV:接收供应商特定的数据。此命令用于从TEE环境接收供应商特定的数据或响应。
  • TEE_IOC_SUPPL_SEND: 发送供应商特定的数据。此命令用于向TEE环境发送供应商特定的数据或命令。
  • TEE_IOC_SHM_REGISTER:注册共享内存。此命令允许用户空间程序注册已经分配的共享内存,使其可以被TEE环境访问。

2. TEE驱动:OPTEE

2.1 OPTEE设备

optee驱动设备:

        optee: optee {
            compatible = "linaro,optee-tz";
            method = "smc";
        };

RISC-V用于TEE通信的channel配置:

    sbi-mpxy-opteed {
        opensbi-domain-instance = <&tdomain>;
        riscv,sbi-mpxy-channel-id = <0x02>;
        compatible = "riscv,sbi-mpxy-opteed";
    };

2.2 OPTEE驱动

OP-TEE的配置如下:

static const struct of_device_id optee_dt_match[] = {
    { .compatible = "linaro,optee-tz" },
    {},
};
MODULE_DEVICE_TABLE(of, optee_dt_match);

static struct platform_driver optee_driver = {
    .probe  = optee_probe,
    .remove_new = optee_smc_remove,
    .shutdown = optee_shutdown,
    .driver = {
        .name = "optee",
        .of_match_table = optee_dt_match,
    },
};

OP-TEE驱动初始化:

optee_core_init
  optee_smc_abi_register
    platform_driver_register
      optee_driver

当dts匹配后optee_probe()进行:

  • 进行RISCV特有的通道初始化。
  • 根据配置获取invoke函数。
  • 加载OPTEE固件。
  • 检查OPTEE能力和兼容性等。
  • 分配/dev/teeX和/dev/teeprivX等相关资源。
static int optee_probe(struct platform_device *pdev)
  sbi_mpxy_tee_probe--RISCV特有通道初始化。
  get_invoke_func--获取TEE调用触发函数,如果SMC则对应optee_smccc_smc。
  optee_load_fw--用于加载OP-TEE固件。
  optee_msg_api_uid_is_optee_api--用于检查API的UID是否匹配OP-TEE API。   optee_msg_get_os_revision--用于获取OPTEE操作系统的版本信息。
  optee_msg_api_revision_is_compatible--检查API的版本信息是否兼容。
  optee_msg_get_thread_count
  optee_msg_exchange_capabilities--用于交换TEE的能力和特性信息。
  tee_device_alloc--分配/dev/teeX和/dev/teeprivX设备对应的TEE结构体。
  tee_device_register--分别注册/dev/teeX和/dev/teeprivX设备。
  optee_cq_init--初始化OP-TEE的命令队列。
  optee_supp_init--supplicant相关初始化。
  optee_shm_arg_cache_init
  teedev_open
  optee_notif_init
  optee_disable_unmapped_shm_cache
  optee_enumerate_devices

struct tee_driver_ops是TEE设备驱动的操作函数集合:

struct tee_driver_ops {
    void (*get_version)(struct tee_device *teedev, struct tee_ioctl_version_data *vers);--返回驱动版本信息。
    int (*open)(struct tee_context *ctx);--当设备文件被打开时被调用。
    void (*release)(struct tee_context *ctx);--释放此打开的文件。
    int (*open_session)(struct tee_context *ctx, struct tee_ioctl_open_session_arg *arg, struct tee_param *param);--打开一个新的会话。
    int (*close_session)(struct tee_context *ctx, u32 session);--关闭一个会话。
    int (*system_session)(struct tee_context *ctx, u32 session);--声明会话为系统会话。
    int (*invoke_func)(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param);--调用一个TEE函数。
    int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session);--请求取消正在进行的调用或打开。
    int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param);--供supplicant获取命令时被调用。
    int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params, struct tee_param *param);--供supplicant发送响应时被调用。
    int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm, struct page **pages, size_t num_pages, unsigned long start);--在TEE中注册共享内存缓冲区。
    int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);--在TEE中注销共享内存缓冲区。
};

TEE最多32个设备,/dev/teeX使用前16个,/dev/teeprivX使用后16个。

static const struct tee_driver_ops optee_clnt_ops = {
    .get_version = optee_get_version,
    .open = optee_smc_open,
    .release = optee_release,
    .open_session = optee_open_session,
    .close_session = optee_close_session,
    .system_session = optee_system_session,
    .invoke_func = optee_invoke_func,
    .cancel_req = optee_cancel_req,
    .shm_register = optee_shm_register,
    .shm_unregister = optee_shm_unregister,
};

static const struct tee_desc optee_clnt_desc = {
    .name = DRIVER_NAME "-clnt",
    .ops = &optee_clnt_ops,
    .owner = THIS_MODULE,
};

static const struct tee_driver_ops optee_supp_ops = {
    .get_version = optee_get_version,
    .open = optee_smc_open,
    .release = optee_release_supp,
    .supp_recv = optee_supp_recv,
    .supp_send = optee_supp_send,
    .shm_register = optee_shm_register_supp,
    .shm_unregister = optee_shm_unregister_supp,
};

static const struct tee_desc optee_supp_desc = {
    .name = DRIVER_NAME "-supp",
    .ops = &optee_supp_ops,
    .owner = THIS_MODULE,
    .flags = TEE_DESC_PRIVILEGED,
};

3 TEE用户空间程序

3.1 tee-supplicant

/etc/init.d/S30tee-supplicant启动tee-supplicant守护进程。

tee-supplicant是一个用户空间守护进程,它与 OP-TEE 操作系统通信,处理来自 OP-TEE OS 的请求,例如访问文件系统、RPMB 或网络资源。

/dev/teepriv0是Linux 内核中的一个设备节点,由 OP-TEE 驱动创建。当tee-supplicant需要执行相关操作时,操作的就是OP-TEE驱动的 /dev/teepriv0 设备。tee-supplicant 执行相关操作时,首先会调用到tee_fops中的成员函数,tee_fops中的成员函数再会去调用到optee_supp_ops中对应的成员函数来完成对/dev/teepriv0设备的实际操作 。

总结来说,tee-supplicant通过/dev/teepriv0与OP-TEE 驱动通信,处理OP-TEE OS的请求,而/dev/teepriv0是OP-TEE 驱动在 Linux 内核中创建的设备节点,用于tee-supplicant与OP-TEE驱动之间的交互 。

#!/bin/sh

DAEMON="tee-supplicant"
PIDFILE="/var/run/$DAEMON.pid"

DAEMON_ARGS="-d /dev/teepriv0"

start() {
    printf 'Starting %s: ' "$DAEMON"
    # shellcheck disable=SC2086 # we need the word splitting
    start-stop-daemon -S -q -p "$PIDFILE" -x "/usr/sbin/$DAEMON" \
        -- $DAEMON_ARGS
...
}

stop() {
...
}
...

case "$1" in
    start|stop|restart)
        "$1";;
    reload)
        # Restart, since there is no true "reload" feature (does not
        # reconfigure/restart on SIGHUP, just closes all open files).
        restart;;
    *)
        echo "Usage: $0 {start|stop|restart|reload}"
        exit 1
esac

3.2 OPTEE CA

基于libteec.so编写OPTEE CA程序,以optee-example-hello-world为例:

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

/* OP-TEE TEE client API (built by optee_client) */
#include <tee_client_api.h>

/* For the UUID (found in the TA's h-file(s)) */
#include <hello_world_ta.h>

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

    /* Initialize a context connecting us to the TEE */
    res = TEEC_InitializeContext(NULL, &ctx);--初始化TEE会话上下文。
    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);--打开TEE会话。
    if (res != TEEC_SUCCESS)
        errx(1, "TEEC_Opensession failed with code 0x%x origin 0x%x",
            res, err_origin);

    memset(&op, 0, sizeof(op));

    op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE,
                     TEEC_NONE, TEEC_NONE);--配置TEE参数。
    op.params[0].value.a = 42;

    printf("Invoking TA to increment %d\n", op.params[0].value.a);
    res = TEEC_InvokeCommand(&sess, TA_HELLO_WORLD_CMD_INC_VALUE, &op,
                 &err_origin);--调用TA函数,并返回结果到op。
    if (res != TEEC_SUCCESS)
        errx(1, "TEEC_InvokeCommand failed with code 0x%x origin 0x%x",
            res, err_origin);
    printf("TA incremented value to %d\n", op.params[0].value.a);

    TEEC_CloseSession(&sess);--关闭TEE会话。

    TEEC_FinalizeContext(&ctx);--释放资源。

    return 0;
}

参考文档:《TEE subsystem

posted on 2024-11-23 14:54  ArnoldLu  阅读(169)  评论(0编辑  收藏  举报

导航