mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-15 17:43:51 +01:00
pkg/xipfs: add XIPFS as vfs module
This commit is contained in:
parent
416db07ac5
commit
8dc500703b
@ -83,6 +83,12 @@ ifneq (,$(filter lwext%_vfs,$(USEMODULE)))
|
|||||||
USEMODULE += vfs
|
USEMODULE += vfs
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter xipfs,$(USEMODULE)))
|
||||||
|
USEPKG += xipfs
|
||||||
|
USEMODULE += vfs
|
||||||
|
USEMODULE += xipfs_fs
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter nimble_%,$(USEMODULE)))
|
ifneq (,$(filter nimble_%,$(USEMODULE)))
|
||||||
USEPKG += nimble
|
USEPKG += nimble
|
||||||
endif
|
endif
|
||||||
|
|||||||
@ -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. |
|
| [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. |
|
| [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. |
|
| [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
|
## Examples from Guides
|
||||||
|
|
||||||
|
|||||||
32
examples/advanced/xipfs/Makefile
Normal file
32
examples/advanced/xipfs/Makefile
Normal 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
|
||||||
24
examples/advanced/xipfs/Makefile.ci
Normal file
24
examples/advanced/xipfs/Makefile.ci
Normal 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 \
|
||||||
|
#
|
||||||
61
examples/advanced/xipfs/README.md
Normal file
61
examples/advanced/xipfs/README.md
Normal 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.
|
||||||
BIN
examples/advanced/xipfs/hello-world.fae
Normal file
BIN
examples/advanced/xipfs/hello-world.fae
Normal file
Binary file not shown.
227
examples/advanced/xipfs/main.c
Normal file
227
examples/advanced/xipfs/main.c
Normal 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;
|
||||||
|
}
|
||||||
@ -443,6 +443,7 @@ PSEUDOMODULES += shell_cmd_sntp
|
|||||||
PSEUDOMODULES += shell_cmd_suit
|
PSEUDOMODULES += shell_cmd_suit
|
||||||
PSEUDOMODULES += shell_cmd_sys
|
PSEUDOMODULES += shell_cmd_sys
|
||||||
PSEUDOMODULES += shell_cmd_udptty
|
PSEUDOMODULES += shell_cmd_udptty
|
||||||
|
PSEUDOMODULES += shell_cmd_xipfs
|
||||||
PSEUDOMODULES += shell_cmd_vfs
|
PSEUDOMODULES += shell_cmd_vfs
|
||||||
PSEUDOMODULES += shell_cmds_default
|
PSEUDOMODULES += shell_cmds_default
|
||||||
PSEUDOMODULES += shell_hooks
|
PSEUDOMODULES += shell_hooks
|
||||||
|
|||||||
17
pkg/xipfs/Makefile
Normal file
17
pkg/xipfs/Makefile
Normal 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
13
pkg/xipfs/Makefile.dep
Normal 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
|
||||||
8
pkg/xipfs/Makefile.include
Normal file
8
pkg/xipfs/Makefile.include
Normal 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
32
pkg/xipfs/doc.txt
Normal 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
5
pkg/xipfs/fs/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
MODULE := xipfs_fs
|
||||||
|
|
||||||
|
CFLAGS+=-DRIOT_OS
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.base
|
||||||
41
pkg/xipfs/fs/xipfs_flashpage.c
Normal file
41
pkg/xipfs/fs/xipfs_flashpage.c
Normal 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
790
pkg/xipfs/fs/xipfs_fs.c
Normal 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
146
sys/include/fs/xipfs_fs.h
Normal 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 */
|
||||||
|
/** @} */
|
||||||
@ -78,10 +78,10 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief MAX6 Function to get the largest of 6 values
|
* @brief MAX7 Function to get the largest of 7 values
|
||||||
*/
|
*/
|
||||||
#ifndef MAX6
|
#ifndef MAX7
|
||||||
#define MAX6(a, b, c, d, e, f) MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f))
|
#define MAX7(a, b, c, d, e, f, g) MAX(MAX(MAX(MAX(MAX((a), (b)), MAX((c), (d))), (e)), (f)), (g))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,6 +217,19 @@ extern "C" {
|
|||||||
#endif
|
#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
|
#ifndef VFS_MAX_OPEN_FILES
|
||||||
/**
|
/**
|
||||||
* @brief Maximum number of simultaneous 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
|
* @attention Put the check in the public header file (.h), do not put the check in the
|
||||||
* implementation (.c) file.
|
* implementation (.c) file.
|
||||||
*/
|
*/
|
||||||
#define VFS_DIR_BUFFER_SIZE MAX6(FATFS_VFS_DIR_BUFFER_SIZE, \
|
#define VFS_DIR_BUFFER_SIZE MAX7(FATFS_VFS_DIR_BUFFER_SIZE, \
|
||||||
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
|
LITTLEFS_VFS_DIR_BUFFER_SIZE, \
|
||||||
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
|
LITTLEFS2_VFS_DIR_BUFFER_SIZE, \
|
||||||
SPIFFS_VFS_DIR_BUFFER_SIZE, \
|
SPIFFS_VFS_DIR_BUFFER_SIZE, \
|
||||||
LWEXT4_VFS_DIR_BUFFER_SIZE, \
|
LWEXT4_VFS_DIR_BUFFER_SIZE, \
|
||||||
NANOCOAP_FS_VFS_DIR_BUFFER_SIZE \
|
NANOCOAP_FS_VFS_DIR_BUFFER_SIZE, \
|
||||||
|
XIPFS_VFS_DIR_BUFFER_SIZE \
|
||||||
)
|
)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -281,12 +295,13 @@ extern "C" {
|
|||||||
* @attention Put the check in the public header file (.h), do not put the check in the
|
* @attention Put the check in the public header file (.h), do not put the check in the
|
||||||
* implementation (.c) file.
|
* implementation (.c) file.
|
||||||
*/
|
*/
|
||||||
#define VFS_FILE_BUFFER_SIZE MAX6(FATFS_VFS_FILE_BUFFER_SIZE, \
|
#define VFS_FILE_BUFFER_SIZE MAX7(FATFS_VFS_FILE_BUFFER_SIZE, \
|
||||||
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
|
LITTLEFS_VFS_FILE_BUFFER_SIZE, \
|
||||||
LITTLEFS2_VFS_FILE_BUFFER_SIZE, \
|
LITTLEFS2_VFS_FILE_BUFFER_SIZE, \
|
||||||
SPIFFS_VFS_FILE_BUFFER_SIZE, \
|
SPIFFS_VFS_FILE_BUFFER_SIZE, \
|
||||||
LWEXT4_VFS_FILE_BUFFER_SIZE, \
|
LWEXT4_VFS_FILE_BUFFER_SIZE, \
|
||||||
NANOCOAP_FS_VFS_FILE_BUFFER_SIZE \
|
NANOCOAP_FS_VFS_FILE_BUFFER_SIZE, \
|
||||||
|
XIPFS_VFS_FILE_BUFFER_SIZE \
|
||||||
)
|
)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,9 @@
|
|||||||
#if IS_USED(MODULE_LWEXT4)
|
#if IS_USED(MODULE_LWEXT4)
|
||||||
#include "fs/lwext4_fs.h"
|
#include "fs/lwext4_fs.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if IS_USED(MODULE_XIPFS)
|
||||||
|
#include "fs/xipfs_fs.h"
|
||||||
|
#endif
|
||||||
#if IS_USED(MODULE_FS_NATIVE)
|
#if IS_USED(MODULE_FS_NATIVE)
|
||||||
#include "fs/native_fs.h"
|
#include "fs/native_fs.h"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -288,6 +288,10 @@ ifneq (,$(filter shell_cmd_vfs,$(USEMODULE)))
|
|||||||
USEMODULE += tiny_strerror
|
USEMODULE += tiny_strerror
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter shell_cmd_xipfs,$(USEMODULE)))
|
||||||
|
USEMODULE += xipfs
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter shell_democommands,$(USEMODULE)))
|
ifneq (,$(filter shell_democommands,$(USEMODULE)))
|
||||||
USEMODULE += rust_riotmodules
|
USEMODULE += rust_riotmodules
|
||||||
USEMODULE += shell
|
USEMODULE += shell
|
||||||
|
|||||||
93
sys/shell/cmds/xipfs.c
Normal file
93
sys/shell/cmds/xipfs.c
Normal 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
13
tests/pkg/xipfs/Makefile
Normal 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
|
||||||
26
tests/pkg/xipfs/Makefile.ci
Normal file
26
tests/pkg/xipfs/Makefile.ci
Normal 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
20
tests/pkg/xipfs/README.md
Normal 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
2507
tests/pkg/xipfs/main.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user