ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux 内核机密计算测量寄存器(TSM MR)框架解析:从 `include/linux/tsm-mr.h` 到 sysfs 暴露

Linux 内核机密计算测量寄存器(TSM MR)框架解析:从 `include/linux/tsm-mr.h` 到 sysfs 暴露 Linux 内核机密计算测量寄存器TSM MR框架解析从include/linux/tsm-mr.h到 sysfs 暴露【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux导读机密计算Confidential Computing, CC场景下受信任执行环境TEE如 Intel TDX、AMD SEV 等依赖**测量寄存器Measurement Register, MR**记录初始镜像、运行时事件与配置信息作为远程证明Attestation的可信根。Linux 内核在drivers/virt/coco下提供了统一的 TSMTrusted Security Module测量寄存器抽象层把各架构私有的 MR 语义收敛为一套通用的数据结构与 sysfs 属性接口。本文以官方文档 Documentation/driver-api/coco/measurement-registers.rst 为核心骨架结合 include/linux/tsm-mr.h 与 drivers/virt/coco/guest/tsm-mr.c 的源码实现以及 TDX Guest 驱动的真实消费示例完整讲解 MR 的数据结构、标志位语义、读写回调、sysfs 属性组生成机制与缓存一致性算法。读完本文你将能够理解并实现一个基于 TSM MR 框架的 CC 测量寄存器驱动并掌握其 sysfs 暴露的完整规则。一、文档定位Coco 驱动 API 文档的入口在 Linux 内核文档树中Documentation/driver-api/coco/目录收录了 Confidential Computing 相关的驱动 API 文档。其中index.rst 是总入口标题为 Confidential Computing通过toctree收录子页面measurement-registers.rst 是唯一的正文页面专门讲解测量寄存器Measurement Registers。值得注意该 RST 文档本身并不直接书写 API 细节而是通过两处kernel-doc指令把头文件与实现文件中的注释直接提取为文档.. kernel-doc:: include/linux/tsm-mr.h :internal: .. kernel-doc:: drivers/virt/coco/guest/tsm-mr.c :export:这意味着一份活的 API 文档include/linux/tsm-mr.h中的内部结构注释与drivers/virt/coco/guest/tsm-mr.c中导出的函数注释共同构成了该文档的全部内容。因此在阅读本文时这两份文件就是最权威的第一手材料。二、背景为什么需要统一的测量寄存器抽象不同 CC 架构的测量设施差异巨大Intel TDX拥有 4 个运行时扩展寄存器 RTMR0~RTMR3SHA-384 摘要、初始测量寄存器 MRTD以及 MRCONFIGID、MROWNER、MROWNERCONFIG 等原始数据寄存器AMD SEV/SNP、ARM CCA等架构各有自己的测量语义本文仓库快照中可见drivers/virt/coco/下同时存在sev-guest/、arm-cca-guest/、pkvm-guest/等目录。若每个驱动各自实现 sysfs 接口就会出现命名、权限、语义各不相同、用户态无法统一消费的局面。TSM MR 框架的目标是用一套通用的测量寄存器描述 读写回调抽象统一生成 sysfs 属性组让上层证明工具、容器运行时、机密 VM 管理面以一致的路径和格式读取/扩展测量值。从源码结构看该框架由三部分协作见 drivers/virt/coco/include/linux/tsm-mr.h定义 MR 描述结构与抽象操作集drivers/virt/coco/guest/tsm-mr.c提供属性组创建/释放的通用实现各架构 Guest 驱动如 drivers/virt/coco/tdx-guest/tdx-guest.c填充 MR 定义并注册属性组。三、核心数据结构struct tsm_measurement_register在 include/linux/tsm-mr.h 中定义struct tsm_measurement_register { const char *mr_name; void *mr_value; u32 mr_size; u32 mr_flags; enum hash_algo mr_hash; };各字段语义如下字段类型含义mr_nameconst char *MR 的名称将参与 sysfs 属性名构造必须在结构体使用期间保持有效mr_valuevoid *指向存放 MR 当前值的缓冲区同样要求在使用期间保持有效mr_sizeu32MR 值缓冲区大小通常等于mr_hash对应算法的摘要长度digest sizemr_flagsu32标志位按位或组合见下文mr_hashenum hash_algo可选的哈希算法标识符定义于include/uapi/linux/hash_info.h当设置了TSM_MR_F_NOHASH时该字段被忽略3.1 标志位语义TSM_MR_F_*include/linux/tsm-mr.h 中定义了四个基本标志与一个组合宏#define TSM_MR_F_NOHASH 1 #define TSM_MR_F_WRITABLE 2 #define TSM_MR_F_READABLE 4 #define TSM_MR_F_LIVE 8 #define TSM_MR_F_RTMR (TSM_MR_F_LIVE | TSM_MR_F_WRITABLE)标志值语义TSM_MR_F_READABLE4该 MR 对应的 sysfs 属性可读。源码中会为该属性追加 0444 权限位TSM_MR_F_WRITABLE2该 MR 对应的 sysfs 属性可写。写语义通常是扩展extend该 MR但具体语义随架构和 MR 类型而变TSM_MR_F_LIVE8该 MR 的值可能与最后一次写入的值不同因此读取时必须从底层 CC 硬件/固件重新读回不能直接使用缓存TSM_MR_F_NOHASH1该 MR没有关联的哈希算法。设置后mr_hash字段被忽略sysfs 属性名不再拼接哈希后缀TSM_MR_F_RTMR8 | 2便捷组合宏等价于既是 LIVE 又是 WRITABLE正好刻画运行时测量寄存器RTMR可被扩展、值会持续变化的双重特性3.2 便捷初始化宏TSM_MR_()为降低驱动书写负担头文件还提供了初始化宏include/linux/tsm-mr.h#define TSM_MR_(mr, hash) \ .mr_name #mr, .mr_size hash##_DIGEST_SIZE, \ .mr_hash HASH_ALGO_##hash, .mr_flags TSM_MR_F_READABLE它一次性完成名称字符串化、以hash##_DIGEST_SIZE设定大小、以HASH_ALGO_##hash设定算法、默认打上TSM_MR_F_READABLE标志。TDX 驱动在 drivers/virt/coco/tdx-guest/tdx-guest.c 中的用法可以很好地说明该宏与标志的组合#define TDX_MR_(r) .mr_value (void *)TDREPORT_##r, TSM_MR_(r, SHA384) static struct tsm_measurement_register tdx_mrs[] { { TDX_MR_(rtmr0) | TSM_MR_F_RTMR }, { TDX_MR_(rtmr1) | TSM_MR_F_RTMR }, { TDX_MR_(rtmr2) | TSM_MR_F_RTMR }, { TDX_MR_(rtmr3) | TSM_MR_F_RTMR }, { TDX_MR_(mrtd) }, { TDX_MR_(mrconfigid) | TSM_MR_F_NOHASH }, { TDX_MR_(mrowner) | TSM_MR_F_NOHASH }, { TDX_MR_(mrownerconfig) | TSM_MR_F_NOHASH }, };可以直观看到三种典型 MR 形态RTMR0~3SHA-384 哈希TSM_MR_F_RTMRLIVE WRITABLE运行时不断被 extend读取需回读硬件MRTDSHA-384 哈希只有TSM_MR_F_READABLE是初始度量不可写MRCONFIGID / MROWNER / MROWNERCONFIG无哈希TSM_MR_F_NOHASH本质是原始配置数据而非摘要。四、抽象操作集struct tsm_measurements单个 MR 只是数据 标志真正驱动读写的是struct tsm_measurementsinclude/linux/tsm-mr.hstruct tsm_measurements { const struct tsm_measurement_register *mrs; size_t nr_mrs; int (*refresh)(const struct tsm_measurements *tm); int (*write)(const struct tsm_measurements *tm, const struct tsm_measurement_register *mr, const u8 *data); };mrs/nr_mrsMR 定义数组及其元素个数是架构私有的测量设施清单refresh一次性加载/同步所有 MR到内核缓存。它只在读取带TSM_MR_F_LIVE标志的 MR 且缓存过期时被调用write写指定 MR。参数data携带要写入的字节长度等于mr-mr_size。典型语义是把输入缓冲区extend扩展进该 MR。两个回调的成功约定统一为成功返回 0失败返回适当的负错误码。TDX 驱动给出了简洁的示例实现drivers/virt/coco/tdx-guest/tdx-guest.cstatic int tdx_mr_refresh(const struct tsm_measurements *tm) { return tdx_do_report(KERNEL_SOCKPTR(NULL), KERNEL_SOCKPTR(NULL)); } static int tdx_mr_extend(const struct tsm_measurements *tm, const struct tsm_measurement_register *mr, const u8 *data) { return tdx_do_extend(mr - tm-mrs, data); } static struct tsm_measurements tdx_measurements { .mrs tdx_mrs, .nr_mrs ARRAY_SIZE(tdx_mrs), .refresh tdx_mr_refresh, .write tdx_mr_extend, };注意tdx_mr_extend利用mr - tm-mrs的指针差计算出 MR 在数组中的下标并把它当作 RTMR 索引传给tdx_do_extend()。后者tdx-guest.c展示了 TDX 扩展操作的两个关键工程细节TDX 要求 extend 缓冲区64 字节对齐因此借用tdx_report_buf TDREPORT_reportdata作为对齐缓冲扩展数据长度固定为SHA384_DIGEST_SIZE即 48 字节与 RTMR 的 SHA-384 语义一致通过tdx_mcall_extend_rtmr(mr_ind, extend_buf)下发 TDCALL并用互斥锁保护与 report 路径互斥。五、sysfs 属性组生成tsm_mr_create_attribute_group()这是框架的核心导出函数drivers/virt/coco/guest/tsm-mr.cconst struct attribute_group * tsm_mr_create_attribute_group(const struct tsm_measurements *tm);5.1 入参校验与错误码函数首先对 MR 定义做严格校验任一不满足即返回-EINVALtm或tm-mrs为空带TSM_MR_F_LIVE的 MR 未提供refresh回调带TSM_MR_F_WRITABLE的 MR 未提供write回调mr_name为空非TSM_MR_F_NOHASH的 MR 其mr_hash HASH_ALGO__LAST哈希算法非法生成的属性名彼此重复重复 MR 定义。此外内核分配失败时返回-ENOMEM。调用方可通过IS_ERR()检查结果。5.2 属性命名规则MRNAME:HASH框架为每个 MR 生成一个struct bin_attribute命名遵循统一格式见 tsm-mr.c若设置了TSM_MR_F_NOHASH属性名直接就是mr_name否则属性名为MRNAME:HASH其中 HASH 取自hash_algo_name[mr-mr_hash]即哈希算法的规范名称。以 TDX 为例最终暴露的属性名即rtmr0:SHA384、rtmr1:SHA384、rtmr2:SHA384、rtmr3:SHA384、mrtd:SHA384、mrconfigid、mrowner、mrownerconfig。这种名称算法的命名方式让用户态仅凭属性名即可推断 MR 的哈希类型。5.3 权限与读写回调挂接框架根据标志位设置属性权限tsm-mr.cTSM_MR_F_READABLEattr.mode | 0444挂接读回调tm_digest_readTSM_MR_F_WRITABLEattr.mode | 0200挂接写回调tm_digest_writebap-size mr_size限定读写长度属性组名固定为measurementsctx-agrp.name measurements。内存管理上有一个值得借鉴的优化属性指针数组与 MR 名称字符串被合并到一次kzalloc分配中attrs之后紧跟着所有名称字符串因此释放时只需kfree(attr_grp-bin_attrs)与kfree(container_of(...))两次释放见 tsm_mr_free_attribute_group无需逐个释放名称。5.4 生命周期约束tsm_mr_create_attribute_group()返回的属性组会引用tm及其成员因此调用方在调用tsm_mr_free_attribute_group()之前不得释放tm。函数可在进程上下文调用可能因内存分配而睡眠。六、读路径与缓存一致性tm_digest_read()由于refresh回调TDCALL/固件调用可能比较昂贵实现引入了读缓存 失效标记机制tsm-mr.c。框架内部维护struct tm_contexttsm-mr.cstruct tm_context { struct rw_semaphore rwsem; /* 保护 MR 缓存的并发访问 */ struct attribute_group agrp; /* 生成的属性组 */ const struct tsm_measurements *tm; bool in_sync; /* MR 缓存是否最新 */ struct bin_attribute mrs[]; /* 每个 MR 一个属性 */ };读路径tm_digest_read()的核心逻辑down_read_interruptible()获取读锁通过attr - ctx-mrs定位当前 MR判断是否为LIVE MR且缓存已失效mr_flags TSM_MR_F_LIVE !ctx-in_sync先释放读锁升级为down_write_killable()写锁二次检查!ctx-in_sync防止竞态重复刷新调用ctx-tm-refresh(ctx-tm)一次性刷新所有 LIVE MR刷新成功则置in_sync true随后downgrade_write()降级回读锁继续memcpy(buffer, mr-mr_value off, count)拷贝值触发trace_tsm_mr_read跟踪事件。两个设计要点值得注意in_sync是全局标记而非按 MR 记录注释中解释这是因为大多数甚至所有架构都支持一次性读取全部 MR全局标记足够且更简单只有 LIVE MR 会触发 refresh非 LIVE 的 MR 值假定在最近一次write()后保持不变读它们不会触发昂贵的回读。七、写路径与 extend 语义tm_digest_write()写回调tm_digest_write()tsm-mr.c逻辑同样清晰拒绝部分写if (off ! 0 || count ! attr-size) return -EINVAL;——只接受对完整 MR 缓冲区的一次性写入down_write_killable()获取写锁调用ctx-tm-write(ctx-tm, mr, buffer)执行架构相关的写入通常是 extend写入成功后置ctx-in_sync false缓存失效下次读取 LIVE MR 将强制 refresh并触发trace_tsm_mr_write跟踪事件。从代码注释可以确认Most (if not all) writable MRs support the extension semantics即写操作语义主要是把输入缓冲区扩展extend进 MR——这正是 TPM 风格的 PCR/RTMR 扩展模型在机密计算场景的延续。八、完整消费示例TDX Guest 驱动drivers/virt/coco/tdx-guest/tdx-guest.c 是当前仓库中唯一消费该框架的驱动其集成流程可作为实现参考初始化tdx_mr_init()L125-L151分配TDX_REPORT_LEN大小的报告缓冲区并预先执行一次tdx_mr_refresh()让 MR 定义中的mr_value先指向报告缓冲区内对应字段由于mr_value初始只是相对偏移(void *)TDREPORT_##r这里把缓冲区基址累加进mr_value使其成为有效指针调用tsm_mr_create_attribute_group(tdx_measurements)生成属性组成功后才把缓冲区交给框架持有挂接tdx_guest_init()L405-L441tdx_attr_groups[0] tdx_mr_init()随 misc 设备注册对外暴露任何失败路径都会调用tdx_mr_deinit()回滚卸载tdx_mr_deinit()L153-L157依次tsm_mr_free_attribute_group()与kfree(tdx_report_buf)。值得注意tdx_mr_refresh()的实现是重新执行一次tdx_do_report()TDX 的 TDREPORT 结构中本身就内嵌了所有测量值RTMR0-3、MRTD、MRCONFIGID 等所以一次报告获取即可刷新全部 MR——这正是refresh回调一次性加载全部 MR语义在 TDX 上的自然映射。九、可观测性tsm_mr 跟踪事件框架在 include/trace/events/tsm_mr.h 中定义了三类 tracepoint供调试与审计事件触发点输出内容tsm_mr_readtm_digest_read()成功读取后MR 名、哈希算法NOHASH 时显示data、完整的摘要十六进制值tsm_mr_refreshrefresh 回调执行后MR 名、成功/失败及返回码tsm_mr_write写操作成功后MR 名、哈希算法、被写入的数据使用时启用对应跟踪点即可例如通过 tracefs 观察tsm_mr_read即可看到每次 sysfs 读取的 MR 名与测量值为远程证明链路的调试提供了内核级证据。十、Kconfig 与构建集成框架通过 drivers/virt/coco/guest/Kconfig 暴露配置config TSM_MEASUREMENTS select TSM_GUEST select CRYPTO_HASH_INFO boolTSM_MEASUREMENTS是bool无法编译为模块依赖CRYPTO_HASH_INFO提供hash_algo_name与摘要大小宏并隐式选择TSM_GUEST构建规则见 drivers/virt/coco/guest/Makefileobj-$(CONFIG_TSM_MEASUREMENTS) tsm-mr.o。需要说明的是本文仓库快照中tsm-mr.c还通过#define CREATE_TRACE_POINTS直接内建了 tracepoint因此其 trace 事件随内核编译而生效无需独立模块加载。十一、给驱动开发者的实践建议综合文档与源码实现一个新的 CC 测量寄存器驱动例如 SEV-SNP 或 ARM CCA 场景时建议遵循以下步骤枚举测量设施明确硬件/固件提供了哪些 MR各自的大小、哈希算法、读写能力据此构造struct tsm_measurement_register数组正确设置标志位会变化的测量值必须加TSM_MR_F_LIVE否则读到过期缓存可扩展的加TSM_MR_F_WRITABLE纯数据寄存器加TSM_MR_F_NOHASH并忽略mr_hash实现两个回调refresh一次性回读全部 LIVE MRwrite按 MR 下标执行架构扩展原语二者都返回 0 或负错误码管理缓冲区生命周期mr_name/mr_value指向的内存在tsm_mr_free_attribute_group()之前必须保持有效建议在驱动的 init/exit 路径中与属性组配对申请与释放挂接属性组把返回的属性组并入驱动设备misc 设备等的attribute_group列表即可在 sysfs 的measurements子目录下获得统一的MRNAME:HASH属性集合。结语TSM 测量寄存器框架以极小的 API 面两个结构体、两个导出函数统一了机密计算领域差异巨大的测量设施上层看到的是命名规范、权限一致、带缓存一致性保证的 sysfs 属性下层各架构只需提供定义 refresh write三件事。对内核开发者而言include/linux/tsm-mr.h 是契约drivers/virt/coco/guest/tsm-mr.c 是通用实现而 drivers/virt/coco/tdx-guest/tdx-guest.c 则是一份可直接参照的完整范例——三者结合即可快速为新的 CC 架构接入标准化的测量寄存器接口。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表