USB4 和 Thunderbolt

USB4 是基于 Thunderbolt 3 协议的公共规范,除其他外,在寄存器级别存在一些差异。连接管理器是在主机路由器(主机控制器)上运行的实体,负责枚举路由器和建立隧道。连接管理器可以在固件或软件中实现。通常,PC 配备用于 Thunderbolt 3 和早期支持 USB4 的系统的固件连接管理器。另一方面,Apple 系统使用软件连接管理器,并且后来的 USB4 兼容设备也遵循该方法。

Linux Thunderbolt 驱动程序同时支持这两种方式,并且可以在运行时检测要使用哪种连接管理器实现。为了安全起见,Linux 中的软件连接管理器还宣布安全级别为 user,这意味着默认情况下禁用 PCIe 隧道。以下文档适用于两种实现,但软件连接管理器仅支持 user 安全级别,并且应附带基于 IOMMU 的 DMA 保护。

安全级别以及如何使用它们

此处提供的接口并非供最终用户使用。相反,应该有一个用户空间工具来处理所有底层细节,维护授权设备的数据库,并提示用户进行新连接。

有关 Thunderbolt 设备的 sysfs 接口的更多详细信息,请参见 Documentation/ABI/testing/sysfs-bus-thunderbolt

那些只想连接任何设备而无需任何手动操作的用户可以在 /etc/udev/rules.d/99-local.rules 中添加以下行

ACTION=="add", SUBSYSTEM=="thunderbolt", ATTR{authorized}=="0", ATTR{authorized}="1"

这将自动授权所有出现的设备。但是,请记住,这会绕过安全级别,并使系统容易受到 DMA 攻击。

从 Intel Falcon Ridge Thunderbolt 控制器开始,有 4 个可用的安全级别。Intel Titan Ridge 添加了一个额外的安全级别 (usbonly)。这些的原因是,所连接的设备可以是 DMA 主设备,从而读取主机内存的内容,而 CPU 和 OS 不知情。可以通过设置 IOMMU 来防止这种情况,但由于各种原因,它并非总是可用。

一些 USB4 系统具有禁用 PCIe 隧道的 BIOS 设置。这被视为另一个安全级别 (nopcie)。

安全级别如下:

none

所有设备都由固件自动连接。无需用户批准。在 BIOS 设置中,这通常称为传统模式

user

将询问用户是否允许连接设备。基于通过 /sys/bus/thunderbolt/devices 可用的设备识别信息,用户可以做出决定。在 BIOS 设置中,这通常称为唯一 ID

secure

将询问用户是否允许连接设备。除了 UUID 之外,如果设备支持安全连接,还会向其发送一个挑战,该挑战应与基于写入 key sysfs 属性的随机密钥的预期挑战相匹配。在 BIOS 设置中,这通常称为一次性保存密钥

dponly

固件自动为显示端口和 USB 创建隧道。不执行 PCIe 隧道。在 BIOS 设置中,这通常称为仅显示端口

usbonly

固件自动为坞站中的 USB 控制器和显示端口创建隧道。删除坞站的所有 PCIe 下行链路。

nopcie

BIOS 禁止/禁用 PCIe 隧道。在某些 USB4 系统中可用。

当前的安全级别可以从 /sys/bus/thunderbolt/devices/domainX/security 中读取,其中 domainX 是主机控制器管理的 Thunderbolt 域。每个 Thunderbolt 主机控制器通常有一个域。

如果安全级别读取为 usersecure,则必须先由用户授权连接的设备,然后才能创建 PCIe 隧道(例如,出现 PCIe 设备)。

插入的每个 Thunderbolt 设备都将出现在 sysfs 中的 /sys/bus/thunderbolt/devices 下。设备目录包含可用于标识特定设备的信息,包括其名称和 UUID。

当安全级别为 usersecure 时授权设备

插入设备时,它将如下所示出现在 sysfs 中

/sys/bus/thunderbolt/devices/0-1/authorized   - 0
/sys/bus/thunderbolt/devices/0-1/device       - 0x8004
/sys/bus/thunderbolt/devices/0-1/device_name  - Thunderbolt to FireWire Adapter
/sys/bus/thunderbolt/devices/0-1/vendor       - 0x1
/sys/bus/thunderbolt/devices/0-1/vendor_name  - Apple, Inc.
/sys/bus/thunderbolt/devices/0-1/unique_id    - e0376f00-0300-0100-ffff-ffffffffffff

authorized 属性读取为 0,这意味着尚未创建 PCIe 隧道。用户可以通过简单地输入以下内容来授权设备

# echo 1 > /sys/bus/thunderbolt/devices/0-1/authorized

这将创建 PCIe 隧道,并且设备现在已连接。

如果设备支持安全连接,并且域安全级别设置为 secure,它会有一个额外的属性 key,该属性可以保存一个随机的 32 字节值,该值用于在未来连接中进行授权和挑战设备

/sys/bus/thunderbolt/devices/0-3/authorized   - 0
/sys/bus/thunderbolt/devices/0-3/device       - 0x305
/sys/bus/thunderbolt/devices/0-3/device_name  - AKiTiO Thunder3 PCIe Box
/sys/bus/thunderbolt/devices/0-3/key          -
/sys/bus/thunderbolt/devices/0-3/vendor       - 0x41
/sys/bus/thunderbolt/devices/0-3/vendor_name  - inXtron
/sys/bus/thunderbolt/devices/0-3/unique_id    - dc010000-0000-8508-a22d-32ca6421cb16

请注意,默认情况下密钥为空。

如果用户不想使用安全连接,他们只需将 echo 1authorized 属性,并且将以与 user 安全级别相同的方式创建 PCIe 隧道。

如果用户想使用安全连接,则第一次插入设备时,需要创建一个密钥并将其发送给设备

# key=$(openssl rand -hex 32)
# echo $key > /sys/bus/thunderbolt/devices/0-3/key
# echo 1 > /sys/bus/thunderbolt/devices/0-3/authorized

现在,设备已连接(已创建 PCIe 隧道),并且密钥已存储在设备 NVM 上。

下次插入设备时,用户可以使用同一密钥来验证(挑战)设备

# echo $key > /sys/bus/thunderbolt/devices/0-3/key
# echo 2 > /sys/bus/thunderbolt/devices/0-3/authorized

如果设备返回的挑战与我们基于密钥的预期挑战相匹配,则会连接设备并创建 PCIe 隧道。但是,如果挑战失败,则不会创建隧道,并且会向用户返回错误。

如果用户仍然想连接设备,则他们可以批准设备而不使用密钥,也可以写入新密钥并将 1 写入 authorized 文件,以将新密钥存储在设备 NVM 上。

取消授权设备

可以通过将设备的 authorized 属性设置为 0 来取消设备的授权。 这需要连接管理器实现的支持,并且可以通过读取域的 deauthorization 属性来检查。如果读取到 1,则表示支持此功能。

当设备被取消授权时,从父设备 PCIe 下行端口(或根端口)到设备 PCIe 上行端口的 PCIe 通道将被拆除。这本质上与 PCIe 热插拔相同,并且在设备再次被授权之前,相关 PCIe 拓扑将无法访问。如果涉及到诸如 NVMe 或类似存储设备,如果该存储设备上的文件系统没有正确关闭,则存在数据丢失的风险。请注意!

使用 IOMMU 的 DMA 保护

2018 年及以后配备 Thunderbolt 端口的最新系统可能原生支持 IOMMU。这意味着 Thunderbolt 安全由 IOMMU 处理,因此连接的设备无法访问驱动程序为其分配的内存区域之外的内存区域。当 Linux 在此类系统上运行时,如果用户尚未启用 IOMMU,它会自动启用 IOMMU。 可以通过读取 /sys/bus/thunderbolt/devices/domainX/iommu_dma_protection 属性中的 1 来识别这些系统。

在这种情况下,驱动程序不做任何特殊处理,但是由于 DMA 保护由 IOMMU 处理,因此安全级别(如果已设置)是多余的。因此,某些系统在出厂时将安全级别设置为 none。其他系统将安全级别设置为 user,以便支持降级到旧版操作系统,因此希望在启用 IOMMU DMA 保护时自动授权设备的用户可以使用以下 udev 规则

ACTION=="add", SUBSYSTEM=="thunderbolt", ATTRS{iommu_dma_protection}=="1", ATTR{authorized}=="0", ATTR{authorized}="1"

升级 Thunderbolt 设备、主机或中继器上的 NVM

由于大部分功能是在主机控制器或设备上运行的固件中处理的,因此重要的是,固件可以升级到最新版本,以便修复其中的潜在错误。通常,OEM 会在其支持站点上提供此固件。

还有一个中心站点,其中包含一些机器的固件下载链接

在升级设备、主机或中继器上的固件之前,请确保它是合适的升级。否则可能会导致设备处于无法正常使用的状态,而没有特殊工具!

Apple Mac 上不支持主机 NVM 升级。

下载 NVM 映像后,您需要插入一个 Thunderbolt 设备,以便主机控制器出现。连接哪个设备并不重要(除非您要升级设备上的 NVM - 那么您需要连接该特定设备)。

请注意,您的系统可能提供 OEM 特定的控制器上电方法(“强制上电”),在这种情况下,无需插入 Thunderbolt 设备。

之后,我们可以将固件写入主机或设备的 NVM 的非活动部分。例如,以下是如何升级 Intel NUC6i7KYK (Skull Canyon) Thunderbolt 控制器 NVM 的方法

# dd if=KYK_TBT_FW_0018.bin of=/sys/bus/thunderbolt/devices/0-0/nvm_non_active0/nvmem

操作完成后,我们可以按如下方式触发 NVM 身份验证和升级过程

# echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate

如果没有返回错误,则主机控制器会在短时间内消失。一旦它恢复,驱动程序会注意到它并启动完全断电循环。一段时间后,主机控制器再次出现,此时它应该是完全可用的。

我们可以通过运行以下命令来验证新的 NVM 固件是否已激活

# cat /sys/bus/thunderbolt/devices/0-0/nvm_authenticate
0x0
# cat /sys/bus/thunderbolt/devices/0-0/nvm_version
18.0

如果 nvm_authenticate 包含 0x0 以外的任何内容,则表示上次身份验证周期的错误代码,这意味着 NVM 映像的身份验证失败。

请注意,NVMem 设备 nvm_activeNnvm_non_activeN 的名称取决于它们在 NVMem 子系统中注册的顺序。名称中的 N 是 NVMem 子系统添加的标识符。

在没有电缆连接的情况下升级板载中继器 NVM

如果平台支持,即使 USB4 端口上未连接任何设备,也可能可以升级中继器 NVM 固件。在这种情况下,usb4_portX 设备具有两个特殊属性:offlinerescan。升级固件的方法是首先将 USB4 端口置于离线模式

# echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline

此步骤确保端口不响应任何热插拔事件,并确保中继器已上电。下一步是扫描中继器

# echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan

这会枚举并添加板载中继器。现在,可以像连接电缆一样升级中继器 NVM(请参见上一节)。但是,中继器不会断开连接(因为我们处于离线模式),因此在将 1 写入 nvm_authenticate 后,应等待 5 秒或更长时间,然后再重新运行扫描

# echo 1 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/rescan

如果一切顺利,此时端口可以恢复到功能状态

# echo 0 > /sys/bus/thunderbolt/devices/0-0/usb4_port1/offline

当主机控制器处于安全模式时升级 NVM

如果现有 NVM 未正确身份验证(或丢失),则主机控制器将进入安全模式,这意味着唯一可用的功能是刷写新的 NVM 映像。在此模式下,读取 nvm_version 会失败并返回 ENODATA,并且设备识别信息丢失。

要从此模式恢复,需要以与上一章中相同的方式将有效的 NVM 映像刷写到主机控制器。

通过 Thunderbolt 电缆进行网络连接

Thunderbolt 技术允许通过 Thunderbolt 电缆连接的两个主机之间进行软件通信。

可以通过 Thunderbolt 链路传输任何类型的流量,但目前我们仅支持 Apple ThunderboltIP 协议。

如果另一台主机运行的是 Windows 或 macOS,您只需在两台主机之间连接一根 Thunderbolt 电缆即可;thunderbolt-net 驱动程序会自动加载。如果另一台主机也是 Linux,则应在一台主机上手动加载 thunderbolt-net(哪一台主机都可以)

# modprobe thunderbolt-net

这会自动触发另一台主机上的模块加载。如果驱动程序内置到内核映像中,则无需执行任何操作。

驱动程序将为每个 Thunderbolt 端口创建一个虚拟以太网接口,其名称类似于 thunderbolt0 等。从这一点开始,您可以使用标准用户空间工具(如 ifconfig)来配置接口,或者让您的 GUI 自动处理它。

强制上电

许多 OEM 都提供了一种方法,即使未连接任何设备,也可以使用该方法强制 Thunderbolt 控制器的电源进入“开启”状态。如果您的机器支持,则可以通过 WMI 总线公开此功能,并带有名为“force_power”的 sysfs 属性。

例如,intel-wmi-thunderbolt 驱动程序在以下位置公开此属性

/sys/bus/wmi/devices/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power

要强制上电,请将 1 写入此属性文件。要禁用强制上电,请将 0 写入此属性文件。

注意:目前无法查询平台的强制上电状态。