1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-13 08:33:49 +01:00

pkg/xipfs: add XIPFS as vfs module

This commit is contained in:
Gregory Guche 2025-06-30 14:39:11 +02:00
parent 416db07ac5
commit 8dc500703b
24 changed files with 4100 additions and 15 deletions

View File

@ -83,6 +83,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

View File

@ -141,6 +141,7 @@ Here is a quick overview of the examples available in the RIOT:
| [twr_aloha](./advanced/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](./advanced/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](./advanced/opendsme/README.md) | This example demonstrates the usage of the OpenDSME module in RIOT. |
| [xipfs](./advanced/xipfs/README.md) | This example demonstrates the usage of XIPFS for creating and executing an executable file. |
## Examples from Guides

View File

@ -0,0 +1,32 @@
# 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
# XIPFS is currently not compatible with the llvm toolchain
TOOLCHAINS_BLACKLIST += llvm
BLOBS += hello-world.fae
# Modules to include:
USEMODULE += shell
USEMODULE += shell_cmds_default
USEMODULE += ps
USEMODULE += saul_default
# Use xipfs file system
USEMODULE += xipfs
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,24 @@
BOARD_INSUFFICIENT_MEMORY := \
blackpill-stm32f103c8 \
bluepill-stm32f030c8 \
bluepill-stm32f103c8 \
i-nucleo-lrwan1 \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f302r8 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
samd10-xmini \
slstk3400a \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
weact-g030f6 \
#

View File

@ -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.
- Per mountpoint file system lock: `xipfs` needs a file system lock per
mountpoint. Such a mechanism can lead to performance bottlenecks,
as it prevents multiple threads from accessing the file system montpoint
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 not shown.

View File

@ -0,0 +1,227 @@
/*
* 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 *.fae 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_FAE (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_FAE, 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_FAE);
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 */
static shell_command_t shell_commands[] = { {NULL, NULL, 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;
}

View File

@ -443,6 +443,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

17
pkg/xipfs/Makefile Normal file
View File

@ -0,0 +1,17 @@
PKG_NAME=xipfs
PKG_URL=https://github.com/2xs/xipfs.git
PKG_VERSION=962c5edc55abb6363ff2988dc3b2c3c293362e96
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)/$(BOARD)/xipfs.a $@
.PHONY: $(BINDIR)/xipfs.a

13
pkg/xipfs/Makefile.dep Normal file
View File

@ -0,0 +1,13 @@
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_REQUIRED += arch_32bit arch_arm
FEATURES_OPTIONAL += periph_flashpage_aux
USEMODULE += periph_flashpage
USEMODULE += mtd_flashpage
USEMODULE += vfs
USEMODULE += shell_cmd_xipfs
endif

View File

@ -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

32
pkg/xipfs/doc.txt Normal file
View File

@ -0,0 +1,32 @@
/**
* @defgroup pkg_xipfs xipfs file system
* @ingroup pkg
* @ingroup sys_fs
* @brief The eXecute In-Place File System
*
*`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.
*
* **To have a viable executable file within XiPFS**, please follow these steps :
* - create a file with executable flag.
* - Fill it with actual compiled code in an appropriate file format, here XiPFS-Format.
* *This is where the actual startup sequence and relocation happen*.
* - Call execute on it with appropriate args *at least the executable filename should be passed along*.
*
* @see https://github.com/2xs/xipfs
* @see https://github.com/2xs/XiPFS_Format
*/

5
pkg/xipfs/fs/Makefile Normal file
View File

@ -0,0 +1,5 @@
MODULE := xipfs_fs
CFLAGS+=-DRIOT_OS
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,41 @@
/*
* 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 sys_fs_xipfs
* @{
*
* @file
* @brief xipfs integration with flashpage
*
* @author Damien Amara <damien.amara@univ-lille.fr>
*
* @}
*/
#include "periph/flashpage.h"
void *xipfs_nvm_addr(unsigned page)
{
return flashpage_addr(page);
}
void xipfs_nvm_erase(unsigned page)
{
flashpage_erase(page);
}
unsigned xipfs_nvm_page(const void *addr)
{
return flashpage_page(addr);
}
void xipfs_nvm_write(void *target_addr, const void *data, size_t len)
{
flashpage_write(target_addr, data, len);
}

790
pkg/xipfs/fs/xipfs_fs.c Normal file
View File

@ -0,0 +1,790 @@
/*
* 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 sys_fs_xipfs
* @{
*
* @file
* @brief xipfs integration with vfs
*
* @author Damien Amara <damien.amara@univ-lille.fr>
* @author Gregory Guche <gregory.guche@univ-lille.fr>
*
* @}
*/
/*
* The following define is required in order to use strnlen(3)
* since glibc 2.10. Refer to the SYNOPSIS section of the
* strnlen(3) manual and the feature_test_macros(7) manual for
* more information
*/
#define _POSIX_C_SOURCE 200809L
/*
* libc includes
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <mutex.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
/*
* RIOT includes
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "fs/xipfs_fs.h"
#include "periph/flashpage.h"
#include "saul_reg.h"
/*
* xipfs includes
*/
#include "include/buffer.h"
#include "include/errno.h"
#include "include/file.h"
#include "include/flash.h"
#include "include/fs.h"
#include "include/path.h"
#ifndef RIOT_OS
#include "include/xipfs.h"
#endif
/*
* The eXecute In Place File System only makes sense if the
* non-volatile memory of the target MCU is addressable
*/
#ifndef MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE
#error "sys/fs/xipfs: the target MCU has no addressable NVM"
#endif /* !MODULE_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE */
/*
* Helper functions
*/
/**
* @internal
*
* @pre dir must be a pointer that references an accessible
* memory region
*
* @pre path must be a pointer that references a path which is
* accessible, null-terminated, starts with a slash, normalized,
* and shorter than XIPFS_PATH_MAX
*
* @brief Copy the directory name component of path, including
* the final slash, into the memory region pointed to by dir
*
* @param dir A pointer to a memory region that respects the
* preconditions for storing the directory name component
*
* @param path A pointer to a path that respects the
* preconditions
*/
static void dirname(char *dir, const char *path)
{
const char *end;
size_t len, i;
assert(dir != NULL);
assert(path != NULL);
assert(path[0] == '/');
if (path[1] == '\0') {
dir[0] = '/';
dir[1] = '\0';
return;
}
len = strnlen(path, XIPFS_PATH_MAX);
assert(len < XIPFS_PATH_MAX);
end = path + len - 1;
if (*end == '/') {
/* skip the trailing slash if the
* path ends with one */
end--;
}
while (end > path && *end != '/') {
/* skip all characters that are not
* slashes */
end--;
}
if (end != path) {
for (i = 0; path + i <= end; i++) {
/* copy the characters of the directory
* name component until end */
dir[i] = path[i];
}
dir[i] = '\0';
} else {
/* no slashes found, except for the root */
dir[0] = '/';
dir[1] = '\0';
}
}
/**
* @internal
*
* @warning This function provides a workaround for xipfs-
* specific functions that need to retrieve the xipfs mount
* point structure directly, bypassing the VFS layer, as these
* functions are not available in the VFS abstraction
*
* @brief Retrieves the xipfs mount point structure from a
* specified path within the mount point
*
* @param path A path within the mount point for retrieving the
* corresponding xipfs mount point structure
*
* @param mp A pointer to an accessible memory region for
* storing the xipfs mount point structure
*
* @return Returns zero if the function succeeds or a negative
* value otherwise
*/
static int get_xipfs_mp(const char *path, xipfs_mount_t *xipfs_mp)
{
char dir[XIPFS_PATH_MAX];
size_t count, len;
int fd, ret;
if (path[0] != '/') {
return -EINVAL;
}
len = strnlen(path, XIPFS_PATH_MAX);
if (len == XIPFS_PATH_MAX) {
return -ENAMETOOLONG;
}
dirname(dir, path);
if (len + strlen(".xipfs_infos") + 1 > XIPFS_PATH_MAX) {
return -ENAMETOOLONG;
}
(void)strcat(dir, ".xipfs_infos");
if ((ret = vfs_open(dir, O_RDONLY, 0)) < 0) {
/* not a xipfs mount point */
return ret;
}
fd = ret;
count = sizeof(*xipfs_mp);
while (count > 0) {
ret = vfs_read(fd, xipfs_mp, count);
if (ret < 0) {
/* error */
return ret;
}
count -= ret;
xipfs_mp += ret;
if (ret == 0) {
/* EOF */
break;
}
}
assert(count == 0);
(void)vfs_close(fd);
return 0;
}
/**
* @internal
*
* @pre full_path must be a pointer that references a path which
* is accessible, null-terminated, starts with a slash,
* normalized, and shorter than XIPFS_PATH_MAX
*
* @brief Returns a pointer to the first character of the
* relative path derived from the absolute path retrieved from
* the vfs_mp mount point structure
*
* @param vfs_mp A pointer to a memory region containing an
* accessible VFS mount point structure
*
* @param full_path A pointer to a path that respects the
* preconditions
*/
static const char *get_rel_path(xipfs_mount_t *mp,
const char *full_path)
{
const char *p1, *p2;
assert(mp != NULL);
assert(full_path != NULL);
p1 = mp->mount_path;
p2 = full_path;
while (*p1 != '\0') {
if (*p1++ != *p2++) {
return NULL;
}
}
return p2;
}
static inline xipfs_mount_t *_get_xipfs_mount_t(vfs_mount_t *vfs_mp)
{
vfs_xipfs_mount_t *vfs_xipfs_mp;
xipfs_mount_t *xipfs_mp;
vfs_xipfs_mp = (vfs_xipfs_mount_t *)(uintptr_t)vfs_mp;
xipfs_mp = (xipfs_mount_t *)(uintptr_t)&vfs_xipfs_mp->magic;
return xipfs_mp;
}
/*
* Operations on open files
*/
static int _close(vfs_file_t *filp)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_close(mp, descp);
mutex_unlock(mp->mutex);
return ret;
}
static int _fstat(vfs_file_t *filp, struct stat *buf)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_fstat(mp, descp, buf);
mutex_unlock(mp->mutex);
return ret;
}
static off_t _lseek(vfs_file_t *filp, off_t off, int whence)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_lseek(mp, descp, off, whence);
mutex_unlock(mp->mutex);
return ret;
}
static int _open(vfs_file_t *filp, const char *name, int flags,
mode_t mode)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_open(mp, descp, name, flags, mode);
mutex_unlock(mp->mutex);
return ret;
}
static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_read(mp, descp, dest, nbytes);
mutex_unlock(mp->mutex);
return ret;
}
static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_write(mp, descp, src, nbytes);
mutex_unlock(mp->mutex);
return ret;
}
static int _fsync(vfs_file_t *filp)
{
xipfs_file_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(filp->mp);
descp = (xipfs_file_desc_t *)(uintptr_t)&filp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_fsync(mp, descp, filp->pos);
mutex_unlock(mp->mutex);
return ret;
}
/*
* Operations on open directories
*/
static int _opendir(vfs_DIR *dirp, const char *dirname)
{
xipfs_dir_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(dirp->mp);
descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_opendir(mp, descp, dirname);
mutex_unlock(mp->mutex);
return ret;
}
static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
{
xipfs_dir_desc_t *descp;
xipfs_dirent_t *direntp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(dirp->mp);
descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr;
direntp = (xipfs_dirent_t *)(uintptr_t)entry->d_name;
mutex_lock(mp->mutex);
ret = xipfs_readdir(mp, descp, direntp);
mutex_unlock(mp->mutex);
return ret;
}
static int _closedir(vfs_DIR *dirp)
{
xipfs_dir_desc_t *descp;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(dirp->mp);
descp = (xipfs_dir_desc_t *)(uintptr_t)&dirp->private_data.ptr;
mutex_lock(mp->mutex);
ret = xipfs_closedir(mp, descp);
mutex_unlock(mp->mutex);
return ret;
}
/*
* Operations on mounted file systems
*/
static int _format(vfs_mount_t *vfs_mp)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->execution_mutex);
mutex_lock(mp->mutex);
ret = xipfs_format(mp);
mutex_lock(mp->mutex);
mutex_lock(mp->execution_mutex);
return ret;
}
static int _mount(vfs_mount_t *vfs_mp)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_mount(mp);
mutex_unlock(mp->mutex);
return ret;
}
static int _umount(vfs_mount_t *vfs_mp)
{
(void)vfs_mp;
/* nothing to do */
return 0;
}
static int _unlink(vfs_mount_t *vfs_mp, const char *name)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->execution_mutex);
mutex_lock(mp->mutex);
ret = xipfs_unlink(mp, name);
mutex_unlock(mp->mutex);
mutex_lock(mp->execution_mutex);
return ret;
}
static int _mkdir(vfs_mount_t *vfs_mp, const char *name, mode_t mode)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_mkdir(mp, name, mode);
mutex_unlock(mp->mutex);
return ret;
}
static int _rmdir(vfs_mount_t *vfs_mp, const char *name)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->execution_mutex);
mutex_lock(mp->mutex);
ret = xipfs_rmdir(mp, name);
mutex_unlock(mp->mutex);
mutex_unlock(mp->execution_mutex);
return ret;
}
static int _rename(vfs_mount_t *vfs_mp, const char *from_path,
const char *to_path)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_rename(mp, from_path, to_path);
mutex_unlock(mp->mutex);
return ret;
}
static int _stat(vfs_mount_t *vfs_mp, const char *restrict path,
struct stat *restrict buf)
{
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_stat(mp, path, buf);
mutex_unlock(mp->mutex);
return ret;
}
static int _statvfs(vfs_mount_t *vfs_mp, const char *restrict path,
struct statvfs *restrict buf)
{
struct xipfs_statvfs xipfs_buf;
xipfs_mount_t *mp;
int ret;
mp = _get_xipfs_mount_t(vfs_mp);
mutex_lock(mp->mutex);
ret = xipfs_statvfs(mp, path, &xipfs_buf);
mutex_unlock(mp->mutex);
(void)memset(buf, 0, sizeof(*buf));
buf->f_bsize = xipfs_buf.f_bsize;
buf->f_frsize = xipfs_buf.f_frsize;
buf->f_blocks = xipfs_buf.f_blocks;
buf->f_bfree = xipfs_buf.f_bfree;
buf->f_bavail = xipfs_buf.f_bavail;
buf->f_flag = xipfs_buf.f_flag;
buf->f_namemax = xipfs_buf.f_namemax;
return ret;
}
/*
* xipfs-specific functions
*/
int xipfs_extended_driver_new_file(const char *full_path, uint32_t size, uint32_t exec)
{
xipfs_mount_t mp;
const char *path;
int ret;
if (full_path == NULL) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
mutex_lock(mp.mutex);
ret = xipfs_new_file(&mp, path, size, exec);
mutex_unlock(mp.mutex);
return ret;
}
static int get_temperature(void) {
phydat_t physical_data;
saul_reg_t *device = saul_reg_find_type(SAUL_SENSE_TEMP);
int res = saul_reg_read(device, &physical_data);
if (res < 0) {
return res;
}
return physical_data.val[0];
}
static int get_led(int pos) {
phydat_t physical_data;
saul_reg_t *device = saul_reg_find_nth(pos);
int res = saul_reg_read(device, &physical_data);
if (res < 0) {
return res;
}
return physical_data.val[0];
}
static int set_led(int pos, int val) {
phydat_t physical_data;
saul_reg_t *device = saul_reg_find_nth(pos);
physical_data.val[0] = val;
int res = saul_reg_write(device, &physical_data);
return res;
}
static int get_file_size(const char *full_path, size_t *size) {
xipfs_mount_t mp;
const char *path;
int ret;
struct stat buf;
if ((full_path == NULL) || (size == NULL)) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
*size = 0;
ret = xipfs_stat(&mp, path, &buf);
if (ret < 0) {
return ret;
}
*size = buf.st_size;
return 0;
}
static ssize_t copy_file(const char *full_path, void *buf, size_t nbyte) {
xipfs_mount_t mp;
const char *path;
xipfs_file_desc_t desc;
int ret;
size_t file_size;
ret = get_file_size(full_path, &file_size);
if (ret < 0) {
return ret;
}
if (nbyte > file_size) {
nbyte = file_size;
}
if (full_path == NULL) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
ret = xipfs_open(&mp, &desc, path, O_RDONLY, 0);
if (ret < 0) {
return ret;
}
ret = xipfs_read(&mp, &desc, buf, nbyte);
if (ret < 0) {
return ret;
}
ret = xipfs_close(&mp, &desc);
return nbyte;
}
static const void *xipfs_user_syscalls_table[XIPFS_USER_SYSCALL_MAX] = {
[ XIPFS_USER_SYSCALL_PRINTF] = vprintf,
[ XIPFS_USER_SYSCALL_GET_TEMP] = get_temperature,
[ XIPFS_USER_SYSCALL_ISPRINT] = isprint,
[ XIPFS_USER_SYSCALL_STRTOL] = strtol,
[ XIPFS_USER_SYSCALL_GET_LED] = get_led,
[ XIPFS_USER_SYSCALL_SET_LED] = set_led,
[ XIPFS_USER_SYSCALL_COPY_FILE] = copy_file,
[XIPFS_USER_SYSCALL_GET_FILE_SIZE] = get_file_size,
[ XIPFS_USER_SYSCALL_MEMSET] = memset
};
int xipfs_extended_driver_execv(const char *full_path, char *const argv[])
{
xipfs_mount_t mp;
const char *path;
int ret;
if (full_path == NULL) {
return -EFAULT;
}
if ((ret = get_xipfs_mp(full_path, &mp)) < 0) {
return ret;
}
if ((path = get_rel_path(&mp, full_path)) == NULL) {
return -EIO;
}
mutex_lock(mp.execution_mutex);
ret = xipfs_execv(&mp, path, argv, xipfs_user_syscalls_table);
mutex_unlock(mp.execution_mutex);
return ret;
}
/*
* File system driver structures
*/
static const vfs_file_ops_t xipfs_file_ops = {
.close = _close,
.fstat = _fstat,
.lseek = _lseek,
.open = _open,
.read = _read,
.write = _write,
.fsync = _fsync,
};
static const vfs_dir_ops_t xipfs_dir_ops = {
.opendir = _opendir,
.readdir = _readdir,
.closedir = _closedir,
};
static const vfs_file_system_ops_t xipfs_fs_ops = {
.format = _format,
.mount = _mount,
.umount = _umount,
.unlink = _unlink,
.mkdir = _mkdir,
.rmdir = _rmdir,
.rename = _rename,
.stat = _stat,
.statvfs = _statvfs,
};
const vfs_file_system_t xipfs_file_system = {
.fs_op = &xipfs_fs_ops,
.f_op = &xipfs_file_ops,
.d_op = &xipfs_dir_ops,
};
int xipfs_construct_from_flashpage(mtd_flashpage_t *flashpage, const char *path,
mutex_t *execution_mutex, mutex_t *mutex,
vfs_xipfs_mount_t *vfs_xipfs_mount) {
if ( (flashpage == NULL) || (path == NULL) || (path[0] == '\0')
|| (execution_mutex == NULL) || (mutex == NULL) ) {
return -EINVAL;
}
vfs_xipfs_mount->vfs_mp.fs = &xipfs_file_system;
vfs_xipfs_mount->vfs_mp.mount_point = path;
vfs_xipfs_mount->magic = XIPFS_MAGIC;
vfs_xipfs_mount->mount_path = path;
vfs_xipfs_mount->page_num = flashpage->base.sector_count;
vfs_xipfs_mount->page_addr = (void *)(
(unsigned char *)XIPFS_NVM_BASE
+ (flashpage->offset * XIPFS_NVM_PAGE_SIZE)
);
vfs_xipfs_mount->execution_mutex = execution_mutex;
mutex_init(vfs_xipfs_mount->execution_mutex);
vfs_xipfs_mount->mutex = mutex;
mutex_init(vfs_xipfs_mount->mutex);
return 0;
}

146
sys/include/fs/xipfs_fs.h Normal file
View File

@ -0,0 +1,146 @@
/*
* 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.
*/
/**
* @defgroup sys_xipfs xipfs integration
* @ingroup pkg_xipfs
* @brief RIOT integration of xipfs
*
* @{
*
* @file
* @brief xipfs integration with vfs
*
* @author Damien Amara <damien.amara@univ-lille.fr>
* @author Gregory Guche <gregory.guche@univ-lille.fr>
*/
#ifndef FS_XIPFS_FS_H
#define FS_XIPFS_FS_H
#include "vfs.h"
#include "mtd_flashpage.h"
#include "mutex.h"
#include "include/xipfs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @def XIPFS_NEW_PARTITION
*
* @brief Allocate a new contiguous space aligned to a page in
* the non-volatile addressable memory of the MCU to serve as a
* partition for an xipfs file system
*
* @param id Identifier name for the mount point used by
* functions that manipulate xipfs file systems
*
* @param path The mount point of the file system in the VFS tree
*
* @param num The total number of pages allocated for the
* partition
*/
#define XIPFS_NEW_PARTITION(id, path, num) \
FLASH_WRITABLE_INIT(xipfs_part_##id, num); \
static mutex_t execution_mutex_##id = MUTEX_INIT; \
static mutex_t mutex_##id = MUTEX_INIT; \
static vfs_xipfs_mount_t id = { \
.vfs_mp = { \
.fs = &xipfs_file_system, \
.mount_point = path, \
}, \
.magic = XIPFS_MAGIC, \
.mount_path = path, \
.page_num = num, \
.page_addr = (void *)xipfs_part_##id, \
.execution_mutex = &execution_mutex_##id, \
.mutex = &mutex_##id \
}
/**
* @brief xipfs descriptor for vfs integration
*
* execution_mutex is taken by xipfs_extended_driver_execv and by deletion operations.
* mutex is taken by all operations except from xipfs_extended_driver_execv
*
* This two mutexes scheme :
* - allows to call xipfs operation from within a code run by xipfs_extended_driver_execv,
* - prevents from deleting files or directories when operations are performed on them.
*/
typedef struct vfs_xipfs_mount_s {
vfs_mount_t vfs_mp; /**< VFS mount point */
unsigned magic; /**< xipfs magic number */
const char *mount_path; /**< mount point path */
size_t page_num; /**< number of flash page */
void *page_addr; /**< first flash page address */
mutex_t *execution_mutex;/**< For execution and deletion operations */
mutex_t *mutex; /**< For regular and deletion operations */
} vfs_xipfs_mount_t;
/** The xipfs vfs driver */
extern const vfs_file_system_t xipfs_file_system;
/**
* @brief vfs_xipfs_mount_t constructor from a mtd_flashpage_t.
*
* This function constructs an xipfs mount point from a mtd_flashpage_t such as mtd_flash_aux_slot.
* It allows to use XIPFS with the RIOT AUX slot mechanism.
*
* @param flashpage A valid mtd_flashpage_t pointer such as &mtd_flash_aux_slot.
* @param path A valid mounting point path.
* @param execution_mutex A valid mutex pointer used for execution and deletions operations.
* @param mutex A valid mutex pointer used by all operations except from execution.
* @param vfs_xipfs_mount A valid vfs xipfs mount point pointer.
*
* @retval -EINVAL when one of the parameters is NULL or when path is an empty string.
* @retval 0 on success.
*
* @note Both mutexes will be initialized by this function.
*/
int xipfs_construct_from_flashpage(mtd_flashpage_t *flashpage, const char *path,
mutex_t *execution_mutex, mutex_t *mutex,
vfs_xipfs_mount_t *vfs_xipfs_mount);
/* Extended driver handling executables */
/**
* @brief Executable or regular new file.
*
* Allows to create a regular or executable new file within XiPFS.
*
* @param full_path A full path such as `/dev/nvme0p0/my_new_file`
*
* @param size The file size in bytes.
*
* @param exec 0 for regular files, 1 for executable files.
*
* @retval Less than 0 on errors.
* @retval 0 on success.
*/
int xipfs_extended_driver_new_file(const char *full_path, uint32_t size, uint32_t exec);
/**
* @brief Executes an executable file with arguments.
*
* @param full_path A full path such as `/dev/nvme0p0/my_executable_file`
*
* @param argv Executable arguments. Cannot be NULL, argv[0] contains the executable filename.
*
* @retval Less than 0 on errors.
* @retval 0 on success.
*/
int xipfs_extended_driver_execv(const char *full_path, char *const argv[]);
#ifdef __cplusplus
}
#endif
#endif /* FS_XIPFS_FS_H */
/** @} */

View File

@ -78,10 +78,10 @@ extern "C" {
#endif
/**
* @brief MAX6 Function to get the largest of 6 values
* @brief MAX7 Function to get the largest of 7 values
*/
#ifndef MAX6
#define MAX6(a, b, c, d, e, f) MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f))
#ifndef MAX7
#define MAX7(a, b, c, d, e, f, g) MAX(MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f)), (g))
#endif
/**
@ -217,6 +217,19 @@ extern "C" {
#endif
/** @} */
/**
* @brief VFS parameters for xipfs
* @{
*/
#if defined(MODULE_XIPFS) || DOXYGEN
# define XIPFS_VFS_DIR_BUFFER_SIZE (68) /**< sizeof(xipfs_dir_desc_t) */
# define XIPFS_VFS_FILE_BUFFER_SIZE (12) /**< sizeof(xipfs_file_desc_t) */
#else
# define XIPFS_VFS_DIR_BUFFER_SIZE (1)
# define XIPFS_VFS_FILE_BUFFER_SIZE (1)
#endif
/** @} */
#ifndef VFS_MAX_OPEN_FILES
/**
* @brief Maximum number of simultaneous open files
@ -252,12 +265,13 @@ extern "C" {
* @attention Put the check in the public header file (.h), do not put the check in the
* implementation (.c) file.
*/
#define VFS_DIR_BUFFER_SIZE MAX6(FATFS_VFS_DIR_BUFFER_SIZE, \
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
SPIFFS_VFS_DIR_BUFFER_SIZE, \
LWEXT4_VFS_DIR_BUFFER_SIZE, \
NANOCOAP_FS_VFS_DIR_BUFFER_SIZE \
#define VFS_DIR_BUFFER_SIZE MAX7(FATFS_VFS_DIR_BUFFER_SIZE, \
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
SPIFFS_VFS_DIR_BUFFER_SIZE, \
LWEXT4_VFS_DIR_BUFFER_SIZE, \
NANOCOAP_FS_VFS_DIR_BUFFER_SIZE, \
XIPFS_VFS_DIR_BUFFER_SIZE \
)
#endif
@ -281,12 +295,13 @@ extern "C" {
* @attention Put the check in the public header file (.h), do not put the check in the
* implementation (.c) file.
*/
#define VFS_FILE_BUFFER_SIZE MAX6(FATFS_VFS_FILE_BUFFER_SIZE, \
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
LITTLEFS2_VFS_FILE_BUFFER_SIZE, \
SPIFFS_VFS_FILE_BUFFER_SIZE, \
LWEXT4_VFS_FILE_BUFFER_SIZE, \
NANOCOAP_FS_VFS_FILE_BUFFER_SIZE \
#define VFS_FILE_BUFFER_SIZE MAX7(FATFS_VFS_FILE_BUFFER_SIZE, \
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
LITTLEFS2_VFS_FILE_BUFFER_SIZE, \
SPIFFS_VFS_FILE_BUFFER_SIZE, \
LWEXT4_VFS_FILE_BUFFER_SIZE, \
NANOCOAP_FS_VFS_FILE_BUFFER_SIZE, \
XIPFS_VFS_FILE_BUFFER_SIZE \
)
#endif

View File

@ -43,6 +43,9 @@
#if IS_USED(MODULE_LWEXT4)
#include "fs/lwext4_fs.h"
#endif
#if IS_USED(MODULE_XIPFS)
#include "fs/xipfs_fs.h"
#endif
#if IS_USED(MODULE_FS_NATIVE)
#include "fs/native_fs.h"
#endif

View File

@ -288,6 +288,10 @@ ifneq (,$(filter shell_cmd_vfs,$(USEMODULE)))
USEMODULE += tiny_strerror
endif
ifneq (,$(filter shell_cmd_xipfs,$(USEMODULE)))
USEMODULE += xipfs
endif
ifneq (,$(filter shell_democommands,$(USEMODULE)))
USEMODULE += rust_riotmodules
USEMODULE += shell

93
sys/shell/cmds/xipfs.c Normal file
View File

@ -0,0 +1,93 @@
/*
* 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 sys_shell_commands
* @{
*
* @file
* @brief Shell commands for XIPFS
*
* @author Gregory Guche <gregory.guche@univ-lille.fr>
*
* @}
*/
#if defined(MODULE_XIPFS_FS) || defined(MODULE_XIPFS)
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include "fs/xipfs_fs.h"
#include "include/xipfs.h"
#include "shell.h"
static char *execute_file_handler_args[XIPFS_EXEC_ARGC_MAX];
static int _execute_file_handler(int argc, char **argv) {
if ( (argc == 1) || (argc > XIPFS_EXEC_ARGC_MAX) ) {
printf("Usage %s xipfs_executable_filename [arg0] [arg1] ... [arg%d]\n",
argv[0], (XIPFS_EXEC_ARGC_MAX - 1));
printf("\t- xipfs_executable_filename : filename of the desired XIPFS file to execute\n");
return 1;
}
memset(execute_file_handler_args, 0, sizeof(execute_file_handler_args));
for (int i = 1; i <argc; ++i) {
execute_file_handler_args[i-1] = argv[i];
}
int ret = xipfs_extended_driver_execv(argv[1], execute_file_handler_args);
if (ret != 0) {
printf("Failed to execute '%s', error=%d\n", argv[1], ret);
return 2;
}
return 0;
}
SHELL_COMMAND(execute, "Execute an XIPFS file", _execute_file_handler);
static void print_create_executable_file_usage(int argc, char **argv) {
(void)argc;
printf("Usage %s xipfs_executable_filename xipfs_executable_file_bytesize\n", argv[0]);
printf("\t- xipfs_executable_filename : filename of the desired XIPFS file\n");
printf("\t- xipfs_executable_file_bytesize : bytesize of the desired XIPFS file\n");
}
static int _create_executable_file(int argc, char **argv) {
if (argc != 3) {
print_create_executable_file_usage(argc, argv);
return 1;
}
unsigned long file_size = 0;
if (argv[2][0] == '-') {
printf("Error : xipfs_executable_file_bytesize must be positive.\n");
print_create_executable_file_usage(argc, argv);
return 2;
}
file_size = strtoul(argv[2], NULL, 10);
if (file_size >= (unsigned long)UINT32_MAX) {
printf("Error : xipfs_executable_file_bytesize must be less than %lu.\n", UINT32_MAX);
print_create_executable_file_usage(argc, argv);
return 3;
}
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]);
return 4;
}
return 0;
}
SHELL_COMMAND(create_executable, "Create an XIPFS executable file", _create_executable_file);
#endif /* MODULE_XIPFS_FS */

13
tests/pkg/xipfs/Makefile Normal file
View File

@ -0,0 +1,13 @@
include ../Makefile.pkg_common
BOARD ?= dwm1001
SLOT_AUX_LEN := 0x8000
USEPKG += xipfs
USEMODULE += xipfs
USEMODULE += saul_default
TOOLCHAINS_BLACKLIST += llvm
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,26 @@
BOARD_INSUFFICIENT_MEMORY := \
blackpill-stm32f103c8 \
bluepill-stm32f030c8 \
bluepill-stm32f103c8 \
i-nucleo-lrwan1 \
nucleo-c031c6 \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f302r8 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
samd10-xmini \
saml10-xpro \
saml11-xpro \
slstk3400a \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
weact-g030f6 \
#

20
tests/pkg/xipfs/README.md Normal file
View File

@ -0,0 +1,20 @@
# The eXecute In-Place File System tests
## Description
This application is a best effort to test the return codes of system
calls provided by `xipfs`. It accomplishes this by executing these calls
in various environments, ensuring that each system call returns the
expected error codes. However, further in-depth tests need to be done.
## Tested cards
`xipfs-tests` 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-tests` 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.

2507
tests/pkg/xipfs/main.c Normal file

File diff suppressed because it is too large Load Diff