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 16 commits into
base: 2025.01-branch
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ ifneq (,$(filter lwext%_vfs,$(USEMODULE)))
USEMODULE += vfs
endif

ifneq (,$(filter xipfs,$(USEMODULE)))
USEPKG += xipfs
USEMODULE += vfs
USEMODULE += xipfs_fs
endif

ifneq (,$(filter nimble_%,$(USEMODULE)))
USEPKG += nimble
endif
Expand Down
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

| [xipfs-tests](./xipfs-tests/README.md) | This example consists of XIPFS tests and demonstrate how to use XIPFS API. |
30 changes: 30 additions & 0 deletions examples/xipfs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# name of your application
APPLICATION = xipfs

# If no BOARD is found in the environment, use this default:
BOARD ?= dwm1001

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

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

BLOBS += hello-world.fae

# Modules to include:
USEMODULE += shell
USEMODULE += shell_cmds_default
USEMODULE += ps

# Use xipfs file system
USEMODULE += xipfs

CFLAGS +=-DRIOT_OS

include $(RIOTBASE)/Makefile.include
61 changes: 61 additions & 0 deletions examples/xipfs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# The eXecute In-Place File System

## Description

`xipfs` is a file system designed to streamline post-issuance software
deployment. `xipfs` allows direct execution of programs from flash
memory, eliminating the need for prior copying to RAM. This approach
conserves memory space and accelerates boot times, as the
microcontroller can run code directly from storage memory without
preloading into RAM.

The `xipfs` structure is based on a linked list, where each file
occupies at least one flash memory page. To prevent fragmentation, when
a file is deleted, subsequent files are shifted to fill the vacant
space.

`xipfs` is compatible with all microcontrollers featuring addressable
flash memory and most operating systems, provided they implement the
necessary functions to interact with the flash controller.

## Limitations

`xipfs` has the following limitations:

- No journaling: `xipfs` doesn't provide journaling. Without journaling,
the file system cannot keep track of changes in a way that allows for
recovery in the event of a crash or power failure. This can lead to
data corruption and loss.

- No checksums: `xipfs` doesn't provide checksums. The lack of checksums
means that the file system cannot verify the integrity of files. This
increases the risk of undetected data corruption, as there is no
mechanism to ensure that files have not been altered or damaged.

- Global file system lock: `xipfs` needs a global file system lock. A
global file system lock can lead to performance bottlenecks, as it
prevents multiple threads from accessing the file system
simultaneously.

- Fixed file size: `xipfs` provide fixed file size. By default, a file
created using `vfs_open(2)` has a fixed space reserved in flash that
is the size of a flash page. This size cannot be extended later. To
create a file larger than the fixed size of one flash page, the
`mk(1)` command or the `xipfs_new_file(3)` function must be used.

- Limited character set: `xipfs` supports only a subset of 7-bit ASCII
characters, specifically `[0-9A-Za-z\/\.\-_]`.

- Limited path length: `xipfs` maximum path length is 64 characters.

## Tested cards

`xipfs` is expected to be compatible with all boards that feature
addressable NVM. However, only the `DWM1001` board has been tested and
is confirmed to function correctly.

## Funding

The `xipfs` project is part of the TinyPART project funded by the
MESRI-BMBF German-French cybersecurity program under grant agreements
n°ANR-20-CYAL-0005 and 16KIS1395K.
Binary file added examples/xipfs/hello-world.fae
Binary file not shown.
230 changes: 230 additions & 0 deletions examples/xipfs/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* Copyright (C) 2024 Université de Lille
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup examples
* @{
*
* @file
* @brief An application demonstrating xipfs
*
* @author Damien Amara <damien.amara@univ-lille.fr>
* @author Gregory Guche <gregory.guche@univ-lille.fr>
*
* @}
*/

#include <fcntl.h>
#include <stdlib.h>

#include "fs/xipfs_fs.h"
#include "periph/flashpage.h"
#include "shell.h"
#include "vfs.h"

/**
* @def PANIC
*
* @brief This macro handles fatal errors
*/
#define PANIC() for (;;);

/**
* @def NVME0P0_PAGE_NUM
*
* @brief The number of flash page for the nvme0p0 file system
*/
#define NVME0P0_PAGE_NUM 10

/**
* @def NVME0P1_PAGE_NUM
*
* @brief The number of flash page for the nvme0p1 file system
*/
#define NVME0P1_PAGE_NUM 15

/*
* Allocate a new contiguous space for the nvme0p0 file system
*/
XIPFS_NEW_PARTITION(nvme0p0, "/dev/nvme0p0", NVME0P0_PAGE_NUM);

/*
* Allocate a new contiguous space for the nvme0p1 file system
*/
XIPFS_NEW_PARTITION(nvme0p1, "/dev/nvme0p1", NVME0P1_PAGE_NUM);

#ifdef BOARD_DWM1001

/**
* @brief hello-world.fae data blob.
*
* To create a *.fae file, you will need to clone the master branch of
* xipfs_format, that can be found at https://github.com/2xs/XiPFS_Format .
*
* Then modify the Makefile to suit your needs/sources and call make.
* You should end up with a *.bin file ready to be uploaded.
*/
#include "blob/hello-world.fae.h"

#define FILENAME_OF_HELLO_WORLD_FAE "/dev/nvme0p0/hello-world.fae"
#define SIZEOF_HELLO_WORLD_BIN (sizeof(hello_world_fae) / sizeof(hello_world_fae[0]))

/**
* @brief Execution in-place demonstrator.
*
* This shell command handler will create a file hello-world.fae on /dev/nvme0p0,
* if none exists yet from the data blob above.
* Then, it will execute this file.
*
* Once the file has been created, execute command can be used to rerun
* the executable file as many times as wanted.
*
* ```bash
* 2025-01-14 09:48:36,303 # main(): This is RIOT! (Version: 2024.10)
* 2025-01-14 09:48:36,307 # vfs_mount: "/dev/nvme0p0": OK
* 2025-01-14 09:48:36,313 # vfs_mount: "/dev/nvme0p1": OK
* > help
* 2025-01-14 09:48:42,300 # help
* 2025-01-14 09:48:42,302 # Command Description
* 2025-01-14 09:48:42,305 # ---------------------------------------
* 2025-01-14 09:48:42,309 # exec Execute Hello World
* 2025-01-14 09:48:42,314 # create_executable Create an XIPFS executable file
* 2025-01-14 09:48:42,317 # execute Execute an XIPFS file
* 2025-01-14 09:48:42,320 # ls list files
* 2025-01-14 09:48:42,325 # pm interact with layered PM subsystem
* 2025-01-14 09:48:42,331 # ps Prints information about running threads.
* 2025-01-14 09:48:42,334 # reboot Reboot the node
* 2025-01-14 09:48:42,338 # version Prints current RIOT_VERSION
* 2025-01-14 09:48:42,343 # vfs virtual file system operations
* > exec
* 2025-01-14 09:48:49,572 # exec
* 2025-01-14 09:48:49,573 # Hello World!
* > ls /dev/nvme0p0
* 2025-01-14 09:48:59,997 # ls /dev/nvme0p0
* 2025-01-14 09:48:59,999 # hello-world.fae 896 B
* 2025-01-14 09:49:00,000 # total 1 files
* > vfs df
* 2025-01-14 09:49:04,957 # vfs df
* 2025-01-14 09:49:04,962 # Mountpoint Total Used Available Use%
* 2025-01-14 09:49:04,968 # /dev/nvme0p0 40 KiB 4 KiB 36 KiB 10%
* 2025-01-14 09:49:04,974 # /dev/nvme0p1 60 KiB 0 B 60 KiB 0%
* execute /dev/nvme0p0/hello-world.fae ipsum dolores it
* 2025-01-14 09:49:14,223 # execute /dev/nvme0p0/hello-world.fae Lorem ipsum dolor sit amet
* 2025-01-14 09:49:14,225 # Hello World!
* 2025-01-14 09:49:14,225 # Lorem
* 2025-01-14 09:49:14,226 # ipsum
* 2025-01-14 09:49:14,226 # dolor
* 2025-01-14 09:49:14,227 # sit
* 2025-01-14 09:49:14,227 # amet
* ```
*/
int execution_handler(int argc, char **argv) {

(void)argc;
(void)argv;

int file_handle = vfs_open(FILENAME_OF_HELLO_WORLD_FAE, O_RDONLY, 0);
if (file_handle < 0) {

/** There's no executable file yet, let's drop one */
int ret = xipfs_extended_driver_new_file(
FILENAME_OF_HELLO_WORLD_FAE, SIZEOF_HELLO_WORLD_BIN, 1
);
if (ret < 0) {
printf("xipfs_extended_driver_new_file : failed to create '%s' : error=%d\n",
FILENAME_OF_HELLO_WORLD_FAE, ret);
return EXIT_FAILURE;
}

/**
* Fill it with blob data
* Take care : vfs does not support O_APPEND with vfs_write, only O_WRONLY or O_RDWR
*/
file_handle = vfs_open(FILENAME_OF_HELLO_WORLD_FAE, O_WRONLY, 0);
if (file_handle < 0) {
printf("vfs_open : failed to open '%s' : error =%d\n",
FILENAME_OF_HELLO_WORLD_FAE, file_handle);
return EXIT_FAILURE;
}

ssize_t write_ret = vfs_write(file_handle, hello_world_fae, SIZEOF_HELLO_WORLD_BIN);
if (write_ret < 0) {
printf("vfs_write : failed to fill '%s' : error=%d\n",
FILENAME_OF_HELLO_WORLD_FAE, write_ret);
vfs_close(file_handle);
return EXIT_FAILURE;
}
}

vfs_close(file_handle);

char *exec_argv[] = {
FILENAME_OF_HELLO_WORLD_FAE,
NULL
};
int ret = xipfs_extended_driver_execv(FILENAME_OF_HELLO_WORLD_FAE, exec_argv);
if (ret < 0) {
printf("Failed to execute '%s' : error=%d\n", FILENAME_OF_HELLO_WORLD_FAE, ret);
return EXIT_FAILURE;
}

return EXIT_SUCCESS;
}

static shell_command_t shell_commands[] = {
{"exec", "Execute Hello World", execution_handler},
{NULL, NULL, NULL},
};

#else // BOARD_DWM1001

#define shell_commands NULL

#endif // BOARD_DWM1001


/**
* @internal
*
* @brief Mount a partition, or if it is corrupted, format and
* remount it
*
* @param xipfs_mp A pointer to a memory region containing an
* xipfs mount point structure
*/

static void mount_or_format(vfs_xipfs_mount_t *xipfs_mp)
{
if (vfs_mount(&xipfs_mp->vfs_mp) < 0) {
printf("vfs_mount: \"%s\": file system has not been "
"initialized or is corrupted\n", xipfs_mp->vfs_mp.mount_point);
printf("vfs_format: \"%s\": try initializing it\n",
xipfs_mp->vfs_mp.mount_point);
vfs_format(&xipfs_mp->vfs_mp);
printf("vfs_format: \"%s\": OK\n", xipfs_mp->vfs_mp.mount_point);
if (vfs_mount(&xipfs_mp->vfs_mp) < 0) {
printf("vfs_mount: \"%s\": file system is corrupted!\n",
xipfs_mp->vfs_mp.mount_point);
PANIC();
}
}
printf("vfs_mount: \"%s\": OK\n", xipfs_mp->vfs_mp.mount_point);
}


int main(void)
{
char line_buf[SHELL_DEFAULT_BUFSIZE];

mount_or_format(&nvme0p0);
mount_or_format(&nvme0p1);

shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);

return 0;
}
1 change: 1 addition & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ PSEUDOMODULES += shell_cmd_sntp
PSEUDOMODULES += shell_cmd_suit
PSEUDOMODULES += shell_cmd_sys
PSEUDOMODULES += shell_cmd_udptty
PSEUDOMODULES += shell_cmd_xipfs
PSEUDOMODULES += shell_cmd_vfs
PSEUDOMODULES += shell_cmds_default
PSEUDOMODULES += shell_hooks
Expand Down
15 changes: 15 additions & 0 deletions pkg/xipfs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
PKG_NAME=xipfs
PKG_URL=https://github.com/2xs/xipfs.git
PKG_VERSION=23f61316430d6cdbc6162e816ab32257a724492d
PKG_LICENSE=CeCILL-2.1

include $(RIOTBASE)/pkg/pkg.mk

export RIOT_INCLUDES=$(INCLUDES)
export RIOT_CFLAGS=$(CFLAGS)

all: $(BINDIR)/xipfs.a

$(BINDIR)/xipfs.a:
$(QQ)"$(MAKE)" -C $(PKG_SOURCE_DIR)
@cp $(PKG_SOURCE_DIR)/xipfs.a $@
12 changes: 12 additions & 0 deletions pkg/xipfs/Makefile.dep
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
DEFAULT_MODULE += xipfs_fs

# require xipfs_fs dependencies if this module isn't disabled
ifeq (,$(filter xipfs_fs,$(DISABLE_MODULE)))
# xipfs only makes sense if the flash memory is addressable
FEATURES_REQUIRED += periph_flashpage_in_address_space
FEATURES_OPTIONAL += periph_flashpage_aux
USEMODULE += periph_flashpage
USEMODULE += mtd_flashpage
USEMODULE += vfs
USEMODULE += shell_cmd_xipfs
endif
8 changes: 8 additions & 0 deletions pkg/xipfs/Makefile.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
INCLUDES += -I$(PKGDIRBASE)/xipfs

ifneq (,$(filter xipfs_fs,$(USEMODULE)))
DIRS += $(RIOTBASE)/pkg/xipfs/fs
endif

# include archive
ARCHIVES += $(BINDIR)/xipfs.a
Loading