> ## 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.

# U-Boot 中的胶囊更新

## 概述

本页是一份实践演练，介绍如何从 U-Boot 控制台执行多镜像 UEFI 胶囊更新。
它是对本章中胶囊概念页面的补充，展示了以下操作的具体命令：

1. 查询开发板可以更新哪些固件镜像。
2. 构建 `mkeficapsule` 主机工具。
3. 将多个固件负载（U-Boot、TZ、XBL 等）打包成单个 UEFI 胶囊（可签名或不签名）。
4. 将胶囊放置到 EFI 系统分区（ESP）上，通过设置 `OsIndications` 触发更新，
   然后重启以应用更新。
5. 验证结果。

## 支持的 SoC

以下 SoC 的流程完全相同，只有 `defconfig` 不同。

| SoC     | defconfig                |
| ------- | ------------------------ |
| QCS6490 | `qcm6490_defconfig`      |
| IQ-9075 | `qcom_lemans_defconfig`  |
| IQ-615  | `qcom_qcs615_defconfig`  |
| Monaco  | `qcom_qcs8300_defconfig` |
| Shikra  | `qcom_shikra_defconfig`  |

## 查询支持的镜像

刷写 U-Boot，停在 U-Boot 控制台，然后运行：

```text theme={null}
=> efidebug capsule images
```

此命令会遍历每个固件管理协议（Firmware Management Protocol，FMP）实例，并为
开发板暴露的每个可更新镜像打印一行：

```text theme={null}
Image Index    Firmware Name         Image Type GUID
===========    ===================== ====================================
          1    UBOOT_UEFI_PARTITION  400ffdcd-22e0-47e7-9a23-f16ed9382388
          2    QCOM-XBL              dea0ba2c-cbdd-4805-b4f9-f428251c3e98
          3    QCOM-TZ               a053aa7f-40b8-4b1c-ba08-2f68ac71a4f4
          ...
```

该列表因开发板而异（根据该开发板的 GPT 构建），因此并非每块开发板都会显示所有
组件。只有出现在此列表中的镜像才能在该开发板上通过胶囊更新。

请记下此命令为每个组件报告的 `image-guid` 和 `image-index` 值——它们会在后续
章节的胶囊配置中使用。`image-guid` 决定应用更新时的目标分区。

## 构建 `mkeficapsule` 工具

首先，获取源码：

```bash theme={null}
# Download the U-Boot source code
git clone https://github.com/qualcomm-linux/u-boot.git

# Go to the U-Boot source directory
cd u-boot

# Switch to the qcom-next branch
git checkout qcom-next
```

然后将主机工具构建到 `.output` 文件夹中：

```bash theme={null}
make O=.output tools-only_defconfig
make O=.output tools
```

生成的二进制文件位于 `.output/tools/mkeficapsule`。下文所有命令均通过路径调用它。

## 设置胶囊文件夹

为开发板创建一个具有以下结构的工作文件夹：

```text theme={null}
generate_capsules/
  capsule_cfg.txt
  Images/
    u-boot.mbn
    tz.mbn
    xbl.elf
    ...
```

* `capsule_cfg.txt` — 胶囊配置文件（请参阅[胶囊配置文件格式](#capsule-config-file-format)）。
* `Images/` — 固件负载。

只包含需要更新的镜像的负载。每个负载都必须出现在 `efidebug capsule images` 的
输出中。

## 胶囊配置文件格式

配置由一系列 `{ … }` 块组成，每个块是一组 `key: value` 键值对。一个块描述一个
负载。

| 键                   | 含义                                                                        |
| ------------------- | ------------------------------------------------------------------------- |
| `image-guid`        | 来自 `efidebug capsule images` 的 Image Type GUID。用于将负载与其目标分区匹配的键。           |
| `image-index`       | 镜像索引——设置为 `efidebug capsule images` 为该组件报告的值。匹配是按 GUID 进行的，因此该值只需能标识组件即可。 |
| `fw-version`        | 此负载的固件版本。                                                                 |
| `payload`           | 负载二进制文件的路径（相对于工具运行位置）。                                                    |
| `capsule`           | 输出的胶囊文件名。                                                                 |
| `hardware-instance` | 硬件实例（通常为 `0`）。                                                            |
| `monotonic-count`   | 单调计数（签名胶囊）。                                                               |
| `private-key`       | 签名私钥的路径（仅签名胶囊）。                                                           |
| `pub-key-cert`      | 签名公共证书的路径（仅签名胶囊）。                                                         |

**分组规则**：共享同一 `capsule:` 文件名的负载会被打包成一个多镜像胶囊。不同的
文件名会生成不同的胶囊。

`test/py/tests/test_efi_capsule/sandbox_capsule_cfg.txt` 中提供了一份可作为模板
的现成示例配置。

### 未签名的多镜像胶囊

以下示例使用 `efidebug capsule images` 报告的值。`image-guid` 决定目标分区；
请将每个 `image-index` 设置为该命令为对应组件报告的值：

```text theme={null}
{
    image-guid: 400ffdcd-22e0-47e7-9a23-f16ed9382388
    image-index: 1
    hardware-instance: 0
    fw-version: 1
    payload: Images/u-boot.mbn
    capsule: Unsigned.cap
}
{
    image-guid: dea0ba2c-cbdd-4805-b4f9-f428251c3e98
    image-index: 2
    hardware-instance: 0
    fw-version: 1
    payload: Images/xbl.elf
    capsule: Unsigned.cap
}
{
    image-guid: a053aa7f-40b8-4b1c-ba08-2f68ac71a4f4
    image-index: 3
    hardware-instance: 0
    fw-version: 1
    payload: Images/tz.mbn
    capsule: Unsigned.cap
}
...
```

三个块都使用 `capsule: Unsigned.cap`，因此该工具会把三个负载都打包到这一个
文件中。

### 签名的多镜像胶囊

首先，使用 OpenSSL 生成签名密钥和自签名公共证书。私钥用于签署每个负载；证书会
嵌入 U-Boot 镜像中（请参阅[为签名胶囊启用身份验证](#enable-authentication-for-signed-capsules)），
并在开发板上用于验证签名：

```bash theme={null}
openssl req -x509 -sha256 -newkey rsa:2048 -subj "/CN=Capsule Signing Key/" -keyout capsule_priv.key -out capsule_pub.crt -nodes -days 365
```

然后向每个块添加 `private-key:` / `pub-key-cert:`（以及 `monotonic-count:`），
指向上面生成的密钥和证书：

```text theme={null}
{
    image-guid: 400ffdcd-22e0-47e7-9a23-f16ed9382388
    image-index: 1
    hardware-instance: 0
    fw-version: 1
    monotonic-count: 1
    payload: Images/u-boot.mbn
    private-key: capsule_priv.key
    pub-key-cert: capsule_pub.crt
    capsule: Signed.cap
}
{
    image-guid: dea0ba2c-cbdd-4805-b4f9-f428251c3e98
    image-index: 2
    hardware-instance: 0
    fw-version: 1
    monotonic-count: 1
    payload: Images/xbl.elf
    private-key: capsule_priv.key
    pub-key-cert: capsule_pub.crt
    capsule: Signed.cap
}
{
    image-guid: a053aa7f-40b8-4b1c-ba08-2f68ac71a4f4
    image-index: 3
    hardware-instance: 0
    fw-version: 1
    monotonic-count: 1
    payload: Images/tz.mbn
    private-key: capsule_priv.key
    pub-key-cert: capsule_pub.crt
    capsule: Signed.cap
}
...
```

<Note>
  在开发板上验证签名胶囊，还要求 U-Boot 镜像在构建时启用了胶囊身份验证并包含匹配
  的公共证书。请参阅[为签名胶囊启用身份验证](#enable-authentication-for-signed-capsules)。
</Note>

## 生成胶囊

在开发板文件夹内运行该工具，以便 `payload:`/`capsule:` 的相对路径能够正确解析：

```bash theme={null}
cd generate_capsules
../.output/tools/mkeficapsule -f capsule_cfg.txt
```

对于多镜像配置，输出如下：

```text theme={null}
Generating multi-payload capsule: Unsigned.cap (3 payloads)
Multi-payload capsule created successfully: Unsigned.cap
  Total size: <N> bytes
  Payloads: 3
```

通过转储胶囊的头部来验证其结构：

```bash theme={null}
../.output/tools/mkeficapsule --dump-capsule Unsigned.cap
```

确认 `EFI_FMP_HDR.PAYLOAD_ITEM_COUNT` 等于负载数量，并且每个
`FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID` / `UPDATE_IMAGE_INDEX` 与配置匹配。
对于签名胶囊，每个负载还会额外显示一个 `EFI_FIRMWARE_IMAGE_AUTH` 块。

## 为签名胶囊启用身份验证

<Note>
  对于未签名胶囊，请跳过本节。出厂的开发板镜像已启用 `EFI_CAPSULE_ON_DISK` 和
  `EFI_CAPSULE_FIRMWARE_RAW`，因此可以直接前往
  [将胶囊放置到 ESP](#get-the-capsule-onto-the-esp)。
</Note>

签名胶囊要求 U-Boot 镜像启用胶囊身份验证，而该功能默认未启用，因此必须在应用
胶囊之前完成以下操作：

1. 克隆 `qualcomm-linux/u-boot` 仓库并切换到 `qcom-next`（如果
   [构建 `mkeficapsule` 工具](#build-the-mkeficapsule-tool)中的检出已存在，可跳过）：

   ```bash theme={null}
   git clone https://github.com/qualcomm-linux/u-boot.git
   cd u-boot
   git checkout qcom-next
   ```

2. 将生成的公共证书（`capsule_pub.crt`）复制到 U-Boot 源码树中，然后将胶囊
   身份验证选项添加到开发板的 defconfig
   （`configs/<board>_defconfig`，例如 `configs/qcom_lemans_defconfig`），并将
   `CONFIG_EFI_CAPSULE_CRT_FILE` 指向该证书（相对于 U-Boot 源码树的路径）：

   ```text theme={null}
   CONFIG_EFI_CAPSULE_AUTHENTICATE=y
   CONFIG_EFI_CAPSULE_CRT_FILE="<path-to>/capsule_pub.crt"
   ```

3. 构建 U-Boot（请参阅 [Qualcomm Linux 上的 U-Boot](./u-boot-on-qualcomm-linux)），
   并将生成的镜像刷写到开发板的 `uefi` 分区。

<Note>
  使用与嵌入证书不匹配的密钥签名的胶囊会在应用时被拒绝。
</Note>

## 将胶囊放置到 ESP

将 `.cap` 文件放到 ESP 上的 `\EFI\UpdateCapsule\` 中。使用开发板支持的任意传输
机制将文件复制到该位置——例如，挂载 ESP
（`mount /dev/disk/by-partlabel/efi /mnt/esp`），如果 `EFI/UpdateCapsule` 目录
不存在则创建它，然后将胶囊复制进去。

只有当 `OsIndications` 设置了
`EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED`（`0x4`）位时，
capsule-on-disk 才会运行。U-Boot（`lib/efi_loader/efi_capsule.c` 中的
`check_run_capsules()`）会在应用后清除该位，因此每次应用前都需要重新设置。
下次启动时，capsule-on-disk 会自动发现 `\EFI\UpdateCapsule\*.cap`，应用所有
负载，清除该文件，并清除 `OsIndications` 位——无需任何 U-Boot 控制台命令来
触发它。

胶囊就位后，从内核提示符触发更新并重启：

```sh theme={null}
# Arm: set OsIndications bit 0x4 (64-bit LE value 0x0000000000000004).
# -f is raw variable data (no attribute header); -A 0x7 = NV|BS|RT, the set
# U-Boot uses. efivar persists it to ubootefi.var on the ESP via VarToFile.
printf '\x04\x00\x00\x00\x00\x00\x00\x00' > /tmp/osindications.bin
efivar -w -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-OsIndications \
       -f /tmp/osindications.bin -A 0x7
efivar -p -n 8be4df61-93ca-11d2-aa0d-00e098032b8c-OsIndications   # verify

sync
reboot
```

## 验证结果

应用更新的那次启动完成后，在 U-Boot 控制台执行：

```text theme={null}
=> efidebug capsule esrt
```

针对每个已更新的镜像，确认 `fw_version` 已提升为配置中的 `fw-version`，并且
`last_attempt_status = success`。

```text theme={null}
=> efidebug capsule result
```

显示上次应用的每个胶囊的结果码。

```text theme={null}
=> efidebug capsule images
```

再次确认镜像集合，并反映已更新的版本。
