本文内容来自于Intel vol3 Chapter 37 Intel? SGX Instruction References
由于本章内容较多,这里只翻译一些比较重要的地方。
本章介绍了Intel? Software Guard Extensions(Intel? SGX)提供的监管者(supervisor)级别和用户(user)级别指令。一般来说,各种功能被编码为ENCLS(监管者级别)、ENCLU(用户级别)和ENCLV(虚拟化操作)指令助记符中的叶函数。不同的叶函数通过在相应的指令助记符的EAX寄存器中指定输入值来进行编码。
level | instruction |
---|---|
APP | ENCLU |
OS(supervisor) | ENCLS |
VMM(Hypervisor) | ENCLV |
主要描述 ENCLS 和ENCLU 指令助记符中的叶函数,ENCLV 指令助记符中的叶函数我这里都跳过。
以下是Intel sgx简单的架构图:
本节涵盖了所有叶函数的ENCLS、ENCLU和ENCLV指令助记符。
对于所有指令,忽略CS.D的值;在64位模式下,地址和操作数为64位,在其他情况下为32位。除了EAX作为输入指定叶函数编号外,每个指令叶函数可能需要RBX/RCX/RDX的全部或部分子集作为输入参数。一些叶函数可能会在一个或多个通用寄存器中返回数据或状态信息。
在Intel SGX中,ENCLS、ENCLU和ENCLV指令助记符用于执行不同的叶函数操作。这些指令的操作和功能由叶函数的编号确定,通过将相应的编号加载到EAX寄存器中作为输入,可以调用特定的叶函数。
除了EAX寄存器之外,一些叶函数可能需要使用RBX、RCX和RDX等寄存器来传递输入参数。这些参数可以是地址、操作数或其他叶函数所需的信息。
一些叶函数可能会在一个或多个通用寄存器(如RAX、RBX、RCX、RDX等)中返回数据或状态信息,开发人员可以根据需要进行相应的寄存器读取。
表37-1总结了supervisor模式enclave指令的隐式寄存器使用情况。
// linux-5.13/arch/x86/include/asm/sgx.h
enum sgx_encls_function {
ECREATE = 0x00,
EADD = 0x01,
EINIT = 0x02,
EREMOVE = 0x03,
EDGBRD = 0x04,
EDGBWR = 0x05,
EEXTEND = 0x06,
ELDU = 0x08,
EBLOCK = 0x09,
EPA = 0x0A,
EWB = 0x0B,
ETRACK = 0x0C,
EAUG = 0x0D,
EMODPR = 0x0E,
EMODT = 0x0F,
};
这里以ECREATE指令为例:
// linux-5.13/arch/x86/kernel/cpu/sgx/ioctl.c
sgx_ioctl:
-->SGX_IOC_ENCLAVE_CREATE:sgx_ioc_enclave_create
-->sgx_encl_create
-->__ecreate
// linux-5.13/arch/x86/kernel/cpu/sgx/encls.h
/**
* __encls_N - encode an ENCLS function that doesn't return an error code
* @rax: function number
* @rbx_out: optional output variable
* @inputs: asm inputs for the function
*
* Emit assembly for an ENCLS function that does not return an error code, e.g.
* ECREATE. Leaves without error codes either succeed or fault. @rbx_out is an
* optional parameter for use by EDGBRD, which returns the requested value in
* RBX.
*
* Return:
* 0 on success,
* trapnr with ENCLS_FAULT_FLAG set on fault
*/
#define __encls_N(rax, rbx_out, inputs...) \
({ \
int ret; \
asm volatile( \
"1: .byte 0x0f, 0x01, 0xcf;\n\t" \
" xor %%eax,%%eax;\n" \
"2:\n" \
".section .fixup,\"ax\"\n" \
"3: orl $"__stringify(ENCLS_FAULT_FLAG)",%%eax\n" \
" jmp 2b\n" \
".previous\n" \
_ASM_EXTABLE_FAULT(1b, 3b) \
: "=a"(ret), "=b"(rbx_out) \
: "a"(rax), inputs \
: "memory"); \
ret; \
})
#define __encls_2(rax, rbx, rcx) \
({ \
unsigned long ign_rbx_out; \
__encls_N(rax, ign_rbx_out, "b"(rbx), "c"(rcx)); \
})
static inline int __ecreate(struct sgx_pageinfo *pginfo, void *secs)
{
return __encls_2(ECREATE, pginfo, secs);
}
表37-2总结了用户模式enclave指令的隐式寄存器使用情况。
省略
省略
省略
在某些情况下,Intel SGX禁止某些叶函数并发操作。以下列举了一些不允许的并发操作的示例:
(1)例如,Intel SGX不允许以下叶子函数与自身同时并发地操作同一个EPC(Enclave Page Cache)页面:
ECREATE、EADD和EREMOVE不允许与自身同时操作同一个EPC页面。
EADD、EEXTEND和EINIT不允许同时操作同一个SECS(Security Enclave Control Structure)。
(2)Intel SGX不允许EREMOVE从正在使用的保护区域中删除页面。
(3)Intel SGX不允许在从保护区域中移除页面时进入(EENTER和ERESUME)该保护区域。
当检测到不允许的操作时,叶子函数可以采取以下其中一种方式:
(1)在RAX中返回SGX_EPC_PAGE_CONFLICT错误代码:
叶子函数可以在RAX寄存器中返回SGX_EPC_PAGE_CONFLICT错误代码。该错误代码表示由于与同一EPC页面或SECS上的并发操作冲突,所请求的操作不被允许。
(2)引发#GP(0)异常:
叶子函数也可以引发通用保护故障(#GP)异常,而不是返回错误代码。具体而言,它可以引发#GP(0)异常。
#GP(0)异常是处理器异常的一种,当特权指令执行不当或检测到内存访问违规时触发。在Intel SGX的上下文中,#GP(0)异常可以用于指示由于并发操作冲突而导致的不允许的操作。
为了防止这类异常,软件必须对叶子函数进行序列化操作,或者防止这些叶子函数访问同一个EPC页面。
序列化叶子函数意味着确保同一时间只有一个叶子函数在执行,从而避免它们并发操作同一个EPC页面或SECS的情况。这可以通过使用互斥锁、信号量或其他同步机制来实现。通过对叶子函数进行序列化,可以确保它们按顺序执行,从而避免并发冲突。
指令说明:
ENCLS指令用于调用指定的特权级Intel SGX叶子函数,用于管理和调试enclave。软件通过将适当的值设置为输入寄存器EAX来指定叶子函数。寄存器RBX、RCX和RDX具有叶子函数特定的用途,可以用作输入、输出或未使用。在64位模式下,指令会忽略RAX寄存器的高32位。
如果CR0.PE = 0或RFLAGS.VM = 1,或者在系统管理模式(SMM)下执行ENCLS指令,则会产生无效操作码异常(#UD)。此外,如果在CPL > 0时尝试执行该指令,将导致#UD的一般保护异常。如果CR0.PG = 0或尝试调用未定义的叶子函数,则该指令会产生一般保护异常(#GP)。
在VMX非根操作中,执行ENCLS指令可能会导致VM退出,如果“enable ENCLS exiting” VM执行控制位设置为1。在这种情况下,ENCLS的各个叶子函数的执行受VMCS中的ENCLS退出位图字段的控制。该字段中的每个位对应于ENCLS叶子函数的索引(如EAX中提供的索引)。在VMX根操作中,软件可以通过设置“enable ENCLS exiting” VM执行控制位并设置ENCLS退出位图中相应的位,拦截VMX非根操作中各种ENCLS叶子函数的调用。
地址和操作数在64位模式下为64位(IA32_EFER.LMA = 1 || CS.L = 1),在64位模式之外为32位(IA32_EFER.LMA = 0 || CS.L = 0)。CS.D的值对地址计算没有影响。DS段用于创建线性地址。
在指令中,忽略段覆盖前缀、地址大小覆盖前缀,以及在64位模式下的REX前缀。
指令说明:
ENCLU指令用于调用指定的非特权级Intel SGX叶子函数。软件通过将适当的值设置为输入寄存器EAX来指定叶子函数。寄存器RBX、RCX和RDX具有叶子函数特定的用途,可以用作输入、输出或未使用。在64位模式下,指令会忽略RAX寄存器的高32位。
如果CR0.PE = 0或RFLAGS.VM = 1,或者在系统管理模式(SMM)下执行ENCLU指令,则会产生无效操作码异常(#UD)。此外,如果在CPL < 3时尝试执行此指令,将导致#UD的一般保护异常。如果CR0.PG或CR0.NE为0,或者尝试调用未定义的叶子函数,则该指令会产生一般保护异常(#GP)。如果CR0.TS = 1,则ENCLU指令会产生设备不可用异常(#NM)。
地址和操作数在64位模式下为64位(IA32_EFER.LMA = 1且CS.L = 1),在64位模式之外为32位(IA32_EFER.LMA = 0或CS.L = 0)。CS.D的值对地址计算没有影响。DS段用于创建线性地址。
在指令中,忽略段覆盖前缀、地址大小覆盖前缀,以及在64位模式下的REX前缀。
省略
ENCLS指令提供了一组叶子函数,每个叶子函数都需要使用EAX来指定叶子函数索引,并可能使用其他隐式寄存器来指定叶子函数特定的输入参数。指令操作数编码表提供了每个隐式寄存器的使用细节以及相关的输入/输出语义。该表详细描述了每个叶子函数的使用方式和相关的输入输出参数。
在许多情况下,输入参数会指定与EPC内或外的内存对象相关联的有效地址,这些内存对象的内存寻址语义也在单独的表格中进行了总结。
在ENCLS指令中,一些叶子函数需要操作内存对象,因此需要传递有效地址作为输入参数。这些内存对象可以是在Enclave Page Cache (EPC) 内或外的内存。针对这些内存对象,有一个单独的表格提供了其内存寻址语义的总结。该表格描述了如何根据有效地址访问这些内存对象,包括内存对象的位置、大小和访问权限等信息。
指令说明:
ENCLS[ECREATE]是在enclave构建过程中执行的第一条指令。ECREATE将EPC之外的SECS结构复制到EPC内的一个SECS页中。SECS的内部结构对软件不可见。
ECREATE将设置受保护的SECS中的字段,并将页面标记为EPC内的有效页面。ECREATE会初始化或检查未使用的字段。
软件在源结构中设置以下字段:SECS:BASEADDR、SECS:SIZE(以字节为单位)、ATTRIBUTES、CONFIGID和CONFIGSVN。SECS:BASEADDR必须自然对齐到一个SECS.SIZE边界。SECS.SIZE必须至少为2个页面(8192字节)。
源操作数RBX包含PAGEINFO结构的有效地址。PAGEINFO包含源SECS的有效地址和SECINFO的有效地址。PAGEINFO中的SECS字段不使用。
RCX寄存器是目标SECS的有效地址。它是EPC中的一个空槽的地址。SECS结构必须按页面对齐。SECINFO标志必须指定该页面为SECS页。
指令说明:
该叶子函数将来自非enclave内存的源页面复制到EPC中,将EPC页面与EPC中的SECS页面关联,并将线性地址和安全属性存储在EPCM中。作为关联的一部分,enclave偏移量和安全属性被测量并扩展到SECS.MRENCLAVE中。只有在当前特权级为0时才能执行此指令。
RBX包含PAGEINFO结构的有效地址,而RCX包含EPC页面的有效地址。
下表提供了EADD叶子函数的内存参数的附加信息:
指令说明:
该叶子函数使用由“EEXTEND” || ENCLAVEOFFSET || PADDING || 256字节的enclave页面组成的EXTEND字符串更新SECS的MRENCLAVE测量寄存器。只有在当前特权级为0且enclave未初始化时才能执行此指令。
RBX包含要测量区域的SECS的有效地址。该地址必须与用于将页面添加到enclave中的地址相同。
RCX包含要测量的256字节区域的EPC页面的有效地址。DS段用于创建线性地址,不支持段覆盖。
指令说明:
该叶子函数是在enclave构建过程中执行的最后一条指令。在EINIT之后,MRENCLAVE测量完成,enclave准备使用EENTER指令开始执行用户代码。
EINIT接受一个SIGSTRUCT和EINITTOKEN的有效地址作为参数。SIGSTRUCT描述了enclave,包括MRENCLAVE、ATTRIBUTES、ISVSVN、3072位RSA密钥以及使用该密钥进行签名的签名。
SIGSTRUCT必须填充两个值q1和q2,这些值使用下面的公式计算:
q1 = floor(Signature2 / Modulus);
q2 = floor((Signature3 - q1 * Signature * Modulus) / Modulus);
EINITTOKEN包含MRENCLAVE、MRSIGNER和ATTRIBUTES。这些值必须与SECS中对应的值匹配。如果使用调试启动密钥创建了EINITTOKEN,则enclave必须处于调试模式。
EINIT执行以下步骤,可以在图37-1中看到:
(1)验证SIGSTRUCT是否使用封闭的公钥进行了签名。
(2)检查完成的SECS.MRENCLAVE计算是否等于SIGSTRUCT.HASHENCLAVE。
(3)检查SIGSTRUCT.ATTRIBUTES中是否没有保留位设置为1,并且SIGSTRUCT.ATTRIBUTESMASK中的保留位都设置为0。
(4)检查除非SIGSTRUCT.MODULUS的SHA256摘要等于IA32_SGX_LEPUBKEYHASH,否则不应在SIGSTRUCT.ATTRIBUTES中设置任何受控的ATTRIBUTES位。
(5)检查SIGSTRUCT.ATTRIBUTES是否等于逻辑与(SIGSTRUCT.ATTRIBUTEMASK与SECS.ATTRIBUTES)的结果。
(6)如果EINITTOKEN.VALID为0,则检查SIGSTRUCT.MODULUS的SHA256摘要是否等于IA32_SGX_LEPUBKEYHASH。
(7)如果EINITTOKEN.VALID为1,则检查EINITTOKEN的有效性。
(8)如果EINITTOKEN.VALID为1,则检查EINITTOKEN.MRENCLAVE是否等于SECS.MRENCLAVE。
(9)如果EINITTOKEN.VALID为1且EINITTOKEN.ATTRIBUTES.DEBUG为1,则SECS.ATTRIBUTES.DEBUG必须为1。
(10)提交SECS.MRENCLAVE,并根据SIGSTRUCT设置SECS.MRSIGNER、SECS.ISVSVN和SECS.ISVPRODID。
(11)更新SECS为已初始化状态。
(12)定期,EINIT轮询某些异步事件。如果检测到此类事件,则以失败代码完成(ZF=1且RAX=SGX_UNMASKED_EVENT),并将RIP递增到下一条指令的位置。这些事件包括外部中断、不可屏蔽中断、系统管理中断、机器检查、INIT信号和VMX抢占定时器。如果待处理事件被禁止(例如,由于MOV SS阻塞或STI阻塞导致外部中断被禁止),EINIT不会失败。
(13)清除RFLAGS中的以下位:CF、PF、AF、OF和SF。当指令以错误完成时,将设置RFLAGS.ZF为1,并在RAX中设置相应的错误位。如果没有发生错误,则清除RFLAGS.ZF,并将RAX设置为0。
指令说明:
该叶子函数将一页从常规主存复制到EPC中。作为复制过程的一部分,页面将进行加密认证和解密。只有在当前特权级为0时才能执行此指令。
ELDB叶子函数在复制后将EPC中目标页面的EPCM条目中的BLOCK位设置为1。ELDU叶子函数在复制后将EPC中目标页面的EPCM条目中的BLOCK位清除为0。
RBX包含PAGEINFO结构的有效地址;RCX包含目标EPC页面的有效地址;RDX保存持有页面版本的版本数组槽的有效地址。
ELDBC和ELDUC叶子函数与ELDB和ELDU非常相似。它们在需要获取锁定的任何页面的并发冲突时提供错误代码。这些页面包括目标页面、SECS和VA槽。
下表提供了ELDB/ELDU叶子函数的内存参数的附加信息:
指令说明:
该叶子函数在由DS:RCX给出的EPC页面中创建一个空的版本数组,并为该页面设置EPCM属性。在执行此指令时,寄存器RBX必须设置为PT_VA。
下表提供了有关EPA叶函数的内存参数的附加信息:
指令说明:
该指令读取有关EPC页面的类型和状态信息,并将其返回在RDINFO结构中。结构的STATUS字段描述页面的状态,并确定其余字段的有效性。FLAGS字段返回页面的EPCM权限、页面类型以及页面的BLOCKED、PENDING、MODIFIED和PR状态。对于enclave页面,结构的ENCLAVECONTEXT字段返回SECS.ENCLAVECONTEXT的值。对于非enclave页面(例如VA),ENCLAVECONTEXT返回0。
对于无效或非EPC页面,该指令返回一个信息代码,指示页面的状态,并填充STATUS字段。
如果目标EPC页面正在被并发的SGX指令修改,ERDINFO将返回一个错误代码。
RBX包含RDINFO结构的有效地址,而RCX包含EPC页面的有效地址。
下表提供了关于ERDINFO叶函数的内存参数的附加信息:
指令说明:
该叶子函数提供了硬件跟踪软件是否成功完成了所需的TLB地址清除的机制。只有在当前特权级为0时才能执行该指令。
RCX的内容是一个EPC页面的有效地址。
下表提供了关于ETRACK叶功能的内存参数的附加信息:
指令说明:
此叶子函数将一个页面从EPC复制到常规主存。在复制过程中,页面将进行加密保护。只有在当前特权级为0时才能执行此指令。
下表提供了关于EPA叶函数的内存参数的附加信息:
指令说明:
ENCLU[EENTER]指令将执行转移到一个enclave中。在指令结束时,逻辑处理器以计算得出的EnclaveBase + TCS.OENTRY为RIP,在enclave模式下执行。如果目标地址不在CS段(32位)内或不是规范地址(64位),将引发#GP(0)异常。
EENTER是一个序列化指令。
指令说明:
ENCLU[EEXIT]指令退出当前正在执行的enclave,并跳转到RBX中指定的位置。RCX接收当前的AEP。如果RBX不在CS内(32位模式)或者不是规范地址(64位模式),将引发#GP(0)异常。
如果RBX指定的地址位于enclave内部,则该指令将正常完成。下一条指令的获取将在非enclave模式下进行,但将尝试从enclave内部获取。该获取将返回一个固定的数据模式。
如果任何寄存器中包含了机密信息,enclave软件有责任清除这些寄存器。
如果在enclave进入时修改了XCR0寄存器的值,它将被恢复为最近一次EENTER或ERESUME时的值。
如果enclave处于opt-out状态,RFLAGS.TF将从先前在EENTER中保存的值加载。
代码和数据断点将取消抑制。
性能监控计数器将取消抑制。
指令说明:
ENCLU[EGETKEY]指令从处理器特定的密钥层级中返回一个128位的秘密密钥(secret key)。寄存器RBX包含一个KEYREQUEST结构的有效地址,该指令解释该结构以确定所请求的密钥。下面的"请求的密钥"部分提供了可以请求的密钥的描述。寄存器RCX包含将返回密钥的有效地址。RBX和RCX中的地址都应该是enclave内部的位置。
EGETKEY指令使用基于证书的身份标识(MRSIGNER、ISVPRODID、ISVSVN)和SGX实现版本(CPUSVN)来获取配置密钥,从而使英特尔的配置服务能够验证配置Enclave是否由英特尔签名。配置服务还可以根据CPUSVN拒绝通信,以确定是否存在漏洞。通过使用这些标识和版本信息,英特尔的配置服务可以验证配置Enclave是否经过英特尔的授权,从而确保了系统的安全性和可信度。如果CPUSVN存在漏洞或不满足安全要求,配置服务可以拒绝与Enclave进行通信,以防止潜在的安全问题。
EGETKEY使用处理器唯一值从一系列可能的输入中派生密钥,以创建特定的密钥。此指令叶子函数只能在enclave内部执行。
在验证操作数之后,该指令确定要生成的密钥,并执行以下操作:
(1)根据Table 37-64,该指令组装密钥的派生数据。
(2)使用派生数据和特定包值计算派生密钥。
(3)将计算得到的密钥输出到RCX中指定的地址。
指令说明:
如果操作数未正确对齐,该指令将返回#GP(0)异常。指令成功完成后,将清除RFLAGS的{ZF, CF, AF, OF, SF, PF}标志位。如果用户尝试基于无效的CPUSVN或ISVSVN(当用户请求被接受时,请参考下表)请求密钥,请求未被授予请求属性的密钥,或请求硬件不支持的密钥,该指令将返回一个错误代码。这些检查可以按任意顺序进行。因此,一个错误号(例如,无效属性)并不意味着没有其他错误。不同的处理器对于相同的Enclave可能会给出不同的错误号。软件的正确性不应依赖于本节文档中所述检查的顺序。在这种情况下,将设置ZF标志,RAX中的相应错误位(SGX_INVALID_SVN、SGX_INVALID_ATTRIBUTE、SGX_INVALID_KEYNAME)将被设置,并且由RCX指定的地址上的数据保持不变。
Requesting Keys
KEYREQUEST结构(参见第34.18.1节)标识所需提供的密钥。Keyrequest.KeyName字段标识请求的密钥类型。
Deriving Keys
密钥派生是基于enclave特定值(参见Table 37-64)和处理器密钥的组合。根据所请求的密钥,字段可能被定义包含,或者该值可以从KeyRequest中包含。Table 37-64中的“yes”表示该字段的值包含在其默认位置中,该位置在源行中标识,“request”表示该字段的值从对应的KeyRequest字段中包含。
允许指定CPU、ISV代码或enclave配置的SVN的密钥具有额外的要求。调用者不能请求超出当前CPU、ISV或enclave配置的SVN的密钥。
几个密钥是受访问控制的。访问Provisioning Key和Provisioning Seal密钥需要设置enclave的ATTRIBUTES.PROVISIONKEY。EINITTOKEN Key需要设置ATTRIBUTES.EINITTOKEN_KEY,并且SECS.MRSIGNER等于IA32_SGXLEPUBKEYHASH。
一些密钥是基于硬编码的PKCS填充常量(352字节字符串)派生的:
HARDCODED_PKCS1_5_PADDING[15:0] := 0100H;
HARDCODED_PKCS1_5_PADDING[2655:16] := SignExtend330Byte(-1); // 330字节的0FFH
HARDCODED_PKCS1_5_PADDING[2815:2656] := 2004000501020403650148866009060D30313000H;
指令说明:
此叶子函数创建一个加密的REPORT,描述enclave的内容。该指令只能在enclave内部执行。加密的报告可以被其他enclave使用,以确定该enclave是否在同一平台上运行。
RBX包含将验证REPORT输出的enclave的MRENCLAVE值的有效地址,该值使用通过EGETKEY命令为该enclave提供的REPORT密钥进行身份验证。RCX包含一个64字节的REPORTDATA结构的有效地址,允许调用指令的人将数据与调用指令的enclave关联起来。RDX包含将由指令输出的REPORT的地址。
该指令叶子函数执行以下操作:
(1)验证三个操作数(RBX、RCX、RDX)是否在enclave内部。
(2)根据RBX(TARGETINFO)中的值,为目标enclave计算报告密钥。
(3)组装enclave的SECS数据以完成REPORT结构(包括使用RCX(REPORTDATA)操作数提供的数据)。
(4)对REPORT结构进行加密哈希计算。
(5)将计算得到的哈希添加到REPORT结构中。
(6)将完成的REPORT结构输出到RDX(OUTPUTDATA)中的地址。
如果操作数没有正确对齐,该指令将失败。
CR_REPORT_KEYID用于提供密钥的磨损保护,由SGX TCB中的可信实体在平台启动时使用统计上唯一的值填充。
描述:
ENCLU[ERESUME]指令用于恢复由于异常或中断而中断的enclave的执行,使用之前存储在SSA(Saved State Area)中的机器状态。通过使用该指令,enclave可以从中断的位置继续执行,利用保存的机器状态信息。
Intel vol Chapter 37