> ## Documentation Index
> Fetch the complete documentation index at: https://dragonwingdocs.qualcomm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用 capsule 和 OSTree 机制更新 Qualcomm Linux 上的固件和操作系统

空中下载（OTA）更新对于保持设备正常运行至关重要，尤其是嵌入式系统和 IoT 设备。这些更新使设备能够接收并安装更新。

Qualcomm Linux 使用 capsule 更新机制来更新固件镜像，并使用 OSTree 更新机制来更新 Linux 操作系统。

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_Introduction.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=323a83b42d4308bb7b86ea091a2067ad" alt="图：Qualcomm Linux 的 OTA 更新" width="596" height="552" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_Introduction.png" />

  <p className="text-sm text-gray-700">
    图：Qualcomm Linux 的 OTA 更新
  </p>
</div>

## **使用 capsule 更新固件**

Capsule 更新是一种在启用 Qualcomm Linux 的设备上更新固件的方法。UEFI capsule 将固件打包成二进制格式。当设备启动并在正常任务模式下运行时，它会下载 capsule 并将其部署到 EFI 分区。重启后，在下一个启动周期中，UEFI 会处理该 capsule 并将更新应用到设备固件。

## **用于 Linux 操作系统更新的 OSTree**

OSTree 是一种用于管理基于 Linux 的操作系统的版本化、原子性更新的工具。它的工作方式类似于整个 Linux 文件系统的 git 仓库。OSTree 将文件系统树的快照存储在仓库中，设备通过网络拉取这些快照。借助 OSTree，更新是原子性的并支持回滚，因此中断的更新不会破坏系统。IoT 和边缘设备可从安全、一致的更新中受益，一旦出现问题即可回滚。

## **结合使用 capsule 和 OSTree 实现完整的 OTA 软件更新**

要在单个 OTA 系统中管理固件和操作系统更新，请将 capsule 和 OSTree 更新机制结合使用。Capsule 更新机制首先处理固件更新，更新底层固件。Capsule 更新完成后，系统重启并进入 Linux 操作系统，在那里检查并应用 OSTree 更新。Qualcomm Linux 使用 capsule 通过 UEFI 更新底层固件。

系统按照以下流程使用 capsule 更新固件：

1. 一个称为 UEFI capsule 的二进制文件封装固件更新。
2. 系统通过将 capsule 二进制文件存储在挂载的 `/EFI` 路径中，将其交付给 UEFI。
3. UEFI 固件在启动周期中处理该 capsule，并将更新应用到设备固件。

Qualcomm Linux 使用 OSTree 管理 Linux 操作系统。系统将 Qualcomm Linux 构建生成的更新包复制到设备上，并使用[使用 OSTree 的 Linux 操作系统更新流程](https://dragonwingdocs.qualcomm.com/Key-Documents/Yocto-Guide/update-firmware-and-os-on-qualcomm-linux-using-capsule-and-os-tree-mechanisms#linux-os-update-flow-using-ostree)中列出的命令将其暂存以待激活。设备重启后，将应用该更新包。

下图显示了使用 OSTree 的 Linux 操作系统的存储视图。

运行时（Runtime）视图指示默认部署时存储中的哪些目录在运行时被映射到挂载点。

创建新部署后的存储视图显示当设备成功创建新的 OSTree 部署时闪存存储如何更新。此部署是新创建的，并将在下次重启时尝试作为新的 Linux 操作系统。

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTAupdate.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=4017c5b2955be0dbd51aa9db89d664f0" alt="图：OTA 更新期间的存储概览" width="911" height="826" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTAupdate.png" />

  <p className="text-sm text-gray-700">
    图：OTA 更新期间的存储概览
  </p>
</div>

以下是作为 OTA 一部分更新的 Linux 操作系统和固件镜像列表：

* Linux 操作系统镜像
* `efi.bin` 文件包含 UKI、initrd 和引导加载程序配置文件。OSTree 在部署期间创建新的配置文件。该文件列出了复制到 EFI 分区的新内核和 initramfs 镜像的路径。
* `system.img` 文件包含 rootfs，包括 `/ostree`、`/ostree/repo` 和 `/ostree/deploy` 等关键组件。创建新部署时，OSTree 会更新文件系统树以反映操作系统的新版本。
* 固件镜像。有关固件镜像列表的信息，请参阅 [GitHub](https://github.com/quic/cbsp-boot-utilities/blob/main/uefi_capsule_generation/FirmwarePartitions.md)。

## **更新 capsule 和 HLOS**

下图显示了 capsule 和高级操作系统（HLOS）的更新流程：

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_flow.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=3b63d79a9ad05c9a5a13532c4d380a85" alt="图：Capsule 和 HLOS 更新流程" width="1166" height="129" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_flow.png" />

  <p className="text-sm text-gray-700">
    图：Capsule 和 HLOS 更新流程
  </p>
</div>

要更新 capsule 和 HLOS，请执行以下操作：

1. 将 `<capsule>.cap` capsule 文件复制到 EFI 分区，该分区在已启动的设备上挂载于 `/boot/EFI/UpdateCapsule`。
2. 使用 `EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED` 设置 EFI 变量（efivar）`OsIndications` 标志并重启设备。当 UEFI 通过 `OsIndications` 标志检测到 capsule 更新请求时，将执行以下步骤：
   1. UEFI 通过 `OsIndications` 标志识别出有可用于更新的 capsule。
   2. UEFI 对 capsule 进行身份验证，并从 capsule 更新固件镜像。
   3. Capsule 更新的状态会在 EFI 系统资源表（ESRT）中更新。
   4. 如果在 capsule 更新期间出现故障，UEFI 会将固件回滚到之前的版本。UEFI 成功从 capsule 更新固件后，设备将使用新固件启动。
3. 将 OSTree 仓库复制到设备。使用 OSTree 命令为 HLOS 更新创建新部署，会创建一个带有计数标签的新配置文件，然后重启设备。
4. Systemd-boot 选取新的配置文件并启动内核和用户空间。设备使用更新后的固件和 HLOS 软件启动。`systemd-bless-boot.service` 将新配置标记为良好。
5. 重置 `OtaStatus` `efivar` 中的 `TrialBootEnabled` 标志，以表明固件良好。UEFI 检查此 efivar 以提交新固件。

### **使用 capsule 更新固件**

下图显示了固件更新流程：

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_firmware.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=cd2fbdc6baf35bc7d71489093f085305" alt="图：使用 capsule 更新固件" width="865" height="128" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_firmware.png" />

  <p className="text-sm text-gray-700">
    图：使用 capsule 更新固件
  </p>
</div>

要使用 capsule 更新固件，请执行以下操作：

> **注意**
>
> 有关 `firmware_capsule.cap` capsule 生成的更多信息，请参阅 [UEFI 中的 capsule 生成](https://dragonwingdocs.qualcomm.com/System/Boot/generate-the-capsule)。

1. 要在设备上创建 `UpdateCapsule` 文件夹，运行以下命令：
   ```text theme={null}
   adb shell mkdir /boot/EFI/UpdateCapsule
   ```
2. 将 capsule 复制到设备：
   ```text theme={null}
   scp -r <firmware_capsule.cap> <user>@<IP_address>:/boot/EFI/UpdateCapsule/<firmware_capsule.cap>
   ```
3. 在设备上创建包含指定十六进制数据的 `data.hex` 文件：
   ```text theme={null}
   echo -e -n "\x4\x0\x0\x0\x0\x0\x0\x0" > data.hex
   ```
4. 使用 `efivar` 工具将设备上 `data.hex` 的内容写入 UEFI 变量 `OsIndications`：
   ```text theme={null}
   efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-OsIndications -f data.hex -w
   ```
   有关 UEFI 变量的更多信息，请参阅[更新和恢复](https://dragonwingdocs.qualcomm.com/System/Boot/update-capsule-and-trial-boot-rollback-for-base-and-variants)。
5. 使用 `efivar` 工具打印 `OsIndications` UEFI 变量的值：
   ```text theme={null}
   efivar -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-OsIndications -p
   ```
6. 重启设备：
   ```text theme={null}
   reboot
   ```
7. 检查 ESRT 表条目：
   ```text theme={null}
   cd /sys/firmware/efi/esrt/entries/entry0
   ```
   1. 检查 `last_attempt_status` 命令的输出。如果为 0，则更新成功：
      ```text theme={null}
      cat last_attempt_status
      ```
   2. 检查 `last_attempt_version` 命令的输出：
      ```text theme={null}
      cat last_attempt_version
      ```
   3. 检查 `fw_version` 命令的输出。如果 `last_attempt_version` 和 `fw_version` 相同，则更新成功：
      ```text theme={null}
      cat fw_version
      ```

## **使用 OSTree 更新 Linux 操作系统**

下图显示了 Linux 操作系统的更新流程：

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_hlos.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=8a6e9b3987fe17410d2eaaa2d8cfe858" alt="图：使用 OSTree 更新 Linux 操作系统" width="1093" height="131" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_hlos.png" />

  <p className="text-sm text-gray-700">
    图：使用 OSTree 更新 Linux 操作系统
  </p>
</div>

要使用 OSTree 更新 Linux 操作系统，请执行以下操作：

1. 要检查 Qualcomm 设备中的当前部署，运行以下命令：
   ```text theme={null}
   ostree admin status
   ```
   输出：
   ```text theme={null}
   * poky 643b332dd72b345b5040a1decbe7e04bd2d208a04ba59417aa667c901c485504.0
      Version: 1.0
      origin refspec: poky:rb3gen2-core-kit
   ```
   `*` 表示设备当前启动所使用的部署。
2. `ostree_repo` 软件包位于主机开发计算机上的 `<workspace>/build/tmp/deploy/images/<MACHINE>/` 路径中。例如 `<workspace>/build/tmp/deploy/images/rb3gen2-core-kit/`。使用以下 `scp` 命令将 `ostree_repo` 软件包从主机复制到 Qualcomm 设备：
   ```text theme={null}
   scp -r <ostree_repo> <user>@<IP_address>:/tmp
   ```
3. 在 Qualcomm 设备上拉取本地 OSTree 仓库。
   ```text theme={null}
   ostree pull-local /tmp/<ostree_repo> <branch_name>
   ```
   要查找该命令所需的 `branch_name`，运行以下命令：
   ```text theme={null}
   ostree refs
   ```
   输出：
   ```text theme={null}
   poky:rb3gen2-core-kit
   ```
   在上述输出中，`rb3gen2-core-kit` 是一个示例分支名称。
4. 在 Qualcomm 设备上创建部署：
   ```text theme={null}
   ostree admin deploy <branch_name>
   ```
   这会在 `/boot/loader/entries/` 目录中创建 `ostree-2-poky.conf` 配置文件。有关更多信息，请参阅 [Systemd 启动计数 - 成功启动](https://dragonwingdocs.qualcomm.com/Key-Documents/Yocto-Guide/update-firmware-and-os-on-qualcomm-linux-using-capsule-and-os-tree-mechanisms#systemd-boot-successful-boot)。
5. 重启设备：
   ```text theme={null}
   reboot
   ```
6. 检查设备是否使用所创建的部署启动：
   ```text theme={null}
   ostree admin status
   ```
   输出：
   ```text theme={null}
   * poky 1e8a01bcc5cd9b3b34043db494ddcd01ec5c1c84312479a55253358a92250fa3.0
      Version: 1.0
      origin refspec: rb3gen2-core-kit
      poky 643b332dd72b345b5040a1decbe7e04bd2d208a04ba59417aa667c901c485504.0 (rollback)
      Version: 1.0
      origin refspec: poky:rb3gen2-core-kit
   ```
   要在构建主机上验证新创建的部署，请检查 `<workspace>/build-<DISTRO>/tmp-glibc/work/<MACHINE>/<IMAGE>/ota-sysroot/ostree/deploy/poky/deploy` 路径中的部署。例如 `<workspace>/build/tmp/work/rb3gen2-core-kit/qcom-multimedia-image/ota-sysroot/ostree/deploy/poky/deploy`。

## **Systemd-boot 计数 - 成功启动**

下图显示了成功启动时的 systemd-boot 计数流程：

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_systemd_boot.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=7c9c9c37aea64bafec6f0f4d0a914363" alt="图：成功启动时的 Systemd-boot 计数" width="859" height="124" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_systemd_boot.png" />

  <p className="text-sm text-gray-700">
    图：成功启动时的 Systemd-boot 计数
  </p>
</div>

为了在成功启动后管理 systemd-boot 中的启动计数，OSTree 执行以下流程：

1. 当 OSTree 部署新配置时，它会创建一个名称中带有 `+3` 标签的配置文件，表示最大重试次数。这将启用启动计数。
2. systemd-boot 检测到条目文件名中的 `+3` 标签，并将其重命名为 `ostree-conf+2-1.conf`，表示已开始一次启动尝试。重命名文件后，启动过程继续。
3. `systemd-bless-boot-generator` 创建 `systemd-bless-boot.service`，该服务被设置为在到达 `boot-complete.target` 时启动。
4. `systemd-bless-boot.service` 通过移除计数标签 `+2-1` 并将文件重命名为 `ostree-conf.conf`，将新配置标记为成功。

## **Systemd 启动计数 - 启动失败和回滚**

下图显示了启动失败和回滚时的 systemd-boot 计数流程：

<div className="flex flex-col items-center gap-2">
  <img src="https://mintcdn.com/qualcomm-prod/Im5W2pUR5LdqxAI6/Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_boot_counting.png?fit=max&auto=format&n=Im5W2pUR5LdqxAI6&q=85&s=1314415b5b499f2a88580fe18ac94b26" alt="图：启动失败和回滚时的 Systemd-boot 计数" width="1356" height="129" data-path="Key-Documents/Yocto-Guide/media/k2c-qli-yocto-build-ga/OTA_boot_counting.png" />

  <p className="text-sm text-gray-700">
    图：启动失败和回滚时的 Systemd-boot 计数
  </p>
</div>

为了在启动失败和回滚后管理 systemd-boot 中的启动计数，OSTree 执行以下流程：

1. 当 OSTree 部署新配置时，它会创建一个名称中带有 `+3` 标签的 systemd 配置文件，表示最大重试次数，例如 `ostree-boot+3.conf`。这将启用启动计数。
2. systemd-boot 检测到配置文件名中的 `+3` 标签，并将其重命名为 `ostree-boot+2-1.conf`，表示已开始一次启动尝试。重命名文件后，启动过程继续。
3. `systemd-bless-boot-generator` 创建 `systemd-bless-boot.service`，该服务在到达 `boot-complete.target` 时启动。如果 Linux 启动过程中出现任何故障，`systemd-bless-boot.service` 不会从配置文件中移除 `+2-1` 计数标签。
4. 在后续启动中，systemd-boot 检测到配置文件名中的 `+2-1` 标签，将文件重命名为 `ostree-boot+1-2.conf`，并尝试以该文件启动。
5. 如果第二次尝试时 Linux 启动失败，`systemd-bless-boot.service` 不会从配置文件中移除计数标签 `+1-2`。
6. 在下一次启动中，systemd-boot 检测到配置文件名中的 `+1-2` 标签，将文件重命名为 `ostree-boot+0-3.conf`，并尝试以该文件启动。这是启动 Linux 部署的最后一次尝试。
7. 如果设备在第三次尝试时未能启动 Linux，`systemd-bless-boot.service` 不会从配置文件中移除计数标签 `+0-3`。
8. 在后续启动中，systemd-boot 发现配置文件名中的 `+0-3` 标签。由于计数器已归零，该条目（配置文件）被视为无效。systemd-boot 会通过尝试有效的配置文件条目回退到较早的版本。

## **Usrmerge**

Linux 中的 Usrmerge 功能通过将某些目录合并到 `/usr` 路径下来简化文件系统布局。它将 `/bin`、`/sbin` 和 `/lib` 分别与 `/usr/bin`、`/usr/sbin` 和 `/usr/lib` 合并。

启用 Usrmerge 后，位于 `/bin`、`/sbin` 和 `/lib` 中的可执行文件和库分别放置在 `/usr/bin`、`/usr/sbin` 和 `/usr/lib` 中。原始目录变为指向其 /usr 对应目录的符号链接。这种统一的结构使维护更加容易并减少了冗余，因为二进制文件和库只有一个存放位置，而不是根级目录和 `/usr` 目录两个独立位置。符号链接确保了兼容性，使引用诸如 `/bin` 之类路径的脚本和软件能够继续正常工作。

Linux 发行版正在采用 Usrmerge，以符合文件系统层次结构标准（FHS）的建议并简化根文件系统，尤其是在容器化和嵌入式系统中。

Debian、Ubuntu 和 Fedora 等发行版已将 Usrmerge 作为其系统布局的一部分，使其成为最新版本中的标准。该过渡涉及创建符号链接，并将原始目录中剩余的文件移动到其 `/usr` 对应位置。

## **在 Qualcomm Linux 中管理** `/var`**、** `/home`**、** `/media`**、** `/mnt`**、** `/opt`**、** `/srv` **和** `/usr `

OSTree 将 `/var` 视为持久目录。这意味着用户和运行时在 `/var` 下创建的内容不会被 OSTree 触碰，并在 OTA 更新之间持久保留。有关更多信息，请参阅 [OSTree 概述](https://ostreedev.github.io/ostree/introduction/)。

在 OTA 更新期间 OSTree 不会触碰的其他目录包括 `/home`、`/media`、`/mnt`、`/opt` 和 `/srv`。OSTree 将这些目录映射为如下符号链接：

* `/home` 是指向 `/var/rootdirs/home` 的符号链接
* `/media` 是指向 `/var/rootdirs/media` 的符号链接
* `/mnt` 是指向 `/var/rootdirs/mnt` 的符号链接
* `/opt` 是指向 `/var/rootdirs/opt` 的符号链接
* `/srv` 是指向 `/var/rootdirs/srv` 的符号链接

存储在 `/home`、`/media`、`/mnt`、`/opt`、`/srv` 和 `/var` 下的任何运行时数据在 OTA 更新之间都保持持久。

为了保持文件系统整洁和一致，请勿在构建时在上述目录下安装任何产物。构建时安装在这些目录中的任何产物都不会打包到 Qualcomm Linux 构建命令（即 `bitbake <image recipe>`）生成的 `rootfs` 镜像中。

要在运行时在持久路径下创建文件和目录，请执行以下操作：

进程在需要时于运行时创建文件或目录。

> 1. `/run/`、`/var/lib/`、`/var/cache/` 和 `/var/log/` 下的路径可以通过相应的 systemd 单元文件创建。这里有一份[参考](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RuntimeDirectory=)。
> 2. 使用 [systemd-tmpfiles](https://www.freedesktop.org/software/systemd/man/latest/systemd-tmpfiles-setup.service.html) 在启动时创建文件、符号链接和目录。

OSTree 在 `/usr` 处创建只读绑定挂载，确保核心操作系统文件对用户保持不可变。此方法有助于维护系统的完整性和安全性。OSTree 使用 `/usr` 挂载点来部署下一次更新。

> **注意**
>
> * OSTree 允许在构建时在 `/var/local` 路径下安装文件和目录。
> * 尽管 OSTree 保留了构建时安装在 `/usr` 下的内容，但它不会将安装在 `/usr/local` 子目录下的内容打包到 rootfs 镜像中。

在启用 OSTree 的 Qualcomm Linux 中，`/etc` 目录的管理方式同时支持系统更新和本地自定义。

> * `/etc` 目录是可变的，允许在运行时进行修改，以维护需要在更新之间持久保留的系统配置。
> * OSTree 支持将配置文件从 `/usr/etc` 目录合并到 `/etc`。这使得 OSTree 可以更新默认配置，同时保留所做的任何本地更改。
> * 应用更新时，OSTree 会使用文件的原始版本、新更新中的文件版本以及本地修改的文件版本，对 `/etc` 中的配置文件执行三方合并。
> * 如果合并过程中出现冲突，OSTree 会保留运行时的修改。这有助于维持系统稳定性，并确保关键配置不会被覆盖。

## **SOTA 发行版功能**

软件空中下载（SOTA）发行版功能允许对嵌入式系统和 IoT 设备进行远程更新。它集成了 OSTree 等系统更新工具，使设备无需物理接触即可接收和安装更新。

要在 Qualcomm Linux 中启用 SOTA 发行版功能，运行以下构建命令：

> ```text theme={null}
> kas build meta-qcom/ci/rb3gen2-core-kit.yml:meta-qcom/ci/qcom-distro-sota.yml
> ```

此命令会自动为您的构建配置并启用 SOTA 发行版功能。

## **后续步骤**

使用配方处理树外（out-of-tree）内核模块和设备树：

* 要构建树外内核模块，请参阅[添加内核模块](https://dragonwingdocs.qualcomm.com/System/Kernel/out-of-tree-kernel-modules)。**注意** Qualcomm Linux 仅在 `custom` 变体中支持内核外设备树覆盖（overlay）。
* 有关设备树/设备树 blob 管理，请参阅[平台设备树](https://dragonwingdocs.qualcomm.com/System/Kernel/locate-and-modify-device-trees)中的内核 DTB 构建支持。
