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

pkg/xipfs: add XIPFS as vfs module #21197

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

Conversation

GGuche-2XS-Univ-Lille
Copy link

@GGuche-2XS-Univ-Lille GGuche-2XS-Univ-Lille commented Feb 7, 2025

Contribution description

This PR aims at integrating the eXecutable In-Place FileSystem into RIOT operating system.

XIPFS implements regular filesystem storage manipulation capabilities, but also allows
to run code almost directly from the FLASH memory.
Just a little bit of RAM is used to relocate what's needed for the execution (data and bss).

It is provided like any other vfs module, and can be called through the defined vfs API.
Nonetheless, it can also be called directly through its own API (xipfs_* functions).

No matter the actual file size is, XIPFS deals with Flash pages granularity of 4KiB.
That means that with two files with different sizes less than 4 Kib, vfs df will display
8 Kib used.

Due to XIPFS design, directories and files are stored in a similar way.
Echoing the remark above, that also means that an empty directory will cost 4 Kib to the
Flash memory.

This implementation has been developed and tested on a Decawave DW1001 board (Quorvo).
However, the Flash memory handling relies on available periph/flashpage.h, and should thus
run on any platform supporting addressable Flash memory.

Testing procedure

Manual and automated tests are provided.
Both are great resources to showcase XIPFS api and abilities.
Manual tests also demonstrate the integration of XIPFS as a VFS submodule.

To run the automated tests :

  • Please open a terminal with a pyterm, listening to the device.
  • Open another one and move to the examples\xipfs-tests directory.
  • Now, please enter the following in this shell :
    BOARD=dwm1001 QUIET=0 make flash
  • The first terminal should display something along the lines of following :
    > 2025-02-06 14:04:16,367 # main(): This is RIOT! (Version: 2025.01)
    2025-02-06 14:04:16,373 # Tests started, awaiting for normal termination or assertion...
    2025-02-06 14:04:57,197 # Tests finished.
    This can take a little while, around 40-ish seconds for 130 tests.

To run the manual tests :

  • Please open a terminal with a pyterm, listening to the device.

  • Open another one and move to the examples\xipfs directory.

  • Now, please enter the following in this shell :

    BOARD=dwm1001 QUIET=0 make flash
  • Come back to the pyterm terminal.
    The first time XIPFS is run onto the board the resulting display should be similar to :

    2025-02-06 11:23:04,736 # main(): This is RIOT! (Version: 2025.01)
    2025-02-06 11:23:04,743 # vfs_mount: "/dev/nvme0p0": file system has not been initialized or is corrupted
    2025-02-06 11:23:04,747 # vfs_format: "/dev/nvme0p0": try initializing it
    2025-02-06 11:23:04,766 # vfs_format: "/dev/nvme0p0": OK
    2025-02-06 11:23:04,772 # vfs_mount: "/dev/nvme0p0": OK
    2025-02-06 11:23:04,784 # vfs_mount: "/dev/nvme0p1": file system has not been initialized or is corrupted
    2025-02-06 11:23:04,789 # vfs_format: "/dev/nvme0p1": try initializing it
    2025-02-06 11:23:04,815 # vfs_format: "/dev/nvme0p1": OK
    2025-02-06 11:23:04,823 # vfs_mount: "/dev/nvme0p1": OK

    , or to

    2025-02-06 14:06:05,692 # main(): This is RIOT! (Version: 2025.01)
    2025-02-06 14:06:05,696 # vfs_mount: "/dev/nvme0p0": OK
    2025-02-06 14:06:05,703 # vfs_mount: "/dev/nvme0p1": OK

    when XIPFS has already be run and has initialized the two mount points of the filesystem.

  • Now, please enter help

    2025-02-06 14:06:35,765 # help
    2025-02-06 14:06:35,768 # Command              Description
    2025-02-06 14:06:35,771 # ---------------------------------------
    2025-02-06 14:06:35,775 # exec                 Execute Hello World
    2025-02-06 14:06:35,780 # pm                   interact with layered PM subsystem
    2025-02-06 14:06:35,785 # ps                   Prints information about running threads.
    2025-02-06 14:06:35,790 # version              Prints current RIOT_VERSION
    2025-02-06 14:06:35,793 # reboot               Reboot the node
    2025-02-06 14:06:35,797 # vfs                  virtual file system operations
    2025-02-06 14:06:35,800 # ls                   list files
    2025-02-06 14:06:35,805 # create_executable    Create an XIPFS executable file
    2025-02-06 14:06:35,809 # execute              Execute an XIPFS file
  • exec is a more a demonstration than a regular shell command.
    However it will :

    • dump an executable file named hello-world.bin, when none is stored onto /dev/nvme0p0;
    • run hello-world.bin then.
    • Display :
    > exec
    2025-02-06 11:23:59,085 # exec
    2025-02-06 11:23:59,682 # Hello World!
  • execute is the XIPFS execution shell command to run an executable file.

    > execute /dev/nvme0p0/hello-world.bin Lorem ipsum dolor sit amet
    2025-02-07 09:41:02,366 # execute /dev/nvme0p0/hello-world.bin Lorem ipsum dolor sit amet
    2025-02-07 09:41:02,367 # Hello World!
    2025-02-07 09:41:02,368 # Lorem
    2025-02-07 09:41:02,369 # ipsum
    2025-02-07 09:41:02,369 # dolor
    2025-02-07 09:41:02,369 # sit
    2025-02-07 09:41:02,370 # amet
  • create_executable is the shell command to create an executable_file.
    Though create_executable takes a name and a bytesize, please remember that it will contain no code until filled by a write command.

     > create_executable /dev/nvme0p0/titi_exec 256
     2025-02-07 10:07:54,970 # create_executable /dev/nvme0p0/titi_exec 256
     > ls
     > ls /dev/nvme0p0
     2025-02-07 10:10:23,591 # ls /dev/nvme0p0
     2025-02-07 10:10:23,593 # hello-world.bin       896 B
     2025-02-07 10:10:23,601 # ploki//
     2025-02-07 10:10:23,601 # titi_exec
     2025-02-07 10:10:23,602 # total 2 files

@github-actions github-actions bot added Area: network Area: Networking Area: doc Area: Documentation Area: build system Area: Build system Area: pkg Area: External package ports Area: CoAP Area: Constrained Application Protocol implementations Area: sys Area: System Area: examples Area: Example Applications labels Feb 7, 2025
@kaspar030
Copy link
Contributor

Interesting!

@crasbe
Copy link
Contributor

crasbe commented Feb 7, 2025

I think something with your rebase went wrong, you have two commits in the PR that are part of the master branch from benpicco and maribu.

Damien Amara and others added 5 commits February 7, 2025 14:25
@github-actions github-actions bot removed Area: network Area: Networking Area: CoAP Area: Constrained Application Protocol implementations labels Feb 7, 2025
@GGuche-2XS-Univ-Lille
Copy link
Author

I think something with your rebase went wrong, you have two commits in the PR that are part of the master branch from benpicco and maribu.

Yeah I should have checked twice, I tried a a git rebase -i and dropped the two commits you cited.

Is it ok with you ?

@mguetschow mguetschow changed the title XIPFS Pull request pkg/xipfs: add XIPFS as vfs module Feb 7, 2025
@crasbe
Copy link
Contributor

crasbe commented Feb 7, 2025

Yes, it looks good now.

Unfortunately I don't have a dwm1001 board for testing this. I'm not a maintainer, so this is not an official review.

I took a quick look at the code and I think it would be possible to eliminate the need for dedicated board header files in your xipfs package.
The contants XIPFS_NVM_NUMOF, XIPFS_NVM_WRITE_BLOCK_ALIGNMENT, XIPFS_NVM_WRITE_BLOCK_SIZE and XIPFS_NVM_PAGE_SIZE can be read from the CPU configuration (for example: https://github.com/RIOT-OS/RIOT/blob/master/cpu/nrf52/include/cpu_conf.h#L89-L113 )

And the rest of the constants seem to be pretty much the same for all platforms.


There is also a bug that it won't allow you to build for other boards than the dwm1001 and refuses to download the package. However once you compiled it for the dwm1001 once, it has downloaded the package and throws errors when you try to compile it for other boards (in my case an nRF52840DK from Nordic).

Before compiling for the dwm1001
~/xipfs-riot/RIOT/examples/xipfs$ BOARD=nrf52840dk make
Building application "xipfs" for "nrf52840dk" with CPU "nrf52".

"make" -C /home/cbuec/xipfs-riot/RIOT/pkg/cmsis/ 
"make" -C /home/cbuec/xipfs-riot/RIOT/pkg/xipfs/ 
/home/cbuec/xipfs-riot/RIOT/build/pkg/xipfs/configure.sh --board=nrf52840dk
Usage: /home/cbuec/xipfs-riot/RIOT/build/pkg/xipfs/configure.sh <MANDATORY ARGUMENT>

  MANDATORY ARGUMENT:

    --board=<name> Specify the name of the board to build xipfs for. Supported
                   boards can be found in the "boards" directory.
make[1]: *** [Makefile:15: /home/cbuec/xipfs-riot/RIOT/build/pkg/xipfs/toolchain.mk] Error 1
make: *** [/home/cbuec/xipfs-riot/RIOT/examples/xipfs/../../Makefile.include:811: pkg-build] Error 2
After compiling for the dwm1001
~/xipfs-riot/RIOT/examples/xipfs$ BOARD=nrf52840dk make
Building application "xipfs" for "nrf52840dk" with CPU "nrf52".

"make" -C /home/cbuec/xipfs-riot/RIOT/pkg/cmsis/ 
"make" -C /home/cbuec/xipfs-riot/RIOT/pkg/xipfs/ 
"make" -C /home/cbuec/xipfs-riot/RIOT/build/pkg/xipfs
make[2]: Nothing to be done for 'all'.
cc1: error: /home/cbuec/xipfs-riot/RIOT/build/pkg/xipfs/boards/nrf52840dk: No such file or directory [-Werror=missing-include-dirs]
In file included from /home/cbuec/xipfs-riot/RIOT/examples/xipfs/main.c:25:
/home/cbuec/xipfs-riot/RIOT/sys/include/fs/xipfs_fs.h:27:10: fatal error: xipfs_config.h: No such file or directory
   27 | #include "xipfs_config.h"
      |          ^~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
compilation terminated.
make[1]: *** [/home/cbuec/xipfs-riot/RIOT/Makefile.base:149: /home/cbuec/xipfs-riot/RIOT/examples/xipfs/bin/nrf52840dk/application_xipfs/main.o] Error 1
make: *** [/home/cbuec/xipfs-riot/RIOT/examples/xipfs/../../Makefile.include:747: application_xipfs.module] Error 2

If the package only supports certain boards, it would be best to add a whitelist for supported boards: https://doc.riot-os.org/creating-an-application.html However I am not sure if that is intended to work for packages as well, I guess a maintainer could answer that.

Adding BOARD_WHITELIST = dwm1001 to the Makefile.dep of your xipfs packages, the following error will be thrown, which is more like the expected behavior.

~/xipfs-riot/RIOT/examples/xipfs$ BOARD=nrf52840dk make
The selected BOARD=nrf52840dk is not whitelisted: dwm1001
/home/cbuec/xipfs-riot/RIOT/examples/xipfs/../../Makefile.include:1006: *** You can let the build continue on expected errors by setting CONTINUE_ON_EXPECTED_ERRORS=1 to the command line.  Stop.

But in general I think it would be better to add general support for all boards (that support flashpage).

@aabadie
Copy link
Contributor

aabadie commented Feb 7, 2025

Unfortunately I don't have a dwm1001 board for testing this. I'm not a maintainer, so this is not an official review.

There are some dwm1001 boards deployed in the IoT-LAB testbed (in Lille and Toulouse sites). You can:

  1. start an experiment (assuming that you already created an account, configured your SSH access and installed the IoT-LAB cli-tools):
iotlab-experiment submit -d 20 -l 1,archi=dwm1001:dw1000+site=lille
iotlab-experiment wait --timeout 30 --cancel-on-timeout
  1. build, flash and access the terminal using the RIOT build system:
make BOARD=dwm1001 IOTLAB_NODE=auto -C examples/default flash term

(I just tried and it still works!)

@GGuche-2XS-Univ-Lille
Copy link
Author

For starters, thanks to @crasbe and @aabadie for their feeback; they are much appreciated.

We were, and still are, quite newbies into the RIOT community.
There is surely room for improvement in different departments on our side; the build system coming first in line.
We would gladly accept guidance and advices.


@crasbe :
We are not willing to constrain XIPFS to a sole platform, nor to "bind" XIPFS too closely to RIOT.
That being said, RIOT OS is great and dwm1001 is the only board we have to test XIPFS before releasing it to the wild.

I am giving a try at the generic code path that you propose, ie relying to FLASH definitions in cpu_conf.h.
If I am not mistaking, the "INCLUDES" variable contains a path to this file such as -I/somewhere/RIOT/cpu/nrf52/include, which should be enough at compilation time to find the FLASH defines.
If I understood correctly, it should work also for other platforms.

But now, I am facing an error from the build system where my CPU type is not found anymore :
error: #error "The CPU_MODEL of your board is currently not supported".

I've searched into RIOT only to find a definition in generated /somewhere/RIOT/examples/xipfs.bin/dwm1001/riotbuild/riotbuild.h, which is not reliable to my eyes.

Am I missing something ?
Or is there another way to access to proper defines which would let the build system sets the appropriate CPU model too ?

Thanks for reading.

@crasbe
Copy link
Contributor

crasbe commented Feb 11, 2025

I think you don't have to make any significant changes to the original project to be compatible with the RIOT definitions.
There are different possibilities like checking for some environment variables that are only set by the RIOT build system in your makefiles and conditionally skipping the include of the boards-specific xipfs_config.h and instead using one with the RIOT definitions.


I can't reproduce the issue you're describing. The error comes from the cpu/nrf52/include/cpu_conf.h file, that is correct:

#error "The CPU_MODEL of your board is currently not supported"

What's odd is that the CPU_MODEL is defined for the DWM1001 board (otherwise pretty much nothing would work):

CPU_MODEL = nrf52832xxaa

To reproduce the issue I included cpu.h in your examples/xipfs/main.c file and added a printf("Flashpage Size: %d\n", FLASHPAGE_SIZE); in the execution_handler function. It compiles without any issues, so the compiler can find the macro.

If you include cpu.h, you should have access to FLASHPAGE_SIZE etc from the nrf52-specific cpu_conf.h. The RIOT build system will do everything for you to include the right one for your CPU based on what's set in the board definition.

You could try to run make clean and delete the /somewhere/RIOT/examples/xipfs/bin folder. Sometimes there's something from previous build runs that causes issues, especially when working with packages.

@GGuche-2XS-Univ-Lille
Copy link
Author

@crasbe
Sorry for the latency.

I scratched my head a bit to get how the build system works, while keeping at the same time the riot integration code as a template for other ones.

I don't know if changes will be ok for reviewers, but now when building xipfs for RIOT, xipfs relies on cpu.h flashpage defines as you mentioned.
The old build path has been kept as a reminder and template for ports in other environments/os, if they occur; hoping it won't bother people.

I compiled both examples/xipfs and examples/xipfs-tests with this compilation path, and it worked as expected.
I run both of them without any problem too.

Please, could you give it a try for your board (nRF52840DK) and let me know if the compilation works for you now ?

@crasbe
Copy link
Contributor

crasbe commented Feb 13, 2025

Unfortunately it still fails, it appears that xipfs_config.h is still included in xipfs.h:

https://github.com/2xs/xipfs/blob/main/include/xipfs.h#L40

Removing that include makes it somewhat work, but of couse, there is no place now that defines XIPFS_NVM_WRITE_BLOCK_SIZE etc:

RIOT/examples/xipfs$ BOARD=nrf52840dk make
Building application "xipfs" for "nrf52840dk" with CPU "nrf52".

"make" -C /home/cbuec/riot-dev/RIOT/pkg/cmsis/ 
"make" -C /home/cbuec/riot-dev/RIOT/pkg/xipfs/ 
"make" -C /home/cbuec/riot-dev/RIOT/build/pkg/xipfs
arm-none-eabi-gcc -Wall -Wextra -Werror -ffreestanding  -Os -I. -DRIOT_OS=1 -isystem /usr/include/newlib/nano -I/home/cbuec/riot-dev/RIOT/core/lib/include -I/home/cbuec/riot-dev/RIOT/core/include -I/home/cbuec/riot-dev/RIOT/drivers/include -I/home/cbuec/riot-dev/RIOT/sys/include -I/home/cbuec/riot-dev/RIOT/boards/nrf52840dk/include -I/home/cbuec/riot-dev/RIOT/boards/common/nrf52xxxdk/include -I/home/cbuec/riot-dev/RIOT/boards/common/nrf52/include -I/home/cbuec/riot-dev/RIOT/cpu/nrf52/include -I/home/cbuec/riot-dev/RIOT/cpu/nrf5x_common/include -I/home/cbuec/riot-dev/RIOT/cpu/cortexm_common/include -I/home/cbuec/riot-dev/RIOT/sys/libc/include -I/home/cbuec/riot-dev/RIOT/build/pkg/cmsis/CMSIS/Core/Include -I/home/cbuec/riot-dev/RIOT/build/pkg/xipfs -I/home/cbuec/riot-dev/RIOT/sys/posix/include -I/home/cbuec/riot-dev/RIOT/sys/auto_init/include -I/home/cbuec/riot-dev/RIOT/examples/xipfs/bin/nrf52840dk/preprocessor -DRIOT_OS -DDEVELHELP -Werror -DCPU_FAM_NRF52 -mno-thumb-interwork -mcpu=cortex-m4 -mlittle-endian -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -fshort-enums -ggdb -g3 -Os -DCPU_MODEL_NRF52840XXAA -DCPU_CORE_CORTEX_M4F -DRIOT_APPLICATION=\"xipfs\" -DBOARD_NRF52840DK=\"nrf52840dk\" -DRIOT_BOARD=BOARD_NRF52840DK -DCPU_NRF52=\"nrf52\" -DRIOT_CPU=CPU_NRF52 -std=c11 -fwrapv -Wstrict-overflow -fno-common -ffunction-sections -fdata-sections -Wall -Wextra -Wmissing-include-dirs -fno-delete-null-pointer-checks -fdiagnostics-color -Wstrict-prototypes -Wold-style-definition -gz -Wformat=2 -Wformat-overflow -Wformat-truncation -fmacro-prefix-map=/home/cbuec/riot-dev/RIOT/= -Wcast-align -DCPU_RAM_BASE=0x20000000 -DCPU_RAM_SIZE=0x40000 -include '/home/cbuec/riot-dev/RIOT/examples/xipfs/bin/nrf52840dk/riotbuild/riotbuild.h' -c src/buffer.c -o src/buffer.o
In file included from src/buffer.c:44:
./include/xipfs.h:41:2: error: #error "xipfs_config.h: XIPFS_PATH_MAX undefined"
   41 | #error "xipfs_config.h: XIPFS_PATH_MAX undefined"
      |  ^~~~~
./include/xipfs.h:45:2: error: #error "xipfs_config.h: XIPFS_MAGIC undefined"
   45 | #error "xipfs_config.h: XIPFS_MAGIC undefined"
      |  ^~~~~
./include/xipfs.h:49:2: error: #error "xipfs_config.h: XIPFS_FILESIZE_SLOT_MAX undefined"
   49 | #error "xipfs_config.h: XIPFS_FILESIZE_SLOT_MAX undefined"
      |  ^~~~~
./include/xipfs.h:53:2: error: #error "xipfs_config.h: XIPFS_EXEC_ARGC_MAX undefined"
   53 | #error "xipfs_config.h: XIPFS_EXEC_ARGC_MAX undefined"
      |  ^~~~~
./include/xipfs.h:57:2: error: #error "xipfs_config.h: XIPFS_NVM_BASE undefined"
   57 | #error "xipfs_config.h: XIPFS_NVM_BASE undefined"
      |  ^~~~~
./include/xipfs.h:61:2: error: #error "xipfs_config.h: XIPFS_NVM_ERASE_STATE undefined"
   61 | #error "xipfs_config.h: XIPFS_NVM_ERASE_STATE undefined"
      |  ^~~~~
./include/xipfs.h:65:2: error: #error "xipfs_config.h: XIPFS_NVM_NUMOF undefined"
   65 | #error "xipfs_config.h: XIPFS_NVM_NUMOF undefined"
      |  ^~~~~
./include/xipfs.h:69:2: error: #error "xipfs_config.h: XIPFS_NVM_WRITE_BLOCK_ALIGNMENT undefined"
   69 | #error "xipfs_config.h: XIPFS_NVM_WRITE_BLOCK_ALIGNMENT undefined"
      |  ^~~~~
./include/xipfs.h:73:2: error: #error "xipfs_config.h: XIPFS_NVM_WRITE_BLOCK_SIZE undefined"
   73 | #error "xipfs_config.h: XIPFS_NVM_WRITE_BLOCK_SIZE undefined"
      |  ^~~~~
./include/xipfs.h:77:2: error: #error "xipfs_config.h: XIPFS_NVM_PAGE_SIZE undefined"
   77 | #error "xipfs_config.h: XIPFS_NVM_PAGE_SIZE undefined"
      |  ^~~~~
./include/xipfs.h:81:2: error: #error "xipfs_config.h: XIPFS_MAX_OPEN_DESC undefined"
   81 | #error "xipfs_config.h: XIPFS_MAX_OPEN_DESC undefined"
      |  ^~~~~
./include/xipfs.h:99:15: error: 'XIPFS_PATH_MAX' undeclared here (not in a function)
   99 |     char path[XIPFS_PATH_MAX];
      |               ^~~~~~~~~~~~~~
./include/xipfs.h:110:17: error: 'XIPFS_FILESIZE_SLOT_MAX' undeclared here (not in a function)
  110 |     size_t size[XIPFS_FILESIZE_SLOT_MAX];
      |                 ^~~~~~~~~~~~~~~~~~~~~~~
src/buffer.c:78:14: error: 'XIPFS_NVM_PAGE_SIZE' undeclared here (not in a function)
   78 |     char buf[XIPFS_NVM_PAGE_SIZE];
      |              ^~~~~~~~~~~~~~~~~~~
src/buffer.c: In function 'xipfs_buffer_read':
src/buffer.c:231:12: error: variable 'pos' set but not used [-Werror=unused-but-set-variable]
  231 |     size_t pos, i;
      |            ^~~
src/buffer.c: In function 'xipfs_buffer_write':
src/buffer.c:312:12: error: variable 'pos' set but not used [-Werror=unused-but-set-variable]
  312 |     size_t pos, i;
      |            ^~~
cc1: all warnings being treated as errors
make[2]: *** [Makefile:76: src/buffer.o] Error 1
make[1]: *** [Makefile:15: /home/cbuec/riot-dev/RIOT/examples/xipfs/bin/nrf52840dk/xipfs.a] Error 2
make: *** [/home/cbuec/riot-dev/RIOT/examples/xipfs/../../Makefile.include:811: pkg-build] Error 2

Also I don't see any includes for cpu.h? Did you miss any files in the commit?


Compiling for the nrf52840dk target will work on your machine as well, you can build RIOT for any target. You just can't flash it if you don't have the board :D

Comment on lines 28 to 32
#ifdef RIOT_OS
#include "include/xipfs.h"
#else /* RIOT_OS */
#include "xipfs_config.h"
#endif /* RIOT_OS */
Copy link
Contributor

Choose a reason for hiding this comment

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

This file is only present in RIOT, isn't it? Therefore, no include guards are required to check if it is RIOT or not.

Choose a reason for hiding this comment

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

Right, fixed.

* @}
*/

#if defined(MODULE_XIPFS_FS) || defined(MODULE_XIPFS)
Copy link
Contributor

Choose a reason for hiding this comment

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

These include guards should not be necessary here. The build system should ensure that only those files are compiled and linked that are actually used.

Choose a reason for hiding this comment

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

Ok, I remove them. Thanks !

@@ -23,4 +23,6 @@ USEMODULE += ps
# Use the default file system
USEMODULE += xipfs

CFLAGS +=-DRIOT_OS
Copy link
Contributor

Choose a reason for hiding this comment

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

You can just use the existing RIOT_VERSION define

Choose a reason for hiding this comment

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

Thanks for the tip.
I've followed your advice and relied on this preprocessor symbol in the library : xipfs library

@crasbe
Copy link
Contributor

crasbe commented Feb 13, 2025

I think the easiest approach would be to create a boards/riot/xipfs_config.h file with the following content:

#ifndef XIPFS_CONFIG_H
#define XIPFS_CONFIG_H

#include "cpu.h"

/**
 * @def XIPFS_PATH_MAX
 *
 * @brief The maximum length of an xipfs path
 */
#define XIPFS_PATH_MAX (64)

/**
 * @def XIPFS_MAGIC
 *
 * @brief The magic number of an xipfs file system
 */
#define XIPFS_MAGIC (0xf9d3b6cb)

/**
 * @def XIPFS_FILESIZE_SLOT_MAX
 *
 * @brief The maximum slot number for the list holding file
 * sizes
 */
#define XIPFS_FILESIZE_SLOT_MAX (86)

/**
 * @def XIPFS_EXEC_ARGC_MAX
 *
 * @brief The maximum number of arguments on the command line
 */
#define XIPFS_EXEC_ARGC_MAX (64)

/**
 * @def XIPFS_NVM_BASE
 *
 * @brief The non-volatile memory base address
 */
#define XIPFS_NVM_BASE (0)

/**
 * @def XIPFS_NVM_ERASE_STATE
 *
 * @brief The non-volatile memory erased state
 */
#define XIPFS_NVM_ERASE_STATE (0xff)

/**
 * @def XIPFS_NVM_NUMOF
 *
 * @brief The non-volatile memory flash page number
 */
#define XIPFS_NVM_NUMOF FLASHPAGE_NUMOF

/**
 * @def XIPFS_NVM_WRITE_BLOCK_ALIGNMENT
 *
 * @brief The write alignment for the non-volatile memory
 */
#define XIPFS_NVM_WRITE_BLOCK_ALIGNMENT FLASHPAGE_WRITE_BLOCK_ALIGNMENT

/**
 * @def XIPFS_NVM_WRITE_BLOCK_SIZE
 *
 * @brief The write size for the non-volatile memory
 */
#define XIPFS_NVM_WRITE_BLOCK_SIZE FLASHPAGE_WRITE_BLOCK_SIZE

/**
 * @def XIPFS_NVM_PAGE_SIZE
 *
 * @brief The non-volatile memory flash page size
 */
#define XIPFS_NVM_PAGE_SIZE FLASHPAGE_SIZE

/**
 * @def XIPFS_MAX_OPEN_DESC
 *
 * @brief The maximum number of opened descriptors
 */
#define XIPFS_MAX_OPEN_DESC (16)

#endif /* XIPFS_CONFIG_H */

And in your Makefile, you do the following changes:

ifdef RIOT_OS
CFLAGS         += -DRIOT_OS=1
CFLAGS         += $(RIOT_INCLUDES)
CFLAGS         += $(RIOT_CFLAGS)
+ CFLAGS         += -Iboards/riot
else # RIOT_OS
CFLAGS         += -Iboards/$(BOARD)
endif # RIOT_OS

Then you can pretty much drop all the #ifndef RIOT_OS checks in your .c and .h files and use your include/xipfs.h file as it is with all the checks and structures.

(I did not test this yet, but you should get the idea).

* Then modify the Makefile to suit your needs/sources and call make.
* You should end up with a *.bin file ready to be uploaded.
*/
const unsigned char hello_world_bin[896] = {
Copy link
Contributor

@kfessel kfessel Feb 14, 2025

Choose a reason for hiding this comment

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

BLOBS += hello_world.bin

in Makefile would generate a header file from your binary that can be included

this bin seem architecture (maybe mcu, maybe even board) specific please ensure to mark it accordingly

might be possible to reduce the sourcecode for an example binary and generate it while compiling the test

Choose a reason for hiding this comment

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

Well spotted, thanks.

You're correct, the bin file is indeed architecture specific, and it should be available only for the dwm1001 board.
I will then use the BLOBS feature and guard all related code with #ifdef/#else/#endif BOARD_DWM1001 in C file, which should be available at compilation time from CFLAGS.

This will remove the exec command, but will format and mount the two mount points in the filesystem.
Reviewers/users will still be able to manipulate files manually.

Is that all right with what you have in mind ?

@GGuche-2XS-Univ-Lille
Copy link
Author

@crasbe We had quite similar ideas, but I did it on the library side, which seemed pretty legitimate in my opinion.
I've added #include "periph/flashpage.h" to have default FLASHPAGE_ERASE_STATE , otherwise the compiler complains about this symbol missing.


BUT...
When compiling the library and other xipfs parts, compiler issues an error because of a non existing-path in the INCLUDESvariable :

cc1: error: /home/greg/Documents/code/RIOT-Github/build/pkg/xipfs/boards/nrf52840dk: No such file or directory [-Werror=missing-include-dirs]

TBH, I don't know where this path comes from, and my fix on it is to create an empty directory; for now.
In pkg/xipfs/Makefile :

FAKE_FOLDER:=$(RIOTBASE)/build/pkg/xipfs/boards/$(BOARD)
#$(shell mkdir -p $(FAKE_FOLDER))

With it, I am able to complete a build for nrf52840dk :

arm-none-eabi-size  /home/greg/Documents/code/RIOT-Github/examples/xipfs/bin/nrf52840dk/xipfs.elf
   text    data     bss     dec     hex filename
 137544    4368    5716  147628   240ac /home/greg/Documents/code/RIOT-Github/examples/xipfs/bin/nrf52840dk/xipfs.elf

I tried to collect the community's opinions in the forum about this workaround, but no luck yet.

Before pushing this change in the PR, I'll be interested if you or someone would have a proper way to fix it.

@crasbe
Copy link
Contributor

crasbe commented Feb 17, 2025

I would assume that this line is creating the error:

https://github.com/GGuche-2XS-Univ-Lille/RIOT/blob/3b1d69ade827f4cc684a00f7fbe0da1e3deac0c2/pkg/xipfs/Makefile.include#L2

The fake folder is not a good solution IMO.

@GGuche-2XS-Univ-Lille
Copy link
Author

My bad. I feel so stupid.
I've inherited this project not so long ago, and did not think about this line.
Please accept all my apologies.

@crasbe
Copy link
Contributor

crasbe commented Feb 17, 2025

No need to apologize. The RIOT build system can be quite a beast and integrating a package that wasn't designed for RIOT is not an easy task.

Unfortunately I don't know when exactly I'll have access to my devboards again, so I can't test it at the moment. At some point this week or next week for sure though.


I think the examples/xipfs-test application would be more at home in tests/pkg/xipfs.

Also, with the recent merge of #21135 which restructured the examples folder, your example code has to find a new subdirectory as well.
I don't really have a suggestion yet, so: ping @mguetschow and @AnnsAnns

@@ -142,3 +142,5 @@ Here is a quick overview of the examples available in the RIOT:
| [twr_aloha](./twr_aloha/README.md) | This example allows testing different two-way ranging algorithms between two boards supporting a dw1000 device. This makes use of the uwb-core pkg. |
| [senml_saul](./senml_saul/README.md) | This example demonstrates the usage of the SAUL (Sensor Actuator Uber Layer) module with the SenML (Sensor Measurement Lists) format. |
| [opendsme](./opendsme/README.md) | This example demonstrates the usage of the OpenDSME module in RIOT. |
| [xipfs](./xipfs/README.md) | This simple example demonstrates the usage of XIPFS for creating and executing an executable file. |

This comment was marked as resolved.

@github-actions github-actions bot added the Area: tests Area: tests and testing framework label Feb 19, 2025
Copy link
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

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

Some minor issues and comments that happened due to the move of the tests to the test folder.

$(RIOTBASE) didn't get updated, but you can just use the common makefile as outlined in the comments.

@@ -0,0 +1,28 @@
# name of your application
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# name of your application
include ../Makefile.pkg_common
# name of your application

You can use the common pkg-test makefile which defines the $(RIOTBASE).

Comment on lines +1 to +3
# name of your application
APPLICATION = xipfs-test

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# name of your application
APPLICATION = xipfs-test

Tests don't necessarily need a name, it will be automatically generated.

Comment on lines +7 to +8
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

See above, $(RIOTBASE) is defined in the common Makefile.


int ret = xipfs_extended_driver_new_file(argv[1], (uint32_t)file_size, 1);
if (ret != 0) {
printf("Failed to create '%s' as an XIPFS executable file.", argv[1]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
printf("Failed to create '%s' as an XIPFS executable file.", argv[1]);
printf("Failed to create '%s' as an XIPFS executable file.\n", argv[1]);

A newline is missing here.

@crasbe
Copy link
Contributor

crasbe commented Feb 19, 2025

I gave this a test with an nRF52DK devboard and the tests run successfully:

Then I tested the example and it did work for small executables (10 bytes), but resulted in a stack overflow for larger executables (123 bytes).

In this log, the file test already existed and therefore the error happened. I guess that error=-2 is expected for an empty executeable?

main(): This is RIOT! (Version: 2025.01)
vfs_mount: "/dev/nvme0p0": OK
vfs_mount: "/dev/nvme0p1": OK
> help
Command              Description
---------------------------------------
pm                   interact with layered PM subsystem
ps                   Prints information about running threads.
version              Prints current RIOT_VERSION
reboot               Reboot the node
vfs                  virtual file system operations
ls                   list files
create_executable    Create an XIPFS executable file
execute              Execute an XIPFS file
> create_executable /dev/nvme0p0/test 10
Failed to create '/dev/nvme0p0/test' as an XIPFS executable file.
> create_executable /dev/nvme0p0/test2 10
> execute /dev/nvme0p0/test1
Failed to execute '/dev/nvme0p0/test1', error=-2
> vfs r /dev/nvme0p0/test 10
-- EOF --
> 
main(): This is RIOT! (Version: 2025.01)
vfs_mount: "/dev/nvme0p0": OK
vfs_mount: "/dev/nvme0p1": OK
> help
Command              Description
---------------------------------------
pm                   interact with layered PM subsystem
ps                   Prints information about running threads.
version              Prints current RIOT_VERSION
reboot               Reboot the node
vfs                  virtual file system operations
ls                   list files
create_executable    Create an XIPFS executable file
execute              Execute an XIPFS file
> create_executable /dev/nvme0p0/test 123
> ls /dev/nvme0p0
test
total 1 files
> execute /dev/nvme0p0/test

Context before hardfault:
   r0: 0x20001e5c
   r1: 0x0000b1a4
   r2: 0x2000226c
   r3: 0x20001e58
  r12: 0x00000000
   lr: 0x00004127
   pc: 0x0000b1a4
  psr: 0x21030200

FSR/FAR:
 CFSR: 0x00010000
 HFSR: 0x40000000
 DFSR: 0x00000000
 AFSR: 0x00000000
Misc
EXC_RET: 0xfffffffd
Active thread: 1 "main"
Attempting to reconstruct state for debugging...
In GDB:
  set $pc=0xb1a4
  frame 0
  bt

ISR stack overflowed by at least 16 bytes.
*** RIOT kernel panic:
HARD FAULT HANDLER

<9>pid | name                 | state    Q | pri | stack  ( used) ( free) | base addr  | current     
<9>  - | isr_stack            | -        - |   - |    512 (  492) (   20) | 0x20000000 | 0x2000220c
<9>  1 | main                 | running  Q |   7 |   1536 ( 1096) (  440) | 0x200013d8 | 0x2000195c 
<9>    | SUM                  |            |     |   2048 ( 1588) (  460)

*** halted.

Inside isr -13

What's a bit odd is that vfs ls only shows one file, even though multiple files exist. In this case, the file test and test2 exist and XIPFS throws an error when I try to create another test2 file. The test1 doesn't exist yet, so it is successfully created, but only test shows up in the ls command.

> create_executable /dev/nvme0p0/test2 10
Failed to create '/dev/nvme0p0/test2' as an XIPFS executable file.
> create_executable /dev/nvme0p0/test1 10
> vfs ls /dev/nvme0p0
test
total 1 files
> vfs r /dev/nvme0p0/test1 10
Error opening file "/dev/nvme0p0/test1": -ENOENT
> 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: build system Area: Build system Area: doc Area: Documentation Area: examples Area: Example Applications Area: pkg Area: External package ports Area: sys Area: System Area: tests Area: tests and testing framework
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants