Skip to content

boot: bootutil: swap-move: Allow trailer area not be a multiple of the sector size #2247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

taltenbach
Copy link
Contributor

When using swap-move, the area allocated to the trailer has currently to be a multiple of the sector size. This is not a big deal on devices having small sectors, but on devices having a large sector size, such most STM32 MCUs, this typically means losing a significant amount of flash memory. For example, on an STM32F4 device with 128-KiB sectors, the trailer is usually only a few hundreds of bytes long but a whole 128 KiB has still to be reserved for storing the trailer and can't therefore be used for firmware data.

The idea of this PR is to relax the need of having a sector-aligned trailer area, enabling therefore to use all the bytes not needed by the trailer, in the first sector holding trailer data, to store firmware data.

To illustrate the solution, let's imagine a device having 4096-byte sectors and a primary slot composed of N sectors. Let's assume the trailer needs 4224 bytes. Before this MR, two entire sectors would have to be allocated to the trailer, so the layout would have been something like:

         PRIMARY                               SECONDARY
|          ...          |              |          ...          |
+-----------------------+              +-----------------------+
| Firmware (4096 bytes) | Sector N-3   | Firmware (4096 bytes) | Sector N-3
|           .           |              |           .           |
+-----------------------+              +-----------------------+
|  Padding (4096 bytes) | Sector N-2   |  Trailer (4096 bytes) | Sector N-2
|           .           |              |           .           |
+-----------------------+              +-----------------------+
|  Trailer (4096 bytes) | Sector N-1   |  Trailer (4096 bytes) | Sector N-1
|                       |              |           .           |
+-----------------------+              +-----------------------+
|  Trailer (4096 bytes) | Sector N
|           .           |
+-----------------------+

With this MR, only the exact amount of bytes actually needed by the trailer have to be allocated to the trailer, so we have 3968 additional bytes available to store the firmware image:

         PRIMARY                               SECONDARY
|          ...          |              |          ...          |
+-----------------------+              +-----------------------+
| Firmware (4096 bytes) | Sector N-3   | Firmware (4096 bytes) | Sector N-3
|           .           |              |           .           |
+-----------------------+              +-----------------------+
| Firmware (3968 bytes) | Sector N-2   | Firmware (3968 bytes) | Sector N-2
|  Padding (128 bytes)  |              |  Trailer (128 bytes)  |
+-----------------------+              +-----------------------+
|  Padding (3968 bytes) | Sector N-1   |  Trailer (4096 bytes) | Sector N-1
|  Trailer (128 bytes)  |              |           .           |
+-----------------------+              +-----------------------+
|  Trailer (4096 bytes) | Sector N
|           .           |
+-----------------------+

At the beginning of an upgrade or revert process, all the sectors in the primary slot are "moved up", ending up with:

         PRIMARY                               SECONDARY
|          ...          |              |          ...          |
+-----------------------+              +-----------------------+
| Firmware (4096 bytes) | Sector N-3   | Firmware (4096 bytes) | Sector N-3
|           .           |              |           .           |
+-----------------------+              +-----------------------+
| Firmware (4096 bytes) | Sector N-2   | Firmware (3968 bytes) | Sector N-2
|           .           |              |  Trailer (128 bytes)  |
+-----------------------+              +-----------------------+
| Firmware (3968 bytes) | Sector N-1   |  Trailer (4096 bytes) | Sector N-1
|  Trailer (128 bytes)  |              |           .           |
+-----------------------+              +-----------------------+
|  Trailer (4096 bytes) | Sector N
|           .           |
+-----------------------+

To main ideas of the solution implemented by this PR is:

  • To copy only the part used by the firmware image when moving up the last sector (sector N-2 in the example), to avoid overwriting the trailer data.
  • To erase the secondary slot's trailer when swapping the last sector holding firmware data in the secondary slot, to ensure no firmware data will be lost when erasing the secondary trailer. Previously the erasure was performed at the beginning of the move process, just after the erasure of the primary trailer, by simplicity.
  • To avoid the need of rewriting the secondary slot's trailer when starting a revert process.

The latter is the most difficult part. Indeed, at the beginning of the revert process, the secondary trailer was rewritten to make the revert look like a permanent upgrade in case an unfortunate reset occurs during the rewriting of the primary trailer. This was a clever trick, but is unfortunately no longer possible if the trailers are not stored in dedicated sectors. The solution proposed by this PR is inspired from the implementation of the swap-offset strategy, which uses the copy_done flag in the secondary trailer to indicate a revert process is ongoing. For the swap-move, since we cannot rewrite the trailer at the beginning of the revert process, the idea is to rewrite it at the time it is erased, at the end of the upgrade process. This means the secondary trailer is now valid after an upgrade, which required a few changes in the swap tables and in the simulator.

If the improvement proposed by this MR is merged, I will try to port those changes to the swap-offset strategy, which should be quite easy, at least in theory.

Resolves #2052

This routine will now also be useful for the swap-move strategy. The
trailer size is removed from the list of parameters as it can be easily
obtained using the 'state' parameter and it will be more convenient, for
the swap-move strategy, not to have to compute it manually before
calling 'boot_get_first_trailer_sector'.

Signed-off-by: Thomas Altenbach <[email protected]>
The swap-move strategy is currently assuming the trailer is stored in
dedicated sector, meaning the size of the area allocated to the trailer
must be a multiple of the sector size. This commit relaxes this
assumption when moving sectors up in the primary slot by allowing the
last sector containing firmware data to also hold part of the trailer
(or the whole trailer if it is small enough).

Signed-off-by: Thomas Altenbach <[email protected]>
When using the swap-move strategy, the secondary trailer was erased just
after the primary trailer, when moving the sectors up in the primary
slot. This was working because it was assumed the trailer is stored in
dedicated sectors, so no sector could contain both trailer and firmware
data. However, if this assumption is relaxed, it means it is no more
possible to erase the secondary trailer before the last sector holding
firmware data has been copied to the primary slot, since that sector
might also contain trailer data. So the erasure of the secondary trailer
has to occur at the time the last sector containing firmware data is
swapped.

Signed-off-by: Thomas Altenbach <[email protected]>
When using the swap-move strategy, at the very beginning of the revert
process, the secondary trailer was rewritten to make the revert look
like a permanent upgrade in case an unfortunate reset occurs when
rewriting the primary trailer. This was possible because the assumption
was that no sector contained both part of the firmware and part of the
trailer.

To relax this assumption, it is necessary to avoid having to rewrite the
secondary trailer at the start of the revert process, since that could
also erase firmware data. The solution chosen is to rewrite the
secondary trailer at the end of the upgrade process, just after that
trailer is erased and to take advantage of the unused 'copy_done' flag
in the secondary trailer to indicate that an upgrade has been performed
and that a revert has to be started or resumed if the primary image is
not confirmed.

Signed-off-by: Thomas Altenbach <[email protected]>
This check is not useful because it is already done during the
validation of the image and was also making sure there was no firmware
data in the first trailer sector, which is no more needed.

Signed-off-by: Thomas Altenbach <[email protected]>
This commit updates the computation of the maximum firmware image size
for the swap-move strategy to allow the first sector containing part of
the trailer to also hold firmware data. This means it is no more
necessary to allocate a sector-aligned area for the trailer when using
swap-move, the area to allocate is now simply equal to the maximum
trailer size.

Signed-off-by: Thomas Altenbach <[email protected]>
For swap-move and swap-offset strategies, the size of the images used
for running the tests was hardcoded and was smaller than the maximum
possible size. This was done this way because the logic used to compute
the maximum image size was not taking into account the padding that is
needed when using those strategies. This commit fixes the issue, making
now possible to run the tests with images having the maximum possible
size.

Signed-off-by: Thomas Altenbach <[email protected]>
When using the swap-move strategy, the simulator now doesn't compute
anymore the maximum image size so the area allocated to the trailer has
a size that is a multiple of the sector size. Indeed, the swap-move
strategy now supports having part of the firmware in the first sector
holding trailer data, allowing larger images.

Signed-off-by: Thomas Altenbach <[email protected]>
When swap-move is used, the secondary slot's trailer is no longer
expected to be empty after an upgrade has been performed. Instead, the
secondary slot should contain a valid trailer with only the 'copy_done'
flag set.

Signed-off-by: Thomas Altenbach <[email protected]>
When using the swap-move strategy, the area allocated to the trailer no
more need to have a size that is a multiple of the sector size, since
the area not allocated to the trailer in the first sector holding
trailer data can now be used to store firmware data.

Signed-off-by: Thomas Altenbach <[email protected]>
This commit updates the description of the swap tables, used to
determine the type of operation to perform or resume at startup, in
order to reflect the changes that were performed in the code to enable
the swap-move strategy to support sectors containing both firmware and
trailer data.

Signed-off-by: Thomas Altenbach <[email protected]>
The swap-move strategy now support having firmware data in the first
sector holding trailer data. This commit adds a release note snippet
regarding this change.

Signed-off-by: Thomas Altenbach <[email protected]>
@nordicjm
Copy link
Collaborator

The problem with this is that it introduces the inability to clear flags e.g. if a firmware update is marked for confirmation or test, once a flag is set it cannot be cleared without potentially bricking the device if power is lost during erase/re-write of that sector

@taltenbach
Copy link
Contributor Author

taltenbach commented Mar 31, 2025

The problem with this is that it introduces the inability to clear flags e.g. if a firmware update is marked for confirmation or test, once a flag is set it cannot be cleared without potentially bricking the device if power is lost during erase/re-write of that sector

@nordicjm Have you a specific scenario in mind where there would be an issue?

  • Before an upgrade the user can erase the secondary trailer before downloading an upgrade and then write it to make the upgrade pending without risking bricking the device.

  • During an upgrade or revert process, the primary and secondary trailers are rewritten but the logic makes sure the process is resumed gracefully on boot in case of a power loss, and that without risking losing part of the firmware image.

  • After an upgrade, the user might have to set the image-ok flag in the primary trailer (which is possible without erasing the trailer) but never has to clear a flag in that trailer, unless I'm missing something.

Also note that the swap-scratch strategy already supports having firmware and trailer data in the same sector and this doesn't seem to cause any issue, so I don't think this should be a problem for swap-move.

@nordicjm
Copy link
Collaborator

nordicjm commented Apr 1, 2025

User uploads image, they mark it for test/swap (which writes the flag to the primary image, not the secondary), then they delete the secondary image without rebooting, the update image is gone, but the flag is permanently set to test/confirm

@taltenbach
Copy link
Contributor Author

taltenbach commented Apr 1, 2025

User uploads image, they mark it for test/swap (which writes the flag to the primary image, not the secondary), then they delete the secondary image without rebooting, the update image is gone, but the flag is permanently set to test/confirm

@nordicjm Perhaps you're talking about a specific MCUboot feature I'm not aware of, but the usual flow is:

  1. Erasing the secondary slot.
  2. Writing the update image to the secondary slot.
  3. Marking the update image as pending, which writes the secondary slot's trailer. To do a permanent upgrade the image-ok flag is set in the secondary slot, otherwise only the magic is written. Nothing is written to the primary slot, please look at the implementation of boot_set_pending_multi, in particular:
    rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap);
    if (rc != 0) {
    return BOOT_EFLASH;
    }
    rc = boot_set_next(fap, false, !(permanent == 0));
  4. At this point, if the user wants to delete the update image before rebooting for some reason, they can do that without any problem and no update will be triggered. There is also no risk of bricking the device, even in the case of a power loss, because erasing only one bit of the image of course prevents the upgrade from occurring because of the hash/signature check, which is performed before attempting an upgrade.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Swap move should only need one extra sector
2 participants