user TA的加载

  • ldelf
    • init_elf:
      • sys_open_ta_bin:
      • sys_map_ta_bin
    • map_segments
    • populate_segments
    • add_dependencies
    • copy_section_headers
    • save_systab
    • close_handle
    • set_tls_offset
  • 后续流程
  • 回到内核之后的流程

动态TA按照存储位置的不同分为REE filesystem TA:存放在REE侧文件系统里的TA;Early TA:被嵌入到optee os里的在supplicant启动之前就可用了。这里我们讲的是常规的存放在REE侧文件系统里的TA。

通过GP标准调用的与TA通信的命令(opensession\invoke\closession)其实都是std smc call,该smc调用后,会进入到TEE中的tee_entry_std中。
tee_entry_std函数中调用就是opensession\invoke\closession的接口。调用到entry_open_session接口。动态TA最终会在tee_ta_init_user_ta_session函数里加载。

ldelf

elf格式的文件是一种可执行文件,ldelf就是加载elf文件的加载器。而TA文件就是加了头的elf文件。所以ldelf其作用为load需要加载的TA。调用ldelf → ta_elf_load_main → load_main加载TA的二进制文件。
tee_ta_init_user_ta_session里调用load_ldelf函数。其实现大致为,在代码编译的时候,optee-os下的ldelf目录下编译出ldelf.elf二进制文件,通过gen_ldelf_hex.py脚本将二进制文件生成ldelf_hex.c文件。其二进制数据会被写入数组ldelf_data,还有代码段,数据段的大小。在load_ldelf函数中将其加载到内存中。再通过init_with_ldelf函数调用thread_enter_user_mode函数切换到用户模式el0,运行此ldelf的代码。

init_elf:

验签、解密、加载TA二进制文件,并判断其是否是ELF格式。

sys_open_ta_bin:

其功能是调用systemPTA,找到并打开TA文件,并返回句柄。
此函数会遍历注册的TA_STORE,调用open等操作函数。注册的是下面这个。

TEE_TA_REGISTER_TA_STORE(9) = {.description = "REE",.open = ree_fs_ta_open,.get_size = ree_fs_ta_get_size,.get_tag = ree_fs_ta_get_tag,.read = ree_fs_ta_read,.close = ree_fs_ta_close,
};

ree_fs_ta_open函数解析:
先调用rpc_load:通过rpc调用ree侧的tee_supplicant ,传入uuid,ree侧将uuid对应的TA二进制文件加载到共享内存中,在rpc_load中包含两次TA加载的rpc请求,第一次参数里只有UUID,因为此时optee-os不知道加载的TA的image大小,tee supplicant接收到TA加载请求后由于解析出参数的image size为0,所以其通过UUID读取TA文件后计算出ta_size,发现传入大小不足,所以此时没有加载TA 并返回需要传入的size给到optee-os的rpc_load(),其得到size后通过rpc请求size大小的共享内存并得到地址,此时将uuid、共享内存地址、size传入作为第二次rpc加载TA请求,tee-suplicant收到后加载TA到对应的共享内存中。

/* Request TA from tee-supplicant */
res = rpc_load(uuid, &ta, &ta_size, &mobj);

接着调用shdr_alloc_and_copy:通过rpc_load()之后得到的TA加载的共享内存地址,因为TA镜像在签名时增加签名相关的头部文件shdr,在此函数中计算shdr的大小,struct shdr + hash_size + sig_size。然后申请此大小的内存并将TA镜像的shdr拷贝到申请的内存里。用于后面的验签流程。

/* Make secure copy of signed header */
shdr = shdr_alloc_and_copy(ta, ta_size);

接着调用shdr_verify_signature执行验签工作。传入镜像头部文件shdr指针。shdr包括了magic、img_type、img_size、algo等参数。先检验代码中的magic与shdr中的是否一致,从前面提到的ta_pub_key.c中读取ta_pub_key_modulus,提取出pub_key,从shdr中提取hash和sig,使用rsa算法验签此shdr。

/* Validate header signature */
res = shdr_verify_signature(shdr);

然后在ree_fs_ta_open()里还要取出TA镜像里的uuid与传入的uuid比较,并初始化hash_ctx。

sys_map_ta_bin

其功能是调用systemPTA,映射TA二进制文件的各种segment到安全内存中。

res = sys_map_ta_bin(&va, SMALL_PAGE_SIZE, flags, elf->handle, 0, 0, 0);

在init_elf中,此处是第一次调用,传入的长度是0x1000,最小页大小。目的为先将TA的从0到0x1000的数据映射到安全内存。主要作用先申请块安全内存,然后映射其地址。

struct fobj *f = fobj_ta_mem_alloc(num_pages);
mobj = mobj_with_fobj_alloc(f, file);
res = vm_map_pad(uctx, va, num_rounded_bytes,TEE_MATTR_PRW, vm_flags, mobj, 0,pad_begin, pad_end, 0);

经过这几个步骤,其就会申请了大小为num_pages个pages的空间,并映射到va_range的地址空间里,调试出的信息就是申请的是0xexxxxxxx的安全内存物理地址,变为了0x4xxxxxxx的虚拟地址里。然后调用binh_copy_to函数,此函数调用注册的ree_fs_ta_read,将共享内存中的TA镜像加载到安全内存中。

binh_copy_to函数解析

/*此函数是根据binh句柄,其中记录了当前读取ELF文件的偏移,文件总大小的信息,来从offs_bytes在文件中的偏移,
读取num_bytes长度的数据到va这个地址,其调用注册的read接口,也就是ree_fs_ta_read*/
static TEE_Result binh_copy_to(struct bin_handle *binh, vaddr_t va,size_t offs_bytes, size_t num_bytes)
{TEE_Result res = TEE_SUCCESS;size_t next_offs = 0;if (offs_bytes < binh->offs_bytes)return TEE_ERROR_BAD_STATE;if (ADD_OVERFLOW(offs_bytes, num_bytes, &next_offs))return TEE_ERROR_BAD_PARAMETERS;if (offs_bytes > binh->offs_bytes) {/*此处需要读取文件的偏移已经大于了文件所处的偏移,因为ELF文件中的segment之间存在空的部分,如果不读取中见的信息,那么ree_fs_ta_read里更新hash_ctx就会出问题,导致校验hash不过,所以传入空的地址给read函数,只是为了读取空的部分更新hash_ctx,并不加载*/res = binh->op->read(binh->h, NULL,offs_bytes - binh->offs_bytes);if (res)return res;binh->offs_bytes = offs_bytes;}if (next_offs > binh->size_bytes) {/*这种情况就是需要读取的部分已经超过了文件的范围,所以此时直接将文件剩下的全部读取*/size_t rb = binh->size_bytes - binh->offs_bytes;res = binh->op->read(binh->h, (void *)va, rb);if (res)return res;memset((uint8_t *)va + rb, 0, num_bytes - rb);binh->offs_bytes = binh->size_bytes;} else {res = binh->op->read(binh->h, (void *)va, num_bytes);if (res)return res;binh->offs_bytes = next_offs;}return TEE_SUCCESS;
}

ree_fs_ta_read函数解析:

static TEE_Result ree_fs_ta_read(struct user_ta_store_handle *h, void *data,size_t len)
{struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h;/*获取TA镜像除去头之后的elf格式所在位置*/uint8_t *src = (uint8_t *)handle->nw_ta + handle->offs;size_t next_offs = 0;uint8_t *dst = src;TEE_Result res = TEE_SUCCESS;if (ADD_OVERFLOW(handle->offs, len, &next_offs) ||next_offs > handle->nw_ta_size)return TEE_ERROR_BAD_PARAMETERS;/*判断TA类型*/if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {if (data) {dst = data; /* Hash secure buffer *//*解密len长度的数据到安全内存中,加载多长的数据就解密多长的数据*/res = tee_ta_decrypt_update(handle->enc_ctx, dst, src,len);if (res != TEE_SUCCESS)return TEE_ERROR_SECURITY;} else {size_t num_bytes = 0;size_t b_size = MIN(1024U, len);uint8_t *b = malloc(b_size);if (!b)return TEE_ERROR_OUT_OF_MEMORY;dst = NULL;while (num_bytes < len) {size_t n = MIN(b_size, len - num_bytes);res = tee_ta_decrypt_update(handle->enc_ctx, b,src + num_bytes, n);if (res)break;num_bytes += n;res = crypto_hash_update(handle->hash_ctx, b,n);if (res)break;}free(b);if (res != TEE_SUCCESS)return TEE_ERROR_SECURITY;}} else if (data) { /*不是加密的TA则直接拷贝*/dst = data; /* Hash secure buffer (shm might be modified) */memcpy(dst, src, len);}if (dst) {/*更新hash_ctx,以便后面计算出TA的hash值使用*/res = crypto_hash_update(handle->hash_ctx, dst, len);if (res != TEE_SUCCESS)return TEE_ERROR_SECURITY;}handle->offs = next_offs;/*将句柄的偏移更新*/if (handle->offs == handle->nw_ta_size) {/*判断当前是否已经加载了所有的segment*/if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {/** Last read: time to finalize authenticated* decryption.*/res = tee_ta_decrypt_final(handle->enc_ctx,handle->ehdr, NULL, NULL, 0);if (res != TEE_SUCCESS)return TEE_ERROR_SECURITY;}/** Last read: time to check if our digest matches the expected* one (from the signed header)*//*加载所有segment之后进行hash校验与shdr里的比较*/res = check_digest(handle);if (res != TEE_SUCCESS)return res;if (handle->bs_hdr)res = check_update_version(handle->bs_hdr);}return res;
}

由此时传入的参数得知,此次的sys_map_ta_bin只是将TA的elf文件的头映射到了内存中。
有了头就可以根据elf头判断其基本信息了。
完成这些之后,程序会解析判断其elf格式

if (!IS_ELF(*(Elf32_Ehdr *)va))/*根据elf head magic判断*/err(TEE_ERROR_BAD_FORMAT, "TA is not an ELF");res = e32_parse_ehdr(elf, (void *)va);if (res == TEE_ERROR_BAD_FORMAT)res = e64_parse_ehdr(elf, (void *)va);/*解析elf head到elf结构体*/if (res)err(res, "Cannot parse ELF");if (MUL_OVERFLOW(elf->e_phnum, elf->e_phentsize, &sz) ||ADD_OVERFLOW(sz, elf->e_phoff, &sz))err(TEE_ERROR_BAD_FORMAT, "Program headers size overflow");if (sz > SMALL_PAGE_SIZE)err(TEE_ERROR_NOT_SUPPORTED, "Cannot read program headers");elf->phdr = (void *)(va + elf->e_phoff);

ELF格式的文件加载到安全内存之后,加载器会判断其是否是ELF格式的文件,以及头是否符合elf规范。
ELF格式分两种,32位和64位,基本一样。格式的基本信息包含在ELF的头里,这个是通用的,使用此结构体表示。

/** ELF header.*/typedef struct {unsigned char   e_ident[EI_NIDENT]; /* File identification. */Elf64_Half    e_type;     /* File type. */Elf64_Half  e_machine;  /* Machine architecture. */Elf64_Word   e_version;  /* ELF format version. */Elf64_Addr e_entry;    /* Entry point. */Elf64_Off e_phoff;    /* Program header file offset. */Elf64_Off  e_shoff;    /* Section header file offset. */Elf64_Word e_flags;    /* Architecture-specific flags. */Elf64_Half    e_ehsize;   /* Size of ELF header in bytes. */Elf64_Half    e_phentsize;    /* Size of program header entry. */Elf64_Half   e_phnum;    /* Number of program header entries. */Elf64_Half   e_shentsize;    /* Size of section header entry. */Elf64_Half   e_shnum;    /* Number of section header entries. */Elf64_Half   e_shstrndx; /* Section name strings section. */
} Elf64_Ehdr;

elf head头后面就是程序头表program segment header,位置就是ELF头里的e_phnum值在的偏移。记录了每个Segment的相关信息,比如类型、对应文件的偏移、大小、属性等。用下面的结构体表示。

typedef struct {Elf64_Word  p_type;     /* Entry type. */Elf64_Word  p_flags;    /* Access permission flags. */Elf64_Off   p_offset;   /* File offset of contents. */Elf64_Addr  p_vaddr;    /* Virtual address in memory image. */Elf64_Addr  p_paddr;    /* Physical address (not used). */Elf64_Xword p_filesz;   /* Size of contents in file. */Elf64_Xword p_memsz;    /* Size of contents in memory. */Elf64_Xword p_align;    /* Alignment in memory and file. */
} Elf64_Phdr;

optee example的一个TA的elf读出其program header:

Init_elf函数结束。
总结就是:找到TA文件,验签成功则解密0x1000头部数据,加载0x1000字节到内存,判断其是否是elf以及是否合法。

map_segments

parse_load_segment:

将elf文件的program segment中的信息填充到elf指针中。放入管理segment的链表里。
在hello_world TA里放入的是Loadable segment类型的segment。所以segments只放入了两个

for (n = 0; n < elf->e_phnum; n++)if (phdr[n].p_type == PT_LOAD) {add_segment(elf, phdr[n].p_offset,phdr[n].p_vaddr, phdr[n].p_filesz,phdr[n].p_memsz, phdr[n].p_flags,phdr[n].p_align);} else if (phdr[n].p_type == PT_TLS) {elf->tls_start = phdr[n].p_vaddr;elf->tls_filesz = phdr[n].p_filesz;elf->tls_memsz = phdr[n].p_memsz;}

adjust_segment:

做segment的健壮性检查。segment相关的一些设置。略过。

populate_segments

此函数的作用是根据添加到elf入参里的segment,不同类型segment进行不同的映射到安全内存中。
遍历放入map_segment函数放入链表里的segment,根据segment不同的flag参数,也就是读写等标识。进行不同的映射。
核心代码如下:

if (flags & PTA_SYSTEM_MAP_FLAG_WRITEABLE) {/*如果是可写的segment*//*映射一块被初始化的地址给给va*/res = sys_map_zi(memsz, 0, &va, pad_begin,pad_end);if (pad_begin && res == TEE_ERROR_OUT_OF_MEMORY)res = sys_map_zi(memsz, 0, &va, 0,pad_end);if (res)err(res, "sys_map_zi");/*调用的还是binh_copy_to函数,通过句柄,将大小为filesz的segment加载到va地址*/res = sys_copy_from_ta_bin((void *)va, filesz,elf->handle, offset);if (res)err(res, "sys_copy_from_ta_bin");
} else {/*不是可写类型的segment*/if (filesz != memsz)err(TEE_ERROR_BAD_FORMAT,"Filesz and memsz mismatch");/*加载segment到新va*/res = sys_map_ta_bin(&va, filesz, flags,elf->handle, offset,pad_begin, pad_end);if (pad_begin && res == TEE_ERROR_OUT_OF_MEMORY)res = sys_map_ta_bin(&va, filesz, flags,elf->handle,offset, 0,pad_end);if (res)err(res, "sys_map_ta_bin");
}

add_dependencies

添加依赖,也就是添加需要动态链接的segment信息

add_deps_from_segment
Dynamic segment存放的是和dynamic section相关的信息,结构为

/** Dynamic structure.  The ".dynamic" section contains an array of them.*/typedef struct {Elf64_Sxword    d_tag;      /* Entry type. */union {Elf64_Xword d_val;  /* Integer value. */Elf64_Addr  d_ptr;  /* Address value. */} d_un;
} Elf64_Dyn

下图是optee_testTA的elf文件的Dynamic section

static void add_dependencies(struct ta_elf *elf)
{size_t n = 0;if (elf->is_32bit) {Elf32_Phdr *phdr = elf->phdr;for (n = 0; n < elf->e_phnum; n++)add_deps_from_segment(elf, phdr[n].p_type,phdr[n].p_vaddr, phdr[n].p_memsz);} else {Elf64_Phdr *phdr = elf->phdr;/*遍历segment,传入参数*/for (n = 0; n < elf->e_phnum; n++)add_deps_from_segment(elf, phdr[n].p_type,phdr[n].p_vaddr, phdr[n].p_memsz);}
}
static void add_deps_from_segment(struct ta_elf *elf, unsigned int type,vaddr_t addr, size_t memsz)
{size_t dyn_entsize = 0;size_t num_dyns = 0;size_t n = 0;unsigned int tag = 0;size_t val = 0;TEE_UUID uuid = { };char *str_tab = NULL;size_t str_tab_sz = 0;/*先判断是否动是态链接的segment,不是则直接退出*/if (type != PT_DYNAMIC)return;check_phdr_in_range(elf, type, addr, memsz);if (elf->is_32bit)dyn_entsize = sizeof(Elf32_Dyn);elsedyn_entsize = sizeof(Elf64_Dyn);assert(!(memsz % dyn_entsize));/*计算出dyn_section数量*/num_dyns = memsz / dyn_entsize;/*遍历Dynamic section成员,直到找到符号表地址和大小 */for (n = 0; n < num_dyns && !(str_tab && str_tab_sz); n++) {/*依次读取Dynamic section的tag和val*/read_dyn(elf, addr, n, &tag, &val);if (tag == DT_STRTAB)/*如果是符号表地址,则保存其地址*/str_tab = (char *)(val + elf->load_addr);else if (tag == DT_STRSZ)/*如果是大小,则保存其大小*/str_tab_sz = val;}check_range(elf, ".dynstr/STRTAB", str_tab, str_tab_sz);for (n = 0; n < num_dyns; n++) {/*依次读取Dynamic section的tag和val*/read_dyn(elf, addr, n, &tag, &val);if (tag != DT_NEEDED)/*找到动态链接库*/continue;if (val >= str_tab_sz)err(TEE_ERROR_BAD_FORMAT,"Offset into .dynstr/STRTAB out of range");tee_uuid_from_str(&uuid, str_tab + val);queue_elf(&uuid);/*存入queue,便于后面加载*/}
}

函数结束,此函数就是寻找动态库,找到就将其uuid放入队列。以便将其从ree_fs加载。
在optee-os里,动态库被编译成了.ta文件,以optee_test里的os_test的TA为例。

os_test依赖os_test_lib,ffd2xxxx.ta就是os_test_lib编译出的TA文件。可以理解为,os_test调用了os_test_lib提供的接口,但是os_test_lib是以动态TA的形式加载的。

copy_section_headers

函数功能是将section拷贝到elf的结构体中。

static void copy_section_headers(struct ta_elf *elf)
{TEE_Result res = TEE_SUCCESS;size_t sz = 0;size_t offs = 0;/*计算出section header大小*/if (MUL_OVERFLOW(elf->e_shnum, elf->e_shentsize, &sz))err(TEE_ERROR_BAD_FORMAT, "Section headers size overflow");/*申请section header空间,此时shdr与TA的shdr不一样,是指section header*/elf->shdr = malloc(sz);if (!elf->shdr)err(TEE_ERROR_OUT_OF_MEMORY, "malloc");/** We're assuming that section headers comes after the load segments,* but if it's a very small dynamically linked library the section* headers can still end up (partially?) in the first mapped page.*/if (elf->e_shoff < SMALL_PAGE_SIZE) {assert(!elf->is_main);offs = MIN(SMALL_PAGE_SIZE - elf->e_shoff, sz);memcpy(elf->shdr, (void *)(elf->load_addr + elf->e_shoff),offs);}if (offs < sz) {/*调用systemPTA,将section header拷贝到elf->shdr的空间,也就是加载到了申请的空间里*/res = sys_copy_from_ta_bin((uint8_t *)elf->shdr + offs,sz - offs, elf->handle,elf->e_shoff + offs);if (res)err(res, "sys_copy_from_ta_bin");}
}

save_systab

将符号表保存到elf结构体中,主要是做符号表参数检查

for (n = 0; n < elf->e_shnum; n++) {/*遍历所有的section header*/if (shdr[n].sh_type == SHT_DYNSYM) {/*找到动态链接符号表*/e64_save_symtab(elf, n);/*保存符号表到elf结构体,做一些参数检查*/break;}}

close_handle

调用sys_close_ta_bin函数调用systemPTA进行释放资源的操作。
其还会调用一次open操作,ree_fs_ta_open。这次的目的是校验hash值。也就是前面讲到的。
ree_fs_ta_read里的最后部分代码

if (handle->offs == handle->nw_ta_size) {/*如果读取完成*/if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {/** Last read: time to finalize authenticated* decryption.*//*解密收尾工作*/res = tee_ta_decrypt_final(handle->enc_ctx,handle->ehdr, NULL, NULL, 0);if (res != TEE_SUCCESS)return TEE_ERROR_SECURITY;}/** Last read: time to check if our digest matches the expected* one (from the signed header)*//*加载所有segment后进行hash校验与shdr里的比较*/res = check_digest(handle);if (res != TEE_SUCCESS)return res;if (handle->bs_hdr)res = check_update_version(handle->bs_hdr);}

set_tls_offset

此函数设置TLS segment相关,暂时没有研究过。略过。

关于elf的加载,总的就是加载了其LOAD类型的segment到了安全内存。

后续流程

经过前面多次的函数调用后elf->load_addr此时就是TA除去头部的文件加载地址。
elf->head就是.ta_head section。是每个TA定义在user_ta_header.c里的。

elf->head = (struct ta_head *)elf->load_addr;
const struct ta_head ta_head __section(".ta_head") = {/* UUID, unique to each TA */.uuid = TA_UUID,/** According to GP Internal API, TA_FRAMEWORK_STACK_SIZE corresponds to* the stack size used by the TA code itself and does not include stack* space possibly used by the Trusted Core Framework.* Hence, stack_size which is the size of the stack to use,* must be enlarged*/.stack_size = TA_STACK_SIZE + TA_FRAMEWORK_STACK_SIZE,.flags = TA_FLAGS,/** The TA entry doesn't go via this field any longer, to be able to* reliably check that an old TA isn't loaded set this field to a* fixed value.*/.depr_entry = UINT64_MAX,
};

所以在接下来才会使用其ta_head里的stack_size去申请内存。

/*申请已经被初始化为0,大小为stack_size的安全内存*/
res = sys_map_zi(elf->head->stack_size, 0, &va, 0, 0);
if (res)err(res, "sys_map_zi stack");if (elf->head->flags & ~TA_FLAGS_MASK)err(TEE_ERROR_BAD_FORMAT, "Invalid TA flags(s) %#"PRIx32,elf->head->flags & ~TA_FLAGS_MASK);
/*将elf里的参数传递给函数入参*/
*ta_flags = elf->head->flags;
*sp = va + elf->head->stack_size;
ta_stack = va/*此处两次赋值是因为ta_stack全局变量用作打印*/;
ta_stack_size = elf->head->stack_size;

sp指针的地址就是这样算出来的。此时外部调用的arg参数就得到了需要的stack_ptr,arg->flag等参数。

/** Load binaries, ta_elf_load() may add external libraries to the* list, so the loop will end when all the dependencies are* satisfied.*/TAILQ_FOREACH(elf, &main_elf_queue, link)/*如果由动态链接的依赖库在这里加载*/ta_elf_load_dependency(elf, arg->is_32bit);

然后ldelf的任务就完成了,此时TA的镜像已经被全部加载到了不i同的内存中,然后带着这些参数返回到optee_os调用其的位置。arg参数会被保存在utc上下文里。

回到内核之后的流程

ldelf结束后,回到init_with_ldelf里,将ldelf返回的参数保存在utc里。

 utc->is_32bit = arg->is_32bit;utc->entry_func = arg->entry_func;utc->stack_ptr = arg->stack_ptr;utc->uctx.ctx.flags = arg->flags;utc->dump_entry_func = arg->dump_entry;
#ifdef CFG_FTRACE_SUPPORTutc->ftrace_entry_func = arg->ftrace_entry;sess->fbuf = arg->fbuf;
#endifutc->dl_entry_func = arg->dl_entry;

而utc里的link节点被放入了tee_ctxes链表里

TAILQ_INSERT_TAIL(&tee_ctxes, &utc->uctx.ctx, link);

可以调用此函数通过to_user_ta_ctx函数取出其地址。所以在后面的user_ta_enter函数里,会体现到。

static inline struct user_ta_ctx *to_user_ta_ctx(struct tee_ta_ctx *ctx)
{assert(is_user_ta_ctx(ctx));return container_of(ctx, struct user_ta_ctx, uctx.ctx);
}

接着返回后会回到tee_open_session里。
关键代码

/*ldelf就是在这里加载运行的*/
res = tee_ta_init_session(err, open_sessions, uuid, &s);
if (tee_ta_try_set_busy(ctx)) {set_invoke_timeout(s, cancel_req_to);/*调用注册的open_session函数*/res = ctx->ops->enter_open_session(s, param, err);tee_ta_clear_busy(ctx);}

因为是user_ta,所以调用的是user_ta_enter_open_session函数。最终调用到user_ta_enter函数。

static const struct tee_ta_ops user_ta_ops __rodata_unpaged = {.enter_open_session = user_ta_enter_open_session,.enter_invoke_cmd = user_ta_enter_invoke_cmd,.enter_close_session = user_ta_enter_close_session,.dump_state = user_ta_dump_state,
#ifdef CFG_FTRACE_SUPPORT.dump_ftrace = user_ta_dump_ftrace,
#endif.destroy = user_ta_ctx_destroy,.get_instance_id = user_ta_get_instance_id,.handle_svc = user_ta_handle_svc,
};

user_ta_enter函数关键代码

***
/*获取utc上下文,前面提到了*/
struct user_ta_ctx *utc = to_user_ta_ctx(session->ctx);
***usr_stack = utc->stack_ptr;/*设置栈指针*/usr_stack -= ROUNDUP(sizeof(struct utee_params), STACK_ALIGNMENT);usr_params = (struct utee_params *)usr_stack;init_utee_param(usr_params, param, param_va);
***
/*切换到用户模式,运行TA*/
res = thread_enter_user_mode(func, kaddr_to_uref(session),(vaddr_t)usr_params, cmd, usr_stack,utc->entry_func, utc->is_32bit,&utc->uctx.ctx.panicked,&utc->uctx.ctx.panic_code);

到这里,TA的加载和运行流程大致就完成了。

【OP-TEE】TA的加载(超详细)相关推荐

  1. optee中User TA的加载/验签和运行

    文章目录 1.TA的加载到TEE内存 思考:- User TA是怎样编译的?- User TA是怎样签名的?- User TA是怎样被load到内存的?- 怎样验证TA签名的?- 如何解析TA hea ...

  2. ios 图片加载内存尺寸_iOS加载超清大图内存暴涨问题解决

    加载超清大图是会引起内存爆表的问题,最近一直困扰着我. SDWebImage在加载大图时做的不是很好,加载大图内存爆表.YYWebImage会好一点,但还是不行. 当不要求图片质量的情况下,最好是在上 ...

  3. 恶魔之魂重制版登陆服务器未响应,数毛社分析《恶魔之魂:重制版》 60帧运行,加载超快...

    原标题:数毛社分析<恶魔之魂:重制版> 60帧运行,加载超快 魂类游戏<恶魔之魂:重制版>将登陆PS5平台,很多玩家都非常期待该作.近日,数毛社公布了PS5<恶魔之魂:重 ...

  4. java 类 重新加载_Java动态类加载和重新加载的详细介绍

    本篇文章给大家带来的内容是关于Java动态类加载和重新加载的详细介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. Java中可以在运行时加载和重新加载类,虽然并不像我们想像中那么简 ...

  5. 44. TA镜像加载时的验证

    历经一年多时间的系统整理合补充,<手机安全和可信应用开发指南:TrustZone与OP-TEE技术详解 >一书得以出版,书中详细介绍了TEE以及系统安全中的所有内容,全书按照从硬件到软件, ...

  6. optee中User TA的加载和运行

    文章目录 1.optee中的TA简介 2.TA的调用 2.tee_entry_std :std smc的调用 3.open_session 思考: User TA是怎样编译的? User TA是怎样签 ...

  7. jQuery deferred应用dom加载完毕详细源码分析(三)

    我承认上章ajax部分写得不好,不要怪我,它的ajax代码太多了,而且跨越大,方法跳跃多,实在不好排版与讲解,但如果你真正想研究源码并且仔细读了得话,你的 收获应该会很大,至少你明白了js的ajax是 ...

  8. PC端打不开微信公众号文章、微信图片加载超缓慢的处理方法(最全的解决方法)

    前言 电脑端的微信加载好友发来的图片非常慢,打开公众号的一些文章时,只能加载文字部分,图片部分加载不出来,排除网络的原因后,以下是几种解决方法. 1.-微信的安装盘,缓存文件过大,硬盘无法存放,可以尝 ...

  9. 滑动门+下滑加载更多详细图解

    <script type="text/javascript"> var type_num = 0; //默认是第一项 $(document).ready(functio ...

  10. win7计算机加载项,详细教您win7禁用ie加载项

    小编发现最近有小伙想知道win系统里面ie浏览器使用禁用加载项的操作办法.所以小编就花了一番时间来了解这个方面的知识是小编现在win7ie浏览器使用禁止加载步骤的,方法来分享给你们. 禁止加载项?小伙 ...

最新文章

  1. 理解Linux和其他UNIX-Like系统上的平均负载
  2. 浅析Unity中的Enlighten与混合光照
  3. 目前最全:52 个深度学习目标检测模型汇总,论文、源码一应俱全!
  4. 机器学习-转换器与估计器05
  5. 在C#项目中使用SQLite(环境安装问题)
  6. 前端开发规范文档(html,css,js)
  7. JavaScript中的标识符(附:关键字表)
  8. 在ASP.NET中清除页面状态
  9. 终端花屏后的恢复办法
  10. Cache-control
  11. SQL操作结果集-并集,差集,交集,结果集排序
  12. 仙剑四、五、五前模型及资源提取
  13. 网站漏洞测试 关于webshell木马后门检测
  14. agv系统介绍_智能自动化物流系统AGV基础知识(完整介绍)
  15. ROS深度图转化为点云
  16. LTE学习笔记-3 OFDM
  17. 野山参怎么吃好,这样炖是不是效果最好
  18. 2004年秋浙江省计算机等级考试二级C 编程题(2)
  19. ubuntu ibus输入法 卡顿
  20. 学渣之路:一个月拯救我英语四级

热门文章

  1. 【libnice】艰难的meson+ ninja手动编译过,vs2022 v143 debug
  2. 人脸识别实名制管理,推动智慧工地建设发展
  3. matlab函数 kron
  4. css2仿微信导航栏-滑动门
  5. c#访问其他服务器的共享文件夹,C#实现访问网络共享文件夹
  6. 【C++】error: passing ‘const xxx’ as ‘this’ argument discards qualifiers [-fpermissive]
  7. 三大云厂商 ARM 架构服务器性能对比
  8. 彻底解决乱码问题(一):为何会出现乱码
  9. Cisco Packet Tracer 子网划分实验
  10. linux 移动硬盘 mac,Mac下使用NTFS格式的移动硬盘