目录

SGX初始化中,我们就碰到过切换上下文进入Enclave的场景

以【ECMD_INIT_ENCLAVE】为例,先大概描述一下切换上下文进入Enclave的具体过程

普及:ECALL索引值和tRTS内ECALL符号表

Switch模式进入Enclave执行ECMD_INIT_ENCLAVE任务【CEnclave::ecall】

先取一个【CTrustThread】用于ECALL Switch【CTrustThreadPool::acquire_thread】

如果不支持EDMM,就另外拿取一个TCS【CTrustThreadPool::_acquire_thread】

回到【CTrustThreadPool::acquire_thread】

回到【CEnclave::ecall】

提取【tcs_t】结构体,进一步执行ECALL【do_ecall】

进一步执行ECALL,切换上下文【enter_enclave(uRTS)/__morestack】

第一次进入Enclave(EENTER硬件指令),【ECMD_INIT_ENCLAVE】任务

当前已经在tRTS了,进一步去完成【ECMD_INIT_ENCLAVE】任务【enter_enclave(tRTS)】

对于用户编写的的ECALL函数,ECALL Switch过程是什么样的呢?

初始化栈保护机制(tRTS)

处理线程数据(、tRTS pthread),然后进一步ECALL【do_ecall(tRTS)】

查ECALL表,找到ECALL索引值对应的虚拟地址,然后执行之【trts_ecall】

ECALL Switch的过程到此就结束了


SGX初始化中,我们就碰到过切换上下文进入Enclave的场景

在SGX初始化过程中,我们其实就碰到了需要切换上下文进入Enclave完成【ECMD_INIT_ENCLAVE】任务,这个任务主要是为了在tRTS内完成一些必要的SGX初始化工作。在《再回顾SGX初始化(三)》中我们提到过。

又比如在支持EDMM时,我们可能需要动态生成TCS,那么也需要切换上下文进入Enclave完成【ECMD_MKTCS】任务,用于动态生成TCS。在《再回顾SGX初始化(四)》、《构建动态TCS页》中我们提到过。

以【ECMD_INIT_ENCLAVE】为例,先大概描述一下切换上下文进入Enclave的具体过程

流程:【CEnclave::ecall(uRTS)】->【do_ecall(uRTS)】->【enter_enclave/__morestack(uRTS, AS)】->【enclave_entry(tRTS, AS)】->【enter_enclave(tRTS)】->【do_init_enclave(tRTS)】

【ECMD_INIT_ENCLAVE】任务是SGX初始化中重要的一步。不过,在tRTS里如何具体处理【ECMD_INIT_ENCLAVE】任务还请看《再回顾SGX初始化(三)》,这里主要是为了讲述ECALL Switch或者说如果切换上下文进入Enclave。

我们使用【CEnclave::ecall】作为一个常用的接口来切换上下文进入Enclave,虽然【CEnclave::ecall】只是一个封装。

int status = enclave->ecall(ECMD_INIT_ENCLAVE, NULL, reinterpret_cast<void *>(&info));

普及:ECALL索引值和tRTS内ECALL符号表

【CEnclave::ecall】的proc参数可选项如下,负数代表了进入Enclave的特殊目的是什么。

补充:【sgx_ecall(uRTS)】最终也是调用的【CEnclave::ecall】,增加的动作包括在【CEnclave::ecall】之前对Enclave的引用计数加一,在【CEnclave::ecall】之后对Enclave的引用计数减一。

而自然数(≥0的整数,这里的ECMD_ECALL只是一个示意,比如某个ECALL的Index值可能是7)则代表每一个用户编写的ECALL的Index索引值,这个Index索引值可以在tRTS中存储的ECALL符号表【g_ecall_table】中找到对应ECALL函数的虚拟地址,在tRTS中通过调用这个ECALL的虚拟地址完成ECALL的执行,并且在uRTS中直接使用ELRANGE中的虚拟地址会由PMH硬件和、EPCM数据结构判断这个访问控制是非法的,也就是SGX的中止页面语义。

/* ECALL command */
#define ECMD_ECALL           0
#define ECMD_INIT_ENCLAVE   -1
#define ECMD_ORET           -2
#define ECMD_EXCEPT         -3
#define ECMD_MKTCS          -4
#define ECMD_UNINIT_ENCLAVE -5
#define ECMD_ECALL_PTHREAD  (-6) //linux-sgx v2.8所做的修改,Commit ID:9ddec08

这里举例说明0以上每一个整数都对应了一个ECALL函数,也就是说这个Index整数本质上就是一个索引值,用来在【g_ecall_table】索引具体的ecall地址。

【g_ecall_table】如下所示:

//位于SampleCode/SampleEnclave/Enclave/Enclave_t.c
SGX_EXTERNC const struct {size_t nr_ecall;struct {void* ecall_addr; uint8_t is_priv; uint8_t is_switchless;} ecall_table[32];
} g_ecall_table = {32,{{(void*)(uintptr_t)sgx_ecall_type_char, 0, 0},{(void*)(uintptr_t)sgx_ecall_type_int, 0, 0},{(void*)(uintptr_t)sgx_ecall_type_float, 0, 0},{(void*)(uintptr_t)sgx_ecall_type_double, 0, 0},{(void*)(uintptr_t)sgx_ecall_type_size_t, 0, 0},...}
};

比如用户编写的【ecall_type_char】对应的Index就是【proc==0】。我们在uRTS中使用0索引值,切换上下文进入到Enclave后,在tRTS的【g_ecall_table】中查到【sgx_ecall_type_char】的虚拟地址,额外提醒一下【sgx_ecall_type_char】是Enclave内部的(也是我们用户编写的)【ecall_type_char】函数的壳。

//位于SampleCode/SampleEnclave/App/Enclave_u.c
sgx_status_t ecall_type_char(sgx_enclave_id_t eid, char val)
{sgx_status_t status;ms_ecall_type_char_t ms;ms.ms_val = val;status = sgx_ecall(eid, 0, &ocall_table_Enclave, &ms);return status;
}
//位于SampleCode/SampleEnclave/Enclave/Enclave_t.c
static sgx_status_t SGX_CDECL sgx_ecall_type_char(void* pms)
{CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_char_t));//// fence after pointer checks//sgx_lfence();ms_ecall_type_char_t* ms = SGX_CAST(ms_ecall_type_char_t*, pms);sgx_status_t status = SGX_SUCCESS;ecall_type_char(ms->ms_val);return status;
}

再比如(用户编写的)【ecall_type_int】对应的Index就是【proc==1】。我们在uRTS中使用1索引值,切换上下文进入到Enclave后,在tRTS的g_ecall_table中查到【sgx_ecall_type_int】的虚拟地址,【sgx_ecall_type_int】是Enclave内部的【ecall_type_int】函数的壳。

//位于SampleCode/SampleEnclave/Enclave/Enclave_u.c
sgx_status_t ecall_type_int(sgx_enclave_id_t eid, int val)
{sgx_status_t status;ms_ecall_type_int_t ms;ms.ms_val = val;status = sgx_ecall(eid, 1, &ocall_table_Enclave, &ms);return status;
}
//位于SampleCode/SampleEnclave/Enclave/Enclave_t.c
static sgx_status_t SGX_CDECL sgx_ecall_type_int(void* pms)
{CHECK_REF_POINTER(pms, sizeof(ms_ecall_type_int_t));//// fence after pointer checks//sgx_lfence();ms_ecall_type_int_t* ms = SGX_CAST(ms_ecall_type_int_t*, pms);sgx_status_t status = SGX_SUCCESS;ecall_type_int(ms->ms_val);return status;
}

Switch模式进入Enclave执行ECMD_INIT_ENCLAVE任务【CEnclave::ecall】

【sgx_ecall(uRTS)】最终也是调用的【CEnclave::ecall】,增加的动作包括在【CEnclave::ecall】之前对Enclave的引用计数加一,在【CEnclave::ecall】之后对Enclave的引用计数减一。

sgx_status_t CEnclave::ecall(const int proc, const void *ocall_table, void *ms, const bool is_switchless)

【CEnclave::ecall】中首先需要上一个读写锁,这是因为可能同时有多个ECALL发生,这旨在保护CEnclave的成员变量(后面很多情况其实都需要开始考虑并发问题了)。当执行【ECMD_INIT_ENCLAVE】任务时,我们还没有初始化过Swtichless模式。

先取一个【CTrustThread】用于ECALL Switch【CTrustThreadPool::acquire_thread】

我们先取得一个【CTrustThread】(对应了一个TCS,前面提到过)。做法是从【CEnclave::m_thread_pool】中取得一个【CTrustThread】对象(实际上一个【CTrustThread】对象对应一个TCS,后面直接用【TCS_CTT】,【CTrustThread】首字母缩写,来指代【CTrustThread】)。

我们当前的ECALL是一个特殊的ECALL,是为了完成【ECMD_INIT_ENCLAVE】任务。

平台情况 所选择用来完成ECMD_INIT_ENCLAVE任务的线程
SGX支持EDMM等特性,那么就支持通用线程【CTrustThreadPool::m_utility_thread】这个特殊的【TCS_CTT]

我们就用通用线程【m_utility_thread】来完成【ECMD_INIT_ENCLAVE】任务。

如果不支持通用线程【CTrustThreadPool::m_utility_thread】 调用【CTrustThreadPool::_acquire_thread】获得一个【TCS_CTT】来完成【ECMD_INIT_ENCLAVE】任务。

如果不支持EDMM,就另外拿取一个TCS【CTrustThreadPool::_acquire_thread】

具体做法如下:

先后顺序 操作
A. 从TCS_CTT缓存【m_thread_list】寻找可用【TCS_CTT】

首先试图从一个“【TCS_CTT】缓存”(【CTrustThreadPool::m_thread_list】,可以类比内核对堆管理时候的那个堆块的缓存,这里的缓存并不是指CPU内部的Cache)获取一个【TCS_CTT】,这样省去了获取【TCS_CTT】的很多繁琐的操作。这个缓存是线程ID到【TCS_CTT】的映射关系。

因此先获取一个线程ID,这个线程ID是和一个全局Key【g_tid_key】绑定的,当【g_tid_key】未绑定任何线程ID时,就用当前线程与【g_tid_key】绑定。然后到TCS_CTT缓存处【CTrustThreadPool::m_thread_list】去找指定线程ID对应的【TCS_CTT】,并且这个【TCS_CTT】不能是【CTrustThreadPool::m_utility_thread】(不过当前我们碰不到这个情况)。

B. 从【TCS_CTT】空闲集【m_free_thread_vector】寻找可用【TCS_CTT】 如果【m_thread_list】里面没有可用的对象缓存,那么就要去【m_free_thread_vector】(前面有提到过,代表空闲的【TCS_CTT】集)里面取一个出来,同时如果是从空闲【TCS_CTT】集里面取出来的对象,那么将这个【TCS_CTT】和前面获取的线程ID进行绑定,放到【TCS_CTT】缓存【CTrustThreadPool::m_thread_list】中,方便后续使用。
C. 回收【TCS_CTT】缓存中空闲项,用作【TCS_CTT】

如果连【m_free_thread_vector】中也没有可用的【TCS_CTT】,那么就进一步对【m_thread_list这个缓存中引用数【CTrustThread::m_reference】为0的【TCS_CTT】放回到【m_free_thread_vector】中(调用【CThreadPoolUnBindMode::garbage_collect】,因为我们使用Switchless扩展特性,因此采用的非Bind模式的TCS池)。然后再把空闲的【TCS_CTT】给用起来去完成【ECMD_INIT_ENCLAVE】任务。

这里的和A的区别在于,A是对已经绑定线程ID的引用数不为0的【TCS_CTT】行使用,而这里是是对引用数为0的【TCS_CTT】进行使用。

D. 没有任何可用【TCS_CTT】 如果没有【TCS_CTT】缓存空闲项可回收,那么返回NULL。

回到【CTrustThreadPool::acquire_thread】

我们对取得的【TCS_CTT】的引用数加1。

回到【CEnclave::ecall】

此时我们已经取得了【TCS_CTT】。之后对【CEnclave::m_ocall_table】更新为传参【ocall_table】(此时【ocall_table】为NULL,因为我们仅仅是为了【ECMD_INIT_ENCLAVE】任务)。紧接着调用【do_ecall】。

提取【tcs_t】结构体,进一步执行ECALL【do_ecall】

ret = do_ecall(proc, m_ocall_table, ms, trust_thread);
//位于psw/urts/linux/sig_handler.cpp
int do_ecall(const int fn, const void *ocall_table, const void *ms, CTrustThread *trust_thread)

从【TCS_CTT】里面提取出【tcs_t】结构体后,调用【enter_enclave】这个汇编函数。

进一步执行ECALL,切换上下文【enter_enclave(uRTS)/__morestack】

status = enter_enclave(tcs, fn, ocall_table, ms, trust_thread);
//位于psw/urts/linux/sig_handler.cpp
//trust_thread is saved at stack for ocall.
#define enter_enclave __morestackextern "C" int enter_enclave(const tcs_t *tcs, const long fn, const void *ocall_table, const void *ms, CTrustThread *trust_thread);

如果不了解汇编可以查看《Red Hat Enterprise Linux 3》《x86 and amd64 instruction reference》

//位于psw/urts/linux/enter_enclave.S
DECLARE_GLOBAL_FUNC __morestack//__morestack:
EENTER_PROLOGmovl    frame_arg1, %edi                    /* fn */
#if defined(__x86_64__)/* we defined fn as int, so we do sign extend.*/movslq  %edi,   %rdi
#endifmov frame_arg3, %xsi                        /* ms */.Ldo_eenter:# clean the upper bits of YMM registerslea_symbol  g_clean_ymm, %xbxmovl (%xbx), %ecxcmpl $0, %ecxje   1fvzeroupper
1:mov frame_arg0, %xbx                        /* tcs addr */lea_pic .Lasync_exit_pointer, %xcx          /* aep addr */mov $SE_EENTER, %xax                        /* EENTER leaf */.Leenter_inst:ENCLU...;some code.size __morestack, .-__morestack

【DECLARE_GLOBAL_FUNC】是他们写的一个宏,目的是为了将这个汇编代码段标记为一个“全局函数”。其中【_CET_ENDBR】是根据环境选择endbr64或endbr32(Terminate an Indirect Branch in 64/32-bit Mode),详细信息可以在《Control-flow Enforcement Technology Specification》中搜索“endbr”。

【EENTER_PROLOG】也是一个宏,包括了CFI的相关选项的配置,通用目的寄存器和传参的保存,并预留一个栈槽用于保存一个指向xsave数据的栈空间,大小是【g_xsave_size】值(esp需要64字节对齐),留出一个影子栈空间给参数用,ES段中保存扩展X特性寄存器。将xsave数据通过XSAVEC指令(【g_xsave_enabled==1】,代表支持X特性)或FXSAVE(【g_xsave_enabled==0】)进行保存(调用【save_xregs】,传参是之前预留的xsave数据的栈空间)。总的来说这个宏的目的就是为了保存寄存器状态、X特性状态等。

接着回来讲【__morestack】做了什么。

【EENTER_PROLOG】之后,会开始设置XDI、XSI、YMM、XAX、XBX、XCX(如下表),然后调用ENCLU硬件指令。如果不了解ENCLU,可以参考《SGX软件栈(二)——硬件指令》

调用ENCLU硬件指令时  ,寄存器的值
XAX 2,代表EENTER功能叶。
XBX

frame_arg0,代表tcs_t对象的地址。

XCX .Lasync_exit_pointer(AEP)的地址。代表异步退出以后,处理完异常后,重新试图进入Enclave的跳板。类似于我们调用函数返回后的CALLER中的下一条指令地址。
XDI frame_arg1,对应参数fn(我们这里是ECMD_INIT_ENCLAVE)
XSI frame_arg3,对应ms(marshalling structure,编排好的结构体)
YMM 根据XFRM_YMM_BITMASK来确定,是不是需要调用vzeroupper硬件指令清零YMM寄存器高位

第一次进入Enclave(EENTER硬件指令),【ECMD_INIT_ENCLAVE】任务

此时我们就进入Enclave环境了,并且此时是我们第一次进入Enclave。同时EENTER的进入点是【enclave_entry】(也就是【EnclaveBase+TCS.OENTRY】,Enclave文件的元数据中的TCS布局都已经把这些都安排好了。在形成Enclave文件时,由SignTool来完成),这也是我们在lds文件中标记的全局符号。

//位于sdk/trts/linux/trts_pic.S
/* * ---------------------------------------------------------------------* Function: enclave_entry*      The entry point of the enclave.** Registers:*      XAX - TCS.CSSA*      XBX - the address of a TCS*      XCX - the address of the instruction following the EENTER*      XDI - the reason of entering the enclave*      XSI - the pointer to the marshalling structure*/
DECLARE_GLOBAL_FUNC enclave_entry

可以看到除了XAX,寄存器的值代表的内容都是一致的。

当进入Enclave是为了处理异常时,XAX从ENCLU叶号替换成了【TCS.CSSA】(Current Slot Index of an SSA frame)的用途。我们这里并不是处理异常的情况,所以值为0,也可以说是因为我们刚进入Enclave,并没有之前因为AEX退出Enclave而使用SSA对Enclave内部寄存器进行保存。

如果进入Enclave是为了处理异常,比如说发生AEX,然后进入uRTS,OS完成初步异常处理,有些异常可能需要Enclave内部才能处理,那么就需要进入Enclave专门处理这个异常。

《CoSMIX: a compiler-based system for secure memory instrumentation and execution in enclaves》里面介绍的还不错。

进入Enclave的目的可以参考《进入Enclave的目的归类》。

首先对Flag寄存器标志位进行清除,使得初始化为如下。Flag可以参考《FLAGS register》

Flag
OF 0
SF 0
AF 0
CF 0
ZF 1
PF 1
DF 0

根据进入Enclave目的的不同,我们还需要分别对栈空间地址进行选择,(进入Enclave处理异常情况,还需要额外对CSSA进行保存)

进入Enclave目的 操作
常规(刚进入Enclave,包括【ECMD_INIT_ENCLAVE】任务) XSP、XBP的值就是【XBX(tcs_t对象地址)- 1<<16 - STATIC_STACK_SIZE】,也就是说我们给刚进入Enclave的线程定一个栈空间
进入Enclave处理异常

需要跳转到【.Ldo_handler】将CSSA存到XDX寄存器上。同时栈空间的XSP、XBP寄存器也是设置为【XBX(tcs_t对象地址)- 1<<16 - STATIC_STACK_SIZE】

之前退出过并重进入Enclave 使用之前的栈空间,将XSP、XBP设置为之前的栈空间

清除EFLAGS中的AC位(调用CLEAN_XFLAGS)。

将不可信世界的XSP压栈,将CSSA、TCS、XSI、XDI压栈。使用预制的SYNTHETIC_STATE(编译时指明放在.niprod Section)来重置X特性寄存器调用XRSTOR【g_xsave_enabled==1,代表支持X特性】、FXRSTOR硬件指令【g_xsave_enabled==0,代表不支持X特性】,目的是为了清理X特性寄存器。

调用tRTS内的【enter_enclave】这个C语言函数(前面用汇编主要是为了对上下文环境的切换),传参情况如下

传参 传参所处位置
Index(fn、proc) 64位放到XDI;32位处于栈中
ms 64位放到XSI;32位处于栈中
TCS 64位放到XDX;32位处于栈中
CSSA 64位放到XCI;32位处于栈中

当前已经在tRTS了,进一步去完成【ECMD_INIT_ENCLAVE】任务【enter_enclave(tRTS)】

extern "C" int enter_enclave(int index, void *ms, void *tcs, int cssa) __attribute__((section(".nipx")));

查询【g_enclave_state】状态(调用【sgx_is_enclave_crashed】,该函数被编译到.nipx节)判断Enclave是否崩溃状态,这个值初始为【ENCLAVE_INIT_NOT_STARTED】,因此目前不是【CRASH】状态。

else if(index == ECMD_INIT_ENCLAVE) { error = do_init_enclave(ms, tcs); }

然后调用【do_init_enclave】,去真正的执行【ECMD_INIT_ENCLAVE】任务。

在tRTS里如果处理【ECMD_INIT_ENCLAVE】任务还请看《再回顾SGX初始化(三)》,这里主要是为了讲述ECALL Switch或者说如果切换上下文进入Enclave。

对于用户编写的的ECALL函数,ECALL Switch过程是什么样的呢?

一开始的流程依然是【sgx_ecall(uRTS)】->【sgx_ecall(uRTS)】->【CEnclave::ecall(uRTS)】->【do_ecall(uRTS)】->【enter_enclave/__morestack(uRTS, AS)】->【enclave_entry(tRTS, AS)】->【enter_enclave(tRTS)】。

在【enter_enclave(tRTS)】中,由于我们目标是执行一个用户编写的ECALL,因此我们的Index是≥0的。稍微提一下,前面说到的ECMD_INIT_ENCLAVE、ECMD_ORET、ECMD_MKTCS、ECMD_UNINIT_ENCLAVE、ECMD_EXCEPT都有专门的处理函数。

对于试图执行用户编写的ECALL。我们首先初始化栈保护机制,然后调用【do_ecall】来在tRTS内执行ECALL。

extern "C" int enter_enclave(int index, void *ms, void *tcs, int cssa)
{...// Initialize stack guard if necessaryinit_stack_guard(tcs);error = do_ecall(index, ms, tcs);...
}

初始化栈保护机制(tRTS)

static void __attribute__((section(".nipx"))) init_stack_guard(void *tcs)

通过线程上下文获取当前的线程数据(记录了线程栈信息、线程局部存储信息等)。如果没有对当前TCS的线程数据中的栈保护页初始化过,那么tRTS内部读取一个随机数来初始化栈保护页。我们前面提到过【ECMD_INIT_ENCLAVE】任务时,通用线程的线程数据被初始化过,但我们此时很可能用的是一个线程数据没有被初始化过的TCS。

处理线程数据(、tRTS pthread),然后进一步ECALL【do_ecall(tRTS)】

sgx_status_t do_ecall(int index, void *ms, void *tcs)

检查Enclave状态。

检查线程数据是否被初始化过。检查是否是UnBind TCS策略。检查这个TCS之前是不是给tRTS pthread线程用过,并且这个tRTS pthread线程现在已经退出了【SGX_PTHREAD_EXIT】。检查这个TCS是不是给tRTS pthread用的。按需初始化一下线程数据【do_init_thread】。

《SGXv2.8起Enclave内新增pthread库》从linux-sgx v2.8开始,支持在tRTS创建pthread线程,这个会涉及SGX内部开辟空间和分配TCS给这个pthread线程,同时SGX内部创建pthread线程也需要OCALL到uRTS,来创建好pthread线程并重新进入到tRTS中。

判断当前是不是Root ECALL。通过当前线程栈是否从栈基址开始增长来判断。像ECALL->OCALL->ECALL这种嵌套情况下,后面的ECALL就不是Root ECALL了,如果不是Root ECALL,那么TCS对应的线程栈上就有多个栈空间存在。如果不是Root ECALL,直接【trts_ecall】,只有Root ECALL需要开启tRTS pthread支持,因为非Root ECALL之前在Root ECALL阶段已经做过一次了。

由于从SGX v2.8开始支持tRTS pthread,因此这里默认试图开启tRTS pthread【调用_pthread_enabled】,构建一个空的TLS信息结构体【pthread_info_tls】(【pthread_info】类型)。(如果【_pthread_enabled】开启失败,直接【trts_ecall】)

pthread_info结构体成员变量

赋值 说明

m_local_storage

NULL  

m_pthread

NULL  

m_state

SGX_SUCCESS

TCS的TLS状态

m_mark

初始为全零。紧接着存储【setjmp】获取的当前的上下文,作为一个上下文保存点。

未来【longjmp】函数会跳转到这里,此时伪装成【setjmp】返回,但是返回值非零。返回值非零,意味着ECALL内部调用了【pthread_exit】让该线程终止,因此需要清理线程栈,并把线程TCS的TLS状态置为【SGX_PTHREAD_EXIT】

TCS的TLS上下文,【jmp_buf】类型

然后就调用【trts_ecall】进一步执行ECALL。这里有个小细节就是,用了【random_stack_advance】函数在栈空间中塞了一段无意义的随机大小的缓冲区,使得【trts_ecall】用到的EBP变得随机。

ECALL执行完成返回后,或者是【trts_ecall】执行完,也或者tRTS pthread调用【pthread_exit】然后跳转到【setjmp】“Fake”返回,总归是返回了。对于【pthread_exit】返回,将线程TCS的TLS状态置为【SGX_PTHREAD_EXIT】,标记下一次Root ECALL需要重置TCS。

/*
* Set the TCS's tls state variable to "SGX_PTHREAD_EXIT":
*  1. Pthread() create thread exits normally.
*  2. ECALL() is exited by calling pthread_exit().
*
* In future, the TCS will always be initialized no matter it's used by a new normal root ECALL() or it's used by a new pthread() create thread.
*
* As example: (In bind mode)
* 1. This TCS is used by pthread created thread. So the TCS's state will be set as "SGX_PTHREAD_EXIT" after the thread exits.
* 2. Then the same TCS is used by a normal root ECALL, the TCS will still be initialized because it's state was set as "SGX_PTHREAD_EXIT".
*
*/
_pthread_tls_store_state(SGX_PTHREAD_EXIT);

然后销毁TLS资源,调用【_pthread_tls_destructors】。

唤醒处于【pthread_join】的那个线程,调用【_pthread_wakeup_join】。

查ECALL表,找到ECALL索引值对应的虚拟地址,然后执行之【trts_ecall】

static sgx_status_t trts_ecall(uint32_t ordinal, void *ms)

如果是这个Enclave全局的第一次ECALL。先确保这个ECALL必须是Root ECALL。将所有静态TCS,链接到【g_tcs_node】链表(静态TCS地址用【g_tcs_cookie】异或加密)。然后在tRTS里面将【PT_LOAD】、【PT_GNU_RELRO】段和【ReservedMemMinSize】的虚拟地址访问控制权限用【trts_mprotect】设置好。全局对象的初始化,从动态段找到初始化函数数组,并逐一进行初始化操作。这些动作只需要执行一次,只在第一次ECALL才执行。

然后调用【get_func_addr】。对于常规ECALL,检查ECALL是否被允许,然后查ECALL表【g_ecall_table】,找到ECALL索引值对应的虚拟地址。对于tRTS pthread,返回【_pthread_thread_run】地址,这个函数最终会执行tRTS中【pthread_create】指定的入口函数。

用一下【sgx_lfence】,然后执行得到的虚拟地址,也就是现在才开始执行ECALL真正的内容。

ECALL Switch的过程到此就结束了

ECALL Switch/Ordinary模式相关推荐

  1. switch类型模式

    switch的模式中有一种叫类型模式,可以根据switch的类型来执行对应的case,这点在代码中用到的比较频繁,特别是在对应同类型对象的操作中.本例是把一组数据,转成一种格式,就是很简单的使用swi ...

  2. 《SGX ECALL》汇总

    目录 相关文档 ECALL Switch/Ordinary总结 ECALL Switchless总结 OCALL Switch/Ordinary总结 OCALL Switchless总结 相关文档 E ...

  3. ECALL Swtichless调用及tRTS端Swtichless初始化

    目录 以SampleCode/Switchless为例讲解ECALL Switchless的代码流程 第一个Switchless ECALL,需要在tRTS端初始化一下Switchless模式 接下来 ...

  4. 小谈Intel SGX

    目录 Intel SGX简介 背景 为什么要Intel SGX? Intel SGX尚处于学术讨论 Intel SGX和可信启动什么关系? 开发者眼中SGX长什么样子? SGX访问控制是什么? MEE ...

  5. ECALL的Swtich和Switchless简介

    Linux SGX仓库:https://github.com/intel/linux-sgx 如下是我的分析. 根据ECALL的真正执行者是谁,可以将ECALL执行模式分为Switch模式(线程切换上 ...

  6. 利用Switch实现两台计算机通信

    第7天:交换机配置基本命令 实验 01:实现交换机命令的配置 实验目标 实现两台计算机之间拼通 实验环境 安装Cisco模拟器计算机一台 实验步骤 分别配置两台计算机的IP地址 1.PC0:192.1 ...

  7. 交换机用户模式、特权模式、全局模式、端口模式

    状态 用途 命令 用户模式 只能用来查看一些统计信息 Switch> 特权模式 用户在该模式下可以查看并修改Cisco设备的配置. Switch>en Switch# 全局配置模式 用户在 ...

  8. 交换机的各种工作模式

    所有的交换机都提供用户模式.特权模式.全局配置模式以及各种子模式(接口模式.Line配置模式.VLAN数据库配置模式)等多种级别的配置模式. 1.用户模式 当用户通过交换机的控制台端口或者Telnet ...

  9. 巧用 Spring 自动注入实现策略模式升级版

    一.前言 1.1 背景 在工作过程中,有时候需要根据不同的枚举(常量)执行不同的逻辑. 比如不同的用户类型,使用不同的优惠政策:不同的配置变化,走不同的处理逻辑等. 下面模拟一个根据不同用户类型,走不 ...

最新文章

  1. AIX5.3安装bash shell
  2. 程序员最讨厌的9句话,你可有补充?
  3. 2014-11-18--Hadoop的基础学习(五)--编写不同MapReudce程序及其特性(下)
  4. ORACLE数据库安装图文教程
  5. Entity Framework 普通操作(复习用)——感觉有点不对,需要撸代码验证
  6. 安装node-rfc时需要的cmake依赖
  7. 无处不再的广告_我的机器人现在无处可去。 无家可归。 无服务器。
  8. jboss7.0.2_JBoss AS 7.0.2“ Arc”发布–使用绑定选项
  9. 教育部成立校外教育培训监管司 K12迎最强监管 教育中概股再跳水
  10. bootstrap分割式下拉菜单显示不全
  11. 双亲委托类加载机制_图解JVM类加载机制和双亲委派模型
  12. 校企合作与集成电路--华为在行动
  13. 网秦任命两位新董事 成立投资委员会
  14. 关于maven项目中的Missing artifact *.jar ...
  15. 如何做软件需求分析(个人工作经验总结)
  16. Linux内核中获取虚拟机KVM结构体信息以及vCPU个数
  17. 央行房贷新政难落地:上海四大行按基准利率执行
  18. wps不能粘贴解决办法
  19. 最大堆和最小堆(数据结构)
  20. 【LeetCode】1641. Count Sorted Vowel Strings(动态规划)

热门文章

  1. 为Adobe Reader添加书签功能
  2. 学校计算机网络教室管理员职责,福建广播电视大学计算机网络教室管理人员工作职责...
  3. 起名如何计算五行与笔划
  4. 施耐德电气:以服务和数字化推动可持续落地
  5. 支持备份/还原win10系统的分区工具DiskGenius v4.9.3专业版下载+序列号注册文件激活教程
  6. 关于磁盘阵列和LVM
  7. 绝对布局absoluteLayout
  8. absolutelayout 屏幕外 android,android基础之AbsoluteLayout布局
  9. ENFI下载器v1.3.1版本更新啦
  10. 在没有Release的日子里