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驱动栈的设计哲学是“分层隔离”。硬件变了,只换最底层;上层逻辑和用户空间工具完全不用改。这就是工程上的解耦。
为了让你更直观地理解这个架构,我画了一张图:
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。只有环境通了,后面的密钥封存、安全存储才能玩得转。