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

cpu: add flash_writable section to linker script #17436

Merged
merged 6 commits into from
Mar 17, 2022

Conversation

Ollrogge
Copy link
Member

@Ollrogge Ollrogge commented Dec 22, 2021

Contribution description

This PR adds an .flash_writable section to all CPUs implementing the flashpage API.
This section enables modules to reserve flash memory at build time. This ensures that
different modules don't corrupt each others data by accidentally writing to each others flash pages.
Furthermore this approach will throw and error at build time if not enough flash memory is available.

To reserve flash memory one has to simple declare an array that is properly aligned and stored in the section e.g.:
static const uint8_t _backing_memory[FLASHPAGE_SIZE] __attribute__((aligned(FLASHPAGE_SIZE))) __attribute__((section(".flash_writable")));

The section is not necessary in most cases. The only case I know of where it is needed is for the nrf52840dongle.
There, reserving flash memory with a const buffer (static const uint8_t _backing_memory[FLASHPAGE_SIZE] __attribute__((aligned(FLASHPAGE_SIZE)));) is not enough since this buffer will be put in the .rodata section which is write protected by the default USB bootloader. Writing to this flash region will cause the dongle to go back into bootloader mode. Declaring an explicit section in flash memory fixes this problem because the buffer will no longer be stored as part of the .rodata section.

Even if the .flash_writable section is only necessary in one case I still think it is reasonable to explicitly declare a section in flash memory where applications can store persistent data. This also allows for giving the section an explicit size.

Testing procedure

To test the functionality I added the test_res_pagewise command to tests/periph_flashpage.

Issues/PRs references

@github-actions github-actions bot added Area: cpu Area: CPU/MCU ports Area: tests Area: tests and testing framework Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Platform: RISC-V Platform: This PR/issue effects RISC-V-based platforms labels Dec 22, 2021
@chrysn
Copy link
Member

chrysn commented Dec 24, 2021

The nrf52840dongle bootloader mechanism has an option PARTICLE_MONOFIRMWARE_CHECKSUMLIMIT that makes it checksum just the header, so this is not strictly necessary.

It may well make sense to do this, especially if we decide that it's legitimate for bootloader to verify the image's integrity, and conversely necessary for any flash writer to declare the flash region.

I suggest that app_data be renamed to reflect these semantics: That it's flash that there is an intention to write to. (So maybe flash_writable?). With PRs like #16730 I expect that some RIOT modules would like to declare such pages as well (even though the resource friendly usage pattern would be for all users to gang up to minimize flash writes), and then the name app_data would be ill-advised.

As we define that region we may also want to consider whether in that region it's legitimate to execute code from. I don't know if any of the MPUs around support that, but setting that section non-executable may be a good idea. (That should not be acted on in this PR, but a note on the documentation of that new region could say something like

While currently not enforced on any system, that memory region should be regarded as non-executable; systems with memory protection may start enforcing this as the use of MPUs in RIOT progresses.

).

@Ollrogge
Copy link
Member Author

pages as well (even though the resource friendly usage pattern would be for all users to gang up to minimize flash writes), and then the

Are you sure that the Open USB bootloader of the nrf52840dongle supports this option ? I can't find it anywhere in the SDK.
Also I am not building the bootloader so I can't supply any CFLAGS.

I will rename the section as you suggested.

@chrysn
Copy link
Member

chrysn commented Dec 24, 2021 via email

@Ollrogge
Copy link
Member Author

@chrysn I updated the section name to be flash_writable. Anything else missing now?

@chrysn
Copy link
Member

chrysn commented Dec 27, 2021 via email

@Ollrogge
Copy link
Member Author

from a quick look (currently on the road): any idea where this section could be documented? if not, i'd like to instigate some documentation on the linker scripts and sections (which would then take over documenting this).

No me neither. I think documenting RIOT specific linker scripts and sections is a good idea. This would have helped me on multiple occasions.

i can do a full implementation review later (incl tests), but would appreciate a high-level review ("is having a dedicated section for any writable parts of flash the way to go?") by someone who knows which trickery is hidden in our linker scripts.

That would be great ! Do you know anyone I could ping for the high-level review ?

@chrysn chrysn added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Dec 27, 2021
Copy link
Member

@chrysn chrysn left a comment

Choose a reason for hiding this comment

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

Running the test on native segfaulted -- not much of a surprise, and "it's out of scope" is an acceptable approach, but I'd like to see this fail somewhat cleanly. I don't have a clue yet how to best do that -- could be a feature of the board that's checked before offering the test (the feature would indicate that this works, and might be unset eg. when a board's bootloader rules out flash modifications inside the firmware-updated area), might be a runtime success check or might be something else.

I've also cleared this for CI runs; looking good so far.

tests/periph_flashpage/main.c Show resolved Hide resolved
@@ -236,4 +236,8 @@ SECTIONS
.end_fw (NOLOAD) : ALIGN(4) {
Copy link
Member

Choose a reason for hiding this comment

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

In particular (from looking at occurrences of end_fw), this puts it in conflict with flashpage_first_free -- as flashpage_first_free looks at end_fw, pages used in flash_writable would also show up in flashpage_first_free. I see this as a successor to flashpage_first_free anyway, but they should still not conflict. If end_fw is not used anywhere else, it might suffice to swap them around so that flash_writable is before end_fw.

(My impression from git praise is that end_fw is exclusively used for flashpage_first_free -- if not, additional questions would be raised as to whether statically initialized content does actually get flashed.)

@Ollrogge Ollrogge force-pushed the reserve_flash branch 2 times, most recently from 43f5a18 to e2041cf Compare December 29, 2021 17:33
@chrysn
Copy link
Member

chrysn commented Dec 29, 2021 via email

@Ollrogge
Copy link
Member Author

Since the section is not part of the firmware I can not statically initialize the memory.
Is it not? What makes it not? AFAIK, none of the bootloaders or programmers look at "end_fw" or other markers, they just put everything that is in ROM onto the chip. (Statics without initial value are probably zero-initialized.)

This is due to the section being defined as NOLOAD. Removing this directive will result in the problem with the nrf52840dongle I described in the FIDO2 follow up PR. Setting PARTICLE_MONOFIRMWARE_CHECKSUMLIMIT does not fix this issue.

@PeterKietzmann
Copy link
Member

Many use cases can benefit from this data section. We saw the need in previous flashage discussions (related to FIDO2). More generically, all sorts of non-volatile key- or configuration storages would make use of it.

if we decide that it's legitimate for bootloader to verify the image's integrity

To me, that is a totally legitimate assumption (what does RIOTBOOT do?). The ARM PSA specification, for example, requires a root of trust which includes the trusted system startup.

documentation on the linker scripts and sections

+1

Copy link
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

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

It would be nice to extend the automated test script with the new command introduced in tests/periph_flashpage. This way there's a simple way to automatically test this feature on various boards.

@@ -643,6 +688,7 @@ static const shell_command_t shell_commands[] = {
{ "edit", "Write bytes to the local page buffer", cmd_edit },
{ "test", "Write and verify test pattern", cmd_test },
{ "test_last_pagewise", "Write and verify test pattern on last page available", cmd_test_last },
{ "test_res_pagewise", "Write and verify short write on reserved page", cmd_test_res},
Copy link
Contributor

Choose a reason for hiding this comment

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

The name of this command is confusing: res can be confused with result. Better be explicit and call it test_reserved_pagewise or something the like.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed and I also added the command to periph_flashpage/tests

@chrysn
Copy link
Member

chrysn commented Feb 3, 2022

Should this FEATURE depend on PERIPH_FLASHPAGE_PAGEWISE ? I am wondering if we should encourage every module using the flash_writable section to use buffers aligned to flash pages or not. If we don't encourage this multiple modules could share a flashpage but the chances that a module accidentally corrupts data by using the page num instead of the address is higher.

What do you think ?

Frankly I don't see what periph_flashpage_pagewise actually describes. Either way, yes I think we should encourage using this mechanism over plain flash page numbers. There is no portable way to know what a flashpage number means, and whether it conflicts with anything.

On platforms that do have (some of their, at least the part in flash_writable) flash memory address-mapped, this should be the preferred mechanism. On others, users will need to make usable page numbers appear out of thin air, but then these platforms (and I think it's just native) hopefully have a means to do that.

(One might even consider playing with the linker script of native, and make the flashpage implementation of native map the file in with mmap, strongly asserting (backed by the linker script's configuration) that the kernel will indeed map the backing file into the right location), [edit] but that's out of scope for here.

@Ollrogge
Copy link
Member Author

Ollrogge commented Mar 1, 2022

Frankly I don't see what periph_flashpage_pagewise actually describes. Either way, yes I think we should encourage using this mechanism over plain flash page numbers. There is no portable way to know what a flashpage number means, and whether it conflicts with anything.

So, I had a chat with @bergzand about the periph_flashpage_pagewise feature.
He added this feature when making the flashpage API optional.
This feature describes if a board supports the pagewise API. The API is supportd
when a device has a single compile-time page size.

Based on this I think that the feature introduced with this PR should be dependent
on the pagewise API feature since the reserved flash regions need to be
aligned to FLASHPAGE_SIZE to not corrupt each other. Otherwise
writing to a buffer that lays in the middle of a flash sector will corrupt
other adjacent buffers since the sector needs to be erased first and no function
of the current flash API's implements read-modify-write-operations
when using addresses instead of page numbers.

This will exclude platforms that don't have a constant page size from this
feature. I think this is fine for now because I see no other convenient way
to use the .flash_writable section without having the reserved regions
be aligned to FLASHPAGE_SIZE.

@chrysn I would really like to move on with this. Can you please look at the changes and tell me what is missing ?

@chrysn
Copy link
Member

chrysn commented Mar 6, 2022

I can't do a comprehensive review right now, but I can add a few questions:

  • The sections are all ALIGN(4). Why? The actual relevant alignments come in through the FLASH_WRITABLE_INIT anyway.
  • In terms of commit history cleanliness, I'd prefer the last rename to be applied back to commits that originally introduced the names, this would ease final review. (There are no unresolved threads open, so please force push once that is applied).

I'll give this a final review once those two are answered and/or addressed [edit: but don't see anything showstopper-wise left].

@github-actions github-actions bot removed the Area: build system Area: Build system label Mar 7, 2022
@Ollrogge
Copy link
Member Author

Ollrogge commented Mar 7, 2022

I can't do a comprehensive review right now, but I can add a few questions:

  • The sections are all ALIGN(4). Why? The actual relevant alignments come in through the FLASH_WRITABLE_INIT anyway.

Good question. I think I added this ALIGN before adding the FLASH_WRITABLE_INIT macro to make sure that the section is properly aligned in order to align flash writes. This however wouldn't even have worked in all cases since FLASHPAGE_WRITE_BLOCK_ALIGNMENT can be 8 as well.

Anyways, I removed the usage of ALIGN.

  • In terms of commit history cleanliness, I'd prefer the last rename to be applied back to commits that originally introduced the names, this would ease final review. (There are no unresolved threads open, so please force push once that is applied).

Done !

@Ollrogge
Copy link
Member Author

@chrysn could you please look at the latest changes and make a final review if possible ?

@chrysn
Copy link
Member

chrysn commented Mar 16, 2022

OK, so for tests I've performed:

  • The new test runs through with plausible output on native and particle-xenon (with riotboot bootloader).
Test on particle-xenon in riotboot partition 1

Flashing command:

$ FEATURES_REQUIRED+=riotboot USEMODULE+=usbus_dfu make BOARD=particle-xenon all riotboot/flash-slot1 test PROGRAMMER=dfu-util

Output (help output removed):

Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
rt addr:                0x00000000
Page size:                      4096
Number of pages:                256
Number of first free page:      140 
Number of last free page:       255 
> 

> 
> test_last_raw

> test_last_raw
wrote raw short buffer to last flash page
> help
help
...
> test_last_pagewise
wrote local page buffer to last flash page
> help
help
...
> test_reserved_pagewise
Reserved page num: 138 
Since the last firmware update this test has been run 3 times 
wrote local page buffer to reserved flash page

When running on a bootloader, as an extra check, try restarting the board and check whether this application still comes up.
> help
help
...
> 
  • The test behaves well when the sizes (both _abacking_memory and _backing_memory have to be set in lockstep because of the way they're compared) are set large enough to fill up the remaining memory (increasing them so that "number of first free page" becomes "number of pages").

    In particular, increasing the memory further does trigger the size checks (as it should -- this checked that the memory is properly accounted for even though it is not flashed).

Modifications and output
diff --git a/tests/periph_flashpage/main.c b/tests/periph_flashpage/main.c
index cff25750ab..5bfbd16280 100644
--- a/tests/periph_flashpage/main.c
+++ b/tests/periph_flashpage/main.c
@@ -64 +64 @@ static uint8_t page_mem[FLASHPAGE_SIZE] ALIGNMENT_ATTR;
-FLASH_WRITABLE_INIT(_backing_memory, 0x1);
+FLASH_WRITABLE_INIT(_backing_memory, 59);
@@ -69 +69 @@ FLASH_WRITABLE_INIT(_backing_memory, 0x1);
-FLASH_WRITABLE_INIT(_abacking_memory, 0x1);
+FLASH_WRITABLE_INIT(_abacking_memory, 59);

Output (help output removed):

Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
rt addr:                0x00000000
Page size:                      4096
Number of pages:                256
Number of first free page:      256 
Number of last free page:       255 
> 

> 
> test_last_raw

> test_last_raw
wrote raw short buffer to last flash page
> help
help
...
> test_last_pagewise
wrote local page buffer to last flash page
> help
help
...
> test_reserved_pagewise
Reserved page num: 196 
Since the last firmware update this test has been run 2 times 
wrote local page buffer to reserved flash page

When running on a bootloader, as an extra check, try restarting the board and check whether this application still comes up.
> help
help
...
> 

By the way, this shows a flaw in the last_pagewise test: It writes to the "last free page" (255) even though the first free page (256) indicates that there are simply no free pages at all -- thus destroying "data" in the area set aside by one of the new reserved pages. Given that that API is becoming deprecated and it's just a flaw in the test (albeit one that a user might create themselves as well), that's likely not too bad -- just please be sure to follow up with that deprecation.

  • The two last commits could need a bit of restructuring:

    • The dependency should be set right where it is defined. (ie. the last commit applied to where the one MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE is defined).
    • The commit could be named "periph/flashpage: Add _in_address_space and FLASH_WRITABLE_INIT" and spell the feature name out in the second line. (CODING_CONVENTION.md is a bit terse on the topic; the commit message length limit is enforced by CI).
    • As that is an awkward workaround for the name being too long, and because the commit does three things in one go (introduce a feature that is (by no fault of its own) spread out over dozens of files, introduce a FLASH_WRITABLE_INIT macro, and extend a test), please consider splitting it up.

So content-wise I'm happy; please adjust the commit and message, and then this can pass.

@chrysn
Copy link
Member

chrysn commented Mar 16, 2022

(Also, with my recent forays through MTD, I think this might be great to combine with an add-on routine that sets up the mtd_flashpage and mtd_mapper to make the flash-writable area available to flash file systems, but that's a different PR.)

@Ollrogge
Copy link
Member Author

Thanks for testing !

By the way, this shows a flaw in the last_pagewise test: It writes to the "last free page" (255) even though the first free page (256) indicates that there are simply no free pages at all -- thus destroying "data" in the area set aside by one of the new reserved pages. Given that that API is becoming deprecated and it's just a flaw in the test (albeit one that a user might create themselves as well), that's likely not too bad -- just please be sure to follow up with that deprecation.

Yes I am planning to deprecate the flashpage_*_free functions once this PR is done.

  • The two last commits could need a bit of restructuring:

  • The dependency should be set right where it is defined. (ie. the last commit applied to where the one MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE is defined).

  • The commit could be named "periph/flashpage: Add _in_address_space and FLASH_WRITABLE_INIT" and spell the feature name out in the second line. (CODING_CONVENTION.md is a bit terse on the topic; the commit message length limit is enforced by CI).

  • As that is an awkward workaround for the name being too long, and because the commit does three things in one go (introduce a feature that is (by no fault of its own) spread out over dozens of files, introduce a FLASH_WRITABLE_INIT macro, and extend a test), please consider splitting it up.

Done :) I split up the commits into three commits. One for the feature, one for the macro and one for the changed tests.

Copy link
Member

@chrysn chrysn left a comment

Choose a reason for hiding this comment

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

Thank you, and ACK.

@chrysn chrysn added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Mar 17, 2022
@chrysn chrysn enabled auto-merge March 17, 2022 08:25
@chrysn chrysn disabled auto-merge March 17, 2022 09:42
@chrysn
Copy link
Member

chrysn commented Mar 17, 2022

CI is complaining about discrepancies between feature names. Looks like an easy name mismatch (one name has an _init in it even though it shouldn't), please squash right away when you find it.

@chrysn chrysn merged commit facb5e6 into RIOT-OS:master Mar 17, 2022
@Ollrogge
Copy link
Member Author

Ollrogge commented Apr 1, 2022

Hi, I think this re-introduced the linker warning I fixed in #17581 because there is no memory region named rom in the RISC-V linker script. Did you mean to use flash instead? (:

... Sorry for that. Yes I meant to use flash. Do you want to fix this quickly or should I ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: cpu Area: CPU/MCU ports Area: drivers Area: Device drivers Area: Kconfig Area: Kconfig integration Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ARM Platform: This PR/issue effects ARM-based platforms Platform: MSP Platform: This PR/issue effects MSP-based platforms Platform: RISC-V Platform: This PR/issue effects RISC-V-based platforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants