Intel MKTME 在Linux Kernel中的初始化和Key编辑API

Intel MKTME 在Linux Kernel中的初始化和Key编辑API

PCONFIG指令枚举 & MKTME Target枚举

#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */

检查cpu是否包含某feature,用的是boot_cpu_has([feature_encode])

#define boot_cpu_has(bit) cpu_has(&boot_cpu_data, bit) #define cpu_has(c, bit) \ (__builtin_constant_p(bit) && REQUIRED_MASK_BIT_SET(bit) ? 1 : \ test_cpu_cap(c, bit))

__builtin_constant_p 是编译器gcc内置函数,用于判断一个值是否为编译时常量,如果是常数,函数返回1 ,否则返回0。由于编译时,cpu_has传入的bit一定是常量,类似 X86_FEATURE_PCONFIG代表的18*32+18,因此这里的__builtin_constant_p返回true。

#define REQUIRED_MASK_BIT_SET(feature_bit) \ ( CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 0, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 1, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 2, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 3, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 4, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 5, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 6, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 7, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 8, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 9, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 10, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 11, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 12, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 13, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 14, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 15, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 16, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 17, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 18, feature_bit) || \ CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 19, feature_bit) || \ REQUIRED_MASK_CHECK || \ BUILD_BUG_ON_ZERO(NCAPINTS != 20)) #define CHECK_BIT_IN_MASK_WORD(maskname, word, bit) \ (((bit)>>5)==(word) && (1UL<<((bit)&31) & maskname##word ))

所以针对CHECK_BIT_IN_MASK_WORD(REQUIRED_MASK, 0, feature_bit),就可以转化为:

(((feature_bit)>>5)==(word) && (1UL<<((feature_bit)&31) & maskname##word ))

Feature_bit即上面的类似于X86_FEATURE_PCONFIG的数,例如1832+18,其中1832,是为了保证这个feature_bit的高于5个bit的位置为word数组中的index,即18 << 5,低5bit为这个feature在这个word中的bit_index,所以上面的这一堆CHECK_BIT_IN_MASK_WORD中,只会有一个宏的(feature_bit)>>5)==(word)为真,(feature_bit & 31)获得该feature bit的低5位,假设低5位为5'b 10010,即十进制18,那么1<<18,正好能与预设的REQUIRED_MASK18 做与运算,获取在该mask中,bit18是否设置为1的结果。

综上, REQUIRED_MASK_BIT_SET(X86_FEATURE_PCONFIG),就是在确认:

在REQUIRED_MASK18中,bit18是否为1,而在arch/x86/include/asm中,REQUIRED_MASK被设置为0,所以REQUIRED_MASK_BIT_SET(X86_FEATURE_PCONFIG)返回0.

kernel引入REQUIRED_MASK的目的,是为了设置优先级,即对于某些基础feature,如FPU,PSE,MSR,PAE等,会首先查询REQUIRED MASK,这样设计,即,REQUIRED_MASKn的优先级高于cpu->capabilities数组。kernel的注释中说明,我们必须构造一个最小feature集,以支持非常早期(early not old)的kernel功能。

回到我们的feature-X86_FEATURE_PCONFIG,这个feature会导致 REQUIRED_MASK_BIT_SET返回0,所以需要查看test_cpu_cap(c,bit).即test_cpu_cap(&boot_cpu_data,X86_FEATURE_PCONFIG).

#define test_cpu_cap(c, bit) \ test_bit(bit, (unsigned long *)((c)->x86_capability))

即,返回boot_cpu_data->x86_capability中,该bit是否为1。那么boot_cpu_data->x86_capability是何时赋值的。

#define NCAPINTS 20 /* N 32-bit words worth of info */ #define NBUGINTS 1 /* N 32-bit bug flags */ struct cpuinfo_x86 { ... union { __u32 x86_capability[NCAPINTS + NBUGINTS]; unsigned long x86_capability_alignment; }; ... }

首先看到x86_capability[21]是一个基础元素为32bit unsigned int类型,包含21个元素的数组。

enum cpuid_leafs { CPUID_1_EDX = 0, CPUID_8000_0001_EDX, CPUID_8086_0001_EDX, CPUID_LNX_1, CPUID_1_ECX, CPUID_C000_0001_EDX, CPUID_8000_0001_ECX, CPUID_LNX_2, CPUID_LNX_3, CPUID_7_0_EBX, CPUID_D_1_EAX, CPUID_LNX_4, CPUID_7_1_EAX, CPUID_8000_0008_EBX, CPUID_6_EAX, CPUID_8000_000A_EDX, CPUID_7_ECX, CPUID_8000_0007_EBX, CPUID_7_EDX, CPUID_8000_001F_EAX, };

kernel定义了一个枚举结构,其中一共19个元素,分别代表CPUID的不同leaf。

PCONFIG的枚举是在CPUID[EAX=7,ECX=0].EDX[bit18]枚举的,因此查询CPU是否支持PCONFIG指令,需要查询x86_capability[CPUID_7_EDX],即x86_capability[18]的bit18是否为1.

在get_cpu_cap()中,会根据cpuid的取值,对c->x86_capability[CPUID_7_EDX]进行赋值:

// arch/x86/kernel/cpu/common.c void get_cpu_cap(struct cpuinfo_x86 *c) { u32 eax, ebx, ecx, edx; ... /* Additional Intel-defined flags: level 0x00000007 */ if (c->cpuid_level >= 0x00000007) { cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx); c->x86_capability[CPUID_7_0_EBX] = ebx; c->x86_capability[CPUID_7_ECX] = ecx; c->x86_capability[CPUID_7_EDX] = edx; /* Check valid sub-leaf index before accessing it */ if (eax >= 1) { cpuid_count(0x00000007, 1, &eax, &ebx, &ecx, &edx); c->x86_capability[CPUID_7_1_EAX] = eax; } } ... }

在intel_pconfig_init()时,会检查是否支持PCONFIG指令,如果不支持,直接返回0.

// arch/x86/kernel/cpu/intel_pconfig static int __init intel_pconfig_init(void) { if (!boot_cpu_has(X86_FEATURE_PCONFIG)) return 0; ... }

如果支持PCONFIG,那么就需要对PCONFIG可以操作的target进行枚举。

static int __init intel_pconfig_init(void) { int subleaf; if (!boot_cpu_has(X86_FEATURE_PCONFIG)) return 0; /* * Scan subleafs of PCONFIG CPUID leaf. * Subleafs of the same type need not to be consecutive. * Stop on the first invalid subleaf type. All subleafs after the first * invalid are invalid too. */ for (subleaf = 0; subleaf < INT_MAX; subleaf++) { struct cpuid_regs regs; cpuid_count(PCONFIG_CPUID, subleaf, &regs.eax, &regs.ebx, &regs.ecx, &regs.edx); switch (regs.eax & PCONFIG_CPUID_SUBLEAF_TYPE_MASK) { case PCONFIG_CPUID_SUBLEAF_INVALID: /* Stop on the first invalid subleaf */ goto out; case PCONFIG_CPUID_SUBLEAF_TYPE_TARGETID: /* Mark supported PCONFIG targets */ if (regs.ebx < 64) targets_supported |= (1ULL << regs.ebx); if (regs.ecx < 64) targets_supported |= (1ULL << regs.ecx); if (regs.edx < 64) targets_supported |= (1ULL << regs.edx); break; default: /* Unknown CPUID.PCONFIG subleaf: ignore */ break; } } out: return 0; }

CPUID[EAX=1BH,ECX=n].EAX会返回subleaf-type

0表示无效subleaf-type,并且EBX=ECX=EDX=0.

非0表示有效subleaf-type,该subleaf-type的其它相关信息会记录在EBX,ECX,EDX中。

现在只支持subleaf-type为1的subleaf,即TARGETID-subleaf-type, EBX,ECX,EDX中记录的是TARGETID数值。

即如果CPUID[EAX=1BH,ECX=n].EAX返回1,那么EBX ECX EDX中记录TARGETID数值。

任何TARGETID不为0均为有效TARGETID,目前只支持TARGETID=1,即MKMET TARGET。

而PCONFIG Leaf,也就是运行PCONFIG时,EAX输入的值与这个TARGET没有关系。

所以,针对上述代码,subleaf=0,CPUID[EAX=1BH,ECX=0].EAX会返回subleaf-type=1,即TARGETID-subleaf-type, EBX,ECX,EDX中记录的是TARGETID数值。而EBX,ECX,EDX中均会返回1,表示支持TARGETID=1.

另外spec明确规定要使用EAX=0来用PCONFIG编辑MKTME,因此之后编辑MKTME时,需要使用PCONFIG EAX=0.

至此,PCONFIG的枚举代码梳理完成。

MKTME key的编辑API

/* Hardware requires the structure to be 256 byte aligned. Otherwise #GP(0). */ struct mktme_key_program { u16 keyid; u32 keyid_ctrl; u8 __rsvd[58]; u8 key_field_1[64]; u8 key_field_2[64]; } __packed __aligned(256); static inline int mktme_key_program(struct mktme_key_program *key_program) { unsigned long rax = MKTME_KEY_PROGRAM; if (!pconfig_target_supported(MKTME_TARGET)) return -ENXIO; asm volatile(PCONFIG : "=a" (rax), "=b" (key_program) : "0" (rax), "1" (key_program) : "memory", "cc"); return rax; }

kernel只是简单提供了一个向key table写入key entry的API: mktme_key_program.


__EOF__

本文作者EwanHai
本文链接https://www.cnblogs.com/haiyonghao/p/16340911.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   EwanHai  阅读(353)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示