You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
externinitcall_t__initcall_start[], __initcall_end[], __early_initcall_end[];
staticvoid__initdo_initcalls(void)
{
initcall_t*fn;
for (fn=__early_initcall_end; fn<__initcall_end; fn++)
do_one_initcall(*fn);
/* Make sure there is no pending stuff from the initcall sequence */flush_scheduled_work();
}
FUNC thread_vector_table , :
UNWIND( .fnstart)
UNWIND( .cantunwind)
b vector_std_smc_entry
b vector_fast_smc_entry
b vector_cpu_on_entry
b vector_cpu_off_entry
b vector_cpu_resume_entry
b vector_cpu_suspend_entry
b vector_fiq_entry
b vector_system_off_entry
b vector_system_reset_entry
UNWIND( .fnend)
END_FUNC thread_vector_table
staticpooloptee_msg_exchange_capabilities(optee_invoke_fn*invoke_fn,u32*sec_caps){
union{
structarm_smccc_ressmccc;
structoptee_smc_exchange_capabilities_resultresult;
} res;
u32a1=0;
//TODO this isn't enough to tell if it's UP system//(from kernel point of view) or not,is_smp() returns the information needed,//but can't be called directly from hereif(!IS_ENABLED(CONFIG_SMP) ||nr_cpu_ids==1)
a1 |=OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
//调用smc操作接口,获取secure world中的变量invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITES,a1,0,0,0,0,0,0,&res.smccc);
if(res.result.status!=OPTEE_SMC_RETURN_OK){
return false;
}
//将返回值中的变量赋值为sec_caps*sec_caps=res.result.capabilities;
return true;
}
staticinttee_ioctl_shm_alloc(structtee_context*ctx,
structtee_ioctl_shm_alloc_data__user*udata)
{
longret;
structtee_ioctl_shm_alloc_datadata;
structtee_shm*shm;
/* 将userspace传递的参数数据拷贝到kernel的buffer中 */if (copy_from_user(&data, udata, sizeof(data)))
return-EFAULT;
/* Currently no input flags are supported */if (data.flags)
return-EINVAL;
/* 将共享内存的ID值设置成-1,以便分配好共享内存之后重新赋值 */data.id=-1;
/* 调用tee_shm_all函数,从驱动与secure world之间的共享内存池中分配对应大小的内存,并设定对应的ID值 */shm=tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
if (IS_ERR(shm))
returnPTR_ERR(shm);
/* 设定需要返回给userspace的数据 */data.id=shm->id;
data.flags=shm->flags;
data.size=shm->size;
/* 将需要返回的数据从kernespace拷贝到userspace层面 */if (copy_to_user(udata, &data, sizeof(data)))
ret=-EFAULT;
elseret=tee_shm_get_fd(shm);
/* * When user space closes the file descriptor the shared memory * should be freed or if tee_shm_get_fd() failed then it will * be freed immediately. */tee_shm_put(shm); //如果分配的是DMA的buffer则要减少count值returnret;
}
u32optee_supp_thrd_req(structtee_context*ctx, u32func, size_tnum_params,
structtee_param*param)
{
structoptee*optee=tee_get_drvdata(ctx->teedev);
structoptee_supp*supp=&optee->supp;
structoptee_supp_req*req=kzalloc(sizeof(*req), GFP_KERNEL);
boolinterruptable;
u32ret;
if (!req)
returnTEEC_ERROR_OUT_OF_MEMORY;
/* 初始化该请求消息的c成员并配置请求数据 */init_completion(&req->c);
req->func=func;
req->num_params=num_params;
req->param=param;
/* Insert the request in the request list *//* 将接受到的请求添加到驱动的TEE请求消息队列中 */mutex_lock(&supp->mutex);
list_add_tail(&req->link, &supp->reqs);
mutex_unlock(&supp->mutex);
/* Tell an eventual waiter there's a new request *//* 将supp->reqs_c置位,通知tee_supplicant的receve操作,当前驱动中 有一个来自TEE的请求 */complete(&supp->reqs_c);
/* * Wait for supplicant to process and return result, once we've * returned from wait_for_completion(&req->c) successfully we have * exclusive access again. *//* block在这里,通过判定req->c是否被置位来判定当前请求是否被处理完毕, 而req->c的置位是有tee_supplicant的send调用来完成的,如果被置位,则进入到 while循环中进行返回值的设定并跳出while*/while (wait_for_completion_interruptible(&req->c)) {
mutex_lock(&supp->mutex);
interruptable= !supp->ctx;
if (interruptable) {
/* * There's no supplicant available and since the * supp->mutex currently is held none can * become available until the mutex released * again. * * Interrupting an RPC to supplicant is only * allowed as a way of slightly improving the user * experience in case the supplicant hasn't been * started yet. During normal operation the supplicant * will serve all requests in a timely manner and * interrupting then wouldn't make sense. */interruptable= !req->busy;
if (!req->busy)
list_del(&req->link);
}
mutex_unlock(&supp->mutex);
if (interruptable) {
req->ret=TEEC_ERROR_COMMUNICATION;
break;
}
}
ret=req->ret;
kfree(req);
returnret;
}
intoptee_supp_recv(structtee_context*ctx, u32*func, u32*num_params,
structtee_param*param)
{
structtee_device*teedev=ctx->teedev;
structoptee*optee=tee_get_drvdata(teedev);
structoptee_supp*supp=&optee->supp;
structoptee_supp_req*req=NULL;
intid;
size_tnum_meta;
intrc;
/* 对被用来存放TEE请求参数的数据的buffer进行检查 */rc=supp_check_recv_params(*num_params, param, &num_meta);
if (rc)
returnrc;
/* 进入到loop循环中,从 驱动的请求消息队列中获取来自TEE中的请求,直到获取之后才会跳出该loop*/while (true) {
mutex_lock(&supp->mutex);
/* 尝试从驱动的请求消息队列中获取来自TEE的一条请求 */req=supp_pop_entry(supp, *num_params-num_meta, &id);
mutex_unlock(&supp->mutex);
/* 判定是否获取到请求如果获取到了则跳出该loop */if (req) {
if (IS_ERR(req))
returnPTR_ERR(req);
break;
}
/* * If we didn't get a request we'll block in * wait_for_completion() to avoid needless spinning. * * This is where supplicant will be hanging most of * the time, let's make this interruptable so we * can easily restart supplicant if needed. *//* block在这里,直到在optee_supp_thrd_req函数中发送了complete(&supp->reqs_c)操作后才继续往下执行 */if (wait_for_completion_interruptible(&supp->reqs_c))
return-ERESTARTSYS;
}
/* 设定参数进行异步处理请求的条件 */if (num_meta) {
/* * tee-supplicant support meta parameters -> requsts can be * processed asynchronously. */param->attr=TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
TEE_IOCTL_PARAM_ATTR_META;
param->u.value.a=id;
param->u.value.b=0;
param->u.value.c=0;
} else {
mutex_lock(&supp->mutex);
supp->req_id=id;
mutex_unlock(&supp->mutex);
}
/* 解析参数,设定tee_supplicant将要执行的具体(加载TA,操作文件系统,操作EMMC的rpmb分区等)操作和相关参数 */*func=req->func;
*num_params=req->num_params+num_meta;
memcpy(param+num_meta, req->param,
sizeof(structtee_param) *req->num_params);
return0;
}
08_OPTEE-OS_系统集成之(六)TEE的驱动
OP-TEE驱动是REE侧与TEE侧之间进行交互的重要通道,在REE侧的CA接口以及RPC请求的接收和结果的返回最终都会被发送到驱动中,由驱动对数据做进一步的处理。OP-TEE驱动通过解析传入的参数,重新组合数据,将需要被传入到TEE侧的数据载入到共享内存中,触发安全监控模式调用(smc)进入到Monitor模式或EL3中将数据发送给TEE。
1. 编译
OP-TEE的驱动通过subsys_initcall和module_init宏来告知系统在初始化阶段的什么时候去加载OPTEE驱动。subsys_initcall定义在linux/include/init.h文件中。这是Linux内核提供的一系列init的操作集合中的一个。该方法利用Linux内核段属性机制。
OP-TEE驱动通过subsys_initcall和module_init宏来告知系统在初始化的什么时候去加载OP-TEE驱动,subsys_initcall定义在
linux/include/init.h
文件中,内容如下:使用subsys_initcall宏定义的函数最终会被编译到
.initcall4.init
段中,linux系统在启动的时候会执行initcallx.init
段中的所有内容,而使用subsys_initcall
宏定义段的执行优先级为4.module_init
的定义和相关扩展在linux/include/linux/module.h
文件和linux/include/linux/init.h
中,内容如下:由此可见,使用module_init宏构造的函数将会在编译的时候被编译到
initcall6.init
段中,该段在linux系统启动的过程中的优先等级为6.结合上述两点看,在系统加载OP-TEE驱动的时候,首先会执行OP-TEE驱动中使用subsys_init定义的函数,然后再执行使用module_init定义的函数。在OP-TEE驱动源代码中使用
subsys_init
定义的函数为tee_init
,使用module_init
定义的函数为optee_driver_init
。2. REE侧OP-TEE驱动的加载
OP-TEE驱动主要作用是REE与TEE端进行数据交互的桥梁作用。
tee_supplicant
和libteec
调用接口之后几乎都会首先通过系统调用陷入到kernel space,然后kernel根据传递的参数找到OP-TEE驱动,并命中驱动的operation结构体中的具体处理函数来完成实际的操作,对于OP-TEE驱动,一般都会触发SMC
调用,并带参数进入到ARM的monitor模式,在monitor模式中对执行normal world和secure world的切换,待状态切换完成之后,会将驱动端带入的参数传递給OP-TEE中的thread进行进一步的处理。OP-TEE驱动的源代码存放在linux/drivers/tee
目录中,其内容如下:https://github.com/carloscn/raspi-linux/tree/master/drivers/tee
OP-TEE驱动的加载过程分为两部分:
首先需要明白两个Linux内核中加载驱动的宏:subsys_initcall和module_init。OP-TEE驱动的第一部分是调用subsys_initcall宏来实现tee_init,而第二部分则是调用module_init宏来实现tee_driver_init。
OP-TEE驱动主要是被期望能够功能实现和数据交换。
2.1 tee_init
该函数定义在
linux/drivers/tee/tee_core.c
文件中,主要完成class的创建和设备号的分配,其内容如下:设备号
和class
将会在驱动执行probe
的时候被使用到。2.2 optee_driver_init
linux启动过程中会执行moudule_init宏定义的函数,在OP-TEE驱动的挂载过程中将会执行
optee_driver_ini
t函数,该函数定义在linux/drivers/tee/optee/core.c
文件中,其内容如下:2.3 挂载probe操作
OP-TEE驱动在optee_driver_init函数中完成probe操作。该函数首先会通过设备树找到OP-TEE驱动的设备信息,然后将获取到的信息传递给optee_probe函数执行probe操作。probe操作主要完成版本的校验、获取OP-TEE驱动与TEE侧共享内存的配置、建立共享内存的地址映射、添加安全监控模式调用(smc)接口、分配/dev/tee0和/dev/teepriv0设备、建立RPC请求队列等操作。optee_probe函数内容如下:
2.4 获取切换到Monitor模式或EL3的接口
正常世界状态与安全世界状态之间的切换是通过在Monitor模式或EL3下设定SCR寄存器中的安全状态位(NS bit)来实现的,OP-TEE驱动被上层调用时,最终会通过触发安全监控模式调用(smc)切换到Monitor模式或EL3,并通过共享内存的方式将数据发送给安全世界状态来进行处理。而用户触发安全监控模式调用的接口函数将在OP-TEE驱动初始化时被填充到OP-TEE驱动的device info中,在OP-TEE驱动中通过调用get_invoke_func函数来获取该接口的指针。该函数的内容如下:
执行安全监控模式调用指令会使ARM核进入EL3或Monitor模式。如果使用hvc,会是ARM核进入到EL2或者hyp模式,该模式主要用在使能虚拟机的系统上。这里以安全监控模式调用为例,实现系统状态切换的函数就是optee_smccc_smc,该函数内容如下:
即是函数get_invoke_func执行完成之后会返回arm_smccc_smc函数的地址。arm_smccc_smc函数就是驱动用来将cortex切换到monitor模式的函数,该函数是以汇编的方式编写,定义在
linux/arch/arm/kernel/smccc-call.S
文件中。如果是64位系统,则该函数定义在linux/arch/arm64/kernel/smccc-call.S
目录中,本文以32位系统为例,该函数内容如下:2.5 驱动版本和API版本校验
OP-TEE驱动挂载过程中会校验驱动的版本以及提供的API版本是否一致,该检查是通过触发快速安全监控模式调用(fast smc)从OP-TEE中获取到版本信息来实现的。快速安全监控模式调用与标准安全监控模式调用(std smc)的不同之处就在于第一个参数的BIT31的值不一样。
驱动加载过程中获取到REE侧与TEE侧之间进行交互的接口函数(调用
get_invoke_func
函数返回的函数地址)之后,OP-TEE驱动会对API的UID和版本信息进行校验。上述操作是通过调用optee_msg_api_uid_is_optee_api
函数和optee_msg_api_revision_is_compatible
函数来实现的。这两个函数的内容如下:2.6 判断OP-TEE是否预留了共享内存
OP-TEE驱动与TEE之间需要进行数据的交互,而进行数据交互则需要一定的共享内存来保存OP-TEE和驱动之间共有的数据。所以在驱动初始化时需要检查该共享内存空间是否被预留出来。通过获取安全世界状态(SWS)中的相关变量的值并判定该相关标识变量是否相等来判定安全世界状态是否预留有共享内存空间。在OP-TEE OS启动过程中,执行MMU初始化时会初始化该变量。在OPTEE驱动端通过调用
optee_msg_exchange_capabilities
函数来获取该变量的值。当驱动获取到sec_caps的值之后会查看该值是否为宏
OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM
定义的值BIT(0)
,如果该值不为BIT(0),则会报错,因为在secure world端都没有预留share memory空间,那驱动与secure world之间也就没法传输数据,所以有没有驱动也就没有必要了。2.7 配置驱动与OP-TEE之间的共享内存
驱动与安全世界状态之间的数据交互是通过共享内存来完成的,在OP-TEE启动过程中会将作为共享内存的物理内存块预留出来,具体可查看OPTEE启动代码中的
core_init_mmu_map
函数。OPTEE驱动初始化阶段会将预留出来作为共享内存的物理内存配置成驱动的内存池,并通知OP-TEE OS执行相同的操作。配置完成后,安全世界状态就能从共享内存中获取到来自REE侧的数据。OP-TEE驱动进行probe操作时,会调用到
optee_config_shm_memremap
函数来完成OP-TEE驱动和OP-TEE之间共享内存的配置。该函数定义在linux/drivers/tee/optee/core.c
文件中。其内容如下:在secure world中预留出来的内存块作为驱动与sercure world之间的共享内存使用。在驱动端将会建立一个内存池,以便驱动在需要的使用通过调用驱动的alloc函数来完成共享内存的分配。而共享内存池的建立是通过调用
tee_shm_pool_alloc_res_mem
来实现的。其函数内容如下:2.8 分配tee0和teepriv0变量
在OP-TEE驱动进行probe操作时会分配和设置两个tee_device结构体变量,分别用来表示被
libteec
库和tee_supplicant
使用的设备。分别通过执行tee_device_alloc(&optee_desc,NULL,pool,optee)
和tee_device_alloc(&optee_supp_desc,NULL,pool,optee)
来实现,主要是设置驱动被libteec库和tee_supplicant使用时的设备具体操作和设备对应的名称等信息。/dev/tee0
设备文件时,系统最终将调用到optee_desc
中具体的函数来实现对应操作。/dev/teepriv0
设备文件时,系统最终将调用到optee_supp_desc
中具体的函数来实现对应操作。上述配置操作都是通过调用
tee_device_all
函数来实现的。完成版本检查,共享内存池配置,不同设备的配置之后就需要将这些配置好的设备注册到系统中去。对于被liteec和tee_supplicant使用的设备分别通过调用
tee_device_register(optee->teedev)
和tee_device_register(optee->supp_teedev)
来实现。其中optee->teedev
和optee->supp_teedev
就是在上一章中被配置好的分别被libteec和tee_supplicant使用的设备结构体。调用tee_device_register
函数来实现将设备注册到系统的目的,该函数内容如下:2.9 两个队列初始化
OP-TEE驱动提供两个设备,分别是被libteec库使用的
/dev/tee0
和被tee_supplicant使用的/dev/teepriv0
。为确保正常世界状态与安全世界状态之间数据交互便利且能在正常世界状态进行异步处理,OP-TEE驱动在挂载时会建立两个类似于消息队列的队列,用于保存正常世界状态的请求数据和安全世界状态的请求。optee_wait_queue_init
用于初始化/dev/tee0
设备使用的队列,optee_supp_init
用于初始化/dev/teepriv0
设备使用的队列。其代码分别如下:2.10 TEE中的共享cache
当一切执行完之后,最后就剩下通知OP-TEE使能共享内存的缓存了,在OP-TEE驱动的挂载过程中通过调用
optee_enable_shm_cache
函数来实现使能共享内存Cache的操作。该函数内容如下:2.11 总结
从OP-TEE驱动的挂载过程来看,OP-TEE驱动会分别针对libteec库和tee_supplicant建立不同的设备/dev/tee0和/dev/teepriv0。同时为两个设备中的des配置各自独有的operation结构体变量,并建立类似消息队列来存放正常世界状态与安全世界状态之间的请求,这样libteec库和tee_supplicant使用OP-TEE驱动时就能做到相对的独立。安全世界状态与OPTEE驱动之间使用共享内存进行数据交互。用于作为共享内存的物理内存块在OP-TEE启动过程中进行MMU初始化时需要被预留出来,在OP-TEE驱动的挂载过程中需要将该内存块映射到系统内存中。
3. REE侧用户空间对驱动的调用
在Linux用户空间对文件系统中的文件执行打开、关闭、读写以及ioctl操作时,最终都会穿透到Linux内核空间执行具体的操作。而从用户空间陷入到内核空间是通过系统调用(systemcall)来实现的(关于syscall的实现可自行查阅资料了解),进入Linux内核空间后,系统会调用相应的驱动来获取设备对应的file_operations变量,该结构体变量中存放了对文件进行各种操作的具体函数指针。所以从用户空间对文件进行操作时,其整个过程大致如图所示。
调用libteec库中按照GP标准定义的API或tee_supplicant执行具体操作时都会经历图所示的流程:
4. OP-TEE驱动中的重要结构体变量
要了解OP-TEE驱动中具体做了哪些操作,首先需要了解在OP-TEE驱动中存在的四个重要的结构体,
libteec
和tee_supplicant
以及dma操作使用驱动时
会使用到这四个结构体,这四个结构体变量会在驱动挂载的时候被注册到系统设备模块或者是该设备的自由结构体中以便被userspace使用,而dma操作的时候会对共享内存进行注册。/dev/tee0
设备的tee_driver_ops结构体/dev/teepriv0
设备的tee_driver_ops结构体4.1 OP-TEE驱动的file_operation结构体变量tee_fops
OP-TEE驱动的file_operation结构体变量定义在
linux/drivers/tee/tee_core.c
文件中,该变量中包含了OP-TEE驱动文件操作函数指针,其内容如下:当在userspace层面调用open, release, ioctl函数操作驱动文件时就会调用到该结构体中的对应函数去执行具体操作。
4.2 OP-TEE驱动中
/dev/tee0
设备的tee_driver_ops结构体变量optee_ops当用户调用libteec中的接口的时,操作的就是OP-TEE驱动的
/dev/tee0
设备,而optee_ops变量中存放的就是针对/dev/tee0
设备的具体操作函数的指针,用户调用libteec接口时,首先会调用到tee_fops中的成员函数,tee_fops中的成员函数再会去调用到optee_ops中对应的成员函数来完成对/dev/tee0
设备的实际操作。optee_ops变量定义在linux/drivers/tee/optee/core.c
文件中。其内容如下:4.3 OP-TEE驱动中
/dev/teepriv0
设备的tee_driver_ops结构体变量optee_supp_ops当tee_supplicant需要执行相关操作时,操作的就是OP-TEE驱动的
/dev/teepriv0
设备,而optee_supp_ops变量中存放的就是针对/dev/teepriv0
设备的具体操作函数的指针,tee_supplicant执行相关操作时,首先会调用到tee_fops中的成员函数,tee_fops中的成员函数再会去调用到optee_supp_ops中对应的成员函数来完成对/dev/teepriv0
设备的实际操作。optee_supp_ops变量定义在linux/drivers/tee/optee/core.c
文件中。其内容如下:4.4 OP-TEE驱动中共享驱动缓存操作的dma_buf_ops结构体变量tee_shm_dma_buf_ops
OP-TEE驱动也支持其他设备访问OP-TEE驱动的共享缓存,该部分的功能当前并不算太完全,有一些功能尚未实现。该变量定义在
linux/drivers/tee/tee_shm.c
文件中,当需要被分配dma缓存时就会调用到该变量中对应的函数。其内容如下:NOTE: libteec中接口的执行和tee_supplicant功能的执行都会用上述四个结构体变量中的两个或者多个。
5. OP-TEE驱动与OP-TEE共享内存的注册和分配
当libteec和tee_supplicant需要分配和注册于secure world之间的共享内存时,可以通过调用驱动的ioctl方法来实现,然后驱动调用tee_ioctl_shm_alloc来实现具体的分配,注册共享内存的操作。该函数的内容如下:
而在tee_shm_all中驱动做了什么操作呢?该函数的内容如下:
从整个过程来看
共享内存
的分配或者是注册操作
时,驱动都会从驱动与secure world的共享内存池中分配一块内存,然后将该分配好的内存的id值返回给libteec,然后在libteec中TEEC_AllocateSharedMemory
函数,则会将该共享内存的id值进行mmap,然后将map后的值赋值给shm中的buffer成员。如果调用的是TEEC_RegisterSharedMemory
,则会将共享内存id执行mmap之后的值赋值给shm中的shadow_buffer成员。由此可见,当libteec中是执行注册共享内存操作时,并不是讲userspace的内存直接共享给secure world,而是将userspace的内存与驱动中分配的一块共享内存做shadow操作,是两者实现一个类似映射的关系。
6. libteec中的接口在驱动中的handle
libteec提供给上层使用的接口总共有十个,这十个接口的功能和定义介绍请参阅07_OPTEE-OS_系统集成之(五)REE侧上层软件,这十个接口通过系统调用最终会调用到驱动中,在接口libteec中调用Open函数的时候,在驱动中就对调用到file_operations结构体变量tee_fops中的Open成员。同理在libteec接口中调用ioctl函数,则在驱动中最终会调用tee_fops中的ioctl函数。本文将介绍驱动中file_operation结构体变量
tee_fops
中各成员函数的具体实现.驱动挂载完成后,CA程序通过调用libteec库中的接口调用OP-TEE驱动来穿透到OP-TEE中,然后调用对应的TA程序。OP-TEE驱动在挂载完成后会在/dev目录下分别创建两个设备节点,分别为
/dev/tee0
和/dev/teepriv
,对/dev/tee0
设备进行相关操作就能够穿透到OP-TEE中实现特定请求的发送。6.1 tee_open
tee_core:
https://github.com/carloscn/raspi-linux/blob/master/drivers/tee/tee_core.c#L109
调用栈如下:
在libteec中调用open函数来打开
/dev/tee0
设备的时候,最终会调用到tee_fops中的open成员指定的函数指针tee_open,该函数的内容如下:对于设备级别(
/dev/tee0
和/dev/teepriv0
),最终会调用到optee_open函数,该函数内容如下: https://github.com/carloscn/raspi-linux/blob/master/drivers/tee/optee/core.c#L220相应的 release的操作也是一样。
6.2 获取版本
在libteec中获取OP-TEE版本信息,创建和关闭session,调用TA,分配和注册共享内存和fd以及释放共享内存,接收来自OP-TEE的请求以及回复数据给OP-TEE都是通过ioctl来完成的。在libteec和tee_supplicant中通过带上对应的参数调用ioctl函数来实现对应的操作需求,最终会调用到OP-TEE驱动中file_operation结构体变量tee_fops变量中的tee_ioctl函数,该函数的内容如下。
当libteec和tee_supplicant调用ioctl来获取OP-TEE OS的版本信息时,驱动会执行tee_ioctl_version函数,该函数内容如下:
而设备的
get_version
的内容如下:6.3 libteec中open session的操作
当用户调用libteec中的TEEC_OpenSession接口时会执行驱动中ioctl函数的TEE_IOC_OPEN_SESSION分支去执行tee_ioctl_open_session函数,该函数只会在打开了
/dev/tee0
设备之后才能被使用,其的内容如下:调用设备的open_session操作来完成向OP-TEE发送打开与特定TA的session操作,open_session函数的执行流程如下图所示:
整个调用中会调用
optee_do_call_with_arg
函数来完成驱动与secure world之间的交互,该函数的内容如下:6.4 invoke操作
当完成了session打开之后,用户就可以调用TEEC_InvokeCommand接口来调用对应的TA中执行特定的操作了,TEEC_InvokeCommand函数最终会调用驱动的tee_ioctl_invoke函数来完成具体的操作。该函数内如如下:
7. tee_supplicant接口在驱动的实现
在07_OPTEE-OS_系统集成之(五)REE侧上层软件一文中介绍了tee_supplicant主要作用,用来实现
secure world端操作REE侧文件系统
,EMMC的rpmb分区
,网络socket操作
,数据库操作
的需求。tee_supplicant与OP-TEE之间的交互模式类似于生产者与消费者的关系。完成上述需求的整个过程包含驱动接收来自OP-TEE的请求、tee_supplicant从驱动中获取OP-TEE的请求并处理、驱动返回请求操作结果给OP-TEE三部分。
当libtee调用驱动来与OP-TEE进行数据的交互的时候,最终会调用
optee_do_call_with_arg
函数完成完成smc的操作,而该函数中有一个loop循环,每次触发smc操作之后会对从secure world中返回的参数res.a0
进行判断,判定当前从secure world返回的数据是要执行RPC操作还是直接返回到CA。如果是来自TEE的RPC请求,则会将请求存放到请求队列req中。然后block住,直到tee_supplicant处理完请求并将
req->c
标记为完成之后才会进入下一个loop,重新出发smc操作,将处理结果返回给TEE。7.1 driver获取来自TEE侧的请求
当libteec调用了需要做smc操作的请求之后,最终会调用到驱动的
optee_do_call_with_arg
函数,该函数会进入到死循环,第一条语句就会调用smc操作,进userspace的请求发送到secure world,待从secure world中返回之后。会对返回值进行判定。如果返回的res.a0
参数是需要驱动做RPC操作,则该函数会调用到optee_handle_rpc
操作。经过各种参数分析和函数调用之后,程序最后会调用optee_supp_thrd_req
函数来将来自TEE的请求存放到tee_supplicant的请求队列中。该函数的内容如下:当请求被处理完成之后,函数返回处理后的数据到optee_do_call_with_arg函数中,并进入optee_do_call_with_arg函数中while中的下一次循环,将处理结果返回给secure world.
7.2 tee_supplicant从驱动中获取TEE侧的请求
在tee_supplicant会调用read_request函数来从驱动的请求队列中获取当前存在的来自TEE的请求。该函数最终会调用到驱动中的optee_supp_recv函数。该函数的内容如下:
从请求消息队列中获取到来自TEE的请求之后,返回到tee_supplicant中继续执行。根据返回的func值和参数执行TEE要求在REE端需要的操作
7.3 驱动返回请求操作的结果给TEE侧
当tee_supplicant执行完TEE请求的操作之后,会调用write_response函数来实现将数据返回给TEE。而write_response函数最终会调用到驱动的optee_supp_send函数。该函数主要是调用
complete(&req->c);
操作来完成对该请求的c成员的置位,告诉optee_supp_thrd_req函数执行下一步操作,返回到optee_do_call_with_arg函数中进入该函数中的下一轮loop中,调用smc操作将结果返回给TEE侧。optee_supp_send函数的内容如下:7.4 总结
从tee_supplicant处理来自TEE侧的请求来看主要是有三个点:
231
Ref
Footnotes
SMC CALLING CONVENTION System Software on ARM Platforms.pdf ↩ ↩2
linux内核中的subsys_initcall是干什么的? ↩
01-OP-TEE驱动篇----驱动编译,加载和初始化(一).md ↩
The text was updated successfully, but these errors were encountered: