# Zephyr RTOS - Note: FLASH Patition and MCUBoot [TOC] ## Introduction Flashing Zephyr is somewhat similiar to installing an operating on a computer. If you have experience installing popular desktop distro, you may be familiar with the concepts of partition of disks. You start with partitioning the disk, and then choose the partition Linux will be installed to. Zephyr has similiar concepts of partitions for FLASH. FLASH storage can be divide by different regions, defined by `partition` nodes in devicetree. These FLASH partitions can be used for bootloader, code binaries, image update, or embedded file system. ## Defining Partitions FLASH partitions are defined by `partition` nodes. See [Fixed flash partitions](https://docs.zephyrproject.org/latest/build/dts/api/api.html#devicetree-flash-api) in devicetree API and [fixed-partitions](https://docs.zephyrproject.org/latest/build/dts/api/bindings/mtd/fixed-partitions.html?highlight=fixed+partition) in devicetree binding documents. Typical partition schemes look like example in [Flash map](https://docs.zephyrproject.org/latest/services/storage/flash_map/flash_map.html#relationship-with-devicetree) API doc. ### Step 1: Partition FLASH Example in [Zephyr RTOS BringUp on stm32](https://yairgadelov.me/zephyr-rtos-bringup-on-stm32/) in Zephyr documentation offers a scheme can be readily used **on a board with 1MB FLASH**: ```python &flash0 { partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; boot_partition: partition@0 { label = "mcuboot"; reg = <0x00000000 0x00010000>; read-only; }; /* * The flash starting at 0x00010000 and ending at * 0x0001ffff (sectors 16-31) is reserved for use * by the application. */ storage_partition: partition@1e000 { label = "storage"; reg = <0x0001e000 0x00002000>; }; slot0_partition: partition@20000 { label = "image-0"; reg = <0x00020000 0x00060000>; }; slot1_partition: partition@80000 { label = "image-1"; reg = <0x00080000 0x00060000>; }; scratch_partition: partition@e0000 { label = "image-scratch"; reg = <0x000e0000 0x00020000>; }; }; }; ``` Partitions are defined by their `reg` properties. Each of them takes the form of `<start, size>`, where `start` defines starting point of a partition relative to base address of flash; `size` defines the size of a partition. For example, the `slot0_partition` has `reg` property `<0x00020000 0x00060000>`, meaning that it's a partition 1) starting at `0x00020000` from base address defined in original `flash0` node 2) with size of `0x00060000` bytes. For boards with less than 1MB FLASH storage, the above partition won't work, simply because it's out of range. However, it can serve as a template. For example, for `stm32f411re`, which has only 512 kB flash, wich means that the `start` + `size` must not exceed `0x80000`. With slight modification, similiar partition scheme can work on `stm32f411re`: ```python &flash0 { partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; boot_partition: partition@0 { label = "mcuboot"; reg = <0x00000000 0x00010000>; read-only; }; slot0_partition: partition@20000 { label = "image-0"; reg = <0x00020000 0x00020000>; }; slot1_partition: partition@40000 { label = "image-1"; reg = <0x00040000 0x00020000>; }; scratch_partition: partition@60000 { label = "image-scratch"; reg = <0x00060000 0x00020000>; }; }; }; ``` Save this devicetree overlay in a separate file (say, `partition.overlay`). We'll needed when compiling mcuboot and Zephyr image. ### Step 2: Select default booting partition in `chosen` ndoe Next step would be defining what image to boot into in chose node. More specific, the `zephyr,code-partition` property in `chosen` node: ```clike / { chosen { zephyr,flash-controller = &flash; zephyr,console = &usart2; zephyr,shell-uart = &usart2; zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; zephyr,uart-mcumgr = &usart2; }; }; ``` All in all, the `partition.overlay` will look like this: ```clike / { chosen { zephyr,flash-controller = &flash; zephyr,console = &usart2; zephyr,shell-uart = &usart2; zephyr,sram = &sram0; zephyr,flash = &flash0; zephyr,code-partition = &slot0_partition; zephyr,uart-mcumgr = &usart2; }; }; &flash0 { partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; boot_partition: partition@0 { label = "mcuboot"; reg = <0x00000000 0x00010000>; read-only; }; slot0_partition: partition@20000 { label = "image-0"; reg = <0x00020000 0x00020000>; }; slot1_partition: partition@40000 { label = "image-1"; reg = <0x00040000 0x00020000>; }; scratch_partition: partition@60000 { label = "image-scratch"; reg = <0x00060000 0x00020000>; }; }; }; ``` ### Step 3: Build MCUBoot according to FLASH partition ```shell $ west build -p \ -s $HOME/zephyrproject/bootloader/mcuboot/boot/zephyr \ -d build-mcuboot \ -b nucleo_f411re \ -- \ -DDTC_OVERLAY_FILE=`pwd`/partition.overlay ``` ``` Memory region Used Size Region Size %age Used FLASH: 38352 B 64 KB 58.52% SRAM: 24256 B 128 KB 18.51% IDT_LIST: 0 GB 2 KB 0.00% ``` ### Step 4: Flash MCUBoot ``` $ minicom --device tty.usbmodem2103 ``` Then flash built mcuboot: ``` $ west flash -d build-mcuboot ``` If succeded, `minicom` shows the following message. Since we haven't flash any image yet, mcuboot will complain `E: Unable to find bootable image`. ```clike *** Booting Zephyr OS build zephyr-v3.1.0-3162-gbe1380cc51bd *** I: Starting bootloader I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Boot source: primary slot I: Swap type: none E: Unable to find bootable image ``` Also note that if flash partitions are not done right (say, offering out-of-range address when defining partitions), mcuboot will complain about `E: Read range invalid.`: ```clike *** Booting Zephyr OS build zephyr-v3.1.0-3162-gbe1380cc51bd *** I: Starting bootloader E: Read range invalid. Offset: 786416, len: 16 I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Scratch: magic=good, swap_type=0x0, copy_done=0x0, image_ok=0x0 I: Boot source: scratch E: Read range invalid. Offset: 786381, len: 1 ``` ### Step 5: Build signed image according to FLASH partition For demonstrating purpose, default key in Zephyr repo is used here, which is located at `west` [topdir](https://docs.zephyrproject.org/latest/develop/west/basics.html#workspace-concepts): ```shell $ west build -p \ -d build-hello-signed \ -b nucleo_f411re \ -- \ -DDTC_OVERLAY_FILE=`pwd`/partition.overlay \ -DCONFIG_MCUBOOT_SIGNATURE_KEY_FILE=\"bootloader/mcuboot/root-rsa-2048.pem\" ``` If image is compiled successfully, `west` will show something like this: ``` Memory region Used Size Region Size %age Used FLASH: 14684 B 128 KB 11.20% SRAM: 4416 B 128 KB 3.37% IDT_LIST: 0 GB 2 KB 0.00% ``` Note that the **Region Size** of **FLASH** shall be equal to that of `zephyr,code-partition` in `chosen` node. For example, in `partition.overlay` the `slot0_partition` is of 128KB, so is the Region Size field of `west` output. If such partition is not selected, the **Region Size** will be the whole FLASH: ``` Memory region Used Size Region Size %age Used FLASH: 14684 B 512 KB 2.80% SRAM: 4416 B 128 KB 3.37% IDT_LIST: 0 GB 2 KB 0.00% ``` ### Step 6: Flash signed image ``` $ west flash -d build-hello-signed ``` If everything goes well, `minicom` will show something like this: ``` *** Booting Zephyr OS build zephyr-v3.1.0-3162-gbe1380cc51bd *** I: Starting bootloader I: Primary image: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 I: Boot source: primary slot I: Swap type: none I: Bootloader chainload address offset: 0x20000 !%%%??ѥ???Zephyr OS build zephyr-v3.1.0-3162-gbe1380cc51bd *** Hello World! nucleo_f411re ```