资讯动态

Linux CoCo 测量寄存器(Measurement Registers)API:用 tsm-mr 为 TVM 度量设施构建 sysfs 接口

发布时间:2026/9/17 21:34:03 来源:尧图企业网站定制
Linux CoCo 测量寄存器Measurement RegistersAPI用 tsm-mr 为 TVM 度量设施构建 sysfs 接口【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linuxLinux 内核在机密计算Confidential ComputingCoCoGuest 侧提供了通用的测量寄存器MR框架用于把 TVMTrusted Virtual Machine硬件/固件支持的度量能力统一暴露为 sysfs 二进制属性。本文基于内核文档 Measurement Registers 及其引用的两份源码完整解析该 API 的数据模型、回调约定、sysfs 属性生成规则以及底层读/写路径的缓存同步机制帮助读者掌握 CC Guest 驱动接入度量寄存器设施所需的内核能力。文档定位CoCo 文档树中的 MR 规范Documentation/driver-api/coco/目录下当前只收录了这一篇测量寄存器文档见 index.rst。文档本身非常简短其全部内容通过kernel-doc指令直接从两份源码文件中提取include/linux/tsm-mr.h以:internal:方式引入即只渲染结构体/宏/函数原型等内部 APIdrivers/virt/coco/guest/tsm-mr.c以:export:方式引入即只渲染导出的EXPORT_SYMBOL_GPL函数。也就是说阅读这两份源文件中的 kernel-doc 注释就等价于阅读了该文档的全部正文。这个组织方式也提示了该 API 的设计取向接口契约写在头文件通用实现放在 Guest 侧公共代码中架构相关部分由各自的 CC Guest 驱动提供。数据模型struct tsm_measurement_register 描述一个测量寄存器框架的核心抽象是单个测量寄存器的描述结构体定义于 include/linux/tsm-mr.hstruct tsm_measurement_register { const char *mr_name; void *mr_value; u32 mr_size; u32 mr_flags; enum hash_algo mr_hash; };各字段语义如下字段含义mr_nameMR 的名称。与mr_value一样必须在该结构体不再被使用之前保持有效生命周期约束。mr_value指向当前 MR 值缓冲区的指针即内核侧的 MR 缓存。mr_sizeMR 的字节大小通常就是mr_hash对应摘要的长度digest size。mr_flags一组标志位的按位或详见下文标志表。mr_hash可选的哈希算法标识取值来自include/uapi/linux/hash_info.h中定义的enum hash_algo。头文件注释明确指出CC Guest 驱动需要把若干struct tsm_measurement_register组织成数组包裹进struct tsm_measurements以此向框架描述底层 CC 硬件支持的度量设施。标志位Flags标志位定义在 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_NOHASH1该 MR不关联哈希算法此时mr_hash字段被忽略。TSM_MR_F_WRITABLE2对应的 sysfs 属性可写。语义上通常是“extend”把输入数据扩展进 MR但具体语义可能因架构和 MR 不同而变化。TSM_MR_F_READABLE4对应的 sysfs 属性可读。TSM_MR_F_LIVE8该 MR 的当前值可能与最后一次写入的值不同例如被固件/其他 CPU 持续扩展因此必须回读底层 CC 硬件/固件刷新。TSM_MR_F_RTMRLIVE \| WRITABLE组合标志既是 LIVE 又可写即一个实时度量寄存器RTMR运行时度量寄存器的常用特征组合。TSM_MR_() 初始化宏对于最常见的“只读 带哈希”的 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它做了三件事字符串化寄存器名mr用hash##_DIGEST_SIZE展开得到摘要长度作为mr_size例如TSM_MR_(rtmr, sha384)会得到mr_size sha384_DIGEST_SIZE把mr_hash设为HASH_ALGO_##hash并默认只置TSM_MR_F_READABLE。带 LIVE/可写/NOHASH 的寄存器则需要在该宏基础上自行补全相应标志。驱动注册入口struct tsm_measurements 与两个回调架构侧驱动与框架的契约是struct tsm_measurements定义于 include/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); };成员说明mrsMR 定义数组。nr_mrs数组元素个数。refresh从 TVM 硬件/固件批量加载/同步所有 MR到内核缓存的回调。当某个带TSM_MR_F_LIVE的 MR 被读取且缓存过期时框架会调用它回调实现必须重新加载所有TSM_MR_F_LIVE的 MR。其唯一参数tm是指回struct tsm_measurements自身的指针。write写入指定 MR 的回调除tm外还接收两个参数mr指向正在写入的 MR即tm-mrs中的元素和data待写入的字节流长度为mr-mr_size。典型语义是把输入缓冲区扩展extend进该 MR。两个回调的返回值约定一致成功返回 0失败返回适当的负错误码。这一约定直接决定了上层 sysfs 读/写路径的错误传播行为见下文实现分析。API 函数创建与释放属性组框架对外的导出接口只有两个函数原型见 include/linux/tsm-mr.h实现见 drivers/virt/coco/guest/tsm-mr.cconst struct attribute_group * tsm_mr_create_attribute_group(const struct tsm_measurements *tm); void tsm_mr_free_attribute_group(const struct attribute_group *attr_grp);tsm_mr_create_attribute_group() 的行为与校验规则实现位于 drivers/virt/coco/guest/tsm-mr.c#L140-L235kernel-doc 说明其运行于进程上下文、可能因内存分配而睡眠。源码中可以看到它在创建属性前做了一组严格的定义校验全部不满足则返回-EINVALtm或tm-mrs为空 —— 直接-EINVAL存在带TSM_MR_F_LIVE的 MR 但tm-refresh为 NULL ——-EINVALLIVE MR 没有刷新来源读到的值不可信存在带TSM_MR_F_WRITABLE的 MR 但tm-write为 NULL ——-EINVAL任一 MR 缺少mr_name——-EINVAL未设TSM_MR_F_NOHASH的 MR其mr_hash必须小于HASH_ALGO__LAST——-EINVAL生成后的 sysfs 属性名见下条命名规则出现重复 ——-EINVAL。此外若内存分配失败则返回-ENOMEM。sysfs 属性命名与权限规则源码揭示了属性在 sysfs 中的落盘形态命名规则带哈希的 MR 属性名为MRNAME:HASH如rtmr:sha384其中 HASH 部分取自hash_algo_name[]表带TSM_MR_F_NOHASH的 MR 直接用mr_name作为属性名见 drivers/virt/coco/guest/tsm-mr.c#L189-L203。属性类型全部是bin_attribute二进制属性bap-size设为该 MR 的mr_size因此用户态读取一次得到的就是完整的 digest。权限位TSM_MR_F_READABLE叠加0444并挂接tm_digest_readTSM_MR_F_WRITABLE叠加0200并挂接tm_digest_write。一个既不可读也不可写的 MR 不会产生任何访问入口。属性组名固定为measurementsctx-agrp.nameCC Guest 驱动拿到返回的attribute_group后把它并入自己的 kobject 属性组即可。一个值得注意的实现细节属性指针数组与所有 MR 名串被合并进单次连续分配属性数组之后紧跟字符串区目的是让释放时只需kfree两次无需逐个释放 MR 名串。调用方生命周期约束与释放函数kernel-doc 明确要求在调用tsm_mr_free_attribute_group()之前不得释放tm因为生成的属性引用了tm及其成员。释放函数实现于 drivers/virt/coco/guest/tsm-mr.c#L244-L251它对IS_ERR_OR_NULL(attr_grp)做了防御因此创建失败返回的错误指针也可以安全地传入释放。底层实现tm_context 与缓存同步机制tm_digest_read()/tm_digest_write()背后的全部状态集中在内部结构体struct tm_contextdrivers/virt/coco/guest/tsm-mr.c#L37-L43struct 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 一个 bin_attribute */ };其中in_sync是理解整个读路径的钥匙。源码注释解释了两层设计取舍为什么需要缓存tm-refresh()可能很昂贵需要向 TVM 硬件/固件发起批量读取因此tm_digest_read()缓存 MR 值仅在必要时调用tm-refresh()。只有 LIVE MR 可能触发 refresh非 LIVE MR 被假定一直保留着上次tm-write()写入的值。为什么是全局标志而非每 MR 标志多数甚至所有架构支持一次性读出全部 MR因此用一个全局in_sync比逐 MR 跟踪更简单。tm_digest_write()成功后会把in_sync置为false表示“自上次 refresh 之后发生过写入缓存已过期”。读路径tm_digest_read() 的锁升级与双重检查tm_digest_read()drivers/virt/coco/guest/tsm-mr.c#L45-L89的流程是先down_read_interruptible()拿读锁若目标 MR 是 LIVE 且!in_sync释放读锁down_write_killable()升为写锁再次检查in_sync双重检查避免两个读者先后升级锁导致重复 refresh调用ctx-tm-refresh(ctx-tm)把in_sync置为!rcrefresh 失败也视为“已尝试同步”不无限重试并通过trace_tsm_mr_refresh()打点最后downgrade_write()降级回读锁memcpy出mr-mr_value off共count字节打trace_tsm_mr_read()点放锁返回。写路径tm_digest_write() 拒绝部分写tm_digest_write()drivers/virt/coco/guest/tsm-mr.c#L91-L120的关键约束是不支持部分写/* partial writes are not supported */ if (off ! 0 || count ! attr-size) return -EINVAL;即用户态必须从偏移 0 开始、一次写入恰好mr_size字节整个 digest 长度。拿到写锁后调用tm-write()成功后把in_sync置false并打trace_tsm_mr_write()点返回rc ?: count保证成功时返回写入字节数。可观测性与构建配置tsm_mr 追踪事件框架自带一组 tracepoint定义在 include/trace/events/tsm_mr.h与读/写/刷新路径一一对应事件触发点输出内容tsm_mr_read每次成功读取 MRMR 名、哈希名NOHASH 时显示data、MR 值 hex 串格式[name] hash:hextsm_mr_refreshLIVE MR 触发刷新后MR 名、succeeded/failed及错误码tsm_mr_write每次成功写入 MRMR 名、哈希名、写入数据 hex 串因此调试 MR 行为时无需改动驱动代码启用tsm_mr:*类追踪即可观察读取、扩展与刷新的完整时序与结果。编译开关该框架由独立 Kconfig 选项CONFIG_TSM_MEASUREMENTS控制构建规则见 drivers/virt/coco/guest/Makefileobj-$(CONFIG_TSM_MEASUREMENTS) tsm-mr.o从源码结构看tsm-mr.o位于drivers/virt/coco/guest/公共 Guest 侧目录同目录还有report.c即 TSM 报告框架而drivers/virt/coco/下按架构并列了sev-guest、tdx-guest、pkvm-guest、arm-cca-guest等各自独立的选择项可以推断各架构 CC Guest 驱动在选中自身选项的同时依赖本框架提供 MR 的 sysfs 抽象。面向 CC Guest 驱动开发者的接入要点综合头文件契约与通用实现一个 CC Guest 驱动接入 MR 设施需要做按顺序定义struct tsm_measurement_register数组为每个 MR 指定mr_name、digest 缓存指针与大小、标志位只读 MR 可直接用TSM_MR_()宏RTMR 用TSM_MR_F_RTMR无哈希的裸数据寄存器加TSM_MR_F_NOHASH实现refresh()回调一次性从 TVM 硬件/固件回填所有LIVE MR 的mr_value缓存成功返回 0如有可写 MR实现write()回调按架构语义通常是 extend把data长度mr_size扩展进指定 MR成功返回 0把以上打包进struct tsm_measurements调用tsm_mr_create_attribute_group()得到名为measurements的属性组并入驱动的 kobject卸载/退出时调用tsm_mr_free_attribute_group()且在此之前不得释放tm及 MR 名串、digest 缓存mr_name与mr_value的“保持有效”约束注意失败路径创建函数可能返回-EINVAL定义非法如 LIVE 无 refresh、WRITABLE 无 write、重名等或-ENOMEM驱动需对错误指针做处理且释放函数可安全接收错误指针。这套 API 的分工非常清晰头文件定义“描述什么”寄存器、哈希、标志、回调契约tsm-mr.c负责“怎么呈现”sysfs 属性生成、缓存与锁、部分写拒绝、追踪点架构驱动只负责“值从哪里来、写到哪里去”。对维护者而言理解in_sync全局同步标志和“LIVE 才触发 refresh”这条规则是掌握整个测量寄存器子系统的关键。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价