Skip to content
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

zephyr: arm: Update reading the flash image reset vector #1661

Merged
merged 1 commit into from
Apr 23, 2024

Conversation

mmahadevan108
Copy link
Contributor

@mmahadevan108 mmahadevan108 commented Mar 24, 2023

This change uses the flash functions to read the applications reset vector. This allows flexibility on where the application image is programmed.
The current implementation in Zephyr for ARM does not allow the use-case where the application is programmed to a different flash device than where mcuboot is programmed and running from.

This was tested on NXP ARM platforms for the case where the Zephyr and mcuboot are in the same flash device and for the case where Zephyr and mcuboot are on different flash devices.

@mmahadevan108
Copy link
Contributor Author

cc @d3zd3z

@nordicjm
Copy link
Collaborator

Seems like a duplicate of #1569

@dleach02
Copy link
Contributor

@nordicjm, it looks like the #1569 is addressing a larger issue and has been in draft mode since January. This particular PR is fairly focused on a single problem we encountered as described in the initial text above. Do you think it would be easier to get this one approved and merged before your referenced PR?

@nordicjm
Copy link
Collaborator

@de-nordic


/* Jump to flash image */
rc = flash_device_base(rsp->br_flash_dev_id, &flash_base);
area_id = flash_area_id_from_image_slot(0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this breaks XIP

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it does, because it hardcodes vt to primary slot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am unclear how this breaks XIP.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XIP can boot from either slot, you are hardcoding this to slot 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is'nt that what we currently do i.e boot with respect to flash_base.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not related to rsp->br_image_off

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nordicjm I am not understanding the above comment.

The current implementation will always read the Zephyr binary with address CONFIG_FLASH_BASE_ADDRESS as the starting point.
This PR adds more flexibility on the starting address where to look for Zephyr binary.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds more flexibility on the starting address where to look for Zephyr binary.

No, it completely breaks XIP, that is not more flexible. I've pointed this out many times now, you need to follow the code. Old code:

    rsp->br_image_off = boot_img_slot_off(state, active_slot);
    rc = flash_device_base(rsp->br_flash_dev_id, &flash_base);
    vt = (struct arm_vector_table *)(flash_base +
                                     rsp->br_image_off +
                                     rsp->br_hdr->ih_hdr_size);

So yes, the base flash address which I never ever mentioned is the same, the image offset which would point to the base of the selected image is now completely absent from this code:

    area_id = flash_area_id_from_image_slot(0);
    rc = flash_area_read(fap, rsp->br_hdr->ih_hdr_size, dst, 8);
    vt = (struct arm_vector_table *)dst;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nordicjm @de-nordic @d3zd3z I have pushed an update to the implementation after understanding the XIP issues that were highlighted by the prior implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nordicjm @de-nordic @d3zd3z can you help re-review.

Copy link
Collaborator

@nordicjm nordicjm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaks XIP

Copy link
Collaborator

@de-nordic de-nordic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@d3zd3z
Copy link
Member

d3zd3z commented Dec 14, 2023

What is the status here? Is this still needed?

@mmahadevan108
Copy link
Contributor Author

@nordicjm @de-nordic @d3zd3z , can you help review this PR.

@mmahadevan108
Copy link
Contributor Author

cc @danieldegrasse

@d3zd3z
Copy link
Member

d3zd3z commented Mar 15, 2024

It is still unclear what problem this is solving. The end of this function jumps to an address in flash, so it only makes sense when there is XIP in flash. Is it just finding the wrong address? Could that be fixed a little easier?

@danieldegrasse
Copy link
Contributor

It is still unclear what problem this is solving. The end of this function jumps to an address in flash, so it only makes sense when there is XIP in flash. Is it just finding the wrong address? Could that be fixed a little easier?

My understanding of the issue is as follows: say you have a system where MCUBoot will boot from internal flash (memory mapped to 0x1000_0000), and then you flash your application image to external flash (memory mapped to 0x1800_0000). The system can XIP from this external flash, but MCUBoot still needs to jump to the program running there.

In the current implementation of MCUBoot for ARM, the function flash_device_base will simply return CONFIG_FLASH_BASE_ADDRESS, which will be incorrect for the case above. We need MCUBoot to instead read the flash vector table from the external flash device at 0x1800_0000. The idea of the code here is to read the flash vector table from the offset it will be at within the image slot we intend to boot from. By using the flash_area interfaces here, we can generically read the flash vector table from the image slot, and we will jump to an offset from 0x1800_000 (in the external flash memory) instead of booting from internal flash memory.

@nordicjm
Copy link
Collaborator

It is still unclear what problem this is solving. The end of this function jumps to an address in flash, so it only makes sense when there is XIP in flash. Is it just finding the wrong address? Could that be fixed a little easier?

My understanding of the issue is as follows: say you have a system where MCUBoot will boot from internal flash (memory mapped to 0x1000_0000), and then you flash your application image to external flash (memory mapped to 0x1800_0000). The system can XIP from this external flash, but MCUBoot still needs to jump to the program running there.

In the current implementation of MCUBoot for ARM, the function flash_device_base will simply return CONFIG_FLASH_BASE_ADDRESS, which will be incorrect for the case above. We need MCUBoot to instead read the flash vector table from the external flash device at 0x1800_0000. The idea of the code here is to read the flash vector table from the offset it will be at within the image slot we intend to boot from. By using the flash_area interfaces here, we can generically read the flash vector table from the image slot, and we will jump to an offset from 0x1800_000 (in the external flash memory) instead of booting from internal flash memory.

So then how about fixing flash_device_base in boot/zephyr/flash_map_extended.c so it returns the correct base address for a given flash area?

@mmahadevan108
Copy link
Contributor Author

It is still unclear what problem this is solving. The end of this function jumps to an address in flash, so it only makes sense when there is XIP in flash. Is it just finding the wrong address? Could that be fixed a little easier?

My understanding of the issue is as follows: say you have a system where MCUBoot will boot from internal flash (memory mapped to 0x1000_0000), and then you flash your application image to external flash (memory mapped to 0x1800_0000). The system can XIP from this external flash, but MCUBoot still needs to jump to the program running there.
In the current implementation of MCUBoot for ARM, the function flash_device_base will simply return CONFIG_FLASH_BASE_ADDRESS, which will be incorrect for the case above. We need MCUBoot to instead read the flash vector table from the external flash device at 0x1800_0000. The idea of the code here is to read the flash vector table from the offset it will be at within the image slot we intend to boot from. By using the flash_area interfaces here, we can generically read the flash vector table from the image slot, and we will jump to an offset from 0x1800_000 (in the external flash memory) instead of booting from internal flash memory.

So then how about fixing flash_device_base in boot/zephyr/flash_map_extended.c so it returns the correct base address for a given flash area?

I don't see an API in Zephyr where we could get this information from a device. Do you suggest I propose a change to Zephyr flash API to get this information?

@nordicjm
Copy link
Collaborator

It is still unclear what problem this is solving. The end of this function jumps to an address in flash, so it only makes sense when there is XIP in flash. Is it just finding the wrong address? Could that be fixed a little easier?

My understanding of the issue is as follows: say you have a system where MCUBoot will boot from internal flash (memory mapped to 0x1000_0000), and then you flash your application image to external flash (memory mapped to 0x1800_0000). The system can XIP from this external flash, but MCUBoot still needs to jump to the program running there.
In the current implementation of MCUBoot for ARM, the function flash_device_base will simply return CONFIG_FLASH_BASE_ADDRESS, which will be incorrect for the case above. We need MCUBoot to instead read the flash vector table from the external flash device at 0x1800_0000. The idea of the code here is to read the flash vector table from the offset it will be at within the image slot we intend to boot from. By using the flash_area interfaces here, we can generically read the flash vector table from the image slot, and we will jump to an offset from 0x1800_000 (in the external flash memory) instead of booting from internal flash memory.

So then how about fixing flash_device_base in boot/zephyr/flash_map_extended.c so it returns the correct base address for a given flash area?

I don't see an API in Zephyr where we could get this information from a device. Do you suggest I propose a change to Zephyr flash API to get this information?

const struct flash_area *fa;
rc = flash_area_open(area_id, &fa);
fa->fa_off ??

@danieldegrasse
Copy link
Contributor

So then how about fixing flash_device_base in boot/zephyr/flash_map_extended.c so it returns the correct base address for a given flash area?

@mmahadevan108 Given that we do have an API to do this, I think this fix makes the most sense

@mmahadevan108
Copy link
Contributor Author

mmahadevan108 commented Mar 20, 2024

It is still unclear what problem this is solving. The end of this function jumps to an address in flash, so it only makes sense when there is XIP in flash. Is it just finding the wrong address? Could that be fixed a little easier?

My understanding of the issue is as follows: say you have a system where MCUBoot will boot from internal flash (memory mapped to 0x1000_0000), and then you flash your application image to external flash (memory mapped to 0x1800_0000). The system can XIP from this external flash, but MCUBoot still needs to jump to the program running there.
In the current implementation of MCUBoot for ARM, the function flash_device_base will simply return CONFIG_FLASH_BASE_ADDRESS, which will be incorrect for the case above. We need MCUBoot to instead read the flash vector table from the external flash device at 0x1800_0000. The idea of the code here is to read the flash vector table from the offset it will be at within the image slot we intend to boot from. By using the flash_area interfaces here, we can generically read the flash vector table from the image slot, and we will jump to an offset from 0x1800_000 (in the external flash memory) instead of booting from internal flash memory.

So then how about fixing flash_device_base in boot/zephyr/flash_map_extended.c so it returns the correct base address for a given flash area?

I don't see an API in Zephyr where we could get this information from a device. Do you suggest I propose a change to Zephyr flash API to get this information?

const struct flash_area *fa;
rc = flash_area_open(area_id, &fa);
fa->fa_off ??

flash_area_open is what this PR is using. However fa->fa_off will give us the offset in a given partition, this does not get us the base address of the flash device.
In this PR, once the area is open we read from the appropriate location using flash_area_read

@danieldegrasse
Copy link
Contributor

flash_area_open is what this PR is using. However fa->fa_off will give us the offset in a given partition, this does not get us the base address of the flash device.
In this PR, cnce the area is open we read from the appropriate location using flash_area_read

Sure- I understand the reasoning behind the current approach. However couldn't we also simply update the way flash_device_base works to return the base address of the flash device the application image is present on? Then flash_base + rsp->br_image_off + rsp->br_hdr->ih_hdr_size would get the correct pointer into flash memory.

@mmahadevan108
Copy link
Contributor Author

flash_area_open is what this PR is using. However fa->fa_off will give us the offset in a given partition, this does not get us the base address of the flash device.
In this PR, cnce the area is open we read from the appropriate location using flash_area_read

Sure- I understand the reasoning behind the current approach. However couldn't we also simply update the way flash_device_base works to return the base address of the flash device the application image is present on? Then flash_base + rsp->br_image_off + rsp->br_hdr->ih_hdr_size would get the correct pointer into flash memory.

The issue is we do not have a flash_base we can get for the Flash device if image partition is on a different flash compared to MCUboot.

@nordicjm
Copy link
Collaborator

flash_area_open is what this PR is using. However fa->fa_off will give us the offset in a given partition, this does not get us the base address of the flash device.
In this PR, cnce the area is open we read from the appropriate location using flash_area_read

Sure- I understand the reasoning behind the current approach. However couldn't we also simply update the way flash_device_base works to return the base address of the flash device the application image is present on? Then flash_base + rsp->br_image_off + rsp->br_hdr->ih_hdr_size would get the correct pointer into flash memory.

The issue is we do not have a flash_base we can get for the Flash device if image partition is on a different flash compared to MCUboot.

The dts has the reg address, use that?

@danieldegrasse
Copy link
Contributor

The dts has the reg address, use that?

This is a good solution for internal flash devices, but it is not always that easy for external ones. To give a concrete example, NXP's FlexSPI peripheral supports memory mapped access to external flash parts. Multiple flash chips can be present on one FlexSPI, and their contents will be placed sequentially in the memory map (based on the chip select index for each flash chip). However, the "reg" property of these flash parts will be set to their chip select value, since the FlexSPI is considered a SPI device, and in that context the "reg" property means SPI chip select index. In order to really effectively support reading the flash base address for these parts, I think we'd need to add to the flash API.

Personally, I'm convinced @mmahadevan108's solution makes sense here. Reading the stack pointer and the reset vector via the flash API should be reliable for XIP from the same flash chip or another device, based on my understanding of how the flash area API works. Do you have a specific concern with implementing the boot process this way, versus extracting the flash base address?

@mmahadevan108
Copy link
Contributor Author

@d3zd3z @nordicjm @de-nordic, can you help revisit this PR.

@mmahadevan108
Copy link
Contributor Author

@d3zd3z @nordicjm @de-nordic, any comments?

@nordicjm
Copy link
Collaborator

The flash partition's reg address should point to the correct memory mapped address if it's being used in memory mapped mode, and that address should be absolute

@mmahadevan108
Copy link
Contributor Author

mmahadevan108 commented Apr 11, 2024

We cannot use the zephyr.flash node as the Zephyr application we are loading is on a different flash device. The other way of getting the reg address is to add NXP/Flash specific defines in the boot/zephyr/flash_map_extended.c file which is not ideal and something we would like to avoid.

@nordicjm
Copy link
Collaborator

You have a partitions part, right? That should contain the memory mapped address of the device if it's being used in memory mapped mode.

@mmahadevan108
Copy link
Contributor Author

mmahadevan108 commented Apr 12, 2024

Here is the partitions I am using in my overlay file. As shared earlier, we cannot get the address of the Flash device from this. The only thing available is the offset within the flash device. What is also available is the Flash Device node, we are using this to access the Flash device via the Flash API's

/delete-node/ &slot0_partition;
/delete-node/ &slot1_partition;
/delete-node/ &scratch_partition;

&w25q64jvssiq {
        partitions {
                compatible = "fixed-partitions";
                #address-cells = <1>;
                #size-cells = <1>;

                slot0_partition: partition@0 {
                        label = "image-0";
                        reg = <0x0000000 DT_SIZE_K(960)>;
                };
                slot1_partition: partition@f0000 {
                        label = "image-1";
                        reg = <0x000f0000 DT_SIZE_K(960)>;
                };
                scratch_partition: partition@1e0000 {
                        label = "image-scratch";
                        reg = <0x001e0000 DT_SIZE_K(128)>;
                };
        };
};

@nordicjm
Copy link
Collaborator

So you are using the external flash from MCUboot in SPI mode then somehow switching to memory mapped mode to execute the slot itself? Where is that code?

vt = (struct arm_vector_table *)(flash_base +
rsp->br_image_off +
rsp->br_hdr->ih_hdr_size);
rc = flash_area_read(fap, rsp->br_hdr->ih_hdr_size, dst, 8);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Th read size should not be hardcoded here, should be picked from the dst.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made this change.

boot/zephyr/main.c Outdated Show resolved Hide resolved
This change uses the flash functions to read the applications
reset vector. This allow flexibility on which flash device the
application is programmed.
For e.g: MCUBoot can be programmed and running from Internal
Flash while Zephyr can be loaded from a different Flash device.
This change is made for ARM platform, it can be extended to
non-ARM platforms as well.

Signed-off-by: Mahesh Mahadevan <mahesh.mahadevan@nxp.com>
Copy link
Collaborator

@nordicjm nordicjm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested swap using move and direct XIP on nrf52840dk, both working

@mmahadevan108
Copy link
Contributor Author

@de-nordic can you help re-review.

@mmahadevan108
Copy link
Contributor Author

@d3zd3z, can this PR be merged?

@d3zd3z
Copy link
Member

d3zd3z commented Apr 23, 2024

I think this looks good to make it into the next release. Please remember to add release notes. I'll go ahead and merge this, and put the release notes in an update for the release.

@d3zd3z d3zd3z merged commit 453096b into mcu-tools:main Apr 23, 2024
55 checks passed
@mmahadevan108 mmahadevan108 deleted the Fix_Flash_Boot branch April 23, 2024 16:49
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.

6 participants