Intel TME&MKTME Spec 翻译
TME & MKTME
本文是对Intel TME&MKTME的翻译,夹带了很多个人理解(Q&A),不周之处还望包含.
overview
在BIOS中配置并lock,一旦lock,SoC中external memory bus上的所有数据均会加密(以加密key的方式),加密方式是128bit/256bit AES XTS算法,具体取决于平台提供的算法和BIOS中的配置。
key由SoC中实现的hardware random number generator产生,这些key无法被软件或者接入到SoC上的硬件接口读到。
TME能够以AES-XTS算法对external memory bus上的数据以及DIMM中的数据进行保护。
MKTME基于TME实现,并支持了更多的key。
软件可以通过配置SoC来使用一定数量(<=SoC支持的数量)的key。
Ewan:
Q: 这些key是平台提供好的,软件从其中选择,还是软件自己产生的?
A: 软件可以通过配置,决定是否使用软件产生的key,或硬件产生的key,或混合模式。
软件可以使用这些key加密任何page。
Ewan:
Q: 这里的加密粒度为什么是page而不是字节或者其他的什么粒度?
A: 这其实由key加密的原理决定。加密引擎通过识别发送到memory controller上的物理地址,提取其中的keyID字段,并查询自己维护的key table,来决定是否要对这笔memory access进行加密,因此理论上加密粒度可以精确到字节,因为通过物理地址可以访问任何一个字节。但是,通常来说系统中的memory都是由分页模式管理的,如果想要加密某个字节,必须对该字节所在的物理地址的keyid field进行填充,而该物理地址一定属于某个page,所以必须将该物理地址&~0xFFF(假设页大小是4K)的结果写入最后一级页表entry,所以写入最后一级页表entry的物理地址是带keyID的,最后12个bit为0的一个地址,如果keyID存在,那么memory controller会对最后一级页表的某个entry指向的一个page全部加密。
spec这里只是说了最寻常的情况是加密任何page,其实还可能加密更小的粒度,例如如果在实模式下,通过写CS/DS/FS/GS段寄存器中的物理地址的keyid field,就可以加密整个段。
MKTME默认使用TME,除非软件指定了使用MKTME.
MKTME还支持软件提供一个key,这种能力适合对非易失性memory的加密,或与认证机制结合的加密,或密钥提供服务的使用。
Q: 第2,3种场景解释?
A: 2: 设想先开机,然后输入密码,如果输入密码失败,则无法开机。 3: 密钥提供软件提供一个密钥,并对全内存或某page进行加密。
在虚拟化场景下,VMM应当使用MKTME功能以透明的向虚拟机提供TME功能。
OS可以通过修改部分code达到在native和虚拟化环境下同时使用MKTME,如果使用得当,在虚拟机中就可以使用MKTME。
TME Introduction
AES XTS加密引擎就在DRAM controller和CPU Core的数据访问通路上,因此所有进出SoC的memory操作都会加密。SoC内部的数据,包括在cache中的,都保留了原始样子,因此适用于当前所有的软件和IO模型。
这些加密key由CPU自己产生,因此软件不可见,如果使用NVRAM,当该NVRAM被当作普通RAM时,可以使用CPU产生的这些key加密,如果该NVRAM被当作非易失性memory时,可以选择在重启系统时,或进入cpu power cycle时,使用同样的key,即不用再重新生成key。
MKTME Introduction
MKTME和TME的硬件架构是一样的,只是支持了更多的key。
MKTME在native OS和虚拟化场景下都能用,图中展示的是在虚拟化场景下使用的一个例子。
一般来说,VMM使用keyID0来加密自己的memory,也就是TME所使用的。当然VMM也可以使用别的keyID来加密自己的memroy。图中,VM1使用keyID1来加密自己的私有memory,VM2使用KeyID2。共有memory,使用keyID3加密。当然,VM1也可以使用KeyID0来加密自己的任何page。
KeyID包含在page table entry中,是物理地址field的高位。
KeyID对物理地址高位的利用,也可以扩展到IA pages(最普通的页,即MMU通过页表管理的页)和IOMMU pages。只要在SoC内部,KeyID总是留在物理地址的高位,除了在tweak时,以及在external bus上时。在SoC外部或在对AES XTS做tweak的时候,不会使用keyID.[tweak**即微调,是AES加密算法的一个步骤]
TME & MKTME: 枚举,控制寄存器
枚举
- 是否支持TME,MKTME
- MKTME支持的最大keys数量
BIOS必须在早期boot阶段通过MSR激活TME/MKTME,并选择具体支持多少keys。一旦激活TME/MKTME,挂在SoC/CPU上的所有memory(除了TME Exclusion Range)都会使用CPU生成的key加密,并且在接下来的每次boot时都会使用CPU生成的加密key加密。注意可以通过设置MSR来绕过加密,即所有使用keyID0的范围跟都不会加密。
可以使用MKTME枚举来发现CPU和集成到external memory controller上的部分memory的capabilities,但是不需要在这个枚举过程中体现external memory controller的features,部分集成在这些controller上的memory也不需要体现。BIOS查询某memory是否可以加密,可以通过基于UEFI2.8协议,去查询该memory的EFI_MEMORY_CPU_CRYPTO属性,该bit为1表示可以加密,为0表示不能加密。BIOS/OS在加密某memory之前,应该查询该memory到底能不能被CPU加密。
TME的枚举
CPUID.TME (CPUID.(EAX=07H, ECX=0H): ECX[13]) 指示以下4个MSR是否存在:
• IA32_TME_CAPABILITY – Address 981H
• IA32_TME_ACTIVATE – Address 982H
• IA32_TME_EXCLUDE_MASK – Address 983H
• IA32_TME_EXCLUDE_BASE – Address 984H
MKTME的枚举
CPUID.TME说明了 IA32_TME_CAPABILITY MSR是否存在,该MSR会指示TME的一些feature,以及MKTME是否存在及MKTME的features。MKTME由BIOS使用 IA32_TME_ACTIVATE这个MSR激活,激活MKTME的先决条件是激活TME。
Related MSRs
IA32_TME_CAPABILITY MSR
keyID所使用的位数不会影响CPUID物理地址最大位数的报告【CPUID(EAX = 0x80000008)的bit0-bit7】,无论上面那个MSR中报告的keyID bits是多少位。
IA32_TME_ACTIVATE MSR
该MSR用于lock以下MSR,在lock之后,任何对这些MSR的读操作将会被ignored。如果CPU reset了,这个lock也会reset。
• IA32_TME_ACTIVATE
• IA32_TME_EXCLUDE_MASK
• IA32_TME_EXCLUDE_BASE
这俩exclude MSR应该在activate MSR之前配置好。
对activate MSR进行WRMSR写操作会导致的情况。
上表即:
- Activate MSR没被枚举,却写该MSR。#GP
- Activate MSR中的lock=1,却写该MSR。 #GP
- 向该MSR中的reserved bits写1.
- 写不支持的加密算法
- 写该MSR,但bypass = 1. 不会报错,但TME会diabled,并且之后对该MSR的读会返回xxxx01b.
- 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=0(即产生一个new key),并且RNG(随机数产生器)成功,这是正常状态,即TME activate成功。之后的读取会返回xxxx11b.
- 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=0(即产生一个new key),并且RNG(随机数产生器)失败,这是错误状态,即TME activate失败。之后的读取会返回xxxx00b.
- 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=1(即从storage 加载一个key),并且从storage加载了一个非0 key,这是正常状态,即TME activate成功之后的读取会返回xxxx111b.
- 写该MSR,但bypass = 0 && hardware encryption enable = 1,并且key select=1(即从storage 加载一个key),并且从storage加载了一个值为0 key,这是错误状态,即TME activate失败。之后的读取会返回xxxx100b.
- 写该MSR,值为其它合理值,之后的读会返回写的值以及lock =1.
- 写该MSR,设置的MKTME的keyid bits大于在枚举时的最大bits,产生#GP
- 写该MSR,设置了keyid bits,但是TME并未enable(bypass = 0 && hardware encryption enable = 1),产生#GP
- 写该MSR,但是lock没被set(hardware没有成功set该bit),则表示write没有被成功submit。
MK_TME_CORE_ACTIVATE MSR
这是一个BIOS-only MSR(每个core一个), 在成功写IA32_TME_ACTIVATE之后,MK_TME_CORE_ACTIVATE 应该被WRMSR EDX:EAX==0,如果这次WRMSR fail,会导致不可预测的行为。如果不支持MKTME,对这个MSR的访问行为会导致#GP。
每一个SMI都会导致core上的该MSR被load with pakage MSR value。(what is pakage MSR?--- a shadow MSR to store value that was written in)
IA32_TME_EXCLUDE_MASK MSR
TME或MKTME(keyid=0时)支持对某些memory加密,同时对特定范围的memory不加密(通过IA32_TME_EXCLUDE_MASK MSR实现)。特别的,在用MKTME时,keyid!=0时,TME exclusion Range MSR不会起作用。
起初这个exclude功能是为了exclude那些不给OS展示的memory,当然,TME/MKTME并没有限制对这个exclude功能的使用仅限于不给OS展示的memory,可以随便用。软件可以通过读这个MSR确认是否有exclude功能。该range 寄存器定义在许多其它range resiter后面。
定义了base和mask,怎么算出一个范围呢?公式如下:
Address_in_range & TMEEMASK = TMEEBASE & TMEEMASK
例如TMEEMASK = 0x0FFF_F000,TMEEBASE = 0x1000_0000
那么根据这个公式,TMEEBASE & TMEEMASK = 0,只要与TMEEMASK做与运算,结果为0的地址都在这个范围内,那么会有多个范围,如0x0000_0000到0x0000_0FFF,0x1000_0000到0x1000_0FFF…
spec规定TME/MKTME exclude的memory范围必须是连续的,所以给MASK设计值时注意连续性。如将TMEEMASK设置为0xFFFF_0000,以将范围固定为0x1000_0000到0x1000_FFFF.
MKTME 的运行时行为
在MKTME由BIOS激活之后,与没有MKTME相比,处理器的行为在运行时会有一些不同。
物理地址规范的改变
MKTME最重要的改变还是对物理地址的重利用,使用物理地址的高bits与memory controller中的加解密引擎交互,基于这种重利用,为了保证物理地址行为的正确性,需要部分硬件和软件行为的变化。
当MKTME激活之后,从MAX_PA_BIT(CPUID报的)开始的几位会被用于KeyID。
即CPU上本来的物理地址宽度不会改变,例如CPUID报这个CPU上的物理地址宽度为48bit,本来这48bit都用来寻址物理memory,但在激活MKTME之后,bit47-bit44就会用于keyID,而不是原来的物理memory寻址。
普通分页模式行为的改变
如果使用分页模式,但不使用EPT(既使用EPT又使用分页模式,说明当前处于虚拟化场景下,即Guest memory管理),物理地址的MAX_PA_BIT-bit0本来用于寻址各级页表,但激活MKTME之后,从MAX_PA_BIT开始的几位会被用于keyID,包括CR3中的物理地址的高几bit也会用于keyID。
注意如果在使用EPT,即处于Guest memory管理上下文,IA paging实际使用的是GPA,由于GPA并没有被MKTME修改,因此GPA只单纯用于EPT页表寻址,而不参与加解密。EPT table walk的优先级比使能MKTME高。
EPT 分页模式行为的改变
如果在non-root下使用EPT,进行EPT table walk时的物理地址的高几个bit,会用于KeyID,包括EPTP中的物理地址的高几bit,也会用于KeyID。注意如果Guest OS中 使用了MKTME,那么EPT使用的物理地址中就会包含KeyID.
即在L1 guest中使用EPT,那么其实进行EPT table walk的物理地址是否包含keyID bits,取决于L1 Guest是否使用了MKTME,也就是L0要向L1提供MKTME功能。
其它物理地址行为
其它物理地址,类似于VMCS pointers,物理地址定位的bitmaps等,他们的物理地址中,都会包含从MAX_PA_BIT开始的keyID。注意所有的reserved bit checking保持不变,这些checking都会基于Max_PA_BIT进行reserved bits检测。例如MAX_PA_BIT = 47,那么63-48为reserved,此时reserved bit check都是检查bit48-bit63是否被set。当然reserved bit分布于各个bit,但是高位bit与我们的话题联系更加紧密。
MKTME Key 的编程
MKTME 引擎肚子里有一个key table,无法被software访问。该table可以存储和每个keyID关联的key和加密模式。每个keyID应该从3种加密模式中选一个:
- 使用指定key加密
- 不加密
- 使用TME key加密
未来的实现中可能会包含更多加密模式。可以使用PCONFIG指令来编辑KEYID属性,目前PCONFIG指令只是用于MKTME,以后会引入更多PCONFIG可以操作的目标。因此,PCONFIG指令的枚举与MKTME的枚举是独立的。
所以PCONFIG中的P意思是platform,即对该platform上的某个功能集进行设置,并没有特定指示MKTME。
PCONFIG 指令描述
软件可以使用PCONFIG指令对platform上的features进行配置。
PCONFIG支持多个leaves(叶),叶可以在执行PCONFIG指令时使用RAX寄存器传入。RBX,RCX,RDX有leaf特定的用途。PCONFIG是一个“pakage scope”的指令,执行时需要在每个物理pakage中执行一次,来配置这个pakage中所有CPU的功能。
目前PCONFIG只有一个leaf function,就是MKTME_KEY_PROGRAM,其leaf号码,也就是要写入RAX的号码为0。
除了leaf function,PCONFIG还有一个重要概念,叫做PCONFIG Target,这个Target即platform上的一个功能集,target=0时,表明该target是一个invalid target, target=1时,表明该target是MKTME target。 target的其他值目前未使用。
PCONFIG 的MKTME_KEY_PROGRAM leaf用于软件管理和某个keyID关联的key。即在执行PCONFIG时,给RAX写0,并给RBX写入MKTME_KEY_PROGRAM_STRUCT的地址。
PCONFIG的成功执行,会clear掉RAX中的值(在MKTME leaf中,RAX本身就是0,因此实际执行时看不出效果),并且clear掉ZF,CF,PF,AF,OF,SF。如果PCONFIG的执行出了问题,具体错误原因会写入RAX,同时ZF置1,CF,PF,AF,OF,SF清0.
回到MKTME leaf。该leaf的操作目标时MKTME_KEY_PROGRAM_STRUCT,该结构如下表所示。
所以可以看到该struct占据的地址空间大小为128+64 = 192个字节。实际情况下,该struct需要以256字节对齐。
COMMAND field,即KEYID_CTRL的第一个字节,有4种取值情况。
-
KEYID_SET_KEY_DIRECT(0) 软件尝试直接将自己提供的keyID与自己提供的Key绑定。
-
KEYID_SET_KEY_RANDOM (1) 软件尝试命令硬件产生一个随机的key,与自己提供的KeyID绑定。每次对同一keyID运行PCONFIG,都会导致key的变更。
-
KEYID_CLEAR_KEY (2) 软件尝试将key(自己提供给加密引擎的)与当前KeyID进行解绑,解绑之后,当前KeyID与KeyID=0的加密行为相同,即只是用TME加密/绕过加密。
-
KEYID_NO_ENCRPT(3)软件尝试配置:使用此keyID的memory不用加密。
CRYPTO_ALG field,即KEYID_CTRL的第2,3个字节,允许软件从activated 算法种选择一种加密算法,与当前的keyID结合,即当前keyID对应的key,会由CRYPTO_ALG field选择的算法生成。可以选择的加密算法是由IA32_TME_ACTIVATE MSR 的bit64-bit48 mask出来供该field选择的算法。注意这里不包括用于keyID=0时负责加密的算法,KeyID=0时的加密算法是由IA32_TME_ACTIVATE MSR的bit7:4进行设置的。
Ewan:
结合COMMAND field,我们可以推断出哪种command模式需要软件提供key及选择加密算法,哪种不需要。
KEYID_SET_KEY_DIRECT**: 需要提供key,需要选择加密算法,加密引擎使用软件提供的key及加密算法进行加密。
KEYID_SET_KEY_RANDOM**: 需要提供key的熵(optional),需要选择加密算法,加密引擎随机生成一个key并与熵混合。
KEYID_CLEAR_KEY**: 需要提供key,需要选择加密算法,因为加密引擎需要这两个数据以清除当前keyID与之前软件提供的key的关联。
KEYID_NO_ENCRPT**: 不需要提供key,但需要选择加密算法,这是spec规定。
Q: 这里spec强调软件需要在KEYID_CLEAR_KEY和KEYID_NO_ENCRPT时选择加密算法的意思是,“即使这两种看起来完全不需要加密算法的命令也需要选择加密算法,”还是“只针对这两种任务,需要选择加密算法?”
A: 我觉得是第1种意思。
KEY_FIELD_1
当COMMAND为 KEYID_SET_KEY_DIRECT时,该filed提供软件传入的用于加密的key。
当COMMAND为KEYID_SET_KEY_RANDOM时,该filed提供软件传入的用于与硬件生成的加密key混合的熵。
软件传入的用于加密的key以及熵,可能会导致weak key的问题,但是这得软件自己保证。如果使用AES XTS-128加密算法,该field的高48字节应该在软件执行PCONFIG前清0. 如果使用AES XTS-256加密算法,该field的高32字节应该在软件执行PCONFIG前清0.
KEY_FIELD_2
当COMMAND为 KEYID_SET_KEY_DIRECT时,该field提供用于加密算法的tweak key。
当COMMAND为KEYID_SET_KEY_RANDOM时,该field提供用于与硬件随机生成的加密key混合的熵。
软件传入的用于加密的key以及熵,可能会导致weak key的问题,但是这得软件自己保证。如果使用AES XTS-128加密算法,该field的高48字节应该在软件执行PCONFIG前清0. 如果使用AES XTS-256加密算法,该field的高32字节应该在软件执行PCONFIG前清0.
所有的keyid的加密行为默认情况下使用TME加密,即如果我们在PCONFIG MKTME_TARGET时,只设置了keyid field而没有设置其它field时,该keyid的默认加密行为就是TME加密。软件可以在任何时候修改keyID对应的key及加密方式。对keyID对应的key的修改不会修改TLB,cache,memory pipeline种的key,因此需要软件在执行PCONFIG前执行适当的指令来保证修改key这一行为的有效性。第7章介绍了软件流的一个例子。
下表展示了在执行PCONFIG MKTME_TARGET后,RAX中的取值对应的情况。
PCONFIG 虚拟化
VMM可以对PCONFIG指令进行虚拟化,硬件在VMCS中提供了2个control,用于支持PCONFIG虚拟化。
- PCONFIG_Enable. 该control为1,表示可以在guest中执行PCONFIG指令
- PCONFIG_Exiting. 如果PCONFIG_ENABLE为1,那么guest中执行PCONFIG指令是否发生vmexit取决于该filed是否为1,1表示在guest中执行PCONFIG会vmexit。
PCONFIG的枚举
CPUID[EAX = 7, ECX = 0].EDX[bit18] 指明当前平台是否支持PCONFIG指令。
CPUID[EAX = 0x1B. ].subleaves 返回PCONFIG指令支持的的一些feature信息。
执行CPUID[EAX=0x1B,ECX=n(n>=0)],则:
- EAX的bits11:0 指明该platform是否支持号码为n的PCONFIG Target. 1-支持,0-不支持
- 如果EAX的bits11:0为1,表示支持该PCONFIG Target,那么EAX[11:0]为1,EAX[31:12]为0,EBX,ECX,EDX分别为该PCONFIG Target的Target_ID_1,2,3.
- 如果EAX的bits 11:0为0,表示不支持该PCONFIG Target,比n大的PCONFIG Target均不支持。
软件应该使用CPUID[EAX=0x1B,ECX=n]扫描支持的PCONFIG TARGET,直到遇到第一个不支持的target为止。
PCONFIG的并发性
如果多个core同时执行PCONFIG MKTME_KEY_PROGRAM,即PCONFIG EAX = 0,RBX =MKTME_KEY_PROGRAM_STRUCT的地址,那么只会有一个core成功更新这个key table(即key table在硬件中只有一份),在其它未成功的core上,PCONFIG会返回DEVICE_BUSY(error_code),软件必须retry来更新key table。
硬件使用了一个lock来保证key table同时只能被一个core更新。
PCONFIG具体操作(硬件伪代码)
/* #UD if PCONFIG is not enumerated or CPL>0 */
if (CPUID.7.0:EDX[18] == 0 OR CPL > 0)
#UD;
if (in VMX non-root mode)
{
if (VMCS.PCONFIG_ENABLE == 1)
{
/* PCONFIG Target 大于 62的部分的exit,均由PCONFIG_EXITING[63]控制*/
if ((EAX > 62 AND VMCS.PCONFIG_EXITING[63] ==1) OR
(EAX < 63 AND VMCS.PCONFIG_EXITING[EAX] == 1))
{
Set VMCS.EXIT_REASON = PCONFIG; //No Exit qualification
Deliver VMEXIT;
}
}
else
{ // 在PCONFIG_ENABLE == 0的情况下,在non-root执行PCONFIG
#UD
}
}
/* #GP(0) for an unsupported leaf 现行CPU只支持MKTME这一个subleaf*/
if(EAX != 0)
#GP(0)
if (EAX == 0)
{
/* #GP(0) if TME_ACTIVATE MSR is not locked or does not enable hardware
encryption or multiple keys are not enabled */
if (IA32_TME_ACTIVATE.LOCK != 1 OR IA32_TME_ACTIVATE.ENABLE != 1 OR
IA32_TME_ACTIVATE.MK_TME_KEYID_BITS == 0)
#GP(0)
/* Check MKTME_KEY_PROGRAM_STRUCT is 256B aligned */
if(DS:RBX is not 256B aligned)
#GP(0);
/* Check that MKTME_KEY_PROGRAM_STRUCT is read accessible */
// DS: RBX should be read accessible
/* Copy MKTME_KEY_PROGRAM_STRUCT to a temporary variable */
TMP_KEY_PROGRAM_STRUCT = DS:RBX.*;
/* RSVD field check */
if(TMP_KEY_PROGRAM_STRUCT.RSVD != 0)
#GP(0);
if(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.RSVD !=0)
#GP(0);
/* KEYID_FIELD字段检查 */
if (TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.ENC_ALG[0] == 1)
{ // AES XTS-128
if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1.BYTES[63:16] != 0)
#GP(0);
if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2.BYTES[63:16] != 0)
#GP(0);
}
if (TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.ENC_ALG[2] == 1)
{ // AES XTS-256
if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1.BYTES[63:32] != 0)
#GP(0);
if(TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2.BYTES[63:32] != 0)
#GP(0);
}
/* Check for a valid command 目前来说是4选1*/
if(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.COMMAND is not a valid command)
{
RFLAGS.ZF = 1;
RAX = INVALID_PROG_CMD;
goto EXIT;
}
/* Check that the KEYID being operated upon is a valid KEYID */
if(TMP_KEY_PROGRAM_STRUCT.KEYID >
2^IA32_TME_ACTIVATE.MK_TME_KEYID_BITS – 1 // keyid bit数应该合规
OR TMP_KEY_PROGRAM_STRUCT.KEYID >
IA32_TME_CAPABILITY.MK_TME_MAX_KEYS // keyid本身的数值应该合规
OR TMP_KEY_PROGRAM_STRUCT.KEYID == 0) // 不应该对keyid=0的key属性进行编辑
{
RFLAGS.ZF = 1;
RAX = INVALID_KEYID;
goto EXIT;
}
/* Check that only one algorithm is requested for the KeyID and it is
One of the activated algorithms */
if(NUM_BITS(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.CRYPTO_ALG) != 1 || // 只能选择一个算法
(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.CRYPTO_ALG &
IA32_TME_ACTIVATE. MK_TME_CRYPTO_ALGS == 0)) // 不能选择编外算法
{
RFLAGS.ZF = 1;
RAX = INVALID_CRYPTO_ALG;
goto EXIT;
}
/* Try to acquire exclusive lock */
if (NOT KEY_TABLE_LOCK.ACQUIRE(WRITE))
{
//PCONFIG failure
RFLAGS.ZF = 1;
RAX = DEVICE_BUSY;
goto EXIT;
}
/* Lock is acquired and key table will be updated as per the command
Before this point no changes to the key table are made */
switch(TMP_KEY_PROGRAM_STRUCT.KEYID_CTRL.COMMAND)
{
/* KEYID_SET_KEY_DIRECT下,tweak_key来自key_field2, data_key来自key_field1,
加密模式为“使用keyID_key加密”
*/
case KEYID_SET_KEY_DIRECT:
<<Write
DATA_KEY=TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1,
TWEAK_KEY=TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2,
ENCRYPTION_MODE=ENCRYPT_WITH_KEYID_KEY,
to MKTME Key table at index TMP_KEY_PROGRAM_STRUCT.KEYID
>>
break;
/* KEYID_SET_KEY_RANDOM下,tweak_key和data_key均是由硬件产生的tweak和data与用户提供的
tweak和data混合形成的,混合方法为亦或。加密模式为“使用keyID_key加密”。
*/
case KEYID_SET_KEY_RANDOM:
TMP_RND_DATA_KEY = <<Generate a random key using hardware RNG>>
if (NOT ENOUGH ENTROPY)
{
RFLAGS.ZF = 1;
RAX = ENTROPY_ERROR;
goto EXIT;
}
TMP_RND_TWEAK_KEY = <<Generate a random key using hardware RNG>>
if (NOT ENOUGH ENTROPY)
{
RFLAGS.ZF = 1;
RAX = ENTROPY_ERROR;
goto EXIT;
}
/* Mix user supplied entropy to the data key and tweak key */
TMP_RND_DATA_KEY = TMP_RND_DATA_KEY XOR
TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_1.BYTES[15:0];
TMP_RND_TWEAK_KEY = TMP_RND_TWEAK_KEY XOR
TMP_KEY_PROGRAM_STRUCT.KEY_FIELD_2.BYTES[15:0];
<<Write
DATA_KEY=TMP_RND_DATA_KEY,
TWEAK_KEY=TMP_RND_TWEAK_KEY,
ENCRYPTION_MODE=ENCRYPT_WITH_KEYID_KEY,
to MKTME_KEY_TABLE at index TMP_KEY_PROGRAM_STRUCT.KEYID
>>
break;
/* 使用TME加密,至于是加密还是绕过,取决于TME的设置 */
case KEYID_CLEAR_KEY:
<<Write
DATA_KEY=’0,
TWEAK_KEY=’0,
ENCRYPTION_MODE = ENCRYPT_WITH_TME_KEY_OR_BYPASS,
to MKTME_KEY_TABLE at index TMP_KEY_PROGRAM_STRUCT.KEYID
>>
break;
/* 不加密 */
case KEYID_NO_ENCRYPT:
<<Write
DATA_KEY=’0,
TWEAK_KEY=’0,
ENCRYPTION_MODE=NO_ENCRYPTION,
to MKTME_KEY_TABLE at index TMP_KEY_PROGRAM_STRUCT.KEYID
>>
break;
}
RAX = 0;
RFLAGS.ZF = 0;
//Release Lock
KEY_TABLE_LOCK(RELEASE);
EXIT:
RFLAGS.CF=0;
RFLAGS.PF=0;
RFLAGS.AF=0;
RFLAGS.OF=0;
RFLAGS.SF=0;
}
end_of_flow
Spec的6.2.6,7,8,9,10,11,12分别列举了:
- PCONFIG 指令会影响的flags位
- 对PCONFIG指令加不同前缀造成的影响
- 保护模式下,实模式下,virtual-8086模式下,兼容模式下,64bit模式下,PCONFIG指令可能会产生的异常以及对应的原因。
软件生命周期: 用keyID管理pages.
KeyID是物理地址的组成部分,这意味着keyID不仅存在于页表中,也存在于TLB,cache中。因此,软件必须采取适当的措施来保护软件的正确和安全。
限制和cache管理
如果2/多个keyID map的是同一块物理页,硬件不会保证这两个keyID映射到的页的cache一致性。软件必须精心保证这两块mapped的页的cache一致性,如果其中一个页的内容发生变化,另一个页的内容理应也发生变化。
如果两个物理地址,除了keyID不一样之外其它都一样,即指向同一个物理地址,但是CPU会把这两个地址当作不一样的地址,这种情况下,软件必须精心设计以满足安全性和准确性。MKTME没有对cache和TLB的行为做任何修改,只是单纯的多了一个keyID bits。
Q: 如何将keyID map到page?
A: mmap一个page,并更新该page对应的页表项内容,加上keyid。
对映射到同一物理地址的keyid mappings的软件指导
- 避免将多个keyID映射到同一个物理地址
- 如果软件必须将多个keyID映射到同一个物理地址,应该将那些页(keyid page)标记为read-only,只有一个带keyid的物理地址能读真正的物理页上的内容。
- 如果这些所有的页(keyid page)必须为可读写的,那么软件必须保证所有每次只有一个keyid page发起的写操作(用锁实现。)
增加页:将Keyid和page关联
- 如果软件暂时未拥有一个free key,就用PCONFIG新建一个keyid及对应的key。
- 如果某个物理page尚未被VMM拥有,那就通过更新页表(包含keyid),将该page映射到VMM的地址空间。
- 确保第一步完成(可能会有疑问说你第2步都已经用了这个keyid了,却在这里检查第一步是不是正确完成?我的想法是,由于OS并不会在使用前真正分配物理页,所以在这里检查没有问题。)
- 使用带keyid的物理地址访问该物理页,写全0,以避免keyID之间的数据泄露
- 使该页对一个新的VM可用,只需要通过EPT将该页映射到新的VM即可,EPT页表结构中的物理地址应该包含keyid。
注意PCONFIG应该在每个pakage的一个logical processor上进行。 本质上是一个memory controller,一个PCONFIG。
删除页: 将keyid与page解关联
- 通过更新EPT页表,保证该物理页无法被VM访问。
- invalid所有page mappings(包括使用INVEPT invalid所有EPT cache,以及对IOMMU对应的页表的cache进行invalid)
- 如果该页没有被映射到VMM,就通过更新页表结构来使该页映射到VMM地址空间。(这一步可能会有疑问说,你这个物理页都已经被VM用了好久了,怎么可能没有映射到VMM,但实际情况下,存在刚刚将keyid和page关联,就要将keyid与page解关联,这种时候尚未建立页表映射。)
- OS/VMM 清掉 dirty cache lines(使用旧的keyid)。如果该页在使用期间一直被EPT PML监控着,或一直被VMM监控该页的accessed/dirty flags,那么就可以省略这一步。
- 该页可以用于与新的keyid关联了。
OS/VMM 访问Guest Memory
出于模拟MMIO便利的需求,VMM可能需要访问Guest Memory,这时VMM可以通过使用guest的keyid设置VMM自己的页表内容,来访问guest memory。
OS/VMM可以使用keyid=0来建立在VMM和Guest之间共享的memory,对于direct io,OS/VMM应该使IOMMU中的页表中包含的keyid和EPT页表中包含的keyid一致,这样Guest可以直接访问该memory,而device也可以直接访问该memory。
所以IOMMU-MKTME的精髓在EPT和IOMMU table中对于访问同一块memory使用相同的keyid。