Zephyr: Mcuboot fails to compile when using single image and usb dfu

Created on 17 Sep 2020  路  20Comments  路  Source: zephyrproject-rtos/zephyr

Describe the bug
I am not sure if this is a bug or a feature request, because I can't find much information on my requirement.

I am writing an application which needs to be bootloadable over USB, and does not have physical access to a boot button. Because of this, I would prefer that the bootload process can be initiated from within the application context. My current idea is to use mcumgr with the mcuboot bootloader. I have done this before with success.

However, in this case my application is slightly too large and so I cannot fit the image into the image-0 location if there is an equally sized image-1. Instead, I have removed image-1 and the scratch partition and built mcuboot with CONFIG_SINGLE_IMAGE_DFU=y.

This compiles perfectly fine and I am able to flash it to my device with no problems. As far as I understand this is supported functionality in the zephyr fork of mcuboot.

Similarly, I can build the application perfectly fine with CONFIG_BOOTLOADER_MCUBOOT=y. However if I try to include an image manager with CONFIG_MCUMGR=y things no longer work because it is looking for the image-1 partition, even though it is not required in my case.

To Reproduce

  1. Remove image-1 partition from board dts
  2. Build mcuboot with CONFIG_SINGLE_IMAGE_DFU=y to confirm this is supported
  3. Build an application with CONFIG_MCUMGR=y and CONFIG_BOOTLOADER_MCUBOOT=y to find errors regarding image-1

Expected behavior
mcumgr and dfu util in general should be aware of CONFIG_SINGLE_IMAGE_DFU=y

Impact
Very difficult to proceed on my application if I must have two image slots

Environment (please complete the following information):

  • OS: Windows 10
  • Toolchain: gnuarmemb
  • latest master

Additional context
I am also unable to build mcuboot with CONFIG_SINGLE_IMAGE_DFU=y and CONFIG_BOOT_WAIT_FOR_USB_DFU=y as this also causes errors looking for image-1.

Edit:

I have realized using mcumgr doesn't really make sense for single image applications since it has to overwrite the application. However, mcuboot with usb dfu should work and it doesn't. I've edited the title to reflect this.

MCUBoot question

All 20 comments

@bpbradley, if you only have one partition why are you trying to use mcumgr. The application can never upgrade itself, so mcumgr does not really do anything (except maybe capture a reboot request).

The main things I wanted with mcumgr were:

  1. Shell transport layer for the actual dfu, although this isn't a big deal.
  2. I had thoughts of doing bluetooth transport at a later stage, but also not a big deal.
  3. Mainly I need a way to initiate the bootload process from the application, and couldn't figure out how else to do that because trying to build mcuboot directly with CONFIG_BOOT_WAIT_FOR_USB_DFU also didn't work, and trying to do it with CDC_ACM and using BOOT_SERIAL doesn't work because mcuboot expects a boot pin.

But what you say makes sense. If the application overwrites itself then it won't functionally accomplish anything. I suppose I need to figure out how to get usb dfu working with single image just using mcuboot. I suppose having a manager in this way is impossible.

If there is a way to do usb dfu without a boot pin and without a second image slot, then I am happy to do it that way.

Perhaps this isn't the correct issue to raise. Maybe I need to focus more on why building mcuboot with CONFIG_BOOT_WAIT_FOR_USB_DFU=y fails. It won't let me compile with that config option without including CONFIG_USB_DEVICE_STACK=y. That configuration causes the same issue in subsys/dfu/boot and subsys/dfu/img-util where it looks for image-1

@bpbradley, maybe you can trick mcuboot into believing there is a image-1 by making image-1 equal to image-0 in the dts.

That does indeed work to trick it. However there is another issue where subsys/dfu/boot/mcuboot.c has a second definition of multiple functions defined within bootloader/mcuboot/boot/zephyr/flash_map_extended.c and bootloader/mcuboot/boot/bootutil/src/bootutil_misc.c. Namely flash_area_erased_val flash_area_read_is_empty boot_magic_compatible_check and boot_read_swap_state_by_id

I don't think you should trick it, but instead just remove image-1. MCUBoot should be able to deal with this, but it could be that the Zephyr DFU is not 100% up to date with this option at the moment. Might be a bit somewhat related to: https://github.com/zephyrproject-rtos/zephyr/issues/27673

/cc @de-nordic @nvlsianpu

I don't think you should trick it, but instead just remove image-1.

It should be enough to remove image-1 as usually the dts would be altered so that image-1 would be removed and image-0 would reuse the space after image-1.

It is enough to define CONFIG_BOOT_WAIT_FOR_USB_DFU=y to catch most of the errors. So the CONFIG_SINGLE_IMAGE_DFU is not culprit here. I need to check it closer but I think that when the USB is selected the "non-single image" code is build, since the problem with missing image-1 is reported.

1. Remove image-1 partition from board dts

USB-DFU depends on it and is not designed to work with CONFIG_SINGLE_IMAGE_DFU=y

1. Remove image-1 partition from board dts

USB-DFU depends on it and is not designed to work with CONFIG_SINGLE_IMAGE_DFU=y

I am not acquainted with the particular implementation, but USB-DFU is a USB class, no? As far as I understand MCUBoot can use USB-CDC for it's BOOT_SERIAL operation (I don't think I've ever tried it myself though!). So I assume when you say USB-DFU, this does not involve USB-CDC; sorry if I am misinterpreting the comment.

1. Remove image-1 partition from board dts

USB-DFU depends on it and is not designed to work with CONFIG_SINGLE_IMAGE_DFU=y

I am not acquainted with the particular implementation, but USB-DFU is a USB class, no? As far as I understand MCUBoot can use USB-CDC for it's BOOT_SERIAL operation (I don't think I've ever tried it myself though!). So I assume when you say USB-DFU, this does not involve USB-CDC; sorry if I am misinterpreting the comment.

This was my original plan. Unfortunately when you configure BOOT_SERIAL it appears to expect CONFIG_BOOT_SERIAL_DETECT_PORT and CONFIG_BOOT_SERIAL_DETECT_PIN to be defined as well, and checks that this pin is at an expected value in order to enter serial recovery mode. But in my application there is no access to a physical button or switch which can be used to trigger this. I am not sure a way forward here other than finding a way to trim my app down far enough to fit it into two images.

1. Remove image-1 partition from board dts

USB-DFU depends on it and is not designed to work with CONFIG_SINGLE_IMAGE_DFU=y

Is that a software configuration issue or implementation issue? The single application loader is very stupid thing it just loads software in most simplistic way possible, not checking other slots, etc. It is very simple thing that is just linked in place of full loader. It is basically called when update path has not been taken.
The heavy lifting regarding serials, USB, DFU, etc is really done by other mcuboot code.
@utzig Shouldn't we be able to weasel out of the problem by proper #ifdefing and configuring the code?

Hi @bpbradley

Opion 1:
MCUBoot serial recovery using USB CDC ACM virtual port should works for you - just need to modify mcuboot to wait some time for command instead of checking GPIO. Am I right?
This is something which can be added to mcuboot pretty quick if you can open such a PR to the jullabs/mcuboot.

Option 2:
CONFIG_BOOT_WAIT_FOR_USB_DFU=y
MCUBoot with USB DFU class can't work now as it uses zephyr library which assumes loading to the secondary slot (and has issue described above). This is something which might be improved as well.

What is your choice?
Also be aware that name of Konfig option CONFIG_SINGLE_IMAGE_DFU was changed to CONFIG_SINGLE_APPLICATION_SLOT.

1. Remove image-1 partition from board dts

USB-DFU depends on it and is not designed to work with CONFIG_SINGLE_IMAGE_DFU=y

Is that a software configuration issue or implementation issue? The single application loader is very stupid thing it just loads software in most simplistic way possible, not checking other slots, etc. It is very simple thing that is just linked in place of full loader. It is basically called when update path has not been taken.

In USB DFU implementation, image_0 is read only area, image_1 can be read and written. The option CONFIG_SINGLE_IMAGE_DFU was unknown when the code was last revised.

What is your choice?
Also be aware that name of Konfig option CONFIG_SINGLE_IMAGE_DFU was changed to CONFIG_SINGLE_APPLICATION_SLOT.

I believe you missed upstreaming from JuulLabs-OSS to have this already available for Zephyr, no?

^^ No, zephyr is in code-freeze for 2.4 release. Only bug-fixes can go in.

Hi @bpbradley

Opion 1:
MCUBoot serial recovery using USB CDC ACM virtual port should works for you - just need to modify mcuboot to wait some time for command instead of checking GPIO. Am I right?
This is something which can be added to mcuboot pretty quick if you can open such a PR to the jullabs/mcuboot.

Option 2:
CONFIG_BOOT_WAIT_FOR_USB_DFU=y
MCUBoot with USB DFU class can't work now as it uses zephyr library which assumes loading to the secondary slot (and has issue described above). This is something which might be improved as well.

What is your choice?
Also be aware that name of Konfig option CONFIG_SINGLE_IMAGE_DFU was changed to CONFIG_SINGLE_APPLICATION_SLOT.

Thanks for the info. Either of these options is acceptable to me. I think I might move forward with option 1 and creating a PR for now since it seems like a lighter lift.

Appreciate the clarifications.

Actually Option 1. is much easier to be achieved.

28506 resolves this issue. Closing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nashif picture nashif  路  5Comments

JusbeR picture JusbeR  路  5Comments

snej picture snej  路  5Comments

pdunaj picture pdunaj  路  3Comments

rosterloh picture rosterloh  路  4Comments