|
| 1 | +## STM32 Secure Patching Bootloader |
| 2 | + |
| 3 | +## Documentation Overview |
| 4 | + |
| 5 | +* [Graphic](stm32-secure-patching-bootloader-MultiSegment_rev1_Dec2021.pdf) describing MultiSegment feature in more detail. |
| 6 | +* [Quick Start Guide](stm32-secure-patching-bootloader-QSG_rev1_Dec2021.pdf) in PDF form with screenshots. |
| 7 | + |
| 8 | +## Quick Start Guide |
| 9 | + |
| 10 | +Integrating the stm32-secure-patching-bootloader is a simple four step process: adding bootloader files to your project repository, configuring STM32CubeIDE, adjusting your project's linker script .ld file and adjusting your project's system_stm32xxxx.c file. |
| 11 | + |
| 12 | +Please refer to [stm32-secure-patching-bootloader-demoapp](https://github.com/firmwaremodules/stm32-secure-patching-bootloader-demoapp) repository for working projects already implementing these steps. |
| 13 | + |
| 14 | +Also refer to this [Quick Start Guide](stm32-secure-patching-bootloader-QSG_rev1_Dec2021.pdf) PDF document for more details including images and screenshots. |
| 15 | + |
| 16 | +1. Adding bootloader files to your project repository |
| 17 | + |
| 18 | + * Create a directory called `Bootloader` in your project root. |
| 19 | + * Copy the directories `Include`, `Linker`, `Scripts`, `Tools` into it. |
| 20 | + * Copy the `Libs` directory, but only the board subdirectories that you are needing. E.g. copy DISCO-F769I directory into your Libs directory if you are using that board. |
| 21 | + * As an alternative, you can add the bootloader files as a git submodule with `git submodule add https://github.com/firmwaremodules/stm32-secure-patching-bootloader Bootloader`. |
| 22 | + |
| 23 | +2. Configure STM32CubeIDE. |
| 24 | + |
| 25 | +We need to add the postbuild command line, update the include and linker paths, and link with the stm32-secure-patching-bootloader application interface. |
| 26 | + |
| 27 | + * Update Post-build steps C/C++ Build -> Settings -> Build Steps |
| 28 | + |
| 29 | +``` |
| 30 | +"../../../../../../../Bootloader/Scripts/STM32CubeIDE/postbuild.sh" "${BuildArtifactFileBaseName}" "../../Keys" "../../../../../Binary" "1.0.0" "1.0.0" "NUCLEO-L073RZ" "1.0.0" 512 0 |
| 31 | + | | | | | | | | | | |
| 32 | + \- Name of postbuild script, relative to build dir | | | | | | | | | |
| 33 | + | | | | | | | | | |
| 34 | + \- Name of applcation build artifact (Build Artifact -> Artifact Name) | | | | | |
| 35 | + | | | | | | | | |
| 36 | + \- Directory where Keys are, relative to build dir, according to DemoApp reference design. |
| 37 | + | | | | | | | |
| 38 | + \- Directory where Outputs are placed and patch reference binaries are found, relative to build dir, according to DemoApp reference design. |
| 39 | + | | | | | | |
| 40 | + \- 'To' version (version to build) | | |
| 41 | + | | | | | |
| 42 | + \- 'From' version (reference for patch generation) |
| 43 | + | | | | |
| 44 | + \- bootloader library to link with (in Libs dir) |
| 45 | + | | | |
| 46 | + \- Version of bootloader library to link with (in Libs dir) |
| 47 | + | | |
| 48 | + \- Board/target specific vector table offset size. 512 unless noted in board_config properties file. |
| 49 | + | |
| 50 | + \- MultiSegment start address. 0x90000000 typical or 0 if MultiSegment is not used. |
| 51 | + |
| 52 | +``` |
| 53 | + |
| 54 | + * Add path to bootloader include: C/C++ Build -> Settings -> MCU GCC Compiler -> Include paths : `../../../../../../../Bootloader/Include` |
| 55 | + |
| 56 | + * Add paths and links to bootloader library files: C/C++ Build -> Settings -> MCU GCC Linker -> Libraries : (Libraries) `:stm32-secure-patching-bootloader-interface-gcc_NUCLEO-L073RZ_v1.0.0.o` (Library search paths) `../../../../../../../Bootloader/Libs/NUCLEO-L073RZ` |
| 57 | + |
| 58 | + * Add paths to bootloader linker files: C/C++ Build -> Settings -> MCU GCC Linker -> Miscellaneous : `-Xlinker -L ../../../../../../../Bootloader/Libs/NUCLEO-L073RZ -L ../../../../../../../Bootloader/Linker` |
| 59 | + |
| 60 | +3. Adjust your application's linker script. |
| 61 | + |
| 62 | +At minimum we need to tell the linker where your application's new start offset is in flash. This is defined by the bootloader's linker configuration file `stm32-secure-patching-bootloader-linker-gcc_<BOARD>_<version>.ld`. |
| 63 | +It defines some symbols that we may need, minimally it is `STM32_SECURE_PATCHING_BOOTLOADER_SLOT0_START`. Then your application's interrupt vector table has to be offset from this by the minimum size of your vector table (to account for the image header), also defined as `VECTOR_SIZE` in the linker configuration file. |
| 64 | + |
| 65 | +``` |
| 66 | +/* For this example end of RAM on STM32L073 is 0x20005000 = 20 KB*/ |
| 67 | +
|
| 68 | +APPLI_region_intvec_start__ = STM32_SECURE_PATCHING_BOOTLOADER_SLOT0_START + VECTOR_SIZE; |
| 69 | +APPLI_region_ROM_start = STM32_SECURE_PATCHING_BOOTLOADER_SLOT0_START + VECTOR_SIZE + VECTOR_SIZE; |
| 70 | +APPLI_region_ROM_length = STM32_SECURE_PATCHING_BOOTLOADER_SLOT0_END - APPLI_region_ROM_start + 1; |
| 71 | +APPLI_region_RAM_start = STM32_SECURE_PATCHING_BOOTLOADER_RAM_START; |
| 72 | +APPLI_region_RAM_length = 0x20005000 - APPLI_region_RAM_start; |
| 73 | +
|
| 74 | +MEMORY |
| 75 | +{ |
| 76 | + ISR_VECTOR (rx) : ORIGIN = APPLI_region_intvec_start__, LENGTH = VECTOR_SIZE |
| 77 | + APPLI_region_ROM : ORIGIN = APPLI_region_ROM_start, LENGTH = APPLI_region_ROM_length |
| 78 | + APPLI_region_RAM : ORIGIN = APPLI_region_RAM_start, LENGTH = APPLI_region_RAM_length |
| 79 | +
|
| 80 | + /* If your application links with the bootloader interface library, add this section: */ |
| 81 | + SE_IF_region_ROM (rx) : ORIGIN = SE_IF_REGION_ROM_START, LENGTH = SE_IF_REGION_ROM_LENGTH |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +To your SECTIONS, add the following: |
| 86 | + |
| 87 | +``` |
| 88 | + /* If your application links with the bootloader interface library, add this section after .isr_vector */ |
| 89 | + .SE_IF_Code : { |
| 90 | + KEEP(*se_interface_app.o (.text .text*)) |
| 91 | + } >SE_IF_region_ROM |
| 92 | +
|
| 93 | + ... |
| 94 | +
|
| 95 | + /* Extra ROM section (last one) to make sure the binary size is a multiple of the AES block size (16 bytes) */ |
| 96 | + .align16 : |
| 97 | + { |
| 98 | + . = . + 1; /* _edata=. is aligned on 8 bytes so could be aligned on 16 bytes: add 1 byte gap */ |
| 99 | + . = ALIGN(16) - 1; /* increment the location counter until next 16 bytes aligned address (-1 byte) */ |
| 100 | + BYTE(0); /* allocate 1 byte (value is 0) to be a multiple of 16 bytes */ |
| 101 | + } > APPLI_region_ROM |
| 102 | +
|
| 103 | + ... |
| 104 | +
|
| 105 | +``` |
| 106 | + |
| 107 | +A multi-target generic linker script that works with most STM32 targets is provided in the bootloader `Linker` directory. See this [reference project linker script](https://github.com/firmwaremodules/stm32-secure-patching-bootloader-demoapp/master/App/Project/DemoApp/NUCLEO-L073RZ/STM32CubeIDE/DemoApp_NUCLEO-L073RZ/STM32L073RZTx.ld). |
| 108 | + |
| 109 | +4. Adjust your system_stm32xxxx.c file. |
| 110 | + |
| 111 | +This file programs the VTOR register (Vector Table Offset Register) by your application in the early startup phase so that interrupts invoke your application's handlers at the linked offset instead of the bootloader's. |
| 112 | + |
| 113 | +``` |
| 114 | +extern uint32_t APPLI_region_intvec_start__; |
| 115 | +#define INTVECT_START ((uint32_t)& APPLI_region_intvec_start__) |
| 116 | +
|
| 117 | +void SystemInit(void) |
| 118 | +{ |
| 119 | + ... |
| 120 | +
|
| 121 | + /* Configure the Vector Table location add offset address ------------------*/ |
| 122 | + SCB->VTOR = INTVECT_START; |
| 123 | +} |
| 124 | +
|
| 125 | +``` |
| 126 | + |
| 127 | +That's it. There is a bit to do here, but all things considered it is pretty minimal and your application sources and build procedure is virtually untouched by the addition of the stm32-secure-patching-bootloader. |
| 128 | + |
| 129 | +### Build Products |
| 130 | + |
| 131 | +The stm32-secure-patching-bootloader postbuild process produces three or four artifacts and places them in the specified output directory (`Binary` in the above). |
| 132 | + |
| 133 | +Naming convention: `BOOT_<Your Artifact Name>_<Board Name>_<version>` |
| 134 | + |
| 135 | + 1. Combined binaries: `BOOT_DemoApp_NUCLEO-L073RZ_1.0.0.hex`, `BOOT_DemoApp_NUCLEO-L073RZ_1.0.0.bin` (.bin produced only when multisegment is not enabled) |
| 136 | + 2. Secured in-field firmware full update file: `DemoApp_NUCLEO-L073RZ_1.0.0.sfb` (full image) |
| 137 | + 3. Secured in-field firmware patch update file: `DemoApp_NUCLEO-L073RZ_1.0.0_1.1.0.sfbp` (patch - produced only if reference version 1.0.0 exists in `Binary\PatchRef`) |
| 138 | + |
| 139 | + |
| 140 | +### Notes |
| 141 | + |
| 142 | +* Patch files .sfbp only work if the source version already exists on the target device. E.g. to update from v1.1.7 to v1.2.0, version v1.1.7 must exist on the device as the active image in SLOT0. |
| 143 | +* To update from v1.0.0 to v1.4.0 when only patches of v1.0.0_v1.1.0, v1.1.0_v1.2.0, v1.2.0_v1.3.0, v1.3.0_v1.4.0 are available, each patch must be applied in series. For each one, firmware is installed by the bootloader through a reboot cycle. |
| 144 | +* The build system (STM32CubeIDE) postbuild command line must explicitly set what the reference firmware version is to build a patch from. With this flexibility it is possible to build patches from any version. Commonly this 'from version' is updated from one release to the next to build a series of patchable updates (v1.0.0->v1.1.0, v1.1.0->v1.2.0 etc). Of course you could create a patch from v1.0.0->v1.2.0 and post this to your update server; if your 'patch picker' algorithm was smart enough it could select the best patch from a list of available patches. |
| 145 | +* Full image files .sfb can be used to update to any version and do not depend on what is currently on the device. These could serve as a fall-back in case the hypothetical 'patch picker' implemented in the user application cannot find a suitable patch for download. |
| 146 | +* The 'patch picker' is implemented in the USB flash drive updater to select the first patch in the root directory that matches the current version on the device and contains the highest 'to' version. It is able to perform a 'patch chain' update as described above by repeatedly rebooting and updating to the next higher patch version. |
| 147 | + |
0 commit comments