2. TPM驱动与栈:Linux内核TPM驱动架构、tpm_tis与tpm_crb驱动、tpm2_tools工具链安装

好,我们正式开始第二讲。上一章我们把TPM的基本概念和硬件形态过了一遍。这一章,咱们得把袖子撸起来,看看Linux系统里到底怎么跟TPM芯片打交道。

说白了,TPM芯片焊在主板上,CPU要跟它通信,中间必须得有个“翻译官”。这个翻译官就是驱动。Linux内核里有一套完整的TPM驱动栈,从最底层的硬件接口,到中间的逻辑层,再到用户空间的操作工具,一层一层叠起来。

我个人习惯把TPM驱动栈想象成一个三层的蛋糕。最下面一层是物理接口,比如LPC总线或者SPI;中间是核心驱动层,负责处理TPM命令的封装和拆解;最上面就是用户空间工具,比如我们马上要装的tpm2_tools。你想想看,没有这个栈,你写个程序想读个PCR值,那得自己拼字节流、处理中断、算校验,太痛苦了。

2.1 Linux内核TPM驱动架构概览

Linux内核的TPM子系统,代码主要在drivers/char/tpm/目录下。我刚开始接触这块代码时,觉得文件挺多,有点乱。但捋清楚之后,结构其实很清晰。

核心架构分为三层:

  • 硬件接口层:直接跟TPM芯片的物理总线打交道。常见的有tpm_tis(LPC/SPI接口)和tpm_crb(ACPI接口)。
  • 核心层:tpm-interface.c、tpm2-cmd.c这些文件。它们负责命令的序列化、响应解析、超时管理、 locality 控制等。这一层对上层屏蔽了硬件差异。
  • 字符设备层:tpm-dev.c和tpmrm-dev.c。它们把TPM暴露成/dev/tpm0和/dev/tpmrm0这样的字符设备,用户空间的程序就能通过open、read、write来操作了。

嗯,这里要注意,/dev/tpm0是直接访问,而/dev/tpmrm0是资源管理型访问,内核会帮你处理命令的排队和 locality 切换。我在项目中遇到过,多线程同时访问/dev/tpm0导致命令冲突,后来改成/dev/tpmrm0就稳了。

核心要点:TPM驱动栈的设计哲学是“分层隔离”。硬件变了,只换最底层;上层逻辑和用户空间工具完全不用改。这就是工程上的解耦。

为了让你更直观地理解这个架构,我画了一张图:

Linux TPM 驱动栈架构图 Linux TPM 驱动栈架构 用户空间 tpm2_tools (tpm2_createprimary, tpm2_pcrread, ...) 字符设备层 /dev/tpm0 (直接访问) | /dev/tpmrm0 (资源管理) TPM核心层 命令封装 / 响应解析 / 超时管理 / Locality控制 硬件接口层 (驱动) tpm_tis (LPC/SPI) | tpm_crb (ACPI/MMIO) | 其他 TPM 硬件芯片 系统调用 (read/write/ioctl)

2.2 tpm_tis 驱动:最通用的TPM接口

tpm_tis是Linux里最老牌、最通用的TPM驱动。它支持TPM 1.2和TPM 2.0,通过LPC总线或者SPI总线跟芯片通信。TIS是“TPM Interface Specification”的缩写,说白了就是TPM芯片的标准接口规范。

这个驱动的核心机制是:

  • Memory-Mapped I/O:把TPM的寄存器映射到内存地址空间,CPU直接读写内存就能控制TPM。
  • Locality:TPM定义了5个Locality(0-4),用来控制不同软件组件的访问权限。驱动默认用Locality 0。
  • 中断或轮询:驱动可以配置使用中断,也可以轮询状态寄存器。我建议能用中断就用中断,省CPU。

我记得有一次调试一个国产主板,TPM死活不工作。dmesg一看,tpm_tis报Timeout错误。后来发现是BIOS里没给TPM分配正确的MMIO地址。嗯,这种问题最坑了,硬件层面没配好,驱动怎么折腾都没用。

调试小技巧:如果tpm_tis驱动加载失败,先检查/proc/iomem里有没有TPM的MMIO区域。如果没有,大概率是BIOS/固件的问题。

2.3 tpm_crb 驱动:ACPI时代的TPM接口

tpm_crb是后来加入的驱动,CRB是“Command Response Buffer”的缩写。它跟tpm_tis最大的区别是:

  • 基于ACPI:通过ACPI表(特别是TPM2表)来发现和配置TPM设备,不需要在设备树或PCIe里枚举。
  • 命令缓冲区:CPU把TPM命令写入一个共享内存缓冲区,然后通知TPM去取。TPM处理完,再把结果写回缓冲区。
  • 更现代:主要用在UEFI固件+ACPI的x86和ARM64平台上。

我个人的经验是,近五年的笔记本和服务器,基本都是tpm_crb驱动。如果你在dmesg里看到tpm_crb: Found TPM,那说明你的TPM是通过ACPI发现的。

下面这个表格对比了两种驱动的主要差异:

特性 tpm_tis tpm_crb
接口标准 TIS (TPM Interface Spec) CRB (Command Response Buffer)
发现方式 ACPI/设备树/PCIe ACPI TPM2表
通信方式 MMIO寄存器 共享内存缓冲区
Locality支持 完整支持 (0-4) 通常只支持Locality 0
适用平台 传统x86/嵌入式 现代UEFI x86/ARM64
驱动代码 tpm_tis.c tpm_crb.c

避坑指南:我曾经在QEMU虚拟机里测试TPM,默认用的是tpm_tis模拟。但如果你在物理机上同时看到tpm_tis和tpm_crb都尝试绑定同一个TPM设备,那就会冲突。解决办法是在内核启动参数里用modprobe.blacklist=tpm_tis禁用一个。

2.4 tpm2_tools 工具链安装

驱动加载好了,/dev/tpm0出现了,接下来我们得装用户空间的工具。最主流的就是tpm2_tools,它是TPM 2.0的瑞士军刀。

安装方式取决于你的发行版:

  • Ubuntu/Debian:sudo apt install tpm2-tools
  • Fedora/RHEL/CentOS:sudo dnf install tpm2-tools
  • Arch Linux:sudo pacman -S tpm2-tools
  • 从源码编译:如果你需要最新版本,或者你的发行版太老,那就得自己编译。

从源码编译的步骤,我建议你按这个来:

# 1. 安装依赖
sudo apt install autoconf automake libtool pkg-config libssl-dev
sudo apt install libjson-c-dev libcurl4-openssl-dev

# 2. 下载源码
git clone https://github.com/tpm2-software/tpm2-tools.git
cd tpm2-tools

# 3. 配置、编译、安装
./bootstrap
./configure --prefix=/usr/local
make -j$(nproc)
sudo make install

# 4. 验证安装
tpm2_getcap --help

安装完成后,你可以先跑一个最简单的命令,看看TPM是否正常工作:

# 获取TPM的固件版本和厂商信息
tpm2_getcap properties-fixed

# 读取PCR 0的当前值
tpm2_pcrread sha256:0

如果tpm2_getcap能返回一堆信息,没有报错,那恭喜你,TPM驱动和工具链都就位了。

重要提醒:tpm2_tools依赖tpm2-abrmd(资源管理守护进程)或者直接访问/dev/tpm0。我个人习惯用tpm2-abrmd,因为它能处理多进程并发访问TPM的问题。安装方式:sudo apt install tpm2-abrmd,然后启动服务:sudo systemctl start tpm2-abrmd。

嗯,这一章的内容就到这里。驱动架构、两种主流驱动、工具链安装,这三块是后续所有操作的基础。你可以在自己的机器上试试,看看dmesg | grep tpm输出的是什么驱动,然后装好tpm2_tools,跑一下tpm2_getcap。只有环境通了,后面的密钥封存、安全存储才能玩得转。