diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index d7513d1..4bac194 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -15,6 +15,7 @@ jobs: # Change the below to match the target(s) you want to compile the project for. - NUCLEO_H743ZI2 - K64F + - MIMXRT1060_EVK steps: - name: Checkout @@ -37,5 +38,5 @@ jobs: - name: Build project for ${{ matrix.mbed_target }} run: | mkdir build && cd build - cmake .. -GNinja -DMBED_TARGET=${{ matrix.mbed_target }} -DMCUBOOT_SIGNING_KEY=signing-keys.pem + cmake .. -GNinja -DMBED_TARGET=${{ matrix.mbed_target }} -DUPLOAD_METHOD=NONE -DMCUBOOT_SIGNING_KEY=signing-keys.pem ninja diff --git a/README.md b/README.md index ecfa230..32e133b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ If configured as such, mcuboot can perform a "swap" update where it will copy th To perform this kind of swap update, mcuboot requires a non-volatile "scratch" space in memory to store pieces of application code and update status information. This enables mcuboot to safely continue an update/revert procedure in the event of a power loss. +While the scratch region can be as small as one erase sector, making the scratch region larger will reduce wear on the scratch flash sectors when installing updates (because the entire image is swapped via the scratch sector as an intermediary). + The scratch region starting address may be specified with the configuration parameter, `mcuboot.scratch-address`. The size of the scratch space can be configured using `mcuboot.scratch-size` -- this value **must** be erase-sector aligned (ie: a multiple of the internal flash's eraseable size). For more advanced information about configuring the scratch space region, see the [mcuboot documentation on Image Slots](https://github.com/mcu-tools/mcuboot/blob/master/docs/design.md#image-slots). For more information on swap updates, see the [mcuboot documentation on Swap Updates](https://github.com/mcu-tools/mcuboot/blob/master/docs/design.md#image-swapping) @@ -119,7 +121,7 @@ $ git clone --recursive https://github.com/mbed-ce/mbed-mcuboot-bootloader.git Then, set up the GNU ARM toolchain (and other programs) on your machine using [the toolchain setup guide](https://github.com/mbed-ce/mbed-os/wiki/Toolchain-Setup-Guide). -Now, set up the CMake project for editing as you would normally. Make sure to configure an upload method. We have three ways to do this: +Now, set up the CMake project for editing as you would normally. Make sure to configure an upload method other than MBED, as that upload method does not allow flashing specific regions of memory by address. We have three ways to do this: - On the [command line](https://github.com/mbed-ce/mbed-os/wiki/Project-Setup:-Command-Line) - Using the [CLion IDE](https://github.com/mbed-ce/mbed-os/wiki/Project-Setup:-CLion) - Using the [VS Code IDE](https://github.com/mbed-ce/mbed-os/wiki/Project-Setup:-VS-Code) @@ -289,6 +291,142 @@ The simple application will do this when you press the button. If you reboot wi In real world situations, your application should run a self test routine to ensure it can receive updates in the future (eg: the UART software works as expected, the BLE stack initializes successfully, etc). +## Porting to New Devices +Currently, mcuboot only has existing configurations for a few Mbed targets. If you need to run mcuboot on a different target, you will need to create the appropriate configs. + +### Understanding your Target Memory Config +In order to create the memory bank config for your target, you will need to understand the layout of its flash bank(s). The easiest way to get this information is to watch the output from the first configure of Mbed. For example, on MIMXRT1060_EVK, I see this: +``` +Summary of available memory banks: +Target RAM banks: ----------------------------------------------------------- +0. SDRAM, start addr 0x80000000, size 256.0 MiB +1. SRAM_DTC, start addr 0x20000000, size 256.0 KiB +2. SRAM_ITC, start addr 0x00000000, size 128.0 KiB +3. SRAM_OC, start addr 0x20280000, size 128.0 KiB +4. SRAM_OC2, start addr 0x20200000, size 512.0 KiB + +Target ROM banks: ----------------------------------------------------------- +0. EXT_FLASH, start addr 0x60000000, size 8.0 MiB +``` + +We can see that there is exactly one ROM bank called EXT_FLASH, and its size is 8.0 MiB. If you see multiple ROM banks instead of one, you will need to consult your target MCU's manual and linker script to determine which one is used for the main application. + +The other piece of information that you need is the page and sector size of your ROM bank, and of the secondary block device if not using XIP mode. This will require a trip to your MCU datasheet, or the datasheet for the external flash for chips that don't have internal flash like the MIMXRT1060. Note that mcuboot *can* handle flashes with non-uniform sector sizes, but you will have to be extra careful about assigning things to sectors correctly. + +### Allocating Memory + +Now we need to decide what slot size we're going to use and allocate the space into regions. On the MIMXRT1060_EVK we have 8MiB of flash available (quite a lot!), split into 4k erase sectors and 256 byte programmable pages. We need to use that flash to store both the primary slot and the secondary slot. Let's say we also want to reserve a bit of space for using KVStore to store data in flash. So, let's go with: +- 128kiB (offset 0 - offset 0x20000) for the bootloader +- 3MiB for primary slot (offset 0x20000 - offset 0x320000) +- 3MiB for secondary slot (offset 0x320000 - offset 0x620000) +- 128kiB scratch space (offset 0x620000 - offset 0x640000) +- Remaining space (2MiB - 256k) for FlashIAPBlockDevice (offset 0x640000 - offset 0x800000) + +### Checking the Linker Script +Before building the project, you will need to check that your device linker script has been ported to use memory banks. Unfortunately, memory bank support is a more recent feature of Mbed and the majority of devices have not had their linker scripts updated to support it yet. + +To find the linker script for your target, you can watch the build output. You should see a line like this: +``` +Preprocess linker script: MIMXRT1052xxxxx.ld -> mbed-mimxrt1060-evk.link_script.ld +``` +This means the linker script for your target is called `MIMXRT1052xxxxx.ld`. Search for this file in the mbed-os source tree and open it. Look for a block like this: +``` +MEMORY +{ + + m_text (RX) : ORIGIN = MBED_CONFIGURED_ROM_BANK_EXT_FLASH_START, LENGTH = MBED_CONFIGURED_ROM_BANK_EXT_FLASH_SIZE + +``` + +You want the entry in MEMORY called `m_text` or `m_flash` or `m_rom` -- wherever the code for your device is stored. + +If the entry is defined in terms of `MBED_CONFIGURED_ROM_BANK` constants, this linker script has already been upgraded. If not, and it references `MBED_APP_START` and `MBED_APP_SIZE`, or it just uses constant addresses, then this target still needs to be upgraded. + +Please file a PR with mbed-ce/mbed-os asking for the linker script to be updated, or, if you are feeling adventurous, you can update the linker script yourself. Updates can be as simple as defining the text section in terms of the `MBED_CONFIGURED_ROM_BANK` constants, though there are also a number of other fixes that are useful for older linker scripts. + +### Setting Up Bootloader mbed_app.json + +Based on what we've learned, we would add a new entry in mbed_app.json like: +```js + "MIMXRT1060_EVK": { + "target.memory_bank_config": { + "EXT_FLASH": { + "size": 0x20000 + } + }, + + // Primary slot is 3MiB and begins right after the bootloader + "mcuboot.primary-slot-address": "0x60020000", + "mcuboot.slot-size": "0x300000", + + // Use flash for secondary slot as well + "secondary-slot-in-flash": true, + "secondary-slot-flash-start-addr": "0x60320000", + + // Store the scratch space at the end of flash + "mcuboot.scratch-address": "0x60620000", + "mcuboot.scratch-size": "0x20000", + + "mcuboot.read-granularity": 1, // Flash is byte addressable + + "mcuboot.max-img-sectors": 768, // Maximum flash sectors per slot. 3MiB/4kiB = 768. + "mcuboot.flash-block-size": 256 + }, +``` +Keep in mind that on MIMXRT, the flash base address in memory is 0x60000000, so we have to add that to all the offsets to get absolute addresses. + +Note: Be careful of the mcuboot.max-img-sectors setting! This needs to be the maximum number of sectors per slot in either the primary or secondary slot, whichever is greater. This means you need to check the sector size of your secondary block device too. + +Note: mcuboot defaults to a flash block size of 8, i.e. it will try to program flash in chunks as small as 8 bytes. The block size on this board is much larger, so we need to increase that constant. + +Now, you can flash the bootloader to your device using the steps above. Make sure that it boots and you see some activity on the serial port before continuing. You should see something like: +``` +[INFO][BL]: Starting MCUboot +[INFO][MCUb]: Primary image: magic=bad, swap_type=0x0, copy_done=0x2, image_ok=0x2 +[INFO][MCUb]: Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 +[INFO][MCUb]: Boot source: none +[INFO][MCUb]: Image index: 0, Swap type: none +[ERR ][BL]: Failed to locate firmware image, error: -1 +``` + +### Setting Up Application mbed_app.json +Now we can set up the demo app. Its mbed_app.json should look the same as the bootloader, *except* the `target.memory_bank_config` section. We want this application to go into the application region of the primary slot. So, we must set the start address to `mcuboot.primary-slot-address` + `mcuboot.header-size` (which defaults to 0x1000). Meanwhile, the size should be set to extend to the end of the primary slot (yes there are some TLVs after there, but the image tool will warn if there isn't enough space). + +So we'd add the following block: +```js + "MIMXRT1060_EVK": { + "target.memory_bank_config": { + "EXT_FLASH": { + "start": 0x60021000, // mcuboot.primary-slot-address + mcuboot.header-size + "size": 0x2FF000 // mcuboot.slot-size - mcuboot.header-size + } + }, + + // Primary slot is 3MiB and begins right after the bootloader + "mcuboot.primary-slot-address": "0x60020000", + "mcuboot.slot-size": "0x300000", + + // Use flash for secondary slot as well + "secondary-slot-in-flash": true, + "secondary-slot-flash-start-addr": "0x60320000", + + // Store the scratch space at the end of flash + "mcuboot.scratch-address": "0x60620000", + "mcuboot.scratch-size": "0x20000", + + "mcuboot.read-granularity": 1, // Flash is byte addressable + + "mcuboot.max-img-sectors": 768, // Maximum flash sectors per slot. 3MiB/4kiB = 768. + "mcuboot.flash-block-size": 256, + + "demo-button-active-low": true +} +``` + +Note that we also need to add `"demo-button-active-low": true` because the user button on this board is low when pressed. + +NOTE: Changes to the configured start address of flash generally require a CMake delete cache and reconfigure operation so that the MBED_UPLOAD_BASE_ADDR variable can be initialized with the right value. So, you will need to do that if you had previously configured the project. + ## Additional Features MCUboot integrates a number of additional features that are commonly used in bootloader situations. More details of these features can be found in the mcuboot documentation/code. diff --git a/mbed-os b/mbed-os index 156cd15..5887f9e 160000 --- a/mbed-os +++ b/mbed-os @@ -1 +1 @@ -Subproject commit 156cd15c50e356a0e81b7adc06ef87ec8f144909 +Subproject commit 5887f9e97e42fed221d4a9fcb5a48a4b4fb7a33e diff --git a/mbed_app.json5 b/mbed_app.json5 index f8911d9..ce45103 100644 --- a/mbed_app.json5 +++ b/mbed_app.json5 @@ -7,6 +7,10 @@ "secondary-slot-in-flash": { "help": "If enabled, store the secondary slot in the application flash immediately after the primary slot.", "value": false + }, + "secondary-slot-flash-start-addr": { + "help": "If secondary-slot-in-flash is enabled, this sets the start address of the secondary slot.", + "value": null } }, "target_overrides": { @@ -14,7 +18,7 @@ "platform.stdio-baud-rate": 115200, "target.c_lib": "small", "mcuboot.log-enable": true, - "mcuboot.log-level": "MCUBOOT_LOG_LEVEL_INFO", + "mcuboot.log-level": "MCUBOOT_LOG_LEVEL_INFO", // Change DEBUG to INFO for debug prints "mbed-trace.enable": true, "mbed-trace.max-level": "TRACE_LEVEL_DEBUG", "mbed-trace.fea-ipv6": false, @@ -85,6 +89,31 @@ "mcuboot.max-img-sectors": "1536" // External SD card has smaller sector size, so divide slot size by read granularity }, + "MIMXRT1060_EVK": { + "target.memory_bank_config": { + "EXT_FLASH": { + "size": 0x20000 + } + }, + + // Primary slot is 3MiB and begins right after the bootloader + "mcuboot.primary-slot-address": "0x60020000", + "mcuboot.slot-size": "0x300000", + + // Use flash for secondary slot as well + "secondary-slot-in-flash": true, + "secondary-slot-flash-start-addr": "0x60320000", + + // Store the scratch space at the end of flash + "mcuboot.scratch-address": "0x60620000", + "mcuboot.scratch-size": "0x20000", + + "mcuboot.read-granularity": 1, // Flash is byte addressable + + "mcuboot.max-img-sectors": 768, // Maximum flash sectors per slot. 3MiB/4kiB = 768. + "mcuboot.flash-block-size": 256 + }, + "MCU_STM32H743xI": { // Configure bootloader to live in the first sector of flash "target.memory_bank_config": { @@ -95,6 +124,7 @@ // Since STM32H743 boards have no external block device, keep everything in the MCU flash. "app.secondary-slot-in-flash": true, + "app.secondary-slot-flash-start-addr": "0x100000", // Slot size can be as big as 896k, since we need to reserve the first flash sector for the bootloader // and the last flash sector for scratch space diff --git a/mcuboot b/mcuboot index ce340d5..49ea56a 160000 --- a/mcuboot +++ b/mcuboot @@ -1 +1 @@ -Subproject commit ce340d5722ee4898137a1e8106988399db20bc53 +Subproject commit 49ea56a0009aa611bf2becceed85d59bbef05564 diff --git a/secondary_bd.cpp b/secondary_bd.cpp index 38e78be..dce104b 100644 --- a/secondary_bd.cpp +++ b/secondary_bd.cpp @@ -15,8 +15,8 @@ mbed::BlockDevice* get_secondary_bd(void) { - // Use a section of FlashIAP immediately after the secondary slot - static FlashIAPBlockDevice flashBD(MCUBOOT_PRIMARY_SLOT_START_ADDR + MCUBOOT_SLOT_SIZE, MCUBOOT_SLOT_SIZE); + // Use FlashIAP for the secondary BD. + static FlashIAPBlockDevice flashBD(MBED_CONF_APP_SECONDARY_SLOT_FLASH_START_ADDR, MCUBOOT_SLOT_SIZE); return &flashBD; } @@ -33,7 +33,7 @@ mbed::BlockDevice* get_secondary_bd(void) { // Otherwise it will return the flash IAP block device. mbed::BlockDevice* default_bd = mbed::BlockDevice::get_default_instance(); - // If this assert fails, there is no block def + // If this assert fails, there is no default block device defined for your board. MBED_ASSERT(default_bd != nullptr); // mcuboot assumes that the read size of the secondary block device is the same as the read size