TEE: OP-TEE
OP-TEE是Linaro开发的Security OS。n年前,最终选定OP-TEE作为我们的移植目标,并最终实现。基于当年的OP-TEE版本,除增加了支持多核的功能,还基于Power state coordination interface(PSCI)协议实现了CPU的电源管理。此文档其实是自己当年的学习记录。
ARM后期期望走类似于Intel的路,抛弃Linux中的一些底层设备操作,将相关功能集成到自己的firmware中,从而诞生了secure-firmware。ARM将OP-TEE正式加入到此firmware中,作为runtime service。
OP-TEE
OP-TEE的结构流程图如下:
图中的USER表示none-security-world的user space;KERNEL表示none-security-world的linux kernel space。而图的最下面部分则是security-world部分,即OP-TEE。
OP-TEE支持Process以及user space。上图是Process的memory layout,包括kernel space部分和user space部分。
上图则是OP-TEE的核心调度代码。也不知道OP-TEE现在的代码长啥样了。。。
User TA
在编译arm elf时,所用的链接脚本是: ./ta/arch/arm32/user_ta_elf_arm.lds
SECTIONS
{
.ta_head : {*(.ta_head)}
.ta_func_head : {*(.ta_func_head)}
。。。。。
}
而在 ./ta/arch/arm32/user_ta_header.c 定义了TA的head:
const struct user_ta_func_head user_ta_func_head[]
__attribute__ ((section(".ta_func_head"))) =
{
{
0, (uint32_t) ta_entry_open_session}, {
0, (uint32_t) ta_entry_close_session}, {
0, (uint32_t) ta_entry_invoke_command}, {
TA_FLAGS, 0 /* Spare */ },
{
TA_DATA_SIZE, TA_STACK_SIZE},};
- ta_entry_open_session:
—> ta_header_add_session –> TA_CreateEntryPoint
—> TA_OpenSessionEntryPoint - ta_entry_invoke_command:
—> TA_InvokeCommandEntryPoint - ta_entry_close_session:
Static TA
Static TA是指静态编译进TEE的TA,会将TA header保存于section(“ta_head_section”)。
TEE的链接脚本: ./core/arch/arm32/plat-orly2/tz-template.lds
SECTIONS
{
.teecore_exec :
{
*(.vector_table)
*(.text); *(.text.*)
*(.rodata); *(.rodata.*)
*(.got); *(.got.*)
*(.data); *(.data.*)
__start_ta_head_section = . ;
*(ta_head_section)
__stop_ta_head_section = . ;
。。。。。
}
样例可见:./core/arch/arm32/sta/sta_helloworld.c
__attribute__ ((section("ta_head_section")))
const ta_static_head_t sta_helloworld_head = {
.uuid = STA_HELLOWORLD_UUID,
.name = (char *)TA_NAME,
.create_entry_point = create_ta,
.destroy_entry_point = destroy_ta,
.open_session_entry_point = open_session,
.close_session_entry_point = close_session,
.invoke_command_entry_point = invoke_command,
};
在load TA时,会优先从__start_ta_head_section~__stop_ta_head_section 读取
ta_static_head_t,查找是否有所需的UUID。如有,则读取信息;如没有,利用RPC向REE发起读取 TA请求,然后由TEE利用tee_ta_load解析TA并load。
Client API–> TEE
在tee_dispatch中定义了全局链表:
/* Sessions opened from normal world */
static struct tee_ta_session_head tee_open_sessions =
TAILQ_HEAD_INITIALIZER(tee_open_sessions);
维护打开的session obj的Context。
Dispatch中有如下4种操作:
tee_dispatch_open_session
tee_dispatch_close_session
tee_dispatch_invoke_command
tee_dispatch_cancel_command
涉及到对session obj的添加,删除,查询操作。
在ta_manager定义了全局链表:
struct tee_ta_ctx_head tee_ctxes = TAILQ_HEAD_INITIALIZER(tee_ctxes);
维护已经opened TA的Context。
访问流程如下所述:
- NS: TEEC_OpenSession–> smc CMD_TEEC_OPEN_SESSION
- S: 对于Rpc,分配1个新的thread运行此rpc请求。
注:此时是kernel线程,运行在svc模式,共享kernel空间。如果TA是TA_FLAG_USER_MODE,后期利用tee_user_ta_enter进入user模式前,会在tee_ta_ctx中创建TA页表,且切换至此页表。TA页表的空间范围包括:
tee core space, TA data&code, TA stack&heap, TA param。
代码调用流程如下:
-> main_tee_entry -> tee_entry -> tee_entry_call_with_arg -> entry_open_session
-> tee_dispatch_open_session
- tee_ta_open_session
- tee_ta_init_session
- 如果所带session ptr有效(在opened session ptr中或指示为static TA),则返回;
- 如果UUID非0,则在Ta-context-link中查找对应的 Context。如可以找到,说明TA之前已经load。创建session对象,添加进opend-session-link,并返回。
- 至此,说明TA还未load,开始load TA。
- 如果TA已经被读至Ram,则load此TA,且创建tee_ta_ctx,且将至加入到Ta-context-link中。此种情形下,默认都是user TA, 即TA_FLAG_USER_MODE。
- 如果UUID不为0,则在static TA中查找UUID,如找到,则此TA context 添加入Ta-context-link。Static TA都具有TA_FLAG_MULTI_SESSION属性。
- 将当前session添加进opend-session-link。如果是static TA,则调用其
COMMAND_CREATE_ENTRY_POINT回调。
- 如果是创建session情形,则对于static TA,则调用TA COMMAND_OPEN_SESSION;
对于user TA,调用TA USER_TA_FUNC_OPEN_CLIENT_SESSION。
- tee_ta_init_session
-> 如果打开失败,且原因是TA还未读入RAM,则利用tee_ta_rpc_load请求REE取得TA文件。然后再次调用tee_ta_open_session。此时需要记录TA在NS的mem映射情况nwumap。