USB4 和 Thunderbolt

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

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

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

这里提供的接口不是为最终用户设计的。相反,应该有一个用户空间工具来处理所有底层细节,维护授权设备数据库,并提示用户建立新连接。

有关 Thunderbolt 设备的 sysfs 接口的更多详细信息,请参见 ABI 文件测试/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 和操作系统不知情的情况下读取主机内存的内容。可以通过设置 IOMMU 来防止这种情况,但由于各种原因,IOMMU 并非始终可用。

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

安全级别如下

none

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

user

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

secure

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

dponly

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

usbonly

固件自动在扩展坞中为 USB 控制器和 Display Port 创建隧道。 所有扩展坞下游的 PCIe 链路都将被删除。

nopcie

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

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

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

每个插入的 Thunderbolt 设备都将出现在 /sys/bus/thunderbolt/devices 下的 sysfs 中。 设备目录包含可用于识别特定设备的信息,包括其名称和 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 上。

取消授权设备

可以通过将 0 写入设备的 authorized 属性来取消授权设备。 这需要连接管理器实现的支持,并且可以通过读取域 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_domain 中发生隧道更改时,驱动程序会将 KOBJ_CHANGE 事件发送到用户空间。 通知携带以下环境变量

TUNNEL_EVENT=<EVENT>
TUNNEL_DETAILS=0:12 <-> 1:20 (USB3)

<EVENT> 的可能值为

activated

隧道已激活(创建)。

changed

此隧道中发生了更改。 例如,带宽分配已更改。

deactivated

隧道已拆除。

low bandwidth

隧道没有获得最佳带宽。

insufficient bandwidth

当前隧道要求没有足够的带宽。

仅当隧道已知时才提供 TUNNEL_DETAILS。 例如,对于固件连接管理器,此信息缺失或未提供完整的隧道信息。 对于软件连接管理器,这包括完整的隧道详细信息。 当前的格式与驱动程序记录时使用的格式匹配。 随着时间的推移,这可能会发生变化。

通过 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 写入此属性文件。

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