如果支持EDMM,我们就可以动态生产TCS,因此一开始我们可能只给一部分TCS分配好空间,还有一部分动态TCS并未分配空间,这些动态TCS按需生成。
这个产生TCS的过程是需要Enclave内外共同合作完成,Enclave内部完成【ECMD_MKTCS】任务,中途会调用OCALL到Enclave外部执行【make_tcs】,其中会发信令【SGX_IOC_ENCLAVE_MKTCS】给SGX驱动,让SGX驱动构建一个TCS页。因为需要用到ENCLS这个Ring0指令,所以需要由SGX驱动完成。
那么下面就要介绍如何动态生成这些动态TCS。
构建动态TCS的API
此时位于uRTS,试图构建Enclave。
sgx_status_t CTrustThreadPool::new_thread()
{
...
void **ocalls = octbl->ocall;
*ocalls = reinterpret_cast<void*>(make_tcs);
...
ret = (sgx_status_t)do_ecall(ECMD_MKTCS, octbl, &ms1, m_utility_thread);
...
}
注册一下【make_tcs】这个OCALL函数。然后进入Enclave,【ECMD_MKTCS】。
进入Enclave来构建动态TCS
进入Enclave的流程:【do_ecall】->【enter_enclave/__morestack(uRTS)】->【enclave_entry】->【enter_enclave(tRTS)】
我们这次是【ECMD_MKTCS】任务,因此紧接着【enter_enclave(tRTS)】走的是【do_ecall_add_thread】函数。
sgx_status_t do_ecall_add_thread(void *ms){
...
if(!is_utility_thread())
...
status = do_save_tcs(ptcs);
...
status = do_add_thread(ptcs);
}
确保当前线程是通用线程(Utility Thread)
先通过gs寄存器(64位下,32位使用fs寄存器)找到每个Enclave线程自己的数据——一个thread_data_t对象。
typedef struct _thread_data_t
{
sys_word_t self_addr;
sys_word_t last_sp; /* set by urts, relative to TCS */
sys_word_t stack_base_addr; /* set by urts, relative to TCS */
sys_word_t stack_limit_addr; /* set by urts, relative to TCS */
sys_word_t first_ssa_gpr; /* set by urts, relative to TCS */
sys_word_t stack_guard; /* GCC expects start_guard at 0x14 on x86 and 0x28 on x64 */
sys_word_t flags;
sys_word_t xsave_size; /* in bytes (se_ptrace.c needs to know its offset).*/
sys_word_t last_error; /* init to be 0. Used by trts. */
#ifdef TD_SUPPORT_MULTI_PLATFORM
sys_word_t m_next; /* next TD used by trusted thread library (of type "struct _thread_data *") */
#else
struct _thread_data_t *m_next;
#endif
sys_word_t tls_addr; /* points to TLS pages */
sys_word_t tls_array; /* points to TD.tls_addr relative to TCS */
#ifdef TD_SUPPORT_MULTI_PLATFORM
sys_word_t exception_flag; /* mark how many exceptions are being handled */
#else
intptr_t exception_flag;
#endif
sys_word_t cxx_thread_info[6];
sys_word_t stack_commit_addr;
} thread_data_t;
这里面记录了线程相关的很多信息。其中一个Flags中的Bit信息是说明当前线程是不是通用线程。因为在支持EDMM的情况下,我们只通过这个通用线程来对动态TCS进行分配操作,此外所有动态结构也都依赖于这个通用线程来完成,通用线程其中的一个目的就是来处理这种动态结构。之前执行的【ECMD_INIT_ENCLAVE】任务,在支持EDMM情况下,也是由通用线程来完成。
Enclave内部记录下TCS地址
status = do_save_tcs(ptcs);
tRTS内保存起来这个TCS的地址。当前第一次存这个TCS的地址时,会构造一个随机数值的全局的TCS Cookie【g_tcs_cookie】,将TCS的地址用TCS Cookie异或加密,并加入【g_tcs_node】链表。
开始构建TCS
// Create a thread dynamically.
// It will add necessary pages and transform one of them into type TCS.
sgx_status_t do_add_thread(void *ptcs){
...
if ( 0 != check_dynamic_range((void *)tcs, 1, &offset, NULL))
...
// check if the tcs provided exactly matches the one in signtool
...
// adding page for all the dynamic entries
...
//Copy and initialize TCS
...
//Adjust the tcs fields
...
//OCALL for MKTCS
...
//EACCEPT for MKTCS
...
}
上面将TCS地址挂载到了一个全局变量。这里是对这个TCS进行赋予内容。首先检查是不是处于元数据指定的动态布局中,因为我们这里其实就是动态的构建TCS,并且此时主要是因为已有的TCS数量达不到最小值。然后检查TCS所在的位置是不是和【LAYOUT_ID_TCS_DYN】布局所在的位置一致。然后接受EECCEPT之前动态线程布局的修改,用g_global_data保存的TCS模板初始化这个TCS,并进行简单基址调整。
退出Enclave,让SGX驱动构建TCS页
这时,就需要先从Enclave出去,执行OCALL Table里的【make_tcs】函数。发信令【SGX_IOC_ENCLAVE_MKTCS】给SGX驱动,让其完成TCS页的构建。
SGX驱动的操作如下。
/**
* sgx_ioc_page_to_tcs() - Pages defined in range are switched to TCS.
* These pages should be of type REG.
* eaccept needs to be invoked after return.
* @arg range address of pages to be switched
*/
long sgx_ioc_page_to_tcs(struct file *filep, unsigned int cmd, unsigned long arg)
回到Enclave,接受SGX驱动对TCS页的构建操作
紧接着就又回到Enclave。【ECMD_ORET】代表这次进入Enclave是之前曾退出过Enclave。回到Enclave,接着之前的Enclave内的流程——EACCEPT接受SGX驱动对TCS页的构建。到此,构建TCS页的任务就结束了。
构建TCS页结束,回到【CTrustThreadPool::new_thread】进行TCS相关列表维护
如果TCS构建成功,那么就把这个TCS封装成的CTrustThread对象加入到“用于调试的TCS信息列表”,并加入到空闲可用线程列表中【CTrustThreadPool::m_free_thread_vector】。将它从未分配空间TCS列表【CTrustThreadPool::m_unallocated_threads】中删除,因为它现在已经被分配空间了。