Sughosh Ganu | f4d1594 | 2020-12-30 19:27:12 +0530 | [diff] [blame] | 1 | .. SPDX-License-Identifier: GPL-2.0+ |
| 2 | .. Copyright (C) 2020, Linaro Limited |
| 3 | |
| 4 | Enabling UEFI Capsule Update feature |
| 5 | ------------------------------------ |
| 6 | |
| 7 | Support has been added for the UEFI capsule update feature which |
| 8 | enables updating the U-Boot image using the UEFI firmware management |
| 9 | protocol (fmp). The capsules are not passed to the firmware through |
| 10 | the UpdateCapsule runtime service. Instead, capsule-on-disk |
| 11 | functionality is used for fetching the capsule from the EFI System |
| 12 | Partition (ESP) by placing the capsule file under the |
| 13 | \EFI\UpdateCapsule directory. |
| 14 | |
| 15 | Currently, support has been added on the QEMU ARM64 virt platform for |
| 16 | updating the U-Boot binary as a raw image when the platform is booted |
| 17 | in non-secure mode, i.e. with CONFIG_TFABOOT disabled. For this |
| 18 | configuration, the QEMU platform needs to be booted with |
| 19 | 'secure=off'. The U-Boot binary placed on the first bank of the NOR |
| 20 | flash at offset 0x0. The U-Boot environment is placed on the second |
| 21 | NOR flash bank at offset 0x4000000. |
| 22 | |
| 23 | The capsule update feature is enabled with the following configuration |
| 24 | settings:: |
| 25 | |
| 26 | CONFIG_MTD=y |
| 27 | CONFIG_FLASH_CFI_MTD=y |
| 28 | CONFIG_CMD_MTDPARTS=y |
| 29 | CONFIG_CMD_DFU=y |
| 30 | CONFIG_DFU_MTD=y |
| 31 | CONFIG_PCI_INIT_R=y |
| 32 | CONFIG_EFI_CAPSULE_ON_DISK=y |
| 33 | CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT=y |
| 34 | CONFIG_EFI_CAPSULE_FIRMWARE=y |
| 35 | CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y |
| 36 | CONFIG_EFI_CAPSULE_FMP_HEADER=y |
| 37 | |
| 38 | In addition, the following config needs to be disabled(QEMU ARM specific):: |
| 39 | |
| 40 | CONFIG_TFABOOT |
| 41 | |
| 42 | The capsule file can be generated by using the GenerateCapsule.py |
| 43 | script in EDKII:: |
| 44 | |
| 45 | $ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \ |
| 46 | <capsule_file_name> --fw-version <val> --lsv <val> --guid \ |
| 47 | e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose --update-image-index \ |
| 48 | <val> --verbose <u-boot.bin> |
| 49 | |
| 50 | The above is a wrapper script(GenerateCapsule) which eventually calls |
| 51 | the actual GenerateCapsule.py script. |
| 52 | |
| 53 | As per the UEFI specification, the capsule file needs to be placed on |
| 54 | the EFI System Partition, under the \EFI\UpdateCapsule directory. The |
| 55 | EFI System Partition can be a virtio-blk-device. |
| 56 | |
| 57 | Before initiating the firmware update, the efi variables BootNext, |
| 58 | BootXXXX and OsIndications need to be set. The BootXXXX variable needs |
| 59 | to be pointing to the EFI System Partition which contains the capsule |
| 60 | file. The BootNext, BootXXXX and OsIndications variables can be set |
| 61 | using the following commands:: |
| 62 | |
| 63 | => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name> |
| 64 | => efidebug boot next 0 |
| 65 | => setenv -e -nv -bs -rt -v OsIndications =0x04 |
| 66 | => saveenv |
| 67 | |
| 68 | Finally, the capsule update can be initiated with the following |
| 69 | command:: |
| 70 | |
| 71 | => efidebug capsule disk-update |
| 72 | |
| 73 | The updated U-Boot image will be booted on subsequent boot. |
| 74 | |
| 75 | Enabling Capsule Authentication |
| 76 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 77 | |
| 78 | The UEFI specification defines a way of authenticating the capsule to |
| 79 | be updated by verifying the capsule signature. The capsule signature |
| 80 | is computed and prepended to the capsule payload at the time of |
| 81 | capsule generation. This signature is then verified by using the |
| 82 | public key stored as part of the X509 certificate. This certificate is |
| 83 | in the form of an efi signature list (esl) file, which is embedded as |
| 84 | part of the platform's device tree blob using the mkeficapsule |
| 85 | utility. |
| 86 | |
| 87 | On the QEMU virt platforms, the device-tree is generated on the fly |
| 88 | based on the devices configured. This device tree is then passed on to |
| 89 | the various software components booting on the platform, including |
| 90 | U-Boot. Therefore, on the QEMU virt platform, the signatute is |
| 91 | embedded on an overlay. This overlay is then applied at runtime to the |
| 92 | base platform device-tree. Steps needed for embedding the esl file in |
| 93 | the overlay are highlighted below. |
| 94 | |
| 95 | The capsule authentication feature can be enabled through the |
| 96 | following config, in addition to the configs listed above for capsule |
| 97 | update:: |
| 98 | |
| 99 | CONFIG_EFI_CAPSULE_AUTHENTICATE=y |
| 100 | |
| 101 | The public and private keys used for the signing process are generated |
| 102 | and used by the steps highlighted below:: |
| 103 | |
| 104 | 1. Install utility commands on your host |
| 105 | * OPENSSL |
| 106 | * efitools |
| 107 | |
| 108 | 2. Create signing keys and certificate files on your host |
| 109 | |
| 110 | $ openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=CRT/ \ |
| 111 | -keyout CRT.key -out CRT.crt -nodes -days 365 |
| 112 | $ cert-to-efi-sig-list CRT.crt CRT.esl |
| 113 | |
| 114 | $ openssl x509 -in CRT.crt -out CRT.cer -outform DER |
| 115 | $ openssl x509 -inform DER -in CRT.cer -outform PEM -out CRT.pub.pem |
| 116 | |
| 117 | $ openssl pkcs12 -export -out CRT.pfx -inkey CRT.key -in CRT.crt |
| 118 | $ openssl pkcs12 -in CRT.pfx -nodes -out CRT.pem |
| 119 | |
| 120 | The capsule file can be generated by using the GenerateCapsule.py |
| 121 | script in EDKII:: |
| 122 | |
| 123 | $ ./BaseTools/BinWrappers/PosixLike/GenerateCapsule -e -o \ |
| 124 | <capsule_file_name> --monotonic-count <val> --fw-version \ |
| 125 | <val> --lsv <val> --guid \ |
| 126 | e2bb9c06-70e9-4b14-97a3-5a7913176e3f --verbose \ |
| 127 | --update-image-index <val> --signer-private-cert \ |
| 128 | /path/to/CRT.pem --trusted-public-cert \ |
| 129 | /path/to/CRT.pub.pem --other-public-cert /path/to/CRT.pub.pem \ |
| 130 | <u-boot.bin> |
| 131 | |
| 132 | Place the capsule generated in the above step on the EFI System |
| 133 | Partition under the EFI/UpdateCapsule directory |
| 134 | |
| 135 | For embedding the public key certificate, the following steps need to |
| 136 | be followed:: |
| 137 | |
| 138 | 1. Generate a skeleton overlay dts file, with a single fragment |
| 139 | node and an empty __overlay__ node |
| 140 | |
| 141 | A typical skeleton overlay file will look like this |
| 142 | |
| 143 | /dts-v1/; |
| 144 | /plugin/; |
| 145 | |
| 146 | / { |
| 147 | fragment@0 { |
| 148 | target-path = "/"; |
| 149 | __overlay__ { |
| 150 | }; |
| 151 | }; |
| 152 | }; |
| 153 | |
| 154 | |
| 155 | 2. Convert the dts to a corresponding dtb with the following |
| 156 | command |
| 157 | ./scripts/dtc/dtc -@ -I dts -O dtb -o <ov_dtb_file_name> \ |
| 158 | <dts_file> |
| 159 | |
| 160 | 3. Run the dtb file generated above through the mkeficapsule tool |
| 161 | in U-Boot |
| 162 | ./tools/mkeficapsule -O <pub_key.esl> -D <ov_dtb> |
| 163 | |
| 164 | Running the above command results in the creation of a 'signature' |
| 165 | node in the dtb, under which the public key is stored as a |
| 166 | 'capsule-key' property. The '-O' option is to be used since the |
| 167 | public key certificate(esl) file is being embedded in an overlay. |
| 168 | |
| 169 | The dtb file embedded with the certificate is now to be placed on an |
| 170 | EFI System Partition. This would then be loaded and "merged" with the |
| 171 | base platform flattened device-tree(dtb) at runtime. |
| 172 | |
| 173 | Build U-Boot with the following steps(QEMU ARM64):: |
| 174 | |
| 175 | $ make qemu_arm64_defconfig |
| 176 | $ make menuconfig |
| 177 | Disable CONFIG_TFABOOT |
| 178 | Enable CONFIG_EFI_CAPSULE_AUTHENTICATE |
| 179 | Enable all configs needed for capsule update(listed above) |
| 180 | $ make all |
| 181 | |
| 182 | Boot the platform and perform the following steps on the U-Boot |
| 183 | command line:: |
| 184 | |
| 185 | 1. Enable capsule authentication by setting the following env |
| 186 | variable |
| 187 | |
| 188 | => setenv capsule_authentication_enabled 1 |
| 189 | => saveenv |
| 190 | |
| 191 | 2. Load the overlay dtb to memory and merge it with the base fdt |
| 192 | |
| 193 | => fatload virtio 0:1 <$fdtovaddr> EFI/<ov_dtb_file> |
| 194 | => fdt addr $fdtcontroladdr |
| 195 | => fdt resize <size_of_ov_dtb_file> |
| 196 | => fdt apply <$fdtovaddr> |
| 197 | |
| 198 | 3. Set the following environment and UEFI boot variables |
| 199 | |
| 200 | => setenv -e -nv -bs -rt -v OsIndications =0x04 |
| 201 | => efidebug boot add 0 Boot0000 virtio 0:1 <capsule_file_name> |
| 202 | => efidebug boot next 0 |
| 203 | => saveenv |
| 204 | |
| 205 | 4. Finally, the capsule update can be initiated with the following |
| 206 | command |
| 207 | |
| 208 | => efidebug capsule disk-update |
| 209 | |
| 210 | On subsequent reboot, the platform should boot the updated U-Boot binary. |