Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 1 | # Building and using MCUboot with Zephyr |
| 2 | |
| 3 | MCUboot began its life as the bootloader for Mynewt. It has since |
| 4 | acquired the ability to be used as a bootloader for Zephyr as well. |
| 5 | There are some pretty significant differences in how apps are built |
| 6 | for Zephyr, and these are documented here. |
| 7 | |
Fabio Utzig | 4dce6aa | 2018-02-12 15:31:32 -0200 | [diff] [blame] | 8 | Please see the [design document](design.md) for documentation on the design |
| 9 | and operation of the bootloader itself. This functionality should be the same |
| 10 | on all supported RTOSs. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 11 | |
| 12 | The first step required for Zephyr is making sure your board has flash |
| 13 | partitions defined in its device tree. These partitions are: |
| 14 | |
| 15 | - `boot_partition`: for MCUboot itself |
Andrzej Puzdrowski | 7500156 | 2022-10-05 17:45:29 +0200 | [diff] [blame] | 16 | - `slot0_partition`: the primary slot of Image 0 |
| 17 | - `slot1_partition`: the secondary slot of Image 0 |
Jamie McCrae | 0b7b7ae | 2022-12-19 11:43:54 +0000 | [diff] [blame] | 18 | |
| 19 | It is not recommended to use the swap-using-scratch algorithm of MCUboot, but |
| 20 | if this operating mode is desired then the following flash partition is also |
| 21 | needed (see end of this help file for details on creating a scratch partition |
| 22 | and how to use the swap-using-scratch algorithm): |
| 23 | |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 24 | - `scratch_partition`: the scratch slot |
| 25 | |
| 26 | Currently, the two image slots must be contiguous. If you are running |
| 27 | MCUboot as your stage 1 bootloader, `boot_partition` must be configured |
David Vincze | ba3bd60 | 2019-06-17 16:01:43 +0200 | [diff] [blame] | 28 | so your SoC runs it out of reset. If there are multiple updateable images |
| 29 | then the corresponding primary and secondary partitions must be defined for |
Andrzej Puzdrowski | 7500156 | 2022-10-05 17:45:29 +0200 | [diff] [blame] | 30 | the rest of the images too (for example, `slot2_partition` and |
| 31 | `slot3_partition` for Image 1). |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 32 | |
| 33 | The flash partitions are typically defined in the Zephyr boards folder, in a |
| 34 | file named `boards/<arch>/<board>/<board>.dts`. An example `.dts` file with |
| 35 | flash partitions defined is the frdm_k64f's in |
Andrzej Puzdrowski | 7500156 | 2022-10-05 17:45:29 +0200 | [diff] [blame] | 36 | `boards/arm/frdm_k64f/frdm_k64f.dts`. Make sure the DT node labels in your board's |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 37 | `.dts` file match the ones used there. |
| 38 | |
Francesco Servidio | 5bc9832 | 2021-11-03 13:19:22 +0100 | [diff] [blame] | 39 | ## Installing requirements and dependencies |
Piotr Mienkowski | 8a474ff | 2018-08-07 21:31:33 +0200 | [diff] [blame] | 40 | |
Francesco Servidio | 4ff0c18 | 2021-10-20 15:27:16 +0200 | [diff] [blame] | 41 | Install additional packages required for development with MCUboot: |
Piotr Mienkowski | 8a474ff | 2018-08-07 21:31:33 +0200 | [diff] [blame] | 42 | |
| 43 | ``` |
Francesco Servidio | 4ff0c18 | 2021-10-20 15:27:16 +0200 | [diff] [blame] | 44 | cd ~/mcuboot # or to your directory where MCUboot is cloned |
Piotr Mienkowski | 8a474ff | 2018-08-07 21:31:33 +0200 | [diff] [blame] | 45 | pip3 install --user -r scripts/requirements.txt |
| 46 | ``` |
| 47 | |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 48 | ## Building the bootloader itself |
| 49 | |
| 50 | The bootloader is an ordinary Zephyr application, at least from |
| 51 | Zephyr's point of view. There is a bit of configuration that needs to |
| 52 | be made before building it. Most of this can be done as documented in |
| 53 | the `CMakeLists.txt` file in boot/zephyr. There are comments there for |
| 54 | guidance. It is important to select a signature algorithm, and decide |
David Vincze | 2d736ad | 2019-02-18 11:50:22 +0100 | [diff] [blame] | 55 | if the primary slot should be validated on every boot. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 56 | |
| 57 | To build MCUboot, create a build directory in boot/zephyr, and build |
| 58 | it as usual: |
| 59 | |
| 60 | ``` |
| 61 | cd boot/zephyr |
Andrzej Puzdrowski | 7500156 | 2022-10-05 17:45:29 +0200 | [diff] [blame] | 62 | west build -b <board> |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 63 | ``` |
| 64 | |
| 65 | In addition to the partitions defined in DTS, some additional |
| 66 | information about the flash layout is currently required to build |
| 67 | MCUboot itself. All the needed configuration is collected in |
| 68 | `boot/zephyr/include/target.h`. Depending on the board, this information |
| 69 | may come from board-specific headers, Device Tree, or be configured by |
| 70 | MCUboot on a per-SoC family basis. |
| 71 | |
| 72 | After building the bootloader, the binaries should reside in |
| 73 | `build/zephyr/zephyr.{bin,hex,elf}`, where `build` is the build |
Andrzej Puzdrowski | 7500156 | 2022-10-05 17:45:29 +0200 | [diff] [blame] | 74 | directory you chose when running `west build`. Use `west flash` |
| 75 | to flash these binaries from the build directory. Depending |
Carles Cufi | 5a9688a | 2018-04-03 17:10:18 +0200 | [diff] [blame] | 76 | on the target and flash tool used, this might erase the whole of the flash |
Francesco Servidio | 482921f | 2021-10-20 15:42:59 +0200 | [diff] [blame] | 77 | memory (mass erase) or only the sectors where the bootloader resides prior to |
Carles Cufi | 5a9688a | 2018-04-03 17:10:18 +0200 | [diff] [blame] | 78 | programming the bootloader image itself. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 79 | |
Francesco Servidio | 5bc9832 | 2021-11-03 13:19:22 +0100 | [diff] [blame] | 80 | ## Building applications for the bootloader |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 81 | |
| 82 | In addition to flash partitions in DTS, some additional configuration |
| 83 | is required to build applications for MCUboot. |
| 84 | |
Carles Cufi | efd783c | 2018-03-26 17:55:40 +0200 | [diff] [blame] | 85 | This is handled internally by the Zephyr configuration system and is wrapped |
| 86 | in the `CONFIG_BOOTLOADER_MCUBOOT` Kconfig variable, which must be enabled in |
| 87 | the application's `prj.conf` file. |
| 88 | |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 89 | The directory `samples/zephyr/hello-world` in the MCUboot tree contains |
| 90 | a simple application with everything you need. You can try it on your |
| 91 | board and then just make a copy of it to get started on your own |
| 92 | application; see samples/zephyr/README.md for a tutorial. |
| 93 | |
Carles Cufi | efd783c | 2018-03-26 17:55:40 +0200 | [diff] [blame] | 94 | The Zephyr `CONFIG_BOOTLOADER_MCUBOOT` configuration option |
Daniel Mangum | fefc398 | 2023-08-02 10:17:26 -0400 | [diff] [blame^] | 95 | [documentation](https://docs.zephyrproject.org/latest/kconfig.html#CONFIG_BOOTLOADER_MCUBOOT) |
Carles Cufi | efd783c | 2018-03-26 17:55:40 +0200 | [diff] [blame] | 96 | provides additional details regarding the changes it makes to the image |
Daniel Mangum | fefc398 | 2023-08-02 10:17:26 -0400 | [diff] [blame^] | 97 | placement and generation in order for an application to be bootable by MCUboot. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 98 | |
| 99 | With this, build the application as your normally would. |
| 100 | |
| 101 | ### Signing the application |
| 102 | |
| 103 | In order to upgrade to an image (or even boot it, if |
David Vincze | 2d736ad | 2019-02-18 11:50:22 +0100 | [diff] [blame] | 104 | `MCUBOOT_VALIDATE_PRIMARY_SLOT` is enabled), the images must be signed. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 105 | To make development easier, MCUboot is distributed with some example |
| 106 | keys. It is important to stress that these should never be used for |
| 107 | production, since the private key is publicly available in this |
| 108 | repository. See below on how to make your own signatures. |
| 109 | |
David Brown | 520e31c | 2018-04-05 14:38:08 -0600 | [diff] [blame] | 110 | Images can be signed with the `scripts/imgtool.py` script. It is best |
| 111 | to look at `samples/zephyr/Makefile` for examples on how to use this. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 112 | |
| 113 | ### Flashing the application |
| 114 | |
| 115 | The application itself can flashed with regular flash tools, but will |
David Vincze | 2d736ad | 2019-02-18 11:50:22 +0100 | [diff] [blame] | 116 | need to be programmed at the offset of the primary slot for this particular |
| 117 | target. Depending on the platform and flash tool you might need to manually |
| 118 | specify a flash offset corresponding to the primary slot starting address. This |
| 119 | is usually not relevant for flash tools that use Intel Hex images (.hex) instead |
| 120 | of raw binary images (.bin) since the former include destination address |
| 121 | information. Additionally you will need to make sure that the flash tool does |
| 122 | not perform a mass erase (erasing the whole of the flash) or else you would be |
| 123 | deleting MCUboot. |
| 124 | These images can also be marked for upgrade, and loaded into the secondary slot, |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 125 | at which point the bootloader should perform an upgrade. It is up to |
David Vincze | 2d736ad | 2019-02-18 11:50:22 +0100 | [diff] [blame] | 126 | the image to mark the primary slot as "image ok" before the next reboot, |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 127 | otherwise the bootloader will revert the application. |
| 128 | |
| 129 | ## Managing signing keys |
| 130 | |
| 131 | The signing keys used by MCUboot are represented in standard formats, |
| 132 | and can be generated and processed using conventional tools. However, |
David Brown | 520e31c | 2018-04-05 14:38:08 -0600 | [diff] [blame] | 133 | `scripts/imgtool.py` is able to generate key pairs in all of the |
| 134 | supported formats. See [the docs](imgtool.md) for more details on |
| 135 | this tool. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 136 | |
| 137 | ### Generating a new keypair |
| 138 | |
| 139 | Generating a keypair with imgtool is a matter of running the keygen |
| 140 | subcommand: |
| 141 | |
| 142 | ``` |
David Brown | 520e31c | 2018-04-05 14:38:08 -0600 | [diff] [blame] | 143 | $ ./scripts/imgtool.py keygen -k mykey.pem -t rsa-2048 |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 144 | ``` |
| 145 | |
| 146 | The argument to `-t` should be the desired key type. See the |
David Brown | 520e31c | 2018-04-05 14:38:08 -0600 | [diff] [blame] | 147 | [the docs](imgtool.md) for more details on the possible key types. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 148 | |
| 149 | ### Extracting the public key |
| 150 | |
| 151 | The generated keypair above contains both the public and the private |
| 152 | key. It is necessary to extract the public key and insert it into the |
Andrzej Puzdrowski | a6a5c08 | 2022-10-05 18:14:42 +0200 | [diff] [blame] | 153 | bootloader. Use the ``CONFIG_BOOT_SIGNATURE_KEY_FILE`` Kconfig option to |
| 154 | provide the path to the key file so the build system can extract |
| 155 | the public key in a format usable by the C compiler. |
| 156 | The generated public key is saved in `build/zephyr/autogen-pubkey.h`, which is included |
| 157 | by the `boot/zephyr/keys.c`. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 158 | |
Andrzej Puzdrowski | a6a5c08 | 2022-10-05 18:14:42 +0200 | [diff] [blame] | 159 | Currently, the Zephyr RTOS port limits its support to one keypair at the time, |
| 160 | although MCUboot's key management infrastructure supports multiple keypairs. |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 161 | |
Andrzej Puzdrowski | a6a5c08 | 2022-10-05 18:14:42 +0200 | [diff] [blame] | 162 | Once MCUboot is built, this new keypair file (`mykey.pem` in this |
Carles Cufi | ecc34bb | 2018-01-22 18:02:46 +0100 | [diff] [blame] | 163 | example) can be used to sign images. |
Jamie McCrae | 0b7b7ae | 2022-12-19 11:43:54 +0000 | [diff] [blame] | 164 | |
| 165 | ## Using swap-using-scratch flash algorithm |
| 166 | |
| 167 | To use the swap-using-scratch flash algorithm, a scratch partition needs to be |
| 168 | present for the target board which is used for holding the data being swapped |
| 169 | from both slots, this section must be at least as big as the largest sector |
| 170 | size of the 2 partitions (e.g. if a device has a primary slot in main flash |
| 171 | with a sector size of 512 bytes and secondar slot in external off-chip flash |
| 172 | with a sector size of 4KB then the scratch area must be at least 4KB in size). |
| 173 | The number of sectors must also be evenly divisable by this sector size, e.g. |
| 174 | 4KB, 8KB, 12KB, 16KB are allowed, 7KB, 7.5KB are not. This scratch partition |
| 175 | needs adding to the .dts file for the board, e.g. for the nrf52dk_nrf52832 |
| 176 | board thus would involve updating |
| 177 | `<zephyr>/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts` with: |
| 178 | |
| 179 | ``` |
| 180 | boot_partition: partition@0 { |
| 181 | label = "mcuboot"; |
| 182 | reg = <0x00000000 0xc000>; |
| 183 | }; |
| 184 | slot0_partition: partition@c000 { |
| 185 | label = "image-0"; |
| 186 | reg = <0x0000C000 0x37000>; |
| 187 | }; |
| 188 | slot1_partition: partition@43000 { |
| 189 | label = "image-1"; |
| 190 | reg = <0x00043000 0x37000>; |
| 191 | }; |
| 192 | storage_partition: partition@7a000 { |
| 193 | label = "storage"; |
| 194 | reg = <0x0007a000 0x00006000>; |
| 195 | }; |
| 196 | ``` |
| 197 | |
| 198 | Which would make the application size 220KB and scratch size 24KB (the nRF52832 |
| 199 | has a 4KB sector size so the size of the scratch partition can be reduced at |
| 200 | the cost of vastly reducing flash lifespan, e.g. for a 32KB firmware update |
| 201 | with an 8KB scratch area, the scratch area would be erased and programmed 8 |
| 202 | times per image upgrade/revert). To configure MCUboot to work in |
| 203 | swap-using-scratch mode, the Kconfig value must be set when building it: |
| 204 | `CONFIG_BOOT_SWAP_USING_SCRATCH=y`. |
| 205 | |
| 206 | Note that it is possible for an application to get into a stuck state when |
| 207 | swap-using-scratch is used whereby an application has loaded a firmware update |
| 208 | and marked it as test/confirmed but MCUboot will not swap the images and |
| 209 | erasing the secondary slot from the zephyr application returns an error |
| 210 | because the slot is marked for upgrade. |
Andrzej Puzdrowski | cdf9de0 | 2022-10-05 14:02:03 +0200 | [diff] [blame] | 211 | |
| 212 | ## Serial recovery |
| 213 | |
| 214 | ### Interface selection |
| 215 | |
| 216 | A serial recovery protocol is available over either a hardware serial port or a USB CDC ACM virtual serial port. |
| 217 | The SMP server implementation can be enabled by the ``CONFIG_MCUBOOT_SERIAL=y`` Kconfig option. |
| 218 | To set a type of an interface, use the ``BOOT_SERIAL_DEVICE`` Kconfig choice, and select either the ``CONFIG_BOOT_SERIAL_UART`` or the ``CONFIG_BOOT_SERIAL_CDC_ACM`` value. |
| 219 | Which interface belongs to the protocol shall be set by the devicetree-chosen node: |
| 220 | - `zephyr,console` - If a hardware serial port is used. |
| 221 | - `zephyr,cdc-acm-uart` - If a virtual serial port is used. |
| 222 | |
| 223 | ### Entering the serial recovery mode |
| 224 | |
| 225 | To enter the serial recovery mode, the device has to initiate rebooting, and a triggering event has to occur (for example, pressing a button). |
| 226 | |
| 227 | By default, the serial recovery GPIO pin active state enters the serial recovery mode. |
| 228 | Use the ``mcuboot_button0`` devicetree button alias to assign the GPIO pin to the MCUboot. |
| 229 | |
| 230 | Alternatively, MCUboot can wait for a limited time to check if DFU is invoked by receiving an MCUmgr command. |
| 231 | Select ``CONFIG_BOOT_SERIAL_WAIT_FOR_DFU=y`` to use this mode. ``CONFIG_BOOT_SERIAL_WAIT_FOR_DFU_TIMEOUT`` option defines |
| 232 | the amount of time in milliseconds the device will wait for the trigger. |
| 233 | |
| 234 | ### Direct image upload |
| 235 | |
| 236 | By default, the SMP server implementation will only use the first slot. |
| 237 | To change it, invoke the `image upload` MCUmgr command with a selected image number, and make sure the ``CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y`` Kconfig option is enabled. |
| 238 | Note that the ``CONFIG_UPDATEABLE_IMAGE_NUMBER`` Kconfig option adjusts the number of image-pairs supported by the MCUboot. |
| 239 | |
| 240 | The mapping of image number to partition is as follows: |
| 241 | * 0 and 1 - image-0, the primary slot of the first image. |
| 242 | * 2 - image-1, the secondary slot of the first image. |
| 243 | * 3 - image-2. |
| 244 | * 4 - image-3. |
| 245 | |
| 246 | 0 is a default upload target when no explicit selection is done. |
| 247 | |
| 248 | ### System-specific commands |
| 249 | |
| 250 | Use the ``CONFIG_ENABLE_MGMT_PERUSER=y`` Kconfig option to enable the following additional commands: |
| 251 | * Storage erase - This command allows erasing the storage partition (enable with ``CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y``). |
| 252 | * Custom image list - This command allows fetching version and installation status (custom properties) for all images (enable with ``CONFIG_BOOT_MGMT_CUSTOM_IMG_LIST=y``). |
| 253 | |
| 254 | ### In-place image decryption |
| 255 | |
| 256 | Images uploaded by the serial recovery can be decrypted on the fly by using ECIES primitives described in the [ECIES encryption](encrypted_images.md#ecies-encryption) section. |
| 257 | |
| 258 | Enable support for this feature by using ``CONFIG_BOOT_SERIAL_ENCRYPT_EC256=y``. |
| 259 | |
| 260 | ### More configuration |
| 261 | |
| 262 | For details on other available configuration options for the serial recovery protocol, check the Kconfig options (for example by using ``menuconfig``). |