再回顾SGX初始化(四)——收尾及Switchless初始化

目录

裁剪页放到Trimmed列表(需要支持EDMM)

设置内存访问控制属性

填充TCS最小池

Switchless初始化

到此,SGX初始化就全部结束了


继《再回顾SGX初始化(三)——uRTS维护Enclave、tRTS完成Enclave构建收尾确认工作》之后,我们又回到了uRTS的__create_enclave函数。此时uRTS、tRTS两边都已经完成了Enclave初始化工作,也就是说Enclave内外两边都已经搞定了Enclave初始化所需要的工作。

那么就继续__create_enclave函数的漫漫长征路。

裁剪页放到Trimmed列表(需要支持EDMM)

if (SGX_SUCCESS != (ret = loader.post_init_action_commit(layout_start, layout_end, 0)))

由于前面uRTS已经发出裁剪请求,并且Enclave已经EACCEPT了。

那么就发SGX_IOC_ENCLAVE_NOTIFY_ACCEPT信号给SGX驱动,让SGX驱动将裁剪页放到Trimmed列表(意味着裁剪页现在开始可以被自由移除),并且这些裁剪页也会从用于管理已分配Enclave虚拟页的基数树上删除掉。

/**
 * sgx_ioc_page_notify_accept() - Pages defined in range will be moved to
 * the trimmed list, i.e. they can be freely removed from now. These pages
 * should have PT_TRIM page type and should have been eaccepted priorly
 * @arg range address of pages
 */
long sgx_ioc_page_notify_accept(struct file *filep, unsigned int cmd, unsigned long arg)

设置内存访问控制属性

if(SGX_SUCCESS != (ret = loader.set_memory_protection()))

使用ELF解析器(使用mprotect)设置ELRANGE中各个Section的访问属性以及各个Segment对应的访问属性【ElfParser::set_memory_protection,先对每个Section按照最开始记录的Section对象中声明的虚拟地址空间访问属性进行mprotect设置,再对每个Segment按照ElfW(Phdr).p_flags记录的那样进行虚拟地址空间访问属性设置】。

如果元数据版本较新,并且当前环境支持EDMM,那么将PT_GNU_RELRO、PT_LOAD(不可写的并且可能会有重定位过程的)段交由SGX驱动(SGX_IOC_ENCLAVE_EMODPR,SGX会调用EMODPR)将Enclave Page设置为读执行(RX)权限,相比于mprotect是对页表机制里面的访问属性进行设置,Ring0权限的EMODPR硬件指令会对管理EPC属性的EPCM进行设置。rsrv内存用EMODPR改为RW。不支持EDMM等情况,就不进行改动。

对上下文相关的内存,用mprotect将内存访问控制属性设置为RW。其中rsrv内存在不支持EDMM情况下,不要用mprotect改变它的属性。

针对EREMOVE掉的页,需要用mprotect设置为PROT_NONE,不然这个页一旦被访问,会发生总线异常(sigbus exception),因为这个EPC页已经被EREMOVE硬件指令给从当前Enclave中去除,EPCM也没有再维护EREMOVE掉的页的信息。

上面所涉及到的ELRANGE的页本身在ECREATE、EADD、EREMOVE时就设置或更改了基本的EPCM项,这里主要是进行调整,以及在进程虚拟地址空间用mprotect进行访问控制设置。

if(!get_enclave_creator()->is_EDMM_supported(get_enclave_id()) && (layout->entry.id == LAYOUT_ID_RSRV_MIN ||layout->entry.id ==  LAYOUT_ID_RSRV_INIT))
    //Don't change the rsrv memory's attributes if platform isn't support EDMM
    continue;
 //Here: URTS will change rsrv memory's attributes to RW forcely although it's signed by sign_tool as RWX (<ReservedMemExecutable>1</ReservedMemExecutable>).

填充TCS最小池

ret = enclave->fill_tcs_mini_pool_fn();

如果支持EDMM,就填充一下TCS最小池(就是说我们最小要填充指定数量【CTrustThreadPool.m_tcs_min_pool,源自config.xml文件里的说明】的TCS池项,这些数量是必须的,而额外的动态TCS这里不做填充)。这个函数的做法挺有趣,在第一次调用该函数的时候,起一个pthread线程,完成TCS最小池的填充,之后这个线程会休眠,当下次调用该函数时,唤醒这个线程去填充TCS最小池,这里是一个循环,每唤醒一次就循环一轮。这种做法的其中一个原因也是因为后续有时也会进行填充TCS最小池。

最终填充TCS最小池的操作是由如下函数完成

sgx_status_t CTrustThreadPool::fill_tcs_mini_pool()

如果有动态当前未分配具体空间的TCS,并且已有的空闲TCS,还不足最小数量的要求,那么就需要构建动态TCS【之前是个未分配空间的TCS】填充到TCS最小池。《构建动态TCS页》讲述了动态TCS构建过程。

然后唤醒一下其他线程,其他线程(比如主线程)可能一直等待新的空闲的TCS。并且有时空闲TCS数量不足最小池的大小,使得我们需要再填充TCS最小池。不过我们这里是第一次填充TCS最小池,为的就是让TCS最小池至少先足数。

Switchless初始化

见《Switchless模式的初始化

到此,SGX初始化就全部结束了

最后将【CLoader::m_enclave_id】,赋值给创建SGX的API中的参数【global_eid】,让编程人员后续可以使用这个【global_eid】调用ECALL函数,主要原因也是因为一个进程可以创建多个Enclave,这个ID是为了区分一个进程内的多个Enclave。

ret = sgx_create_enclave_ex(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, NULL, NULL, &global_eid, NULL, SGX_CREATE_ENCLAVE_EX_SWITCHLESS, enclave_ex_p);

有空我调整一下章节的编排

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值